diff --git a/package-lock.json b/package-lock.json index afd09ea1..cfb9d615 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,24 @@ { "name": "@serverlessworkflow/sdk", - "version": "1.0.3-alpha1", + "version": "1.0.3-alpha2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@serverlessworkflow/sdk", - "version": "1.0.3-alpha1", + "version": "1.0.3-alpha2", "license": "Apache-2.0", "dependencies": { "ajv": "^8.20.0", "ajv-formats": "^3.0.1", - "js-yaml": "^4.1.1" + "js-yaml": "^4.1.1", + "semver": "^7.8.5" }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^15.3.1", "@arethetypeswrong/cli": "^0.18.2", "@types/js-yaml": "^4.0.9", + "@types/semver": "^7.7.1", "@types/yargs": "^17.0.32", "http-server": "^14.1.1", "husky": "^9.1.7", @@ -1406,6 +1408,13 @@ "undici-types": "~7.18.0" } }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -4069,10 +4078,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", - "dev": true, + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/package.json b/package.json index e47c9566..f468ea2f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "@serverlessworkflow/sdk", - "version": "1.0.3-alpha1", + "version": "1.0.3-alpha2", "schemaVersion": "1.0.3", + "supportedDslVersions": ">=1.0.0 <=1.0.3", "description": "Typescript SDK for Serverless Workflow Specification", "type": "commonjs", "main": "./index.cjs", @@ -77,12 +78,14 @@ "dependencies": { "ajv": "^8.20.0", "ajv-formats": "^3.0.1", - "js-yaml": "^4.1.1" + "js-yaml": "^4.1.1", + "semver": "^7.8.5" }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^15.3.1", "@arethetypeswrong/cli": "^0.18.2", "@types/js-yaml": "^4.0.9", + "@types/semver": "^7.7.1", "@types/yargs": "^17.0.32", "http-server": "^14.1.1", "husky": "^9.1.7", diff --git a/scripts/prepare-dist-package.mjs b/scripts/prepare-dist-package.mjs index 12c1b2c7..1a0da42c 100644 --- a/scripts/prepare-dist-package.mjs +++ b/scripts/prepare-dist-package.mjs @@ -21,6 +21,7 @@ const distPackageJson = { name: packageJson.name, version: packageJson.version, schemaVersion: packageJson.schemaVersion, + supportedDslVersions: packageJson.supportedDslVersions, description: packageJson.description, type: packageJson.type, main: packageJson.main, diff --git a/src/lib/hooks/workflow-hooks.ts b/src/lib/hooks/workflow-hooks.ts index 7cb819ea..54cd642e 100644 --- a/src/lib/hooks/workflow-hooks.ts +++ b/src/lib/hooks/workflow-hooks.ts @@ -13,17 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import satisfies from 'semver/functions/satisfies'; import { LifecycleHooks } from '../lifecycle-hooks'; import { Specification } from '../generated/definitions'; -import { schemaVersion } from '../../../package.json'; +import { supportedDslVersions } from '../../../package.json'; export const WorkflowHooks = { preValidation(instance) { - if (instance?.document?.dsl !== schemaVersion) { + const dsl = instance?.document?.dsl; + const isSupportedDsl = typeof dsl === 'string' && satisfies(dsl, supportedDslVersions, { includePrerelease: true }); + + if (!isSupportedDsl) { throw new Error( - `'Workflow' is invalid - The DSL version of the workflow '${instance?.document?.dsl}' doesn't match the supported version of the SDK '${schemaVersion}'.`, + `'Workflow' is invalid - The DSL version of the workflow '${dsl}' does not saisfy the DSL version range supported by this SDK '${supportedDslVersions}'.`, ); } return; diff --git a/tests/validation/workflow-validation.spec.ts b/tests/validation/workflow-validation.spec.ts index b645ac3f..786da0e3 100644 --- a/tests/validation/workflow-validation.spec.ts +++ b/tests/validation/workflow-validation.spec.ts @@ -18,7 +18,7 @@ import { Classes } from '../../src/lib/generated/classes'; import { validate } from '../../src/lib/validation'; -import { schemaVersion } from '../../package.json'; +import { schemaVersion, supportedDslVersions } from '../../package.json'; describe('Workflow validation', () => { it('should be valid', () => { @@ -57,8 +57,8 @@ describe('Workflow validation', () => { expect(test).toThrow(/'Workflow' is invalid/); }); - it('should throw with incompatible DSL version', () => { - const oldVersion = '0.9'; + it('should throw with a DSL version below the minimum supported version', () => { + const oldVersion = '0.9.0'; const workflow = new Classes.Workflow({ document: { dsl: oldVersion, @@ -68,7 +68,100 @@ describe('Workflow validation', () => { }, }); expect(() => workflow.validate()).toThrow( - `The DSL version of the workflow '${oldVersion}' doesn't match the supported version of the SDK '${schemaVersion}'.`, + `The DSL version of the workflow '${oldVersion}' does not saisfy the DSL version range supported by this SDK '${supportedDslVersions}'.`, + ); + }); + + it('should throw with a DSL version newer than the current schemaVersion', () => { + const newerVersion = '3.0.0'; + const workflow = new Classes.Workflow({ + document: { + dsl: newerVersion, + name: 'test', + version: '1.0.0', + namespace: 'default', + }, + }); + expect(() => workflow.validate()).toThrow( + `The DSL version of the workflow '${newerVersion}' does not saisfy the DSL version range supported by this SDK '${supportedDslVersions}'.`, + ); + }); + + it('should be valid with an older DSL version than the current schema but still supported', () => { + const oldVersion = '1.0.0'; + const workflow = new Classes.Workflow({ + document: { + dsl: oldVersion, + name: 'test', + version: '1.0.0', + namespace: 'default', + }, + do: [ + { + step1: { + set: { + foo: 'bar', + }, + }, + }, + ], + }); + + const result = () => validate('Workflow', workflow); + expect(result).not.toThrow(Error); + }); + + it('should be valid with a pre-release version within the supported range', () => { + const preReleaseVersion = `${schemaVersion}-alpha`; + const workflow = new Classes.Workflow({ + document: { + dsl: preReleaseVersion, + name: 'test', + version: '1.0.0', + namespace: 'default', + }, + do: [ + { + step1: { + set: { + foo: 'bar', + }, + }, + }, + ], + }); + + const result = () => validate('Workflow', workflow); + expect(result).not.toThrow(Error); + }); + + it('should throw with a pre-release version below the minimum supported', () => { + const preReleaseVersion = '1.0.0-alpha'; + const workflow = new Classes.Workflow({ + document: { + dsl: preReleaseVersion, + name: 'test', + version: '1.0.0', + namespace: 'default', + }, + }); + expect(() => workflow.validate()).toThrow( + `The DSL version of the workflow '${preReleaseVersion}' does not saisfy the DSL version range supported by this SDK '${supportedDslVersions}'.`, + ); + }); + + it('should throw with a pre-release version above the supported range', () => { + const preReleaseVersion = '2.0.0-alpha'; + const workflow = new Classes.Workflow({ + document: { + dsl: preReleaseVersion, + name: 'test', + version: '1.0.0', + namespace: 'default', + }, + }); + expect(() => workflow.validate()).toThrow( + `The DSL version of the workflow '${preReleaseVersion}' does not saisfy the DSL version range supported by this SDK '${supportedDslVersions}'.`, ); }); }); diff --git a/tsdown.config.mjs b/tsdown.config.mjs index 01462a16..5baac9cf 100644 --- a/tsdown.config.mjs +++ b/tsdown.config.mjs @@ -23,7 +23,7 @@ export default defineConfig([ globalName: 'serverWorkflowSdk', dts: false, deps: { - alwaysBundle: [/^ajv(?:\/.*)?$/, /^ajv-formats$/, /^js-yaml$/], + alwaysBundle: [/^ajv(?:\/.*)?$/, /^ajv-formats$/, /^js-yaml$/, /^semver(?:\/.*)?$/], onlyBundle: false, }, }, @@ -35,7 +35,7 @@ export default defineConfig([ dts: false, minify: true, deps: { - alwaysBundle: [/^ajv(?:\/.*)?$/, /^ajv-formats$/, /^js-yaml$/], + alwaysBundle: [/^ajv(?:\/.*)?$/, /^ajv-formats$/, /^js-yaml$/, /^semver(?:\/.*)?$/], onlyBundle: false, }, outExtensions: () => ({