From 3a2d80febdca03928cf5607fe13268e04709d887 Mon Sep 17 00:00:00 2001 From: Sagar Seth Date: Tue, 25 Nov 2025 18:20:43 +0530 Subject: [PATCH 01/14] fix: correctly detect default values including false in template parameters --- apps/generator/lib/templates/config/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/generator/lib/templates/config/loader.js b/apps/generator/lib/templates/config/loader.js index 8685b32b7c..b523813471 100644 --- a/apps/generator/lib/templates/config/loader.js +++ b/apps/generator/lib/templates/config/loader.js @@ -46,7 +46,7 @@ async function loadTemplateConfig(templateDir, templateParams) { */ function loadDefaultValues(templateConfig, templateParams) { const parameters = templateConfig.parameters; - const defaultValues = Object.keys(parameters || {}).filter(key => parameters[key].default); + const defaultValues = Object.keys(parameters || {}).filter(key => parameters[key].default !== undefined); defaultValues.filter(dv => !Object.prototype.hasOwnProperty.call(templateParams, dv)).forEach(dv => Object.defineProperty(templateParams, dv, { From deff9893026f29eca662f1943197bdc5fff04276 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Mon, 1 Dec 2025 19:35:23 +0100 Subject: [PATCH 02/14] ci: update release pipeline (#1774) Co-authored-by: Lukasz Gornicki --- .github/workflows/release-with-changesets.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release-with-changesets.yml b/.github/workflows/release-with-changesets.yml index 327dfd4eb4..48bcb66100 100644 --- a/.github/workflows/release-with-changesets.yml +++ b/.github/workflows/release-with-changesets.yml @@ -29,9 +29,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - # Using macos-13 instead of latest (macos-14) due to an issue with Puppeteer and such runner. - # See: https://github.com/puppeteer/puppeteer/issues/12327 and https://github.com/asyncapi/parser-js/issues/1001 - os: [ubuntu-latest, macos-13, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Set git to use LF # To once and for all finish the never-ending fight between Unix and Windows run: | @@ -121,7 +119,6 @@ jobs: setupGitUser: false env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GIT_AUTHOR_NAME: asyncapi-bot GIT_AUTHOR_EMAIL: info@asyncapi.io GIT_COMMITTER_NAME: asyncapi-bot From a04a2b4aa2c4fff859530da5412b36ccf69e1a9b Mon Sep 17 00:00:00 2001 From: Adi Boghawala Date: Tue, 2 Dec 2025 00:21:33 +0530 Subject: [PATCH 03/14] feat: release `@asyncapi/keeper` and use for message validation in `JS` websocket client (#1747) Co-authored-by: Adi-204 Co-authored-by: Adi Boghawala Co-authored-by: Chan Co-authored-by: Lukasz Gornicki --- .changeset/release-keeper.md | 5 + apps/generator/jest.config.js | 12 +- apps/keeper/jest.config.js | 14 + apps/keeper/jest.setup.js | 5 - apps/keeper/package.json | 38 +- apps/keeper/src/index.js | 119 ++--- apps/keeper/src/utils.js | 10 - .../asyncapi-message-validation.yml | 7 +- apps/keeper/test/index.test.js | 92 ++-- jest.config.base.js | 25 + package-lock.json | 448 ++++-------------- packages/components/jest.config.js | 14 + packages/components/package.json | 14 - .../src/components/DependencyProvider.js | 2 +- .../src/components/SendOperations.js | 38 +- .../DependencyProvider.test.js.snap | 5 +- .../__snapshots__/SendOperations.test.js.snap | 38 +- packages/helpers/jest.config.js | 6 + packages/helpers/package.json | 6 - .../clients/kafka/java/quarkus/.ageneratorrc | 4 +- .../clients/kafka/java/quarkus/jest.config.js | 14 + .../clients/kafka/java/quarkus/package.json | 14 - .../test/integration-test/jest.config.js | 14 + .../kafka/test/integration-test/package.json | 14 - .../clients/websocket/dart/jest.config.js | 14 + .../clients/websocket/dart/package.json | 14 - .../websocket/java/quarkus/jest.config.js | 14 + .../websocket/java/quarkus/package.json | 14 - .../websocket/javascript/.ageneratorrc | 8 + .../javascript/components/ClientClass.js | 6 +- .../components/CompileOperationSchemas.js | 40 ++ .../javascript/components/Constructor.js | 8 +- .../websocket/javascript/jest.config.js | 14 + .../clients/websocket/javascript/package.json | 23 +- .../javascript/template/client.js.js | 6 +- .../clients/websocket/python/jest.config.js | 14 + .../clients/websocket/python/package.json | 14 - .../__snapshots__/integration.test.js.snap | 327 ++++++++++++- .../test/integration-test/jest.config.js | 14 + .../test/integration-test/package.json | 14 - .../websocket/test/javascript/jest.config.js | 14 + .../websocket/test/javascript/package.json | 14 - 42 files changed, 853 insertions(+), 677 deletions(-) create mode 100644 .changeset/release-keeper.md create mode 100644 apps/keeper/jest.config.js delete mode 100644 apps/keeper/jest.setup.js create mode 100644 jest.config.base.js create mode 100644 packages/components/jest.config.js create mode 100644 packages/helpers/jest.config.js create mode 100644 packages/templates/clients/kafka/java/quarkus/jest.config.js create mode 100644 packages/templates/clients/kafka/test/integration-test/jest.config.js create mode 100644 packages/templates/clients/websocket/dart/jest.config.js create mode 100644 packages/templates/clients/websocket/java/quarkus/jest.config.js create mode 100644 packages/templates/clients/websocket/javascript/components/CompileOperationSchemas.js create mode 100644 packages/templates/clients/websocket/javascript/jest.config.js create mode 100644 packages/templates/clients/websocket/python/jest.config.js create mode 100644 packages/templates/clients/websocket/test/integration-test/jest.config.js create mode 100644 packages/templates/clients/websocket/test/javascript/jest.config.js diff --git a/.changeset/release-keeper.md b/.changeset/release-keeper.md new file mode 100644 index 0000000000..e581c9a6f0 --- /dev/null +++ b/.changeset/release-keeper.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/keeper": minor +--- + +Initial release of `@asyncapi/keeper` to make it available for JS templates in `@asyncapi/generator`. The generator release will contain a new JS client version with enabled message validation. diff --git a/apps/generator/jest.config.js b/apps/generator/jest.config.js index 9baf4dad80..e77554f86b 100644 --- a/apps/generator/jest.config.js +++ b/apps/generator/jest.config.js @@ -1,9 +1,11 @@ const path = require('path'); +const baseConfig = require('../../jest.config.base'); + module.exports = { + ...baseConfig(__dirname, { + moduleNameMapper: { + '^@asyncapi/nunjucks-filters$': path.resolve(__dirname, '../nunjucks-filters'), + }, + }), clearMocks: true, - moduleNameMapper: { - '^nimma/legacy$': '../../node_modules/nimma/dist/legacy/cjs/index.js', - '^nimma/(.*)': '../../node_modules/nimma/dist/cjs/$1', - '^@asyncapi/nunjucks-filters$': path.resolve(__dirname, '../nunjucks-filters'), - }, }; diff --git a/apps/keeper/jest.config.js b/apps/keeper/jest.config.js new file mode 100644 index 0000000000..220352db41 --- /dev/null +++ b/apps/keeper/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/apps/keeper/jest.setup.js b/apps/keeper/jest.setup.js deleted file mode 100644 index 1f1045b329..0000000000 --- a/apps/keeper/jest.setup.js +++ /dev/null @@ -1,5 +0,0 @@ -import structuredClone from '@ungap/structured-clone'; - -if (typeof global.structuredClone !== 'function') { - global.structuredClone = structuredClone; -} \ No newline at end of file diff --git a/apps/keeper/package.json b/apps/keeper/package.json index 207e0aa538..7a35ebf4f4 100644 --- a/apps/keeper/package.json +++ b/apps/keeper/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/keeper", - "version": "0.0.1", + "version": "0.1.0", "description": "AsyncAPI message payload validation library that validates messages against JSON Schema (Draft-07)", "scripts": { "build": "babel src --out-dir lib", @@ -13,46 +13,32 @@ "type": "git", "url": "https://github.com/asyncapi/generator.git" }, + "files": [ + "lib/**", + "README.md", + "LICENSE" + ], "main": "lib/index.js", "author": "Adi Boghawala ", "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, "dependencies": { "@asyncapi/parser": "^3.4.0", - "@hyperjump/json-schema": "^1.16.2" + "ajv": "^8.17.1" }, "devDependencies": { "@babel/cli": "^7.25.9", "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", - "@ungap/structured-clone": "^1.3.0", + "babel-jest": "^27.3.1", "jest": "^27.3.1", "jest-esm-transformer": "^1.0.0" }, - "jest": { - "setupFiles": [ - "/jest.setup.js" - ], - "transformIgnorePatterns": [ - "node_modules/(?!@hyperjump)" - ], - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "jest-esm-transformer" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../node_modules/nimma/dist/cjs/$1", - "^@hyperjump/browser/jref$": "/../../node_modules/@hyperjump/browser/lib/jref/index.js", - "^@hyperjump/json-schema/experimental$": "/node_modules/@hyperjump/json-schema/lib/experimental.js" - } - }, "babel": { "presets": [ "@babel/preset-env" ] } -} +} \ No newline at end of file diff --git a/apps/keeper/src/index.js b/apps/keeper/src/index.js index 2ea0d742c5..e4fa68fb30 100644 --- a/apps/keeper/src/index.js +++ b/apps/keeper/src/index.js @@ -1,44 +1,31 @@ -import { - getAllRegisteredSchemaUris, - registerSchema, - unregisterSchema, - validate, - setMetaSchemaOutputFormat -} from '@hyperjump/json-schema/draft-07'; -import { generateSchemaURI, parseAsyncAPIDocumentFromFile } from './utils'; -import { DETAILED } from '@hyperjump/json-schema/experimental'; +import { parseAsyncAPIDocumentFromFile } from './utils.js'; +import Ajv from 'ajv'; -setMetaSchemaOutputFormat(DETAILED); +const ajv = new Ajv({ strict: false, allErrors: true }); + +/** + * Compiles a single JSON Schema for validation. + * @param {object} schema - The JSON Schema object to compile. + * @returns {function} A compiled schema validator function. + * @throws {Error} Throws error if AJV cannot compile the schema. + */ +export function compileSchema(schema) { + return ajv.compile(schema); +} /** * Compiles multiple JSON Schemas for validation. - * @async - * @param {Array} schemas - Array of JSON Schema objects to compile - * @returns {Promise>} Array of compiled schema validator functions - * @throws {Error} When schemas parameter is not an array + * @param {Array} schemas - Array of JSON Schema objects to compile. + * @returns {Array} Array of compiled schema validator functions. + * @throws {Error} Throws error if AJV cannot compile a schema. */ -export async function compileSchemas(schemas) { - if (!schemas || schemas.length === 0) { - console.log('Skipping compilation: no schemas provided.'); - return []; - } +export function compileSchemas(schemas) { if (!Array.isArray(schemas)) { - throw new Error(`Invalid "schemas" parameter: expected an array, received ${typeof schemas}`); + throw new Error('Invalid "schemas" parameter: must be an array.'); } - const dialectId = 'http://json-schema.org/draft-07/schema#'; - const schemaUris = []; - - // Register all schemas first - for (const schema of schemas) { - const schemaURI = generateSchemaURI(); - registerSchema(schema, schemaURI, dialectId); - schemaUris.push(schemaURI); - } - const compiledSchemas = []; - for (const schemaUri of schemaUris) { - const compileSchema = await validate(schemaUri); - compiledSchemas.push(compileSchema); + for (const schema of schemas) { + compiledSchemas.push(compileSchema(schema)); } return compiledSchemas; } @@ -48,57 +35,51 @@ export async function compileSchemas(schemas) { * @async * @param {string} asyncapiFilepath - Path to the AsyncAPI document file * @param {string} operationId - ID of the operation to extract message schemas from - * @returns {Promise>} Array of compiled schema validator functions - * @throws {Error} When operationId is invalid or when the operation/messages can't be found + * @returns {Promise>} A Promise that resolves to an array of compiled schema validator functions. + * @throws {Error} When operationId is invalid or when the operation/messages can't be found. */ export async function compileSchemasByOperationId(asyncapiFilepath, operationId) { if (typeof operationId !== 'string' || !operationId.trim()) { - throw new Error(`Invalid "operationId" parameter: must be a non-empty string, received ${operationId}`); + throw new Error(`Invalid "operationId" parameter: must be a non-empty string, received ${operationId}`); } const asyncapi = await parseAsyncAPIDocumentFromFile(asyncapiFilepath); const operation = asyncapi.operations().get(operationId); + if (!operation) { + throw new Error(`Operation with ID "${operationId}" not found in the AsyncAPI document.`); + } const messages = operation.messages().all(); + if (!messages || messages.length === 0) { + console.warn(`No messages found for operation ID "${operationId}".`); + return []; + } const schemas = messages .filter(message => message.hasPayload()) - .map(message => message.payload().json()); + .map(message => message.payload().json()); return compileSchemas(schemas); } /** - * Validates a message payload against an array of operation message schemas. - * Uses the Hyperjump JSON Schema validator (Draft-07) to check if the message - * conforms to at least one of the provided schemas. - * @param {Array} compiledSchemas - Array of compiled schema validator functions - * @param {object} message - The message payload to validate - * @returns {{ isValid: boolean, validationErrors?: Array }} Object containing validation result and errors if invalid - * @throws {Error} When message parameter is null or undefined + * Validates a message payload against a compiled message schema. + * + * @param {function} compiledSchema - Compiled schema validator function. + * @param {object} message - The message payload to validate. + * @returns {{ isValid: boolean, validationErrors?: Array }} Object containing validation result. + * If invalid, `validationErrors` will contain the validation errors. + * @throws {Error} When message parameter is null/undefined or compiledSchema is not a function. */ -export function validateMessage(compiledSchemas, message) { - if (!compiledSchemas || compiledSchemas.length === 0) { - console.log('Skipping validation: no schemas provided for message validation.'); - return { isValid: true }; - } - if (message === null || message === undefined) { - throw new Error(`Invalid "message" parameter: expected a non-null object to validate, but received ${message}`); +export function validateMessage(compiledSchema, message) { + if (message === undefined) { + throw new Error(`Invalid "message" parameter: expected a value to validate, but received ${message}`); } - const validationErrors = []; - for (const compiledSchema of compiledSchemas) { - const result = compiledSchema(message, DETAILED); - if (result.valid) { - return { isValid: true }; - } - validationErrors.push(...result.errors); + if (typeof compiledSchema !== 'function') { + throw new Error(`Invalid "compiledSchema" parameter: expected a validator function, received ${typeof compiledSchema}`); } - return { isValid: false, validationErrors }; -} - -/** - * Unregisters all currently registered schemas from the validator. - * @returns {void} - */ -export function removeCompiledSchemas() { - const schemaUris = getAllRegisteredSchemaUris(); - for (const schemaUri of schemaUris) { - unregisterSchema(schemaUri); + const validate = compiledSchema(message); + if (validate) { + return { isValid: true }; } + return { + isValid: false, + validationErrors: compiledSchema.errors || [] + }; } \ No newline at end of file diff --git a/apps/keeper/src/utils.js b/apps/keeper/src/utils.js index f5f7a7c6ce..679c1b616e 100644 --- a/apps/keeper/src/utils.js +++ b/apps/keeper/src/utils.js @@ -21,14 +21,4 @@ export const parseAsyncAPIDocumentFromFile = async (asyncapiFilepath) => { throw new Error(`Failed to parse AsyncAPI document: ${error.message}`); } return asyncapi; -}; - -/** - * Generates a unique schema URI using the current timestamp. - * - * @returns {string} A unique schema URI. - */ -export const generateSchemaURI = () => { - const timestamp = Date.now().toString(); - return `http://asyncapi/keeper/schema/${timestamp}`; }; \ No newline at end of file diff --git a/apps/keeper/test/__fixtures__/asyncapi-message-validation.yml b/apps/keeper/test/__fixtures__/asyncapi-message-validation.yml index 3a81cd1c2f..4bb00321d7 100644 --- a/apps/keeper/test/__fixtures__/asyncapi-message-validation.yml +++ b/apps/keeper/test/__fixtures__/asyncapi-message-validation.yml @@ -31,9 +31,8 @@ components: type: string description: The content to be echoed back example: "test message" - timestamp: - type: string - format: date-time - description: When the message was sent + messageId: + type: number + description: Unique identifier for the message required: - content \ No newline at end of file diff --git a/apps/keeper/test/index.test.js b/apps/keeper/test/index.test.js index 8015129184..90638b913f 100644 --- a/apps/keeper/test/index.test.js +++ b/apps/keeper/test/index.test.js @@ -1,38 +1,37 @@ import path from 'path'; import { Parser, fromFile } from '@asyncapi/parser'; import { + compileSchema, compileSchemas, validateMessage, - compileSchemasByOperationId, - removeCompiledSchemas + compileSchemasByOperationId } from '../src/index.js'; const parser = new Parser(); const asyncapi_v3_path = path.resolve(__dirname, '../test/__fixtures__/asyncapi-message-validation.yml'); -describe('Integration Tests for message validation module', () => { - // Cleanup: Remove all compiled schemas from the local registry after all tests complete - afterAll(() => { - removeCompiledSchemas(); - }); +// Single helper function that can be used differently +async function parseAsyncAPIFile() { + const parseResult = await fromFile(parser, asyncapi_v3_path).parse(); + return parseResult.document; +} +describe('Integration Tests for message validation module', () => { describe('Schema Compilation & Basic Validation', () => { - let compiledSchemas; - let rawSchemas; + let compiledSchema; beforeAll(async () => { - const parseResult = await fromFile(parser, asyncapi_v3_path).parse(); - const parsedAsyncAPIDocument = parseResult.document; - rawSchemas = parsedAsyncAPIDocument.schemas().all().map(schema => schema.json()); - compiledSchemas = await compileSchemas(rawSchemas); + const parsedAsyncAPIDocument = await parseAsyncAPIFile(); + const firstSchema = parsedAsyncAPIDocument.schemas().all()[0].json(); + compiledSchema = compileSchema(firstSchema); }); - test('should validate a correct message against schemas and return true', async () => { + test('should validate a correct message against schema and return true', async () => { const validMessage = { content: 'This is a test message', - timestamp: new Date().toISOString() + messageId: 11 }; - const result = validateMessage(compiledSchemas, validMessage); + const result = validateMessage(compiledSchema, validMessage); expect(result.isValid).toBe(true); expect(result.validationErrors).toBeUndefined(); }); @@ -41,58 +40,91 @@ describe('Integration Tests for message validation module', () => { const invalidMessage = { content: 42 }; - const result = validateMessage(compiledSchemas, invalidMessage); + const result = validateMessage(compiledSchema, invalidMessage); expect(result.isValid).toBe(false); expect(result.validationErrors.length).toBeGreaterThan(0); }); - test('should return false when message cannot match any schema', async () => { + test('should return false when message cannot match schema', async () => { const invalidMessage = 42; - const result = validateMessage(compiledSchemas, invalidMessage); + const result = validateMessage(compiledSchema, invalidMessage); expect(result.isValid).toBe(false); expect(result.validationErrors.length).toBeGreaterThan(0); }); test('should return false when required field is missing', async () => { const invalidMessage = { - timestamp: new Date().toISOString() + messageId: 12 }; - const result = validateMessage(compiledSchemas, invalidMessage); + const result = validateMessage(compiledSchema, invalidMessage); expect(result.isValid).toBe(false); expect(result.validationErrors.length).toBeGreaterThan(0); }); - test('should throw error if message is null', () => { - expect(() => validateMessage(compiledSchemas, null)).toThrow('Invalid "message" parameter'); + test('should throw error if message is undefined', () => { + expect(() => validateMessage(compiledSchema, undefined)).toThrow('Invalid "message" parameter'); }); - test('should throw error if message is undefined', () => { - expect(() => validateMessage(compiledSchemas, undefined)).toThrow('Invalid "message" parameter'); + test('should throw error if compiledSchema is not a function', () => { + expect(() => validateMessage({}, { content: 'test' })).toThrow('Invalid "compiledSchema" parameter'); + }); + }); + + describe('compileSchemas utility function', () => { + test('should compile multiple schemas successfully', async () => { + const parsedAsyncAPIDocument = await parseAsyncAPIFile(); + const allSchemas = parsedAsyncAPIDocument.schemas().all(); + const rawSchemas = allSchemas.map(schema => schema.json()); + const compiledSchemas = compileSchemas(rawSchemas); + + expect(Array.isArray(compiledSchemas)).toBe(true); + expect(compiledSchemas.length).toBe(rawSchemas.length); + compiledSchemas.forEach(schema => { + expect(typeof schema).toBe('function'); + }); + }); + + test('should throw error if schemas parameter is not an array', () => { + expect(() => compileSchemas({})).toThrow('Invalid "schemas" parameter'); }); }); describe('Operation-Specific Validation', () => { let compiledSchemas; + let compiledSchema; beforeAll(async () => { compiledSchemas = await compileSchemasByOperationId(asyncapi_v3_path, 'sendHelloMessage'); + compiledSchema = compiledSchemas[0]; }); - test('should validate a correct message against operation schemas and return true', async () => { + test('should validate a correct message against operation schema and return true', async () => { const validMessage = { content: 'This is a operation test message' }; - const result = validateMessage(compiledSchemas, validMessage); + const result = validateMessage(compiledSchema, validMessage); expect(result.isValid).toBe(true); }); - test('should return false for invalid message against operation schemas', async () => { + test('should return false for invalid message against operation schema', async () => { const invalidMessage = { - time: new Date().toISOString() + messageId: 10 }; - const result = validateMessage(compiledSchemas, invalidMessage); + const result = validateMessage(compiledSchema, invalidMessage); expect(result.isValid).toBe(false); expect(result.validationErrors.length).toBeGreaterThan(0); }); + + test('should throw error for invalid operationId', async () => { + await expect( + compileSchemasByOperationId(asyncapi_v3_path, 'nonExistentOperation') + ).rejects.toThrow('Operation with ID "nonExistentOperation" not found'); + }); + + test('should throw error for empty operationId', async () => { + await expect( + compileSchemasByOperationId(asyncapi_v3_path, '') + ).rejects.toThrow('Invalid "operationId" parameter'); + }); }); }); \ No newline at end of file diff --git a/jest.config.base.js b/jest.config.base.js new file mode 100644 index 0000000000..e4b32baafd --- /dev/null +++ b/jest.config.base.js @@ -0,0 +1,25 @@ +const path = require('path'); + +/** + * Base Jest configuration for the AsyncAPI Generator monorepo. + * This provides shared configuration, particularly for the nimma module mapping. + * + * @param {string} dirname - The __dirname of the package using this config + * @param {object} options - Additional configuration options + * @param {object} options.moduleNameMapper - Additional moduleNameMapper entries to merge + * @returns {object} Jest configuration object + */ +module.exports = (dirname, options = {}) => { + const repoRoot = __dirname; + const relativePath = path.relative(dirname, repoRoot); + const nodeModulesPath = relativePath ? `${relativePath}/node_modules` : 'node_modules'; + + return { + moduleNameMapper: { + '^nimma/legacy$': `/${nodeModulesPath}/nimma/dist/legacy/cjs/index.js`, + '^nimma/(.*)': `/${nodeModulesPath}/nimma/dist/cjs/$1`, + ...(options.moduleNameMapper || {}), + }, + }; +}; + diff --git a/package-lock.json b/package-lock.json index 3500bc862a..88f73efb46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,76 +93,21 @@ }, "apps/keeper": { "name": "@asyncapi/keeper", - "version": "0.0.1", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@asyncapi/parser": "^3.4.0", - "@hyperjump/json-schema": "^1.16.2" + "ajv": "^8.17.1" }, "devDependencies": { "@babel/cli": "^7.25.9", "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", - "@ungap/structured-clone": "^1.3.0", + "babel-jest": "^27.3.1", "jest": "^27.3.1", "jest-esm-transformer": "^1.0.0" } }, - "apps/keeper/node_modules/@hyperjump/json-pointer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@hyperjump/json-pointer/-/json-pointer-1.1.1.tgz", - "integrity": "sha512-M0T3s7TC2JepoWPMZQn1W6eYhFh06OXwpMqL+8c5wMVpvnCKNsPgpu9u7WyCI03xVQti8JAeAy4RzUa6SYlJLA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - } - }, - "apps/keeper/node_modules/@hyperjump/json-schema": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/@hyperjump/json-schema/-/json-schema-1.16.2.tgz", - "integrity": "sha512-MJNvaEFc79+h5rvBPgAJK4OHEUr0RqsKcLC5rc3V9FEsJyQAjnP910deRFoZCE068kX/NrAPPhunMgUMwonPtg==", - "license": "MIT", - "dependencies": { - "@hyperjump/json-pointer": "^1.1.0", - "@hyperjump/pact": "^1.2.0", - "@hyperjump/uri": "^1.2.0", - "content-type": "^1.0.4", - "json-stringify-deterministic": "^1.0.12", - "just-curry-it": "^5.3.0", - "uuid": "^9.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - }, - "peerDependencies": { - "@hyperjump/browser": "^1.1.0" - } - }, - "apps/keeper/node_modules/@hyperjump/pact": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@hyperjump/pact/-/pact-1.4.0.tgz", - "integrity": "sha512-01Q7VY6BcAkp9W31Fv+ciiZycxZHGlR2N6ba9BifgyclHYHdbaZgITo0U6QMhYRlem4k8pf8J31/tApxvqAz8A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - } - }, - "apps/keeper/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "apps/nunjucks-filters": { "name": "@asyncapi/nunjucks-filters", "version": "2.1.0", @@ -863,6 +808,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1175,6 +1121,7 @@ "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^26.6.3", "import-local": "^3.0.2", @@ -2572,6 +2519,7 @@ "node_modules/@babel/core": { "version": "7.28.0", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -4358,37 +4306,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@hyperjump/browser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@hyperjump/browser/-/browser-1.3.1.tgz", - "integrity": "sha512-Le5XZUjnVqVjkgLYv6yyWgALat/0HpB1XaCPuCZ+GCFki9NvXloSZITIJ0H+wRW7mb9At1SxvohKBbNQbrr/cw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@hyperjump/json-pointer": "^1.1.0", - "@hyperjump/uri": "^1.2.0", - "content-type": "^1.0.5", - "just-curry-it": "^5.3.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - } - }, - "node_modules/@hyperjump/browser/node_modules/@hyperjump/json-pointer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@hyperjump/json-pointer/-/json-pointer-1.1.1.tgz", - "integrity": "sha512-M0T3s7TC2JepoWPMZQn1W6eYhFh06OXwpMqL+8c5wMVpvnCKNsPgpu9u7WyCI03xVQti8JAeAy4RzUa6SYlJLA==", - "license": "MIT", - "peer": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - } - }, "node_modules/@hyperjump/json": { "version": "0.1.0", "license": "MIT", @@ -4461,16 +4378,6 @@ "version": "3.2.1", "license": "MIT" }, - "node_modules/@hyperjump/uri": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@hyperjump/uri/-/uri-1.3.1.tgz", - "integrity": "sha512-2ecKymxf6prQMgrNpAvlx4RhsuM5+PFT6oh6uUTZdv5qmBv0RZvxv8LJ7oR30ZxGhdPdZAl4We/1NFc0nqHeAw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jdesrosiers" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "license": "ISC", @@ -6012,235 +5919,6 @@ "version": "2.8.1", "license": "0BSD" }, - "node_modules/@swc/core": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.29.tgz", - "integrity": "sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.21" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.11.29", - "@swc/core-darwin-x64": "1.11.29", - "@swc/core-linux-arm-gnueabihf": "1.11.29", - "@swc/core-linux-arm64-gnu": "1.11.29", - "@swc/core-linux-arm64-musl": "1.11.29", - "@swc/core-linux-x64-gnu": "1.11.29", - "@swc/core-linux-x64-musl": "1.11.29", - "@swc/core-win32-arm64-msvc": "1.11.29", - "@swc/core-win32-ia32-msvc": "1.11.29", - "@swc/core-win32-x64-msvc": "1.11.29" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.29.tgz", - "integrity": "sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.29.tgz", - "integrity": "sha512-S3eTo/KYFk+76cWJRgX30hylN5XkSmjYtCBnM4jPLYn7L6zWYEPajsFLmruQEiTEDUg0gBEWLMNyUeghtswouw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.29.tgz", - "integrity": "sha512-o9gdshbzkUMG6azldHdmKklcfrcMx+a23d/2qHQHPDLUPAN+Trd+sDQUYArK5Fcm7TlpG4sczz95ghN0DMkM7g==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.29.tgz", - "integrity": "sha512-sLoaciOgUKQF1KX9T6hPGzvhOQaJn+3DHy4LOHeXhQqvBgr+7QcZ+hl4uixPKTzxk6hy6Hb0QOvQEdBAAR1gXw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.29.tgz", - "integrity": "sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.29.tgz", - "integrity": "sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.29.tgz", - "integrity": "sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.29.tgz", - "integrity": "sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.29.tgz", - "integrity": "sha512-h+NjOrbqdRBYr5ItmStmQt6x3tnhqgwbj9YxdGPepbTDamFv7vFnhZR0YfB3jz3UKJ8H3uGJ65Zw1VsC+xpFkg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.29.tgz", - "integrity": "sha512-Q8cs2BDV9wqDvqobkXOYdC+pLUSEpX/KvI0Dgfun1F+LzuLotRFuDhrvkU9ETJA6OnD2+Fn/ieHgloiKA/Mn/g==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true - }, - "node_modules/@swc/types": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", - "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "dev": true, @@ -6269,6 +5947,7 @@ "version": "7.20.5", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -6478,6 +6157,7 @@ "version": "14.1.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -6656,6 +6336,7 @@ "node_modules/acorn": { "version": "8.15.0", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6747,6 +6428,7 @@ "node_modules/ajv": { "version": "8.17.1", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7629,6 +7311,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -9216,25 +8899,6 @@ "version": "9.2.2", "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -9488,6 +9152,7 @@ "version": "6.8.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", @@ -13564,6 +13229,7 @@ "node_modules/jsep": { "version": "1.4.0", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -13639,15 +13305,6 @@ "version": "1.0.1", "license": "MIT" }, - "node_modules/json-stringify-deterministic": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/json-stringify-deterministic/-/json-stringify-deterministic-1.0.12.tgz", - "integrity": "sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/json-stringify-nice": { "version": "1.1.4", "license": "ISC", @@ -15132,7 +14789,8 @@ }, "node_modules/openapi-types": { "version": "12.1.3", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/optionator": { "version": "0.8.3", @@ -16695,6 +16353,7 @@ "node_modules/rollup": { "version": "2.79.2", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -16830,7 +16489,7 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/sane": { @@ -18618,6 +18277,76 @@ "darwin" ] }, + "node_modules/turbo-darwin-arm64": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.13.3.tgz", + "integrity": "sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-linux-64": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.13.3.tgz", + "integrity": "sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-linux-arm64": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.13.3.tgz", + "integrity": "sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-windows-64": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.13.3.tgz", + "integrity": "sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/turbo-windows-arm64": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.13.3.tgz", + "integrity": "sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/type-check": { "version": "0.3.2", "dev": true, @@ -19831,7 +19560,8 @@ "dependencies": { "@asyncapi/generator-components": "0.3.1", "@asyncapi/generator-helpers": "0.2.0", - "@asyncapi/generator-react-sdk": "*" + "@asyncapi/generator-react-sdk": "*", + "@asyncapi/keeper": "0.1.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", diff --git a/packages/components/jest.config.js b/packages/components/jest.config.js new file mode 100644 index 0000000000..220352db41 --- /dev/null +++ b/packages/components/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/components/package.json b/packages/components/package.json index 36ace8f788..6b2c85d28f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -40,20 +40,6 @@ "jest": "^27.3.1", "jest-esm-transformer": "^1.0.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../node_modules/nimma/dist/cjs/$1" - } - }, "babel": { "presets": [ "@babel/preset-env", diff --git a/packages/components/src/components/DependencyProvider.js b/packages/components/src/components/DependencyProvider.js index 9e4c6d83ed..2c1438e1b4 100644 --- a/packages/components/src/components/DependencyProvider.js +++ b/packages/components/src/components/DependencyProvider.js @@ -16,7 +16,7 @@ const dependenciesConfig = { dependencies: ['import json', 'import certifi', 'import threading', 'import websocket'] }, javascript: { - dependencies: ['const WebSocket = require(\'ws\');'] + dependencies: ['const WebSocket = require(\'ws\');', 'const { compileSchemasByOperationId, validateMessage } = require(\'@asyncapi/keeper\');'] }, dart: { dependencies: ['import \'dart:convert\';', 'import \'package:web_socket_channel/web_socket_channel.dart\';'] diff --git a/packages/components/src/components/SendOperations.js b/packages/components/src/components/SendOperations.js index b48496cda2..60aeee53fa 100644 --- a/packages/components/src/components/SendOperations.js +++ b/packages/components/src/components/SendOperations.js @@ -46,26 +46,54 @@ async def ${staticMethodName}(message, socket): return { nonStaticMethod: `/** * Instance method version of ${methodName} that uses the client's own WebSocket connection. + * Automatically compiles schemas if not already compiled. + * * @param {Object} message - The message payload to send * @throws {Error} If WebSocket connection is not established + * @throws {Error} If schema compilation fails + * @throws {Error} If message validation fails against all schemas */ -${methodName}(message){ +async ${methodName}(message){ if(!this.websocket){ throw new Error('WebSocket connection not established. Call connect() first.'); } - ${clientName}.${methodName}(message, this.websocket); + await this.compileOperationSchemas(); + const schemas = this.compiledSchemas['${methodName}']; + ${clientName}.${methodName}(message, this.websocket, schemas); }`, staticMethod: `/** * Sends a ${methodName} message over the WebSocket connection. * * @param {Object} message - The message payload to send. Should match the schema defined in the AsyncAPI document. - * @param {WebSocket} [socket] - The WebSocket connection to use. If not provided, the client's own connection will be used. + * @param {WebSocket} socket - The WebSocket connection to use. + * @param {Array} schemas - Array of compiled schema validator functions for this operation. * @throws {TypeError} If message cannot be stringified to JSON * @throws {Error} If WebSocket connection is not in OPEN state + * @throws {Error} If message validation fails against all schemas */ -static ${methodName}(message, socket) { +static ${methodName}(message, socket, schemas) { try { - socket.send(JSON.stringify(message)); + if (!schemas || schemas.length === 0) { + socket.send(JSON.stringify(message)); + return { isValid: true }; + } + const allValidationErrors = []; + let isValid = false; + for(const compiledSchema of schemas){ + const validationResult = validateMessage(compiledSchema, message); + if (validationResult.isValid) { + isValid = true; + socket.send(JSON.stringify(message)); + break; + } else { + if (validationResult.validationErrors) { + allValidationErrors.push(...validationResult.validationErrors); + } + } + if (!isValid) { + console.error('Validation errors:', JSON.stringify(allValidationErrors, null, 2)); + } + } } catch (error) { console.error('Error sending ${methodName} message:', error); } diff --git a/packages/components/test/components/__snapshots__/DependencyProvider.test.js.snap b/packages/components/test/components/__snapshots__/DependencyProvider.test.js.snap index be34b49139..58c8748ee7 100644 --- a/packages/components/test/components/__snapshots__/DependencyProvider.test.js.snap +++ b/packages/components/test/components/__snapshots__/DependencyProvider.test.js.snap @@ -47,7 +47,10 @@ import io.quarkus.logging.Log; import io.quarkus.runtime.Startup;" `; -exports[`Testing of DependencyProvider function render js websockets file dependecies correctly 1`] = `"const WebSocket = require('ws');"`; +exports[`Testing of DependencyProvider function render js websockets file dependecies correctly 1`] = ` +"const WebSocket = require('ws'); +const { compileSchemasByOperationId, validateMessage } = require('@asyncapi/keeper');" +`; exports[`Testing of DependencyProvider function render python websockets file dependecies correctly 1`] = ` "import json diff --git a/packages/components/test/components/__snapshots__/SendOperations.test.js.snap b/packages/components/test/components/__snapshots__/SendOperations.test.js.snap index 2e523b5df1..42e5211e13 100644 --- a/packages/components/test/components/__snapshots__/SendOperations.test.js.snap +++ b/packages/components/test/components/__snapshots__/SendOperations.test.js.snap @@ -7,13 +7,35 @@ exports[`Testing of SendOperation function render js websockets with send operat * Sends a sendUserSignedup message over the WebSocket connection. * * @param {Object} message - The message payload to send. Should match the schema defined in the AsyncAPI document. - * @param {WebSocket} [socket] - The WebSocket connection to use. If not provided, the client's own connection will be used. + * @param {WebSocket} socket - The WebSocket connection to use. + * @param {Array} schemas - Array of compiled schema validator functions for this operation. * @throws {TypeError} If message cannot be stringified to JSON * @throws {Error} If WebSocket connection is not in OPEN state + * @throws {Error} If message validation fails against all schemas */ - static sendUserSignedup(message, socket) { + static sendUserSignedup(message, socket, schemas) { try { - socket.send(JSON.stringify(message)); + if (!schemas || schemas.length === 0) { + socket.send(JSON.stringify(message)); + return { isValid: true }; + } + const allValidationErrors = []; + let isValid = false; + for(const compiledSchema of schemas){ + const validationResult = validateMessage(compiledSchema, message); + if (validationResult.isValid) { + isValid = true; + socket.send(JSON.stringify(message)); + break; + } else { + if (validationResult.validationErrors) { + allValidationErrors.push(...validationResult.validationErrors); + } + } + if (!isValid) { + console.error('Validation errors:', JSON.stringify(allValidationErrors, null, 2)); + } + } } catch (error) { console.error('Error sending sendUserSignedup message:', error); } @@ -21,14 +43,20 @@ exports[`Testing of SendOperation function render js websockets with send operat /** * Instance method version of sendUserSignedup that uses the client's own WebSocket connection. + * Automatically compiles schemas if not already compiled. + * * @param {Object} message - The message payload to send * @throws {Error} If WebSocket connection is not established + * @throws {Error} If schema compilation fails + * @throws {Error} If message validation fails against all schemas */ - sendUserSignedup(message){ + async sendUserSignedup(message){ if(!this.websocket){ throw new Error('WebSocket connection not established. Call connect() first.'); } - AccountServiceAPI.sendUserSignedup(message, this.websocket); + await this.compileOperationSchemas(); + const schemas = this.compiledSchemas['sendUserSignedup']; + AccountServiceAPI.sendUserSignedup(message, this.websocket, schemas); }" `; diff --git a/packages/helpers/jest.config.js b/packages/helpers/jest.config.js new file mode 100644 index 0000000000..8eec1fc538 --- /dev/null +++ b/packages/helpers/jest.config.js @@ -0,0 +1,6 @@ +const baseConfig = require('../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), +}; + diff --git a/packages/helpers/package.json b/packages/helpers/package.json index bce7a12752..f7c9fa4d3a 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -20,11 +20,5 @@ }, "devDependencies": { "jest": "^27.3.1" - }, - "jest": { - "moduleNameMapper": { - "^nimma/legacy$": "/../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../node_modules/nimma/dist/cjs/$1" - } } } diff --git a/packages/templates/clients/kafka/java/quarkus/.ageneratorrc b/packages/templates/clients/kafka/java/quarkus/.ageneratorrc index 79ad07b277..1336c57768 100644 --- a/packages/templates/clients/kafka/java/quarkus/.ageneratorrc +++ b/packages/templates/clients/kafka/java/quarkus/.ageneratorrc @@ -13,11 +13,11 @@ parameters: description: The custom name for the generated client class required: false env: - description: 'The environment for the generated client, e.g., dev, prod' + description: The environment for the generated client, e.g., dev, prod required: false default: dev version: - description: 'The version for the generated client, e.g., V1, V2' + description: The version for the generated client, e.g., V1, V2 required: false default: V1 nonRenderableFiles: diff --git a/packages/templates/clients/kafka/java/quarkus/jest.config.js b/packages/templates/clients/kafka/java/quarkus/jest.config.js new file mode 100644 index 0000000000..9225f6e643 --- /dev/null +++ b/packages/templates/clients/kafka/java/quarkus/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/kafka/java/quarkus/package.json b/packages/templates/clients/kafka/java/quarkus/package.json index 59dc903d51..c811ae8df2 100644 --- a/packages/templates/clients/kafka/java/quarkus/package.json +++ b/packages/templates/clients/kafka/java/quarkus/package.json @@ -35,20 +35,6 @@ "eslint-plugin-sonarjs": "^0.5.0", "jest-esm-transformer": "^1.0.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } - }, "babel": { "presets": [ "@babel/preset-env", diff --git a/packages/templates/clients/kafka/test/integration-test/jest.config.js b/packages/templates/clients/kafka/test/integration-test/jest.config.js new file mode 100644 index 0000000000..9225f6e643 --- /dev/null +++ b/packages/templates/clients/kafka/test/integration-test/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/kafka/test/integration-test/package.json b/packages/templates/clients/kafka/test/integration-test/package.json index 7fff333cd9..f20838a5fe 100644 --- a/packages/templates/clients/kafka/test/integration-test/package.json +++ b/packages/templates/clients/kafka/test/integration-test/package.json @@ -25,19 +25,5 @@ "eslint-plugin-jest": "^23.8.2", "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../../node_modules/nimma/dist/cjs/$1" - } } } \ No newline at end of file diff --git a/packages/templates/clients/websocket/dart/jest.config.js b/packages/templates/clients/websocket/dart/jest.config.js new file mode 100644 index 0000000000..163a5dc322 --- /dev/null +++ b/packages/templates/clients/websocket/dart/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/dart/package.json b/packages/templates/clients/websocket/dart/package.json index d18fbd2aca..f1d6061fed 100644 --- a/packages/templates/clients/websocket/dart/package.json +++ b/packages/templates/clients/websocket/dart/package.json @@ -27,20 +27,6 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } - }, "babel": { "presets": [ "@babel/preset-env", diff --git a/packages/templates/clients/websocket/java/quarkus/jest.config.js b/packages/templates/clients/websocket/java/quarkus/jest.config.js new file mode 100644 index 0000000000..9225f6e643 --- /dev/null +++ b/packages/templates/clients/websocket/java/quarkus/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/java/quarkus/package.json b/packages/templates/clients/websocket/java/quarkus/package.json index d7a24e0649..5a3a2044b9 100644 --- a/packages/templates/clients/websocket/java/quarkus/package.json +++ b/packages/templates/clients/websocket/java/quarkus/package.json @@ -36,20 +36,6 @@ "eslint-plugin-sonarjs": "^0.5.0", "jest-esm-transformer": "^1.0.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } - }, "babel": { "presets": [ "@babel/preset-env", diff --git a/packages/templates/clients/websocket/javascript/.ageneratorrc b/packages/templates/clients/websocket/javascript/.ageneratorrc index 7b5a8e6fb5..286a850821 100644 --- a/packages/templates/clients/websocket/javascript/.ageneratorrc +++ b/packages/templates/clients/websocket/javascript/.ageneratorrc @@ -15,7 +15,15 @@ parameters: customClientName: description: The custom name for the generated client class required: false + asyncapiFileDir: + description: >- + Custom location of the AsyncAPI file that you provided as an input in generation. By default it is located in the + root of the output directory + required: false + default: . metadata: type: client protocol: websocket target: javascript +hooks: + '@asyncapi/generator-hooks': createAsyncapiFile diff --git a/packages/templates/clients/websocket/javascript/components/ClientClass.js b/packages/templates/clients/websocket/javascript/components/ClientClass.js index c505892906..70eee1fe75 100644 --- a/packages/templates/clients/websocket/javascript/components/ClientClass.js +++ b/packages/templates/clients/websocket/javascript/components/ClientClass.js @@ -2,14 +2,15 @@ import { Text } from '@asyncapi/generator-react-sdk'; import { Constructor } from './Constructor'; import { CloseConnection, RegisterMessageHandler, RegisterErrorHandler, SendOperations, Connect, HandleMessage } from '@asyncapi/generator-components'; import { ModuleExport } from './ModuleExport'; +import { CompileOperationSchemas } from './CompileOperationSchemas'; -export function ClientClass({ clientName, serverUrl, title, sendOperations}) { +export function ClientClass({ clientName, serverUrl, title, sendOperations }) { return ( {`class ${clientName} {`} - + + + + { + `/** + * Initialize and compile all schemas for operations defined. + * This should be called once after creating the client instance. + * Subsequent calls will be ignored if schemas are already compiled. + * + * @returns {Promise} + * @throws {Error} If schema compilation fails for any operation + */ +async compileOperationSchemas() { + if (this.schemasCompiled) { + return; + } + + try { + // Compile schemas for all send operations + for (const operationId of this.sendOperationsId) { + this.compiledSchemas[operationId] = await compileSchemasByOperationId(asyncapiFilepath, operationId); + } + this.schemasCompiled = true; + console.log('Schemas initialized successfully for operations:', this.sendOperationsId.join(', ')); + } catch (error) { + console.error('Error initializing schemas:', error); + } +}` + } + + + ); +} diff --git a/packages/templates/clients/websocket/javascript/components/Constructor.js b/packages/templates/clients/websocket/javascript/components/Constructor.js index ecc6d9b4f1..9df093ad6a 100644 --- a/packages/templates/clients/websocket/javascript/components/Constructor.js +++ b/packages/templates/clients/websocket/javascript/components/Constructor.js @@ -1,6 +1,9 @@ import { Text } from '@asyncapi/generator-react-sdk'; -export function Constructor({ serverUrl }) { +export function Constructor({ serverUrl, sendOperations }) { + const sendOperationsId = sendOperations.map((operation) => operation.id()); + const sendOperationsArray = JSON.stringify(sendOperationsId); + return ( { @@ -13,6 +16,9 @@ constructor(url) { this.websocket = null; this.messageHandlers = []; this.errorHandlers = []; + this.compiledSchemas = {}; + this.schemasCompiled = false; + this.sendOperationsId = ${sendOperationsArray}; } ` } diff --git a/packages/templates/clients/websocket/javascript/jest.config.js b/packages/templates/clients/websocket/javascript/jest.config.js new file mode 100644 index 0000000000..163a5dc322 --- /dev/null +++ b/packages/templates/clients/websocket/javascript/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/javascript/package.json b/packages/templates/clients/websocket/javascript/package.json index 553eb2b0a4..ae94431b04 100644 --- a/packages/templates/clients/websocket/javascript/package.json +++ b/packages/templates/clients/websocket/javascript/package.json @@ -11,9 +11,10 @@ "author": "Lukasz Gornicki ", "license": "Apache-2.0", "dependencies": { - "@asyncapi/generator-react-sdk": "*", "@asyncapi/generator-helpers": "0.2.0", - "@asyncapi/generator-components": "0.3.1" + "@asyncapi/generator-react-sdk": "*", + "@asyncapi/generator-components": "0.3.1", + "@asyncapi/keeper": "0.1.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", @@ -21,25 +22,11 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/preset-react": "^7.25.9", - "jest-esm-transformer": "^1.0.0", "eslint": "^6.8.0", "eslint-plugin-jest": "^23.8.2", "eslint-plugin-react": "^7.34.1", - "eslint-plugin-sonarjs": "^0.5.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } + "eslint-plugin-sonarjs": "^0.5.0", + "jest-esm-transformer": "^1.0.0" }, "babel": { "presets": [ diff --git a/packages/templates/clients/websocket/javascript/template/client.js.js b/packages/templates/clients/websocket/javascript/template/client.js.js index de479941e4..207fb33dc7 100644 --- a/packages/templates/clients/websocket/javascript/template/client.js.js +++ b/packages/templates/clients/websocket/javascript/template/client.js.js @@ -10,6 +10,7 @@ export default function ({ asyncapi, params }) { const clientName = getClientName(asyncapi, params.appendClientSuffix, params.customClientName); const serverUrl = getServerUrl(server); const sendOperations = asyncapi.operations().filterBySend(); + const asyncapiFilepath = `${params.asyncapiFileDir}/asyncapi.yaml`; return ( - + ); diff --git a/packages/templates/clients/websocket/python/jest.config.js b/packages/templates/clients/websocket/python/jest.config.js new file mode 100644 index 0000000000..163a5dc322 --- /dev/null +++ b/packages/templates/clients/websocket/python/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/python/package.json b/packages/templates/clients/websocket/python/package.json index 424950f3e1..f4745ffdcb 100644 --- a/packages/templates/clients/websocket/python/package.json +++ b/packages/templates/clients/websocket/python/package.json @@ -27,20 +27,6 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } - }, "babel": { "presets": [ "@babel/preset-env", diff --git a/packages/templates/clients/websocket/test/integration-test/__snapshots__/integration.test.js.snap b/packages/templates/clients/websocket/test/integration-test/__snapshots__/integration.test.js.snap index 91955334ab..20affd481b 100644 --- a/packages/templates/clients/websocket/test/integration-test/__snapshots__/integration.test.js.snap +++ b/packages/templates/clients/websocket/test/integration-test/__snapshots__/integration.test.js.snap @@ -6127,6 +6127,42 @@ main(); +" +`; + +exports[`WebSocket Clients Integration Tests JavaScript Client Common Integration tests for JavaScript client generation generate simple client for hoppscotch echo with custom client name: asyncapi.yaml 1`] = ` +"asyncapi: 3.0.0 +info: + title: Hoppscotch Echo WebSocket Client + version: 1.0.0 + description: > + Undestand how to use Hoppscotch Echo WebSocket as a client. + Hoppscotch Echo WebSocket server echoes back any messages sent to it. You can use this to test WebSocket connections and message flows. + +servers: + echoServer: + $ref: './commons/servers.yml#/servers/echoServer' + +channels: + echo: + $ref: './commons/channels.yml#/channels/echo' + +operations: + sendEchoMessage: + action: send + channel: + $ref: '#/channels/echo' + summary: Send a message to the echo server. + messages: + - $ref: '#/channels/echo/messages/echo' + + handleTimeStampMessage: + action: receive + channel: + $ref: '#/channels/echo' + summary: Receive the timestamp message sent from server every second. + messages: + - $ref: '#/channels/echo/messages/timestamp' " `; @@ -6140,6 +6176,9 @@ exports[`WebSocket Clients Integration Tests JavaScript Client Common Integratio ////////////////////////////////////////////////// const WebSocket = require('ws'); +const { compileSchemasByOperationId, validateMessage } = require('@asyncapi/keeper'); +const path = require('path'); +const asyncapiFilepath = path.resolve(__dirname, './asyncapi.yaml'); class HoppscotchClient { /* @@ -6151,6 +6190,9 @@ class HoppscotchClient { this.websocket = null; this.messageHandlers = []; this.errorHandlers = []; + this.compiledSchemas = {}; + this.schemasCompiled = false; + this.sendOperationsId = [\\"sendEchoMessage\\"]; } // Method to establish a WebSocket connection @@ -6221,17 +6263,64 @@ class HoppscotchClient { if (cb) cb(message); } + /** + * Initialize and compile all schemas for operations defined. + * This should be called once after creating the client instance. + * Subsequent calls will be ignored if schemas are already compiled. + * + * @returns {Promise} + * @throws {Error} If schema compilation fails for any operation + */ + async compileOperationSchemas() { + if (this.schemasCompiled) { + return; + } + + try { + // Compile schemas for all send operations + for (const operationId of this.sendOperationsId) { + this.compiledSchemas[operationId] = await compileSchemasByOperationId(asyncapiFilepath, operationId); + } + this.schemasCompiled = true; + console.log('Schemas initialized successfully for operations:', this.sendOperationsId.join(', ')); + } catch (error) { + console.error('Error initializing schemas:', error); + } + } + /** * Sends a sendEchoMessage message over the WebSocket connection. * * @param {Object} message - The message payload to send. Should match the schema defined in the AsyncAPI document. - * @param {WebSocket} [socket] - The WebSocket connection to use. If not provided, the client's own connection will be used. + * @param {WebSocket} socket - The WebSocket connection to use. + * @param {Array} schemas - Array of compiled schema validator functions for this operation. * @throws {TypeError} If message cannot be stringified to JSON * @throws {Error} If WebSocket connection is not in OPEN state + * @throws {Error} If message validation fails against all schemas */ - static sendEchoMessage(message, socket) { + static sendEchoMessage(message, socket, schemas) { try { - socket.send(JSON.stringify(message)); + if (!schemas || schemas.length === 0) { + socket.send(JSON.stringify(message)); + return { isValid: true }; + } + const allValidationErrors = []; + let isValid = false; + for(const compiledSchema of schemas){ + const validationResult = validateMessage(compiledSchema, message); + if (validationResult.isValid) { + isValid = true; + socket.send(JSON.stringify(message)); + break; + } else { + if (validationResult.validationErrors) { + allValidationErrors.push(...validationResult.validationErrors); + } + } + if (!isValid) { + console.error('Validation errors:', JSON.stringify(allValidationErrors, null, 2)); + } + } } catch (error) { console.error('Error sending sendEchoMessage message:', error); } @@ -6239,14 +6328,20 @@ class HoppscotchClient { /** * Instance method version of sendEchoMessage that uses the client's own WebSocket connection. + * Automatically compiles schemas if not already compiled. + * * @param {Object} message - The message payload to send * @throws {Error} If WebSocket connection is not established + * @throws {Error} If schema compilation fails + * @throws {Error} If message validation fails against all schemas */ - sendEchoMessage(message){ + async sendEchoMessage(message){ if(!this.websocket){ throw new Error('WebSocket connection not established. Call connect() first.'); } - HoppscotchClient.sendEchoMessage(message, this.websocket); + await this.compileOperationSchemas(); + const schemas = this.compiledSchemas['sendEchoMessage']; + HoppscotchClient.sendEchoMessage(message, this.websocket, schemas); } // Method to close the WebSocket connection @@ -6395,6 +6490,42 @@ main(); +" +`; + +exports[`WebSocket Clients Integration Tests JavaScript Client Common Integration tests for JavaScript client generation generate simple client for hoppscotch echo: asyncapi.yaml 1`] = ` +"asyncapi: 3.0.0 +info: + title: Hoppscotch Echo WebSocket Client + version: 1.0.0 + description: > + Undestand how to use Hoppscotch Echo WebSocket as a client. + Hoppscotch Echo WebSocket server echoes back any messages sent to it. You can use this to test WebSocket connections and message flows. + +servers: + echoServer: + $ref: './commons/servers.yml#/servers/echoServer' + +channels: + echo: + $ref: './commons/channels.yml#/channels/echo' + +operations: + sendEchoMessage: + action: send + channel: + $ref: '#/channels/echo' + summary: Send a message to the echo server. + messages: + - $ref: '#/channels/echo/messages/echo' + + handleTimeStampMessage: + action: receive + channel: + $ref: '#/channels/echo' + summary: Receive the timestamp message sent from server every second. + messages: + - $ref: '#/channels/echo/messages/timestamp' " `; @@ -6408,6 +6539,9 @@ exports[`WebSocket Clients Integration Tests JavaScript Client Common Integratio ////////////////////////////////////////////////// const WebSocket = require('ws'); +const { compileSchemasByOperationId, validateMessage } = require('@asyncapi/keeper'); +const path = require('path'); +const asyncapiFilepath = path.resolve(__dirname, './asyncapi.yaml'); class HoppscotchEchoWebSocketClient { /* @@ -6419,6 +6553,9 @@ class HoppscotchEchoWebSocketClient { this.websocket = null; this.messageHandlers = []; this.errorHandlers = []; + this.compiledSchemas = {}; + this.schemasCompiled = false; + this.sendOperationsId = [\\"sendEchoMessage\\"]; } // Method to establish a WebSocket connection @@ -6489,17 +6626,64 @@ class HoppscotchEchoWebSocketClient { if (cb) cb(message); } + /** + * Initialize and compile all schemas for operations defined. + * This should be called once after creating the client instance. + * Subsequent calls will be ignored if schemas are already compiled. + * + * @returns {Promise} + * @throws {Error} If schema compilation fails for any operation + */ + async compileOperationSchemas() { + if (this.schemasCompiled) { + return; + } + + try { + // Compile schemas for all send operations + for (const operationId of this.sendOperationsId) { + this.compiledSchemas[operationId] = await compileSchemasByOperationId(asyncapiFilepath, operationId); + } + this.schemasCompiled = true; + console.log('Schemas initialized successfully for operations:', this.sendOperationsId.join(', ')); + } catch (error) { + console.error('Error initializing schemas:', error); + } + } + /** * Sends a sendEchoMessage message over the WebSocket connection. * * @param {Object} message - The message payload to send. Should match the schema defined in the AsyncAPI document. - * @param {WebSocket} [socket] - The WebSocket connection to use. If not provided, the client's own connection will be used. + * @param {WebSocket} socket - The WebSocket connection to use. + * @param {Array} schemas - Array of compiled schema validator functions for this operation. * @throws {TypeError} If message cannot be stringified to JSON * @throws {Error} If WebSocket connection is not in OPEN state + * @throws {Error} If message validation fails against all schemas */ - static sendEchoMessage(message, socket) { + static sendEchoMessage(message, socket, schemas) { try { - socket.send(JSON.stringify(message)); + if (!schemas || schemas.length === 0) { + socket.send(JSON.stringify(message)); + return { isValid: true }; + } + const allValidationErrors = []; + let isValid = false; + for(const compiledSchema of schemas){ + const validationResult = validateMessage(compiledSchema, message); + if (validationResult.isValid) { + isValid = true; + socket.send(JSON.stringify(message)); + break; + } else { + if (validationResult.validationErrors) { + allValidationErrors.push(...validationResult.validationErrors); + } + } + if (!isValid) { + console.error('Validation errors:', JSON.stringify(allValidationErrors, null, 2)); + } + } } catch (error) { console.error('Error sending sendEchoMessage message:', error); } @@ -6507,14 +6691,20 @@ class HoppscotchEchoWebSocketClient { /** * Instance method version of sendEchoMessage that uses the client's own WebSocket connection. + * Automatically compiles schemas if not already compiled. + * * @param {Object} message - The message payload to send * @throws {Error} If WebSocket connection is not established + * @throws {Error} If schema compilation fails + * @throws {Error} If message validation fails against all schemas */ - sendEchoMessage(message){ + async sendEchoMessage(message){ if(!this.websocket){ throw new Error('WebSocket connection not established. Call connect() first.'); } - HoppscotchEchoWebSocketClient.sendEchoMessage(message, this.websocket); + await this.compileOperationSchemas(); + const schemas = this.compiledSchemas['sendEchoMessage']; + HoppscotchEchoWebSocketClient.sendEchoMessage(message, this.websocket, schemas); } // Method to close the WebSocket connection @@ -6641,6 +6831,54 @@ main(); " `; +exports[`WebSocket Clients Integration Tests JavaScript Client Common Integration tests for JavaScript client generation generate simple client for postman echo: asyncapi.yaml 1`] = ` +"asyncapi: 3.0.0 + +info: + title: Postman Echo WebSocket Client + version: 1.0.0 + description: Understand how to use the Postman Echo WebSocket as a client. + The Postman Echo WebSocket server echoes back any messages sent to it. + +servers: + echoServer: + host: ws.postman-echo.com + pathname: /raw + protocol: wss + +channels: + echo: + description: The main channel where messages are sent and echoed back by the Postman Echo WebSocket server. + address: / + messages: + echo: + $ref: '#/components/messages/echo' + +operations: + sendEchoMessage: + summary: Send a message to the Postman Echo server. + action: send + channel: + $ref: '#/channels/echo' + messages: + - $ref: '#/channels/echo/messages/echo' + reply: + channel: + $ref: '#/channels/echo' + +components: + messages: + echo: + summary: A message exchanged with the echo server. + payload: {} + examples: + - name: string + payload: test + - name: object + payload: + test: test text" +`; + exports[`WebSocket Clients Integration Tests JavaScript Client Common Integration tests for JavaScript client generation generate simple client for postman echo: client.js 1`] = ` "////////////////////////////////////////////////// // @@ -6652,6 +6890,9 @@ exports[`WebSocket Clients Integration Tests JavaScript Client Common Integratio ////////////////////////////////////////////////// const WebSocket = require('ws'); +const { compileSchemasByOperationId, validateMessage } = require('@asyncapi/keeper'); +const path = require('path'); +const asyncapiFilepath = path.resolve(__dirname, './asyncapi.yaml'); class PostmanEchoWebSocketClientClient { /* @@ -6663,6 +6904,9 @@ class PostmanEchoWebSocketClientClient { this.websocket = null; this.messageHandlers = []; this.errorHandlers = []; + this.compiledSchemas = {}; + this.schemasCompiled = false; + this.sendOperationsId = [\\"sendEchoMessage\\"]; } // Method to establish a WebSocket connection @@ -6733,17 +6977,64 @@ class PostmanEchoWebSocketClientClient { if (cb) cb(message); } + /** + * Initialize and compile all schemas for operations defined. + * This should be called once after creating the client instance. + * Subsequent calls will be ignored if schemas are already compiled. + * + * @returns {Promise} + * @throws {Error} If schema compilation fails for any operation + */ + async compileOperationSchemas() { + if (this.schemasCompiled) { + return; + } + + try { + // Compile schemas for all send operations + for (const operationId of this.sendOperationsId) { + this.compiledSchemas[operationId] = await compileSchemasByOperationId(asyncapiFilepath, operationId); + } + this.schemasCompiled = true; + console.log('Schemas initialized successfully for operations:', this.sendOperationsId.join(', ')); + } catch (error) { + console.error('Error initializing schemas:', error); + } + } + /** * Sends a sendEchoMessage message over the WebSocket connection. * * @param {Object} message - The message payload to send. Should match the schema defined in the AsyncAPI document. - * @param {WebSocket} [socket] - The WebSocket connection to use. If not provided, the client's own connection will be used. + * @param {WebSocket} socket - The WebSocket connection to use. + * @param {Array} schemas - Array of compiled schema validator functions for this operation. * @throws {TypeError} If message cannot be stringified to JSON * @throws {Error} If WebSocket connection is not in OPEN state + * @throws {Error} If message validation fails against all schemas */ - static sendEchoMessage(message, socket) { + static sendEchoMessage(message, socket, schemas) { try { - socket.send(JSON.stringify(message)); + if (!schemas || schemas.length === 0) { + socket.send(JSON.stringify(message)); + return { isValid: true }; + } + const allValidationErrors = []; + let isValid = false; + for(const compiledSchema of schemas){ + const validationResult = validateMessage(compiledSchema, message); + if (validationResult.isValid) { + isValid = true; + socket.send(JSON.stringify(message)); + break; + } else { + if (validationResult.validationErrors) { + allValidationErrors.push(...validationResult.validationErrors); + } + } + if (!isValid) { + console.error('Validation errors:', JSON.stringify(allValidationErrors, null, 2)); + } + } } catch (error) { console.error('Error sending sendEchoMessage message:', error); } @@ -6751,14 +7042,20 @@ class PostmanEchoWebSocketClientClient { /** * Instance method version of sendEchoMessage that uses the client's own WebSocket connection. + * Automatically compiles schemas if not already compiled. + * * @param {Object} message - The message payload to send * @throws {Error} If WebSocket connection is not established + * @throws {Error} If schema compilation fails + * @throws {Error} If message validation fails against all schemas */ - sendEchoMessage(message){ + async sendEchoMessage(message){ if(!this.websocket){ throw new Error('WebSocket connection not established. Call connect() first.'); } - PostmanEchoWebSocketClientClient.sendEchoMessage(message, this.websocket); + await this.compileOperationSchemas(); + const schemas = this.compiledSchemas['sendEchoMessage']; + PostmanEchoWebSocketClientClient.sendEchoMessage(message, this.websocket, schemas); } // Method to close the WebSocket connection diff --git a/packages/templates/clients/websocket/test/integration-test/jest.config.js b/packages/templates/clients/websocket/test/integration-test/jest.config.js new file mode 100644 index 0000000000..9225f6e643 --- /dev/null +++ b/packages/templates/clients/websocket/test/integration-test/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/test/integration-test/package.json b/packages/templates/clients/websocket/test/integration-test/package.json index 7dfca4d253..ded3674561 100644 --- a/packages/templates/clients/websocket/test/integration-test/package.json +++ b/packages/templates/clients/websocket/test/integration-test/package.json @@ -24,19 +24,5 @@ "eslint-plugin-jest": "^23.8.2", "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../../node_modules/nimma/dist/cjs/$1" - } } } diff --git a/packages/templates/clients/websocket/test/javascript/jest.config.js b/packages/templates/clients/websocket/test/javascript/jest.config.js new file mode 100644 index 0000000000..9225f6e643 --- /dev/null +++ b/packages/templates/clients/websocket/test/javascript/jest.config.js @@ -0,0 +1,14 @@ +const baseConfig = require('../../../../../../jest.config.base'); + +module.exports = { + ...baseConfig(__dirname), + moduleFileExtensions: [ + 'js', + 'json', + 'jsx' + ], + transform: { + '^.+\\.jsx?$': 'babel-jest' + }, +}; + diff --git a/packages/templates/clients/websocket/test/javascript/package.json b/packages/templates/clients/websocket/test/javascript/package.json index aed2c9b99e..5762b0c880 100644 --- a/packages/templates/clients/websocket/test/javascript/package.json +++ b/packages/templates/clients/websocket/test/javascript/package.json @@ -18,19 +18,5 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0", "jest-esm-transformer": "^1.0.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "jsx" - ], - "transform": { - "^.+\\.jsx?$": "babel-jest" - }, - "moduleNameMapper": { - "^nimma/legacy$": "/../../../../../node_modules/nimma/dist/legacy/cjs/index.js", - "^nimma/(.*)": "/../../../../../node_modules/nimma/dist/cjs/$1" - } } } From ce7ceda557f44e6223c0b24519b7a7fa8b2deade Mon Sep 17 00:00:00 2001 From: Kartikay Singh pundir <130781246+Kartikayy007@users.noreply.github.com> Date: Tue, 2 Dec 2025 07:39:08 +0000 Subject: [PATCH 04/14] fix: add generator onboarding webinar to contributing guides (#1753) Co-authored-by: kartikayy007 Co-authored-by: Adi Boghawala Co-authored-by: Lukasz Gornicki --- CONTRIBUTING.md | 24 +++++++++++++----------- Development.md | 21 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afce32a01f..d0220be0b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,14 +30,16 @@ We use [All Contributors](https://allcontributors.org/docs/en/specification) spe ## Guidelines for New Contributors -1. Read the [generator docs](https://www.asyncapi.com/docs/tools/generator) to get an overview of what the project is about. -2. While reading through the docs one might identify issues (e.g., broken links, text formatting issues, etc.). -3. Some concepts in the documentation might not be presented clearly, so contributors can suggest improvements (e.g., adding more examples or code snippets to make the documentation easy to understand). -4. Familiarize yourself with the project's structure by reviewing the source code and understanding the roles of different files and components. -5. Testing is one of the areas where new contributors can contribute, as running tests reveals uncovered lines in a particular file (e.g., a **test coverage report**). -6. You can then try to add tests for those uncovered lines (e.g., a function within a file might not be covered). For reference, you can check out this [PR](https://github.com/asyncapi/generator/pull/1379). -7. Instead of creating entirely new tests, consider enhancing existing ones by adding more edge cases to include additional test scenarios. -8. We encourage contributors to help improve code quality by reviewing reported issues on [SonarCloud](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=asyncapi_generator) and suggesting improvements, identifying false reported issues that may need to be removed, and proposing fixes for valid issues. +1. **Watch the Onboarding Webinar** - Before diving into code, watch our [Generator onboarding webinar](https://www.youtube.com/watch?v=Mkd7FgKOMNE) to understand the architecture and design decisions. +2. Read the [generator docs](https://www.asyncapi.com/docs/tools/generator) to get an overview of what the project is about. +3. While reading through the docs one might identify issues (e.g., broken links, text formatting issues, etc.). +4. Some concepts in the documentation might not be presented clearly, so contributors can suggest improvements (e.g., adding more examples or code snippets to make the documentation easy to understand). +5. Familiarize yourself with the project's structure by reviewing the source code and understanding the roles of different files and components. +6. Testing is one of the areas where new contributors can contribute, as running tests reveals uncovered lines in a particular file (e.g., a **test coverage report**). +7. You can then try to add tests for those uncovered lines (e.g., a function within a file might not be covered). For reference, you can check out this [PR](https://github.com/asyncapi/generator/pull/1379). +8. Instead of creating entirely new tests, consider enhancing existing ones by adding more edge cases to include additional test scenarios. +9. We encourage contributors to help improve code quality by reviewing reported issues on [SonarCloud](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=asyncapi_generator) and suggesting improvements, identifying false reported issues that may need to be removed, and proposing fixes for valid issues. +10. See the [Development Guide](Development.md#before-you-begin---new-contributor-onboarding) for more details on what you'll learn during onboarding. ## Few Tips for Effective Contributions @@ -46,10 +48,10 @@ We use [All Contributors](https://allcontributors.org/docs/en/specification) spe 3. **PATIENCE** is crucial. Focus on creating meaningful contributions rather than rushing to submit a PR that adds little or no value to the project. 4. Adding entirely new features might be challenging because most features are already well-established. You may need to spend more time understanding the repository to identify areas for improvement. 5. You should research on Google regarding the @asyncapi/generator and related repositories (e.g., @asyncapi/parser or any of the template repositories) to identify potential improvements. AI can be a helpful assistant, but always double-check the information it provides with Google or StackOverflow. -6. Follow **Issue First, PR Later** - Always check if there’s an existing issue before creating a new one, and raise your PR once the issue is confirmed/approved by any of the maintainers. +6. Follow **Issue First, PR Later** - Always check if there's an existing issue before creating a new one, and raise your PR once the issue is confirmed/approved by any of the maintainers. 7. Keep **PRs small and focused** – A small PR with a well-defined scope is easier to review and merge. Avoid bundling multiple changes into one PR. 8. Collaborate with Other Contributors – If someone else has already raised an issue and you are interested in contributing to it, please communicate with them and collaborate instead of raising a separate PR independently. Working together leads to better contributions and avoids duplication of efforts. Open source is driven by **collaboration, not competition**. -9. **Respond to Maintainer Reviews Promptly** - If a maintainer requests changes or provides feedback on your PR, please try to address them within **7 days**. If you’re unable to respond by then, the PR may be closed. Contributors are always welcome to open a new PR once they’re ready to continue. +9. **Respond to Maintainer Reviews Promptly** - If a maintainer requests changes or provides feedback on your PR, please try to address them within **7 days**. If you're unable to respond by then, the PR may be closed. Contributors are always welcome to open a new PR once they're ready to continue. ## Summary of the Contribution Flow @@ -207,4 +209,4 @@ This document was adapted from: - [Facebook’s Draft Contributor Guide](https://github.com/facebook/draft-js/blob/master/CONTRIBUTING.md) - [Modelina Champion Guidelines](https://github.com/asyncapi/modelina) -- [Website Triager/Committer Guidelines](https://github.com/asyncapi/website/blob/master/CONTRIBUTING.md#maintainers-setup) \ No newline at end of file +- [Website Triager/Committer Guidelines](https://github.com/asyncapi/website/blob/master/CONTRIBUTING.md#maintainers-setup) diff --git a/Development.md b/Development.md index eebd7e7a58..aacccf882e 100644 --- a/Development.md +++ b/Development.md @@ -2,6 +2,25 @@ This guide will help you set up the `generator` locally, run tests, and use Docker for isolated testing. +## Before You Begin - New Contributor Onboarding + +New to AsyncAPI Generator? We strongly recommend watching our comprehensive onboarding webinar first: + +**Watch: [One Tool, One Flow: AsyncAPI's New Take on Code/Docs/Config Generation](https://www.youtube.com/watch?v=Mkd7FgKOMNE)** + +### What you'll learn: + +- What AsyncAPI is and the challenges it solves +- The origins and evolution of the Generator (legacy vs. future architecture) +- Understanding event-driven architectures and protocol complexity +- How the Generator works: templates, render engines, and the generation process +- The shift from Nunjucks to React render engine +- Component-based template development for better reusability +- Baked-in templates and the monorepo structure +- Live demonstrations of code generation from AsyncAPI documents + +This webinar provides essential context about the Generator's architecture, design decisions, and development workflow. Watching it will make the rest of this development guide much clearer and help you contribute more effectively. + ## Getting started 1. Fork & Clone the repository: @@ -181,4 +200,4 @@ If you encounter any issues during development or testing, please check the foll 2. Clear the `node_modules` directory and reinstall dependencies if you encounter unexpected behavior. 3. For Docker-related issues, make sure Docker is running and you have sufficient permissions. -If problems persist, please open an issue on the GitHub repository. +If problems persist, please open an issue on the GitHub repository. \ No newline at end of file From 1131dcae0d1ea469a3ad4c5ad9550b2f105dac04 Mon Sep 17 00:00:00 2001 From: Chan Date: Tue, 2 Dec 2025 08:45:59 +0100 Subject: [PATCH 05/14] chore(release): release and bump versions of packages (#1775) --- .changeset/release-keeper.md | 5 ----- apps/keeper/CHANGELOG.md | 7 +++++++ apps/keeper/package.json | 4 ++-- .../templates/clients/websocket/javascript/package.json | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) delete mode 100644 .changeset/release-keeper.md create mode 100644 apps/keeper/CHANGELOG.md diff --git a/.changeset/release-keeper.md b/.changeset/release-keeper.md deleted file mode 100644 index e581c9a6f0..0000000000 --- a/.changeset/release-keeper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@asyncapi/keeper": minor ---- - -Initial release of `@asyncapi/keeper` to make it available for JS templates in `@asyncapi/generator`. The generator release will contain a new JS client version with enabled message validation. diff --git a/apps/keeper/CHANGELOG.md b/apps/keeper/CHANGELOG.md new file mode 100644 index 0000000000..7f89015574 --- /dev/null +++ b/apps/keeper/CHANGELOG.md @@ -0,0 +1,7 @@ +# @asyncapi/keeper + +## 0.2.0 + +### Minor Changes + +- 2bd801e: Initial release of `@asyncapi/keeper` to make it available for JS templates in `@asyncapi/generator`. The generator release will contain a new JS client version with enabled message validation. diff --git a/apps/keeper/package.json b/apps/keeper/package.json index 7a35ebf4f4..d1b7a8056e 100644 --- a/apps/keeper/package.json +++ b/apps/keeper/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/keeper", - "version": "0.1.0", + "version": "0.2.0", "description": "AsyncAPI message payload validation library that validates messages against JSON Schema (Draft-07)", "scripts": { "build": "babel src --out-dir lib", @@ -21,7 +21,7 @@ "main": "lib/index.js", "author": "Adi Boghawala ", "license": "Apache-2.0", - "publishConfig": { + "publishConfig": { "access": "public" }, "dependencies": { diff --git a/packages/templates/clients/websocket/javascript/package.json b/packages/templates/clients/websocket/javascript/package.json index ae94431b04..f19013848a 100644 --- a/packages/templates/clients/websocket/javascript/package.json +++ b/packages/templates/clients/websocket/javascript/package.json @@ -14,7 +14,7 @@ "@asyncapi/generator-helpers": "0.2.0", "@asyncapi/generator-react-sdk": "*", "@asyncapi/generator-components": "0.3.1", - "@asyncapi/keeper": "0.1.0" + "@asyncapi/keeper": "0.2.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", From c0744b630a85672d43f3c4510b4b9de6436c5f50 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Wed, 3 Dec 2025 17:53:54 +0100 Subject: [PATCH 06/14] feat: update release worflow and trigger release (#1776) Co-authored-by: Lukasz Gornicki --- .changeset/yellow-foxes-shout.md | 6 ++++++ .github/workflows/release-with-changesets.yml | 1 + 2 files changed, 7 insertions(+) create mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md new file mode 100644 index 0000000000..bb01829d7f --- /dev/null +++ b/.changeset/yellow-foxes-shout.md @@ -0,0 +1,6 @@ +--- +"@asyncapi/generator": minor +"@asyncapi/keeper": minor +--- + +Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/.github/workflows/release-with-changesets.yml b/.github/workflows/release-with-changesets.yml index 48bcb66100..bf39adceed 100644 --- a/.github/workflows/release-with-changesets.yml +++ b/.github/workflows/release-with-changesets.yml @@ -51,6 +51,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: "${{ steps.lockversion.outputs.version }}" + registry-url: "https://registry.npmjs.org" - if: steps.lockversion.outputs.version == '18' && matrix.os == 'windows-latest' name: Install npm cli 8 shell: bash From e1a13c82977de81ba59274c6337b561486cfbad0 Mon Sep 17 00:00:00 2001 From: Chan Date: Wed, 3 Dec 2025 18:00:43 +0100 Subject: [PATCH 07/14] chore(release): release and bump versions of packages (#1777) --- .changeset/yellow-foxes-shout.md | 6 ------ apps/generator/CHANGELOG.md | 6 ++++++ apps/generator/package.json | 2 +- apps/keeper/CHANGELOG.md | 6 ++++++ apps/keeper/package.json | 2 +- .../templates/clients/websocket/javascript/package.json | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) delete mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md deleted file mode 100644 index bb01829d7f..0000000000 --- a/.changeset/yellow-foxes-shout.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@asyncapi/generator": minor -"@asyncapi/keeper": minor ---- - -Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/apps/generator/CHANGELOG.md b/apps/generator/CHANGELOG.md index d1e8e000bc..90de0d917b 100644 --- a/apps/generator/CHANGELOG.md +++ b/apps/generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/generator +## 2.9.0 + +### Minor Changes + +- 8168bcd: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 2.8.4 ### Patch Changes diff --git a/apps/generator/package.json b/apps/generator/package.json index 75aff7d228..76fb83917c 100644 --- a/apps/generator/package.json +++ b/apps/generator/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/generator", - "version": "2.8.4", + "version": "2.9.0", "description": "The AsyncAPI generator. It can generate documentation, code, anything!", "main": "./lib/generator.js", "bin": { diff --git a/apps/keeper/CHANGELOG.md b/apps/keeper/CHANGELOG.md index 7f89015574..552c8cbed3 100644 --- a/apps/keeper/CHANGELOG.md +++ b/apps/keeper/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/keeper +## 0.3.0 + +### Minor Changes + +- 8168bcd: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 0.2.0 ### Minor Changes diff --git a/apps/keeper/package.json b/apps/keeper/package.json index d1b7a8056e..991334fb86 100644 --- a/apps/keeper/package.json +++ b/apps/keeper/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/keeper", - "version": "0.2.0", + "version": "0.3.0", "description": "AsyncAPI message payload validation library that validates messages against JSON Schema (Draft-07)", "scripts": { "build": "babel src --out-dir lib", diff --git a/packages/templates/clients/websocket/javascript/package.json b/packages/templates/clients/websocket/javascript/package.json index f19013848a..538939a676 100644 --- a/packages/templates/clients/websocket/javascript/package.json +++ b/packages/templates/clients/websocket/javascript/package.json @@ -14,7 +14,7 @@ "@asyncapi/generator-helpers": "0.2.0", "@asyncapi/generator-react-sdk": "*", "@asyncapi/generator-components": "0.3.1", - "@asyncapi/keeper": "0.2.0" + "@asyncapi/keeper": "0.3.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", From 43f50f36c961d8a7d2b111a7f124ca5008d32999 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Wed, 3 Dec 2025 18:51:59 +0100 Subject: [PATCH 08/14] fix: add proper token requirements to release (#1778) Co-authored-by: Lukasz Gornicki --- .changeset/yellow-foxes-shout.md | 6 ++++++ .github/workflows/release-with-changesets.yml | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md new file mode 100644 index 0000000000..bb01829d7f --- /dev/null +++ b/.changeset/yellow-foxes-shout.md @@ -0,0 +1,6 @@ +--- +"@asyncapi/generator": minor +"@asyncapi/keeper": minor +--- + +Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/.github/workflows/release-with-changesets.yml b/.github/workflows/release-with-changesets.yml index bf39adceed..5b4ba78172 100644 --- a/.github/workflows/release-with-changesets.yml +++ b/.github/workflows/release-with-changesets.yml @@ -78,6 +78,10 @@ jobs: needs: [test-nodejs] name: Publish to any of NPM, GitHub, or Docker Hub runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + pull-requests: write steps: - name: Set git to use LF # To once and for all finish the never-ending fight between Unix and Windows run: | From 1a70eb971d7ffa7451a320bacccbae3a4e511619 Mon Sep 17 00:00:00 2001 From: Chan Date: Wed, 3 Dec 2025 18:59:56 +0100 Subject: [PATCH 09/14] chore(release): release and bump versions of packages (#1779) --- .changeset/yellow-foxes-shout.md | 6 ------ apps/generator/CHANGELOG.md | 6 ++++++ apps/generator/package.json | 2 +- apps/keeper/CHANGELOG.md | 6 ++++++ apps/keeper/package.json | 2 +- .../templates/clients/websocket/javascript/package.json | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) delete mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md deleted file mode 100644 index bb01829d7f..0000000000 --- a/.changeset/yellow-foxes-shout.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@asyncapi/generator": minor -"@asyncapi/keeper": minor ---- - -Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/apps/generator/CHANGELOG.md b/apps/generator/CHANGELOG.md index 90de0d917b..dcf579df11 100644 --- a/apps/generator/CHANGELOG.md +++ b/apps/generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/generator +## 2.10.0 + +### Minor Changes + +- aee45ba: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 2.9.0 ### Minor Changes diff --git a/apps/generator/package.json b/apps/generator/package.json index 76fb83917c..13587babc6 100644 --- a/apps/generator/package.json +++ b/apps/generator/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/generator", - "version": "2.9.0", + "version": "2.10.0", "description": "The AsyncAPI generator. It can generate documentation, code, anything!", "main": "./lib/generator.js", "bin": { diff --git a/apps/keeper/CHANGELOG.md b/apps/keeper/CHANGELOG.md index 552c8cbed3..ab7c8aff45 100644 --- a/apps/keeper/CHANGELOG.md +++ b/apps/keeper/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/keeper +## 0.4.0 + +### Minor Changes + +- aee45ba: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 0.3.0 ### Minor Changes diff --git a/apps/keeper/package.json b/apps/keeper/package.json index 991334fb86..fd71fe4b64 100644 --- a/apps/keeper/package.json +++ b/apps/keeper/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/keeper", - "version": "0.3.0", + "version": "0.4.0", "description": "AsyncAPI message payload validation library that validates messages against JSON Schema (Draft-07)", "scripts": { "build": "babel src --out-dir lib", diff --git a/packages/templates/clients/websocket/javascript/package.json b/packages/templates/clients/websocket/javascript/package.json index 538939a676..7a9000f507 100644 --- a/packages/templates/clients/websocket/javascript/package.json +++ b/packages/templates/clients/websocket/javascript/package.json @@ -14,7 +14,7 @@ "@asyncapi/generator-helpers": "0.2.0", "@asyncapi/generator-react-sdk": "*", "@asyncapi/generator-components": "0.3.1", - "@asyncapi/keeper": "0.3.0" + "@asyncapi/keeper": "0.4.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", From 9be72ce57c19756b3200de2e6c6722a5b4dcb923 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Wed, 3 Dec 2025 20:51:12 +0100 Subject: [PATCH 10/14] fix: make sure to use latest npm for release pipeline (#1780) Co-authored-by: Lukasz Gornicki --- .changeset/yellow-foxes-shout.md | 6 ++++++ .github/workflows/release-with-changesets.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md new file mode 100644 index 0000000000..bb01829d7f --- /dev/null +++ b/.changeset/yellow-foxes-shout.md @@ -0,0 +1,6 @@ +--- +"@asyncapi/generator": minor +"@asyncapi/keeper": minor +--- + +Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/.github/workflows/release-with-changesets.yml b/.github/workflows/release-with-changesets.yml index 5b4ba78172..b16706c3c4 100644 --- a/.github/workflows/release-with-changesets.yml +++ b/.github/workflows/release-with-changesets.yml @@ -95,7 +95,7 @@ jobs: shell: bash - if: steps.packagejson.outputs.exists == 'true' name: Check package-lock version - uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master + uses: derberg/.github-asyncapi/.github/actions/get-node-version-from-package-lock@fixnodereelase id: lockversion - if: steps.packagejson.outputs.exists == 'true' name: Setup Node.js From 091ec693ca728e19b9dbe8258845dc5a7fd31610 Mon Sep 17 00:00:00 2001 From: Lukasz Gornicki Date: Thu, 4 Dec 2025 06:03:36 +0100 Subject: [PATCH 11/14] fix: add to release option to read NODE_VERSION variable (#1781) Co-authored-by: Lukasz Gornicki --- .github/workflows/release-with-changesets.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-with-changesets.yml b/.github/workflows/release-with-changesets.yml index b16706c3c4..61b446b3e3 100644 --- a/.github/workflows/release-with-changesets.yml +++ b/.github/workflows/release-with-changesets.yml @@ -96,6 +96,8 @@ jobs: - if: steps.packagejson.outputs.exists == 'true' name: Check package-lock version uses: derberg/.github-asyncapi/.github/actions/get-node-version-from-package-lock@fixnodereelase + with: + node-version: ${{ vars.NODE_VERSION }} id: lockversion - if: steps.packagejson.outputs.exists == 'true' name: Setup Node.js From 2141594bb8e9d1130e7f073b8e9e6f04fcd2dc65 Mon Sep 17 00:00:00 2001 From: Chan Date: Thu, 4 Dec 2025 06:10:26 +0100 Subject: [PATCH 12/14] chore(release): release and bump versions of packages (#1782) --- .changeset/yellow-foxes-shout.md | 6 ------ apps/generator/CHANGELOG.md | 6 ++++++ apps/generator/package.json | 2 +- apps/keeper/CHANGELOG.md | 6 ++++++ apps/keeper/package.json | 2 +- .../templates/clients/websocket/javascript/package.json | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) delete mode 100644 .changeset/yellow-foxes-shout.md diff --git a/.changeset/yellow-foxes-shout.md b/.changeset/yellow-foxes-shout.md deleted file mode 100644 index bb01829d7f..0000000000 --- a/.changeset/yellow-foxes-shout.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@asyncapi/generator": minor -"@asyncapi/keeper": minor ---- - -Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. diff --git a/apps/generator/CHANGELOG.md b/apps/generator/CHANGELOG.md index dcf579df11..cf502c1abd 100644 --- a/apps/generator/CHANGELOG.md +++ b/apps/generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/generator +## 2.11.0 + +### Minor Changes + +- ced1404: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 2.10.0 ### Minor Changes diff --git a/apps/generator/package.json b/apps/generator/package.json index 13587babc6..ffa198a896 100644 --- a/apps/generator/package.json +++ b/apps/generator/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/generator", - "version": "2.10.0", + "version": "2.11.0", "description": "The AsyncAPI generator. It can generate documentation, code, anything!", "main": "./lib/generator.js", "bin": { diff --git a/apps/keeper/CHANGELOG.md b/apps/keeper/CHANGELOG.md index ab7c8aff45..2fceef9496 100644 --- a/apps/keeper/CHANGELOG.md +++ b/apps/keeper/CHANGELOG.md @@ -1,5 +1,11 @@ # @asyncapi/keeper +## 0.5.0 + +### Minor Changes + +- ced1404: Pushing of release https://github.com/asyncapi/generator/pull/1747 that failed due to pipeline issues. + ## 0.4.0 ### Minor Changes diff --git a/apps/keeper/package.json b/apps/keeper/package.json index fd71fe4b64..6a0630f9cd 100644 --- a/apps/keeper/package.json +++ b/apps/keeper/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/keeper", - "version": "0.4.0", + "version": "0.5.0", "description": "AsyncAPI message payload validation library that validates messages against JSON Schema (Draft-07)", "scripts": { "build": "babel src --out-dir lib", diff --git a/packages/templates/clients/websocket/javascript/package.json b/packages/templates/clients/websocket/javascript/package.json index 7a9000f507..6c89484565 100644 --- a/packages/templates/clients/websocket/javascript/package.json +++ b/packages/templates/clients/websocket/javascript/package.json @@ -14,7 +14,7 @@ "@asyncapi/generator-helpers": "0.2.0", "@asyncapi/generator-react-sdk": "*", "@asyncapi/generator-components": "0.3.1", - "@asyncapi/keeper": "0.4.0" + "@asyncapi/keeper": "0.5.0" }, "devDependencies": { "@asyncapi/parser": "^3.0.14", From f96cf7e19138fa1507a4ca98c59c97ae64285abf Mon Sep 17 00:00:00 2001 From: Chan Date: Thu, 4 Dec 2025 17:50:57 +0100 Subject: [PATCH 13/14] ci: update of files from global .github repo (#1783) --- .github/workflows/update-docs-on-docs-commits.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-docs-on-docs-commits.yml b/.github/workflows/update-docs-on-docs-commits.yml index 1c1623b2b9..b6186d4e89 100644 --- a/.github/workflows/update-docs-on-docs-commits.yml +++ b/.github/workflows/update-docs-on-docs-commits.yml @@ -22,8 +22,11 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v4 - - name: Check package-lock version - uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master + - name: Determine what node version to use + # This workflow is from our own org repo and safe to reference by 'master'. + uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master # //NOSONAR + with: + node-version: ${{ vars.NODE_VERSION }} id: lockversion - name: Use Node.js uses: actions/setup-node@v4 From 4046fa5ee861f02dea8fda445cb0ca55196b1b06 Mon Sep 17 00:00:00 2001 From: Sagar Seth Date: Sun, 7 Dec 2025 03:10:52 +0530 Subject: [PATCH 14/14] fix(template-params): correctly detect default values including false, null, and empty string --- apps/generator/lib/templates/config/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/generator/lib/templates/config/loader.js b/apps/generator/lib/templates/config/loader.js index b523813471..b23a6599f8 100644 --- a/apps/generator/lib/templates/config/loader.js +++ b/apps/generator/lib/templates/config/loader.js @@ -46,7 +46,7 @@ async function loadTemplateConfig(templateDir, templateParams) { */ function loadDefaultValues(templateConfig, templateParams) { const parameters = templateConfig.parameters; - const defaultValues = Object.keys(parameters || {}).filter(key => parameters[key].default !== undefined); + const defaultValues = Object.keys(parameters || {}).filter(key => Object.hasOwn(parameters[key], "default")); defaultValues.filter(dv => !Object.prototype.hasOwnProperty.call(templateParams, dv)).forEach(dv => Object.defineProperty(templateParams, dv, {