@nanobpm/urban 0.25.0 → 0.27.0

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.
@@ -34,6 +34,9 @@ export interface WorkerIo {
34
34
 
35
35
  /** Filename the console uses; kept identical so the artifact is a drop-in. */
36
36
  export const WORKER_BINDINGS_DTS = "worker-io.d.ts";
37
+ /** The generated typed-`defineWorker` wrapper's basename — re-exports the worker SDK and overrides
38
+ * `defineWorker` with a taskType-keyed typed signature. */
39
+ export const WORKER_BINDINGS_TS = "workers.ts";
37
40
  /** The domain type declarations `worker-io.d.ts` imports from (console: domain-rows.d.ts). */
38
41
  export const DOMAIN_DTS = "domain-rows.d.ts";
39
42
 
@@ -93,6 +96,28 @@ export interface WorkerBindingDecl {
93
96
  taskType?: string;
94
97
  inputType?: string;
95
98
  outputType?: string;
99
+ /** The `zeebe:header` keys declared on the task; reified into a typed `job.customHeaders` shape
100
+ * (known keys, `string` values — headers are strings on the wire). Empty/absent leaves
101
+ * `customHeaders` the untyped fallback. */
102
+ headerKeys?: string[];
103
+ }
104
+
105
+ /** The TS type expression for a worker's declared custom-header keys: an object type mapping each
106
+ * declared key to `string` (Zeebe headers are strings on the wire) plus a `string` index signature
107
+ * so undeclared headers stay accessible with the same honest wire type. Returns `undefined` when no
108
+ * keys are declared (the caller omits the entry so the taskType falls back to `WorkerHdrs`). */
109
+ function headerRefFor(keys: string[] | undefined): string | undefined {
110
+ const clean = [
111
+ ...new Set(
112
+ (keys ?? [])
113
+ .filter((k) => typeof k === "string")
114
+ .map((k) => k.trim())
115
+ .filter((k) => k.length > 0),
116
+ ),
117
+ ];
118
+ if (clean.length === 0) return undefined;
119
+ const fields = clean.map((k) => `${JSON.stringify(k)}: string`).join("; ");
120
+ return `{ ${fields}; [key: string]: string }`;
96
121
  }
97
122
 
98
123
  /** Emit `worker-io.d.ts` from worker bindings + the set of declared domain type ids. */
@@ -114,12 +139,15 @@ export function emitWorkerBindings(
114
139
  : "string";
115
140
  const inputs: string[] = [];
116
141
  const outputs: string[] = [];
142
+ const headers: string[] = [];
117
143
  for (const w of workers) {
118
144
  if (typeof w?.taskType !== "string" || w.taskType.length === 0) continue;
119
145
  const inRef = typeRefFor(w.inputType, declared);
120
146
  if (inRef) inputs.push(` ${propKey(w.taskType)}: ${inRef};`);
121
147
  const outRef = typeRefFor(w.outputType, declared);
122
148
  if (outRef) outputs.push(` ${propKey(w.taskType)}: ${outRef};`);
149
+ const hdrRef = headerRefFor(w.headerKeys);
150
+ if (hdrRef) headers.push(` ${propKey(w.taskType)}: ${hdrRef};`);
123
151
  }
124
152
 
125
153
  const header =
@@ -141,7 +169,9 @@ export function emitWorkerBindings(
141
169
  const outputsIface = outputs.length > 0
142
170
  ? `export interface WorkerOutputs {\n${outputs.join("\n")}\n}\n`
143
171
  : `export interface WorkerOutputs {}\n`;
144
- const headersIface = `export interface WorkerHeaders {}\n`;
172
+ const headersIface = headers.length > 0
173
+ ? `export interface WorkerHeaders {\n${headers.join("\n")}\n}\n`
174
+ : `export interface WorkerHeaders {}\n`;
145
175
 
146
176
  return `${header}\n` +
147
177
  importTypes +
@@ -186,3 +216,75 @@ export const workerIoDeriver: Deriver<{ models: ModelSource[]; declaredTypeIds:
186
216
  describe: "Derive worker-io.d.ts (task type + data-envelope in/out) from BPMN models.",
187
217
  derive: ({ models, declaredTypeIds }) => deriveWorkerBindings(models, declaredTypeIds),
188
218
  };
219
+
220
+ /**
221
+ * Overlay the model-derived worker-IO map onto the manifest `workers[]`. The model is authoritative
222
+ * for the envelope (`inputType`/`outputType`), so every scanned `taskType` takes its I/O from
223
+ * `derived` (clearing a stale manifest value when the model carries none); manifest entries the scan
224
+ * did not cover (e.g. a worker declared only for an `llm` binding, with no service task) are
225
+ * preserved. New `taskType`s seen only in the model are appended. A byte-faithful port of the
226
+ * console's `overlayDerivedWorkerIo` (server `data_cli.ts`).
227
+ */
228
+ export function overlayDerivedWorkerIo(
229
+ manifest: WorkerBindingDecl[],
230
+ derived: WorkerBindingDecl[],
231
+ ): WorkerBindingDecl[] {
232
+ const byType = new Map<string, WorkerBindingDecl>();
233
+ for (const w of manifest) {
234
+ if (typeof w.taskType === "string") byType.set(w.taskType, { ...w });
235
+ }
236
+ for (const d of derived) {
237
+ if (typeof d.taskType !== "string") continue;
238
+ const existing = byType.get(d.taskType);
239
+ const merged: WorkerBindingDecl = existing
240
+ ? { ...existing, taskType: d.taskType }
241
+ : { taskType: d.taskType };
242
+ if (d.inputType) merged.inputType = d.inputType;
243
+ else delete merged.inputType;
244
+ if (d.outputType) merged.outputType = d.outputType;
245
+ else delete merged.outputType;
246
+ // Header keys are authored on the model, so the derived set is authoritative: replace (not
247
+ // union) the manifest's, and clear when the model declares none.
248
+ if (d.headerKeys && d.headerKeys.length > 0) merged.headerKeys = [...d.headerKeys];
249
+ else delete merged.headerKeys;
250
+ byType.set(d.taskType, merged);
251
+ }
252
+ return [...byType.values()];
253
+ }
254
+
255
+ /**
256
+ * The static typed-`defineWorker` wrapper (`workers.ts`). It re-exports the whole worker SDK and
257
+ * overrides `defineWorker` with a signature that keys off the `type:` string literal: when a job
258
+ * type is present in the generated `WorkerInputs`/`WorkerOutputs` (ADR 0033 §3), the handler's
259
+ * `job.variables` and result are typed from the declared domain type; otherwise they fall back to
260
+ * `WorkerVars`. The body is a pass-through — the wire stays untyped JSON (ADR 0029 §3). This file
261
+ * never changes with the schema, so it is written verbatim (unlike the regenerated
262
+ * `worker-io.d.ts`). A byte-faithful port of the console's `emitWorkerBindingsRuntime`.
263
+ */
264
+ export function emitWorkerBindingsRuntime(): string {
265
+ return "// AUTO-GENERATED by nanobpmn (ADR 0033 §3): the typed `defineWorker`.\n" +
266
+ "// Re-exports the worker SDK and overrides `defineWorker` with a taskType-keyed\n" +
267
+ "// typed signature (job.variables, job.customHeaders + result typed from the\n" +
268
+ "// worker's declared input/output/header contract). Erased to a pass-through at\n" +
269
+ "// runtime. Do not edit.\n" +
270
+ "// eslint-disable\n\n" +
271
+ `import { defineWorker as defineWorkerRaw } from "./worker-sdk.ts";\n` +
272
+ `import type { WorkerOptions } from "./worker-sdk.ts";\n` +
273
+ `import type { WorkerInputs, WorkerOutputs, WorkerHeaders, WorkerTaskType, WorkerVars, WorkerHdrs } from "./${WORKER_BINDINGS_DTS}";\n\n` +
274
+ `export * from "./worker-sdk.ts";\n\n` +
275
+ `type InFor<K extends WorkerTaskType> = K extends keyof WorkerInputs ? WorkerInputs[K] : WorkerVars;\n` +
276
+ `type OutFor<K extends WorkerTaskType> = K extends keyof WorkerOutputs ? WorkerOutputs[K] : WorkerVars;\n` +
277
+ `type HdrFor<K extends WorkerTaskType> = K extends keyof WorkerHeaders ? WorkerHeaders[K] : WorkerHdrs;\n\n` +
278
+ `/**\n` +
279
+ ` * Typed \`defineWorker\`: \`type\` is constrained to the model's declared job types\n` +
280
+ ` * (\`WorkerTaskType\`, ADR 0033 §3) so it autocompletes and rejects unknown jobs,\n` +
281
+ ` * and the handler's \`job.variables\`, \`job.customHeaders\` + result are typed from\n` +
282
+ ` * the worker's declared \`inputType\`/\`outputType\`/header keys. A declared job type\n` +
283
+ ` * with no declared type falls back to WorkerVars / WorkerHdrs.\n` +
284
+ ` */\n` +
285
+ `export function defineWorker<K extends WorkerTaskType>(\n` +
286
+ ` opts: { type: K } & WorkerOptions<InFor<K> & object, OutFor<K> & object, HdrFor<K> & object>,\n` +
287
+ `): void {\n` +
288
+ ` defineWorkerRaw(opts as unknown as WorkerOptions);\n` +
289
+ `}\n`;
290
+ }
@@ -28,28 +28,50 @@ export {
28
28
  deriveDomain,
29
29
  emitDomain,
30
30
  emitDomainModel,
31
+ emitDomainBindings,
31
32
  emitDomainDts,
32
33
  emitDomainDtsForSources,
33
34
  emitDomainTypeRegistry,
34
35
  domainDeriver,
36
+ isPrimitiveKeyword,
35
37
  sourcesFromManifest,
36
38
  registryFromManifest,
37
39
  sqliteAffinityToTs,
38
40
  interfaceName,
41
+ DOMAIN_BINDINGS,
39
42
  } from "./derivers/domain.ts";
40
43
  export type {
41
44
  SourceSchema,
42
45
  DomainFieldDef,
43
46
  DomainTypeDef,
44
47
  DomainTypeRegistry,
48
+ ColumnMeta,
49
+ TableMeta,
45
50
  } from "./derivers/domain.ts";
46
51
 
52
+ export {
53
+ resolveShapes,
54
+ columnToField,
55
+ emitDomainModelJson,
56
+ DOMAIN_MODEL_JSON,
57
+ } from "./derivers/shapes.ts";
58
+ export type {
59
+ ShapeOp,
60
+ ShapeDecl,
61
+ ShapeDiagnostic,
62
+ ShapeResolution,
63
+ FusedMetaDecl,
64
+ } from "./derivers/shapes.ts";
65
+
47
66
  export {
48
67
  deriveWorkerBindings,
49
68
  emitWorkerBindings,
69
+ emitWorkerBindingsRuntime,
70
+ overlayDerivedWorkerIo,
50
71
  workerIoDeriver,
51
72
  scanModelWorkers,
52
73
  WORKER_BINDINGS_DTS,
74
+ WORKER_BINDINGS_TS,
53
75
  DOMAIN_DTS,
54
76
  } from "./derivers/worker-io.ts";
55
77
  export type { ModelSource, WorkerIo, WorkerBindingDecl } from "./derivers/worker-io.ts";
@@ -68,9 +90,11 @@ export type { MetaDecl } from "./derivers/meta.ts";
68
90
  export {
69
91
  deriveMessageBindings,
70
92
  emitMessageBindings,
93
+ emitMessageBindingsRuntime,
71
94
  messagesDeriver,
72
95
  scanModelMessages,
73
96
  MESSAGE_BINDINGS_DTS,
97
+ MESSAGE_BINDINGS_TS,
74
98
  } from "./derivers/messages.ts";
75
99
  export type { MessageIo, MessageBindingDecl } from "./derivers/messages.ts";
76
100