@nanobpm/nano-ide-app-embedded-nano 1.0.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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "embedded-nano",
3
+ "kind": "app",
4
+ "displayName": "Embedded μ-nano app",
5
+ "fileTypes": [],
6
+ "templates": [
7
+ { "id": "embedded-starter", "label": "Embedded engine — self-contained binary, no gateway (ADR 0005)" }
8
+ ],
9
+ "toolchain": {}
10
+ }
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@nanobpm/nano-ide-app-embedded-nano",
3
+ "version": "1.0.0",
4
+ "description": "Embedded μ-nano app pack for the Nano RAD IDE: compile a self-contained Deno binary with the Nano BPMN engine running in-process (ADR 0005), no gateway required.",
5
+ "license": "Apache-2.0",
6
+ "keywords": ["nano-ide-ext", "nano-ide-app", "nanobpm", "embedded"],
7
+ "publishConfig": { "access": "public" },
8
+ "repository": { "type": "git", "url": "https://github.com/jwulf/nano-ide.git", "directory": "packages/app-embedded-nano" },
9
+ "files": ["nano-ide.ext.json", "templates"]
10
+ }
@@ -0,0 +1,27 @@
1
+ # Embedded μ-nano starter
2
+
3
+ A self-contained process app: the Nano BPMN engine runs **in this process** via
4
+ WebAssembly (ADR 0005). No gateway, no sockets. `deno compile` ships engine + app
5
+ as one binary.
6
+
7
+ ## Run
8
+
9
+ ```sh
10
+ deno task start # N=1000 by default; override with N=5000 deno task start
11
+ ```
12
+
13
+ ## Compile to a single binary
14
+
15
+ ```sh
16
+ deno task compile # -> ./embedded-app (includes engine wasm + BPMN)
17
+ ./embedded-app
18
+ ```
19
+
20
+ ## How it works
21
+
22
+ - `engine/unano_bg.wasm` is `engine-core` compiled to wasm; `engine/host.ts` wraps it
23
+ as an `EmbeddedHost` (deploy / createInstance / activateJobs / complete / tick).
24
+ - `@nanobpm/nano-sdk` with `CAMUNDA_TRANSPORT=embedded` binds the host directly, so the
25
+ same SDK code that talks to a real cluster drives the in-process engine.
26
+ - The host injects `Date.now()` via `tickNow`, so the clock-free engine-core runs as a
27
+ real wall-clock runtime.
@@ -0,0 +1,7 @@
1
+ {
2
+ "imports": { "@nanobpm/nano-sdk": "npm:@nanobpm/nano-sdk@^1" },
3
+ "tasks": {
4
+ "start": "deno run --allow-read --allow-env main.ts",
5
+ "compile": "deno compile --allow-read --allow-env --include engine --include resources --output embedded-app main.ts"
6
+ }
7
+ }
@@ -0,0 +1,73 @@
1
+ // EmbeddedHost (ADR 0005, realization (a) "in-process direct"). Wraps μ-nano.wasm
2
+ // (engine-core compiled to wasm) and implements the EmbeddedHost contract the
3
+ // nano-sdk-js embedded transport binds to. engine-core stays clock-free; this host
4
+ // injects Date.now() via tickNow so the engine runs as a real, wall-clock runtime.
5
+ import { TestEngine } from "./unano.js";
6
+
7
+ export interface EmbeddedJob {
8
+ jobKey: string;
9
+ type: string;
10
+ processInstanceKey: string;
11
+ elementId: string;
12
+ retries: number;
13
+ variables: Record<string, unknown>;
14
+ }
15
+
16
+ export class EmbeddedHost {
17
+ private engine: TestEngine;
18
+ private constructor(engine: TestEngine) {
19
+ this.engine = engine;
20
+ }
21
+
22
+ /** Boot the wasm engine. The deno-target unano.js instantiates μ-nano.wasm at
23
+ * import time (top-level await), so there is nothing else to load. */
24
+ static async create(): Promise<EmbeddedHost> {
25
+ return new EmbeddedHost(new TestEngine());
26
+ }
27
+
28
+ async deploy(xml: string): Promise<{ processIds: string[] }> {
29
+ const r = JSON.parse(this.engine.deploy(xml));
30
+ return { processIds: r.processIds ?? [] };
31
+ }
32
+
33
+ async createInstance(input: { processDefinitionId?: string; variables?: Record<string, unknown> }): Promise<{ processInstanceKey: string }> {
34
+ const snap = JSON.parse(this.engine.createInstance(input.processDefinitionId ?? "", JSON.stringify(input.variables ?? {})));
35
+ return { processInstanceKey: String(snap.created) };
36
+ }
37
+
38
+ async activateJobs(type: string, max: number, timeoutMs: number, worker: string): Promise<EmbeddedJob[]> {
39
+ const jobs = JSON.parse(this.engine.activateJobs(type, max, timeoutMs, worker)) as any[];
40
+ return jobs.map((j) => ({
41
+ jobKey: String(j.key),
42
+ type: j.type,
43
+ processInstanceKey: String(j.instanceKey ?? ""),
44
+ elementId: j.elementId ?? "",
45
+ retries: j.retries ?? 3,
46
+ variables: j.variables ?? {},
47
+ }));
48
+ }
49
+
50
+ async completeJob(jobKey: string, variables?: Record<string, unknown>): Promise<void> {
51
+ this.engine.completeJob(jobKey, JSON.stringify(variables ?? {}));
52
+ }
53
+
54
+ async failJob(jobKey: string, retries: number, errorMessage?: string): Promise<void> {
55
+ this.engine.failJob(jobKey, retries, errorMessage ?? "");
56
+ }
57
+
58
+ instanceCompleted(key: string): boolean {
59
+ const snap = JSON.parse(this.engine.snapshot());
60
+ const inst = (snap.instances ?? []).find((i: any) => String(i.key) === key);
61
+ return !inst || inst.completed === true;
62
+ }
63
+
64
+ instanceVariables(key: string): Record<string, unknown> {
65
+ const snap = JSON.parse(this.engine.snapshot());
66
+ const inst = (snap.instances ?? []).find((i: any) => String(i.key) === key);
67
+ return inst?.variables ?? {};
68
+ }
69
+
70
+ tick(): void {
71
+ this.engine.tickNow(Date.now());
72
+ }
73
+ }
@@ -0,0 +1,70 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * A simulated engine instance bound to one modeler session.
6
+ */
7
+ export class TestEngine {
8
+ free(): void;
9
+ [Symbol.dispose](): void;
10
+ /**
11
+ * Activate up to `max_jobs` `Created` jobs of `job_type`, locking them to
12
+ * `worker` until `now + timeout_ms`. Returns a JSON array of activated jobs
13
+ * (key, type, instance/element, retries, variables) for the dispatch loop to
14
+ * hand to worker handlers. The host owns the wall clock via `tickNow`.
15
+ */
16
+ activateJobs(job_type: string, max_jobs: number, timeout_ms: number, worker: string): string;
17
+ /**
18
+ * Advance the virtual clock by `by_ms` milliseconds, firing any timers that
19
+ * become due and expiring any lapsed job locks.
20
+ */
21
+ advanceTime(by_ms: number): string;
22
+ /**
23
+ * Complete a waiting job by key, merging `variables_json` (a JSON object
24
+ * string) into the instance. The job is activated first if it has not been
25
+ * already, so the UI can complete a freshly-created job directly.
26
+ */
27
+ completeJob(job_key: string, variables_json: string): string;
28
+ /**
29
+ * Start a new instance of `process_id`, seeding it with the given variables
30
+ * (a JSON object string; pass `"{}"` or `""` for none). Returns the
31
+ * post-run [`Snapshot`] with a top-level `created` field holding the new
32
+ * instance key.
33
+ */
34
+ createInstance(process_id: string, variables_json: string): string;
35
+ /**
36
+ * Parse and deploy a BPMN resource. Returns a JSON object
37
+ * `{ "processIds": [...], "snapshot": {...} }` on success, or throws a
38
+ * JS error carrying the parse/deploy failure message.
39
+ */
40
+ deploy(xml: string): string;
41
+ /**
42
+ * The full ordered event log emitted so far, as a JSON array of
43
+ * `{ seq, now, type, ...payload }`. Useful for a step-through / trace view.
44
+ */
45
+ events(): string;
46
+ /**
47
+ * Fail a waiting job by key with the given remaining `retries` and message.
48
+ * With no retries left this raises an incident (visible in the snapshot).
49
+ */
50
+ failJob(job_key: string, retries: number, message: string): string;
51
+ /**
52
+ * Create a fresh, empty simulated engine. The virtual clock starts at 0.
53
+ */
54
+ constructor();
55
+ /**
56
+ * The current simulation state as a JSON [`Snapshot`].
57
+ */
58
+ snapshot(): string;
59
+ /**
60
+ * Set the engine clock to a wall-clock instant (ms), then trigger due timers
61
+ * and expire lapsed job locks. The embedded host calls this with `Date.now()`
62
+ * so `engine-core` stays clock-free while running as a real runtime. The
63
+ * clock never moves backwards. Returns the snapshot.
64
+ */
65
+ tickNow(now_ms: number): string;
66
+ /**
67
+ * The current virtual clock (milliseconds).
68
+ */
69
+ readonly now: number;
70
+ }
@@ -0,0 +1,456 @@
1
+ /* @ts-self-types="./unano.d.ts" */
2
+
3
+ /**
4
+ * A simulated engine instance bound to one modeler session.
5
+ */
6
+ export class TestEngine {
7
+ __destroy_into_raw() {
8
+ const ptr = this.__wbg_ptr;
9
+ this.__wbg_ptr = 0;
10
+ TestEngineFinalization.unregister(this);
11
+ return ptr;
12
+ }
13
+ free() {
14
+ const ptr = this.__destroy_into_raw();
15
+ wasm.__wbg_testengine_free(ptr, 0);
16
+ }
17
+ /**
18
+ * Activate up to `max_jobs` `Created` jobs of `job_type`, locking them to
19
+ * `worker` until `now + timeout_ms`. Returns a JSON array of activated jobs
20
+ * (key, type, instance/element, retries, variables) for the dispatch loop to
21
+ * hand to worker handlers. The host owns the wall clock via `tickNow`.
22
+ * @param {string} job_type
23
+ * @param {number} max_jobs
24
+ * @param {number} timeout_ms
25
+ * @param {string} worker
26
+ * @returns {string}
27
+ */
28
+ activateJobs(job_type, max_jobs, timeout_ms, worker) {
29
+ let deferred4_0;
30
+ let deferred4_1;
31
+ try {
32
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
33
+ const ptr0 = passStringToWasm0(job_type, wasm.__wbindgen_export, wasm.__wbindgen_export2);
34
+ const len0 = WASM_VECTOR_LEN;
35
+ const ptr1 = passStringToWasm0(worker, wasm.__wbindgen_export, wasm.__wbindgen_export2);
36
+ const len1 = WASM_VECTOR_LEN;
37
+ wasm.testengine_activateJobs(retptr, this.__wbg_ptr, ptr0, len0, max_jobs, timeout_ms, ptr1, len1);
38
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
39
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
40
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
41
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
42
+ var ptr3 = r0;
43
+ var len3 = r1;
44
+ if (r3) {
45
+ ptr3 = 0; len3 = 0;
46
+ throw takeObject(r2);
47
+ }
48
+ deferred4_0 = ptr3;
49
+ deferred4_1 = len3;
50
+ return getStringFromWasm0(ptr3, len3);
51
+ } finally {
52
+ wasm.__wbindgen_add_to_stack_pointer(16);
53
+ wasm.__wbindgen_export3(deferred4_0, deferred4_1, 1);
54
+ }
55
+ }
56
+ /**
57
+ * Advance the virtual clock by `by_ms` milliseconds, firing any timers that
58
+ * become due and expiring any lapsed job locks.
59
+ * @param {number} by_ms
60
+ * @returns {string}
61
+ */
62
+ advanceTime(by_ms) {
63
+ let deferred2_0;
64
+ let deferred2_1;
65
+ try {
66
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
67
+ wasm.testengine_advanceTime(retptr, this.__wbg_ptr, by_ms);
68
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
69
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
70
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
71
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
72
+ var ptr1 = r0;
73
+ var len1 = r1;
74
+ if (r3) {
75
+ ptr1 = 0; len1 = 0;
76
+ throw takeObject(r2);
77
+ }
78
+ deferred2_0 = ptr1;
79
+ deferred2_1 = len1;
80
+ return getStringFromWasm0(ptr1, len1);
81
+ } finally {
82
+ wasm.__wbindgen_add_to_stack_pointer(16);
83
+ wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
84
+ }
85
+ }
86
+ /**
87
+ * Complete a waiting job by key, merging `variables_json` (a JSON object
88
+ * string) into the instance. The job is activated first if it has not been
89
+ * already, so the UI can complete a freshly-created job directly.
90
+ * @param {string} job_key
91
+ * @param {string} variables_json
92
+ * @returns {string}
93
+ */
94
+ completeJob(job_key, variables_json) {
95
+ let deferred4_0;
96
+ let deferred4_1;
97
+ try {
98
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
99
+ const ptr0 = passStringToWasm0(job_key, wasm.__wbindgen_export, wasm.__wbindgen_export2);
100
+ const len0 = WASM_VECTOR_LEN;
101
+ const ptr1 = passStringToWasm0(variables_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
102
+ const len1 = WASM_VECTOR_LEN;
103
+ wasm.testengine_completeJob(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1);
104
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
105
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
106
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
107
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
108
+ var ptr3 = r0;
109
+ var len3 = r1;
110
+ if (r3) {
111
+ ptr3 = 0; len3 = 0;
112
+ throw takeObject(r2);
113
+ }
114
+ deferred4_0 = ptr3;
115
+ deferred4_1 = len3;
116
+ return getStringFromWasm0(ptr3, len3);
117
+ } finally {
118
+ wasm.__wbindgen_add_to_stack_pointer(16);
119
+ wasm.__wbindgen_export3(deferred4_0, deferred4_1, 1);
120
+ }
121
+ }
122
+ /**
123
+ * Start a new instance of `process_id`, seeding it with the given variables
124
+ * (a JSON object string; pass `"{}"` or `""` for none). Returns the
125
+ * post-run [`Snapshot`] with a top-level `created` field holding the new
126
+ * instance key.
127
+ * @param {string} process_id
128
+ * @param {string} variables_json
129
+ * @returns {string}
130
+ */
131
+ createInstance(process_id, variables_json) {
132
+ let deferred4_0;
133
+ let deferred4_1;
134
+ try {
135
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
136
+ const ptr0 = passStringToWasm0(process_id, wasm.__wbindgen_export, wasm.__wbindgen_export2);
137
+ const len0 = WASM_VECTOR_LEN;
138
+ const ptr1 = passStringToWasm0(variables_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
139
+ const len1 = WASM_VECTOR_LEN;
140
+ wasm.testengine_createInstance(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1);
141
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
142
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
143
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
144
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
145
+ var ptr3 = r0;
146
+ var len3 = r1;
147
+ if (r3) {
148
+ ptr3 = 0; len3 = 0;
149
+ throw takeObject(r2);
150
+ }
151
+ deferred4_0 = ptr3;
152
+ deferred4_1 = len3;
153
+ return getStringFromWasm0(ptr3, len3);
154
+ } finally {
155
+ wasm.__wbindgen_add_to_stack_pointer(16);
156
+ wasm.__wbindgen_export3(deferred4_0, deferred4_1, 1);
157
+ }
158
+ }
159
+ /**
160
+ * Parse and deploy a BPMN resource. Returns a JSON object
161
+ * `{ "processIds": [...], "snapshot": {...} }` on success, or throws a
162
+ * JS error carrying the parse/deploy failure message.
163
+ * @param {string} xml
164
+ * @returns {string}
165
+ */
166
+ deploy(xml) {
167
+ let deferred3_0;
168
+ let deferred3_1;
169
+ try {
170
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
171
+ const ptr0 = passStringToWasm0(xml, wasm.__wbindgen_export, wasm.__wbindgen_export2);
172
+ const len0 = WASM_VECTOR_LEN;
173
+ wasm.testengine_deploy(retptr, this.__wbg_ptr, ptr0, len0);
174
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
175
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
176
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
177
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
178
+ var ptr2 = r0;
179
+ var len2 = r1;
180
+ if (r3) {
181
+ ptr2 = 0; len2 = 0;
182
+ throw takeObject(r2);
183
+ }
184
+ deferred3_0 = ptr2;
185
+ deferred3_1 = len2;
186
+ return getStringFromWasm0(ptr2, len2);
187
+ } finally {
188
+ wasm.__wbindgen_add_to_stack_pointer(16);
189
+ wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
190
+ }
191
+ }
192
+ /**
193
+ * The full ordered event log emitted so far, as a JSON array of
194
+ * `{ seq, now, type, ...payload }`. Useful for a step-through / trace view.
195
+ * @returns {string}
196
+ */
197
+ events() {
198
+ let deferred2_0;
199
+ let deferred2_1;
200
+ try {
201
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
202
+ wasm.testengine_events(retptr, this.__wbg_ptr);
203
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
204
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
205
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
206
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
207
+ var ptr1 = r0;
208
+ var len1 = r1;
209
+ if (r3) {
210
+ ptr1 = 0; len1 = 0;
211
+ throw takeObject(r2);
212
+ }
213
+ deferred2_0 = ptr1;
214
+ deferred2_1 = len1;
215
+ return getStringFromWasm0(ptr1, len1);
216
+ } finally {
217
+ wasm.__wbindgen_add_to_stack_pointer(16);
218
+ wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
219
+ }
220
+ }
221
+ /**
222
+ * Fail a waiting job by key with the given remaining `retries` and message.
223
+ * With no retries left this raises an incident (visible in the snapshot).
224
+ * @param {string} job_key
225
+ * @param {number} retries
226
+ * @param {string} message
227
+ * @returns {string}
228
+ */
229
+ failJob(job_key, retries, message) {
230
+ let deferred4_0;
231
+ let deferred4_1;
232
+ try {
233
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
234
+ const ptr0 = passStringToWasm0(job_key, wasm.__wbindgen_export, wasm.__wbindgen_export2);
235
+ const len0 = WASM_VECTOR_LEN;
236
+ const ptr1 = passStringToWasm0(message, wasm.__wbindgen_export, wasm.__wbindgen_export2);
237
+ const len1 = WASM_VECTOR_LEN;
238
+ wasm.testengine_failJob(retptr, this.__wbg_ptr, ptr0, len0, retries, ptr1, len1);
239
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
240
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
241
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
242
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
243
+ var ptr3 = r0;
244
+ var len3 = r1;
245
+ if (r3) {
246
+ ptr3 = 0; len3 = 0;
247
+ throw takeObject(r2);
248
+ }
249
+ deferred4_0 = ptr3;
250
+ deferred4_1 = len3;
251
+ return getStringFromWasm0(ptr3, len3);
252
+ } finally {
253
+ wasm.__wbindgen_add_to_stack_pointer(16);
254
+ wasm.__wbindgen_export3(deferred4_0, deferred4_1, 1);
255
+ }
256
+ }
257
+ /**
258
+ * Create a fresh, empty simulated engine. The virtual clock starts at 0.
259
+ */
260
+ constructor() {
261
+ const ret = wasm.testengine_new();
262
+ this.__wbg_ptr = ret;
263
+ TestEngineFinalization.register(this, this.__wbg_ptr, this);
264
+ return this;
265
+ }
266
+ /**
267
+ * The current virtual clock (milliseconds).
268
+ * @returns {number}
269
+ */
270
+ get now() {
271
+ const ret = wasm.testengine_now(this.__wbg_ptr);
272
+ return ret;
273
+ }
274
+ /**
275
+ * The current simulation state as a JSON [`Snapshot`].
276
+ * @returns {string}
277
+ */
278
+ snapshot() {
279
+ let deferred2_0;
280
+ let deferred2_1;
281
+ try {
282
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
283
+ wasm.testengine_snapshot(retptr, this.__wbg_ptr);
284
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
285
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
286
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
287
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
288
+ var ptr1 = r0;
289
+ var len1 = r1;
290
+ if (r3) {
291
+ ptr1 = 0; len1 = 0;
292
+ throw takeObject(r2);
293
+ }
294
+ deferred2_0 = ptr1;
295
+ deferred2_1 = len1;
296
+ return getStringFromWasm0(ptr1, len1);
297
+ } finally {
298
+ wasm.__wbindgen_add_to_stack_pointer(16);
299
+ wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
300
+ }
301
+ }
302
+ /**
303
+ * Set the engine clock to a wall-clock instant (ms), then trigger due timers
304
+ * and expire lapsed job locks. The embedded host calls this with `Date.now()`
305
+ * so `engine-core` stays clock-free while running as a real runtime. The
306
+ * clock never moves backwards. Returns the snapshot.
307
+ * @param {number} now_ms
308
+ * @returns {string}
309
+ */
310
+ tickNow(now_ms) {
311
+ let deferred2_0;
312
+ let deferred2_1;
313
+ try {
314
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
315
+ wasm.testengine_tickNow(retptr, this.__wbg_ptr, now_ms);
316
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
317
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
318
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
319
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
320
+ var ptr1 = r0;
321
+ var len1 = r1;
322
+ if (r3) {
323
+ ptr1 = 0; len1 = 0;
324
+ throw takeObject(r2);
325
+ }
326
+ deferred2_0 = ptr1;
327
+ deferred2_1 = len1;
328
+ return getStringFromWasm0(ptr1, len1);
329
+ } finally {
330
+ wasm.__wbindgen_add_to_stack_pointer(16);
331
+ wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
332
+ }
333
+ }
334
+ }
335
+ if (Symbol.dispose) TestEngine.prototype[Symbol.dispose] = TestEngine.prototype.free;
336
+ function __wbg_get_imports() {
337
+ const import0 = {
338
+ __proto__: null,
339
+ __wbg___wbindgen_throw_ea4887a5f8f9a9db: function(arg0, arg1) {
340
+ throw new Error(getStringFromWasm0(arg0, arg1));
341
+ },
342
+ __wbindgen_cast_0000000000000001: function(arg0, arg1) {
343
+ // Cast intrinsic for `Ref(String) -> Externref`.
344
+ const ret = getStringFromWasm0(arg0, arg1);
345
+ return addHeapObject(ret);
346
+ },
347
+ };
348
+ return {
349
+ __proto__: null,
350
+ "./unano_bg.js": import0,
351
+ };
352
+ }
353
+
354
+ const TestEngineFinalization = (typeof FinalizationRegistry === 'undefined')
355
+ ? { register: () => {}, unregister: () => {} }
356
+ : new FinalizationRegistry(ptr => wasm.__wbg_testengine_free(ptr, 1));
357
+
358
+ function addHeapObject(obj) {
359
+ if (heap_next === heap.length) heap.push(heap.length + 1);
360
+ const idx = heap_next;
361
+ heap_next = heap[idx];
362
+
363
+ heap[idx] = obj;
364
+ return idx;
365
+ }
366
+
367
+ function dropObject(idx) {
368
+ if (idx < 1028) return;
369
+ heap[idx] = heap_next;
370
+ heap_next = idx;
371
+ }
372
+
373
+ let cachedDataViewMemory0 = null;
374
+ function getDataViewMemory0() {
375
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
376
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
377
+ }
378
+ return cachedDataViewMemory0;
379
+ }
380
+
381
+ function getStringFromWasm0(ptr, len) {
382
+ return decodeText(ptr >>> 0, len);
383
+ }
384
+
385
+ let cachedUint8ArrayMemory0 = null;
386
+ function getUint8ArrayMemory0() {
387
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
388
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
389
+ }
390
+ return cachedUint8ArrayMemory0;
391
+ }
392
+
393
+ function getObject(idx) { return heap[idx]; }
394
+
395
+ let heap = new Array(1024).fill(undefined);
396
+ heap.push(undefined, null, true, false);
397
+
398
+ let heap_next = heap.length;
399
+
400
+ function passStringToWasm0(arg, malloc, realloc) {
401
+ if (realloc === undefined) {
402
+ const buf = cachedTextEncoder.encode(arg);
403
+ const ptr = malloc(buf.length, 1) >>> 0;
404
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
405
+ WASM_VECTOR_LEN = buf.length;
406
+ return ptr;
407
+ }
408
+
409
+ let len = arg.length;
410
+ let ptr = malloc(len, 1) >>> 0;
411
+
412
+ const mem = getUint8ArrayMemory0();
413
+
414
+ let offset = 0;
415
+
416
+ for (; offset < len; offset++) {
417
+ const code = arg.charCodeAt(offset);
418
+ if (code > 0x7F) break;
419
+ mem[ptr + offset] = code;
420
+ }
421
+ if (offset !== len) {
422
+ if (offset !== 0) {
423
+ arg = arg.slice(offset);
424
+ }
425
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
426
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
427
+ const ret = cachedTextEncoder.encodeInto(arg, view);
428
+
429
+ offset += ret.written;
430
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
431
+ }
432
+
433
+ WASM_VECTOR_LEN = offset;
434
+ return ptr;
435
+ }
436
+
437
+ function takeObject(idx) {
438
+ const ret = getObject(idx);
439
+ dropObject(idx);
440
+ return ret;
441
+ }
442
+
443
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
444
+ cachedTextDecoder.decode();
445
+ function decodeText(ptr, len) {
446
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
447
+ }
448
+
449
+ const cachedTextEncoder = new TextEncoder();
450
+
451
+ let WASM_VECTOR_LEN = 0;
452
+
453
+ const wasmUrl = new URL('unano_bg.wasm', import.meta.url);
454
+ const wasmInstantiated = await WebAssembly.instantiateStreaming(fetch(wasmUrl), __wbg_get_imports());
455
+ const wasmInstance = wasmInstantiated.instance;
456
+ const wasm = wasmInstance.exports;
@@ -0,0 +1,19 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export const memory: WebAssembly.Memory;
4
+ export const __wbg_testengine_free: (a: number, b: number) => void;
5
+ export const testengine_activateJobs: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void;
6
+ export const testengine_advanceTime: (a: number, b: number, c: number) => void;
7
+ export const testengine_completeJob: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
8
+ export const testengine_createInstance: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
9
+ export const testengine_deploy: (a: number, b: number, c: number, d: number) => void;
10
+ export const testengine_events: (a: number, b: number) => void;
11
+ export const testengine_failJob: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
12
+ export const testengine_new: () => number;
13
+ export const testengine_now: (a: number) => number;
14
+ export const testengine_snapshot: (a: number, b: number) => void;
15
+ export const testengine_tickNow: (a: number, b: number, c: number) => void;
16
+ export const __wbindgen_add_to_stack_pointer: (a: number) => number;
17
+ export const __wbindgen_export: (a: number, b: number) => number;
18
+ export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
19
+ export const __wbindgen_export3: (a: number, b: number, c: number) => void;
@@ -0,0 +1,30 @@
1
+ // Embedded μ-nano starter (ADR 0005). The Nano engine runs IN this process via
2
+ // wasm — no gateway, no sockets. The nano-sdk-js "embedded" transport binds to the
3
+ // host directly, so the same SDK code that talks to a real cluster also drives the
4
+ // in-process engine. `deno compile --include engine` ships engine + app as one binary.
5
+ import { createCamundaClient } from "@nanobpm/nano-sdk";
6
+ import { EmbeddedHost } from "./engine/host.ts";
7
+
8
+ const BPMN = await Deno.readTextFile(new URL("./resources/processes/throughput.bpmn", import.meta.url));
9
+
10
+ const host = await EmbeddedHost.create();
11
+ await host.deploy(BPMN);
12
+
13
+ const camunda = createCamundaClient({ config: { CAMUNDA_TRANSPORT: "embedded" }, embeddedHost: host });
14
+
15
+ camunda.createJobWorker({
16
+ jobType: "work",
17
+ workerName: "embedded-worker",
18
+ maxParallelJobs: 16,
19
+ jobHandler: async (job: { complete: (v?: Record<string, unknown>) => Promise<unknown> }) => job.complete({ done: true }),
20
+ });
21
+
22
+ const N = Number(Deno.env.get("N") ?? 1000);
23
+ const t0 = performance.now();
24
+ await Promise.all(
25
+ Array.from({ length: N }, () =>
26
+ camunda.createProcessInstance({ processDefinitionId: "throughput", variables: {}, awaitCompletion: true })),
27
+ );
28
+ const dt = (performance.now() - t0) / 1000;
29
+ console.log(`embedded: ${N} instances in ${dt.toFixed(2)}s — ${(N / dt).toFixed(0)}/s`);
30
+ camunda.stopAllWorkers?.();
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
3
+ xmlns:zeebe="http://camunda.org/schema/zeebe/1.0"
4
+ targetNamespace="http://nanobpm/embedded">
5
+ <process id="throughput" isExecutable="true">
6
+ <startEvent id="start"/>
7
+ <sequenceFlow id="f1" sourceRef="start" targetRef="work"/>
8
+ <serviceTask id="work">
9
+ <extensionElements>
10
+ <zeebe:taskDefinition type="work"/>
11
+ </extensionElements>
12
+ </serviceTask>
13
+ <sequenceFlow id="f2" sourceRef="work" targetRef="end"/>
14
+ <endEvent id="end"/>
15
+ </process>
16
+ </definitions>