@dudousxd/nestjs-durable-dashboard 0.53.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/client/durable-client.d.ts +67 -3
  2. package/dist/client/durable-client.d.ts.map +1 -1
  3. package/dist/client/durable-client.js +26 -46
  4. package/dist/client/durable-client.js.map +1 -1
  5. package/dist/client/event-tail.d.ts +25 -0
  6. package/dist/client/event-tail.d.ts.map +1 -0
  7. package/dist/client/event-tail.js +30 -0
  8. package/dist/client/event-tail.js.map +1 -0
  9. package/dist/server/index.cjs +322 -1337
  10. package/dist/server/index.cjs.map +1 -1
  11. package/dist/server/index.d.cts +149 -389
  12. package/dist/server/index.d.ts +149 -389
  13. package/dist/server/index.js +308 -1317
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/spa/assets/{bench-Db-gqOEF.js → bench-DCclNvcZ.js} +1 -1
  16. package/dist/spa/assets/index-B-wtX4GV.js +66 -0
  17. package/dist/spa/assets/index-CJdk28vt.css +1 -0
  18. package/dist/spa/assets/{index-4tEXGRlA.js → index-ChjKRQZx.js} +1 -1
  19. package/dist/spa/assets/{preview-BvCp1zR5.js → preview-CdsjvOrK.js} +1 -1
  20. package/dist/spa/bench.html +3 -3
  21. package/dist/spa/index.html +3 -3
  22. package/dist/spa/preview.html +3 -3
  23. package/package.json +5 -18
  24. package/dist/client/console-session.d.ts +0 -63
  25. package/dist/client/console-session.d.ts.map +0 -1
  26. package/dist/client/console-session.js +0 -107
  27. package/dist/client/console-session.js.map +0 -1
  28. package/dist/react/index.d.ts +0 -4
  29. package/dist/react/index.d.ts.map +0 -1
  30. package/dist/react/index.js +0 -14
  31. package/dist/react/index.js.map +0 -1
  32. package/dist/react/open-console-button.d.ts +0 -26
  33. package/dist/react/open-console-button.d.ts.map +0 -1
  34. package/dist/react/open-console-button.js +0 -26
  35. package/dist/react/open-console-button.js.map +0 -1
  36. package/dist/react/use-open-console.d.ts +0 -40
  37. package/dist/react/use-open-console.d.ts.map +0 -1
  38. package/dist/react/use-open-console.js +0 -77
  39. package/dist/react/use-open-console.js.map +0 -1
  40. package/dist/spa/assets/index-BbR4CZ1K.css +0 -1
  41. package/dist/spa/assets/index-CEV7yPmk.js +0 -66
@@ -24,21 +24,15 @@ var index_exports = {};
24
24
  __export(index_exports, {
25
25
  DASHBOARD_API_PATH: () => DASHBOARD_API_PATH,
26
26
  DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
27
- DASHBOARD_GATE: () => DASHBOARD_GATE,
28
- DURABLE_DASHBOARD_TOKEN_ENV: () => DURABLE_DASHBOARD_TOKEN_ENV,
27
+ DASHBOARD_PROTECTION_CHECK: () => DASHBOARD_PROTECTION_CHECK,
29
28
  DashboardService: () => DashboardService,
30
29
  DurableApiController: () => DurableApiController,
30
+ DurableApiControllerBase: () => DurableApiControllerBase,
31
31
  DurableApiModule: () => DurableApiModule,
32
- DurableApiSessionGuard: () => DurableApiSessionGuard,
33
- DurableAuthController: () => DurableAuthController,
34
- DurableDashboardGateGuard: () => DurableDashboardGateGuard,
35
32
  DurableDashboardModule: () => DurableDashboardModule,
36
33
  DurableUiController: () => DurableUiController,
37
- DurableUiSessionGuard: () => DurableUiSessionGuard,
38
- dashboardGateProvider: () => dashboardGateProvider,
39
- readDashboardToken: () => readDashboardToken,
40
- reportDashboardGate: () => reportDashboardGate,
41
- resolveDashboardGate: () => resolveDashboardGate
34
+ DurableUiControllerBase: () => DurableUiControllerBase,
35
+ UNPROTECTED_CONSOLE_IN_PRODUCTION: () => UNPROTECTED_CONSOLE_IN_PRODUCTION
42
36
  });
43
37
  module.exports = __toCommonJS(index_exports);
44
38
 
@@ -215,6 +209,51 @@ var DashboardService = class {
215
209
  return this.gateway.cancel(runId, opts);
216
210
  }
217
211
  /**
212
+ * Publish a settled run's lifecycle event again, to its subscribers that have not started (or to
213
+ * `subscriber` alone). Undefined when the gateway cannot republish (a tenant proxy).
214
+ */
215
+ async republishLifecycle(runId, subscriber) {
216
+ return this.gateway.republishLifecycle?.({
217
+ runId,
218
+ ...subscriber !== void 0 ? {
219
+ subscriber
220
+ } : {}
221
+ });
222
+ }
223
+ /**
224
+ * Who starts whom through lifecycle events, for `workflow`, plus `triggerCounts` when this process
225
+ * counts the engine's events (the control plane) and the workflow has subscribers. Undefined when
226
+ * the gateway cannot answer.
227
+ */
228
+ async workflowLifecycle(workflow) {
229
+ const links = await this.gateway.workflowLifecycle?.(workflow);
230
+ if (!links) return links;
231
+ const counts = this.triggerCountsOf(links);
232
+ return counts ? {
233
+ ...links,
234
+ triggerCounts: counts
235
+ } : links;
236
+ }
237
+ /**
238
+ * How many runs each subscriber of `links.workflow` was started for and how many times starting it
239
+ * failed, counted by this process's metrics since it started. Undefined without metrics (a tenant)
240
+ * or without subscribers.
241
+ */
242
+ triggerCountsOf(links) {
243
+ const snapshot = this.metricsCollector?.snapshot();
244
+ if (!snapshot || links.startsOnSettle.length === 0) return void 0;
245
+ const triggered = snapshot.triggeredBySubscriber[links.workflow] ?? {};
246
+ const failed = snapshot.triggerFailedBySubscriber[links.workflow] ?? {};
247
+ const counts = {};
248
+ for (const { workflow } of links.startsOnSettle) {
249
+ counts[workflow] = {
250
+ triggered: triggered[workflow] ?? 0,
251
+ failed: failed[workflow] ?? 0
252
+ };
253
+ }
254
+ return counts;
255
+ }
256
+ /**
218
257
  * Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow / namespace /
219
258
  * origin) — e.g. "retry every `dead` run tagged `type:mel`". The filter is the same one the run
220
259
  * list uses, so a bulk action can never reach wider than the list it was launched from — but note
@@ -392,87 +431,6 @@ __name(runValueAxisFor, "runValueAxisFor");
392
431
 
393
432
  // src/server/not-modified.ts
394
433
  var import_common2 = require("@nestjs/common");
395
-
396
- // src/server/auth/response.ts
397
- function isRecord(value) {
398
- return typeof value === "object" && value !== null;
399
- }
400
- __name(isRecord, "isRecord");
401
- function isRawCookieResponse(value) {
402
- return isRecord(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function";
403
- }
404
- __name(isRawCookieResponse, "isRawCookieResponse");
405
- function resolveCookieResponse(response) {
406
- if (isRawCookieResponse(response)) return response;
407
- if (isRecord(response) && isRawCookieResponse(response.raw)) return response.raw;
408
- return null;
409
- }
410
- __name(resolveCookieResponse, "resolveCookieResponse");
411
- function existingSetCookies(response) {
412
- const current = response.getHeader("set-cookie");
413
- if (Array.isArray(current)) return current;
414
- if (typeof current === "string") return [
415
- current
416
- ];
417
- return [];
418
- }
419
- __name(existingSetCookies, "existingSetCookies");
420
- function appendSetCookie(response, cookie) {
421
- const raw = resolveCookieResponse(response);
422
- if (!raw) return;
423
- raw.setHeader("set-cookie", [
424
- ...existingSetCookies(raw),
425
- cookie
426
- ]);
427
- }
428
- __name(appendSetCookie, "appendSetCookie");
429
- function isRawRedirectResponse(value) {
430
- return isRecord(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function" && typeof value.end === "function";
431
- }
432
- __name(isRawRedirectResponse, "isRawRedirectResponse");
433
- function resolveRedirectResponse(response) {
434
- if (isRawRedirectResponse(response)) return response;
435
- if (isRecord(response) && isRawRedirectResponse(response.raw)) return response.raw;
436
- return null;
437
- }
438
- __name(resolveRedirectResponse, "resolveRedirectResponse");
439
- function redirectRaw(response, location, status = 302) {
440
- const raw = resolveRedirectResponse(response);
441
- if (!raw) return;
442
- raw.statusCode = status;
443
- raw.setHeader("location", location);
444
- raw.end();
445
- }
446
- __name(redirectRaw, "redirectRaw");
447
- function responseAlreadyWritten(response) {
448
- const raw = resolveRedirectResponse(response);
449
- return raw?.headersSent === true;
450
- }
451
- __name(responseAlreadyWritten, "responseAlreadyWritten");
452
- function writeHtmlRaw(response, html, status = 200) {
453
- const raw = resolveRedirectResponse(response);
454
- if (!raw) return;
455
- raw.statusCode = status;
456
- raw.setHeader("content-type", "text/html; charset=utf-8");
457
- raw.setHeader("cache-control", "no-store, must-revalidate");
458
- raw.end(html);
459
- }
460
- __name(writeHtmlRaw, "writeHtmlRaw");
461
- function setRawHeader(response, name, value) {
462
- resolveCookieResponse(response)?.setHeader(name, value);
463
- }
464
- __name(setRawHeader, "setRawHeader");
465
- function writeNotModifiedRaw(response, etag) {
466
- const raw = resolveRedirectResponse(response);
467
- if (!raw) return;
468
- raw.statusCode = 304;
469
- raw.setHeader("etag", etag);
470
- raw.setHeader("cache-control", "private, no-cache");
471
- raw.end();
472
- }
473
- __name(writeNotModifiedRaw, "writeNotModifiedRaw");
474
-
475
- // src/server/not-modified.ts
476
434
  function _ts_decorate2(decorators, target, key, desc) {
477
435
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
478
436
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -487,6 +445,31 @@ function _ts_decorate2(decorators, target, key, desc) {
487
445
  return c > 3 && r && Object.defineProperty(target, key, r), r;
488
446
  }
489
447
  __name(_ts_decorate2, "_ts_decorate");
448
+ function isRawResponse(value) {
449
+ if (typeof value !== "object" || value === null) return false;
450
+ const candidate = value;
451
+ return typeof candidate.setHeader === "function" && typeof candidate.end === "function";
452
+ }
453
+ __name(isRawResponse, "isRawResponse");
454
+ function rawResponse(response) {
455
+ if (isRawResponse(response)) return response;
456
+ const raw = response?.raw;
457
+ return isRawResponse(raw) ? raw : null;
458
+ }
459
+ __name(rawResponse, "rawResponse");
460
+ function setRawHeader(response, name, value) {
461
+ rawResponse(response)?.setHeader(name, value);
462
+ }
463
+ __name(setRawHeader, "setRawHeader");
464
+ function writeNotModifiedRaw(response, etag) {
465
+ const raw = rawResponse(response);
466
+ if (!raw) return;
467
+ raw.statusCode = 304;
468
+ raw.setHeader("etag", etag);
469
+ raw.setHeader("cache-control", "private, no-cache");
470
+ raw.end();
471
+ }
472
+ __name(writeNotModifiedRaw, "writeNotModifiedRaw");
490
473
  var NotModifiedException = class extends import_common2.HttpException {
491
474
  static {
492
475
  __name(this, "NotModifiedException");
@@ -802,10 +785,10 @@ var RunQueryAdapter = class {
802
785
  }
803
786
  unsupported(clause, "equals, notEquals, gt, gte, lt, lte, in");
804
787
  }
805
- applyOffsetPagination(qb, page2, size) {
788
+ applyOffsetPagination(qb, page, size) {
806
789
  this.draft(qb).narrow({
807
790
  limit: size,
808
- offset: (page2 - 1) * size
791
+ offset: (page - 1) * size
809
792
  });
810
793
  }
811
794
  /**
@@ -1149,9 +1132,9 @@ function _ts_param2(paramIndex, decorator) {
1149
1132
  }
1150
1133
  __name(_ts_param2, "_ts_param");
1151
1134
  var FAN_INLINE_MAX = 50;
1152
- var DurableApiController = class {
1135
+ var DurableApiControllerBase = class {
1153
1136
  static {
1154
- __name(this, "DurableApiController");
1137
+ __name(this, "DurableApiControllerBase");
1155
1138
  }
1156
1139
  dashboard;
1157
1140
  runner;
@@ -1243,6 +1226,15 @@ var DurableApiController = class {
1243
1226
  triggerSchedule(id) {
1244
1227
  return this.dashboard.triggerSchedule(id).catch(notFound);
1245
1228
  }
1229
+ /**
1230
+ * Who starts whom through lifecycle events, for one workflow: the workflows that start when it
1231
+ * settles, and the ones whose settling starts it. What the console's workflow page lists.
1232
+ */
1233
+ async workflowLifecycle(name) {
1234
+ const links = await this.dashboard.workflowLifecycle(name).catch(notSupportedByServer);
1235
+ if (!links) throw new import_common5.NotImplementedException("workflow lifecycle links need the control plane");
1236
+ return links;
1237
+ }
1246
1238
  /** This deployment's durable role (control plane vs tenant) + tenant name — for the header badge. */
1247
1239
  topology() {
1248
1240
  return this.dashboard.topology();
@@ -1272,7 +1264,7 @@ var DurableApiController = class {
1272
1264
  }
1273
1265
  /** One page of a summarized fan's members, in seq order. Versioned like {@link run}. */
1274
1266
  async fanMembers(id, group, offset, limit, failed, ifNoneMatch, response) {
1275
- const page2 = await this.dashboard.getFanMembers(id, group, {
1267
+ const page = await this.dashboard.getFanMembers(id, group, {
1276
1268
  offset: offset === void 0 ? void 0 : Number(offset),
1277
1269
  limit: limit === void 0 ? void 0 : Number(limit),
1278
1270
  failedOnly: failed === "true",
@@ -1280,8 +1272,8 @@ var DurableApiController = class {
1280
1272
  ifNoneMatch
1281
1273
  }
1282
1274
  });
1283
- if (!page2) throw new import_common5.NotFoundException(`fan ${group} of run ${id} not found`);
1284
- const { etag, notModified, ...body } = page2;
1275
+ if (!page) throw new import_common5.NotFoundException(`fan ${group} of run ${id} not found`);
1276
+ const { etag, notModified, ...body } = page;
1285
1277
  if (etag) {
1286
1278
  if (notModified) throw new NotModifiedException(etag);
1287
1279
  setEtag(response, etag);
@@ -1318,6 +1310,28 @@ var DurableApiController = class {
1318
1310
  if (!result) throw new import_common5.NotFoundException(`run ${id} not found`);
1319
1311
  return result;
1320
1312
  }
1313
+ /**
1314
+ * Publish a settled run's lifecycle event again, to the subscribers that have not started from it
1315
+ * (or only `subscriber`). Same gate as retry/cancel.
1316
+ */
1317
+ async republishLifecycle(id, body) {
1318
+ let result;
1319
+ try {
1320
+ result = await this.dashboard.republishLifecycle(id, body?.subscriber || void 0);
1321
+ } catch (error) {
1322
+ const message = error instanceof Error ? error.message : String(error);
1323
+ if (/^run .* not found$/.test(message)) throw new import_common5.NotFoundException(message);
1324
+ if (/^run .* has not settled/.test(message)) throw new import_common5.BadRequestException(message);
1325
+ if (error?.code === "bad-request") {
1326
+ throw new import_common5.BadRequestException(message);
1327
+ }
1328
+ return notSupportedByServer(error);
1329
+ }
1330
+ if (!result) {
1331
+ throw new import_common5.NotImplementedException("republishing lifecycle events needs the control plane");
1332
+ }
1333
+ return result;
1334
+ }
1321
1335
  async continue(id) {
1322
1336
  const result = await this.dashboard.continue(id);
1323
1337
  if (!result) throw new import_common5.NotFoundException(`run ${id} is not paused at a breakpoint`);
@@ -1357,7 +1371,7 @@ _ts_decorate5([
1357
1371
  typeof RunQueryDraft === "undefined" ? Object : RunQueryDraft
1358
1372
  ]),
1359
1373
  _ts_metadata4("design:returntype", Promise)
1360
- ], DurableApiController.prototype, "runs", null);
1374
+ ], DurableApiControllerBase.prototype, "runs", null);
1361
1375
  _ts_decorate5([
1362
1376
  (0, import_common5.Get)("runs/facets"),
1363
1377
  _ts_param2(0, (0, import_nestjs_filter2.ApplyFilter)(RunFilter)),
@@ -1366,7 +1380,7 @@ _ts_decorate5([
1366
1380
  typeof RunQueryDraft === "undefined" ? Object : RunQueryDraft
1367
1381
  ]),
1368
1382
  _ts_metadata4("design:returntype", void 0)
1369
- ], DurableApiController.prototype, "facets", null);
1383
+ ], DurableApiControllerBase.prototype, "facets", null);
1370
1384
  _ts_decorate5([
1371
1385
  (0, import_common5.Get)("runs/values"),
1372
1386
  _ts_param2(0, (0, import_common5.Query)()),
@@ -1375,20 +1389,20 @@ _ts_decorate5([
1375
1389
  typeof Record === "undefined" ? Object : Record
1376
1390
  ]),
1377
1391
  _ts_metadata4("design:returntype", void 0)
1378
- ], DurableApiController.prototype, "values", null);
1392
+ ], DurableApiControllerBase.prototype, "values", null);
1379
1393
  _ts_decorate5([
1380
1394
  (0, import_common5.Get)("metrics"),
1381
1395
  (0, import_common5.Header)("Content-Type", "text/plain; version=0.0.4"),
1382
1396
  _ts_metadata4("design:type", Function),
1383
1397
  _ts_metadata4("design:paramtypes", []),
1384
1398
  _ts_metadata4("design:returntype", void 0)
1385
- ], DurableApiController.prototype, "metrics", null);
1399
+ ], DurableApiControllerBase.prototype, "metrics", null);
1386
1400
  _ts_decorate5([
1387
1401
  (0, import_common5.Get)("workers"),
1388
1402
  _ts_metadata4("design:type", Function),
1389
1403
  _ts_metadata4("design:paramtypes", []),
1390
1404
  _ts_metadata4("design:returntype", void 0)
1391
- ], DurableApiController.prototype, "workers", null);
1405
+ ], DurableApiControllerBase.prototype, "workers", null);
1392
1406
  _ts_decorate5([
1393
1407
  (0, import_common5.Get)("schedules"),
1394
1408
  _ts_param2(0, (0, import_common5.Query)("namespace")),
@@ -1405,7 +1419,7 @@ _ts_decorate5([
1405
1419
  String
1406
1420
  ]),
1407
1421
  _ts_metadata4("design:returntype", void 0)
1408
- ], DurableApiController.prototype, "schedules", null);
1422
+ ], DurableApiControllerBase.prototype, "schedules", null);
1409
1423
  _ts_decorate5([
1410
1424
  (0, import_common5.Post)("schedules/:id/pause"),
1411
1425
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1414,7 +1428,7 @@ _ts_decorate5([
1414
1428
  String
1415
1429
  ]),
1416
1430
  _ts_metadata4("design:returntype", void 0)
1417
- ], DurableApiController.prototype, "pauseSchedule", null);
1431
+ ], DurableApiControllerBase.prototype, "pauseSchedule", null);
1418
1432
  _ts_decorate5([
1419
1433
  (0, import_common5.Post)("schedules/:id/resume"),
1420
1434
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1423,7 +1437,7 @@ _ts_decorate5([
1423
1437
  String
1424
1438
  ]),
1425
1439
  _ts_metadata4("design:returntype", void 0)
1426
- ], DurableApiController.prototype, "resumeSchedule", null);
1440
+ ], DurableApiControllerBase.prototype, "resumeSchedule", null);
1427
1441
  _ts_decorate5([
1428
1442
  (0, import_common5.Post)("schedules/:id/trigger"),
1429
1443
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1432,13 +1446,22 @@ _ts_decorate5([
1432
1446
  String
1433
1447
  ]),
1434
1448
  _ts_metadata4("design:returntype", void 0)
1435
- ], DurableApiController.prototype, "triggerSchedule", null);
1449
+ ], DurableApiControllerBase.prototype, "triggerSchedule", null);
1450
+ _ts_decorate5([
1451
+ (0, import_common5.Get)("workflows/:name/lifecycle"),
1452
+ _ts_param2(0, (0, import_common5.Param)("name")),
1453
+ _ts_metadata4("design:type", Function),
1454
+ _ts_metadata4("design:paramtypes", [
1455
+ String
1456
+ ]),
1457
+ _ts_metadata4("design:returntype", Promise)
1458
+ ], DurableApiControllerBase.prototype, "workflowLifecycle", null);
1436
1459
  _ts_decorate5([
1437
1460
  (0, import_common5.Get)("topology"),
1438
1461
  _ts_metadata4("design:type", Function),
1439
1462
  _ts_metadata4("design:paramtypes", []),
1440
1463
  _ts_metadata4("design:returntype", void 0)
1441
- ], DurableApiController.prototype, "topology", null);
1464
+ ], DurableApiControllerBase.prototype, "topology", null);
1442
1465
  _ts_decorate5([
1443
1466
  (0, import_common5.Get)("runs/:id"),
1444
1467
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1455,7 +1478,7 @@ _ts_decorate5([
1455
1478
  Object
1456
1479
  ]),
1457
1480
  _ts_metadata4("design:returntype", Promise)
1458
- ], DurableApiController.prototype, "run", null);
1481
+ ], DurableApiControllerBase.prototype, "run", null);
1459
1482
  _ts_decorate5([
1460
1483
  (0, import_common5.Get)("runs/:id/fans/:group"),
1461
1484
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1478,7 +1501,7 @@ _ts_decorate5([
1478
1501
  Object
1479
1502
  ]),
1480
1503
  _ts_metadata4("design:returntype", Promise)
1481
- ], DurableApiController.prototype, "fanMembers", null);
1504
+ ], DurableApiControllerBase.prototype, "fanMembers", null);
1482
1505
  _ts_decorate5([
1483
1506
  (0, import_common5.Sse)("runs/:id/stream"),
1484
1507
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1487,7 +1510,7 @@ _ts_decorate5([
1487
1510
  String
1488
1511
  ]),
1489
1512
  _ts_metadata4("design:returntype", void 0)
1490
- ], DurableApiController.prototype, "stream", null);
1513
+ ], DurableApiControllerBase.prototype, "stream", null);
1491
1514
  _ts_decorate5([
1492
1515
  (0, import_common5.Post)("runs/:id/retry"),
1493
1516
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1496,7 +1519,7 @@ _ts_decorate5([
1496
1519
  String
1497
1520
  ]),
1498
1521
  _ts_metadata4("design:returntype", void 0)
1499
- ], DurableApiController.prototype, "retry", null);
1522
+ ], DurableApiControllerBase.prototype, "retry", null);
1500
1523
  _ts_decorate5([
1501
1524
  (0, import_common5.Post)("runs/:id/retry-with-input"),
1502
1525
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1507,7 +1530,7 @@ _ts_decorate5([
1507
1530
  Object
1508
1531
  ]),
1509
1532
  _ts_metadata4("design:returntype", void 0)
1510
- ], DurableApiController.prototype, "retryWithInput", null);
1533
+ ], DurableApiControllerBase.prototype, "retryWithInput", null);
1511
1534
  _ts_decorate5([
1512
1535
  (0, import_common5.Post)("bulk/:action"),
1513
1536
  _ts_param2(0, (0, import_common5.Param)("action")),
@@ -1520,7 +1543,7 @@ _ts_decorate5([
1520
1543
  String
1521
1544
  ]),
1522
1545
  _ts_metadata4("design:returntype", void 0)
1523
- ], DurableApiController.prototype, "bulk", null);
1546
+ ], DurableApiControllerBase.prototype, "bulk", null);
1524
1547
  _ts_decorate5([
1525
1548
  (0, import_common5.Post)("runs/:id/cancel"),
1526
1549
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1531,7 +1554,18 @@ _ts_decorate5([
1531
1554
  String
1532
1555
  ]),
1533
1556
  _ts_metadata4("design:returntype", Promise)
1534
- ], DurableApiController.prototype, "cancel", null);
1557
+ ], DurableApiControllerBase.prototype, "cancel", null);
1558
+ _ts_decorate5([
1559
+ (0, import_common5.Post)("runs/:id/republish-lifecycle"),
1560
+ _ts_param2(0, (0, import_common5.Param)("id")),
1561
+ _ts_param2(1, (0, import_common5.Body)()),
1562
+ _ts_metadata4("design:type", Function),
1563
+ _ts_metadata4("design:paramtypes", [
1564
+ String,
1565
+ Object
1566
+ ]),
1567
+ _ts_metadata4("design:returntype", Promise)
1568
+ ], DurableApiControllerBase.prototype, "republishLifecycle", null);
1535
1569
  _ts_decorate5([
1536
1570
  (0, import_common5.Post)("runs/:id/continue"),
1537
1571
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1540,7 +1574,7 @@ _ts_decorate5([
1540
1574
  String
1541
1575
  ]),
1542
1576
  _ts_metadata4("design:returntype", Promise)
1543
- ], DurableApiController.prototype, "continue", null);
1577
+ ], DurableApiControllerBase.prototype, "continue", null);
1544
1578
  _ts_decorate5([
1545
1579
  (0, import_common5.Post)("runs/:id/redispatch"),
1546
1580
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1549,7 +1583,7 @@ _ts_decorate5([
1549
1583
  String
1550
1584
  ]),
1551
1585
  _ts_metadata4("design:returntype", Promise)
1552
- ], DurableApiController.prototype, "redispatch", null);
1586
+ ], DurableApiControllerBase.prototype, "redispatch", null);
1553
1587
  _ts_decorate5([
1554
1588
  (0, import_common5.Post)("webhooks/:token"),
1555
1589
  _ts_param2(0, (0, import_common5.Param)("token")),
@@ -1560,7 +1594,7 @@ _ts_decorate5([
1560
1594
  Object
1561
1595
  ]),
1562
1596
  _ts_metadata4("design:returntype", Promise)
1563
- ], DurableApiController.prototype, "webhook", null);
1597
+ ], DurableApiControllerBase.prototype, "webhook", null);
1564
1598
  _ts_decorate5([
1565
1599
  (0, import_common5.Get)("runs/:id/events/:key"),
1566
1600
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1571,7 +1605,7 @@ _ts_decorate5([
1571
1605
  String
1572
1606
  ]),
1573
1607
  _ts_metadata4("design:returntype", void 0)
1574
- ], DurableApiController.prototype, "event", null);
1608
+ ], DurableApiControllerBase.prototype, "event", null);
1575
1609
  _ts_decorate5([
1576
1610
  (0, import_common5.Post)("runs/:id/updates/:name"),
1577
1611
  _ts_param2(0, (0, import_common5.Param)("id")),
@@ -1584,8 +1618,8 @@ _ts_decorate5([
1584
1618
  Object
1585
1619
  ]),
1586
1620
  _ts_metadata4("design:returntype", void 0)
1587
- ], DurableApiController.prototype, "update", null);
1588
- DurableApiController = _ts_decorate5([
1621
+ ], DurableApiControllerBase.prototype, "update", null);
1622
+ DurableApiControllerBase = _ts_decorate5([
1589
1623
  (0, import_common5.Controller)(),
1590
1624
  (0, import_common5.UseInterceptors)(import_nestjs_filter2.ApplyFilterInterceptor),
1591
1625
  (0, import_common5.UseFilters)(NotModifiedFilter),
@@ -1594,7 +1628,7 @@ DurableApiController = _ts_decorate5([
1594
1628
  typeof DashboardService === "undefined" ? Object : DashboardService,
1595
1629
  typeof import_nestjs_filter2.FilterRunner === "undefined" ? Object : import_nestjs_filter2.FilterRunner
1596
1630
  ])
1597
- ], DurableApiController);
1631
+ ], DurableApiControllerBase);
1598
1632
  function notFound(error) {
1599
1633
  if (error instanceof Error && error.name === "ScheduleNotFoundError") {
1600
1634
  throw new import_common5.NotFoundException(error.message);
@@ -1602,497 +1636,58 @@ function notFound(error) {
1602
1636
  throw error;
1603
1637
  }
1604
1638
  __name(notFound, "notFound");
1605
-
1606
- // src/server/durable-auth.controller.ts
1607
- var import_common9 = require("@nestjs/common");
1608
-
1609
- // src/server/auth/dashboard-auth-config.ts
1610
- var DASHBOARD_AUTH = Symbol("DASHBOARD_AUTH");
1611
- var DEFAULT_TTL = "8h";
1612
- var DEFAULT_TTL_MS = 8 * 60 * 60 * 1e3;
1613
- var DURATION_UNITS = {
1614
- s: 1e3,
1615
- m: 60 * 1e3,
1616
- h: 60 * 60 * 1e3,
1617
- d: 24 * 60 * 60 * 1e3
1618
- };
1619
- function durationToMs(ttl) {
1620
- const match = /^(\d+)([smhd])$/.exec(ttl.trim());
1621
- if (!match) return DEFAULT_TTL_MS;
1622
- const unit = DURATION_UNITS[match[2] ?? ""];
1623
- if (unit === void 0) return DEFAULT_TTL_MS;
1624
- return Number(match[1]) * unit;
1625
- }
1626
- __name(durationToMs, "durationToMs");
1627
- function resolveDashboardAuth(options) {
1628
- if (options === void 0) return null;
1629
- if (typeof options.secret !== "string" || options.secret === "") {
1630
- throw new Error("DurableDashboardModule: dashboardAuth.secret is required and must be a non-empty string (HMAC-SHA256 signing key, 32+ bytes recommended). Failing closed.");
1631
- }
1632
- const modes = [];
1633
- if (options.session !== void 0) {
1634
- if (typeof options.session !== "function") {
1635
- throw new Error("DurableDashboardModule: dashboardAuth.session must be a function.");
1636
- }
1637
- modes.push("session");
1638
- }
1639
- if (options.login !== void 0) {
1640
- if (typeof options.login !== "function") {
1641
- throw new Error("DurableDashboardModule: dashboardAuth.login must be a function.");
1642
- }
1643
- modes.push("login");
1644
- }
1645
- if (options.revalidate !== void 0 && typeof options.revalidate !== "function") {
1646
- throw new Error("DurableDashboardModule: dashboardAuth.revalidate must be a function.");
1639
+ function notSupportedByServer(error) {
1640
+ if (error?.code === "unsupported-request") {
1641
+ throw new import_common5.NotImplementedException(error instanceof Error ? error.message : String(error));
1647
1642
  }
1648
- if (options.unauthenticatedPage !== void 0 && typeof options.unauthenticatedPage !== "function") {
1649
- throw new Error("DurableDashboardModule: dashboardAuth.unauthenticatedPage must be a function.");
1650
- }
1651
- if (modes.length === 0) {
1652
- throw new Error("DurableDashboardModule: dashboardAuth needs at least one of `session` or `login` (otherwise the cookie can never be minted). Failing closed.");
1653
- }
1654
- return {
1655
- secret: options.secret,
1656
- ttlMs: durationToMs(options.ttl ?? DEFAULT_TTL),
1657
- modes,
1658
- ...options.session !== void 0 ? {
1659
- session: options.session
1660
- } : {},
1661
- ...options.login !== void 0 ? {
1662
- login: options.login
1663
- } : {},
1664
- ...options.revalidate !== void 0 ? {
1665
- revalidate: options.revalidate
1666
- } : {},
1667
- ...options.unauthenticatedPage !== void 0 ? {
1668
- unauthenticatedPage: options.unauthenticatedPage
1669
- } : {}
1670
- };
1671
- }
1672
- __name(resolveDashboardAuth, "resolveDashboardAuth");
1673
-
1674
- // src/server/auth/login-page.ts
1675
- function page(content, title = "Durable") {
1676
- return `<!doctype html>
1677
- <html lang="en">
1678
- <head>
1679
- <meta charset="utf-8" />
1680
- <meta name="viewport" content="width=device-width, initial-scale=1" />
1681
- <meta name="robots" content="noindex, nofollow" />
1682
- <title>${title}</title>
1683
- <style>
1684
- :root { color-scheme: dark; }
1685
- * { box-sizing: border-box; }
1686
- body {
1687
- margin: 0;
1688
- min-height: 100vh;
1689
- display: flex;
1690
- align-items: center;
1691
- justify-content: center;
1692
- background: #09090b;
1693
- color: #e4e4e7;
1694
- font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1695
- padding: 16px;
1696
- }
1697
- .card {
1698
- width: 100%;
1699
- max-width: 384px;
1700
- border: 1px solid #27272a;
1701
- background: #18181b;
1702
- border-radius: 8px;
1703
- padding: 32px;
1704
- box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
1705
- }
1706
- .brand {
1707
- margin: 0 0 24px;
1708
- text-align: center;
1709
- font-size: 18px;
1710
- font-weight: 600;
1711
- color: #34d399;
1712
- }
1713
- h1 {
1714
- margin: 0 0 12px;
1715
- font-size: 16px;
1716
- font-weight: 600;
1717
- color: #f4f4f5;
1718
- }
1719
- p { margin: 0 0 16px; color: #a1a1aa; }
1720
- form { display: flex; flex-direction: column; gap: 16px; }
1721
- label { display: flex; flex-direction: column; gap: 6px; }
1722
- .field-label {
1723
- font-size: 11px;
1724
- text-transform: uppercase;
1725
- letter-spacing: 0.05em;
1726
- color: #71717a;
1727
- }
1728
- input {
1729
- border-radius: 4px;
1730
- border: 1px solid #3f3f46;
1731
- background: #09090b;
1732
- color: #f4f4f5;
1733
- padding: 8px 12px;
1734
- font: inherit;
1735
- outline: none;
1736
- }
1737
- input:focus { border-color: rgb(52 211 153 / 0.6); }
1738
- #error {
1739
- display: none;
1740
- margin: 0;
1741
- font-size: 12px;
1742
- color: #fb7185;
1743
- }
1744
- button, .button {
1745
- margin-top: 8px;
1746
- border-radius: 4px;
1747
- border: 1px solid rgb(52 211 153 / 0.4);
1748
- background: rgb(52 211 153 / 0.1);
1749
- color: #6ee7b7;
1750
- padding: 8px 12px;
1751
- font: inherit;
1752
- font-size: 12px;
1753
- font-weight: 500;
1754
- text-transform: uppercase;
1755
- letter-spacing: 0.05em;
1756
- cursor: pointer;
1757
- display: inline-block;
1758
- text-align: center;
1759
- text-decoration: none;
1760
- box-sizing: border-box;
1761
- }
1762
- button:hover:not(:disabled), .button:hover { background: rgb(52 211 153 / 0.2); }
1763
- button:disabled { opacity: 0.6; cursor: default; }
1764
- </style>
1765
- </head>
1766
- <body>
1767
- <div class="card">
1768
- ${content}
1769
- </div>
1770
- </body>
1771
- </html>`;
1643
+ throw error;
1772
1644
  }
1773
- __name(page, "page");
1774
- function renderLoginPage(basePath) {
1775
- const loginAction = `${basePath}/login`;
1776
- const defaultReturnTo = basePath;
1777
- return page(`<p class="brand">Durable</p>
1778
- <form id="login-form" autocomplete="on">
1779
- <label>
1780
- <span class="field-label">Username</span>
1781
- <input id="username" name="username" type="text" autocomplete="username" required autofocus />
1782
- </label>
1783
- <label>
1784
- <span class="field-label">Password</span>
1785
- <input id="password" name="password" type="password" autocomplete="current-password" />
1786
- </label>
1787
- <p id="error" role="alert">Invalid username or password.</p>
1788
- <button id="submit" type="submit">Sign in</button>
1789
- </form>
1790
- <script>
1791
- (function () {
1792
- var LOGIN_ACTION = ${JSON.stringify(loginAction)};
1793
- var DEFAULT_RETURN_TO = ${JSON.stringify(defaultReturnTo)};
1794
- var params = new URLSearchParams(window.location.search);
1795
- var errorBox = document.getElementById('error');
1796
- if (params.get('error')) errorBox.style.display = 'block';
1797
-
1798
- function sameOriginReturnTo(value) {
1799
- if (typeof value !== 'string' || value === '') return DEFAULT_RETURN_TO;
1800
- if (value.charAt(0) !== '/' || value.charAt(1) === '/' || value.indexOf('://') !== -1) {
1801
- return DEFAULT_RETURN_TO;
1802
- }
1803
- return value;
1645
+ __name(notSupportedByServer, "notSupportedByServer");
1646
+ var DurableApiController = class extends DurableApiControllerBase {
1647
+ static {
1648
+ __name(this, "DurableApiController");
1804
1649
  }
1650
+ };
1651
+ DurableApiController = _ts_decorate5([
1652
+ (0, import_common5.Controller)()
1653
+ ], DurableApiController);
1805
1654
 
1806
- var form = document.getElementById('login-form');
1807
- var submitButton = document.getElementById('submit');
1808
- form.addEventListener('submit', function (event) {
1809
- event.preventDefault();
1810
- submitButton.disabled = true;
1811
- errorBox.style.display = 'none';
1812
- var returnTo = sameOriginReturnTo(params.get('returnTo'));
1813
- fetch(LOGIN_ACTION, {
1814
- method: 'POST',
1815
- headers: { 'content-type': 'application/json' },
1816
- credentials: 'same-origin',
1817
- body: JSON.stringify({
1818
- username: document.getElementById('username').value,
1819
- password: document.getElementById('password').value,
1820
- returnTo: returnTo,
1821
- }),
1822
- })
1823
- .then(function (response) {
1824
- if (!response.ok) throw new Error('unauthorized');
1825
- return response.json();
1826
- })
1827
- .then(function (data) {
1828
- window.location.href = data.redirectTo || DEFAULT_RETURN_TO;
1829
- })
1830
- .catch(function () {
1831
- errorBox.style.display = 'block';
1832
- submitButton.disabled = false;
1833
- });
1834
- });
1835
- })();
1836
- </script>`, "Sign in \u2014 Durable");
1837
- }
1838
- __name(renderLoginPage, "renderLoginPage");
1839
- function renderSessionRequiredPage(basePath) {
1840
- return page(`<h1>Open this console from your application</h1>
1841
- <p>Your session is minted by the host app. Use its console launcher to sign in, then reload.</p>
1842
- <a class="button" href="${basePath}">Retry</a>`);
1843
- }
1844
- __name(renderSessionRequiredPage, "renderSessionRequiredPage");
1845
-
1846
- // src/server/auth/request.ts
1847
- function isRecord2(value) {
1848
- return typeof value === "object" && value !== null;
1849
- }
1850
- __name(isRecord2, "isRecord");
1851
- function readHeaders(request) {
1852
- if (!isRecord2(request)) return {};
1853
- const headers = request.headers;
1854
- return isRecord2(headers) ? headers : {};
1855
- }
1856
- __name(readHeaders, "readHeaders");
1857
- function headerToString(value) {
1858
- if (typeof value === "string") return value;
1859
- if (Array.isArray(value) && value.every((part) => typeof part === "string")) {
1860
- return value.join(",");
1861
- }
1862
- return void 0;
1863
- }
1864
- __name(headerToString, "headerToString");
1865
- function readCookieHeader(request) {
1866
- return headerToString(readHeaders(request).cookie);
1867
- }
1868
- __name(readCookieHeader, "readCookieHeader");
1869
- function isHttpsRequest(request) {
1870
- if (isRecord2(request) && request.protocol === "https") return true;
1871
- const forwarded = headerToString(readHeaders(request)["x-forwarded-proto"]);
1872
- if (forwarded === void 0) return false;
1873
- return forwarded.split(",").map((proto) => proto.trim().toLowerCase()).includes("https");
1874
- }
1875
- __name(isHttpsRequest, "isHttpsRequest");
1876
- function originalRequestUrl(request) {
1877
- if (!isRecord2(request)) return "/";
1878
- if (typeof request.originalUrl === "string" && request.originalUrl !== "") {
1879
- return request.originalUrl;
1880
- }
1881
- if (typeof request.url === "string" && request.url !== "") return request.url;
1882
- return "/";
1883
- }
1884
- __name(originalRequestUrl, "originalRequestUrl");
1885
- function sanitizeReturnTo(candidate, fallback) {
1886
- if (typeof candidate !== "string") return fallback;
1887
- if (!candidate.startsWith("/")) return fallback;
1888
- if (candidate.startsWith("//")) return fallback;
1889
- if (candidate.includes("://")) return fallback;
1890
- return candidate;
1891
- }
1892
- __name(sanitizeReturnTo, "sanitizeReturnTo");
1655
+ // src/server/durable-dashboard.module.ts
1656
+ var import_reflect_metadata2 = require("reflect-metadata");
1657
+ var import_nestjs_filter3 = require("@dudousxd/nestjs-filter");
1658
+ var import_common7 = require("@nestjs/common");
1659
+ var import_core = require("@nestjs/core");
1893
1660
 
1894
- // src/server/auth/cookie-header.ts
1895
- function parseCookieHeader(header) {
1896
- const cookies = {};
1897
- if (typeof header !== "string" || header === "") return cookies;
1898
- for (const segment of header.split(";")) {
1899
- const eq = segment.indexOf("=");
1900
- if (eq <= 0) continue;
1901
- const name = segment.slice(0, eq).trim();
1902
- if (name === "") continue;
1903
- let rawValue = segment.slice(eq + 1).trim();
1904
- if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
1905
- rawValue = rawValue.slice(1, -1);
1906
- }
1907
- if (name in cookies) continue;
1908
- try {
1909
- cookies[name] = decodeURIComponent(rawValue);
1910
- } catch {
1911
- cookies[name] = rawValue;
1661
+ // src/server/controller-decorators.ts
1662
+ var import_reflect_metadata = require("reflect-metadata");
1663
+ var CAN_ANYONE_METADATA = "authorization:can-anyone";
1664
+ function markOpenToAuthorization(controller) {
1665
+ Reflect.defineMetadata(CAN_ANYONE_METADATA, true, controller);
1666
+ }
1667
+ __name(markOpenToAuthorization, "markOpenToAuthorization");
1668
+ function controllerFor(base, decorators) {
1669
+ if (decorators === void 0 || decorators.length === 0) return base;
1670
+ const derived = class extends base {
1671
+ static {
1672
+ __name(this, "derived");
1912
1673
  }
1913
- }
1914
- return cookies;
1915
- }
1916
- __name(parseCookieHeader, "parseCookieHeader");
1917
- function serializeSetCookie(name, value, options) {
1918
- const parts = [
1919
- `${name}=${options.clear ? "" : encodeURIComponent(value)}`
1920
- ];
1921
- parts.push(`Path=${options.path}`);
1922
- parts.push("HttpOnly");
1923
- parts.push("SameSite=Lax");
1924
- if (options.secure) parts.push("Secure");
1925
- if (options.clear) {
1926
- parts.push("Max-Age=0");
1927
- parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
1928
- } else {
1929
- parts.push(`Max-Age=${options.maxAgeSeconds}`);
1930
- }
1931
- return parts.join("; ");
1932
- }
1933
- __name(serializeSetCookie, "serializeSetCookie");
1934
-
1935
- // src/server/auth/session-cookie.ts
1936
- var import_node_crypto = require("crypto");
1937
- var EXP_GRACE_MS = 3e4;
1938
- function base64urlEncode(value) {
1939
- return Buffer.from(value, "utf8").toString("base64url");
1940
- }
1941
- __name(base64urlEncode, "base64urlEncode");
1942
- function base64urlDecode(value) {
1943
- return Buffer.from(value, "base64url").toString("utf8");
1944
- }
1945
- __name(base64urlDecode, "base64urlDecode");
1946
- function hmac(secret, payload) {
1947
- return (0, import_node_crypto.createHmac)("sha256", secret).update(payload).digest("base64url");
1948
- }
1949
- __name(hmac, "hmac");
1950
- function signSessionCookie(user, options) {
1951
- const issuedAt = options.now ?? Date.now();
1952
- const session = {
1953
- sub: user.id,
1954
- ...user.name !== void 0 ? {
1955
- name: user.name
1956
- } : {},
1957
- roles: user.roles ?? [],
1958
- iat: issuedAt,
1959
- exp: issuedAt + options.ttlMs
1960
- };
1961
- const encodedPayload = base64urlEncode(JSON.stringify(session));
1962
- const signature = hmac(options.secret, encodedPayload);
1963
- return `${encodedPayload}.${signature}`;
1964
- }
1965
- __name(signSessionCookie, "signSessionCookie");
1966
- function isSessionPayload(value) {
1967
- if (typeof value !== "object" || value === null) return false;
1968
- const record = {
1969
- ...value
1970
1674
  };
1971
- if (typeof record.sub !== "string") return false;
1972
- if (record.name !== void 0 && typeof record.name !== "string") return false;
1973
- if (!Array.isArray(record.roles)) return false;
1974
- if (!record.roles.every((role) => typeof role === "string")) return false;
1975
- if (typeof record.iat !== "number" || !Number.isFinite(record.iat)) return false;
1976
- if (typeof record.exp !== "number" || !Number.isFinite(record.exp)) return false;
1977
- return true;
1978
- }
1979
- __name(isSessionPayload, "isSessionPayload");
1980
- function verifySessionCookie(value, options) {
1981
- try {
1982
- const dot = value.indexOf(".");
1983
- if (dot <= 0 || dot === value.length - 1) return null;
1984
- const encodedPayload = value.slice(0, dot);
1985
- const providedSignature = value.slice(dot + 1);
1986
- const expectedSignature = hmac(options.secret, encodedPayload);
1987
- const provided = Buffer.from(providedSignature, "base64url");
1988
- const expected = Buffer.from(expectedSignature, "base64url");
1989
- if (provided.length !== expected.length) return null;
1990
- if (!(0, import_node_crypto.timingSafeEqual)(provided, expected)) return null;
1991
- const decoded = JSON.parse(base64urlDecode(encodedPayload));
1992
- if (!isSessionPayload(decoded)) return null;
1993
- const now = options.now ?? Date.now();
1994
- if (now > decoded.exp + EXP_GRACE_MS) return null;
1995
- const session = {
1996
- sub: decoded.sub,
1997
- ...decoded.name !== void 0 ? {
1998
- name: decoded.name
1999
- } : {},
2000
- roles: decoded.roles,
2001
- iat: decoded.iat,
2002
- exp: decoded.exp
2003
- };
2004
- return session;
2005
- } catch {
2006
- return null;
2007
- }
2008
- }
2009
- __name(verifySessionCookie, "verifySessionCookie");
2010
-
2011
- // src/server/auth/session-cookie-io.ts
2012
- var SESSION_COOKIE_NAME = "durable_dashboard_session";
2013
- var COOKIE_PATH = "/";
2014
- function issueSessionCookie(user, context) {
2015
- const value = signSessionCookie(user, {
2016
- secret: context.auth.secret,
2017
- ttlMs: context.auth.ttlMs,
2018
- ...context.now !== void 0 ? {
2019
- now: context.now
2020
- } : {}
2021
- });
2022
- const cookie = serializeSetCookie(SESSION_COOKIE_NAME, value, {
2023
- path: COOKIE_PATH,
2024
- maxAgeSeconds: Math.floor(context.auth.ttlMs / 1e3),
2025
- secure: isHttpsRequest(context.request)
2026
- });
2027
- appendSetCookie(context.response, cookie);
2028
- }
2029
- __name(issueSessionCookie, "issueSessionCookie");
2030
- function clearSessionCookie(context) {
2031
- const cookie = serializeSetCookie(SESSION_COOKIE_NAME, "", {
2032
- path: COOKIE_PATH,
2033
- maxAgeSeconds: 0,
2034
- secure: isHttpsRequest(context.request),
2035
- clear: true
2036
- });
2037
- appendSetCookie(context.response, cookie);
2038
- }
2039
- __name(clearSessionCookie, "clearSessionCookie");
2040
- function readSessionFromRequest(auth, request) {
2041
- const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];
2042
- if (cookieValue === void 0) return null;
2043
- return verifySessionCookie(cookieValue, {
2044
- secret: auth.secret
1675
+ Object.defineProperty(derived, "name", {
1676
+ value: base.name
2045
1677
  });
2046
- }
2047
- __name(readSessionFromRequest, "readSessionFromRequest");
2048
- var inFlightRevalidations = /* @__PURE__ */ new Map();
2049
- function revalidateOnce(auth, user) {
2050
- const revalidate = auth.revalidate;
2051
- if (!revalidate) return Promise.resolve(true);
2052
- const key = `${auth.secret}:${user.id}`;
2053
- const inFlight = inFlightRevalidations.get(key);
2054
- if (inFlight) return inFlight;
2055
- const call = Promise.resolve().then(() => revalidate(user)).catch(() => false).finally(() => {
2056
- inFlightRevalidations.delete(key);
2057
- });
2058
- inFlightRevalidations.set(key, call);
2059
- return call;
2060
- }
2061
- __name(revalidateOnce, "revalidateOnce");
2062
- async function maybeRenewSession(auth, session, request, response, now = Date.now()) {
2063
- if (now - session.iat <= auth.ttlMs / 2) return true;
2064
- const user = {
2065
- id: session.sub,
2066
- ...session.name !== void 0 ? {
2067
- name: session.name
2068
- } : {},
2069
- roles: session.roles
2070
- };
2071
- const allowed = await revalidateOnce(auth, user);
2072
- if (!allowed) {
2073
- clearSessionCookie({
2074
- request,
2075
- response
2076
- });
2077
- return false;
1678
+ markOpenToAuthorization(derived);
1679
+ let decorated = derived;
1680
+ for (const decorator of decorators) {
1681
+ decorated = decorator(decorated) ?? decorated;
2078
1682
  }
2079
- issueSessionCookie(user, {
2080
- auth,
2081
- request,
2082
- response,
2083
- now
2084
- });
2085
- return true;
1683
+ return decorated;
2086
1684
  }
2087
- __name(maybeRenewSession, "maybeRenewSession");
1685
+ __name(controllerFor, "controllerFor");
2088
1686
 
2089
1687
  // src/server/durable-ui.controller.ts
2090
1688
  var import_node_fs = require("fs");
2091
1689
  var import_node_path = require("path");
2092
1690
  var import_node_url = require("url");
2093
- var import_common8 = require("@nestjs/common");
2094
-
2095
- // src/server/auth/login-redirect.exception.ts
2096
1691
  var import_common6 = require("@nestjs/common");
2097
1692
  function _ts_decorate6(decorators, target, key, desc) {
2098
1693
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2108,46 +1703,6 @@ function _ts_decorate6(decorators, target, key, desc) {
2108
1703
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2109
1704
  }
2110
1705
  __name(_ts_decorate6, "_ts_decorate");
2111
- var DashboardLoginRedirectException = class extends import_common6.HttpException {
2112
- static {
2113
- __name(this, "DashboardLoginRedirectException");
2114
- }
2115
- location;
2116
- constructor(location) {
2117
- super({
2118
- location
2119
- }, import_common6.HttpStatus.FOUND), this.location = location;
2120
- }
2121
- };
2122
- var DashboardLoginRedirectFilter = class {
2123
- static {
2124
- __name(this, "DashboardLoginRedirectFilter");
2125
- }
2126
- catch(exception, host) {
2127
- const response = host.switchToHttp().getResponse();
2128
- redirectRaw(response, exception.location);
2129
- }
2130
- };
2131
- DashboardLoginRedirectFilter = _ts_decorate6([
2132
- (0, import_common6.Catch)(DashboardLoginRedirectException)
2133
- ], DashboardLoginRedirectFilter);
2134
-
2135
- // src/server/auth/session-required.exception.ts
2136
- var import_common7 = require("@nestjs/common");
2137
- function _ts_decorate7(decorators, target, key, desc) {
2138
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2139
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2140
- r = Reflect.decorate(decorators, target, key, desc);
2141
- } else {
2142
- for (var i = decorators.length - 1; i >= 0; i--) {
2143
- if (d = decorators[i]) {
2144
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2145
- }
2146
- }
2147
- }
2148
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2149
- }
2150
- __name(_ts_decorate7, "_ts_decorate");
2151
1706
  function _ts_metadata5(metadataKey, metadataValue) {
2152
1707
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2153
1708
  return Reflect.metadata(metadataKey, metadataValue);
@@ -2160,91 +1715,6 @@ function _ts_param3(paramIndex, decorator) {
2160
1715
  };
2161
1716
  }
2162
1717
  __name(_ts_param3, "_ts_param");
2163
- var DashboardSessionRequiredException = class extends import_common7.HttpException {
2164
- static {
2165
- __name(this, "DashboardSessionRequiredException");
2166
- }
2167
- basePath;
2168
- constructor(basePath) {
2169
- super({
2170
- basePath
2171
- }, import_common7.HttpStatus.UNAUTHORIZED), this.basePath = basePath;
2172
- }
2173
- };
2174
- var DashboardSessionRequiredFilter = class _DashboardSessionRequiredFilter {
2175
- static {
2176
- __name(this, "DashboardSessionRequiredFilter");
2177
- }
2178
- auth;
2179
- logger = new import_common7.Logger(_DashboardSessionRequiredFilter.name);
2180
- warned = false;
2181
- constructor(auth = null) {
2182
- this.auth = auth;
2183
- }
2184
- async catch(exception, host) {
2185
- const http = host.switchToHttp();
2186
- const response = http.getResponse();
2187
- const hook = this.auth?.unauthenticatedPage;
2188
- if (hook) {
2189
- try {
2190
- await hook({
2191
- request: http.getRequest(),
2192
- response,
2193
- basePath: exception.basePath
2194
- });
2195
- if (responseAlreadyWritten(response)) return;
2196
- this.warnOnce("dashboardAuth.unauthenticatedPage returned without writing a response; falling back to the built-in page. The hook must write AND end the response it is given.");
2197
- } catch (error) {
2198
- this.warnOnce(`dashboardAuth.unauthenticatedPage threw; falling back to the built-in page. ${String(error)}`);
2199
- if (responseAlreadyWritten(response)) return;
2200
- }
2201
- }
2202
- writeHtmlRaw(response, renderSessionRequiredPage(exception.basePath), import_common7.HttpStatus.UNAUTHORIZED);
2203
- }
2204
- /** Once per process, not once per request: a broken hook fires on every denied navigation. */
2205
- warnOnce(message) {
2206
- if (this.warned) return;
2207
- this.warned = true;
2208
- this.logger.warn(message);
2209
- }
2210
- };
2211
- DashboardSessionRequiredFilter = _ts_decorate7([
2212
- (0, import_common7.Catch)(DashboardSessionRequiredException),
2213
- _ts_param3(0, (0, import_common7.Optional)()),
2214
- _ts_param3(0, (0, import_common7.Inject)(DASHBOARD_AUTH)),
2215
- _ts_metadata5("design:type", Function),
2216
- _ts_metadata5("design:paramtypes", [
2217
- Object
2218
- ])
2219
- ], DashboardSessionRequiredFilter);
2220
-
2221
- // src/server/durable-ui.controller.ts
2222
- function _ts_decorate8(decorators, target, key, desc) {
2223
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2224
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2225
- r = Reflect.decorate(decorators, target, key, desc);
2226
- } else {
2227
- for (var i = decorators.length - 1; i >= 0; i--) {
2228
- if (d = decorators[i]) {
2229
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2230
- }
2231
- }
2232
- }
2233
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2234
- }
2235
- __name(_ts_decorate8, "_ts_decorate");
2236
- function _ts_metadata6(metadataKey, metadataValue) {
2237
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2238
- return Reflect.metadata(metadataKey, metadataValue);
2239
- }
2240
- }
2241
- __name(_ts_metadata6, "_ts_metadata");
2242
- function _ts_param4(paramIndex, decorator) {
2243
- return function(target, key) {
2244
- decorator(target, key, paramIndex);
2245
- };
2246
- }
2247
- __name(_ts_param4, "_ts_param");
2248
1718
  var DASHBOARD_BASE_PATH = Symbol("DASHBOARD_BASE_PATH");
2249
1719
  var DASHBOARD_API_PATH = Symbol("DASHBOARD_API_PATH");
2250
1720
  var BUILD_BASE = "/durable";
@@ -2261,9 +1731,9 @@ var CONTENT_TYPES = {
2261
1731
  ".woff2": "font/woff2",
2262
1732
  ".ico": "image/x-icon"
2263
1733
  };
2264
- var DurableUiController = class {
1734
+ var DurableUiControllerBase = class {
2265
1735
  static {
2266
- __name(this, "DurableUiController");
1736
+ __name(this, "DurableUiControllerBase");
2267
1737
  }
2268
1738
  basePath;
2269
1739
  apiBasePath;
@@ -2277,7 +1747,7 @@ var DurableUiController = class {
2277
1747
  index() {
2278
1748
  const indexPath = (0, import_node_path.join)(this.dir, "index.html");
2279
1749
  if (!(0, import_node_fs.existsSync)(indexPath)) {
2280
- throw new import_common8.NotFoundException("Dashboard is not built. Run the package build.");
1750
+ throw new import_common6.NotFoundException("Dashboard is not built. Run the package build.");
2281
1751
  }
2282
1752
  const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
2283
1753
  const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>`;
@@ -2285,235 +1755,57 @@ var DurableUiController = class {
2285
1755
  }
2286
1756
  asset(file) {
2287
1757
  const safe = (0, import_node_path.basename)(file);
2288
- if (safe !== file) throw new import_common8.NotFoundException();
1758
+ if (safe !== file) throw new import_common6.NotFoundException();
2289
1759
  const root = (0, import_node_path.resolve)(this.dir, "assets");
2290
1760
  const assetPath = (0, import_node_path.resolve)(root, safe);
2291
1761
  if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
2292
- throw new import_common8.NotFoundException();
1762
+ throw new import_common6.NotFoundException();
2293
1763
  }
2294
1764
  const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
2295
- return new import_common8.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
1765
+ return new import_common6.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
2296
1766
  type
2297
1767
  });
2298
1768
  }
2299
1769
  };
2300
- _ts_decorate8([
2301
- (0, import_common8.Get)(),
2302
- (0, import_common8.Header)("Content-Type", "text/html; charset=utf-8"),
2303
- (0, import_common8.Header)("Cache-Control", "no-store, must-revalidate"),
2304
- _ts_metadata6("design:type", Function),
2305
- _ts_metadata6("design:paramtypes", []),
2306
- _ts_metadata6("design:returntype", String)
2307
- ], DurableUiController.prototype, "index", null);
2308
- _ts_decorate8([
2309
- (0, import_common8.Get)("assets/:file"),
2310
- (0, import_common8.Header)("Cache-Control", "public, max-age=31536000, immutable"),
2311
- _ts_param4(0, (0, import_common8.Param)("file")),
2312
- _ts_metadata6("design:type", Function),
2313
- _ts_metadata6("design:paramtypes", [
1770
+ _ts_decorate6([
1771
+ (0, import_common6.Get)(),
1772
+ (0, import_common6.Header)("Content-Type", "text/html; charset=utf-8"),
1773
+ (0, import_common6.Header)("Cache-Control", "no-store, must-revalidate"),
1774
+ _ts_metadata5("design:type", Function),
1775
+ _ts_metadata5("design:paramtypes", []),
1776
+ _ts_metadata5("design:returntype", String)
1777
+ ], DurableUiControllerBase.prototype, "index", null);
1778
+ _ts_decorate6([
1779
+ (0, import_common6.Get)("assets/:file"),
1780
+ (0, import_common6.Header)("Cache-Control", "public, max-age=31536000, immutable"),
1781
+ _ts_param3(0, (0, import_common6.Param)("file")),
1782
+ _ts_metadata5("design:type", Function),
1783
+ _ts_metadata5("design:paramtypes", [
2314
1784
  String
2315
1785
  ]),
2316
- _ts_metadata6("design:returntype", typeof import_common8.StreamableFile === "undefined" ? Object : import_common8.StreamableFile)
2317
- ], DurableUiController.prototype, "asset", null);
2318
- DurableUiController = _ts_decorate8([
2319
- (0, import_common8.UseFilters)(DashboardLoginRedirectFilter, DashboardSessionRequiredFilter),
2320
- (0, import_common8.Controller)(),
2321
- _ts_param4(0, (0, import_common8.Inject)(DASHBOARD_BASE_PATH)),
2322
- _ts_param4(1, (0, import_common8.Inject)(DASHBOARD_API_PATH)),
2323
- _ts_metadata6("design:type", Function),
2324
- _ts_metadata6("design:paramtypes", [
1786
+ _ts_metadata5("design:returntype", typeof import_common6.StreamableFile === "undefined" ? Object : import_common6.StreamableFile)
1787
+ ], DurableUiControllerBase.prototype, "asset", null);
1788
+ DurableUiControllerBase = _ts_decorate6([
1789
+ (0, import_common6.Controller)(),
1790
+ _ts_param3(0, (0, import_common6.Inject)(DASHBOARD_BASE_PATH)),
1791
+ _ts_param3(1, (0, import_common6.Inject)(DASHBOARD_API_PATH)),
1792
+ _ts_metadata5("design:type", Function),
1793
+ _ts_metadata5("design:paramtypes", [
2325
1794
  String,
2326
1795
  String
2327
1796
  ])
2328
- ], DurableUiController);
2329
-
2330
- // src/server/durable-auth.controller.ts
2331
- function _ts_decorate9(decorators, target, key, desc) {
2332
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2333
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2334
- r = Reflect.decorate(decorators, target, key, desc);
2335
- } else {
2336
- for (var i = decorators.length - 1; i >= 0; i--) {
2337
- if (d = decorators[i]) {
2338
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2339
- }
2340
- }
2341
- }
2342
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2343
- }
2344
- __name(_ts_decorate9, "_ts_decorate");
2345
- function _ts_metadata7(metadataKey, metadataValue) {
2346
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2347
- return Reflect.metadata(metadataKey, metadataValue);
2348
- }
2349
- }
2350
- __name(_ts_metadata7, "_ts_metadata");
2351
- function _ts_param5(paramIndex, decorator) {
2352
- return function(target, key) {
2353
- decorator(target, key, paramIndex);
2354
- };
2355
- }
2356
- __name(_ts_param5, "_ts_param");
2357
- function isString(value) {
2358
- return typeof value === "string";
2359
- }
2360
- __name(isString, "isString");
2361
- var DurableAuthController = class _DurableAuthController {
1797
+ ], DurableUiControllerBase);
1798
+ var DurableUiController = class extends DurableUiControllerBase {
2362
1799
  static {
2363
- __name(this, "DurableAuthController");
2364
- }
2365
- auth;
2366
- basePath;
2367
- logger = new import_common9.Logger(_DurableAuthController.name);
2368
- /** Warn once per hook kind so a throwing hook doesn't spam logs on every failed attempt. */
2369
- warnedHooks = /* @__PURE__ */ new Set();
2370
- constructor(auth, basePath) {
2371
- this.auth = auth;
2372
- this.basePath = basePath;
2373
- }
2374
- // The login page reads `returnTo`/`error` from `location.search` client-side (see
2375
- // `login-page.ts`), so this response never varies per request and needs no templating beyond
2376
- // the developer-controlled `basePath`.
2377
- loginPage() {
2378
- const auth = this.requireAuth();
2379
- if (!auth.modes.includes("login")) throw new import_common9.NotFoundException();
2380
- return renderLoginPage(this.basePath);
2381
- }
2382
- // Called by the login page's own fetch (JSON, not a classic form POST — see `login-page.ts` for
2383
- // why: it keeps the login page working with no host-configured body parser beyond the
2384
- // JSON one Nest wires in by default on both Express and Fastify). Uniform `401` on any failure
2385
- // (unknown user, wrong password, throwing hook) — no user-enumeration.
2386
- async login(body, request, response) {
2387
- const auth = this.requireAuth();
2388
- if (!auth.modes.includes("login")) throw new import_common9.NotFoundException();
2389
- if (!isString(body?.username) || !isString(body?.password)) {
2390
- throw new import_common9.BadRequestException("Body must include string `username` and `password`.");
2391
- }
2392
- const { username, password } = body;
2393
- const user = await this.runHook("login", () => auth.login?.(username, password) ?? null);
2394
- if (!user) throw new import_common9.UnauthorizedException({
2395
- message: "Invalid username or password."
2396
- });
2397
- issueSessionCookie(user, {
2398
- auth,
2399
- request,
2400
- response
2401
- });
2402
- return {
2403
- redirectTo: sanitizeReturnTo(body.returnTo, this.basePath)
2404
- };
2405
- }
2406
- // Mode A: the host frontend (carrying its own auth) POSTs here; the host hook validates the raw
2407
- // request and returns the session user, or `null` to deny. This is the SSO path — the host's own
2408
- // guard decides, and no credential ever reaches this library.
2409
- async session(request, response) {
2410
- const auth = this.requireAuth();
2411
- if (!auth.modes.includes("session")) throw new import_common9.NotFoundException();
2412
- const user = await this.runHook("session", () => auth.session?.(request) ?? null);
2413
- if (!user) throw new import_common9.UnauthorizedException();
2414
- issueSessionCookie(user, {
2415
- auth,
2416
- request,
2417
- response
2418
- });
2419
- }
2420
- // Plain `GET` (not `POST`): logging out only ever destroys the CALLER's own session, so it's
2421
- // safe/idempotent from the caller's perspective — a simple `<a href>` (no JS, no CSRF token)
2422
- // works. Non-passthrough `@Res()`: this handler owns the response outright (clears the cookie,
2423
- // then redirects), so Nest must not also try to send a return value afterwards.
2424
- logout(request, response) {
2425
- clearSessionCookie({
2426
- request,
2427
- response
2428
- });
2429
- redirectRaw(response, this.auth?.modes.includes("login") ? `${this.basePath}/login` : this.basePath);
2430
- }
2431
- requireAuth() {
2432
- if (!this.auth) throw new import_common9.NotFoundException();
2433
- return this.auth;
2434
- }
2435
- /**
2436
- * Run a host hook (`login` or `session`) defensively: a throw is treated as a denial (uniform
2437
- * failure) and warn-logged once PER KIND, so a buggy hook never 500s the endpoint into a
2438
- * stack-trace leak nor floods the logs on repeated bad attempts.
2439
- */
2440
- async runHook(kind, run) {
2441
- try {
2442
- return await run() ?? null;
2443
- } catch (error) {
2444
- if (!this.warnedHooks.has(kind)) {
2445
- this.warnedHooks.add(kind);
2446
- this.logger.warn(`dashboardAuth ${kind} hook threw; treating as denial. ${error instanceof Error ? error.message : String(error)}`);
2447
- }
2448
- return null;
2449
- }
1800
+ __name(this, "DurableUiController");
2450
1801
  }
2451
1802
  };
2452
- _ts_decorate9([
2453
- (0, import_common9.Get)("login"),
2454
- (0, import_common9.Header)("Content-Type", "text/html; charset=utf-8"),
2455
- (0, import_common9.Header)("Cache-Control", "no-store, must-revalidate"),
2456
- _ts_metadata7("design:type", Function),
2457
- _ts_metadata7("design:paramtypes", []),
2458
- _ts_metadata7("design:returntype", String)
2459
- ], DurableAuthController.prototype, "loginPage", null);
2460
- _ts_decorate9([
2461
- (0, import_common9.Post)("login"),
2462
- (0, import_common9.HttpCode)(200),
2463
- _ts_param5(0, (0, import_common9.Body)()),
2464
- _ts_param5(1, (0, import_common9.Req)()),
2465
- _ts_param5(2, (0, import_common9.Res)({
2466
- passthrough: true
2467
- })),
2468
- _ts_metadata7("design:type", Function),
2469
- _ts_metadata7("design:paramtypes", [
2470
- typeof LoginBody === "undefined" ? Object : LoginBody,
2471
- Object,
2472
- Object
2473
- ]),
2474
- _ts_metadata7("design:returntype", Promise)
2475
- ], DurableAuthController.prototype, "login", null);
2476
- _ts_decorate9([
2477
- (0, import_common9.Post)("session"),
2478
- (0, import_common9.HttpCode)(204),
2479
- _ts_param5(0, (0, import_common9.Req)()),
2480
- _ts_param5(1, (0, import_common9.Res)({
2481
- passthrough: true
2482
- })),
2483
- _ts_metadata7("design:type", Function),
2484
- _ts_metadata7("design:paramtypes", [
2485
- Object,
2486
- Object
2487
- ]),
2488
- _ts_metadata7("design:returntype", Promise)
2489
- ], DurableAuthController.prototype, "session", null);
2490
- _ts_decorate9([
2491
- (0, import_common9.Get)("logout"),
2492
- _ts_param5(0, (0, import_common9.Req)()),
2493
- _ts_param5(1, (0, import_common9.Res)()),
2494
- _ts_metadata7("design:type", Function),
2495
- _ts_metadata7("design:paramtypes", [
2496
- Object,
2497
- Object
2498
- ]),
2499
- _ts_metadata7("design:returntype", void 0)
2500
- ], DurableAuthController.prototype, "logout", null);
2501
- DurableAuthController = _ts_decorate9([
2502
- (0, import_common9.Controller)(),
2503
- _ts_param5(0, (0, import_common9.Optional)()),
2504
- _ts_param5(0, (0, import_common9.Inject)(DASHBOARD_AUTH)),
2505
- _ts_param5(1, (0, import_common9.Inject)(DASHBOARD_BASE_PATH)),
2506
- _ts_metadata7("design:type", Function),
2507
- _ts_metadata7("design:paramtypes", [
2508
- Object,
2509
- String
2510
- ])
2511
- ], DurableAuthController);
1803
+ DurableUiController = _ts_decorate6([
1804
+ (0, import_common6.Controller)()
1805
+ ], DurableUiController);
2512
1806
 
2513
- // src/server/durable-dashboard-gate.guard.ts
2514
- var import_node_crypto2 = require("crypto");
2515
- var import_common10 = require("@nestjs/common");
2516
- function _ts_decorate10(decorators, target, key, desc) {
1807
+ // src/server/durable-dashboard.module.ts
1808
+ function _ts_decorate7(decorators, target, key, desc) {
2517
1809
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2518
1810
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2519
1811
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2526,342 +1818,59 @@ function _ts_decorate10(decorators, target, key, desc) {
2526
1818
  }
2527
1819
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2528
1820
  }
2529
- __name(_ts_decorate10, "_ts_decorate");
2530
- function _ts_metadata8(metadataKey, metadataValue) {
2531
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2532
- return Reflect.metadata(metadataKey, metadataValue);
2533
- }
2534
- }
2535
- __name(_ts_metadata8, "_ts_metadata");
2536
- function _ts_param6(paramIndex, decorator) {
2537
- return function(target, key) {
2538
- decorator(target, key, paramIndex);
2539
- };
2540
- }
2541
- __name(_ts_param6, "_ts_param");
2542
- var DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
2543
- var DASHBOARD_GATE = Symbol("DASHBOARD_GATE");
2544
- var LOG_CONTEXT = "DurableDashboard";
2545
- var HOW_TO_CONFIGURE = [
2546
- "Configure one of: `guards: [YourAuthGuard]`, `dashboardAuth: { secret, session | login }`",
2547
- "(or `useDashboardAuth` in forRootAsync), a bearer token via the `token` option or the",
2548
- `${DURABLE_DASHBOARD_TOKEN_ENV} env var, or \`allowUnauthenticated: true\` when the console is`,
2549
- "already protected outside this module (a global guard, a reverse proxy, a private network)."
1821
+ __name(_ts_decorate7, "_ts_decorate");
1822
+ markOpenToAuthorization(DurableApiController);
1823
+ markOpenToAuthorization(DurableUiController);
1824
+ var DASHBOARD_PROTECTION_CHECK = Symbol("DASHBOARD_PROTECTION_CHECK");
1825
+ var UNPROTECTED_CONSOLE_IN_PRODUCTION = [
1826
+ "DurableDashboardModule: NODE_ENV=production and the durable console is mounted with no",
1827
+ "protection, so the app refuses to boot. Protect it one of two ways:",
1828
+ "(1) decorators on the mounted console: DurableDashboardModule.forRoot({ decorators: [UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')] });",
1829
+ "(2) mount it yourself: DurableDashboardModule.forRoot({ routes: false }) plus @Controller('durable') @UseGuards(AuthenticationGuard) @Can(ConsolePolicy, 'view') class ConsoleUi extends DurableUiControllerBase {} (and the same over DurableApiControllerBase at 'durable/api')."
2550
1830
  ].join(" ");
2551
1831
  function isProduction() {
2552
1832
  return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
2553
1833
  }
2554
1834
  __name(isProduction, "isProduction");
2555
- function resolveDashboardGate(options, auth) {
2556
- if (options.hasGuards || auth !== null) return {
2557
- mode: "delegated"
2558
- };
2559
- const token = options.token ?? process.env[DURABLE_DASHBOARD_TOKEN_ENV];
2560
- if (typeof token === "string" && token !== "") return {
2561
- mode: "token",
2562
- token
2563
- };
2564
- if (options.allowUnauthenticated === true) return {
2565
- mode: "open"
2566
- };
1835
+ function protectionCheckProvider() {
2567
1836
  return {
2568
- mode: isProduction() ? "deny" : "unconfigured"
2569
- };
2570
- }
2571
- __name(resolveDashboardGate, "resolveDashboardGate");
2572
- function reportDashboardGate(gate, options, logger = new import_common10.Logger(LOG_CONTEXT)) {
2573
- switch (gate.mode) {
2574
- case "open":
2575
- logger.warn("allowUnauthenticated: true. The durable console (including retry/cancel and other mutating endpoints) is served with NO authentication from this module. Only do this when a global guard or reverse proxy already protects it.");
2576
- return;
2577
- case "token":
2578
- if (options.allowUnauthenticated === true) {
2579
- logger.warn("allowUnauthenticated: true is ignored because a dashboard token is configured; requests must carry the token.");
2580
- }
2581
- return;
2582
- case "unconfigured":
2583
- logger.warn([
2584
- "The durable console is UNAUTHENTICATED: no guards, dashboardAuth, token or",
2585
- "allowUnauthenticated were configured. It is served open because NODE_ENV is not",
2586
- '"production", but it WILL deny every request when NODE_ENV=production.',
2587
- HOW_TO_CONFIGURE
2588
- ].join(" "));
2589
- return;
2590
- case "deny":
2591
- logger.error([
2592
- "The durable console has no authentication configured and NODE_ENV=production, so it",
2593
- "is failing closed: every console request (UI and API) is denied.",
2594
- HOW_TO_CONFIGURE
2595
- ].join(" "));
2596
- return;
2597
- case "delegated":
2598
- return;
2599
- }
2600
- }
2601
- __name(reportDashboardGate, "reportDashboardGate");
2602
- function dashboardGateProvider(options) {
2603
- return {
2604
- provide: DASHBOARD_GATE,
2605
- inject: [
2606
- DASHBOARD_AUTH
2607
- ],
2608
- useFactory: /* @__PURE__ */ __name((auth) => {
2609
- const gate = resolveDashboardGate(options, auth);
2610
- reportDashboardGate(gate, options);
2611
- return gate;
1837
+ provide: DASHBOARD_PROTECTION_CHECK,
1838
+ useFactory: /* @__PURE__ */ __name(() => {
1839
+ if (isProduction()) throw new Error(UNPROTECTED_CONSOLE_IN_PRODUCTION);
1840
+ new import_common7.Logger("DurableDashboard").warn("The durable console is mounted with no `decorators`, so it is served OPEN (retry/cancel included). That is refused when NODE_ENV=production: pass `decorators` (e.g. [UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]) or mount it yourself with `routes: false`.");
1841
+ return true;
2612
1842
  }, "useFactory")
2613
1843
  };
2614
1844
  }
2615
- __name(dashboardGateProvider, "dashboardGateProvider");
2616
- function isRecord3(value) {
2617
- return typeof value === "object" && value !== null;
2618
- }
2619
- __name(isRecord3, "isRecord");
2620
- function headerValue(request, name) {
2621
- const headers = request.headers;
2622
- if (!isRecord3(headers)) return void 0;
2623
- const value = headers[name];
2624
- if (typeof value === "string") return value;
2625
- if (Array.isArray(value) && typeof value[0] === "string") return value[0];
2626
- return void 0;
2627
- }
2628
- __name(headerValue, "headerValue");
2629
- function readDashboardToken(request) {
2630
- if (!isRecord3(request)) return void 0;
2631
- const authorization = headerValue(request, "authorization");
2632
- if (authorization) {
2633
- const match = /^Bearer\s+(.+)$/i.exec(authorization);
2634
- if (match?.[1]) return match[1].trim();
2635
- }
2636
- const header = headerValue(request, "x-durable-token");
2637
- if (header) return header.trim();
2638
- const method = typeof request.method === "string" ? request.method.toUpperCase() : "";
2639
- if (method === "GET" || method === "HEAD") {
2640
- const query = request.query;
2641
- if (isRecord3(query)) {
2642
- const value = query.token;
2643
- if (typeof value === "string" && value !== "") return value;
2644
- }
2645
- }
2646
- return void 0;
2647
- }
2648
- __name(readDashboardToken, "readDashboardToken");
2649
- function secretsMatch(provided, expected) {
2650
- const a = Buffer.from(provided);
2651
- const b = Buffer.from(expected);
2652
- if (a.length !== b.length) return false;
2653
- return (0, import_node_crypto2.timingSafeEqual)(a, b);
2654
- }
2655
- __name(secretsMatch, "secretsMatch");
2656
- var DurableDashboardGateGuard = class {
2657
- static {
2658
- __name(this, "DurableDashboardGateGuard");
2659
- }
2660
- gate;
2661
- constructor(gate) {
2662
- this.gate = gate;
2663
- }
2664
- canActivate(context) {
2665
- switch (this.gate.mode) {
2666
- case "delegated":
2667
- case "open":
2668
- case "unconfigured":
2669
- return true;
2670
- case "deny":
2671
- throw new import_common10.ForbiddenException([
2672
- "Durable dashboard authentication is not configured, and NODE_ENV=production denies",
2673
- "an unauthenticated console.",
2674
- HOW_TO_CONFIGURE
2675
- ].join(" "));
2676
- case "token": {
2677
- const provided = readDashboardToken(context.switchToHttp().getRequest());
2678
- if (provided !== void 0 && secretsMatch(provided, this.gate.token ?? "")) return true;
2679
- throw new import_common10.UnauthorizedException("A valid durable dashboard token is required (Authorization: Bearer <token>, x-durable-token, or ?token= on GET).");
2680
- }
2681
- }
2682
- }
2683
- };
2684
- DurableDashboardGateGuard = _ts_decorate10([
2685
- (0, import_common10.Injectable)(),
2686
- _ts_param6(0, (0, import_common10.Inject)(DASHBOARD_GATE)),
2687
- _ts_metadata8("design:type", Function),
2688
- _ts_metadata8("design:paramtypes", [
2689
- typeof DashboardGate === "undefined" ? Object : DashboardGate
2690
- ])
2691
- ], DurableDashboardGateGuard);
2692
-
2693
- // src/server/durable-dashboard.module.ts
2694
- var import_reflect_metadata = require("reflect-metadata");
2695
- var import_nestjs_filter3 = require("@dudousxd/nestjs-filter");
2696
- var import_common12 = require("@nestjs/common");
2697
- var import_core = require("@nestjs/core");
2698
-
2699
- // src/server/durable-session.guard.ts
2700
- var import_common11 = require("@nestjs/common");
2701
- function _ts_decorate11(decorators, target, key, desc) {
2702
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2703
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2704
- r = Reflect.decorate(decorators, target, key, desc);
2705
- } else {
2706
- for (var i = decorators.length - 1; i >= 0; i--) {
2707
- if (d = decorators[i]) {
2708
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2709
- }
2710
- }
2711
- }
2712
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2713
- }
2714
- __name(_ts_decorate11, "_ts_decorate");
2715
- function _ts_metadata9(metadataKey, metadataValue) {
2716
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2717
- return Reflect.metadata(metadataKey, metadataValue);
2718
- }
2719
- }
2720
- __name(_ts_metadata9, "_ts_metadata");
2721
- function _ts_param7(paramIndex, decorator) {
2722
- return function(target, key) {
2723
- decorator(target, key, paramIndex);
2724
- };
2725
- }
2726
- __name(_ts_param7, "_ts_param");
2727
- var DurableUiSessionGuard = class {
2728
- static {
2729
- __name(this, "DurableUiSessionGuard");
2730
- }
2731
- auth;
2732
- basePath;
2733
- constructor(auth, basePath) {
2734
- this.auth = auth;
2735
- this.basePath = basePath;
2736
- }
2737
- async canActivate(context) {
2738
- if (!this.auth) return true;
2739
- const auth = this.auth;
2740
- const http = context.switchToHttp();
2741
- const request = http.getRequest();
2742
- const session = readSessionFromRequest(auth, request);
2743
- if (!session) {
2744
- this.denyUnauthenticated(auth, request);
2745
- }
2746
- if (!await maybeRenewSession(auth, session, request, http.getResponse())) {
2747
- this.denyUnauthenticated(auth, request);
2748
- }
2749
- return true;
2750
- }
2751
- /**
2752
- * A revoked session (the host's `revalidate` hook says no) is denied exactly like an absent
2753
- * one — by design, there is nothing left to distinguish "never had a session" from "had one,
2754
- * then lost it" once the cookie is cleared, so both get the same Mode-aware treatment.
2755
- *
2756
- * Takes `auth` explicitly rather than reading `this.auth`: `canActivate`'s `!this.auth` early
2757
- * return narrows the field within that method only — TS doesn't carry it across a method call
2758
- * — so callers pass the already-narrowed value instead of this method re-deriving (or
2759
- * asserting away) the nullability.
2760
- */
2761
- denyUnauthenticated(auth, request) {
2762
- if (!auth.modes.includes("login")) throw new DashboardSessionRequiredException(this.basePath);
2763
- const returnTo = encodeURIComponent(originalRequestUrl(request));
2764
- throw new DashboardLoginRedirectException(`${this.basePath}/login?returnTo=${returnTo}`);
2765
- }
2766
- };
2767
- DurableUiSessionGuard = _ts_decorate11([
2768
- (0, import_common11.Injectable)(),
2769
- _ts_param7(0, (0, import_common11.Inject)(DASHBOARD_AUTH)),
2770
- _ts_param7(1, (0, import_common11.Inject)(DASHBOARD_BASE_PATH)),
2771
- _ts_metadata9("design:type", Function),
2772
- _ts_metadata9("design:paramtypes", [
2773
- Object,
2774
- String
2775
- ])
2776
- ], DurableUiSessionGuard);
2777
- var DurableApiSessionGuard = class {
2778
- static {
2779
- __name(this, "DurableApiSessionGuard");
2780
- }
2781
- auth;
2782
- constructor(auth) {
2783
- this.auth = auth;
2784
- }
2785
- async canActivate(context) {
2786
- if (!this.auth) return true;
2787
- const auth = this.auth;
2788
- const http = context.switchToHttp();
2789
- const request = http.getRequest();
2790
- const session = readSessionFromRequest(auth, request);
2791
- if (!session) throw this.unauthorized(auth);
2792
- if (!await maybeRenewSession(auth, session, request, http.getResponse())) {
2793
- throw this.unauthorized(auth);
2794
- }
2795
- return true;
2796
- }
2797
- /**
2798
- * A bare 401 body carrying `{ auth: { modes } }` — mirrors `@dudousxd/nestjs-telescope`'s
2799
- * dashboardAuth 401. The API is fetched (never navigated), so this is the ONLY way the console
2800
- * SPA (`durable-client.ts`) learns which auth surface to send the operator to on a mid-session
2801
- * 401; `DurableUiSessionGuard`'s redirect/session-required exceptions above don't reach it.
2802
- */
2803
- unauthorized(auth) {
2804
- return new import_common11.UnauthorizedException({
2805
- auth: {
2806
- modes: auth.modes
2807
- }
2808
- });
2809
- }
2810
- };
2811
- DurableApiSessionGuard = _ts_decorate11([
2812
- (0, import_common11.Injectable)(),
2813
- _ts_param7(0, (0, import_common11.Inject)(DASHBOARD_AUTH)),
2814
- _ts_metadata9("design:type", Function),
2815
- _ts_metadata9("design:paramtypes", [
2816
- Object
2817
- ])
2818
- ], DurableApiSessionGuard);
2819
-
2820
- // src/server/durable-dashboard.module.ts
2821
- function _ts_decorate12(decorators, target, key, desc) {
2822
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2823
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2824
- r = Reflect.decorate(decorators, target, key, desc);
2825
- } else {
2826
- for (var i = decorators.length - 1; i >= 0; i--) {
2827
- if (d = decorators[i]) {
2828
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2829
- }
2830
- }
2831
- }
2832
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2833
- }
2834
- __name(_ts_decorate12, "_ts_decorate");
2835
- var GUARDS_METADATA = "__guards__";
1845
+ __name(protectionCheckProvider, "protectionCheckProvider");
1846
+ function assertNothingToDecorate(options) {
1847
+ if (options.decorators === void 0) return;
1848
+ throw new Error([
1849
+ "DurableDashboardModule.forRoot({ routes: false }) mounts no controllers, so `decorators`",
1850
+ "would protect nothing. Put your protection on the controllers you mount (extend",
1851
+ "DurableUiControllerBase / DurableApiControllerBase and decorate them with @UseGuards /",
1852
+ "@Can), and drop `decorators`."
1853
+ ].join(" "));
1854
+ }
1855
+ __name(assertNothingToDecorate, "assertNothingToDecorate");
2836
1856
  function normalize(path) {
2837
1857
  return `/${path.replace(/^\/+|\/+$/g, "")}`;
2838
1858
  }
2839
1859
  __name(normalize, "normalize");
2840
- function stampGuards(hostGuards, builtIn, controller) {
2841
- const merged = [
2842
- ...builtIn.filter((guard) => guard !== void 0),
2843
- ...hostGuards ?? []
2844
- ];
2845
- Reflect.defineMetadata(GUARDS_METADATA, merged, controller);
2846
- }
2847
- __name(stampGuards, "stampGuards");
2848
1860
  var DurableApiModule = class _DurableApiModule {
2849
1861
  static {
2850
1862
  __name(this, "DurableApiModule");
2851
1863
  }
2852
1864
  static register(options) {
2853
- stampGuards(options.guards, [
2854
- options.authMayBeConfigured ? DurableApiSessionGuard : void 0,
2855
- options.stampGate ? DurableDashboardGateGuard : void 0
2856
- ], DurableApiController);
1865
+ const routes = options.routes !== false;
2857
1866
  return {
2858
1867
  module: _DurableApiModule,
2859
1868
  imports: [
2860
1869
  ...options.imports ?? []
2861
1870
  ],
2862
- controllers: [
2863
- DurableApiController
2864
- ],
1871
+ controllers: routes ? [
1872
+ controllerFor(DurableApiController, options.decorators)
1873
+ ] : [],
2865
1874
  providers: [
2866
1875
  DashboardService,
2867
1876
  // `@dudousxd/nestjs-filter` is wired PRIVATELY here rather than through
@@ -2894,82 +1903,81 @@ var DurableApiModule = class _DurableApiModule {
2894
1903
  provide: RUN_QUERY_ADAPTER,
2895
1904
  useExisting: RunQueryAdapter
2896
1905
  },
2897
- RunFilter,
2898
- options.authProvider,
2899
- DurableApiSessionGuard,
2900
- // Resolved ONCE here (this module is imported by the UI module, which reuses it via the
2901
- // export below), so the boot report is logged once per mount.
2902
- dashboardGateProvider(options.gate),
2903
- DurableDashboardGateGuard,
2904
- ...options.guards ?? []
1906
+ RunFilter
2905
1907
  ],
2906
- exports: [
1908
+ // Self-mounted (`routes: false`): the host's own subclass of `DurableApiControllerBase` is
1909
+ // hosted in the HOST's module, so what its constructor injects must be exported. `FilterRunner`
1910
+ // is only exported then: exporting it from a mounted console would shadow a host's own
1911
+ // `FilterModule` runner in whatever module imports the dashboard.
1912
+ exports: routes ? [
1913
+ DashboardService
1914
+ ] : [
2907
1915
  DashboardService,
2908
- DASHBOARD_GATE
1916
+ import_nestjs_filter3.FilterRunner
2909
1917
  ]
2910
1918
  };
2911
1919
  }
2912
1920
  };
2913
- DurableApiModule = _ts_decorate12([
2914
- (0, import_common12.Module)({})
1921
+ DurableApiModule = _ts_decorate7([
1922
+ (0, import_common7.Module)({})
2915
1923
  ], DurableApiModule);
2916
1924
  var DurableDashboardModule = class _DurableDashboardModule {
2917
1925
  static {
2918
1926
  __name(this, "DurableDashboardModule");
2919
1927
  }
2920
1928
  static forRoot(options = {}) {
2921
- const authProvider = {
2922
- provide: DASHBOARD_AUTH,
2923
- useValue: resolveDashboardAuth(options.dashboardAuth)
2924
- };
2925
- const authConfigured = options.dashboardAuth !== void 0;
2926
- return _DurableDashboardModule.build(options, authProvider, authConfigured, authConfigured);
2927
- }
2928
- static forRootAsync(options) {
2929
- const authProvider = {
2930
- provide: DASHBOARD_AUTH,
2931
- inject: options.inject ?? [],
2932
- useFactory: /* @__PURE__ */ __name(async (...deps) => resolveDashboardAuth(await options.useDashboardAuth(...deps)), "useFactory")
2933
- };
2934
- return _DurableDashboardModule.build(options, authProvider, true, false);
2935
- }
2936
- /** Shared wiring: static routing + the API module, with `dashboardAuth` supplied by `authProvider`. */
2937
- static build(options, authProvider, authMayBeConfigured, authStaticallyConfigured) {
2938
1929
  const basePath = normalize(options.basePath ?? "/durable");
2939
1930
  const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
2940
- const hasGuards = (options.guards?.length ?? 0) > 0;
2941
- const gate = {
2942
- hasGuards,
2943
- ...options.allowUnauthenticated !== void 0 ? {
2944
- allowUnauthenticated: options.allowUnauthenticated
2945
- } : {},
2946
- ...options.token !== void 0 ? {
2947
- token: options.token
2948
- } : {}
2949
- };
2950
- const stampGate = !hasGuards && !authStaticallyConfigured;
2951
- stampGuards(options.guards, [
2952
- authMayBeConfigured ? DurableUiSessionGuard : void 0,
2953
- stampGate ? DurableDashboardGateGuard : void 0
2954
- ], DurableUiController);
1931
+ const pathProviders = [
1932
+ {
1933
+ provide: DASHBOARD_BASE_PATH,
1934
+ useValue: basePath
1935
+ },
1936
+ {
1937
+ provide: DASHBOARD_API_PATH,
1938
+ useValue: apiBasePath
1939
+ }
1940
+ ];
1941
+ if (options.routes === false) {
1942
+ assertNothingToDecorate(options);
1943
+ const api = DurableApiModule.register({
1944
+ ...options.imports ? {
1945
+ imports: options.imports
1946
+ } : {},
1947
+ routes: false
1948
+ });
1949
+ return {
1950
+ module: _DurableDashboardModule,
1951
+ imports: [
1952
+ ...options.imports ?? [],
1953
+ api
1954
+ ],
1955
+ controllers: [],
1956
+ providers: pathProviders,
1957
+ // What a host's subclasses of `DurableUiControllerBase` / `DurableApiControllerBase` inject:
1958
+ // the SPA's paths, and the read/action service and the filter runner via the API module.
1959
+ exports: [
1960
+ DurableApiModule,
1961
+ DASHBOARD_BASE_PATH,
1962
+ DASHBOARD_API_PATH
1963
+ ]
1964
+ };
1965
+ }
1966
+ const decorated = (options.decorators?.length ?? 0) > 0;
2955
1967
  return {
2956
1968
  module: _DurableDashboardModule,
2957
1969
  imports: [
2958
1970
  ...options.imports ?? [],
2959
- // Guards + host imports must reach the API controller's HOST module — enhancers resolve
2960
- // from their controller's own module, never from a parent (see DurableApiModule.register).
1971
+ // Host imports must reach the API controller's HOST module: enhancers resolve from their
1972
+ // controller's own module, never from a parent (see DurableApiModule.register).
2961
1973
  // Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.
2962
1974
  DurableApiModule.register({
2963
1975
  ...options.imports ? {
2964
1976
  imports: options.imports
2965
1977
  } : {},
2966
- ...options.guards ? {
2967
- guards: options.guards
2968
- } : {},
2969
- authProvider,
2970
- authMayBeConfigured,
2971
- gate,
2972
- stampGate
1978
+ ...options.decorators ? {
1979
+ decorators: options.decorators
1980
+ } : {}
2973
1981
  }),
2974
1982
  import_core.RouterModule.register([
2975
1983
  {
@@ -2983,30 +1991,13 @@ var DurableDashboardModule = class _DurableDashboardModule {
2983
1991
  ])
2984
1992
  ],
2985
1993
  controllers: [
2986
- DurableUiController,
2987
- // The auth controller (login page + logout) only exists when dashboardAuth might be
2988
- // configured — mounting it unconditionally would add a new `/login` route even for hosts
2989
- // that never set `dashboardAuth`, breaking "absent -> today's behavior byte-for-byte".
2990
- ...authMayBeConfigured ? [
2991
- DurableAuthController
2992
- ] : []
1994
+ controllerFor(DurableUiController, options.decorators)
2993
1995
  ],
2994
1996
  providers: [
2995
- {
2996
- provide: DASHBOARD_BASE_PATH,
2997
- useValue: basePath
2998
- },
2999
- {
3000
- provide: DASHBOARD_API_PATH,
3001
- useValue: apiBasePath
3002
- },
3003
- authProvider,
3004
- DurableUiSessionGuard,
3005
- // `DASHBOARD_GATE` itself comes from the imported DurableApiModule (resolved once there).
3006
- DurableDashboardGateGuard,
3007
- // DurableUiController (and DurableAuthController, when present) are hosted HERE, so their
3008
- // guards DI-instantiate from this module.
3009
- ...options.guards ?? []
1997
+ ...pathProviders,
1998
+ ...decorated ? [] : [
1999
+ protectionCheckProvider()
2000
+ ]
3010
2001
  ],
3011
2002
  // Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).
3012
2003
  exports: [
@@ -3015,27 +2006,21 @@ var DurableDashboardModule = class _DurableDashboardModule {
3015
2006
  };
3016
2007
  }
3017
2008
  };
3018
- DurableDashboardModule = _ts_decorate12([
3019
- (0, import_common12.Module)({})
2009
+ DurableDashboardModule = _ts_decorate7([
2010
+ (0, import_common7.Module)({})
3020
2011
  ], DurableDashboardModule);
3021
2012
  // Annotate the CommonJS export names for ESM import in node:
3022
2013
  0 && (module.exports = {
3023
2014
  DASHBOARD_API_PATH,
3024
2015
  DASHBOARD_BASE_PATH,
3025
- DASHBOARD_GATE,
3026
- DURABLE_DASHBOARD_TOKEN_ENV,
2016
+ DASHBOARD_PROTECTION_CHECK,
3027
2017
  DashboardService,
3028
2018
  DurableApiController,
2019
+ DurableApiControllerBase,
3029
2020
  DurableApiModule,
3030
- DurableApiSessionGuard,
3031
- DurableAuthController,
3032
- DurableDashboardGateGuard,
3033
2021
  DurableDashboardModule,
3034
2022
  DurableUiController,
3035
- DurableUiSessionGuard,
3036
- dashboardGateProvider,
3037
- readDashboardToken,
3038
- reportDashboardGate,
3039
- resolveDashboardGate
2023
+ DurableUiControllerBase,
2024
+ UNPROTECTED_CONSOLE_IN_PRODUCTION
3040
2025
  });
3041
2026
  //# sourceMappingURL=index.cjs.map