@nice-code/action 0.5.1 → 0.5.2

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/build/index.js CHANGED
@@ -1,246 +1,2588 @@
1
- // src/devtools/server/NiceActionServerDevtools.ts
2
- class ActionServerDevtools {
3
- _options;
4
- _inFlight = new Map;
5
- constructor(options = {}) {
6
- const defaultEnabled = typeof process !== "undefined" ? true : true;
7
- this._options = {
8
- logger: options.logger ?? defaultConsoleLogger,
9
- format: options.format ?? "pretty",
10
- logPayloads: options.logPayloads ?? true,
11
- enabled: options.enabled ?? defaultEnabled
12
- };
13
- }
14
- attachToDomain(domain) {
15
- if (!this._options.enabled) {
16
- return () => {};
17
- }
18
- return domain.addActionListener((update) => {
19
- const { runningAction, type, time } = update;
20
- const actionPath = [...runningAction.allDomains, runningAction.id].join(".");
21
- if (type === "started" /* started */) {
22
- this._inFlight.set(runningAction.cuid, { startTime: time });
23
- this._log("started", actionPath, runningAction.cuid, {
24
- ...this._options.logPayloads ? { input: runningAction.state?.request?.input } : {}
1
+ // src/ActionDefinition/Action/Core/ActionCore.ts
2
+ import { nanoid } from "nanoid";
3
+
4
+ // src/nice_action.static.ts
5
+ var DEFAULT_TRANSPORT_TIMEOUT = 1e4;
6
+ var UNSET_RUNTIME_ENV_ID = "_unset_";
7
+
8
+ // src/ActionRuntime/utils/runtimeCoordinateToStringIds.ts
9
+ function runtimeCoordinateToStringIds(coordinate) {
10
+ return [
11
+ `envId[${coordinate.envId}]perId[${coordinate.perId ?? "_"}]:insId[${coordinate.insId ?? "_"}]`,
12
+ `envId[${coordinate.envId}]perId[${coordinate.perId ?? "_"}]:insId[_]`,
13
+ `envId[${coordinate.envId}]perId[_]:insId[_]`
14
+ ];
15
+ }
16
+
17
+ // src/ActionRuntime/RuntimeCoordinate.ts
18
+ class RuntimeCoordinate {
19
+ envId;
20
+ perId;
21
+ insId;
22
+ static get unknown() {
23
+ return new RuntimeCoordinate({
24
+ envId: UNSET_RUNTIME_ENV_ID
25
+ });
26
+ }
27
+ static env(envId) {
28
+ return new RuntimeCoordinate({
29
+ envId
30
+ });
31
+ }
32
+ withPersistentId(perId) {
33
+ return this.specify({ perId });
34
+ }
35
+ constructor({ envId, perId, insId }) {
36
+ this.envId = envId;
37
+ this.perId = perId;
38
+ this.insId = insId;
39
+ }
40
+ specify(specifics) {
41
+ return new RuntimeCoordinate({
42
+ envId: this.envId,
43
+ perId: this.perId,
44
+ insId: this.insId,
45
+ ...specifics
46
+ });
47
+ }
48
+ specifyIfUnset(specifics) {
49
+ return new RuntimeCoordinate({
50
+ envId: this.envId,
51
+ perId: this.perId ?? specifics.perId,
52
+ insId: this.insId ?? specifics.insId
53
+ });
54
+ }
55
+ toJsonObject() {
56
+ return {
57
+ envId: this.envId,
58
+ perId: this.perId,
59
+ insId: this.insId
60
+ };
61
+ }
62
+ isExactlySame(other) {
63
+ return this.envId === other.envId && this.perId === other.perId && this.insId === other.insId;
64
+ }
65
+ isSameFor(other) {
66
+ return {
67
+ id: this.envId === other.envId,
68
+ perId: this.envId === other.envId && this.perId === other.perId,
69
+ insId: this.envId === other.envId && this.perId === other.perId && this.insId === other.insId
70
+ };
71
+ }
72
+ similarityLevel(other) {
73
+ const sameFor = this.isSameFor(other);
74
+ if (sameFor.insId)
75
+ return 3;
76
+ if (sameFor.perId)
77
+ return 2;
78
+ if (sameFor.id)
79
+ return 1;
80
+ return 0;
81
+ }
82
+ get stringId() {
83
+ return `envId[${this.envId}]perId[${this.perId ?? "_"}]:insId[${this.insId ?? "_"}]`;
84
+ }
85
+ toStringIds() {
86
+ return runtimeCoordinateToStringIds(this);
87
+ }
88
+ }
89
+
90
+ // src/ActionDefinition/Action/ActionBase.ts
91
+ class ActionBase {
92
+ form;
93
+ _domain;
94
+ id;
95
+ domain;
96
+ allDomains;
97
+ schema;
98
+ constructor(form, _domain, id) {
99
+ this.form = form;
100
+ this._domain = _domain;
101
+ this.id = id;
102
+ this.domain = _domain.domain;
103
+ this.allDomains = _domain.allDomains;
104
+ this.schema = _domain.actionSchema[id];
105
+ }
106
+ toJsonObject() {
107
+ return {
108
+ form: this.form,
109
+ domain: this.domain,
110
+ allDomains: this.allDomains,
111
+ id: this.id
112
+ };
113
+ }
114
+ toJsonString() {
115
+ return JSON.stringify(this.toJsonObject());
116
+ }
117
+ }
118
+
119
+ // src/ActionDefinition/Action/Context/ActionContext.ts
120
+ class ActionContext extends ActionBase {
121
+ _domain;
122
+ form = "context" /* context */;
123
+ _routing;
124
+ timeCreated;
125
+ cuid;
126
+ originClient;
127
+ constructor(_domain, id, hydrationData) {
128
+ super("context" /* context */, _domain, id);
129
+ this._domain = _domain;
130
+ this.timeCreated = hydrationData.timeCreated;
131
+ this.cuid = hydrationData.cuid;
132
+ this._routing = hydrationData.routing;
133
+ this.originClient = hydrationData.originClient;
134
+ }
135
+ _setOriginClient(client) {
136
+ this.originClient = client;
137
+ }
138
+ toJsonString() {
139
+ return JSON.stringify(this.toJsonObject());
140
+ }
141
+ toContextDataJsonObject() {
142
+ return {
143
+ timeCreated: this.timeCreated,
144
+ cuid: this.cuid,
145
+ routing: this.routing.map((item) => ({
146
+ runtime: item.runtime.toJsonObject(),
147
+ handler: item.handler,
148
+ time: item.time
149
+ })),
150
+ originClient: this.originClient.toJsonObject()
151
+ };
152
+ }
153
+ toJsonObject() {
154
+ return {
155
+ ...super.toJsonObject(),
156
+ ...this.toContextDataJsonObject()
157
+ };
158
+ }
159
+ get routing() {
160
+ return this._routing;
161
+ }
162
+ addRouteItem(item) {
163
+ this._routing.push(item);
164
+ }
165
+ deserializeInput(serialized) {
166
+ return this.schema.deserializeInput(serialized);
167
+ }
168
+ serializeInput(raw) {
169
+ return this.schema.serializeInput(raw);
170
+ }
171
+ validateInput(input) {
172
+ return this.schema.validateInput(input, {
173
+ domain: this.domain,
174
+ actionId: this.id
175
+ });
176
+ }
177
+ validateOutput(output) {
178
+ return this.schema.validateOutput(output, {
179
+ domain: this.domain,
180
+ actionId: this.id
181
+ });
182
+ }
183
+ }
184
+
185
+ // src/ActionDefinition/Action/Payload/ActionPayload.ts
186
+ class ActionPayload extends ActionBase {
187
+ form = "data" /* data */;
188
+ type;
189
+ context;
190
+ time;
191
+ constructor(context, type, data) {
192
+ super("data" /* data */, context._domain, context.id);
193
+ this.context = context;
194
+ this.type = type;
195
+ this.time = data.time;
196
+ }
197
+ toBaseJsonObject() {
198
+ return {
199
+ ...super.toJsonObject(),
200
+ type: this.type,
201
+ context: this.context.toContextDataJsonObject(),
202
+ time: this.time
203
+ };
204
+ }
205
+ }
206
+
207
+ // src/utils/hashPayloadData.ts
208
+ function stableStringify(value) {
209
+ if (value === null || value === undefined)
210
+ return String(value);
211
+ if (typeof value !== "object")
212
+ return JSON.stringify(value) ?? "undefined";
213
+ if (Array.isArray(value))
214
+ return `[${value.map(stableStringify).join(",")}]`;
215
+ const keys = Object.keys(value).sort();
216
+ return "{" + keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",") + "}";
217
+ }
218
+ function fnv1a32(str) {
219
+ let hash = 2166136261;
220
+ for (let i = 0;i < str.length; i++) {
221
+ hash = (hash ^ str.charCodeAt(i)) * 16777619 >>> 0;
222
+ }
223
+ return hash.toString(16).padStart(8, "0");
224
+ }
225
+ function hashPayloadData(data) {
226
+ return fnv1a32(stableStringify(data));
227
+ }
228
+
229
+ // src/ActionDefinition/Action/Payload/ActionPayload.types.ts
230
+ var EActionPayloadType;
231
+ ((EActionPayloadType2) => {
232
+ EActionPayloadType2["request"] = "request";
233
+ EActionPayloadType2["progress"] = "progress";
234
+ EActionPayloadType2["result"] = "result";
235
+ EActionPayloadType2["stream"] = "stream";
236
+ EActionPayloadType2["push"] = "push";
237
+ })(EActionPayloadType ||= {});
238
+ var EActionProgressType;
239
+ ((EActionProgressType2) => {
240
+ EActionProgressType2["none"] = "none";
241
+ EActionProgressType2["percentage"] = "percentage";
242
+ EActionProgressType2["custom"] = "custom";
243
+ })(EActionProgressType ||= {});
244
+
245
+ // src/ActionDefinition/Action/Payload/ActionPayload_Progress.ts
246
+ class ActionPayload_Progress extends ActionPayload {
247
+ progress;
248
+ constructor(params, progress, data) {
249
+ super(params.context, "progress" /* progress */, data);
250
+ this.progress = progress;
251
+ }
252
+ toJsonObject() {
253
+ return {
254
+ ...this.toBaseJsonObject(),
255
+ progress: this.progress
256
+ };
257
+ }
258
+ toJsonString() {
259
+ return JSON.stringify(this.toJsonObject());
260
+ }
261
+ toHttpResponse() {
262
+ return new Response(this.toJsonString(), {
263
+ status: 200,
264
+ headers: { "Content-Type": "application/json" }
265
+ });
266
+ }
267
+ }
268
+
269
+ // src/ActionDefinition/Action/Payload/ActionPayload_Result.ts
270
+ class ActionPayload_Result extends ActionPayload {
271
+ result;
272
+ outputHash;
273
+ constructor(params, result, data) {
274
+ super(params.context, "result" /* result */, data);
275
+ this.result = result;
276
+ this.outputHash = result.ok ? hashPayloadData(this.context.schema.serializeOutput(result.output)) : hashPayloadData(result.error.message);
277
+ }
278
+ toJsonObject() {
279
+ const wireResult = this.result.ok ? { ok: true, output: this.context.schema.serializeOutput(this.result.output) } : this.result;
280
+ return {
281
+ ...this.toBaseJsonObject(),
282
+ result: wireResult,
283
+ outputHash: this.outputHash
284
+ };
285
+ }
286
+ toJsonString() {
287
+ return JSON.stringify(this.toJsonObject());
288
+ }
289
+ toHttpResponse({ useErrorStatus = true } = {}) {
290
+ return new Response(this.toJsonString(), {
291
+ status: this.result.ok ? 200 : useErrorStatus ? this.result.error.httpStatusCode : 500,
292
+ headers: { "Content-Type": "application/json" }
293
+ });
294
+ }
295
+ }
296
+
297
+ // src/ActionDefinition/Action/Payload/ActionPayload_Request.ts
298
+ class ActionPayload_Request extends ActionPayload {
299
+ input;
300
+ inputHash;
301
+ _callSite;
302
+ constructor(params, input, data) {
303
+ super(params.context, "request" /* request */, data);
304
+ this.input = input;
305
+ this.inputHash = hashPayloadData(this.context.schema.serializeInput(input));
306
+ }
307
+ successResult(...args) {
308
+ const output = args[0];
309
+ const finalOutput = this.context.schema.validateOutput(output, {
310
+ domain: this.domain,
311
+ actionId: this.id
312
+ });
313
+ return new ActionPayload_Result(this, { ok: true, output: finalOutput }, { time: Date.now() });
314
+ }
315
+ errorResult(err) {
316
+ return new ActionPayload_Result(this, { ok: false, error: err }, { time: Date.now() });
317
+ }
318
+ progress(progress) {
319
+ return new ActionPayload_Progress(this, progress, { time: Date.now() });
320
+ }
321
+ toJsonObject() {
322
+ return {
323
+ ...super.toBaseJsonObject(),
324
+ input: this.context.schema.serializeInput(this.input),
325
+ inputHash: this.inputHash
326
+ };
327
+ }
328
+ toJsonString() {
329
+ return JSON.stringify(this.toJsonObject());
330
+ }
331
+ async runToOutput(options) {
332
+ const running = await this.run(options);
333
+ const result = await running.waitForResultPayload();
334
+ if (result.result.ok)
335
+ return result.result.output;
336
+ throw result.result.error;
337
+ }
338
+ async runToResultPayload(options) {
339
+ const value = await this.run(options);
340
+ return value.waitForResultPayload();
341
+ }
342
+ async run(options) {
343
+ if (this._callSite == null) {
344
+ this._callSite = new Error().stack;
345
+ }
346
+ return this._domain.runAction(this, options);
347
+ }
348
+ }
349
+
350
+ // src/ActionDefinition/Action/Core/ActionCore.ts
351
+ class ActionCore extends ActionBase {
352
+ _domain;
353
+ form = "core" /* core */;
354
+ constructor(_domain, id) {
355
+ super("core" /* core */, _domain, id);
356
+ this._domain = _domain;
357
+ }
358
+ is(action) {
359
+ return action instanceof ActionPayload && action.domain === this.domain && action.id === this.id;
360
+ }
361
+ toJsonObject() {
362
+ return {
363
+ id: this.id,
364
+ form: this.form,
365
+ domain: this.domain,
366
+ allDomains: this.allDomains
367
+ };
368
+ }
369
+ request(...args) {
370
+ const input = args[0];
371
+ const validatedInput = this.schema.validateInput(input, {
372
+ actionId: this.id,
373
+ domain: this.domain
374
+ });
375
+ const context = new ActionContext(this._domain, this.id, {
376
+ cuid: nanoid(),
377
+ timeCreated: Date.now(),
378
+ routing: [],
379
+ originClient: RuntimeCoordinate.unknown
380
+ });
381
+ return new ActionPayload_Request({ context }, validatedInput, {
382
+ time: Date.now()
383
+ });
384
+ }
385
+ deserializeInput(serialized) {
386
+ return this.schema.deserializeInput(serialized);
387
+ }
388
+ serializeInput(raw) {
389
+ return this.schema.serializeInput(raw);
390
+ }
391
+ validateInput(input) {
392
+ return this.schema.validateInput(input, {
393
+ domain: this.domain,
394
+ actionId: this.id
395
+ });
396
+ }
397
+ validateOutput(output) {
398
+ return this.schema.validateOutput(output, {
399
+ domain: this.domain,
400
+ actionId: this.id
401
+ });
402
+ }
403
+ }
404
+ // src/ActionDefinition/Action/RunningAction.types.ts
405
+ var ERunningActionState;
406
+ ((ERunningActionState2) => {
407
+ ERunningActionState2["running"] = "running";
408
+ ERunningActionState2["completed"] = "completed";
409
+ })(ERunningActionState ||= {});
410
+ var ERunningActionUpdateType;
411
+ ((ERunningActionUpdateType2) => {
412
+ ERunningActionUpdateType2["started"] = "started";
413
+ ERunningActionUpdateType2["progress"] = "progress";
414
+ ERunningActionUpdateType2["finished"] = "finished";
415
+ })(ERunningActionUpdateType ||= {});
416
+ var ERunningActionFinishedType;
417
+ ((ERunningActionFinishedType2) => {
418
+ ERunningActionFinishedType2["aborted"] = "aborted";
419
+ ERunningActionFinishedType2["failed"] = "failed";
420
+ ERunningActionFinishedType2["success"] = "success";
421
+ })(ERunningActionFinishedType ||= {});
422
+
423
+ // src/ActionDefinition/Action/RunningAction.ts
424
+ class RunningAction {
425
+ _state;
426
+ context;
427
+ cuid;
428
+ id;
429
+ _domain;
430
+ domain;
431
+ allDomains;
432
+ parentCuid;
433
+ callSite;
434
+ _resultPayloadPromise;
435
+ _resolveResult;
436
+ _rejectResult;
437
+ _isAborted = false;
438
+ _updates = [];
439
+ _updateListeners = [];
440
+ constructor(initialState) {
441
+ this.context = initialState.context;
442
+ this.cuid = initialState.context.cuid;
443
+ this.id = initialState.context.id;
444
+ this.domain = initialState.context.domain;
445
+ this.allDomains = initialState.context.allDomains;
446
+ this._domain = initialState.context._domain;
447
+ this.parentCuid = initialState.parentCuid;
448
+ this.callSite = initialState.callSite;
449
+ this._resultPayloadPromise = new Promise((resolve, reject) => {
450
+ this._resolveResult = resolve;
451
+ this._rejectResult = reject;
452
+ });
453
+ this._resultPayloadPromise.catch(() => {});
454
+ this._state = {
455
+ request: initialState.request,
456
+ progress: initialState.progress ?? [],
457
+ result: initialState.result
458
+ };
459
+ this._sendUpdate({
460
+ type: "started" /* started */,
461
+ runningAction: this,
462
+ time: Date.now()
463
+ });
464
+ }
465
+ get state() {
466
+ return this._state;
467
+ }
468
+ abort(reason) {
469
+ this._abort(reason);
470
+ }
471
+ addUpdateListeners(listeners) {
472
+ this._updateListeners.push(...listeners);
473
+ for (const event of this._updates) {
474
+ for (const listener of listeners) {
475
+ listener(event);
476
+ }
477
+ }
478
+ return () => {
479
+ for (const listener of listeners) {
480
+ const i = this._updateListeners.indexOf(listener);
481
+ if (i !== -1)
482
+ this._updateListeners.splice(i, 1);
483
+ }
484
+ };
485
+ }
486
+ async* iterateUpdates() {
487
+ const queue = [];
488
+ let resolveWaiter = null;
489
+ const unsubscribe = this.addUpdateListeners([
490
+ (event) => {
491
+ queue.push(event);
492
+ if (resolveWaiter) {
493
+ resolveWaiter();
494
+ resolveWaiter = null;
495
+ }
496
+ }
497
+ ]);
498
+ try {
499
+ while (true) {
500
+ if (queue.length === 0) {
501
+ await new Promise((resolve) => {
502
+ resolveWaiter = resolve;
503
+ });
504
+ }
505
+ const event = queue.shift();
506
+ yield event;
507
+ if (event.type === "finished" /* finished */) {
508
+ break;
509
+ }
510
+ }
511
+ } finally {
512
+ unsubscribe();
513
+ }
514
+ }
515
+ _sendUpdate(update) {
516
+ this._updates.push(update);
517
+ for (const listener of this._updateListeners)
518
+ listener(update);
519
+ }
520
+ _completeWithResult(result) {
521
+ if (this._state.result != null || this._isAborted)
522
+ return false;
523
+ this._state = {
524
+ request: this._state.request,
525
+ progress: this._state.progress,
526
+ result
527
+ };
528
+ this._resolveResult(result);
529
+ this._sendUpdate({
530
+ type: "finished" /* finished */,
531
+ finishType: "success" /* success */,
532
+ runningAction: this,
533
+ time: Date.now(),
534
+ response: result
535
+ });
536
+ return true;
537
+ }
538
+ _abort(reason) {
539
+ if (this._state.result != null || this._isAborted)
540
+ return false;
541
+ this._isAborted = true;
542
+ this._rejectResult(reason);
543
+ this._sendUpdate({
544
+ type: "finished" /* finished */,
545
+ finishType: "aborted" /* aborted */,
546
+ runningAction: this,
547
+ time: Date.now(),
548
+ reason
549
+ });
550
+ return true;
551
+ }
552
+ _failWithError(error) {
553
+ if (this._state.result != null || this._isAborted)
554
+ return false;
555
+ this._isAborted = true;
556
+ this._rejectResult(error);
557
+ this._sendUpdate({
558
+ type: "finished" /* finished */,
559
+ finishType: "failed" /* failed */,
560
+ runningAction: this,
561
+ time: Date.now(),
562
+ error
563
+ });
564
+ return true;
565
+ }
566
+ _updateProgress(progress) {
567
+ if (this._state.result != null || this._isAborted)
568
+ return;
569
+ this._state.progress.push(progress);
570
+ this._sendUpdate({
571
+ type: "progress" /* progress */,
572
+ runningAction: this,
573
+ time: Date.now(),
574
+ progress: progress.progress
575
+ });
576
+ }
577
+ waitForResultPayload() {
578
+ return this._resultPayloadPromise;
579
+ }
580
+ _resolveFromJson(resultJson) {
581
+ if (this._state.result != null || this._isAborted)
582
+ return false;
583
+ const result = this._domain.hydrateResultPayload(resultJson);
584
+ return this._completeWithResult(result);
585
+ }
586
+ }
587
+ // src/ActionRuntime/Handler/Local/ActionLocalHandler.ts
588
+ import { castNiceError as castNiceError2, isNiceErrorObject, NiceError } from "@nice-code/error";
589
+
590
+ // src/errors/err_nice_action.ts
591
+ import { err, err_nice } from "@nice-code/error";
592
+ var EErrId_NiceAction;
593
+ ((EErrId_NiceAction2) => {
594
+ EErrId_NiceAction2["not_implemented"] = "not_implemented";
595
+ EErrId_NiceAction2["action_id_not_in_domain"] = "action_id_not_in_domain";
596
+ EErrId_NiceAction2["domain_already_exists_in_hierarchy"] = "domain_already_exists_in_hierarchy";
597
+ EErrId_NiceAction2["domain_no_handler"] = "domain_no_handler";
598
+ EErrId_NiceAction2["hydration_domain_mismatch"] = "hydration_domain_mismatch";
599
+ EErrId_NiceAction2["hydration_action_state_mismatch"] = "hydration_action_state_mismatch";
600
+ EErrId_NiceAction2["hydration_action_id_not_found"] = "hydration_action_id_not_found";
601
+ EErrId_NiceAction2["no_action_execution_handler"] = "no_action_execution_handler";
602
+ EErrId_NiceAction2["wire_action_not_payload"] = "wire_action_not_payload";
603
+ EErrId_NiceAction2["wire_not_action_data"] = "wire_not_action_data";
604
+ EErrId_NiceAction2["client_runtime_already_registered"] = "client_runtime_already_registered";
605
+ EErrId_NiceAction2["client_runtime_not_registered"] = "client_runtime_not_registered";
606
+ EErrId_NiceAction2["runtime_reset"] = "runtime_reset";
607
+ EErrId_NiceAction2["no_client_runtimes_registered"] = "no_client_runtimes_registered";
608
+ EErrId_NiceAction2["action_input_validation_failed"] = "action_input_validation_failed";
609
+ EErrId_NiceAction2["action_input_validation_promise"] = "action_input_validation_promise";
610
+ EErrId_NiceAction2["action_output_validation_failed"] = "action_output_validation_failed";
611
+ EErrId_NiceAction2["action_output_validation_promise"] = "action_output_validation_promise";
612
+ })(EErrId_NiceAction ||= {});
613
+ var err_nice_action = err_nice.createChildDomain({
614
+ domain: "err_nice_action",
615
+ defaultHttpStatusCode: 500,
616
+ schema: {
617
+ ["not_implemented" /* not_implemented */]: err({
618
+ message: ({ label }) => `The "${label}" functionality is not implemented yet.`
619
+ }),
620
+ ["action_id_not_in_domain" /* action_id_not_in_domain */]: err({
621
+ message: ({ actionId, domain }) => `Action with id "${actionId}" does not exist in domain "${domain}".`
622
+ }),
623
+ ["domain_already_exists_in_hierarchy" /* domain_already_exists_in_hierarchy */]: err({
624
+ message: ({ domain, allParentDomains, parentDomain }) => `Domain "${domain}" already exists in the hierarchy under the parent "${parentDomain}". All parent domains ["${allParentDomains.join(", ")}"]`
625
+ }),
626
+ ["domain_no_handler" /* domain_no_handler */]: err({
627
+ message: ({ domain }) => `Domain "${domain}" has no action handler registered.`
628
+ }),
629
+ ["hydration_domain_mismatch" /* hydration_domain_mismatch */]: err({
630
+ message: ({ expected, received }) => `Cannot hydrate action: domain mismatch. Expected "${expected}", got "${received}".`
631
+ }),
632
+ ["hydration_action_state_mismatch" /* hydration_action_state_mismatch */]: err({
633
+ message: ({ expected, received }) => `Cannot hydrate action: action state type mismatch. Expected "${expected}", got "${received}".`
634
+ }),
635
+ ["hydration_action_id_not_found" /* hydration_action_id_not_found */]: err({
636
+ message: ({ domain, actionId }) => `Cannot hydrate action: id "${actionId}" does not exist in domain "${domain}".`
637
+ }),
638
+ ["no_action_execution_handler" /* no_action_execution_handler */]: err({
639
+ message: ({ domain, actionId, specifiedClient }) => `${specifiedClient ? ` The targeted client runtime [${specifiedClient.stringId}] has no` : "No"} action handler registered for "${actionId}" in domain "${domain}".`
640
+ }),
641
+ ["wire_action_not_payload" /* wire_action_not_payload */]: err({
642
+ message: ({ domain, actionId, actionState }) => `Cannot handle wire for action "${actionId}" in domain "${domain}": expected action form of "${"data" /* data */}" and type of "${"request" /* request */}", "${"progress" /* progress */}" or "${"result" /* result */}", got "${actionState}".`
643
+ }),
644
+ ["wire_not_action_data" /* wire_not_action_data */]: err({
645
+ message: () => `Cannot handle wire for action: expected an object with a "domain" property of type string, a "form" property of "${"data" /* data */}" and a "type" property of "${"request" /* request */}", "${"progress" /* progress */}" or "${"result" /* result */}".`
646
+ }),
647
+ ["runtime_reset" /* runtime_reset */]: err({
648
+ message: () => `Runtime has been reset.`
649
+ }),
650
+ ["client_runtime_already_registered" /* client_runtime_already_registered */]: err({
651
+ message: ({ context, client }) => `Environment is already registered${context?.domain ? ` on domain "${context.domain}"` : ""} for client [${client.stringId}]. Each client specifier (exact match on all properties) may only be registered once.`
652
+ }),
653
+ ["client_runtime_not_registered" /* client_runtime_not_registered */]: err({
654
+ message: ({ context, clientStringId }) => `No runtime registered${context?.domain ? ` on domain "${context.domain}"` : ""} for client [${clientStringId}].`
655
+ }),
656
+ ["no_client_runtimes_registered" /* no_client_runtimes_registered */]: err({
657
+ message: ({ context }) => `No runtimes registered${context?.domain ? ` on domain "${context.domain}"` : ""}. Add handlers to a runtime via runtime.addHandlers([handler]) before executing actions.`
658
+ }),
659
+ ["action_input_validation_failed" /* action_input_validation_failed */]: err({
660
+ message: ({ domain, actionId, validationMessage }) => `Input validation failed for action "${actionId}" in domain "${domain}":
661
+ ${validationMessage}`,
662
+ httpStatusCode: 400
663
+ }),
664
+ ["action_input_validation_promise" /* action_input_validation_promise */]: err({
665
+ message: ({ domain, actionId }) => `Input validation for action "${actionId}" in domain "${domain}" returned a promise, which is not supported.`,
666
+ httpStatusCode: 400
667
+ }),
668
+ ["action_output_validation_failed" /* action_output_validation_failed */]: err({
669
+ message: ({ domain, actionId, validationMessage }) => `Output validation failed for action "${actionId}" in domain "${domain}":
670
+ ${validationMessage}`,
671
+ httpStatusCode: 500
672
+ }),
673
+ ["action_output_validation_promise" /* action_output_validation_promise */]: err({
674
+ message: ({ domain, actionId }) => `Output validation for action "${actionId}" in domain "${domain}" returned a promise, which is not supported.`,
675
+ httpStatusCode: 500
676
+ })
677
+ }
678
+ });
679
+
680
+ // src/utils/isAction_Base_JsonObject.ts
681
+ var isAction_Base_JsonObject = (obj) => {
682
+ return typeof obj === "object" && obj !== null && typeof obj.domain === "string" && typeof obj.id === "string" && typeof obj.form === "string";
683
+ };
684
+
685
+ // src/utils/isActionPayload_Result_JsonObject.ts
686
+ var isActionPayload_Result_JsonObject = (obj) => {
687
+ return isAction_Base_JsonObject(obj) && obj.result != null && obj.form === "data" /* data */ && obj.type === "result" /* result */;
688
+ };
689
+
690
+ // src/ActionRuntime/ActionRuntime.ts
691
+ import { castNiceError } from "@nice-code/error";
692
+ import { nanoid as nanoid2 } from "nanoid";
693
+
694
+ // src/utils/getAssumedRuntimeEnvironment.ts
695
+ import { runtime } from "std-env";
696
+ var getAssumedRuntimeInfo = () => {
697
+ return {
698
+ assumed: true,
699
+ runtimeName: runtime
700
+ };
701
+ };
702
+
703
+ // src/utils/isActionPayload_Progress_JsonObject.ts
704
+ var isActionPayload_Progress_JsonObject = (obj) => {
705
+ return isAction_Base_JsonObject(obj) && "progress" in obj && obj.form === "data" /* data */ && obj.type === "progress" /* progress */;
706
+ };
707
+
708
+ // src/utils/isActionPayload_Request_JsonObject.ts
709
+ var isActionPayload_Request_JsonObject = (obj) => {
710
+ return isAction_Base_JsonObject(obj) && "input" in obj && obj.form === "data" /* data */ && obj.type === "request" /* request */;
711
+ };
712
+
713
+ // src/utils/isActionPayload_Any_JsonObject.ts
714
+ function isActionPayload_Any_JsonObject(obj) {
715
+ return isActionPayload_Request_JsonObject(obj) || isActionPayload_Result_JsonObject(obj) || isActionPayload_Progress_JsonObject(obj);
716
+ }
717
+
718
+ // src/ActionRuntime/ActionDomainManager.ts
719
+ class ActionDomainManager {
720
+ _domains = new Map;
721
+ addDomain(domain) {
722
+ this._domains.set(domain.domain, domain);
723
+ }
724
+ getDomains() {
725
+ return [...this._domains.values()];
726
+ }
727
+ verifyIsActionJson(action) {
728
+ if (typeof action.domain !== "string" || typeof action.id !== "string") {
729
+ throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
730
+ }
731
+ }
732
+ getActionDomain(action) {
733
+ this.verifyIsActionJson(action);
734
+ const domain = this._domains.get(action.domain);
735
+ if (!domain) {
736
+ return;
737
+ }
738
+ return domain;
739
+ }
740
+ getActionDomainOrThrow(action) {
741
+ this.verifyIsActionJson(action);
742
+ const domain = this._domains.get(action.domain);
743
+ if (!domain) {
744
+ throw err_nice_action.fromId("domain_no_handler" /* domain_no_handler */, {
745
+ domain: action.domain
746
+ });
747
+ }
748
+ return domain;
749
+ }
750
+ hydrateActionPayload(actionJson) {
751
+ const domain = this.getActionDomainOrThrow(actionJson);
752
+ return domain.hydrateAnyAction(actionJson);
753
+ }
754
+ }
755
+
756
+ // src/ActionRuntime/Routing/ActionRouter.ts
757
+ class ActionRouter {
758
+ domainManager = new ActionDomainManager;
759
+ actionRouteData = new Map;
760
+ _context;
761
+ constructor(context) {
762
+ this._context = context;
763
+ }
764
+ mergeRouter(actionRouter) {
765
+ for (const domain of actionRouter.getDomains()) {
766
+ this.domainManager.addDomain(domain);
767
+ }
768
+ for (const [matchKey, routeDataEntries] of actionRouter.actionRouteData.entries()) {
769
+ this.actionRouteData.set(matchKey, [...routeDataEntries]);
770
+ }
771
+ }
772
+ addDomainsFromOther(actionRouter) {
773
+ for (const domain of actionRouter.getDomains()) {
774
+ this.domainManager.addDomain(domain);
775
+ }
776
+ }
777
+ getRouteDataEntriesForAction(action) {
778
+ const idKey = `dom[${action.domain}]id[${action.id}]`;
779
+ const domKey = `dom[${action.domain}]id[_]`;
780
+ return [
781
+ ...this.actionRouteData.get(idKey) ?? [],
782
+ ...this.actionRouteData.get(domKey) ?? []
783
+ ];
784
+ }
785
+ getRouteDataForAction(action) {
786
+ return this.getRouteDataEntriesForAction(action)[0];
787
+ }
788
+ throwNoHandlerForAction(action, context) {
789
+ if (this._context.contextType === "handler_route" /* handler_route */) {
790
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
791
+ domain: action.domain,
792
+ actionId: action.id,
793
+ specifiedClient: context.targetLocalRuntime?.coordinate
794
+ });
795
+ }
796
+ if (this._context.contextType === "runtime_to_handler" /* runtime_to_handler */) {
797
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
798
+ domain: action.domain,
799
+ actionId: action.id,
800
+ specifiedClient: this._context.runtime.coordinate
801
+ });
802
+ }
803
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
804
+ domain: action.domain,
805
+ actionId: action.id
806
+ });
807
+ }
808
+ getRouteDataEntriesForActionOrThrow(action, context) {
809
+ const entries = this.getRouteDataEntriesForAction(action);
810
+ if (entries.length === 0) {
811
+ this.throwNoHandlerForAction(action, context);
812
+ }
813
+ return entries;
814
+ }
815
+ getRouteDataForActionOrThrow(action, context) {
816
+ const routeData = this.getRouteDataForAction(action);
817
+ if (!routeData) {
818
+ this.throwNoHandlerForAction(action, context);
819
+ }
820
+ return routeData;
821
+ }
822
+ getForKey(key) {
823
+ return this.actionRouteData.get(key) ?? [];
824
+ }
825
+ getRegisteredKeys() {
826
+ return [...this.actionRouteData.keys()];
827
+ }
828
+ getDomains() {
829
+ return this.domainManager.getDomains();
830
+ }
831
+ forDomain(domain, routeData) {
832
+ this.domainManager.addDomain(domain);
833
+ this.actionRouteData.set(`dom[${domain.domain}]id[_]`, [routeData]);
834
+ return this;
835
+ }
836
+ forAction(action, routeData) {
837
+ return this.forActionId(action._domain, action.id, routeData);
838
+ }
839
+ forActionId(domain, id, routeData) {
840
+ this.domainManager.addDomain(domain);
841
+ this.actionRouteData.set(`dom[${domain.domain}]id[${id}]`, [routeData]);
842
+ return this;
843
+ }
844
+ forActionIds(domain, ids, routeData) {
845
+ this.domainManager.addDomain(domain);
846
+ for (const id of ids) {
847
+ this.forActionId(domain, id, routeData);
848
+ }
849
+ return this;
850
+ }
851
+ forDomainActionCases(domain, cases) {
852
+ this.domainManager.addDomain(domain);
853
+ for (const id of Object.keys(cases)) {
854
+ const routeData = cases[id];
855
+ if (routeData != null) {
856
+ this.actionRouteData.set(`dom[${domain.domain}]id[${id}]`, [routeData]);
857
+ }
858
+ }
859
+ return this;
860
+ }
861
+ addForDomain(domain, routeData) {
862
+ this.domainManager.addDomain(domain);
863
+ this._push(`dom[${domain.domain}]id[_]`, routeData);
864
+ return this;
865
+ }
866
+ addForAction(domain, id, routeData) {
867
+ this.domainManager.addDomain(domain);
868
+ this._push(`dom[${domain.domain}]id[${id}]`, routeData);
869
+ return this;
870
+ }
871
+ addForActionIds(domain, ids, routeData) {
872
+ this.domainManager.addDomain(domain);
873
+ for (const id of ids) {
874
+ this.addForAction(domain, id, routeData);
875
+ }
876
+ return this;
877
+ }
878
+ addForDomainActionCases(domain, cases) {
879
+ this.domainManager.addDomain(domain);
880
+ for (const id of Object.keys(cases)) {
881
+ const routeData = cases[id];
882
+ if (routeData != null) {
883
+ this._push(`dom[${domain.domain}]id[${id}]`, routeData);
884
+ }
885
+ }
886
+ return this;
887
+ }
888
+ addForKey(key, routeData) {
889
+ this._push(key, routeData);
890
+ return this;
891
+ }
892
+ _push(key, routeData) {
893
+ const existing = this.actionRouteData.get(key);
894
+ if (existing != null) {
895
+ existing.push(routeData);
896
+ } else {
897
+ this.actionRouteData.set(key, [routeData]);
898
+ }
899
+ }
900
+ }
901
+
902
+ // src/ActionRuntime/ActionRuntime.ts
903
+ class ActionRuntime {
904
+ _coordinate;
905
+ timeCreated;
906
+ runtimeInfo = getAssumedRuntimeInfo();
907
+ actionRouter;
908
+ _pendingRunningActions = new Map;
909
+ _registeredExternalHandlers = [];
910
+ _applied = false;
911
+ static getDefault() {
912
+ return getDefaultActionRuntime();
913
+ }
914
+ constructor(coordinate) {
915
+ this._coordinate = coordinate.specifyIfUnset({
916
+ insId: nanoid2(14)
917
+ });
918
+ this.timeCreated = Date.now();
919
+ this.actionRouter = new ActionRouter({
920
+ contextType: "runtime_to_handler" /* runtime_to_handler */,
921
+ runtime: this
922
+ });
923
+ }
924
+ get coordinate() {
925
+ return this._coordinate;
926
+ }
927
+ specifyRuntimeCoordinate(specifics) {
928
+ if (specifics.envId != null && this._coordinate.envId !== specifics.envId) {
929
+ throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
930
+ label: `updating RuntimeCoordinate with a different "envId" ("${this._coordinate.envId}" → "${specifics.envId}")`
931
+ });
932
+ }
933
+ this._coordinate = this._coordinate.specify(specifics);
934
+ this.apply();
935
+ }
936
+ registerRunningAction(ra) {
937
+ this._pendingRunningActions.set(ra.cuid, ra);
938
+ ra.addUpdateListeners([
939
+ (update) => {
940
+ if (update.type === "finished" /* finished */) {
941
+ this._pendingRunningActions.delete(ra.cuid);
942
+ }
943
+ }
944
+ ]);
945
+ }
946
+ resolveIncomingActionPayload(json) {
947
+ if (json.type === "request" /* request */) {
948
+ this.handleActionPayloadWire(json).catch((err2) => {
949
+ console.error(`[ActionRuntime] Incoming action [${json.domain}:${json.id}:${json.form}:${json.type}] unhandled:`, err2);
950
+ });
951
+ return;
952
+ }
953
+ this._pendingRunningActions.get(json.context.cuid)?._resolveFromJson(json);
954
+ }
955
+ async handleActionPayloadWire(wire) {
956
+ let action;
957
+ if (isActionPayload_Any_JsonObject(wire)) {
958
+ const domain = this.actionRouter.domainManager.getActionDomainOrThrow(wire);
959
+ action = domain.hydrateAnyAction(wire);
960
+ }
961
+ if (action == null) {
962
+ throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
963
+ }
964
+ return this.handleActionPayload(action);
965
+ }
966
+ async handleActionPayload(action, options) {
967
+ if (action.type === "request" /* request */) {
968
+ let handlerForAction;
969
+ try {
970
+ handlerForAction = this.getHandlerForActionOrThrow(action, options);
971
+ } catch (err2) {
972
+ const runningAction2 = new RunningAction({
973
+ context: action.context,
974
+ request: action
975
+ });
976
+ runningAction2._completeWithResult(action.errorResult(castNiceError(err2)));
977
+ return runningAction2;
978
+ }
979
+ const runningAction = await handlerForAction.handleActionRequest(action, {
980
+ ...options,
981
+ targetLocalRuntime: this
982
+ });
983
+ this._trySetupReturnDispatch(runningAction);
984
+ return runningAction;
985
+ }
986
+ throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
987
+ label: `Handling incoming action payloads of type "${action.type}"`
988
+ });
989
+ }
990
+ _getHandlerForAction(action, options) {
991
+ const handlers = this.actionRouter.getRouteDataEntriesForAction(action);
992
+ const targetExternalClient = options?.targetExternalClient;
993
+ const possibleHandlers = handlers.filter((handler2) => {
994
+ if (handler2.handlerType === "external" /* external */) {
995
+ if (targetExternalClient && !targetExternalClient.isSameFor(handler2.externalClient).id) {
996
+ return false;
997
+ }
998
+ return true;
999
+ }
1000
+ if (targetExternalClient != null) {
1001
+ return false;
1002
+ }
1003
+ if (action.type === "request" /* request */) {
1004
+ return true;
1005
+ }
1006
+ return false;
1007
+ });
1008
+ if (possibleHandlers.length === 0) {
1009
+ return;
1010
+ }
1011
+ const scoringExternalClient = targetExternalClient ?? RuntimeCoordinate.unknown;
1012
+ let handlerScore = -1;
1013
+ let handler;
1014
+ for (const possibleHandler of possibleHandlers) {
1015
+ if (possibleHandler.handlerType === "local" /* local */ && handler == null) {
1016
+ return possibleHandler;
1017
+ }
1018
+ if (possibleHandler.handlerType === "external" /* external */) {
1019
+ const score = scoringExternalClient.similarityLevel(possibleHandler.externalClient);
1020
+ if (score > handlerScore) {
1021
+ handlerScore = score;
1022
+ handler = possibleHandler;
1023
+ }
1024
+ }
1025
+ }
1026
+ return handler;
1027
+ }
1028
+ getHandlerForActionOrThrow(action, options) {
1029
+ const handler = this._getHandlerForAction(action, options);
1030
+ if (handler == null) {
1031
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1032
+ actionId: action.id,
1033
+ domain: action.domain,
1034
+ specifiedClient: options?.targetExternalClient
1035
+ });
1036
+ }
1037
+ return handler;
1038
+ }
1039
+ addHandlers(handlers) {
1040
+ for (const handler of handlers) {
1041
+ if (handler.handlerType === "external" /* external */) {
1042
+ handler._setIncomingActionDataListener((json) => this.resolveIncomingActionPayload(json));
1043
+ this._registeredExternalHandlers.push(handler);
1044
+ }
1045
+ const handlerRouter = handler.getActionRouter();
1046
+ this.actionRouter.addDomainsFromOther(handlerRouter);
1047
+ if (this._applied) {
1048
+ this.apply();
1049
+ }
1050
+ for (const key of handlerRouter.getRegisteredKeys()) {
1051
+ const alreadyRegistered = this.actionRouter.getForKey(key).some((h) => h.cuid === handler.cuid);
1052
+ if (!alreadyRegistered) {
1053
+ this.actionRouter.addForKey(key, handler);
1054
+ }
1055
+ }
1056
+ }
1057
+ return this;
1058
+ }
1059
+ applyRuntimeForDomain(domain) {
1060
+ const rootDomain = domain.rootDomain;
1061
+ if (!rootDomain._hasRuntime(this)) {
1062
+ rootDomain._registerRuntime(this);
1063
+ }
1064
+ }
1065
+ apply() {
1066
+ this._applied = true;
1067
+ for (const domain of this.actionRouter.getDomains()) {
1068
+ this.applyRuntimeForDomain(domain);
1069
+ }
1070
+ return this;
1071
+ }
1072
+ getReturnHandlerForOrigin(originClient) {
1073
+ if (originClient.envId === UNSET_RUNTIME_ENV_ID)
1074
+ return;
1075
+ let bestScore = -1;
1076
+ let bestHandler;
1077
+ for (const handler of this._registeredExternalHandlers) {
1078
+ const score = originClient.similarityLevel(handler.externalClient);
1079
+ if (score > bestScore) {
1080
+ bestScore = score;
1081
+ bestHandler = handler;
1082
+ }
1083
+ }
1084
+ return bestScore > 0 ? bestHandler : undefined;
1085
+ }
1086
+ resetRuntime() {
1087
+ for (const ra of this._pendingRunningActions.values()) {
1088
+ ra._abort(err_nice_action.fromId("runtime_reset" /* runtime_reset */));
1089
+ }
1090
+ for (const handler of this._registeredExternalHandlers) {
1091
+ handler.clearTransportCache();
1092
+ }
1093
+ }
1094
+ _trySetupReturnDispatch(runningAction) {
1095
+ const originClient = runningAction.context.originClient;
1096
+ if (originClient.envId === UNSET_RUNTIME_ENV_ID || originClient.isSameFor(this._coordinate).id) {
1097
+ return;
1098
+ }
1099
+ runningAction.addUpdateListeners([
1100
+ (update) => {
1101
+ if (update.type === "finished" /* finished */ && update.finishType === "success" /* success */) {
1102
+ const returnHandler = this.getReturnHandlerForOrigin(originClient);
1103
+ returnHandler?.sendReturnPayload(update.response, { targetLocalRuntime: this }).catch(() => {});
1104
+ }
1105
+ }
1106
+ ]);
1107
+ }
1108
+ }
1109
+ var runtimeState = {
1110
+ defaultLocalRuntime: undefined,
1111
+ assumedRuntimeInfo: undefined
1112
+ };
1113
+ function getDefaultActionRuntime() {
1114
+ if (runtimeState.assumedRuntimeInfo == null) {
1115
+ runtimeState.assumedRuntimeInfo = getAssumedRuntimeInfo();
1116
+ }
1117
+ if (runtimeState.defaultLocalRuntime == null) {
1118
+ runtimeState.defaultLocalRuntime = new ActionRuntime(RuntimeCoordinate.unknown.specify({
1119
+ perId: `${runtimeState.assumedRuntimeInfo?.runtimeName ?? "unknown"}-runtime`
1120
+ }));
1121
+ }
1122
+ return runtimeState.defaultLocalRuntime;
1123
+ }
1124
+
1125
+ // src/ActionRuntime/HandlerCallStack.ts
1126
+ var _stack = [];
1127
+ function pushHandlerCuid(cuid) {
1128
+ _stack.push(cuid);
1129
+ }
1130
+ function popHandlerCuid() {
1131
+ _stack.pop();
1132
+ }
1133
+ function peekHandlerCuid() {
1134
+ return _stack[_stack.length - 1];
1135
+ }
1136
+
1137
+ // src/ActionRuntime/Handler/ActionHandler.ts
1138
+ import { nanoid as nanoid3 } from "nanoid";
1139
+
1140
+ class ActionHandler {
1141
+ cuid;
1142
+ constructor() {
1143
+ this.cuid = nanoid3();
1144
+ }
1145
+ getActionRouter() {
1146
+ return this.actionRouter;
1147
+ }
1148
+ }
1149
+
1150
+ // src/ActionRuntime/Handler/Local/ActionLocalHandler.ts
1151
+ class ActionLocalHandler extends ActionHandler {
1152
+ handlerType = "local" /* local */;
1153
+ actionRouter = new ActionRouter({
1154
+ contextType: "handler_route" /* handler_route */,
1155
+ handler: this
1156
+ });
1157
+ constructor() {
1158
+ super();
1159
+ }
1160
+ forDomain(domain, handler) {
1161
+ this.actionRouter.forDomain(domain, handler);
1162
+ return this;
1163
+ }
1164
+ forAction(action, handler) {
1165
+ this.actionRouter.forAction(action, handler);
1166
+ return this;
1167
+ }
1168
+ forActionIds(domain, ids, handler) {
1169
+ this.actionRouter.forActionIds(domain, ids, handler);
1170
+ return this;
1171
+ }
1172
+ forDomainActionCases(domain, cases) {
1173
+ this.actionRouter.forDomainActionCases(domain, cases);
1174
+ return this;
1175
+ }
1176
+ async handleActionRequest(action, config) {
1177
+ const targetLocalRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1178
+ const handler = this.actionRouter.getRouteDataForActionOrThrow(action, {
1179
+ targetLocalRuntime
1180
+ });
1181
+ action.context.addRouteItem({
1182
+ runtime: targetLocalRuntime.coordinate,
1183
+ handler: this.toHandlerRouteItem(),
1184
+ time: Date.now()
1185
+ });
1186
+ const runningAction = new RunningAction({
1187
+ context: action.context,
1188
+ request: action,
1189
+ parentCuid: peekHandlerCuid(),
1190
+ callSite: action._callSite ?? new Error().stack
1191
+ });
1192
+ this._handleRunningAction(handler, runningAction).catch((err2) => {
1193
+ if (err2 instanceof NiceError) {
1194
+ runningAction._completeWithResult(action.errorResult(err2));
1195
+ } else if (isNiceErrorObject(err2)) {
1196
+ runningAction._completeWithResult(action.errorResult(castNiceError2(err2)));
1197
+ } else {
1198
+ runningAction._abort(err2);
1199
+ }
1200
+ });
1201
+ return runningAction;
1202
+ }
1203
+ async _handleRunningAction(handler, runningAction) {
1204
+ const state = runningAction.state;
1205
+ if (state.result != null) {
1206
+ return;
1207
+ }
1208
+ await Promise.resolve();
1209
+ pushHandlerCuid(runningAction.cuid);
1210
+ try {
1211
+ const rawResult = await handler(state.request);
1212
+ let result;
1213
+ if (rawResult instanceof ActionPayload_Result) {
1214
+ result = rawResult;
1215
+ } else if (rawResult != null && isActionPayload_Result_JsonObject(rawResult)) {
1216
+ const domain = this.actionRouter.domainManager.getActionDomainOrThrow(state.request);
1217
+ result = domain.hydrateResultPayload(rawResult);
1218
+ } else {
1219
+ result = state.request.successResult(rawResult);
1220
+ }
1221
+ runningAction._completeWithResult(result);
1222
+ } finally {
1223
+ popHandlerCuid();
1224
+ }
1225
+ }
1226
+ async handlePayloadWireOrThrow(wire, config) {
1227
+ const hydratedAction = this.actionRouter.domainManager.hydrateActionPayload(wire);
1228
+ if (!(hydratedAction instanceof ActionPayload_Request)) {
1229
+ throw err_nice_action.fromId("wire_action_not_payload" /* wire_action_not_payload */, {
1230
+ domain: hydratedAction.domain,
1231
+ actionId: hydratedAction.id,
1232
+ actionState: hydratedAction.type ?? hydratedAction.form
1233
+ });
1234
+ }
1235
+ return await this.handleActionRequest(hydratedAction, config);
1236
+ }
1237
+ toJsonObject() {
1238
+ return {
1239
+ type: this.handlerType
1240
+ };
1241
+ }
1242
+ toHandlerRouteItem() {
1243
+ return {
1244
+ type: this.handlerType
1245
+ };
1246
+ }
1247
+ }
1248
+ var createLocalHandler = () => {
1249
+ return new ActionLocalHandler;
1250
+ };
1251
+
1252
+ // src/utils/isAction_Context_JsonObject.ts
1253
+ var isAction_Context_JsonObject = (obj) => {
1254
+ return isAction_Base_JsonObject(obj) && obj.form === "context" /* context */;
1255
+ };
1256
+
1257
+ // src/utils/isAction_Core_JsonObject.ts
1258
+ var isAction_Core_JsonObject = (obj) => {
1259
+ return isAction_Base_JsonObject(obj) && obj.form === "core" /* core */;
1260
+ };
1261
+
1262
+ // src/utils/isAction_Any_JsonObject.ts
1263
+ function isAction_Any_JsonObject(obj) {
1264
+ return isActionPayload_Any_JsonObject(obj) || isAction_Context_JsonObject(obj) || isAction_Core_JsonObject(obj);
1265
+ }
1266
+
1267
+ // src/utils/assertIsActionJson.ts
1268
+ function assertIsActionJson(obj) {
1269
+ if (!isAction_Any_JsonObject(obj)) {
1270
+ throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
1271
+ }
1272
+ }
1273
+
1274
+ // src/utils/isAction_Any_Instance.ts
1275
+ function isAction_Any_Instance(value) {
1276
+ return value instanceof ActionCore || value instanceof ActionPayload || value instanceof ActionContext;
1277
+ }
1278
+
1279
+ // src/ActionDefinition/Domain/ActionDomainBase.ts
1280
+ class ActionDomainBase {
1281
+ domain;
1282
+ allDomains;
1283
+ actionSchema;
1284
+ _listeners = [];
1285
+ constructor(definition) {
1286
+ this.domain = definition.domain;
1287
+ this.allDomains = definition.allDomains;
1288
+ this.actionSchema = definition.actionSchema;
1289
+ }
1290
+ addActionListener(listener) {
1291
+ this._listeners.push(listener);
1292
+ return () => {
1293
+ this._listeners = this._listeners.filter((l) => l !== listener);
1294
+ };
1295
+ }
1296
+ }
1297
+
1298
+ // src/ActionDefinition/Domain/ActionDomain.ts
1299
+ class ActionDomain extends ActionDomainBase {
1300
+ _rootDomain;
1301
+ _actionMap;
1302
+ constructor(definition, {
1303
+ rootDomain
1304
+ }) {
1305
+ super(definition);
1306
+ this._rootDomain = rootDomain;
1307
+ this._actionMap = this.createActionMap();
1308
+ }
1309
+ get rootDomain() {
1310
+ return this._rootDomain;
1311
+ }
1312
+ _registerRuntime(runtime2) {
1313
+ this._rootDomain._registerRuntime(runtime2);
1314
+ }
1315
+ createChildDomain(subDomainDef) {
1316
+ if (this.allDomains.includes(subDomainDef.domain)) {
1317
+ throw err_nice_action.fromId("domain_already_exists_in_hierarchy" /* domain_already_exists_in_hierarchy */, {
1318
+ domain: subDomainDef.domain,
1319
+ allParentDomains: this.allDomains,
1320
+ parentDomain: this.domain
1321
+ });
1322
+ }
1323
+ return new ActionDomain({
1324
+ allDomains: [...this.allDomains, subDomainDef.domain],
1325
+ domain: subDomainDef.domain,
1326
+ actionSchema: subDomainDef.actions
1327
+ }, { rootDomain: this._rootDomain });
1328
+ }
1329
+ get action() {
1330
+ return this._actionMap;
1331
+ }
1332
+ actionsMap() {
1333
+ return this._actionMap;
1334
+ }
1335
+ actionForId(id) {
1336
+ const actionSchema = this.actionSchema[id];
1337
+ if (!actionSchema) {
1338
+ throw err_nice_action.fromId("action_id_not_in_domain" /* action_id_not_in_domain */, {
1339
+ domain: this.domain,
1340
+ actionId: id
1341
+ });
1342
+ }
1343
+ return new ActionCore(this, id);
1344
+ }
1345
+ wrapAsPartialLocalHandler(wrappedActionExecutor) {
1346
+ const _handler = new ActionLocalHandler;
1347
+ const executor = wrappedActionExecutor;
1348
+ for (const actionKey in wrappedActionExecutor) {
1349
+ if (!this.actionSchema[actionKey]) {
1350
+ continue;
1351
+ }
1352
+ _handler.forAction(this.actionForId(actionKey), (request) => executor[request.id](request.input));
1353
+ }
1354
+ return _handler;
1355
+ }
1356
+ wrapAsLocalHandler(wrappedActionExecutor) {
1357
+ const _handler = new ActionLocalHandler;
1358
+ const executor = wrappedActionExecutor;
1359
+ return _handler.forDomain(this, (request) => executor[request.id](request.input));
1360
+ }
1361
+ hydrateContext(id, contextData) {
1362
+ return new ActionContext(this, id, {
1363
+ timeCreated: contextData.timeCreated,
1364
+ cuid: contextData.cuid,
1365
+ routing: contextData.routing.map((item) => {
1366
+ return {
1367
+ runtime: new RuntimeCoordinate(item.runtime),
1368
+ handler: item.handler,
1369
+ time: item.time
1370
+ };
1371
+ }),
1372
+ originClient: contextData.originClient ? new RuntimeCoordinate(contextData.originClient) : RuntimeCoordinate.unknown
1373
+ });
1374
+ }
1375
+ isDomainAction(action) {
1376
+ return isAction_Any_Instance(action) && action.domain === this.domain;
1377
+ }
1378
+ hydrateRequestPayload(serialized) {
1379
+ if (serialized.type !== "request" /* request */) {
1380
+ throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1381
+ expected: "request" /* request */,
1382
+ received: serialized.type
1383
+ });
1384
+ }
1385
+ if (serialized.domain !== this.domain) {
1386
+ throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1387
+ expected: this.domain,
1388
+ received: serialized.domain
1389
+ });
1390
+ }
1391
+ const id = serialized.id;
1392
+ if (!this.actionSchema[id]) {
1393
+ throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1394
+ domain: this.domain,
1395
+ actionId: serialized.id
1396
+ });
1397
+ }
1398
+ const contextAction = this.hydrateContext(id, serialized.context);
1399
+ return new ActionPayload_Request({ context: contextAction }, contextAction.deserializeInput(serialized.input), {
1400
+ time: serialized.time
1401
+ });
1402
+ }
1403
+ hydrateResultPayload(serialized) {
1404
+ if (serialized.type !== "result" /* result */) {
1405
+ throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1406
+ expected: "result" /* result */,
1407
+ received: serialized.type
1408
+ });
1409
+ }
1410
+ if (serialized.domain !== this.domain) {
1411
+ throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1412
+ expected: this.domain,
1413
+ received: serialized.domain
1414
+ });
1415
+ }
1416
+ const id = serialized.id;
1417
+ if (!this.actionSchema[id]) {
1418
+ throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1419
+ domain: this.domain,
1420
+ actionId: serialized.id
1421
+ });
1422
+ }
1423
+ const contextAction = this.hydrateContext(id, serialized.context);
1424
+ const result = serialized.result.ok ? {
1425
+ ok: true,
1426
+ output: contextAction.schema.deserializeOutput(serialized.result.output)
1427
+ } : serialized.result;
1428
+ return new ActionPayload_Result({ context: contextAction }, result, {
1429
+ time: serialized.time
1430
+ });
1431
+ }
1432
+ hydrateAnyAction(actionJson) {
1433
+ assertIsActionJson(actionJson);
1434
+ if (actionJson.form === "data" /* data */) {
1435
+ if (actionJson.type === "request" /* request */) {
1436
+ return this.hydrateRequestPayload(actionJson);
1437
+ }
1438
+ if (actionJson.type === "result" /* result */) {
1439
+ return this.hydrateResultPayload(actionJson);
1440
+ }
1441
+ }
1442
+ return this.actionForId(actionJson.id);
1443
+ }
1444
+ async runAction(request, options) {
1445
+ const allListeners = [
1446
+ ...options?.listeners ?? [],
1447
+ ...this._listeners
1448
+ ];
1449
+ return this._rootDomain._runAction(request, {
1450
+ ...options,
1451
+ listeners: allListeners
1452
+ });
1453
+ }
1454
+ createActionMap() {
1455
+ const map = {};
1456
+ for (const id in this.actionSchema) {
1457
+ map[id] = new ActionCore(this, id);
1458
+ }
1459
+ return map;
1460
+ }
1461
+ }
1462
+ // src/ActionRuntime/ActionRuntimeManager.ts
1463
+ class ActionRuntimeManager {
1464
+ _runtimes = new Map;
1465
+ _preferredRuntimeClientId = null;
1466
+ _context;
1467
+ constructor(context) {
1468
+ this._context = context ?? {};
1469
+ }
1470
+ registerRuntime(runtime2) {
1471
+ const runtimeId = runtime2.coordinate.stringId;
1472
+ if (this._runtimes.has(runtimeId)) {
1473
+ throw err_nice_action.fromId("client_runtime_already_registered" /* client_runtime_already_registered */, {
1474
+ context: this._context,
1475
+ client: runtime2.coordinate
1476
+ });
1477
+ }
1478
+ for (const id of runtime2.coordinate.toStringIds()) {
1479
+ if (this._runtimes.has(id)) {
1480
+ continue;
1481
+ }
1482
+ this._runtimes.set(id, runtime2);
1483
+ }
1484
+ }
1485
+ getRuntimeAndHandlerForAction(action, options, throwOnIssue) {
1486
+ const localRuntime = options?.targetLocalRuntime;
1487
+ if (localRuntime != null) {
1488
+ const runtime2 = throwOnIssue ? this.getBestRuntimeOrThrow(options?.targetLocalRuntime?.coordinate) : this.getBestRuntime(options?.targetLocalRuntime?.coordinate);
1489
+ if (runtime2 == null) {
1490
+ return;
1491
+ }
1492
+ const handler = runtime2._getHandlerForAction(action, options);
1493
+ if (handler != null) {
1494
+ return { handler, runtime: runtime2 };
1495
+ }
1496
+ if (throwOnIssue) {
1497
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1498
+ domain: action.domain,
1499
+ actionId: action.id,
1500
+ specifiedClient: localRuntime.coordinate
25
1501
  });
26
- } else if (type === "progress" /* progress */) {
27
- this._log("progress", actionPath, runningAction.cuid, { progress: update.progress });
28
- } else if (type === "finished" /* finished */) {
29
- const timing = this._inFlight.get(runningAction.cuid);
30
- const duration = timing != null ? time - timing.startTime : undefined;
31
- this._inFlight.delete(runningAction.cuid);
32
- const finishType = update.finishType;
33
- if (finishType === "success" /* success */) {
34
- const result = update.response?.result;
35
- if (result != null && !result.ok) {
36
- this._log("action-error", actionPath, runningAction.cuid, {
37
- ...duration != null ? { duration: `${duration}ms` } : {},
38
- error: serializeError(result.error)
39
- });
40
- } else {
41
- this._log("success", actionPath, runningAction.cuid, {
42
- ...duration != null ? { duration: `${duration}ms` } : {},
43
- ...this._options.logPayloads ? { output: result?.output } : {}
1502
+ }
1503
+ }
1504
+ for (const runtime2 of this._runtimes.values()) {
1505
+ const handler = runtime2._getHandlerForAction(action);
1506
+ if (handler) {
1507
+ return { handler, runtime: runtime2 };
1508
+ }
1509
+ }
1510
+ if (throwOnIssue) {
1511
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1512
+ domain: action.domain,
1513
+ actionId: action.id,
1514
+ specifiedClient: options?.targetLocalRuntime?.coordinate
1515
+ });
1516
+ }
1517
+ }
1518
+ getRuntimeAndHandlerForActionOrThrow(action, options) {
1519
+ return this.getRuntimeAndHandlerForAction(action, options, true);
1520
+ }
1521
+ setPreferredRuntime(runtime2) {
1522
+ const runtimeId = runtime2.coordinate.stringId;
1523
+ this._preferredRuntimeClientId = runtimeId;
1524
+ }
1525
+ getPreferredRuntime() {
1526
+ if (this._preferredRuntimeClientId) {
1527
+ const runtime2 = this._runtimes.get(this._preferredRuntimeClientId);
1528
+ if (runtime2) {
1529
+ return runtime2;
1530
+ }
1531
+ }
1532
+ return this._runtimes.values().next().value;
1533
+ }
1534
+ getBestRuntimeForSpecifier(clientSpecifier) {
1535
+ const actionClient = new RuntimeCoordinate(clientSpecifier);
1536
+ const ids = actionClient.toStringIds();
1537
+ for (const id of ids) {
1538
+ const runtime2 = this._runtimes.get(id);
1539
+ if (runtime2) {
1540
+ return runtime2;
1541
+ }
1542
+ }
1543
+ }
1544
+ getBestRuntime(clientSpecifier) {
1545
+ return clientSpecifier != null ? this.getBestRuntimeForSpecifier(clientSpecifier) : this.getPreferredRuntime();
1546
+ }
1547
+ hasRuntime(runtime2) {
1548
+ return this._runtimes.has(runtime2.coordinate.stringId);
1549
+ }
1550
+ getBestRuntimeOrThrow(specifier) {
1551
+ const runtime2 = this.getBestRuntime(specifier);
1552
+ if (!runtime2) {
1553
+ if (specifier == null) {
1554
+ throw err_nice_action.fromId("no_client_runtimes_registered" /* no_client_runtimes_registered */, {
1555
+ context: this._context
1556
+ });
1557
+ }
1558
+ throw err_nice_action.fromId("client_runtime_not_registered" /* client_runtime_not_registered */, {
1559
+ context: this._context,
1560
+ clientStringId: runtimeCoordinateToStringIds(specifier)[0]
1561
+ });
1562
+ }
1563
+ return runtime2;
1564
+ }
1565
+ }
1566
+
1567
+ // src/ActionDefinition/Domain/ActionRootDomain.ts
1568
+ class ActionRootDomain extends ActionDomainBase {
1569
+ domainDefinition;
1570
+ _actionRuntimeManager;
1571
+ constructor(domainDefinition) {
1572
+ const domainId = domainDefinition.domain;
1573
+ super({
1574
+ domain: domainId,
1575
+ allDomains: [domainId],
1576
+ actionSchema: {}
1577
+ });
1578
+ this.domainDefinition = domainDefinition;
1579
+ this._actionRuntimeManager = new ActionRuntimeManager({ domain: domainId });
1580
+ }
1581
+ createChildDomain(subDomainDef) {
1582
+ if (this.allDomains.includes(subDomainDef.domain)) {
1583
+ throw err_nice_action.fromId("domain_already_exists_in_hierarchy" /* domain_already_exists_in_hierarchy */, {
1584
+ domain: subDomainDef.domain,
1585
+ allParentDomains: this.allDomains,
1586
+ parentDomain: this.domain
1587
+ });
1588
+ }
1589
+ return new ActionDomain({
1590
+ allDomains: [...this.allDomains, subDomainDef.domain],
1591
+ domain: subDomainDef.domain,
1592
+ actionSchema: subDomainDef.actions
1593
+ }, { rootDomain: this });
1594
+ }
1595
+ _registerRuntime(runtime2) {
1596
+ this._actionRuntimeManager.registerRuntime(runtime2);
1597
+ }
1598
+ _hasRuntime(runtime2) {
1599
+ return this._actionRuntimeManager.hasRuntime(runtime2);
1600
+ }
1601
+ getRuntime(clientSpecifier) {
1602
+ return this._actionRuntimeManager.getBestRuntimeForSpecifier(clientSpecifier);
1603
+ }
1604
+ async _runAction(actionPayload, options) {
1605
+ const allListeners = [...this._listeners, ...options?.listeners ?? []];
1606
+ let handlerAndRuntime;
1607
+ try {
1608
+ handlerAndRuntime = this._actionRuntimeManager.getRuntimeAndHandlerForActionOrThrow(actionPayload, options);
1609
+ } catch (err2) {
1610
+ const runningAction2 = new RunningAction({
1611
+ context: actionPayload.context,
1612
+ request: actionPayload,
1613
+ callSite: actionPayload._callSite
1614
+ });
1615
+ runningAction2.addUpdateListeners(allListeners);
1616
+ runningAction2._failWithError(err2);
1617
+ throw err2;
1618
+ }
1619
+ const { handler, runtime: runtime2 } = handlerAndRuntime;
1620
+ actionPayload.context._setOriginClient(runtime2.coordinate);
1621
+ const runningAction = await handler.handleActionRequest(actionPayload, {
1622
+ targetLocalRuntime: runtime2
1623
+ });
1624
+ runningAction.addUpdateListeners(allListeners);
1625
+ return runningAction;
1626
+ }
1627
+ }
1628
+ // src/ActionDefinition/Domain/helpers/createRootActionDomain.ts
1629
+ var createActionRootDomain = (definition) => {
1630
+ return new ActionRootDomain(definition);
1631
+ };
1632
+ // src/ActionDefinition/Schema/ActionSchema.ts
1633
+ import { extractMessageFromStandardSchema } from "@nice-code/common-errors";
1634
+ class ActionSchema {
1635
+ _errorDeclarations = [];
1636
+ inputOptions;
1637
+ outputOptions;
1638
+ get inputSchema() {
1639
+ return this.inputOptions?.schema;
1640
+ }
1641
+ get outputSchema() {
1642
+ return this.outputOptions?.schema;
1643
+ }
1644
+ input(options) {
1645
+ this.inputOptions = options;
1646
+ return this;
1647
+ }
1648
+ output(options) {
1649
+ this.outputOptions = options;
1650
+ return this;
1651
+ }
1652
+ throws(domain, ids) {
1653
+ this._errorDeclarations.push({ _domain: domain, _ids: ids });
1654
+ return this;
1655
+ }
1656
+ serializeInput(rawInput) {
1657
+ if (this.inputOptions?.serialization) {
1658
+ return this.inputOptions.serialization.serialize(rawInput);
1659
+ }
1660
+ return rawInput;
1661
+ }
1662
+ deserializeInput(serialized) {
1663
+ if (this.inputOptions?.serialization) {
1664
+ return this.inputOptions.serialization.deserialize(serialized);
1665
+ }
1666
+ return serialized;
1667
+ }
1668
+ validateInput(value, meta) {
1669
+ if (this.inputOptions?.schema == null) {
1670
+ return value;
1671
+ }
1672
+ const result = this.inputOptions.schema["~standard"].validate(value);
1673
+ if (result instanceof Promise) {
1674
+ throw err_nice_action.fromId("action_input_validation_promise" /* action_input_validation_promise */, {
1675
+ domain: meta.domain,
1676
+ actionId: meta.actionId
1677
+ });
1678
+ }
1679
+ if (result.issues != null) {
1680
+ throw err_nice_action.fromId("action_input_validation_failed" /* action_input_validation_failed */, {
1681
+ domain: meta.domain,
1682
+ actionId: meta.actionId,
1683
+ validationMessage: extractMessageFromStandardSchema(result)
1684
+ });
1685
+ }
1686
+ return result.value;
1687
+ }
1688
+ validateOutput(value, meta) {
1689
+ if (this.outputOptions?.schema == null) {
1690
+ return value;
1691
+ }
1692
+ const result = this.outputOptions.schema["~standard"].validate(value);
1693
+ if (result instanceof Promise) {
1694
+ throw err_nice_action.fromId("action_output_validation_promise" /* action_output_validation_promise */, {
1695
+ domain: meta.domain,
1696
+ actionId: meta.actionId
1697
+ });
1698
+ }
1699
+ if (result.issues != null) {
1700
+ throw err_nice_action.fromId("action_output_validation_failed" /* action_output_validation_failed */, {
1701
+ domain: meta.domain,
1702
+ actionId: meta.actionId,
1703
+ validationMessage: extractMessageFromStandardSchema(result)
1704
+ });
1705
+ }
1706
+ return result.value;
1707
+ }
1708
+ serializeOutput(rawOutput) {
1709
+ if (this.outputOptions?.serialization) {
1710
+ return this.outputOptions.serialization.serialize(rawOutput);
1711
+ }
1712
+ return rawOutput;
1713
+ }
1714
+ deserializeOutput(serialized) {
1715
+ if (this.outputOptions?.serialization) {
1716
+ return this.outputOptions.serialization.deserialize(serialized);
1717
+ }
1718
+ return serialized;
1719
+ }
1720
+ }
1721
+ var actionSchema = () => {
1722
+ return new ActionSchema;
1723
+ };
1724
+ // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
1725
+ import { nanoid as nanoid4 } from "nanoid";
1726
+
1727
+ // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
1728
+ import { err as err2 } from "@nice-code/error";
1729
+
1730
+ // src/ActionRuntime/Handler/ExternalClient/err_nice_external_client.ts
1731
+ var err_nice_external_client = err_nice_action.createChildDomain({
1732
+ domain: "err_nice_external_client",
1733
+ schema: {}
1734
+ });
1735
+
1736
+ // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
1737
+ var EErrId_NiceTransport;
1738
+ ((EErrId_NiceTransport2) => {
1739
+ EErrId_NiceTransport2["timeout"] = "timeout";
1740
+ EErrId_NiceTransport2["not_found"] = "not_found";
1741
+ EErrId_NiceTransport2["unsupported"] = "unsupported";
1742
+ EErrId_NiceTransport2["initialization_failed"] = "initialization_failed";
1743
+ EErrId_NiceTransport2["send_failed"] = "send_failed";
1744
+ EErrId_NiceTransport2["invalid_action_response"] = "invalid_action_response";
1745
+ })(EErrId_NiceTransport ||= {});
1746
+ var err_nice_transport = err_nice_external_client.createChildDomain({
1747
+ domain: "err_nice_transport",
1748
+ schema: {
1749
+ ["timeout" /* timeout */]: err2({
1750
+ message: ({ timeout }) => `ActionConnect transport timed out after ${timeout}ms.`
1751
+ }),
1752
+ ["not_found" /* not_found */]: err2({
1753
+ message: ({ actionId }) => `No connected transport found for action "${actionId}".`
1754
+ }),
1755
+ ["unsupported" /* unsupported */]: err2({
1756
+ message: ({ transportTypes }) => `${transportTypes.length} Transport(s) [${transportTypes.join(", ")}] found but returned "unsupported" status.`
1757
+ }),
1758
+ ["initialization_failed" /* initialization_failed */]: err2({
1759
+ message: ({ actionId }) => `Transports found for action "${actionId}", but none are ready.`
1760
+ }),
1761
+ ["send_failed" /* send_failed */]: err2({
1762
+ message: ({ actionId, httpStatusCode, message }) => `Failed to send action "${actionId}" [${httpStatusCode ?? "Unknown status"}]: ${message ?? "Unknown error"}.`,
1763
+ httpStatusCode: ({ httpStatusCode }) => httpStatusCode ?? 500
1764
+ }),
1765
+ ["invalid_action_response" /* invalid_action_response */]: err2({
1766
+ message: ({ actionId }) => `Invalid action response JSON structure for action "${actionId}"`
1767
+ })
1768
+ }
1769
+ });
1770
+
1771
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Transport.types.ts
1772
+ var ETransportType;
1773
+ ((ETransportType2) => {
1774
+ ETransportType2["ws"] = "ws";
1775
+ ETransportType2["http"] = "http";
1776
+ ETransportType2["custom"] = "custom";
1777
+ })(ETransportType ||= {});
1778
+ var ETransportStatus;
1779
+ ((ETransportStatus2) => {
1780
+ ETransportStatus2["uninitialized"] = "uninitialized";
1781
+ ETransportStatus2["unsupported"] = "unsupported";
1782
+ ETransportStatus2["initializing"] = "initializing";
1783
+ ETransportStatus2["ready"] = "ready";
1784
+ ETransportStatus2["failed"] = "failed";
1785
+ })(ETransportStatus ||= {});
1786
+
1787
+ // src/ActionRuntime/Handler/ExternalClient/Transport/ConnectionTransportManager.ts
1788
+ class ConnectionTransportManager {
1789
+ _cache;
1790
+ _transports = [];
1791
+ constructor(_cache) {
1792
+ this._cache = _cache;
1793
+ }
1794
+ addTransport(transport) {
1795
+ this._transports.push(transport);
1796
+ }
1797
+ async getReadyTransport(routeActionParams) {
1798
+ const initializingWaiters = [];
1799
+ const unavailableTransports = [];
1800
+ const action = routeActionParams.action;
1801
+ for (const transport of this._transports) {
1802
+ const cacheKey = transport.getCacheKey(routeActionParams);
1803
+ if (cacheKey != null) {
1804
+ const cached = this._cache.get(cacheKey);
1805
+ if (cached != null) {
1806
+ if (cached instanceof Promise) {
1807
+ initializingWaiters.push(cached);
1808
+ continue;
1809
+ }
1810
+ return { ...cached, transport };
1811
+ }
1812
+ }
1813
+ const statusInfo = transport.getTransport(routeActionParams);
1814
+ if (statusInfo.status === "ready" /* ready */) {
1815
+ const readyData = statusInfo.readyData;
1816
+ if (cacheKey != null) {
1817
+ this._cache.set(cacheKey, { methods: readyData, transport });
1818
+ readyData.addOnDisconnectListener?.(() => this._cache.delete(cacheKey));
1819
+ }
1820
+ return { methods: readyData, transport };
1821
+ }
1822
+ if (statusInfo.status === "unsupported" /* unsupported */) {
1823
+ unavailableTransports.push(transport);
1824
+ continue;
1825
+ }
1826
+ if (statusInfo.status === "initializing" /* initializing */) {
1827
+ const promise = statusInfo.initializationPromise.then((info) => {
1828
+ if (info.status === "failed" /* failed */) {
1829
+ throw info.error;
1830
+ }
1831
+ if (info.status === "unsupported" /* unsupported */) {
1832
+ throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1833
+ transportTypes: [transport.type]
44
1834
  });
45
1835
  }
46
- } else if (finishType === "failed" /* failed */) {
47
- this._log("failed", actionPath, runningAction.cuid, {
48
- ...duration != null ? { duration: `${duration}ms` } : {},
49
- error: serializeError(update.error)
50
- });
51
- } else {
52
- this._log("aborted", actionPath, runningAction.cuid, {
53
- ...duration != null ? { duration: `${duration}ms` } : {},
54
- ...update.reason != null ? { reason: String(update.reason) } : {}
55
- });
1836
+ const readyData = info.readyData;
1837
+ if (cacheKey != null) {
1838
+ this._cache.set(cacheKey, { methods: readyData, transport });
1839
+ readyData.addOnDisconnectListener?.(() => this._cache.delete(cacheKey));
1840
+ }
1841
+ return { methods: readyData, transport };
1842
+ }).catch((e) => {
1843
+ if (cacheKey != null)
1844
+ this._cache.delete(cacheKey);
1845
+ throw e;
1846
+ });
1847
+ if (cacheKey != null) {
1848
+ this._cache.set(cacheKey, promise);
56
1849
  }
1850
+ initializingWaiters.push(promise);
1851
+ }
1852
+ }
1853
+ if (initializingWaiters.length === 0) {
1854
+ if (unavailableTransports.length > 0) {
1855
+ throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1856
+ transportTypes: unavailableTransports.map((t) => t.type)
1857
+ });
57
1858
  }
1859
+ throw err_nice_transport.fromId("not_found" /* not_found */, {
1860
+ actionId: action.id
1861
+ });
1862
+ }
1863
+ try {
1864
+ return await Promise.any(initializingWaiters).then();
1865
+ } catch (e) {
1866
+ throw err_nice_transport.fromId("initialization_failed" /* initialization_failed */, {
1867
+ actionId: action.id
1868
+ }).withOriginError(e);
1869
+ }
1870
+ }
1871
+ }
1872
+
1873
+ // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
1874
+ class ActionExternalClientHandler extends ActionHandler {
1875
+ externalClient;
1876
+ handlerType = "external" /* external */;
1877
+ cuid;
1878
+ _defaultTimeout;
1879
+ _transportCache = new Map;
1880
+ transportManager = new ConnectionTransportManager(this._transportCache);
1881
+ _incomingActionDataListeners = [];
1882
+ actionRouter = new ActionRouter({
1883
+ contextType: "handler_route" /* handler_route */,
1884
+ handler: this
1885
+ });
1886
+ constructor({
1887
+ runtimeCoordinate: externalClientSpecifier,
1888
+ transports,
1889
+ defaultTimeout
1890
+ }) {
1891
+ super();
1892
+ this.externalClient = externalClientSpecifier;
1893
+ this.cuid = nanoid4();
1894
+ this._defaultTimeout = defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;
1895
+ for (const transport of transports) {
1896
+ const connection = transport._createConnection({
1897
+ resolvers: {
1898
+ onIncomingActionDataJson: (json) => {
1899
+ for (const l of this._incomingActionDataListeners)
1900
+ l(json);
1901
+ }
1902
+ }
1903
+ });
1904
+ connection.definition = transport;
1905
+ this.transportManager.addTransport(connection);
1906
+ }
1907
+ }
1908
+ forDomain(domain) {
1909
+ this.actionRouter.forDomain(domain, true);
1910
+ return this;
1911
+ }
1912
+ forAction(action) {
1913
+ this.actionRouter.forAction(action, true);
1914
+ return this;
1915
+ }
1916
+ forActionIds(domain, ids) {
1917
+ this.actionRouter.forActionIds(domain, ids, true);
1918
+ return this;
1919
+ }
1920
+ _setIncomingActionDataListener(listener) {
1921
+ this._incomingActionDataListeners.push(listener);
1922
+ }
1923
+ async handleActionRequest(action, config) {
1924
+ const localRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1925
+ const localClient = localRuntime.coordinate;
1926
+ const incomingTimeout = config?.timeout ?? this._defaultTimeout;
1927
+ const parentCuid = peekHandlerCuid();
1928
+ const callSite = action._callSite ?? new Error().stack;
1929
+ const { methods, transport } = await this.transportManager.getReadyTransport({
1930
+ action,
1931
+ localClient,
1932
+ externalClient: this.externalClient
1933
+ });
1934
+ action.context.addRouteItem({
1935
+ runtime: localClient,
1936
+ handler: this.toHandlerRouteItem(transport, {
1937
+ action,
1938
+ localClient,
1939
+ externalClient: this.externalClient
1940
+ }),
1941
+ time: Date.now()
58
1942
  });
1943
+ const runningAction = new RunningAction({
1944
+ context: action.context,
1945
+ request: action,
1946
+ parentCuid,
1947
+ callSite
1948
+ });
1949
+ localRuntime.registerRunningAction(runningAction);
1950
+ const routeActionParams = {
1951
+ action,
1952
+ runningAction,
1953
+ localClient,
1954
+ externalClient: this.externalClient,
1955
+ timeout: incomingTimeout
1956
+ };
1957
+ if (action.type === "request" /* request */ && methods.updateRunConfig != null) {
1958
+ const runConfig = methods.updateRunConfig(routeActionParams);
1959
+ routeActionParams.timeout = runConfig?.timeout ?? incomingTimeout;
1960
+ }
1961
+ try {
1962
+ methods.sendActionData(routeActionParams);
1963
+ } catch (err3) {
1964
+ runningAction._abort(err3);
1965
+ }
1966
+ return runningAction;
59
1967
  }
60
- _log(event, actionPath, cuid, data) {
61
- const { logger, format } = this._options;
62
- if (format === "json") {
63
- logger(JSON.stringify({ time: new Date().toISOString(), event, action: actionPath, cuid, ...data }));
64
- return;
1968
+ async sendReturnPayload(payload, config) {
1969
+ const localClient = config.targetLocalRuntime.coordinate;
1970
+ try {
1971
+ const { methods } = await this.transportManager.getReadyTransport({
1972
+ action: payload,
1973
+ localClient,
1974
+ externalClient: this.externalClient
1975
+ });
1976
+ if (methods.sendReturnData == null)
1977
+ return false;
1978
+ methods.sendReturnData(payload);
1979
+ return true;
1980
+ } catch {
1981
+ return false;
65
1982
  }
66
- const prefix = PRETTY_PREFIX[event] ?? `[${event}]`;
67
- const suffix = Object.keys(data).length > 0 ? ` ${formatPrettyData(data)}` : "";
68
- logger(`${prefix} ${actionPath} cuid=${cuid}${suffix}`);
1983
+ }
1984
+ toJsonObject() {
1985
+ return {
1986
+ type: this.handlerType,
1987
+ client: this.externalClient
1988
+ };
1989
+ }
1990
+ toHandlerRouteItem(transport, input) {
1991
+ return {
1992
+ type: this.handlerType,
1993
+ client: this.externalClient,
1994
+ transOrd: transport.transOrd,
1995
+ transType: transport.type,
1996
+ transInfo: transport.getRouteInfo(input)
1997
+ };
1998
+ }
1999
+ clearTransportCache() {
2000
+ this._transportCache.clear();
69
2001
  }
70
2002
  }
71
- var PRETTY_PREFIX = {
72
- started: "[nice-action] ►",
73
- progress: "[nice-action] ",
74
- success: "[nice-action] ✓",
75
- failed: "[nice-action] ✗",
76
- aborted: "[nice-action] ○"
2003
+ var createExternalClientHandler = (config) => {
2004
+ return new ActionExternalClientHandler(config);
77
2005
  };
78
- function formatPrettyData(data) {
79
- return Object.entries(data).map(([k, v]) => `${k}=${safeStringify(v)}`).join(" ");
80
- }
81
- function safeStringify(value) {
82
- if (value === undefined)
83
- return "undefined";
84
- if (value === null)
85
- return "null";
86
- if (typeof value === "string")
87
- return `"${value}"`;
88
- try {
89
- return JSON.stringify(value);
90
- } catch {
91
- return String(value);
2006
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Transport.ts
2007
+ class Transport {
2008
+ }
2009
+
2010
+ // src/ActionRuntime/Handler/ExternalClient/Transport/helpers/addTransportStatusMetadata.ts
2011
+ function addTransportStatusMetadata(transportStatus) {
2012
+ if (transportStatus.status === "ready" /* ready */) {
2013
+ return {
2014
+ status: "ready" /* ready */,
2015
+ readyData: transportStatus.readyData
2016
+ };
92
2017
  }
2018
+ if (transportStatus.status === "initializing" /* initializing */) {
2019
+ return {
2020
+ status: "initializing" /* initializing */,
2021
+ initializationPromise: transportStatus.initializationPromise,
2022
+ timeStarted: Date.now()
2023
+ };
2024
+ }
2025
+ if (transportStatus.status === "failed" /* failed */) {
2026
+ return {
2027
+ status: "failed" /* failed */,
2028
+ error: transportStatus.error,
2029
+ timeFailed: Date.now()
2030
+ };
2031
+ }
2032
+ if (transportStatus.status === "unsupported" /* unsupported */) {
2033
+ return {
2034
+ status: "unsupported" /* unsupported */
2035
+ };
2036
+ }
2037
+ return {
2038
+ status: "uninitialized" /* uninitialized */
2039
+ };
93
2040
  }
94
- function serializeError(err) {
95
- if (err == null)
96
- return err;
97
- if (err instanceof Error)
98
- return { message: err.message, name: err.name, stack: err.stack };
99
- if (typeof err === "object") {
100
- try {
101
- return JSON.parse(JSON.stringify(err));
102
- } catch {
103
- return String(err);
2041
+
2042
+ // src/ActionRuntime/Handler/ExternalClient/Transport/TransportConnection.ts
2043
+ var transportOrd = 0;
2044
+
2045
+ class TransportConnection {
2046
+ def;
2047
+ transOrd = transportOrd++;
2048
+ type;
2049
+ initialized;
2050
+ definition;
2051
+ constructor(def) {
2052
+ this.def = def;
2053
+ this.type = def.type;
2054
+ this.initialized = def.initialize();
2055
+ }
2056
+ getRouteInfo(input) {
2057
+ return this.definition?.getRouteInfo(input);
2058
+ }
2059
+ _getCacheKey(input) {
2060
+ const parts = this.initialized.getTransportCacheKey?.(input);
2061
+ if (parts == null)
2062
+ return null;
2063
+ return parts.join("\x00");
2064
+ }
2065
+ getCacheKey(input) {
2066
+ const inner = this._getCacheKey(input);
2067
+ if (inner == null)
2068
+ return null;
2069
+ return `${this.transOrd}:${inner}`;
2070
+ }
2071
+ getTransport(input) {
2072
+ return this._processTransportStatus(input);
2073
+ }
2074
+ _processTransportStatus(input) {
2075
+ const transportStatusInfo = addTransportStatusMetadata(this.initialized.getTransport(input));
2076
+ if (transportStatusInfo.status !== "initializing" /* initializing */ && transportStatusInfo.status !== "ready" /* ready */) {
2077
+ return transportStatusInfo;
104
2078
  }
2079
+ if (transportStatusInfo.status === "initializing" /* initializing */) {
2080
+ const promiseForReadyData = transportStatusInfo.initializationPromise.then((result) => {
2081
+ if (result.status === "ready" /* ready */) {
2082
+ return {
2083
+ status: "ready" /* ready */,
2084
+ readyData: this._finalizeTransportMethods(result.readyData)
2085
+ };
2086
+ }
2087
+ return result;
2088
+ });
2089
+ return {
2090
+ status: "initializing" /* initializing */,
2091
+ timeStarted: transportStatusInfo.timeStarted,
2092
+ initializationPromise: promiseForReadyData
2093
+ };
2094
+ }
2095
+ return {
2096
+ status: "ready" /* ready */,
2097
+ readyData: this._finalizeTransportMethods(transportStatusInfo.readyData)
2098
+ };
105
2099
  }
106
- return err;
107
2100
  }
108
- function defaultConsoleLogger(message) {
109
- console.log(message);
2101
+
2102
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Custom/CustomConnection.ts
2103
+ class CustomConnection extends TransportConnection {
2104
+ constructor(def) {
2105
+ super({
2106
+ ...def,
2107
+ type: "custom" /* custom */
2108
+ });
2109
+ }
2110
+ _finalizeTransportMethods(inputs) {
2111
+ return {
2112
+ sendActionData: inputs.sendActionData,
2113
+ sendReturnData: inputs.sendReturnData,
2114
+ updateRunConfig: inputs.updateRunConfig
2115
+ };
2116
+ }
110
2117
  }
111
- // src/devtools/core/ActionDevtoolsCore.ts
112
- function serializeErrorForDisplay(error) {
113
- if (error != null && typeof error === "object" && error.name === "NiceError" && typeof error.toJsonObject === "function") {
114
- return error.toJsonObject();
2118
+
2119
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Custom/CustomTransport.ts
2120
+ class CustomTransport extends Transport {
2121
+ options;
2122
+ type = "custom" /* custom */;
2123
+ constructor(options) {
2124
+ super();
2125
+ this.options = options;
2126
+ }
2127
+ static create(options) {
2128
+ return new CustomTransport({ ...options, mode: "send" });
2129
+ }
2130
+ static createAdvanced(options) {
2131
+ return new CustomTransport({ ...options, mode: "advanced" });
2132
+ }
2133
+ _createConnection(_ctx) {
2134
+ const options = this.options;
2135
+ let getTransport;
2136
+ if (options.mode === "advanced") {
2137
+ getTransport = options.getTransport;
2138
+ } else {
2139
+ getTransport = () => ({
2140
+ status: "ready" /* ready */,
2141
+ readyData: {
2142
+ sendActionData: options.sendActionData,
2143
+ sendReturnData: options.sendReturnData,
2144
+ updateRunConfig: options.updateRunConfig,
2145
+ closeTransport: options.closeTransport ?? (() => {})
2146
+ }
2147
+ });
2148
+ }
2149
+ return new CustomConnection({
2150
+ initialize: () => ({
2151
+ getTransportCacheKey: options.getTransportCacheKey,
2152
+ getTransport
2153
+ })
2154
+ });
2155
+ }
2156
+ getRouteInfo(input) {
2157
+ if (this.options.getRouteInfo != null)
2158
+ return this.options.getRouteInfo(input);
2159
+ return {
2160
+ type: "custom" /* custom */,
2161
+ summary: this.options.label ?? "custom"
2162
+ };
115
2163
  }
116
- return error;
117
2164
  }
118
- function extractRouting(context) {
119
- return (context?.routing ?? []).map((item) => {
120
- const handler = item.handler;
121
- const isExternal = handler?.type === "external";
2165
+ // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport_ws.ts
2166
+ import { err as err3 } from "@nice-code/error";
2167
+ var EErrId_NiceTransport_WebSocket;
2168
+ ((EErrId_NiceTransport_WebSocket2) => {
2169
+ EErrId_NiceTransport_WebSocket2["ws_disconnected"] = "ws_disconnected";
2170
+ EErrId_NiceTransport_WebSocket2["ws_create_failed"] = "ws_create_failed";
2171
+ EErrId_NiceTransport_WebSocket2["ws_error"] = "ws_error";
2172
+ })(EErrId_NiceTransport_WebSocket ||= {});
2173
+ var err_nice_transport_ws = err_nice_transport.createChildDomain({
2174
+ domain: "ws_transport",
2175
+ schema: {
2176
+ ["ws_disconnected" /* ws_disconnected */]: err3({
2177
+ message: () => `WebSocket transport disconnected.`
2178
+ }),
2179
+ ["ws_create_failed" /* ws_create_failed */]: err3({
2180
+ message: ({ originalError }) => `Failed to create WebSocket transport.${originalError ? ` Original error: ${originalError.message}` : ""}`
2181
+ }),
2182
+ ["ws_error" /* ws_error */]: err3({
2183
+ message: ({ originalError }) => `WebSocket transport error.${originalError ? ` Original error: ${originalError.message}` : ""}`
2184
+ })
2185
+ }
2186
+ });
2187
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Http/HttpConnection.ts
2188
+ import { castNiceError as castNiceError3, isNiceErrorObject as isNiceErrorObject2, NiceError as NiceError2 } from "@nice-code/error";
2189
+ class HttpConnection extends TransportConnection {
2190
+ constructor(def) {
2191
+ super({
2192
+ ...def,
2193
+ type: "http" /* http */
2194
+ });
2195
+ }
2196
+ _finalizeTransportMethods(methods) {
122
2197
  return {
123
- runtime: {
124
- envId: item.runtime?.envId ?? "unknown",
125
- perId: item.runtime?.perId,
126
- insId: item.runtime?.insId
2198
+ sendActionData: (input) => {
2199
+ const request = methods.createRequest(input);
2200
+ this.send({ ...input, params: { request }, runningAction: input.runningAction }).catch((err4) => input.runningAction._abort(err4));
127
2201
  },
128
- handlerType: isExternal ? "external" : "local",
129
- handlerClient: isExternal && handler.client != null ? {
130
- envId: handler.client.envId ?? "unknown",
131
- perId: handler.client.perId,
132
- insId: handler.client.insId
133
- } : undefined,
134
- transport: isExternal ? handler.transType : undefined,
135
- transportSummary: isExternal ? handler.transInfo?.summary : undefined,
136
- transportUrl: isExternal ? handler.transInfo?.url : undefined,
137
- time: item.time
2202
+ updateRunConfig: methods.updateRunConfig
138
2203
  };
139
- });
2204
+ }
2205
+ async send(input) {
2206
+ const {
2207
+ action,
2208
+ runningAction,
2209
+ timeout,
2210
+ params: { request }
2211
+ } = input;
2212
+ const wire = action.toJsonObject();
2213
+ const ac = new AbortController;
2214
+ let timedOut = false;
2215
+ const timeoutId = setTimeout(() => {
2216
+ timedOut = true;
2217
+ ac.abort();
2218
+ }, timeout);
2219
+ const unsubscribe = input.runningAction.addUpdateListeners([
2220
+ (update) => {
2221
+ if (update.type === "finished" /* finished */) {
2222
+ clearTimeout(timeoutId);
2223
+ ac.abort();
2224
+ }
2225
+ }
2226
+ ]);
2227
+ try {
2228
+ const res = await fetch(request.url, {
2229
+ method: "POST",
2230
+ headers: { "Content-Type": "application/json", ...request.headers },
2231
+ body: request.body ?? JSON.stringify(wire),
2232
+ signal: ac.signal
2233
+ });
2234
+ if (!res.ok) {
2235
+ if (action.type === "request" /* request */) {
2236
+ try {
2237
+ const jsonData = await res.json();
2238
+ if (isActionPayload_Result_JsonObject(jsonData)) {
2239
+ runningAction._completeWithResult(action._domain.hydrateResultPayload(jsonData));
2240
+ } else if (isNiceErrorObject2(jsonData)) {
2241
+ runningAction._completeWithResult(action.errorResult(castNiceError3(jsonData)));
2242
+ } else {
2243
+ runningAction._completeWithResult(action.errorResult(err_nice_transport.fromId("invalid_action_response" /* invalid_action_response */, {
2244
+ actionId: action.id
2245
+ })));
2246
+ }
2247
+ } catch (e) {
2248
+ throw err_nice_transport.fromId("send_failed" /* send_failed */, {
2249
+ actionState: action.type,
2250
+ actionId: action.id,
2251
+ httpStatusCode: res.status,
2252
+ message: e.message
2253
+ }).withOriginError(e);
2254
+ }
2255
+ } else {
2256
+ let text;
2257
+ try {
2258
+ text = await res.text();
2259
+ } catch (e) {
2260
+ console.warn(`Failed to read error response body for failed HTTP request in HttpConnection:`, e);
2261
+ }
2262
+ throw err_nice_transport.fromId("send_failed" /* send_failed */, {
2263
+ actionState: action.type,
2264
+ actionId: action.id,
2265
+ httpStatusCode: res.status,
2266
+ message: text ?? `HTTP error with status ${res.status}`
2267
+ });
2268
+ }
2269
+ return;
2270
+ }
2271
+ if (action.type === "request" /* request */) {
2272
+ const json = await res.json();
2273
+ if (!isActionPayload_Result_JsonObject(json)) {
2274
+ throw err_nice_transport.fromId("invalid_action_response" /* invalid_action_response */, {
2275
+ actionId: action.id
2276
+ });
2277
+ }
2278
+ runningAction._completeWithResult(action._domain.hydrateResultPayload(json));
2279
+ }
2280
+ } catch (err4) {
2281
+ if (timedOut) {
2282
+ throw err_nice_transport.fromId("timeout" /* timeout */, { timeout });
2283
+ }
2284
+ if (err4 instanceof NiceError2) {
2285
+ throw err4;
2286
+ }
2287
+ throw err_nice_transport.fromId("send_failed" /* send_failed */, {
2288
+ actionState: action.type,
2289
+ actionId: action.id,
2290
+ message: err4 instanceof Error ? err4.message : String(err4)
2291
+ }).withOriginError(err4 instanceof Error ? err4 : undefined);
2292
+ } finally {
2293
+ clearTimeout(timeoutId);
2294
+ unsubscribe();
2295
+ }
2296
+ }
140
2297
  }
141
- function extractMeta(context) {
142
- return {
143
- timeCreated: context?.timeCreated ?? Date.now(),
144
- originClient: {
145
- envId: context?.originClient?.envId ?? "unknown",
146
- perId: context?.originClient?.perId,
147
- insId: context?.originClient?.insId
148
- },
149
- routing: extractRouting(context)
150
- };
2298
+
2299
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Http/HttpTransport.ts
2300
+ function shortPath(url) {
2301
+ try {
2302
+ return new URL(url).pathname || url;
2303
+ } catch {
2304
+ return url;
2305
+ }
2306
+ }
2307
+
2308
+ class HttpTransport extends Transport {
2309
+ options;
2310
+ type = "http" /* http */;
2311
+ constructor(options) {
2312
+ super();
2313
+ this.options = options;
2314
+ }
2315
+ static create(options) {
2316
+ return new HttpTransport(options);
2317
+ }
2318
+ _createConnection(_ctx) {
2319
+ return new HttpConnection({
2320
+ initialize: () => ({
2321
+ getTransportCacheKey: this.options.getTransportCacheKey,
2322
+ getTransport: () => ({
2323
+ status: "ready" /* ready */,
2324
+ readyData: {
2325
+ createRequest: this.options.createRequest,
2326
+ updateRunConfig: this.options.updateRunConfig
2327
+ }
2328
+ })
2329
+ })
2330
+ });
2331
+ }
2332
+ getRouteInfo(input) {
2333
+ const { url } = this.options.createRequest(input);
2334
+ return {
2335
+ type: "http" /* http */,
2336
+ method: "POST",
2337
+ url,
2338
+ summary: `POST ${shortPath(url)}`
2339
+ };
2340
+ }
2341
+ }
2342
+ // src/ActionRuntime/Handler/ExternalClient/Transport/helpers/createUnsetTransportResolvers.ts
2343
+ var createUnsetTransportResolvers = (type) => ({
2344
+ onIncomingActionDataJson: (json) => {
2345
+ console.warn(`Received incoming action JSON [${json.domain}:${json.id}] on Transport [${type}] but no incoming data listener has been set.`);
2346
+ }
2347
+ });
2348
+
2349
+ // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/ws_util.ts
2350
+ function shortWs(url) {
2351
+ try {
2352
+ const u = new URL(url);
2353
+ return `${u.host}${u.pathname}`;
2354
+ } catch {
2355
+ return url;
2356
+ }
151
2357
  }
152
2358
 
153
- class ActionDevtoolsCore {
154
- _entries = [];
155
- _listeners = new Set;
156
- constructor(_options = {}) {}
157
- attachToDomain(domain) {
158
- return domain.addActionListener((update) => {
159
- const { runningAction, type, time } = update;
160
- if (type === "started" /* started */) {
161
- const entry = {
162
- cuid: runningAction.cuid,
163
- actionId: runningAction.id,
164
- domain: runningAction.domain,
165
- allDomains: [...runningAction.allDomains],
166
- status: "running",
167
- startTime: time,
168
- input: runningAction.state?.request?.input,
169
- inputHash: runningAction.state?.request?.inputHash,
170
- progressUpdates: [],
171
- meta: extractMeta(runningAction.context),
172
- parentCuid: runningAction.parentCuid,
173
- callSite: runningAction.callSite
2359
+ // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketConnection.ts
2360
+ class WebSocketConnection extends TransportConnection {
2361
+ resolvers;
2362
+ _abortSet = new Set;
2363
+ _liveSocketUrl;
2364
+ constructor(def, resolvers) {
2365
+ super({ ...def, type: "ws" /* ws */ });
2366
+ this.resolvers = resolvers ?? createUnsetTransportResolvers("ws" /* ws */);
2367
+ }
2368
+ _getCacheKey(_input) {
2369
+ return this.initialized.getTransportCacheKey?.(_input).join("\x00") ?? "";
2370
+ }
2371
+ _processTransportStatus(input) {
2372
+ const transportStatusInfo = addTransportStatusMetadata(this.initialized.getTransport(input));
2373
+ if (transportStatusInfo.status !== "initializing" /* initializing */ && transportStatusInfo.status !== "ready" /* ready */) {
2374
+ return transportStatusInfo;
2375
+ }
2376
+ if (transportStatusInfo.status === "ready" /* ready */) {
2377
+ const ws = transportStatusInfo.readyData.ws;
2378
+ if (ws.readyState !== WebSocket.OPEN) {
2379
+ const initialization = async () => {
2380
+ await new Promise((resolve, reject) => {
2381
+ ws.addEventListener("open", () => resolve(), { once: true });
2382
+ ws.addEventListener("error", (event) => reject(event), { once: true });
2383
+ ws.addEventListener("close", (event) => reject(new Error(`WebSocket closed before open: code=${event.code}`)), { once: true });
2384
+ });
2385
+ return {
2386
+ status: "ready" /* ready */,
2387
+ readyData: this._finalizeTransportMethods(transportStatusInfo.readyData)
2388
+ };
174
2389
  };
175
- this._entries = [entry, ...this._entries];
176
- this._notify();
177
- } else if (type === "progress" /* progress */) {
178
- this._updateEntry(runningAction.cuid, (e) => ({
179
- ...e,
180
- progressUpdates: [...e.progressUpdates, update.progress]
181
- }));
182
- } else if (type === "finished" /* finished */) {
183
- this._updateEntry(runningAction.cuid, (e) => {
184
- const finishedRoutingContext = update.response?.context ?? runningAction.context;
185
- const base = {
186
- ...e,
187
- endTime: time,
188
- meta: { ...e.meta, routing: extractRouting(finishedRoutingContext) }
2390
+ return {
2391
+ status: "initializing" /* initializing */,
2392
+ timeStarted: Date.now(),
2393
+ initializationPromise: initialization()
2394
+ };
2395
+ }
2396
+ }
2397
+ if (transportStatusInfo.status === "initializing" /* initializing */) {
2398
+ const promiseForReadyData = transportStatusInfo.initializationPromise.then(async (result) => {
2399
+ if (result.status === "ready" /* ready */) {
2400
+ const ws = result.readyData.ws;
2401
+ await new Promise((resolve, reject) => {
2402
+ ws.addEventListener("open", () => resolve(), { once: true });
2403
+ ws.addEventListener("error", (event) => reject(event), { once: true });
2404
+ ws.addEventListener("close", (event) => reject(new Error(`WebSocket closed before open: code=${event.code}`)), { once: true });
2405
+ });
2406
+ return {
2407
+ status: "ready" /* ready */,
2408
+ readyData: this._finalizeTransportMethods(result.readyData)
189
2409
  };
190
- const finishType = update.finishType;
191
- if (finishType === "success" /* success */) {
192
- const result = update.response?.result;
193
- const outputHash = update.response?.outputHash;
194
- if (result != null && !result.ok) {
195
- const rawError = result.error;
196
- const errorStack2 = rawError instanceof Error ? rawError.stack : undefined;
197
- return {
198
- ...base,
199
- status: "action-error",
200
- outputHash,
201
- error: serializeErrorForDisplay(rawError),
202
- errorStack: errorStack2
203
- };
2410
+ }
2411
+ return result;
2412
+ });
2413
+ return {
2414
+ status: "initializing" /* initializing */,
2415
+ initializationPromise: promiseForReadyData,
2416
+ timeStarted: transportStatusInfo.timeStarted
2417
+ };
2418
+ }
2419
+ return {
2420
+ status: "ready" /* ready */,
2421
+ readyData: this._finalizeTransportMethods(transportStatusInfo.readyData)
2422
+ };
2423
+ }
2424
+ getRouteInfo(input) {
2425
+ const base = this.definition?.getRouteInfo(input);
2426
+ if (base?.url != null || this._liveSocketUrl == null)
2427
+ return base;
2428
+ return {
2429
+ type: "ws" /* ws */,
2430
+ ...base,
2431
+ url: this._liveSocketUrl,
2432
+ summary: `ws ${shortWs(this._liveSocketUrl)}`
2433
+ };
2434
+ }
2435
+ _finalizeTransportMethods(wsData) {
2436
+ const ws = wsData.ws;
2437
+ const disconnectListeners = [];
2438
+ if (ws.url != null && ws.url !== "")
2439
+ this._liveSocketUrl = ws.url;
2440
+ const sendActionData = (inputs) => {
2441
+ const { action, runningAction, timeout } = inputs;
2442
+ if (action.type === "request" /* request */) {
2443
+ this._abortSet.add(runningAction);
2444
+ const timeoutId = setTimeout(() => {
2445
+ runningAction._abort(err_nice_transport.fromId("timeout" /* timeout */, { timeout }));
2446
+ }, timeout);
2447
+ runningAction.addUpdateListeners([
2448
+ (update) => {
2449
+ if (update.type === "finished" /* finished */) {
2450
+ clearTimeout(timeoutId);
2451
+ this._abortSet.delete(runningAction);
204
2452
  }
205
- return { ...base, status: "success", output: result?.output, outputHash };
206
2453
  }
207
- if (finishType === "failed" /* failed */) {
208
- const rawError = update.error;
209
- const errorStack2 = rawError instanceof Error ? rawError.stack : undefined;
210
- return { ...base, status: "failed", error: serializeErrorForDisplay(rawError), errorStack: errorStack2 };
211
- }
212
- const abortReason = update.reason;
213
- const errorStack = abortReason instanceof Error ? abortReason.stack : undefined;
214
- return { ...base, status: "aborted", abortReason: serializeErrorForDisplay(abortReason), errorStack };
215
- });
2454
+ ]);
216
2455
  }
2456
+ ws.send(wsData.formatMessage?.outgoing(inputs) ?? JSON.stringify(inputs.action.toJsonObject()));
2457
+ };
2458
+ ws.addEventListener("message", (event) => {
2459
+ if (typeof event.data === "string") {
2460
+ const rawJson = wsData.formatMessage?.incoming?.(event.data) ?? this._parseActionMessage(event.data);
2461
+ if (rawJson != null && isActionPayload_Any_JsonObject(rawJson)) {
2462
+ this.resolvers.onIncomingActionDataJson(rawJson);
2463
+ }
2464
+ }
2465
+ });
2466
+ ws.addEventListener("close", (event) => {
2467
+ console.error("WebSocket closed:", event);
2468
+ for (const cb of disconnectListeners)
2469
+ cb();
2470
+ this._abortAll(err_nice_transport_ws.fromId("ws_disconnected" /* ws_disconnected */));
217
2471
  });
2472
+ ws.addEventListener("error", (event) => {
2473
+ console.error("WebSocket error:", event);
2474
+ for (const cb of disconnectListeners)
2475
+ cb();
2476
+ this._abortAll(err_nice_transport_ws.fromId("ws_error" /* ws_error */, {
2477
+ originalError: event instanceof Error ? event : undefined
2478
+ }));
2479
+ });
2480
+ return {
2481
+ sendActionData,
2482
+ updateRunConfig: wsData.updateRunConfig,
2483
+ addOnDisconnectListener: (cb) => {
2484
+ disconnectListeners.push(cb);
2485
+ },
2486
+ sendReturnData: (payload) => {
2487
+ ws.send(JSON.stringify(payload.toJsonObject()));
2488
+ }
2489
+ };
218
2490
  }
219
- getEntries() {
220
- return this._entries;
2491
+ _parseActionMessage(message) {
2492
+ let json;
2493
+ try {
2494
+ json = JSON.parse(message);
2495
+ } catch {
2496
+ return;
2497
+ }
2498
+ return isActionPayload_Any_JsonObject(json) ? json : undefined;
221
2499
  }
222
- subscribe(listener) {
223
- this._listeners.add(listener);
224
- listener(this._entries);
225
- return () => {
226
- this._listeners.delete(listener);
227
- };
2500
+ _abortAll(error) {
2501
+ const snapshot = [...this._abortSet];
2502
+ for (const ra of snapshot) {
2503
+ ra._abort(error);
2504
+ }
228
2505
  }
229
- clear() {
230
- this._entries = [];
231
- this._notify();
2506
+ }
2507
+
2508
+ // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketTransport.ts
2509
+ class WebSocketTransport extends Transport {
2510
+ options;
2511
+ type = "ws" /* ws */;
2512
+ constructor(options) {
2513
+ super();
2514
+ this.options = options;
2515
+ }
2516
+ static create(options) {
2517
+ return new WebSocketTransport({ ...options, mode: "socket" });
2518
+ }
2519
+ static createAdvanced(options) {
2520
+ return new WebSocketTransport({ ...options, mode: "advanced" });
232
2521
  }
233
- _updateEntry(cuid, updater) {
234
- this._entries = this._entries.map((e) => e.cuid === cuid ? updater(e) : e);
235
- this._notify();
2522
+ _createConnection(ctx) {
2523
+ const options = this.options;
2524
+ let getTransport;
2525
+ if (options.mode === "advanced") {
2526
+ getTransport = options.getTransport;
2527
+ } else {
2528
+ getTransport = (input) => ({
2529
+ status: "ready" /* ready */,
2530
+ readyData: {
2531
+ ws: options.createWebSocket(input),
2532
+ formatMessage: options.formatMessage,
2533
+ updateRunConfig: options.updateRunConfig
2534
+ }
2535
+ });
2536
+ }
2537
+ return new WebSocketConnection({
2538
+ initialize: () => ({
2539
+ getTransportCacheKey: options.getTransportCacheKey,
2540
+ getTransport
2541
+ })
2542
+ }, ctx.resolvers);
236
2543
  }
237
- _notify() {
238
- const snapshot = this._entries;
239
- for (const listener of this._listeners)
240
- listener(snapshot);
2544
+ getRouteInfo(input) {
2545
+ if (this.options.getRouteInfo != null)
2546
+ return this.options.getRouteInfo(input);
2547
+ return {
2548
+ type: "ws" /* ws */,
2549
+ summary: "ws"
2550
+ };
241
2551
  }
242
2552
  }
243
2553
  export {
244
- ActionServerDevtools,
245
- ActionDevtoolsCore
2554
+ isActionPayload_Result_JsonObject,
2555
+ isActionPayload_Request_JsonObject,
2556
+ isActionPayload_Any_JsonObject,
2557
+ err_nice_transport_ws,
2558
+ err_nice_transport,
2559
+ err_nice_external_client,
2560
+ err_nice_action,
2561
+ createLocalHandler,
2562
+ createExternalClientHandler,
2563
+ createActionRootDomain,
2564
+ actionSchema,
2565
+ WebSocketTransport,
2566
+ Transport,
2567
+ RuntimeCoordinate,
2568
+ RunningAction,
2569
+ HttpTransport,
2570
+ ETransportType,
2571
+ ETransportStatus,
2572
+ ERunningActionUpdateType,
2573
+ ERunningActionState,
2574
+ ERunningActionFinishedType,
2575
+ EErrId_NiceTransport_WebSocket,
2576
+ EErrId_NiceTransport,
2577
+ EErrId_NiceAction,
2578
+ EActionProgressType,
2579
+ EActionPayloadType,
2580
+ CustomTransport,
2581
+ ActionSchema,
2582
+ ActionRuntime,
2583
+ ActionRootDomain,
2584
+ ActionLocalHandler,
2585
+ ActionExternalClientHandler,
2586
+ ActionDomain,
2587
+ ActionCore
246
2588
  };