@nanobpm/urban 0.90.2 → 0.92.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.
- package/dist/runtime/core/host.d.ts +17 -13
- package/dist/runtime/core/host.js +7 -5
- package/dist/runtime/core/modules/runtime.gen.js +1 -1
- package/dist/runtime/engine/nanosdk.js +4 -3
- package/package.json +1 -1
- package/src/runtime/browser/runtime.browser.js +9 -1
- package/src/runtime/browser/runtime.engineform.test.ts +100 -0
- package/src/runtime/core/host.ts +18 -14
- package/src/runtime/core/modules/runtime.gen.ts +1 -1
- package/src/runtime/engine/nanosdk.test.ts +58 -10
- package/src/runtime/engine/nanosdk.ts +4 -3
|
@@ -640,14 +640,16 @@ test("normalizeWaitStateType maps the wait-state kinds (case-insensitive) and re
|
|
|
640
640
|
});
|
|
641
641
|
|
|
642
642
|
test("assertDeployedWaitStateType: allows the floor and an absent selector, rejects the rest", () => {
|
|
643
|
-
// The deployed floor is exactly JOB|MESSAGE — the single source of truth both
|
|
644
|
-
|
|
643
|
+
// The deployed floor is exactly JOB|MESSAGE|USER_TASK — the single source of truth both
|
|
644
|
+
// adapters gate on (USER_TASK shipped in Magikcraft/nano-bpm#1042).
|
|
645
|
+
assert.deepEqual([...DEPLOYED_WAIT_STATE_TYPES], ["JOB", "MESSAGE", "USER_TASK"]);
|
|
645
646
|
// In-floor and absent selectors are no-ops (no throw).
|
|
646
647
|
assert.doesNotThrow(() => assertDeployedWaitStateType(undefined));
|
|
647
648
|
assert.doesNotThrow(() => assertDeployedWaitStateType("JOB"));
|
|
648
649
|
assert.doesNotThrow(() => assertDeployedWaitStateType("MESSAGE"));
|
|
649
|
-
|
|
650
|
-
|
|
650
|
+
assert.doesNotThrow(() => assertDeployedWaitStateType("USER_TASK"));
|
|
651
|
+
// Every canonical type still outside the floor is rejected with a typed, descriptive error.
|
|
652
|
+
for (const waitStateType of ["TIMER", "SIGNAL", "CONDITION"] as const) {
|
|
651
653
|
assert.throws(
|
|
652
654
|
() => assertDeployedWaitStateType(waitStateType),
|
|
653
655
|
(err: unknown) => {
|
|
@@ -843,11 +845,56 @@ test("searchElementInstanceWaitStates forwards a waitStateType selector and read
|
|
|
843
845
|
]);
|
|
844
846
|
});
|
|
845
847
|
|
|
848
|
+
test("searchElementInstanceWaitStates maps a USER_TASK park, unwrapping details.taskKey → userTaskKey (mock SDK client, not a live gateway)", async () => {
|
|
849
|
+
// MOCK-ONLY LEG: this exercises the SDK adapter's row mapping against a `fakeSdkClient`, not
|
|
850
|
+
// a live gateway — the canonical parity assertion against a real engine lives in the
|
|
851
|
+
// adapter-agnostic `runEngineClientContract` USER_TASK leg (contract.ts), run against the
|
|
852
|
+
// live `SdkEngineClient` in the integration lane. Here we only pin the shape the engine's
|
|
853
|
+
// wait-state read model returns for a USER_TASK park (Magikcraft/nano-bpm#1042): the park's
|
|
854
|
+
// `taskKey` under `details` unwraps to the seam's `userTaskKey`.
|
|
855
|
+
let seenInput: unknown;
|
|
856
|
+
const client = fakeSdkClient({
|
|
857
|
+
searchElementInstanceWaitStates: async (input) => {
|
|
858
|
+
seenInput = input;
|
|
859
|
+
return {
|
|
860
|
+
items: [
|
|
861
|
+
{
|
|
862
|
+
elementInstanceKey: 7,
|
|
863
|
+
processInstanceKey: 3,
|
|
864
|
+
elementId: "review",
|
|
865
|
+
type: "USER_TASK",
|
|
866
|
+
details: { waitStateType: "USER_TASK", taskKey: 90 },
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
elementInstanceKey: 8,
|
|
870
|
+
processInstanceKey: 3,
|
|
871
|
+
elementId: "review2",
|
|
872
|
+
details: { waitStateType: "USER_TASK" },
|
|
873
|
+
}, // USER_TASK with no taskKey → dropped (missing required identity)
|
|
874
|
+
],
|
|
875
|
+
};
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
const engine = new SdkEngineClient(client);
|
|
879
|
+
const out = await engine.searchElementInstanceWaitStates({ processInstanceKey: "3", waitStateType: "USER_TASK" });
|
|
880
|
+
assert.deepEqual(seenInput, { filter: { processInstanceKey: "3", waitStateType: "USER_TASK" } });
|
|
881
|
+
assert.deepEqual(out, [
|
|
882
|
+
{
|
|
883
|
+
elementInstanceKey: "7",
|
|
884
|
+
processInstanceKey: "3",
|
|
885
|
+
elementId: "review",
|
|
886
|
+
elementType: "USER_TASK",
|
|
887
|
+
waitStateType: "USER_TASK",
|
|
888
|
+
userTaskKey: "90",
|
|
889
|
+
},
|
|
890
|
+
]);
|
|
891
|
+
});
|
|
892
|
+
|
|
846
893
|
test("searchElementInstanceWaitStates rejects a waitStateType outside the deployed floor without calling the gateway", async () => {
|
|
847
|
-
// The deployed gateway's read model implements
|
|
848
|
-
// value. The SDK adapter must fail fast client-side (not issue the call), so an app
|
|
849
|
-
// `waitStateType: "
|
|
850
|
-
// nanobpm/nano-ide#497).
|
|
894
|
+
// The deployed gateway's read model implements JOB|MESSAGE|USER_TASK and 422s any other
|
|
895
|
+
// filter value. The SDK adapter must fail fast client-side (not issue the call), so an app
|
|
896
|
+
// authoring `waitStateType: "TIMER"` cannot ship green then blow up at runtime (issues
|
|
897
|
+
// nanobpm/nano-ide#497, #498).
|
|
851
898
|
let called = false;
|
|
852
899
|
const client = fakeSdkClient({
|
|
853
900
|
searchElementInstanceWaitStates: async () => {
|
|
@@ -856,7 +903,7 @@ test("searchElementInstanceWaitStates rejects a waitStateType outside the deploy
|
|
|
856
903
|
},
|
|
857
904
|
});
|
|
858
905
|
const engine = new SdkEngineClient(client);
|
|
859
|
-
for (const waitStateType of ["
|
|
906
|
+
for (const waitStateType of ["TIMER", "SIGNAL", "CONDITION"] as const) {
|
|
860
907
|
await assert.rejects(
|
|
861
908
|
engine.searchElementInstanceWaitStates({ waitStateType }),
|
|
862
909
|
(err: unknown) => {
|
|
@@ -868,8 +915,9 @@ test("searchElementInstanceWaitStates rejects a waitStateType outside the deploy
|
|
|
868
915
|
);
|
|
869
916
|
}
|
|
870
917
|
assert.equal(called, false, "the gateway must not be called for an out-of-floor filter");
|
|
871
|
-
//
|
|
918
|
+
// Every in-floor filter still reaches the gateway.
|
|
872
919
|
await engine.searchElementInstanceWaitStates({ waitStateType: "JOB" });
|
|
920
|
+
await engine.searchElementInstanceWaitStates({ waitStateType: "USER_TASK" });
|
|
873
921
|
assert.equal(called, true);
|
|
874
922
|
});
|
|
875
923
|
|
|
@@ -867,9 +867,10 @@ export class SdkEngineClient implements EngineClient {
|
|
|
867
867
|
filter?: ElementInstanceWaitStateFilter,
|
|
868
868
|
): Promise<ElementInstanceWaitState[]> {
|
|
869
869
|
// The wait-states search surfaces the parks the deployed engine's read model serves.
|
|
870
|
-
// A `waitStateType` selector outside the deployed floor (`JOB | MESSAGE`) is
|
|
871
|
-
// the gateway with HTTP 422; fail fast client-side with a clear error instead,
|
|
872
|
-
// divergence is caught offline and identically to the WASM adapter (No Drift
|
|
870
|
+
// A `waitStateType` selector outside the deployed floor (`JOB | MESSAGE | USER_TASK`) is
|
|
871
|
+
// rejected by the gateway with HTTP 422; fail fast client-side with a clear error instead,
|
|
872
|
+
// so the divergence is caught offline and identically to the WASM adapter (No Drift
|
|
873
|
+
// Surfaces).
|
|
873
874
|
assertDeployedWaitStateType(filter?.waitStateType);
|
|
874
875
|
// Same zero-wait read + per-row mapping-gate shape as `searchElementInstances`; the SDK
|
|
875
876
|
// nests park-specific fields under `details`, which `mapElementInstanceWaitStateRow`
|