@nanobpm/urban 0.72.0 → 0.72.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.
|
@@ -85,11 +85,19 @@ export async function resolveInstalledConnectors(ctx) {
|
|
|
85
85
|
* error boundary by the engine adapter); `job.fail(msg)` a plain throw (retry). */
|
|
86
86
|
export function adaptConnectorHandler(worker) {
|
|
87
87
|
return async (job) => {
|
|
88
|
+
// A connector job always belongs to a process instance; a missing key is a
|
|
89
|
+
// protocol fault. Fail loud (the engine retries / raises an incident) rather
|
|
90
|
+
// than handing the connector a fabricated "" that would silently corrupt any
|
|
91
|
+
// instance-scoped call it makes.
|
|
92
|
+
const processInstanceKey = job.processInstanceKey;
|
|
93
|
+
if (processInstanceKey === undefined || processInstanceKey === "") {
|
|
94
|
+
throw new Error(`connector job '${job.jobType}' (${job.jobKey}) has no processInstanceKey`);
|
|
95
|
+
}
|
|
88
96
|
let outcome;
|
|
89
97
|
const facade = {
|
|
90
98
|
jobKey: job.jobKey,
|
|
91
99
|
type: job.jobType,
|
|
92
|
-
processInstanceKey
|
|
100
|
+
processInstanceKey,
|
|
93
101
|
elementId: job.elementId,
|
|
94
102
|
variables: job.variables,
|
|
95
103
|
async complete(vars) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/urban",
|
|
3
|
-
"version": "0.72.
|
|
3
|
+
"version": "0.72.1",
|
|
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",
|
|
@@ -145,6 +145,7 @@ test("mountConnectors imports the pack entry and registers the drained worker",
|
|
|
145
145
|
const out = await engine.deliver("slack:send-message", {
|
|
146
146
|
jobKey: "j1",
|
|
147
147
|
jobType: "slack:send-message",
|
|
148
|
+
processInstanceKey: "pi1",
|
|
148
149
|
variables: { text: "hi" },
|
|
149
150
|
});
|
|
150
151
|
assert.deepEqual(out, { echoed: "hi" });
|
|
@@ -214,7 +215,7 @@ test("mountConnectors fails closed on a duplicate worker type in one pack entry"
|
|
|
214
215
|
});
|
|
215
216
|
|
|
216
217
|
test("adaptConnectorHandler maps complete/fail/error(BpmnError) and a returned value", async () => {
|
|
217
|
-
const job: EngineJob = { jobKey: "j", jobType: "t", variables: {} };
|
|
218
|
+
const job: EngineJob = { jobKey: "j", jobType: "t", processInstanceKey: "pi", variables: {} };
|
|
218
219
|
|
|
219
220
|
const completes = adaptConnectorHandler({ type: "t", handle: async (j) => void (await j.complete({ a: 1 })) });
|
|
220
221
|
assert.deepEqual(await completes(job), { a: 1 });
|
|
@@ -231,3 +232,22 @@ test("adaptConnectorHandler maps complete/fail/error(BpmnError) and a returned v
|
|
|
231
232
|
(e: unknown) => e instanceof BpmnError && e.errorCode === "CODE",
|
|
232
233
|
);
|
|
233
234
|
});
|
|
235
|
+
|
|
236
|
+
test("adaptConnectorHandler fails loud on a job with no processInstanceKey (no masking to '')", async () => {
|
|
237
|
+
// Guard for the decode-boundary defect class (Magikcraft/nano-bpm#940): a
|
|
238
|
+
// keyless job must reject, never invoke the connector with a fabricated "".
|
|
239
|
+
let handlerRan = false;
|
|
240
|
+
const h = adaptConnectorHandler({
|
|
241
|
+
type: "t",
|
|
242
|
+
handle: async () => {
|
|
243
|
+
handlerRan = true;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
const keyless: EngineJob = { jobKey: "j", jobType: "t", variables: {} };
|
|
247
|
+
await assert.rejects(() => Promise.resolve(h(keyless)), /no processInstanceKey/);
|
|
248
|
+
assert.equal(handlerRan, false, "connector handler must not run for a keyless job");
|
|
249
|
+
|
|
250
|
+
// An empty-string key is the exact masking source and is rejected too.
|
|
251
|
+
const emptyKey: EngineJob = { jobKey: "j", jobType: "t", processInstanceKey: "", variables: {} };
|
|
252
|
+
await assert.rejects(() => Promise.resolve(h(emptyKey)), /no processInstanceKey/);
|
|
253
|
+
});
|
|
@@ -134,6 +134,14 @@ export async function resolveInstalledConnectors(
|
|
|
134
134
|
* error boundary by the engine adapter); `job.fail(msg)` a plain throw (retry). */
|
|
135
135
|
export function adaptConnectorHandler(worker: DefinedConnectorWorker): JobHandler {
|
|
136
136
|
return async (job: EngineJob) => {
|
|
137
|
+
// A connector job always belongs to a process instance; a missing key is a
|
|
138
|
+
// protocol fault. Fail loud (the engine retries / raises an incident) rather
|
|
139
|
+
// than handing the connector a fabricated "" that would silently corrupt any
|
|
140
|
+
// instance-scoped call it makes.
|
|
141
|
+
const processInstanceKey = job.processInstanceKey;
|
|
142
|
+
if (processInstanceKey === undefined || processInstanceKey === "") {
|
|
143
|
+
throw new Error(`connector job '${job.jobType}' (${job.jobKey}) has no processInstanceKey`);
|
|
144
|
+
}
|
|
137
145
|
let outcome:
|
|
138
146
|
| { kind: "complete"; vars?: Record<string, unknown> }
|
|
139
147
|
| { kind: "fail"; message: string }
|
|
@@ -142,7 +150,7 @@ export function adaptConnectorHandler(worker: DefinedConnectorWorker): JobHandle
|
|
|
142
150
|
const facade: ConnectorWorkerJob = {
|
|
143
151
|
jobKey: job.jobKey,
|
|
144
152
|
type: job.jobType,
|
|
145
|
-
processInstanceKey
|
|
153
|
+
processInstanceKey,
|
|
146
154
|
elementId: job.elementId,
|
|
147
155
|
variables: job.variables,
|
|
148
156
|
async complete(vars) {
|