@dudousxd/nestjs-durable-dashboard 0.22.1 → 0.22.3

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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/durable-client.d.ts +3 -3
  3. package/dist/client/durable-client.d.ts.map +1 -1
  4. package/dist/client/durable-client.js.map +1 -1
  5. package/dist/client/group-subprocesses.d.ts +5 -5
  6. package/dist/client/group-subprocesses.d.ts.map +1 -1
  7. package/dist/server/index.cjs +695 -0
  8. package/dist/server/index.cjs.map +1 -0
  9. package/dist/server/index.d.cts +160 -0
  10. package/dist/server/index.d.ts +160 -5
  11. package/dist/server/index.js +662 -4
  12. package/dist/server/index.js.map +1 -1
  13. package/dist/spa/assets/{index-2MKVc5AZ.css → index-BaCR0uUG.css} +1 -1
  14. package/dist/spa/assets/index-D8irnyfw.js +62 -0
  15. package/dist/spa/index.html +2 -2
  16. package/package.json +14 -7
  17. package/dist/server/attr-filter.d.ts +0 -8
  18. package/dist/server/attr-filter.d.ts.map +0 -1
  19. package/dist/server/attr-filter.js +0 -30
  20. package/dist/server/attr-filter.js.map +0 -1
  21. package/dist/server/dashboard.service.d.ts +0 -70
  22. package/dist/server/dashboard.service.d.ts.map +0 -1
  23. package/dist/server/dashboard.service.js +0 -157
  24. package/dist/server/dashboard.service.js.map +0 -1
  25. package/dist/server/durable-api.controller.d.ts +0 -44
  26. package/dist/server/durable-api.controller.d.ts.map +0 -1
  27. package/dist/server/durable-api.controller.js +0 -197
  28. package/dist/server/durable-api.controller.js.map +0 -1
  29. package/dist/server/durable-dashboard.module.d.ts +0 -26
  30. package/dist/server/durable-dashboard.module.d.ts.map +0 -1
  31. package/dist/server/durable-dashboard.module.js +0 -60
  32. package/dist/server/durable-dashboard.module.js.map +0 -1
  33. package/dist/server/durable-ui.controller.d.ts +0 -19
  34. package/dist/server/durable-ui.controller.d.ts.map +0 -1
  35. package/dist/server/durable-ui.controller.js +0 -99
  36. package/dist/server/durable-ui.controller.js.map +0 -1
  37. package/dist/server/index.d.ts.map +0 -1
  38. package/dist/spa/assets/index-CAZ66zlb.js +0 -62
@@ -0,0 +1,695 @@
1
+ const __importMetaUrl = require('url').pathToFileURL(__filename).href;
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/server/index.ts
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ DASHBOARD_API_PATH: () => DASHBOARD_API_PATH,
26
+ DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
27
+ DashboardService: () => DashboardService,
28
+ DurableApiController: () => DurableApiController,
29
+ DurableApiModule: () => DurableApiModule,
30
+ DurableDashboardModule: () => DurableDashboardModule,
31
+ DurableUiController: () => DurableUiController
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/server/dashboard.service.ts
36
+ var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
37
+ var import_common = require("@nestjs/common");
38
+ var import_rxjs = require("rxjs");
39
+ function _ts_decorate(decorators, target, key, desc) {
40
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
41
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
42
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
43
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
44
+ }
45
+ __name(_ts_decorate, "_ts_decorate");
46
+ function _ts_metadata(k, v) {
47
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
48
+ }
49
+ __name(_ts_metadata, "_ts_metadata");
50
+ function _ts_param(paramIndex, decorator) {
51
+ return function(target, key) {
52
+ decorator(target, key, paramIndex);
53
+ };
54
+ }
55
+ __name(_ts_param, "_ts_param");
56
+ var DashboardService = class {
57
+ static {
58
+ __name(this, "DashboardService");
59
+ }
60
+ store;
61
+ engine;
62
+ /** Prometheus counters accumulated from engine events since boot (per process). */
63
+ metricsCollector;
64
+ constructor(store, engine) {
65
+ this.store = store;
66
+ this.engine = engine;
67
+ this.metricsCollector = (0, import_nestjs_durable_core.collectMetrics)(this.engine);
68
+ }
69
+ /**
70
+ * Prometheus-text metrics for a `/metrics` scrape: the event-counters (runs/steps by outcome,
71
+ * per-workflow counts) plus live **backlog gauges** queried at scrape time — `durable_pending_runs`
72
+ * (the dispatch backlog: the key health signal of the dispatch model) and `durable_dead_runs` (DLQ
73
+ * size). Capped per status so the scrape can't load an unbounded result set.
74
+ */
75
+ async metrics() {
76
+ const cap = 1e4;
77
+ const [pending, running, dead] = await Promise.all([
78
+ this.store.listRuns({
79
+ status: "pending",
80
+ limit: cap
81
+ }),
82
+ this.store.listRuns({
83
+ status: "running",
84
+ limit: cap
85
+ }),
86
+ this.store.listRuns({
87
+ status: "dead",
88
+ limit: cap
89
+ })
90
+ ]);
91
+ const gauges = [
92
+ "# TYPE durable_pending_runs gauge",
93
+ `durable_pending_runs ${pending.length}`,
94
+ "# TYPE durable_running_runs gauge",
95
+ `durable_running_runs ${running.length}`,
96
+ "# TYPE durable_dead_runs gauge",
97
+ `durable_dead_runs ${dead.length}`
98
+ ];
99
+ const health = await this.engine.workerHealth();
100
+ if (health.length > 0) {
101
+ gauges.push("# TYPE durable_group_queue_depth gauge");
102
+ for (const h of health) {
103
+ gauges.push(`durable_group_queue_depth{group="${h.group}"} ${h.depth}`);
104
+ }
105
+ gauges.push("# TYPE durable_group_live_workers gauge");
106
+ for (const h of health) {
107
+ gauges.push(`durable_group_live_workers{group="${h.group}"} ${h.liveWorkers.length}`);
108
+ }
109
+ }
110
+ return `${this.metricsCollector.prometheus()}${gauges.join("\n")}
111
+ `;
112
+ }
113
+ listRuns(query) {
114
+ return this.store.listRuns(query);
115
+ }
116
+ /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. The
117
+ * alert state a row turns red on is `depth > 0 && liveWorkers.length === 0`. */
118
+ workerHealth() {
119
+ return this.engine.workerHealth();
120
+ }
121
+ async getRunDetail(runId) {
122
+ const run = await this.store.getRun(runId);
123
+ if (!run) return null;
124
+ const [timeline, children] = await Promise.all([
125
+ this.store.listCheckpoints(runId),
126
+ this.engine.getRunChildren(runId)
127
+ ]);
128
+ return {
129
+ run,
130
+ timeline,
131
+ children
132
+ };
133
+ }
134
+ /** Fix-and-replay a dead/failed run with a corrected input — a fresh linked run. Returns its id. */
135
+ retryWithInput(runId, input) {
136
+ return this.engine.retryWithInput(runId, input);
137
+ }
138
+ /** Re-run a failed/incomplete run; completed steps replay from their checkpoints. */
139
+ retry(runId) {
140
+ return this.engine.requeue(runId);
141
+ }
142
+ cancel(runId, opts) {
143
+ return this.engine.cancel(runId, opts);
144
+ }
145
+ /**
146
+ * Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow) — e.g. "retry
147
+ * every `dead` run tagged `type:mel`". Capped at 500 runs per call; runs that can't be acted on
148
+ * (already terminal) are skipped. Returns how many matched and how many the action applied to.
149
+ */
150
+ async bulk(action, filter, opts) {
151
+ const runs = await this.store.listRuns({
152
+ ...filter,
153
+ limit: 500
154
+ });
155
+ let applied = 0;
156
+ for (const r of runs) {
157
+ try {
158
+ if (action === "retry") await this.engine.requeue(r.id);
159
+ else await this.engine.cancel(r.id, opts);
160
+ applied += 1;
161
+ } catch {
162
+ }
163
+ }
164
+ return {
165
+ matched: runs.length,
166
+ applied
167
+ };
168
+ }
169
+ /** Resume a run paused at a `ctx.breakpoint` (the "continue" button). */
170
+ continue(runId) {
171
+ return this.engine.continue(runId);
172
+ }
173
+ /**
174
+ * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
175
+ * waiting run is parked on. Returns the run result, or `null` if no run waits on that token (a
176
+ * stale/duplicate callback) — a safe no-op the controller maps to 404.
177
+ */
178
+ deliverWebhook(token, body) {
179
+ return this.engine.signal(token, body);
180
+ }
181
+ /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). */
182
+ getEvent(runId, key) {
183
+ return this.engine.getEvent(runId, key);
184
+ }
185
+ /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). */
186
+ update(runId, name, arg) {
187
+ return this.engine.update(runId, name, arg);
188
+ }
189
+ /**
190
+ * Live stream of a run's lifecycle events for SSE. Backed by `engine.subscribe`, which — when the
191
+ * transport has a control plane — receives events from EVERY instance, so a dashboard-only pod
192
+ * tails a run executing on a worker pod. Without a control plane it only sees same-process events.
193
+ */
194
+ streamRun(runId) {
195
+ return new import_rxjs.Observable((subscriber) => {
196
+ const off = this.engine.subscribe((event) => {
197
+ if (event.runId === runId) subscriber.next({
198
+ data: event
199
+ });
200
+ });
201
+ return () => off();
202
+ });
203
+ }
204
+ };
205
+ DashboardService = _ts_decorate([
206
+ (0, import_common.Injectable)(),
207
+ _ts_param(0, (0, import_common.Inject)(import_nestjs_durable_core.STATE_STORE)),
208
+ _ts_metadata("design:type", Function),
209
+ _ts_metadata("design:paramtypes", [
210
+ typeof StateStore === "undefined" ? Object : StateStore,
211
+ typeof import_nestjs_durable_core.WorkflowEngine === "undefined" ? Object : import_nestjs_durable_core.WorkflowEngine
212
+ ])
213
+ ], DashboardService);
214
+
215
+ // src/server/durable-api.controller.ts
216
+ var import_common2 = require("@nestjs/common");
217
+
218
+ // src/server/attr-filter.ts
219
+ var ATTR_OPS = /* @__PURE__ */ new Set([
220
+ "eq",
221
+ "ne",
222
+ "gt",
223
+ "gte",
224
+ "lt",
225
+ "lte"
226
+ ]);
227
+ function coerce(v) {
228
+ if (v === "true") return true;
229
+ if (v === "false") return false;
230
+ if (v !== "" && !Number.isNaN(Number(v))) return Number(v);
231
+ return v;
232
+ }
233
+ __name(coerce, "coerce");
234
+ function parseAttrFilters(attr) {
235
+ if (!attr) return void 0;
236
+ const raw = Array.isArray(attr) ? attr : [
237
+ attr
238
+ ];
239
+ const filters = [];
240
+ for (const entry of raw) {
241
+ const [key, op, ...rest] = entry.split(":");
242
+ if (!key || !ATTR_OPS.has(op) || rest.length === 0) continue;
243
+ filters.push({
244
+ key,
245
+ op,
246
+ value: coerce(rest.join(":"))
247
+ });
248
+ }
249
+ return filters.length ? filters : void 0;
250
+ }
251
+ __name(parseAttrFilters, "parseAttrFilters");
252
+
253
+ // src/server/durable-api.controller.ts
254
+ function _ts_decorate2(decorators, target, key, desc) {
255
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
256
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
257
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
258
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
259
+ }
260
+ __name(_ts_decorate2, "_ts_decorate");
261
+ function _ts_metadata2(k, v) {
262
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
263
+ }
264
+ __name(_ts_metadata2, "_ts_metadata");
265
+ function _ts_param2(paramIndex, decorator) {
266
+ return function(target, key) {
267
+ decorator(target, key, paramIndex);
268
+ };
269
+ }
270
+ __name(_ts_param2, "_ts_param");
271
+ var DurableApiController = class {
272
+ static {
273
+ __name(this, "DurableApiController");
274
+ }
275
+ dashboard;
276
+ constructor(dashboard) {
277
+ this.dashboard = dashboard;
278
+ }
279
+ runs(status, workflow, tag, attr) {
280
+ return this.dashboard.listRuns({
281
+ status,
282
+ workflow,
283
+ tag,
284
+ attributes: parseAttrFilters(attr)
285
+ });
286
+ }
287
+ /** Prometheus-text metrics (runs/steps by outcome, per-workflow counts) for a scrape. */
288
+ metrics() {
289
+ return this.dashboard.metrics();
290
+ }
291
+ /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. */
292
+ workers() {
293
+ return this.dashboard.workerHealth();
294
+ }
295
+ async run(id) {
296
+ const detail = await this.dashboard.getRunDetail(id);
297
+ if (!detail) throw new import_common2.NotFoundException(`run ${id} not found`);
298
+ return detail;
299
+ }
300
+ /** Server-Sent Events stream of a run's live lifecycle events — the dashboard tails it instead
301
+ * of polling. Cross-pod when the transport has a control plane (see DashboardService.streamRun). */
302
+ stream(id) {
303
+ return this.dashboard.streamRun(id);
304
+ }
305
+ retry(id) {
306
+ return this.dashboard.retry(id);
307
+ }
308
+ /** Fix-and-replay: re-run a dead/failed run with a corrected input (a fresh linked run). */
309
+ retryWithInput(id, body) {
310
+ return this.dashboard.retryWithInput(id, body?.input);
311
+ }
312
+ /** Bulk retry/cancel every run matching a filter (status / tag / workflow). */
313
+ bulk(action, status, tag, workflow, attr, compensate) {
314
+ return this.dashboard.bulk(action === "cancel" ? "cancel" : "retry", {
315
+ status,
316
+ tag,
317
+ workflow,
318
+ attributes: parseAttrFilters(attr)
319
+ }, {
320
+ compensate: compensate === "true"
321
+ });
322
+ }
323
+ async cancel(id, compensate) {
324
+ const result = await this.dashboard.cancel(id, {
325
+ compensate: compensate === "true"
326
+ });
327
+ if (!result) throw new import_common2.NotFoundException(`run ${id} not found`);
328
+ return result;
329
+ }
330
+ async continue(id) {
331
+ const result = await this.dashboard.continue(id);
332
+ if (!result) throw new import_common2.NotFoundException(`run ${id} is not paused at a breakpoint`);
333
+ return result;
334
+ }
335
+ /**
336
+ * Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),
337
+ * and the body resumes the waiting run. NOTE: this is reachable by external systems — protect it
338
+ * by treating the token as a secret (it embeds runId:seq) and/or fronting it with signature
339
+ * verification in your own middleware.
340
+ */
341
+ async webhook(token, body) {
342
+ const result = await this.dashboard.deliverWebhook(token, body);
343
+ if (!result) throw new import_common2.NotFoundException(`no run is waiting on webhook ${token}`);
344
+ return result;
345
+ }
346
+ /** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */
347
+ event(id, key) {
348
+ return this.dashboard.getEvent(id, key);
349
+ }
350
+ /** Deliver a validated `ctx.onUpdate` to a run; the body is the update argument. */
351
+ update(id, name, body) {
352
+ return this.dashboard.update(id, name, body);
353
+ }
354
+ };
355
+ _ts_decorate2([
356
+ (0, import_common2.Get)("runs"),
357
+ _ts_param2(0, (0, import_common2.Query)("status")),
358
+ _ts_param2(1, (0, import_common2.Query)("workflow")),
359
+ _ts_param2(2, (0, import_common2.Query)("tag")),
360
+ _ts_param2(3, (0, import_common2.Query)("attr")),
361
+ _ts_metadata2("design:type", Function),
362
+ _ts_metadata2("design:paramtypes", [
363
+ typeof RunStatus === "undefined" ? Object : RunStatus,
364
+ String,
365
+ String,
366
+ Object
367
+ ]),
368
+ _ts_metadata2("design:returntype", void 0)
369
+ ], DurableApiController.prototype, "runs", null);
370
+ _ts_decorate2([
371
+ (0, import_common2.Get)("metrics"),
372
+ (0, import_common2.Header)("Content-Type", "text/plain; version=0.0.4"),
373
+ _ts_metadata2("design:type", Function),
374
+ _ts_metadata2("design:paramtypes", []),
375
+ _ts_metadata2("design:returntype", void 0)
376
+ ], DurableApiController.prototype, "metrics", null);
377
+ _ts_decorate2([
378
+ (0, import_common2.Get)("workers"),
379
+ _ts_metadata2("design:type", Function),
380
+ _ts_metadata2("design:paramtypes", []),
381
+ _ts_metadata2("design:returntype", void 0)
382
+ ], DurableApiController.prototype, "workers", null);
383
+ _ts_decorate2([
384
+ (0, import_common2.Get)("runs/:id"),
385
+ _ts_param2(0, (0, import_common2.Param)("id")),
386
+ _ts_metadata2("design:type", Function),
387
+ _ts_metadata2("design:paramtypes", [
388
+ String
389
+ ]),
390
+ _ts_metadata2("design:returntype", Promise)
391
+ ], DurableApiController.prototype, "run", null);
392
+ _ts_decorate2([
393
+ (0, import_common2.Sse)("runs/:id/stream"),
394
+ _ts_param2(0, (0, import_common2.Param)("id")),
395
+ _ts_metadata2("design:type", Function),
396
+ _ts_metadata2("design:paramtypes", [
397
+ String
398
+ ]),
399
+ _ts_metadata2("design:returntype", void 0)
400
+ ], DurableApiController.prototype, "stream", null);
401
+ _ts_decorate2([
402
+ (0, import_common2.Post)("runs/:id/retry"),
403
+ _ts_param2(0, (0, import_common2.Param)("id")),
404
+ _ts_metadata2("design:type", Function),
405
+ _ts_metadata2("design:paramtypes", [
406
+ String
407
+ ]),
408
+ _ts_metadata2("design:returntype", void 0)
409
+ ], DurableApiController.prototype, "retry", null);
410
+ _ts_decorate2([
411
+ (0, import_common2.Post)("runs/:id/retry-with-input"),
412
+ _ts_param2(0, (0, import_common2.Param)("id")),
413
+ _ts_param2(1, (0, import_common2.Body)()),
414
+ _ts_metadata2("design:type", Function),
415
+ _ts_metadata2("design:paramtypes", [
416
+ String,
417
+ Object
418
+ ]),
419
+ _ts_metadata2("design:returntype", void 0)
420
+ ], DurableApiController.prototype, "retryWithInput", null);
421
+ _ts_decorate2([
422
+ (0, import_common2.Post)("bulk/:action"),
423
+ _ts_param2(0, (0, import_common2.Param)("action")),
424
+ _ts_param2(1, (0, import_common2.Query)("status")),
425
+ _ts_param2(2, (0, import_common2.Query)("tag")),
426
+ _ts_param2(3, (0, import_common2.Query)("workflow")),
427
+ _ts_param2(4, (0, import_common2.Query)("attr")),
428
+ _ts_param2(5, (0, import_common2.Query)("compensate")),
429
+ _ts_metadata2("design:type", Function),
430
+ _ts_metadata2("design:paramtypes", [
431
+ String,
432
+ typeof RunStatus === "undefined" ? Object : RunStatus,
433
+ String,
434
+ String,
435
+ Object,
436
+ String
437
+ ]),
438
+ _ts_metadata2("design:returntype", void 0)
439
+ ], DurableApiController.prototype, "bulk", null);
440
+ _ts_decorate2([
441
+ (0, import_common2.Post)("runs/:id/cancel"),
442
+ _ts_param2(0, (0, import_common2.Param)("id")),
443
+ _ts_param2(1, (0, import_common2.Query)("compensate")),
444
+ _ts_metadata2("design:type", Function),
445
+ _ts_metadata2("design:paramtypes", [
446
+ String,
447
+ String
448
+ ]),
449
+ _ts_metadata2("design:returntype", Promise)
450
+ ], DurableApiController.prototype, "cancel", null);
451
+ _ts_decorate2([
452
+ (0, import_common2.Post)("runs/:id/continue"),
453
+ _ts_param2(0, (0, import_common2.Param)("id")),
454
+ _ts_metadata2("design:type", Function),
455
+ _ts_metadata2("design:paramtypes", [
456
+ String
457
+ ]),
458
+ _ts_metadata2("design:returntype", Promise)
459
+ ], DurableApiController.prototype, "continue", null);
460
+ _ts_decorate2([
461
+ (0, import_common2.Post)("webhooks/:token"),
462
+ _ts_param2(0, (0, import_common2.Param)("token")),
463
+ _ts_param2(1, (0, import_common2.Body)()),
464
+ _ts_metadata2("design:type", Function),
465
+ _ts_metadata2("design:paramtypes", [
466
+ String,
467
+ Object
468
+ ]),
469
+ _ts_metadata2("design:returntype", Promise)
470
+ ], DurableApiController.prototype, "webhook", null);
471
+ _ts_decorate2([
472
+ (0, import_common2.Get)("runs/:id/events/:key"),
473
+ _ts_param2(0, (0, import_common2.Param)("id")),
474
+ _ts_param2(1, (0, import_common2.Param)("key")),
475
+ _ts_metadata2("design:type", Function),
476
+ _ts_metadata2("design:paramtypes", [
477
+ String,
478
+ String
479
+ ]),
480
+ _ts_metadata2("design:returntype", void 0)
481
+ ], DurableApiController.prototype, "event", null);
482
+ _ts_decorate2([
483
+ (0, import_common2.Post)("runs/:id/updates/:name"),
484
+ _ts_param2(0, (0, import_common2.Param)("id")),
485
+ _ts_param2(1, (0, import_common2.Param)("name")),
486
+ _ts_param2(2, (0, import_common2.Body)()),
487
+ _ts_metadata2("design:type", Function),
488
+ _ts_metadata2("design:paramtypes", [
489
+ String,
490
+ String,
491
+ Object
492
+ ]),
493
+ _ts_metadata2("design:returntype", void 0)
494
+ ], DurableApiController.prototype, "update", null);
495
+ DurableApiController = _ts_decorate2([
496
+ (0, import_common2.Controller)(),
497
+ _ts_metadata2("design:type", Function),
498
+ _ts_metadata2("design:paramtypes", [
499
+ typeof DashboardService === "undefined" ? Object : DashboardService
500
+ ])
501
+ ], DurableApiController);
502
+
503
+ // src/server/durable-dashboard.module.ts
504
+ var import_common4 = require("@nestjs/common");
505
+ var import_core = require("@nestjs/core");
506
+
507
+ // src/server/durable-ui.controller.ts
508
+ var import_node_fs = require("fs");
509
+ var import_node_path = require("path");
510
+ var import_node_url = require("url");
511
+ var import_common3 = require("@nestjs/common");
512
+ function _ts_decorate3(decorators, target, key, desc) {
513
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
514
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
515
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
516
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
517
+ }
518
+ __name(_ts_decorate3, "_ts_decorate");
519
+ function _ts_metadata3(k, v) {
520
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
521
+ }
522
+ __name(_ts_metadata3, "_ts_metadata");
523
+ function _ts_param3(paramIndex, decorator) {
524
+ return function(target, key) {
525
+ decorator(target, key, paramIndex);
526
+ };
527
+ }
528
+ __name(_ts_param3, "_ts_param");
529
+ var DASHBOARD_BASE_PATH = Symbol("DASHBOARD_BASE_PATH");
530
+ var DASHBOARD_API_PATH = Symbol("DASHBOARD_API_PATH");
531
+ var BUILD_BASE = "/durable";
532
+ function spaDir() {
533
+ return (0, import_node_url.fileURLToPath)(new URL("../spa", __importMetaUrl));
534
+ }
535
+ __name(spaDir, "spaDir");
536
+ var CONTENT_TYPES = {
537
+ ".js": "text/javascript; charset=utf-8",
538
+ ".css": "text/css; charset=utf-8",
539
+ ".map": "application/json; charset=utf-8",
540
+ ".svg": "image/svg+xml",
541
+ ".json": "application/json; charset=utf-8",
542
+ ".woff2": "font/woff2",
543
+ ".ico": "image/x-icon"
544
+ };
545
+ var DurableUiController = class {
546
+ static {
547
+ __name(this, "DurableUiController");
548
+ }
549
+ basePath;
550
+ apiBasePath;
551
+ dir = spaDir();
552
+ constructor(basePath, apiBasePath) {
553
+ this.basePath = basePath;
554
+ this.apiBasePath = apiBasePath;
555
+ }
556
+ // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the
557
+ // classic "stuck loading after a deploy"). The hashed assets below are immutable.
558
+ index() {
559
+ const indexPath = (0, import_node_path.join)(this.dir, "index.html");
560
+ if (!(0, import_node_fs.existsSync)(indexPath)) {
561
+ throw new import_common3.NotFoundException("Dashboard is not built. Run the package build.");
562
+ }
563
+ const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
564
+ const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>`;
565
+ return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
566
+ }
567
+ asset(file) {
568
+ const safe = (0, import_node_path.basename)(file);
569
+ if (safe !== file) throw new import_common3.NotFoundException();
570
+ const root = (0, import_node_path.resolve)(this.dir, "assets");
571
+ const assetPath = (0, import_node_path.resolve)(root, safe);
572
+ if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
573
+ throw new import_common3.NotFoundException();
574
+ }
575
+ const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
576
+ return new import_common3.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
577
+ type
578
+ });
579
+ }
580
+ };
581
+ _ts_decorate3([
582
+ (0, import_common3.Get)(),
583
+ (0, import_common3.Header)("Content-Type", "text/html; charset=utf-8"),
584
+ (0, import_common3.Header)("Cache-Control", "no-store, must-revalidate"),
585
+ _ts_metadata3("design:type", Function),
586
+ _ts_metadata3("design:paramtypes", []),
587
+ _ts_metadata3("design:returntype", String)
588
+ ], DurableUiController.prototype, "index", null);
589
+ _ts_decorate3([
590
+ (0, import_common3.Get)("assets/:file"),
591
+ (0, import_common3.Header)("Cache-Control", "public, max-age=31536000, immutable"),
592
+ _ts_param3(0, (0, import_common3.Param)("file")),
593
+ _ts_metadata3("design:type", Function),
594
+ _ts_metadata3("design:paramtypes", [
595
+ String
596
+ ]),
597
+ _ts_metadata3("design:returntype", typeof import_common3.StreamableFile === "undefined" ? Object : import_common3.StreamableFile)
598
+ ], DurableUiController.prototype, "asset", null);
599
+ DurableUiController = _ts_decorate3([
600
+ (0, import_common3.Controller)(),
601
+ _ts_param3(0, (0, import_common3.Inject)(DASHBOARD_BASE_PATH)),
602
+ _ts_param3(1, (0, import_common3.Inject)(DASHBOARD_API_PATH)),
603
+ _ts_metadata3("design:type", Function),
604
+ _ts_metadata3("design:paramtypes", [
605
+ String,
606
+ String
607
+ ])
608
+ ], DurableUiController);
609
+
610
+ // src/server/durable-dashboard.module.ts
611
+ function _ts_decorate4(decorators, target, key, desc) {
612
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
613
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
614
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
615
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
616
+ }
617
+ __name(_ts_decorate4, "_ts_decorate");
618
+ function normalize(path) {
619
+ return `/${path.replace(/^\/+|\/+$/g, "")}`;
620
+ }
621
+ __name(normalize, "normalize");
622
+ var DurableApiModule = class {
623
+ static {
624
+ __name(this, "DurableApiModule");
625
+ }
626
+ };
627
+ DurableApiModule = _ts_decorate4([
628
+ (0, import_common4.Module)({
629
+ controllers: [
630
+ DurableApiController
631
+ ],
632
+ providers: [
633
+ DashboardService
634
+ ],
635
+ exports: [
636
+ DashboardService
637
+ ]
638
+ })
639
+ ], DurableApiModule);
640
+ var DurableDashboardModule = class _DurableDashboardModule {
641
+ static {
642
+ __name(this, "DurableDashboardModule");
643
+ }
644
+ static forRoot(options = {}) {
645
+ const basePath = normalize(options.basePath ?? "/durable");
646
+ const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
647
+ return {
648
+ module: _DurableDashboardModule,
649
+ imports: [
650
+ DurableApiModule,
651
+ import_core.RouterModule.register([
652
+ {
653
+ path: basePath,
654
+ module: _DurableDashboardModule
655
+ },
656
+ {
657
+ path: apiBasePath,
658
+ module: DurableApiModule
659
+ }
660
+ ])
661
+ ],
662
+ controllers: [
663
+ DurableUiController
664
+ ],
665
+ providers: [
666
+ {
667
+ provide: DASHBOARD_BASE_PATH,
668
+ useValue: basePath
669
+ },
670
+ {
671
+ provide: DASHBOARD_API_PATH,
672
+ useValue: apiBasePath
673
+ }
674
+ ],
675
+ // Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).
676
+ exports: [
677
+ DurableApiModule
678
+ ]
679
+ };
680
+ }
681
+ };
682
+ DurableDashboardModule = _ts_decorate4([
683
+ (0, import_common4.Module)({})
684
+ ], DurableDashboardModule);
685
+ // Annotate the CommonJS export names for ESM import in node:
686
+ 0 && (module.exports = {
687
+ DASHBOARD_API_PATH,
688
+ DASHBOARD_BASE_PATH,
689
+ DashboardService,
690
+ DurableApiController,
691
+ DurableApiModule,
692
+ DurableDashboardModule,
693
+ DurableUiController
694
+ });
695
+ //# sourceMappingURL=index.cjs.map