@nanobpm/urban 0.36.0 → 0.37.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.
|
@@ -26,7 +26,7 @@ export interface WorkerStubPlan {
|
|
|
26
26
|
stub: string;
|
|
27
27
|
manifestEntry: StubManifestEntry;
|
|
28
28
|
}
|
|
29
|
-
export type SkipReason = "already-wired" | "orchestrator" | "duplicate";
|
|
29
|
+
export type SkipReason = "already-wired" | "orchestrator" | "duplicate" | "external";
|
|
30
30
|
export interface SkippedWorker {
|
|
31
31
|
taskType: string;
|
|
32
32
|
reason: SkipReason;
|
|
@@ -41,8 +41,10 @@ export declare function slugifyTaskType(taskType: string): string;
|
|
|
41
41
|
export declare function renderWorkerStub(taskType: string, typedIn: boolean, typedOut: boolean): string;
|
|
42
42
|
/**
|
|
43
43
|
* Plan write-once handler stubs from the models. A stub is planned for each service-task
|
|
44
|
-
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator,
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator, NOT
|
|
45
|
+
* declared external (serviced by an out-of-process worker), and not a duplicate of one already
|
|
46
|
+
* planned. `declaredTypeIds` are the manifest `types` ids — an envelope `in`/`out` is typed only
|
|
47
|
+
* when it names one (matching the worker-io deriver). `externalTaskTypes` are the manifest's
|
|
48
|
+
* declared external task types, which the app owns the model for but deliberately does not host.
|
|
47
49
|
*/
|
|
48
|
-
export declare function planWorkerScaffold(models: ModelSource[], workers?: ScaffoldWorker[], declaredTypeIds?: Iterable<string>): WorkerScaffoldPlan;
|
|
50
|
+
export declare function planWorkerScaffold(models: ModelSource[], workers?: ScaffoldWorker[], declaredTypeIds?: Iterable<string>, externalTaskTypes?: Iterable<string>): WorkerScaffoldPlan;
|
|
@@ -57,15 +57,18 @@ export function renderWorkerStub(taskType, typedIn, typedOut) {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Plan write-once handler stubs from the models. A stub is planned for each service-task
|
|
60
|
-
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator,
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator, NOT
|
|
61
|
+
* declared external (serviced by an out-of-process worker), and not a duplicate of one already
|
|
62
|
+
* planned. `declaredTypeIds` are the manifest `types` ids — an envelope `in`/`out` is typed only
|
|
63
|
+
* when it names one (matching the worker-io deriver). `externalTaskTypes` are the manifest's
|
|
64
|
+
* declared external task types, which the app owns the model for but deliberately does not host.
|
|
63
65
|
*/
|
|
64
|
-
export function planWorkerScaffold(models, workers = [], declaredTypeIds = []) {
|
|
66
|
+
export function planWorkerScaffold(models, workers = [], declaredTypeIds = [], externalTaskTypes = []) {
|
|
65
67
|
const wired = new Set(workers
|
|
66
68
|
.map((w) => w?.taskType)
|
|
67
69
|
.filter((t) => typeof t === "string" && t.length > 0));
|
|
68
70
|
const declared = new Set(declaredTypeIds);
|
|
71
|
+
const external = new Set(externalTaskTypes);
|
|
69
72
|
const plans = [];
|
|
70
73
|
const skipped = [];
|
|
71
74
|
const seenTaskTypes = new Set();
|
|
@@ -77,6 +80,10 @@ export function planWorkerScaffold(models, workers = [], declaredTypeIds = []) {
|
|
|
77
80
|
skipped.push({ taskType, reason: "orchestrator" });
|
|
78
81
|
continue;
|
|
79
82
|
}
|
|
83
|
+
if (external.has(taskType)) {
|
|
84
|
+
skipped.push({ taskType, reason: "external" });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
80
87
|
if (wired.has(taskType)) {
|
|
81
88
|
skipped.push({ taskType, reason: "already-wired" });
|
|
82
89
|
continue;
|
package/dist/toolkit/scaffold.js
CHANGED
|
@@ -15,7 +15,7 @@ export async function scaffoldWorkers(opts) {
|
|
|
15
15
|
const manifest = JSON.parse(await io.readText(manifestPath));
|
|
16
16
|
const models = await readModels(root, io, manifest);
|
|
17
17
|
const declaredTypeIds = Object.keys(manifest.types ?? {});
|
|
18
|
-
const { plans, skipped } = planWorkerScaffold(models, manifest.workers ?? [], declaredTypeIds);
|
|
18
|
+
const { plans, skipped } = planWorkerScaffold(models, manifest.workers ?? [], declaredTypeIds, manifest.externalTaskTypes ?? []);
|
|
19
19
|
const outcomes = [];
|
|
20
20
|
const wired = [];
|
|
21
21
|
for (const plan of plans) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/urban",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Urban: build and run code-first apps on Nano — runtime, derivation toolkit, and CLI in one. Author durable processes with @nanobpm/workflow (defineFlow), a typed datasource, forms, triggers and surfaces from a nano.app.json manifest, on Node or Deno.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"test:deno": "deno test -A --no-check src/"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@nanobpm/nano-app-schema": "^0.
|
|
65
|
+
"@nanobpm/nano-app-schema": "^0.5.0",
|
|
66
66
|
"@nanobpm/nano-sdk": "^1.2.3",
|
|
67
67
|
"@nanobpm/workflow": "^0.8.0",
|
|
68
68
|
"bpmn-auto-layout": "^2.0.0-alpha.2",
|
|
@@ -99,6 +99,16 @@ test("planWorkerScaffold: skips already-wired, orchestrator, and duplicates", ()
|
|
|
99
99
|
assert.equal(reasons["t.new"], "duplicate");
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
test("planWorkerScaffold: skips a task type declared external (out-of-process worker)", () => {
|
|
103
|
+
const models = [
|
|
104
|
+
model("a.bpmn", "p1", serviceTask("A", "senior:review"), serviceTask("B", "t.new")),
|
|
105
|
+
];
|
|
106
|
+
const { plans, skipped } = planWorkerScaffold(models, [], [], ["senior:review"]);
|
|
107
|
+
assert.deepEqual(plans.map((p) => p.taskType), ["t.new"]);
|
|
108
|
+
const reasons = Object.fromEntries(skipped.map((s) => [s.taskType, s.reason]));
|
|
109
|
+
assert.equal(reasons["senior:review"], "external");
|
|
110
|
+
});
|
|
111
|
+
|
|
102
112
|
test("planWorkerScaffold: colliding slugs get a numeric suffix", () => {
|
|
103
113
|
const m = model("a.bpmn", "p", serviceTask("A", "orders.charge"), serviceTask("B", "orders-charge"));
|
|
104
114
|
const { plans } = planWorkerScaffold([m], [], []);
|
|
@@ -195,3 +205,28 @@ test("scaffoldWorkers is idempotent: a second --write run is a no-op", async ()
|
|
|
195
205
|
assert.equal(run2.manifestPatched, false, "no new wiring on the second run");
|
|
196
206
|
assert.equal(JSON.stringify(io.files), snapshot, "files unchanged on re-run");
|
|
197
207
|
});
|
|
208
|
+
|
|
209
|
+
test("scaffoldWorkers reads manifest.externalTaskTypes and skips those (never scaffolds/wires)", async () => {
|
|
210
|
+
const files = {
|
|
211
|
+
"/app/nano.app.json": JSON.stringify({
|
|
212
|
+
id: "demo",
|
|
213
|
+
models: { processes: ["processes/*.bpmn"] },
|
|
214
|
+
externalTaskTypes: ["senior:review"],
|
|
215
|
+
}),
|
|
216
|
+
"/app/processes/p.bpmn": model("p.bpmn", "p",
|
|
217
|
+
serviceTask("A", "senior:review"),
|
|
218
|
+
serviceTask("B", "t.new"),
|
|
219
|
+
).xml,
|
|
220
|
+
};
|
|
221
|
+
const io = memIO(files);
|
|
222
|
+
const run = await scaffoldWorkers({ root: "/app", io, write: true });
|
|
223
|
+
// Only the app-hosted task is scaffolded; the external one is skipped.
|
|
224
|
+
assert.deepEqual(run.outcomes.map((o) => o.taskType), ["t.new"]);
|
|
225
|
+
assert.ok(run.skipped.some((s) => s.taskType === "senior:review" && s.reason === "external"));
|
|
226
|
+
assert.equal(io.files["/app/workers/senior-review/worker.ts"], undefined, "external task gets no stub");
|
|
227
|
+
const manifest = JSON.parse(io.files["/app/nano.app.json"]);
|
|
228
|
+
assert.ok(
|
|
229
|
+
!manifest.workers?.some((w: { taskType: string }) => w.taskType === "senior:review"),
|
|
230
|
+
"external task is never wired into workers[]",
|
|
231
|
+
);
|
|
232
|
+
});
|
|
@@ -39,7 +39,7 @@ export interface WorkerStubPlan {
|
|
|
39
39
|
manifestEntry: StubManifestEntry;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export type SkipReason = "already-wired" | "orchestrator" | "duplicate";
|
|
42
|
+
export type SkipReason = "already-wired" | "orchestrator" | "duplicate" | "external";
|
|
43
43
|
|
|
44
44
|
export interface SkippedWorker {
|
|
45
45
|
taskType: string;
|
|
@@ -110,14 +110,17 @@ export function renderWorkerStub(
|
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Plan write-once handler stubs from the models. A stub is planned for each service-task
|
|
113
|
-
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator,
|
|
114
|
-
*
|
|
115
|
-
*
|
|
113
|
+
* `taskType` that is NOT already wired in the manifest, NOT the imperative orchestrator, NOT
|
|
114
|
+
* declared external (serviced by an out-of-process worker), and not a duplicate of one already
|
|
115
|
+
* planned. `declaredTypeIds` are the manifest `types` ids — an envelope `in`/`out` is typed only
|
|
116
|
+
* when it names one (matching the worker-io deriver). `externalTaskTypes` are the manifest's
|
|
117
|
+
* declared external task types, which the app owns the model for but deliberately does not host.
|
|
116
118
|
*/
|
|
117
119
|
export function planWorkerScaffold(
|
|
118
120
|
models: ModelSource[],
|
|
119
121
|
workers: ScaffoldWorker[] = [],
|
|
120
122
|
declaredTypeIds: Iterable<string> = [],
|
|
123
|
+
externalTaskTypes: Iterable<string> = [],
|
|
121
124
|
): WorkerScaffoldPlan {
|
|
122
125
|
const wired = new Set(
|
|
123
126
|
workers
|
|
@@ -125,6 +128,7 @@ export function planWorkerScaffold(
|
|
|
125
128
|
.filter((t): t is string => typeof t === "string" && t.length > 0),
|
|
126
129
|
);
|
|
127
130
|
const declared = new Set(declaredTypeIds);
|
|
131
|
+
const external = new Set(externalTaskTypes);
|
|
128
132
|
|
|
129
133
|
const plans: WorkerStubPlan[] = [];
|
|
130
134
|
const skipped: SkippedWorker[] = [];
|
|
@@ -138,6 +142,10 @@ export function planWorkerScaffold(
|
|
|
138
142
|
skipped.push({ taskType, reason: "orchestrator" });
|
|
139
143
|
continue;
|
|
140
144
|
}
|
|
145
|
+
if (external.has(taskType)) {
|
|
146
|
+
skipped.push({ taskType, reason: "external" });
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
141
149
|
if (wired.has(taskType)) {
|
|
142
150
|
skipped.push({ taskType, reason: "already-wired" });
|
|
143
151
|
continue;
|
package/src/toolkit/scaffold.ts
CHANGED
|
@@ -19,6 +19,7 @@ interface ScaffoldManifest {
|
|
|
19
19
|
types?: Record<string, unknown>;
|
|
20
20
|
models?: { processes?: string[] };
|
|
21
21
|
workers?: ScaffoldWorker[];
|
|
22
|
+
externalTaskTypes?: string[];
|
|
22
23
|
api?: { spec?: string; dir?: string };
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -64,7 +65,12 @@ export async function scaffoldWorkers(opts: ScaffoldOptions): Promise<ScaffoldRu
|
|
|
64
65
|
const models = await readModels(root, io, manifest);
|
|
65
66
|
const declaredTypeIds = Object.keys(manifest.types ?? {});
|
|
66
67
|
|
|
67
|
-
const { plans, skipped } = planWorkerScaffold(
|
|
68
|
+
const { plans, skipped } = planWorkerScaffold(
|
|
69
|
+
models,
|
|
70
|
+
manifest.workers ?? [],
|
|
71
|
+
declaredTypeIds,
|
|
72
|
+
manifest.externalTaskTypes ?? [],
|
|
73
|
+
);
|
|
68
74
|
|
|
69
75
|
const outcomes: StubOutcome[] = [];
|
|
70
76
|
const wired: StubManifestEntry[] = [];
|