@nanobpm/urban 0.86.0 → 0.87.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/openapi/spec.d.ts +63 -5
- package/dist/openapi/spec.js +172 -3
- package/dist/runtime/core/host.d.ts +67 -9
- package/dist/runtime/core/host.js +48 -0
- package/dist/runtime/core/modules/mcp.js +5 -24
- package/dist/runtime/engine/nanosdk.js +9 -5
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +4 -0
- package/package.json +1 -1
- package/src/openapi/mcp-parity.fixture.yaml +46 -0
- package/src/openapi/mcp-tools.snapshot.json +50 -2
- package/src/openapi/spec.test.ts +221 -0
- package/src/openapi/spec.ts +204 -7
- package/src/runtime/core/host.ts +83 -9
- package/src/runtime/core/modules/mcp.test.ts +29 -0
- package/src/runtime/core/modules/mcp.ts +5 -20
- package/src/runtime/engine/nanosdk.test.ts +69 -11
- package/src/runtime/engine/nanosdk.ts +9 -5
- package/src/runtime/index.ts +8 -0
package/src/runtime/core/host.ts
CHANGED
|
@@ -296,10 +296,19 @@ export interface ElementInstanceSummary {
|
|
|
296
296
|
|
|
297
297
|
/**
|
|
298
298
|
* The kind of wait an element instance is parked in, as a
|
|
299
|
-
* `/v2/element-instances/wait-states/search` result reports it.
|
|
300
|
-
*
|
|
301
|
-
* (an event awaiting correlation), a `TIMER`, a `SIGNAL`, a `CONDITION`, as well
|
|
302
|
-
*
|
|
299
|
+
* `/v2/element-instances/wait-states/search` result reports it. This is the *canonical*
|
|
300
|
+
* six-type contract (Zeebe's, as of 2026): a `JOB` (a service task awaiting a worker), a
|
|
301
|
+
* `MESSAGE` (an event awaiting correlation), a `TIMER`, a `SIGNAL`, a `CONDITION`, as well
|
|
302
|
+
* as a `USER_TASK`.
|
|
303
|
+
*
|
|
304
|
+
* **Not every deployed engine implements the whole union.** It is forward-looking on
|
|
305
|
+
* purpose (the response mapper and {@link normalizeWaitStateType} tolerate all six so a
|
|
306
|
+
* newer engine's rows map cleanly), but the *currently deployed* nanobpmn gateway's
|
|
307
|
+
* read-model floor is narrower — see {@link DEPLOYED_WAIT_STATE_TYPES}. A
|
|
308
|
+
* {@link ElementInstanceWaitStateFilter} whose `waitStateType` falls outside that floor is
|
|
309
|
+
* rejected (the gateway answers HTTP 422); {@link assertDeployedWaitStateType} enforces the
|
|
310
|
+
* same floor client-side so the divergence surfaces as a clear error offline, in tests, and
|
|
311
|
+
* against a live engine alike. Ref: Magikcraft/nano-bpm#1042.
|
|
303
312
|
*/
|
|
304
313
|
export type WaitStateType =
|
|
305
314
|
| "JOB"
|
|
@@ -309,6 +318,59 @@ export type WaitStateType =
|
|
|
309
318
|
| "SIGNAL"
|
|
310
319
|
| "CONDITION";
|
|
311
320
|
|
|
321
|
+
/**
|
|
322
|
+
* The `waitStateType` values the *currently deployed* nanobpmn gateway's wait-state read
|
|
323
|
+
* model actually implements — the **deployed floor**, a strict subset of the canonical
|
|
324
|
+
* {@link WaitStateType} union. The gateway's read model is a snapshot of the Zeebe contract
|
|
325
|
+
* taken 2026-06-09 (the same day Zeebe extended it with `USER_TASK`, then `TIMER`/`SIGNAL`/
|
|
326
|
+
* `CONDITION`), so it recognizes only `JOB | MESSAGE` and rejects any other filter value
|
|
327
|
+
* with HTTP 422.
|
|
328
|
+
*
|
|
329
|
+
* This constant is the **single source of truth** for that floor: the SDK-backed and
|
|
330
|
+
* WASM-backed {@link EngineClient} adapters both gate through {@link assertDeployedWaitStateType},
|
|
331
|
+
* and the WASM emulation synthesizes only these park kinds — so a test cannot pass against
|
|
332
|
+
* the emulation while a real gateway would 422 (No Drift Surfaces). Widen it (adding a leg
|
|
333
|
+
* backed by the real gateway) only once the engine actually serves the richer type — the
|
|
334
|
+
* tracking gap is Magikcraft/nano-bpm#1042.
|
|
335
|
+
*/
|
|
336
|
+
export const DEPLOYED_WAIT_STATE_TYPES: readonly WaitStateType[] = ["JOB", "MESSAGE"];
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Thrown when a wait-state search is asked to filter on a {@link WaitStateType} the deployed
|
|
340
|
+
* gateway's read model does not implement (outside {@link DEPLOYED_WAIT_STATE_TYPES}). It
|
|
341
|
+
* fails fast client-side with a clear, offline-detectable error rather than letting the call
|
|
342
|
+
* reach the gateway and surface as an opaque HTTP 422 "did not match any variant of untagged
|
|
343
|
+
* enum WaitStateTypeFilterProperty". Ref: Magikcraft/nano-bpm#1042.
|
|
344
|
+
*/
|
|
345
|
+
export class UnsupportedWaitStateTypeError extends Error {
|
|
346
|
+
readonly waitStateType: string;
|
|
347
|
+
readonly supported: readonly WaitStateType[];
|
|
348
|
+
constructor(waitStateType: string) {
|
|
349
|
+
super(
|
|
350
|
+
`unsupported waitStateType "${waitStateType}": the deployed nanobpmn gateway's ` +
|
|
351
|
+
`wait-state read model implements only ${DEPLOYED_WAIT_STATE_TYPES.join(" | ")} and ` +
|
|
352
|
+
`rejects any other value with HTTP 422. Ref: Magikcraft/nano-bpm#1042.`,
|
|
353
|
+
);
|
|
354
|
+
this.name = "UnsupportedWaitStateTypeError";
|
|
355
|
+
this.waitStateType = waitStateType;
|
|
356
|
+
this.supported = DEPLOYED_WAIT_STATE_TYPES;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Assert a wait-state filter's `waitStateType` is within the deployed floor
|
|
362
|
+
* ({@link DEPLOYED_WAIT_STATE_TYPES}), throwing {@link UnsupportedWaitStateTypeError}
|
|
363
|
+
* otherwise. An absent selector is always allowed (the search is unfiltered by type). Both
|
|
364
|
+
* {@link EngineClient} adapters call this before issuing a wait-state search, so the floor is
|
|
365
|
+
* enforced identically whether the query runs against a live gateway or the in-process
|
|
366
|
+
* emulation. No-ops for an in-floor type.
|
|
367
|
+
*/
|
|
368
|
+
export function assertDeployedWaitStateType(waitStateType?: WaitStateType): void {
|
|
369
|
+
if (waitStateType !== undefined && !DEPLOYED_WAIT_STATE_TYPES.includes(waitStateType)) {
|
|
370
|
+
throw new UnsupportedWaitStateTypeError(waitStateType);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
312
374
|
/**
|
|
313
375
|
* A single parked (waiting) element instance as
|
|
314
376
|
* {@link EngineClient.searchElementInstanceWaitStates} reports it. Every variant carries the
|
|
@@ -352,6 +414,10 @@ export interface ElementInstanceFilter {
|
|
|
352
414
|
* Selectors for an element-instance *wait-state* search: which process instance, element,
|
|
353
415
|
* and/or kind of wait to match. Used by {@link EngineClient.searchElementInstanceWaitStates}.
|
|
354
416
|
* There is deliberately no lifecycle-`state` selector — a wait state is always an active park.
|
|
417
|
+
*
|
|
418
|
+
* A `waitStateType` selector must fall within the deployed floor
|
|
419
|
+
* ({@link DEPLOYED_WAIT_STATE_TYPES}); a value outside it is rejected with
|
|
420
|
+
* {@link UnsupportedWaitStateTypeError} (the live gateway answers HTTP 422).
|
|
355
421
|
*/
|
|
356
422
|
export interface ElementInstanceWaitStateFilter {
|
|
357
423
|
processInstanceKey?: string;
|
|
@@ -512,11 +578,19 @@ export interface EngineClient {
|
|
|
512
578
|
): Promise<ElementInstanceSummary[]>;
|
|
513
579
|
/**
|
|
514
580
|
* Search the *wait states* of element instances
|
|
515
|
-
* (`POST /v2/element-instances/wait-states/search`) —
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
581
|
+
* (`POST /v2/element-instances/wait-states/search`) — the parks the deployed engine's
|
|
582
|
+
* read model serves. A zero-wait read; each result is discriminated by `waitStateType`
|
|
583
|
+
* (see {@link ElementInstanceWaitState}), so a consumer can read the job/message parks a
|
|
584
|
+
* {@link searchUserTasks} cannot surface.
|
|
585
|
+
*
|
|
586
|
+
* **Deployed floor.** The currently deployed nanobpmn gateway implements only
|
|
587
|
+
* `JOB | MESSAGE` ({@link DEPLOYED_WAIT_STATE_TYPES}) — a `USER_TASK` park is read via
|
|
588
|
+
* {@link searchUserTasks}, not here. A `waitStateType` filter outside the floor is rejected
|
|
589
|
+
* with {@link UnsupportedWaitStateTypeError} (the live gateway answers HTTP 422); an
|
|
590
|
+
* unfiltered search returns only in-floor parks. The `waitStateType` union is
|
|
591
|
+
* forward-looking (richer engines may serve more), but this adapter contract pins the
|
|
592
|
+
* floor so a query cannot pass in emulation while a real engine rejects it. Ref:
|
|
593
|
+
* Magikcraft/nano-bpm#1042.
|
|
520
594
|
*/
|
|
521
595
|
searchElementInstanceWaitStates(
|
|
522
596
|
filter?: ElementInstanceWaitStateFilter,
|
|
@@ -327,6 +327,35 @@ test("a projected tool's input schema matches the spec's params", async () => {
|
|
|
327
327
|
});
|
|
328
328
|
});
|
|
329
329
|
|
|
330
|
+
test("a $ref-bodied tool projects a self-contained body schema (no $ref, explicit type)", async () => {
|
|
331
|
+
// createInvoice's requestBody is `{ $ref: "#/components/schemas/Invoice" }` — a bare $ref with no
|
|
332
|
+
// inline `type`. An MCP client cannot resolve `#/components/...` outside the document, so the
|
|
333
|
+
// projected tool schema MUST inline the concrete Invoice shape (ADR 0067). Regression guard for the
|
|
334
|
+
// leak that shipped an opaque `#/components/...` pointer into `inputSchema.properties.body`.
|
|
335
|
+
const { router } = buildHarness();
|
|
336
|
+
const session = await connect(router);
|
|
337
|
+
const list = await rpc(router, session, "tools/list", {});
|
|
338
|
+
const tools = list.result?.tools;
|
|
339
|
+
assert.ok(Array.isArray(tools));
|
|
340
|
+
const createInvoice = tools.find((t) => Reflect.get(t, "name") === "createInvoice");
|
|
341
|
+
assert.ok(createInvoice, "createInvoice must be present in tools/list before checking its schema");
|
|
342
|
+
const schema = Reflect.get(createInvoice, "inputSchema");
|
|
343
|
+
assert.deepEqual(schema, {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: {
|
|
346
|
+
body: {
|
|
347
|
+
type: "object",
|
|
348
|
+
properties: { id: { type: "string" }, amount: { type: "integer", minimum: 1 } },
|
|
349
|
+
required: ["id", "amount"],
|
|
350
|
+
additionalProperties: false,
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
required: ["body"],
|
|
354
|
+
});
|
|
355
|
+
// Self-containment holds structurally: the serialized schema carries no `$ref` token at all.
|
|
356
|
+
assert.ok(!JSON.stringify(schema).includes("$ref"), "projected body schema must not leak a $ref");
|
|
357
|
+
});
|
|
358
|
+
|
|
330
359
|
test("a tool call routes through the SAME mountApi delegate as the HTTP call", async () => {
|
|
331
360
|
let seenParams: Record<string, unknown> | undefined;
|
|
332
361
|
const { router, imported } = buildHarness({
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
parseSpec,
|
|
56
56
|
type SecurityDecision,
|
|
57
57
|
sharedSecretSchemeName,
|
|
58
|
+
toolInputSchema,
|
|
58
59
|
} from "../../../openapi/spec.ts";
|
|
59
60
|
import type { AppApi, RuntimeContext } from "../context.ts";
|
|
60
61
|
import { errorMessage } from "../guards.ts";
|
|
@@ -250,26 +251,10 @@ function isMutatingOperation(op: OperationInfo): boolean {
|
|
|
250
251
|
return isMutatingMethod(op.method);
|
|
251
252
|
}
|
|
252
253
|
|
|
253
|
-
/** A JSON Schema (OpenAPI object subset) describing a tool's input
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
|
|
257
|
-
const properties: Record<string, unknown> = {};
|
|
258
|
-
const required: string[] = [];
|
|
259
|
-
for (const p of op.parameters) {
|
|
260
|
-
if (p.in === "path" || p.in === "query") {
|
|
261
|
-
properties[p.name] = p.schema ?? {};
|
|
262
|
-
if (p.required) required.push(p.name);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (op.requestBodySchema) {
|
|
266
|
-
properties.body = op.requestBodySchema;
|
|
267
|
-
if (op.requestBodyRequired) required.push("body");
|
|
268
|
-
}
|
|
269
|
-
const schema: Record<string, unknown> = { type: "object", properties };
|
|
270
|
-
if (required.length > 0) schema.required = required;
|
|
271
|
-
return schema;
|
|
272
|
-
}
|
|
254
|
+
/** A JSON Schema (OpenAPI object subset) describing a tool's input is derived once, in
|
|
255
|
+
* `openapi/spec.ts`'s {@link toolInputSchema}, from an operation's resolved (`$ref`-free) parameter
|
|
256
|
+
* and request-body metadata — so the tool input schema the runtime advertises and the schema the CI
|
|
257
|
+
* parity guard pins are the SAME derivation (one source of truth, ADR 0053/0067). */
|
|
273
258
|
|
|
274
259
|
/** Substitute an operation's `{param}` path-template segments with the supplied arguments, building
|
|
275
260
|
* the request path under the shared `/app/api` base so dispatch hits the exact `mountApi` route. */
|
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
type NanoSdkJobWorkerConfig,
|
|
14
14
|
} from "./nanosdk.ts";
|
|
15
15
|
import { BpmnError } from "../core/host.ts";
|
|
16
|
+
import {
|
|
17
|
+
assertDeployedWaitStateType,
|
|
18
|
+
DEPLOYED_WAIT_STATE_TYPES,
|
|
19
|
+
UnsupportedWaitStateTypeError,
|
|
20
|
+
} from "../core/host.ts";
|
|
16
21
|
import { __resetExecStoreForTests, installExecStore, runInJobContext } from "../core/execContext.ts";
|
|
17
22
|
import { readLineage } from "../core/lineage.ts";
|
|
18
23
|
import { createNodeHost } from "../adapters/node.ts";
|
|
@@ -498,6 +503,27 @@ test("normalizeWaitStateType maps the wait-state kinds (case-insensitive) and re
|
|
|
498
503
|
assert.equal(normalizeWaitStateType(3), undefined);
|
|
499
504
|
});
|
|
500
505
|
|
|
506
|
+
test("assertDeployedWaitStateType: allows the floor and an absent selector, rejects the rest", () => {
|
|
507
|
+
// The deployed floor is exactly JOB|MESSAGE — the single source of truth both adapters gate on.
|
|
508
|
+
assert.deepEqual([...DEPLOYED_WAIT_STATE_TYPES], ["JOB", "MESSAGE"]);
|
|
509
|
+
// In-floor and absent selectors are no-ops (no throw).
|
|
510
|
+
assert.doesNotThrow(() => assertDeployedWaitStateType(undefined));
|
|
511
|
+
assert.doesNotThrow(() => assertDeployedWaitStateType("JOB"));
|
|
512
|
+
assert.doesNotThrow(() => assertDeployedWaitStateType("MESSAGE"));
|
|
513
|
+
// Every canonical type outside the floor is rejected with a typed, descriptive error.
|
|
514
|
+
for (const waitStateType of ["USER_TASK", "TIMER", "SIGNAL", "CONDITION"] as const) {
|
|
515
|
+
assert.throws(
|
|
516
|
+
() => assertDeployedWaitStateType(waitStateType),
|
|
517
|
+
(err: unknown) => {
|
|
518
|
+
assert.ok(err instanceof UnsupportedWaitStateTypeError);
|
|
519
|
+
assert.equal(err.waitStateType, waitStateType);
|
|
520
|
+
assert.match(err.message, /HTTP 422/);
|
|
521
|
+
return true;
|
|
522
|
+
},
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
501
527
|
test("searchElementInstances passes zero-wait consistency, maps rows, and forwards selectors", async () => {
|
|
502
528
|
let seenInput: unknown;
|
|
503
529
|
let seenConsistency: unknown;
|
|
@@ -650,35 +676,67 @@ test("searchElementInstanceWaitStates forwards a waitStateType selector and read
|
|
|
650
676
|
{
|
|
651
677
|
elementInstanceKey: "9",
|
|
652
678
|
processInstanceKey: "3",
|
|
653
|
-
elementId: "
|
|
654
|
-
waitStateType: "
|
|
655
|
-
|
|
679
|
+
elementId: "catch",
|
|
680
|
+
waitStateType: "MESSAGE",
|
|
681
|
+
messageName: "Go",
|
|
682
|
+
correlationKey: "K1",
|
|
656
683
|
},
|
|
657
684
|
{
|
|
658
685
|
elementInstanceKey: "11",
|
|
659
686
|
processInstanceKey: "3",
|
|
660
|
-
elementId: "
|
|
661
|
-
waitStateType: "
|
|
662
|
-
}, //
|
|
687
|
+
elementId: "catch2",
|
|
688
|
+
waitStateType: "MESSAGE",
|
|
689
|
+
}, // MESSAGE with no messageName → dropped (invalid identity)
|
|
663
690
|
{ elementInstanceKey: "10", processInstanceKey: "3", elementId: "?" }, // no waitStateType → dropped
|
|
664
691
|
],
|
|
665
692
|
};
|
|
666
693
|
},
|
|
667
694
|
});
|
|
668
695
|
const engine = new SdkEngineClient(client);
|
|
669
|
-
const out = await engine.searchElementInstanceWaitStates({ waitStateType: "
|
|
670
|
-
assert.deepEqual(seenInput, { filter: { waitStateType: "
|
|
696
|
+
const out = await engine.searchElementInstanceWaitStates({ waitStateType: "MESSAGE" });
|
|
697
|
+
assert.deepEqual(seenInput, { filter: { waitStateType: "MESSAGE" } });
|
|
671
698
|
assert.deepEqual(out, [
|
|
672
699
|
{
|
|
673
700
|
elementInstanceKey: "9",
|
|
674
701
|
processInstanceKey: "3",
|
|
675
|
-
elementId: "
|
|
676
|
-
waitStateType: "
|
|
677
|
-
|
|
702
|
+
elementId: "catch",
|
|
703
|
+
waitStateType: "MESSAGE",
|
|
704
|
+
messageName: "Go",
|
|
705
|
+
correlationKey: "K1",
|
|
678
706
|
},
|
|
679
707
|
]);
|
|
680
708
|
});
|
|
681
709
|
|
|
710
|
+
test("searchElementInstanceWaitStates rejects a waitStateType outside the deployed floor without calling the gateway", async () => {
|
|
711
|
+
// The deployed gateway's read model implements only JOB|MESSAGE and 422s any other filter
|
|
712
|
+
// value. The SDK adapter must fail fast client-side (not issue the call), so an app authoring
|
|
713
|
+
// `waitStateType: "USER_TASK"` cannot ship green then blow up at runtime (issue
|
|
714
|
+
// nanobpm/nano-ide#497).
|
|
715
|
+
let called = false;
|
|
716
|
+
const client = fakeSdkClient({
|
|
717
|
+
searchElementInstanceWaitStates: async () => {
|
|
718
|
+
called = true;
|
|
719
|
+
return { items: [] };
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
const engine = new SdkEngineClient(client);
|
|
723
|
+
for (const waitStateType of ["USER_TASK", "TIMER", "SIGNAL", "CONDITION"] as const) {
|
|
724
|
+
await assert.rejects(
|
|
725
|
+
engine.searchElementInstanceWaitStates({ waitStateType }),
|
|
726
|
+
(err: unknown) => {
|
|
727
|
+
assert.ok(err instanceof Error);
|
|
728
|
+
assert.match(err.message, /unsupported waitStateType/);
|
|
729
|
+
assert.match(err.message, new RegExp(waitStateType));
|
|
730
|
+
return true;
|
|
731
|
+
},
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
assert.equal(called, false, "the gateway must not be called for an out-of-floor filter");
|
|
735
|
+
// An in-floor filter still reaches the gateway.
|
|
736
|
+
await engine.searchElementInstanceWaitStates({ waitStateType: "JOB" });
|
|
737
|
+
assert.equal(called, true);
|
|
738
|
+
});
|
|
739
|
+
|
|
682
740
|
test("searchElementInstanceWaitStates drops parks missing their required discriminator field", async () => {
|
|
683
741
|
const client = fakeSdkClient({
|
|
684
742
|
searchElementInstanceWaitStates: async () => ({
|
|
@@ -32,7 +32,7 @@ import type {
|
|
|
32
32
|
UserTaskFilter,
|
|
33
33
|
WorkerSubscription,
|
|
34
34
|
} from "../core/host.ts";
|
|
35
|
-
import { isBpmnError } from "../core/host.ts";
|
|
35
|
+
import { assertDeployedWaitStateType, isBpmnError } from "../core/host.ts";
|
|
36
36
|
import {
|
|
37
37
|
buildFormSchema,
|
|
38
38
|
parseFormSchema,
|
|
@@ -709,10 +709,14 @@ export class SdkEngineClient implements EngineClient {
|
|
|
709
709
|
async searchElementInstanceWaitStates(
|
|
710
710
|
filter?: ElementInstanceWaitStateFilter,
|
|
711
711
|
): Promise<ElementInstanceWaitState[]> {
|
|
712
|
-
// The wait-states search surfaces
|
|
713
|
-
//
|
|
714
|
-
//
|
|
715
|
-
//
|
|
712
|
+
// The wait-states search surfaces the parks the deployed engine's read model serves.
|
|
713
|
+
// A `waitStateType` selector outside the deployed floor (`JOB | MESSAGE`) is rejected by
|
|
714
|
+
// the gateway with HTTP 422; fail fast client-side with a clear error instead, so the
|
|
715
|
+
// divergence is caught offline and identically to the WASM adapter (No Drift Surfaces).
|
|
716
|
+
assertDeployedWaitStateType(filter?.waitStateType);
|
|
717
|
+
// Same zero-wait read + per-row mapping-gate shape as `searchElementInstances`; the SDK
|
|
718
|
+
// nests park-specific fields under `details`, which `mapElementInstanceWaitStateRow`
|
|
719
|
+
// unwraps.
|
|
716
720
|
const f: Record<string, unknown> = {};
|
|
717
721
|
if (filter?.processInstanceKey) f.processInstanceKey = filter.processInstanceKey;
|
|
718
722
|
if (filter?.elementId) f.elementId = filter.elementId;
|
package/src/runtime/index.ts
CHANGED
|
@@ -133,6 +133,14 @@ export { BpmnError, isBpmnError } from "./core/host.ts";
|
|
|
133
133
|
// double / adapter iterates it to prove at runtime that it implements the full interface,
|
|
134
134
|
// catching the "fake lags the SDK" seam-lag class categorically.
|
|
135
135
|
export { ENGINE_CLIENT_METHODS } from "./core/host.ts";
|
|
136
|
+
// Deployed wait-state floor (issue nanobpm/nano-ide#497): the `waitStateType` values the
|
|
137
|
+
// deployed gateway actually serves, plus the shared guard both EngineClient adapters use so
|
|
138
|
+
// a filter outside the floor fails identically in emulation and against a live engine.
|
|
139
|
+
export {
|
|
140
|
+
assertDeployedWaitStateType,
|
|
141
|
+
DEPLOYED_WAIT_STATE_TYPES,
|
|
142
|
+
UnsupportedWaitStateTypeError,
|
|
143
|
+
} from "./core/host.ts";
|
|
136
144
|
|
|
137
145
|
// Connector packs (ADR 0050, in-process port)
|
|
138
146
|
export {
|