@lssm-tech/lib.workflow-composer 3.7.29 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @contractspec/lib.workflow-composer
1
+ # @lssm-tech/lib.workflow-composer
2
2
 
3
3
  Website: https://contractspec.io
4
4
 
@@ -8,20 +8,20 @@ Website: https://contractspec.io
8
8
 
9
9
  - **Layer**: lib.
10
10
  - **Consumers**: bundles.
11
- - Related ContractSpec packages include `@contractspec/lib.ai-providers`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
12
- - Related ContractSpec packages include `@contractspec/lib.ai-providers`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
11
+ - Related ContractSpec packages include `@lssm-tech/lib.ai-providers`, `@lssm-tech/lib.contracts-spec`, `@lssm-tech/tool.bun`, `@lssm-tech/tool.typescript`.
12
+ - Related ContractSpec packages include `@lssm-tech/lib.ai-providers`, `@lssm-tech/lib.contracts-spec`, `@lssm-tech/tool.bun`, `@lssm-tech/tool.typescript`.
13
13
 
14
14
  ## Installation
15
15
 
16
- `npm install @contractspec/lib.workflow-composer`
16
+ `npm install @lssm-tech/lib.workflow-composer`
17
17
 
18
18
  or
19
19
 
20
- `bun add @contractspec/lib.workflow-composer`
20
+ `bun add @lssm-tech/lib.workflow-composer`
21
21
 
22
22
  ## Usage
23
23
 
24
- Import the root entrypoint from `@contractspec/lib.workflow-composer`, or choose a documented subpath when you only need one part of the package surface.
24
+ Import the root entrypoint from `@lssm-tech/lib.workflow-composer`, or choose a documented subpath when you only need one part of the package surface.
25
25
 
26
26
  Workflow composition is deterministic by default:
27
27
 
@@ -47,12 +47,12 @@ Workflow composition is deterministic by default:
47
47
  ## Local Commands
48
48
 
49
49
  - `bun run dev` — contractspec-bun-build dev
50
- - `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
50
+ - `bun run build` — contractspec-bun-build build
51
51
  - `bun run test` — bun test --pass-with-no-tests
52
52
  - `bun run lint` — bun lint:fix
53
53
  - `bun run lint:check` — biome check .
54
54
  - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
55
- - `bun run typecheck` — tsc --noEmit
55
+ - `bun run typecheck` — tsgo --noEmit
56
56
  - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
57
57
  - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
58
58
  - `bun run clean` — rimraf dist .turbo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm-tech/lib.workflow-composer",
3
- "version": "3.7.29",
3
+ "version": "5.0.1",
4
4
  "description": "Tenant-aware workflow composition helpers for ContractSpec.",
5
5
  "keywords": [
6
6
  "contractspec",
@@ -12,13 +12,12 @@
12
12
  "type": "module",
13
13
  "types": "./dist/index.d.ts",
14
14
  "files": [
15
- "dist",
16
- "README.md"
15
+ "package.json"
17
16
  ],
18
17
  "scripts": {
19
18
  "publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
20
19
  "publish:pkg:canary": "bun publish:pkg --tag canary",
21
- "build": "bun run build:bundle && bun run build:types",
20
+ "build": "contractspec-bun-build build",
22
21
  "build:bundle": "contractspec-bun-build transpile",
23
22
  "build:types": "contractspec-bun-build types",
24
23
  "dev": "contractspec-bun-build dev",
@@ -28,26 +27,20 @@
28
27
  "lint:check": "node ../../../scripts/biome.cjs check .",
29
28
  "test": "bun test --pass-with-no-tests",
30
29
  "prebuild": "contractspec-bun-build prebuild",
31
- "typecheck": "tsc --noEmit"
30
+ "typecheck": "tsgo --noEmit"
32
31
  },
33
32
  "dependencies": {
34
- "@contractspec/lib.contracts-spec": "6.4.0",
35
- "@contractspec/lib.ai-providers": "3.7.22",
36
- "compare-versions": "^6.1.1"
33
+ "@lssm-tech/lib.contracts-spec": "workspace:^",
34
+ "@lssm-tech/lib.ai-providers": "workspace:^",
35
+ "compare-versions": "catalog:data"
37
36
  },
38
37
  "devDependencies": {
39
- "@contractspec/tool.typescript": "3.7.13",
40
- "typescript": "^5.9.3",
41
- "@contractspec/tool.bun": "3.7.18"
38
+ "@lssm-tech/tool.typescript": "workspace:*",
39
+ "typescript": "catalog:",
40
+ "@lssm-tech/tool.bun": "workspace:*"
42
41
  },
43
42
  "exports": {
44
- ".": {
45
- "types": "./dist/index.d.ts",
46
- "browser": "./dist/browser/index.js",
47
- "bun": "./dist/index.js",
48
- "node": "./dist/node/index.js",
49
- "default": "./dist/index.js"
50
- }
43
+ ".": "./src/index.ts"
51
44
  },
52
45
  "publishConfig": {
53
46
  "access": "public",
@@ -68,5 +61,75 @@
68
61
  "url": "https://github.com/lssm-tech/contractspec.git",
69
62
  "directory": "packages/libs/workflow-composer"
70
63
  },
71
- "homepage": "https://contractspec.io"
64
+ "homepage": "https://contractspec.io",
65
+ "contractspec": {
66
+ "catalog": {
67
+ "family": "sharedLibs",
68
+ "kind": "library",
69
+ "status": "active",
70
+ "adoptionMode": "adoptable",
71
+ "stability": "stable",
72
+ "platforms": [
73
+ "server"
74
+ ],
75
+ "runtimes": [
76
+ "node"
77
+ ],
78
+ "entrypoints": [
79
+ "."
80
+ ],
81
+ "documentation": [
82
+ {
83
+ "title": "Lib Workflow Composer documentation",
84
+ "url": "https://www.npmjs.com/package/%40lssm-tech%2Flib.workflow-composer",
85
+ "kind": "readme"
86
+ }
87
+ ],
88
+ "examples": [],
89
+ "replacements": [],
90
+ "capabilities": [
91
+ {
92
+ "id": "package.lssm.tech.lib.workflow.composer",
93
+ "title": "Lib Workflow Composer",
94
+ "description": "Tenant-aware workflow composition helpers for ContractSpec.",
95
+ "status": "active",
96
+ "useCases": [
97
+ "Adopt tenant-aware workflow composition helpers for contractspec"
98
+ ],
99
+ "aliases": [
100
+ "contractspec",
101
+ "workflow",
102
+ "composer",
103
+ "multi-tenant",
104
+ "typescript",
105
+ "lib"
106
+ ],
107
+ "pythonEquivalents": [],
108
+ "canonicalImports": [
109
+ {
110
+ "from": "@lssm-tech/lib.workflow-composer",
111
+ "symbols": []
112
+ }
113
+ ],
114
+ "constraints": {
115
+ "platforms": [
116
+ "server"
117
+ ],
118
+ "runtimes": [
119
+ "node"
120
+ ],
121
+ "avoidWhen": [
122
+ "The @lssm-tech/lib.workflow-composer package constraints do not match the target runtime or platform."
123
+ ],
124
+ "requires": [
125
+ "TypeScript"
126
+ ]
127
+ },
128
+ "alternatives": [],
129
+ "relationships": [],
130
+ "resolutionPriority": 50
131
+ }
132
+ ]
133
+ }
134
+ }
72
135
  }
@@ -1 +0,0 @@
1
- import{satisfies as x}from"compare-versions";import{validateWorkflowSpec as i}from"@contractspec/lib.contracts-spec/workflow";function d(o,r){let e=[],s=new Set(r.definition.steps.map((t)=>t.id)),a=new Set(o.hiddenSteps??[]),f=new Set(s),m=r.definition.transitions.filter((t)=>f.has(t.from)&&f.has(t.to)).map((t)=>({...t}));if(!o.workflow.trim())e.push({code:"workflow.extension.workflow",message:"workflow is required"});if(o.workflow!==r.meta.key)e.push({code:"workflow.extension.workflow.mismatch",message:`extension targets "${o.workflow}" but base workflow is "${r.meta.key}"`});a.forEach((t)=>{if(!s.has(t)){e.push({code:"workflow.extension.hidden-step",message:`hidden step "${t}" does not exist`});return}f.delete(t)}),c(m,a);let w=new Set([...s].filter((t)=>!a.has(t)));o.customSteps?.forEach((t,p)=>{let u=t.inject.id?.trim();if(!u){e.push({code:"workflow.extension.step.id",message:`customSteps[${p}] is missing an id`});return}if(t.id&&t.id!==u)e.push({code:"workflow.extension.step.id-mismatch",message:`customSteps[${p}] has mismatched id (id="${t.id}", inject.id="${u}")`});if(w.has(u))e.push({code:"workflow.extension.step.id.duplicate",message:`customSteps[${p}] injects duplicate step id "${u}"`});if(t.after&&t.before)e.push({code:"workflow.extension.step.anchor.multiple",message:`customSteps[${p}] cannot set both after and before`});if(!t.after&&!t.before)e.push({code:"workflow.extension.step.anchor",message:`customSteps[${p}] must set after or before`});if(t.after&&!w.has(t.after))e.push({code:"workflow.extension.step.after",message:`customSteps[${p}] references unknown step "${t.after}"`});if(t.before&&!w.has(t.before))e.push({code:"workflow.extension.step.before",message:`customSteps[${p}] references unknown step "${t.before}"`});if(t.transitionFrom&&!w.has(t.transitionFrom))e.push({code:"workflow.extension.step.transition-from",message:`customSteps[${p}] references unknown transitionFrom step "${t.transitionFrom}"`});if(t.transitionTo&&!w.has(t.transitionTo))e.push({code:"workflow.extension.step.transition-to",message:`customSteps[${p}] references unknown transitionTo step "${t.transitionTo}"`});if(w.add(u),f.add(u),t.transitionFrom)m.push({from:t.transitionFrom,to:u,condition:t.when});if(t.transitionTo)m.push({from:u,to:t.transitionTo,condition:t.when})});let l=r.definition.entryStepId??r.definition.steps[0]?.id;if(l&&a.has(l))e.push({code:"workflow.extension.hidden-step.entry",message:`hiddenSteps removes the entry step "${l}"`});if(l&&!a.has(l)){let t=g(l,m);for(let p of f)if(!t.has(p))e.push({code:"workflow.extension.hidden-step.orphan",message:`extension leaves step "${p}" unreachable from entry step "${l}"`})}if(e.length){let t=e.map((p)=>`${p.code}: ${p.message}`).join("; ");throw Error(`Invalid workflow extension for ${o.workflow}: ${t}`)}}function c(o,r){for(let e=o.length-1;e>=0;e-=1){let s=o[e];if(!s)continue;if(r.has(s.from)||r.has(s.to))o.splice(e,1)}}function g(o,r){let e=new Map;for(let f of r){let m=e.get(f.from)??[];m.push(f.to),e.set(f.from,m)}let s=new Set,a=[o];while(a.length>0){let f=a.shift();if(!f||s.has(f))continue;s.add(f);for(let m of e.get(f)??[])a.push(m)}return s}function n(o,r){d(r,o);let e=v(o),s=[...e.definition.steps],a=[...e.definition.transitions],f=new Set(r.hiddenSteps??[]);if(f.forEach((l)=>{let t=s.findIndex((p)=>p.id===l);if(t!==-1)s.splice(t,1)}),f.size)a=a.filter((l)=>!f.has(l.from)&&!f.has(l.to));r.customSteps?.forEach((l)=>{S(s,l),$(a,l)}),e.definition.steps=s,e.definition.transitions=E(a),e.metadata=h(e.metadata,r.metadata),e.annotations=h(e.annotations,r.annotations);let w=i(e).filter((l)=>l.level==="error");if(w.length>0)throw Error(`Invalid composed workflow ${e.meta.key}.v${e.meta.version}: ${w.map((l)=>l.message).join("; ")}`);return e}function S(o,r){let e=y(o,r);if(e===-1)throw Error(`Unable to place injected step "${r.inject.id}"`);o.splice(e,0,{...r.inject})}function y(o,r){if(r.after){let e=o.findIndex((s)=>s.id===r.after);return e===-1?-1:e+1}if(r.before){let e=o.findIndex((s)=>s.id===r.before);return e===-1?-1:e}return o.length}function $(o,r){if(!r.inject.id)return;if(r.transitionFrom)o.push({from:r.transitionFrom,to:r.inject.id,condition:r.when});if(r.transitionTo)o.push({from:r.inject.id,to:r.transitionTo,condition:r.when})}function E(o){let r=new Set,e=[];return o.forEach((s)=>{let a=`${s.from}->${s.to}:${s.condition??""}`;if(r.has(a))return;r.add(a),e.push(s)}),e}function v(o){return JSON.parse(JSON.stringify(o))}function h(o,r){if(!o&&!r)return;return{...o??{},...r??{}}}function k(o){return[...o].sort((r,e)=>{let s=(r.priority??0)-(e.priority??0);if(s!==0)return s;let a=r.workflow.localeCompare(e.workflow);if(a!==0)return a;let f=(r.tenantId??"").localeCompare(e.tenantId??"");if(f!==0)return f;let m=(r.role??"").localeCompare(e.role??"");if(m!==0)return m;let w=(r.device??"").localeCompare(e.device??"");if(w!==0)return w;return(r.baseVersion??"").localeCompare(e.baseVersion??"")})}class W{extensions=[];register(o){return this.extensions.push(o),this}registerMany(o){return o.forEach((r)=>{this.register(r)}),this}compose(o){return k(this.extensions.filter((e)=>T(o,e))).reduce((e,s)=>n(e,s),o.base)}}function T(o,r){if(r.workflow!==o.base.meta.key)return!1;if(r.baseVersion&&!x(o.base.meta.version,r.baseVersion))return!1;if(r.tenantId&&r.tenantId!==o.tenantId)return!1;if(r.role&&r.role!==o.role)return!1;if(r.device&&r.device!==o.device)return!1;return!0}function M(o){return{id:o.id,label:o.label,type:o.type??"human",description:o.description??"Tenant-specific approval",action:o.action,guard:o.guardExpression?{type:"expression",value:o.guardExpression}:void 0}}import{defineFeature as b}from"@contractspec/lib.contracts-spec/features";var D=b({meta:{key:"libs.workflow-composer",version:"1.0.0",title:"Workflow Composer",description:"Tenant-aware workflow composition helpers for ContractSpec.",domain:"workflow-composer",owners:["@contractspec-core"],tags:["package","libs","workflow-composer"],stability:"experimental"}});export{d as validateExtension,k as mergeExtensions,M as approvalStepTemplate,n as applyWorkflowExtension,D as WorkflowComposerFeature,W as WorkflowComposer};
@@ -1,8 +0,0 @@
1
- import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
2
- import type { ComposeParams, WorkflowExtension } from './types';
3
- export declare class WorkflowComposer {
4
- private readonly extensions;
5
- register(extension: WorkflowExtension): this;
6
- registerMany(extensions: WorkflowExtension[]): this;
7
- compose(params: ComposeParams): WorkflowSpec;
8
- }
@@ -1 +0,0 @@
1
- export {};
package/dist/index.d.ts DELETED
@@ -1,7 +0,0 @@
1
- export * from './composer';
2
- export * from './injector';
3
- export * from './merger';
4
- export * from './templates';
5
- export * from './types';
6
- export * from './validator';
7
- export * from './workflow-composer.feature';
package/dist/index.js DELETED
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{satisfies as x}from"compare-versions";import{validateWorkflowSpec as i}from"@contractspec/lib.contracts-spec/workflow";function d(o,r){let e=[],s=new Set(r.definition.steps.map((t)=>t.id)),a=new Set(o.hiddenSteps??[]),f=new Set(s),m=r.definition.transitions.filter((t)=>f.has(t.from)&&f.has(t.to)).map((t)=>({...t}));if(!o.workflow.trim())e.push({code:"workflow.extension.workflow",message:"workflow is required"});if(o.workflow!==r.meta.key)e.push({code:"workflow.extension.workflow.mismatch",message:`extension targets "${o.workflow}" but base workflow is "${r.meta.key}"`});a.forEach((t)=>{if(!s.has(t)){e.push({code:"workflow.extension.hidden-step",message:`hidden step "${t}" does not exist`});return}f.delete(t)}),c(m,a);let w=new Set([...s].filter((t)=>!a.has(t)));o.customSteps?.forEach((t,p)=>{let u=t.inject.id?.trim();if(!u){e.push({code:"workflow.extension.step.id",message:`customSteps[${p}] is missing an id`});return}if(t.id&&t.id!==u)e.push({code:"workflow.extension.step.id-mismatch",message:`customSteps[${p}] has mismatched id (id="${t.id}", inject.id="${u}")`});if(w.has(u))e.push({code:"workflow.extension.step.id.duplicate",message:`customSteps[${p}] injects duplicate step id "${u}"`});if(t.after&&t.before)e.push({code:"workflow.extension.step.anchor.multiple",message:`customSteps[${p}] cannot set both after and before`});if(!t.after&&!t.before)e.push({code:"workflow.extension.step.anchor",message:`customSteps[${p}] must set after or before`});if(t.after&&!w.has(t.after))e.push({code:"workflow.extension.step.after",message:`customSteps[${p}] references unknown step "${t.after}"`});if(t.before&&!w.has(t.before))e.push({code:"workflow.extension.step.before",message:`customSteps[${p}] references unknown step "${t.before}"`});if(t.transitionFrom&&!w.has(t.transitionFrom))e.push({code:"workflow.extension.step.transition-from",message:`customSteps[${p}] references unknown transitionFrom step "${t.transitionFrom}"`});if(t.transitionTo&&!w.has(t.transitionTo))e.push({code:"workflow.extension.step.transition-to",message:`customSteps[${p}] references unknown transitionTo step "${t.transitionTo}"`});if(w.add(u),f.add(u),t.transitionFrom)m.push({from:t.transitionFrom,to:u,condition:t.when});if(t.transitionTo)m.push({from:u,to:t.transitionTo,condition:t.when})});let l=r.definition.entryStepId??r.definition.steps[0]?.id;if(l&&a.has(l))e.push({code:"workflow.extension.hidden-step.entry",message:`hiddenSteps removes the entry step "${l}"`});if(l&&!a.has(l)){let t=g(l,m);for(let p of f)if(!t.has(p))e.push({code:"workflow.extension.hidden-step.orphan",message:`extension leaves step "${p}" unreachable from entry step "${l}"`})}if(e.length){let t=e.map((p)=>`${p.code}: ${p.message}`).join("; ");throw Error(`Invalid workflow extension for ${o.workflow}: ${t}`)}}function c(o,r){for(let e=o.length-1;e>=0;e-=1){let s=o[e];if(!s)continue;if(r.has(s.from)||r.has(s.to))o.splice(e,1)}}function g(o,r){let e=new Map;for(let f of r){let m=e.get(f.from)??[];m.push(f.to),e.set(f.from,m)}let s=new Set,a=[o];while(a.length>0){let f=a.shift();if(!f||s.has(f))continue;s.add(f);for(let m of e.get(f)??[])a.push(m)}return s}function n(o,r){d(r,o);let e=v(o),s=[...e.definition.steps],a=[...e.definition.transitions],f=new Set(r.hiddenSteps??[]);if(f.forEach((l)=>{let t=s.findIndex((p)=>p.id===l);if(t!==-1)s.splice(t,1)}),f.size)a=a.filter((l)=>!f.has(l.from)&&!f.has(l.to));r.customSteps?.forEach((l)=>{S(s,l),$(a,l)}),e.definition.steps=s,e.definition.transitions=E(a),e.metadata=h(e.metadata,r.metadata),e.annotations=h(e.annotations,r.annotations);let w=i(e).filter((l)=>l.level==="error");if(w.length>0)throw Error(`Invalid composed workflow ${e.meta.key}.v${e.meta.version}: ${w.map((l)=>l.message).join("; ")}`);return e}function S(o,r){let e=y(o,r);if(e===-1)throw Error(`Unable to place injected step "${r.inject.id}"`);o.splice(e,0,{...r.inject})}function y(o,r){if(r.after){let e=o.findIndex((s)=>s.id===r.after);return e===-1?-1:e+1}if(r.before){let e=o.findIndex((s)=>s.id===r.before);return e===-1?-1:e}return o.length}function $(o,r){if(!r.inject.id)return;if(r.transitionFrom)o.push({from:r.transitionFrom,to:r.inject.id,condition:r.when});if(r.transitionTo)o.push({from:r.inject.id,to:r.transitionTo,condition:r.when})}function E(o){let r=new Set,e=[];return o.forEach((s)=>{let a=`${s.from}->${s.to}:${s.condition??""}`;if(r.has(a))return;r.add(a),e.push(s)}),e}function v(o){return JSON.parse(JSON.stringify(o))}function h(o,r){if(!o&&!r)return;return{...o??{},...r??{}}}function k(o){return[...o].sort((r,e)=>{let s=(r.priority??0)-(e.priority??0);if(s!==0)return s;let a=r.workflow.localeCompare(e.workflow);if(a!==0)return a;let f=(r.tenantId??"").localeCompare(e.tenantId??"");if(f!==0)return f;let m=(r.role??"").localeCompare(e.role??"");if(m!==0)return m;let w=(r.device??"").localeCompare(e.device??"");if(w!==0)return w;return(r.baseVersion??"").localeCompare(e.baseVersion??"")})}class W{extensions=[];register(o){return this.extensions.push(o),this}registerMany(o){return o.forEach((r)=>{this.register(r)}),this}compose(o){return k(this.extensions.filter((e)=>T(o,e))).reduce((e,s)=>n(e,s),o.base)}}function T(o,r){if(r.workflow!==o.base.meta.key)return!1;if(r.baseVersion&&!x(o.base.meta.version,r.baseVersion))return!1;if(r.tenantId&&r.tenantId!==o.tenantId)return!1;if(r.role&&r.role!==o.role)return!1;if(r.device&&r.device!==o.device)return!1;return!0}function M(o){return{id:o.id,label:o.label,type:o.type??"human",description:o.description??"Tenant-specific approval",action:o.action,guard:o.guardExpression?{type:"expression",value:o.guardExpression}:void 0}}import{defineFeature as b}from"@contractspec/lib.contracts-spec/features";var D=b({meta:{key:"libs.workflow-composer",version:"1.0.0",title:"Workflow Composer",description:"Tenant-aware workflow composition helpers for ContractSpec.",domain:"workflow-composer",owners:["@contractspec-core"],tags:["package","libs","workflow-composer"],stability:"experimental"}});export{d as validateExtension,k as mergeExtensions,M as approvalStepTemplate,n as applyWorkflowExtension,D as WorkflowComposerFeature,W as WorkflowComposer};
@@ -1,3 +0,0 @@
1
- import { type WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
2
- import type { WorkflowExtension } from './types';
3
- export declare function applyWorkflowExtension(base: WorkflowSpec, extension: WorkflowExtension): WorkflowSpec;
package/dist/merger.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type { WorkflowExtension } from './types';
2
- export declare function mergeExtensions(extensions: WorkflowExtension[]): WorkflowExtension[];
@@ -1 +0,0 @@
1
- import{satisfies as x}from"compare-versions";import{validateWorkflowSpec as i}from"@contractspec/lib.contracts-spec/workflow";function d(o,r){let e=[],s=new Set(r.definition.steps.map((t)=>t.id)),a=new Set(o.hiddenSteps??[]),f=new Set(s),m=r.definition.transitions.filter((t)=>f.has(t.from)&&f.has(t.to)).map((t)=>({...t}));if(!o.workflow.trim())e.push({code:"workflow.extension.workflow",message:"workflow is required"});if(o.workflow!==r.meta.key)e.push({code:"workflow.extension.workflow.mismatch",message:`extension targets "${o.workflow}" but base workflow is "${r.meta.key}"`});a.forEach((t)=>{if(!s.has(t)){e.push({code:"workflow.extension.hidden-step",message:`hidden step "${t}" does not exist`});return}f.delete(t)}),c(m,a);let w=new Set([...s].filter((t)=>!a.has(t)));o.customSteps?.forEach((t,p)=>{let u=t.inject.id?.trim();if(!u){e.push({code:"workflow.extension.step.id",message:`customSteps[${p}] is missing an id`});return}if(t.id&&t.id!==u)e.push({code:"workflow.extension.step.id-mismatch",message:`customSteps[${p}] has mismatched id (id="${t.id}", inject.id="${u}")`});if(w.has(u))e.push({code:"workflow.extension.step.id.duplicate",message:`customSteps[${p}] injects duplicate step id "${u}"`});if(t.after&&t.before)e.push({code:"workflow.extension.step.anchor.multiple",message:`customSteps[${p}] cannot set both after and before`});if(!t.after&&!t.before)e.push({code:"workflow.extension.step.anchor",message:`customSteps[${p}] must set after or before`});if(t.after&&!w.has(t.after))e.push({code:"workflow.extension.step.after",message:`customSteps[${p}] references unknown step "${t.after}"`});if(t.before&&!w.has(t.before))e.push({code:"workflow.extension.step.before",message:`customSteps[${p}] references unknown step "${t.before}"`});if(t.transitionFrom&&!w.has(t.transitionFrom))e.push({code:"workflow.extension.step.transition-from",message:`customSteps[${p}] references unknown transitionFrom step "${t.transitionFrom}"`});if(t.transitionTo&&!w.has(t.transitionTo))e.push({code:"workflow.extension.step.transition-to",message:`customSteps[${p}] references unknown transitionTo step "${t.transitionTo}"`});if(w.add(u),f.add(u),t.transitionFrom)m.push({from:t.transitionFrom,to:u,condition:t.when});if(t.transitionTo)m.push({from:u,to:t.transitionTo,condition:t.when})});let l=r.definition.entryStepId??r.definition.steps[0]?.id;if(l&&a.has(l))e.push({code:"workflow.extension.hidden-step.entry",message:`hiddenSteps removes the entry step "${l}"`});if(l&&!a.has(l)){let t=g(l,m);for(let p of f)if(!t.has(p))e.push({code:"workflow.extension.hidden-step.orphan",message:`extension leaves step "${p}" unreachable from entry step "${l}"`})}if(e.length){let t=e.map((p)=>`${p.code}: ${p.message}`).join("; ");throw Error(`Invalid workflow extension for ${o.workflow}: ${t}`)}}function c(o,r){for(let e=o.length-1;e>=0;e-=1){let s=o[e];if(!s)continue;if(r.has(s.from)||r.has(s.to))o.splice(e,1)}}function g(o,r){let e=new Map;for(let f of r){let m=e.get(f.from)??[];m.push(f.to),e.set(f.from,m)}let s=new Set,a=[o];while(a.length>0){let f=a.shift();if(!f||s.has(f))continue;s.add(f);for(let m of e.get(f)??[])a.push(m)}return s}function n(o,r){d(r,o);let e=v(o),s=[...e.definition.steps],a=[...e.definition.transitions],f=new Set(r.hiddenSteps??[]);if(f.forEach((l)=>{let t=s.findIndex((p)=>p.id===l);if(t!==-1)s.splice(t,1)}),f.size)a=a.filter((l)=>!f.has(l.from)&&!f.has(l.to));r.customSteps?.forEach((l)=>{S(s,l),$(a,l)}),e.definition.steps=s,e.definition.transitions=E(a),e.metadata=h(e.metadata,r.metadata),e.annotations=h(e.annotations,r.annotations);let w=i(e).filter((l)=>l.level==="error");if(w.length>0)throw Error(`Invalid composed workflow ${e.meta.key}.v${e.meta.version}: ${w.map((l)=>l.message).join("; ")}`);return e}function S(o,r){let e=y(o,r);if(e===-1)throw Error(`Unable to place injected step "${r.inject.id}"`);o.splice(e,0,{...r.inject})}function y(o,r){if(r.after){let e=o.findIndex((s)=>s.id===r.after);return e===-1?-1:e+1}if(r.before){let e=o.findIndex((s)=>s.id===r.before);return e===-1?-1:e}return o.length}function $(o,r){if(!r.inject.id)return;if(r.transitionFrom)o.push({from:r.transitionFrom,to:r.inject.id,condition:r.when});if(r.transitionTo)o.push({from:r.inject.id,to:r.transitionTo,condition:r.when})}function E(o){let r=new Set,e=[];return o.forEach((s)=>{let a=`${s.from}->${s.to}:${s.condition??""}`;if(r.has(a))return;r.add(a),e.push(s)}),e}function v(o){return JSON.parse(JSON.stringify(o))}function h(o,r){if(!o&&!r)return;return{...o??{},...r??{}}}function k(o){return[...o].sort((r,e)=>{let s=(r.priority??0)-(e.priority??0);if(s!==0)return s;let a=r.workflow.localeCompare(e.workflow);if(a!==0)return a;let f=(r.tenantId??"").localeCompare(e.tenantId??"");if(f!==0)return f;let m=(r.role??"").localeCompare(e.role??"");if(m!==0)return m;let w=(r.device??"").localeCompare(e.device??"");if(w!==0)return w;return(r.baseVersion??"").localeCompare(e.baseVersion??"")})}class W{extensions=[];register(o){return this.extensions.push(o),this}registerMany(o){return o.forEach((r)=>{this.register(r)}),this}compose(o){return k(this.extensions.filter((e)=>T(o,e))).reduce((e,s)=>n(e,s),o.base)}}function T(o,r){if(r.workflow!==o.base.meta.key)return!1;if(r.baseVersion&&!x(o.base.meta.version,r.baseVersion))return!1;if(r.tenantId&&r.tenantId!==o.tenantId)return!1;if(r.role&&r.role!==o.role)return!1;if(r.device&&r.device!==o.device)return!1;return!0}function M(o){return{id:o.id,label:o.label,type:o.type??"human",description:o.description??"Tenant-specific approval",action:o.action,guard:o.guardExpression?{type:"expression",value:o.guardExpression}:void 0}}import{defineFeature as b}from"@contractspec/lib.contracts-spec/features";var D=b({meta:{key:"libs.workflow-composer",version:"1.0.0",title:"Workflow Composer",description:"Tenant-aware workflow composition helpers for ContractSpec.",domain:"workflow-composer",owners:["@contractspec-core"],tags:["package","libs","workflow-composer"],stability:"experimental"}});export{d as validateExtension,k as mergeExtensions,M as approvalStepTemplate,n as applyWorkflowExtension,D as WorkflowComposerFeature,W as WorkflowComposer};
@@ -1,10 +0,0 @@
1
- import type { Step, StepAction, StepType } from '@contractspec/lib.contracts-spec/workflow';
2
- export interface StepTemplateOptions {
3
- id: string;
4
- label: string;
5
- type?: StepType;
6
- description?: string;
7
- action?: StepAction;
8
- guardExpression?: string;
9
- }
10
- export declare function approvalStepTemplate(options: StepTemplateOptions): Step;
package/dist/types.d.ts DELETED
@@ -1,41 +0,0 @@
1
- import type { ModelSelector } from '@contractspec/lib.ai-providers/selector-types';
2
- import type { Step, StepModelHints, WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
3
- /**
4
- * Context provided to operation executors during workflow step execution.
5
- * Includes an optional ranking-driven model selector for AI-powered steps.
6
- */
7
- export interface OperationExecutorContext {
8
- tenantId?: string;
9
- role?: string;
10
- /** Ranking-driven model selector; executors should use this when selecting AI models. */
11
- modelSelector?: ModelSelector;
12
- /** Model hints from the current step, if any. */
13
- stepModelHints?: StepModelHints;
14
- metadata?: Record<string, unknown>;
15
- }
16
- export interface WorkflowExtensionScope {
17
- tenantId?: string;
18
- role?: string;
19
- device?: string;
20
- }
21
- export interface StepInjection {
22
- id?: string;
23
- after?: string;
24
- before?: string;
25
- inject: Step;
26
- when?: string;
27
- transitionTo?: string;
28
- transitionFrom?: string;
29
- }
30
- export interface WorkflowExtension extends WorkflowExtensionScope {
31
- workflow: string;
32
- baseVersion?: string;
33
- priority?: number;
34
- customSteps?: StepInjection[];
35
- hiddenSteps?: string[];
36
- metadata?: Record<string, unknown>;
37
- annotations?: Record<string, unknown>;
38
- }
39
- export interface ComposeParams extends WorkflowExtensionScope {
40
- base: WorkflowSpec;
41
- }
@@ -1,7 +0,0 @@
1
- import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
2
- import type { WorkflowExtension } from './types';
3
- export interface WorkflowExtensionValidationIssue {
4
- code: string;
5
- message: string;
6
- }
7
- export declare function validateExtension(extension: WorkflowExtension, base: WorkflowSpec): void;
@@ -1 +0,0 @@
1
- export declare const WorkflowComposerFeature: import("@contractspec/lib.contracts-spec").FeatureModuleSpec;