@dudousxd/nestjs-durable-dashboard 0.57.0 → 0.58.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.
@@ -5,7 +5,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
5
5
  import "reflect-metadata";
6
6
  import { createHash, timingSafeEqual } from "node:crypto";
7
7
  import { Catch, HttpException, Inject, Injectable, Logger, UseFilters, UseGuards, applyDecorators } from "@nestjs/common";
8
- import { HttpAdapterHost } from "@nestjs/core";
8
+ import { HttpAdapterHost, ModuleRef } from "@nestjs/core";
9
9
  function _ts_decorate(decorators, target, key, desc) {
10
10
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
11
11
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -36,9 +36,12 @@ var CONSOLE_GATE = Symbol.for("@dudousxd/aviary:console-gate");
36
36
  var PACKAGE = "@dudousxd/nestjs-durable-dashboard";
37
37
  var MODULE = "DurableDashboardModule";
38
38
  var CONSOLE_AUTHORIZE_MISSING_WARNING = `[${PACKAGE}] console has no \`authorize\` configured: it is open outside production and denies every request in production. Set \`authorize\` in ${MODULE}.forRoot().`;
39
- var allow = /* @__PURE__ */ __name(() => ({
39
+ var allow = /* @__PURE__ */ __name((caller) => caller !== void 0 && caller !== "" ? {
40
+ allowed: true,
41
+ caller
42
+ } : {
40
43
  allowed: true
41
- }), "allow");
44
+ }, "allow");
42
45
  var forbidden = /* @__PURE__ */ __name(() => ({
43
46
  allowed: false,
44
47
  reason: "forbidden"
@@ -78,7 +81,9 @@ function normalizeConsoleResult(result) {
78
81
  redirectTo: shape.redirectTo
79
82
  };
80
83
  }
81
- if (shape.allowed === true) return allow();
84
+ if (shape.allowed === true) {
85
+ return allow(typeof shape.caller === "string" ? shape.caller : void 0);
86
+ }
82
87
  return shape.reason === "unauthenticated" ? unauthenticated() : forbidden();
83
88
  }
84
89
  __name(normalizeConsoleResult, "normalizeConsoleResult");
@@ -161,11 +166,11 @@ function classDecider(moduleRef) {
161
166
  }
162
167
  __name(classDecider, "classDecider");
163
168
  async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
164
- const logger = options.logger ?? new Logger(MODULE);
169
+ const logger3 = options.logger ?? new Logger(MODULE);
165
170
  if (authorize === void 0) {
166
- if (options.warnIfMissing !== false) logger.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
171
+ if (options.warnIfMissing !== false) logger3.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
167
172
  const production = isProduction();
168
- return async () => production ? forbidden() : allow();
173
+ return async () => production ? forbidden() : allow("dev-open");
169
174
  }
170
175
  let decide;
171
176
  if (typeof authorize === "string") {
@@ -192,7 +197,7 @@ async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
192
197
  try {
193
198
  return await decide(context);
194
199
  } catch (error) {
195
- logger.warn(`console \`authorize\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`);
200
+ logger3.warn(`console \`authorize\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`);
196
201
  return forbidden();
197
202
  }
198
203
  };
@@ -205,10 +210,16 @@ function authorizeByToken(options) {
205
210
  throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
206
211
  }
207
212
  const expected = sha256(token);
213
+ const caller = `token:${expected.toString("hex").slice(0, 12)}`;
208
214
  return (context) => {
209
215
  const header = context.switchToHttp().getRequest()?.headers?.authorization;
210
216
  const match = typeof header === "string" ? /^Bearer\s+(.+)$/i.exec(header.trim()) : null;
211
- if (match?.[1] && timingSafeEqual(sha256(match[1]), expected)) return true;
217
+ if (match?.[1] && timingSafeEqual(sha256(match[1]), expected)) {
218
+ return {
219
+ allowed: true,
220
+ caller
221
+ };
222
+ }
212
223
  return {
213
224
  allowed: false,
214
225
  reason: "unauthenticated"
@@ -216,6 +227,48 @@ function authorizeByToken(options) {
216
227
  };
217
228
  }
218
229
  __name(authorizeByToken, "authorizeByToken");
230
+ function anyAuthorize(...forms) {
231
+ if (forms.length === 0) throw new Error("anyAuthorize: pass at least one authorize form.");
232
+ let AnyConsoleAuthorizer = class AnyConsoleAuthorizer {
233
+ static {
234
+ __name(this, "AnyConsoleAuthorizer");
235
+ }
236
+ moduleRef;
237
+ #decides;
238
+ constructor(moduleRef) {
239
+ this.moduleRef = moduleRef;
240
+ }
241
+ async authorize(context) {
242
+ this.#decides ??= Promise.all(forms.map((form) => resolveConsoleAuthorize(form, this.moduleRef, {
243
+ warnIfMissing: false
244
+ })));
245
+ this.#decides.catch(() => {
246
+ this.#decides = void 0;
247
+ });
248
+ let unauthenticated2;
249
+ for (const decide of await this.#decides) {
250
+ const decision = await decide(context);
251
+ if (decision.allowed) return decision;
252
+ if (decision.reason === "unauthenticated") unauthenticated2 ??= decision;
253
+ }
254
+ if (unauthenticated2 === void 0) return {
255
+ allowed: false,
256
+ reason: "forbidden"
257
+ };
258
+ return "redirectTo" in unauthenticated2 && unauthenticated2.redirectTo !== void 0 ? {
259
+ redirectTo: unauthenticated2.redirectTo
260
+ } : {
261
+ allowed: false,
262
+ reason: "unauthenticated"
263
+ };
264
+ }
265
+ };
266
+ Reflect.defineMetadata("design:paramtypes", [
267
+ ModuleRef
268
+ ], AnyConsoleAuthorizer);
269
+ return AnyConsoleAuthorizer;
270
+ }
271
+ __name(anyAuthorize, "anyAuthorize");
219
272
  var CONSOLE_AUTHORIZE_DECIDE = Symbol(`${PACKAGE}:console-authorize`);
220
273
  var CONSOLE_PAGE_METADATA = `${PACKAGE}:console-page`;
221
274
  var ConsolePage = /* @__PURE__ */ __name(() => (target) => {
@@ -227,7 +280,7 @@ var ConsoleAccessDenied = class extends HttpException {
227
280
  }
228
281
  decision;
229
282
  page;
230
- constructor(decision, page) {
283
+ constructor(decision, page2) {
231
284
  const unauthenticated2 = decision.reason === "unauthenticated";
232
285
  super(unauthenticated2 ? {
233
286
  error: "unauthenticated",
@@ -236,7 +289,7 @@ var ConsoleAccessDenied = class extends HttpException {
236
289
  } : {}
237
290
  } : {
238
291
  error: "forbidden"
239
- }, unauthenticated2 ? 401 : 403), this.decision = decision, this.page = page;
292
+ }, unauthenticated2 ? 401 : 403), this.decision = decision, this.page = page2;
240
293
  }
241
294
  };
242
295
  var CONSOLE_PUBLIC_METADATA = `${PACKAGE}:console-public`;
@@ -288,12 +341,12 @@ var ConsoleAuthorizeFilter = class {
288
341
  catch(exception, host) {
289
342
  const adapter = this.adapterHost.httpAdapter;
290
343
  const response = host.switchToHttp().getResponse();
291
- const { decision, page } = exception;
292
- if (page && decision.redirectTo) {
344
+ const { decision, page: page2 } = exception;
345
+ if (page2 && decision.redirectTo) {
293
346
  adapter.redirect(response, 302, decision.redirectTo);
294
347
  return;
295
348
  }
296
- if (page) {
349
+ if (page2) {
297
350
  const forbiddenPage = decision.reason === "forbidden";
298
351
  adapter.setHeader(response, "Content-Type", "text/html; charset=utf-8");
299
352
  adapter.reply(response, forbiddenPage ? DENIED_PAGE : SIGN_IN_PAGE, forbiddenPage ? 403 : 401);
@@ -465,31 +518,31 @@ var DashboardService = class {
465
518
  async workerHealth() {
466
519
  return this.gateway.workerHealth();
467
520
  }
468
- getRunDetail(runId, options) {
469
- return this.gateway.getRunDetail(runId, options);
521
+ getRunDetail(runId2, options) {
522
+ return this.gateway.getRunDetail(runId2, options);
470
523
  }
471
524
  /** A page of one fan's members, or null when the run is unknown or the gateway can't page fans. */
472
- async getFanMembers(runId, group, query) {
473
- return await this.gateway.getFanMembers?.(runId, group, query) ?? null;
525
+ async getFanMembers(runId2, group, query) {
526
+ return await this.gateway.getFanMembers?.(runId2, group, query) ?? null;
474
527
  }
475
528
  /** Fix-and-replay a dead/failed run with a corrected input — a fresh linked run. Returns its id. */
476
- retryWithInput(runId, input) {
477
- return this.gateway.retryWithInput(runId, input);
529
+ retryWithInput(runId2, input) {
530
+ return this.gateway.retryWithInput(runId2, input);
478
531
  }
479
532
  /** Re-run a failed/incomplete run; completed steps replay from their checkpoints. */
480
- retry(runId) {
481
- return this.gateway.retry(runId);
533
+ retry(runId2) {
534
+ return this.gateway.retry(runId2);
482
535
  }
483
- cancel(runId, opts) {
484
- return this.gateway.cancel(runId, opts);
536
+ cancel(runId2, opts) {
537
+ return this.gateway.cancel(runId2, opts);
485
538
  }
486
539
  /**
487
540
  * Publish a settled run's lifecycle event again, to its subscribers that have not started (or to
488
541
  * `subscriber` alone). Undefined when the gateway cannot republish (a tenant proxy).
489
542
  */
490
- async republishLifecycle(runId, subscriber) {
543
+ async republishLifecycle(runId2, subscriber) {
491
544
  return this.gateway.republishLifecycle?.({
492
- runId,
545
+ runId: runId2,
493
546
  ...subscriber !== void 0 ? {
494
547
  subscriber
495
548
  } : {}
@@ -557,12 +610,12 @@ var DashboardService = class {
557
610
  };
558
611
  }
559
612
  /** Resume a run paused at a `ctx.breakpoint` (the "continue" button). */
560
- continue(runId) {
561
- return this.gateway.continue(runId);
613
+ continue(runId2) {
614
+ return this.gateway.continue(runId2);
562
615
  }
563
616
  /** Re-dispatch a run's stuck `pending` remote steps — recovery for a lost step dispatch. */
564
- redispatch(runId) {
565
- return this.gateway.redispatchPending(runId);
617
+ redispatch(runId2) {
618
+ return this.gateway.redispatchPending(runId2);
566
619
  }
567
620
  /**
568
621
  * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
@@ -591,13 +644,39 @@ var DashboardService = class {
591
644
  status: fresh?.status ?? run.status
592
645
  };
593
646
  }
647
+ /**
648
+ * What this deployment can serve: `controlPlane` (signals, run events, updates, metrics) and
649
+ * `schedules` (persisted schedules). Both are false on a store-less tenant.
650
+ */
651
+ capabilities() {
652
+ const controlPlane = this.store !== void 0 && this.engine !== void 0;
653
+ return {
654
+ controlPlane,
655
+ schedules: controlPlane && this.engine?.schedules?.supported === true
656
+ };
657
+ }
658
+ /** Whether `runId` names a run the caller may see (tenancy applies). */
659
+ async runExists(runId2) {
660
+ if (this.gateway.getRun) return await this.gateway.getRun(runId2) !== null;
661
+ return await this.gateway.getRunDetail(runId2, {
662
+ summarizeFansOver: 0
663
+ }) !== null;
664
+ }
665
+ /**
666
+ * Deliver `payload` to the run waiting on `token` (`engine.signal`). Null when no run the caller
667
+ * may see waits on it yet: the engine then buffers the payload for the run that waits next.
668
+ * Control-plane-only.
669
+ */
670
+ signal(token, payload) {
671
+ return this.controlPlane().engine.signal(token, payload);
672
+ }
594
673
  /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
595
- async getEvent(runId, key) {
596
- return this.controlPlane().engine.getEvent(runId, key);
674
+ async getEvent(runId2, key) {
675
+ return this.controlPlane().engine.getEvent(runId2, key);
597
676
  }
598
677
  /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). Control-plane-only. */
599
- async update(runId, name, arg) {
600
- return this.controlPlane().engine.update(runId, name, arg);
678
+ async update(runId2, name, arg) {
679
+ return this.controlPlane().engine.update(runId2, name, arg);
601
680
  }
602
681
  /**
603
682
  * Live stream of a run's lifecycle events for SSE. Backed by `gateway.subscribe`, which — on the
@@ -605,9 +684,9 @@ var DashboardService = class {
605
684
  * executing on a worker pod), and on a tenant round-trips over the transport. Either way the
606
685
  * gateway already filters to `runId`; the dashboard just wires the events through.
607
686
  */
608
- streamRun(runId) {
687
+ streamRun(runId2) {
609
688
  return new Observable((subscriber) => {
610
- const off = this.gateway.subscribe(runId, (event) => subscriber.next({
689
+ const off = this.gateway.subscribe(runId2, (event) => subscriber.next({
611
690
  data: event
612
691
  }));
613
692
  return () => off();
@@ -855,7 +934,7 @@ var RunQueryDraft2 = class {
855
934
  /** The predicates minus the page window and the axes a facet count reports on — what
856
935
  * `runFacets`/`runValueFacets` are taken over. */
857
936
  facetQuery() {
858
- const { status, statuses, origin, origins, limit, offset, ...facetQuery } = this.query;
937
+ const { status, statuses, origin, origins, limit: limit2, offset, ...facetQuery } = this.query;
859
938
  return facetQuery;
860
939
  }
861
940
  };
@@ -1060,10 +1139,10 @@ var RunQueryAdapter = class {
1060
1139
  }
1061
1140
  unsupported(clause, "equals, notEquals, gt, gte, lt, lte, in");
1062
1141
  }
1063
- applyOffsetPagination(qb, page, size) {
1142
+ applyOffsetPagination(qb, page2, size) {
1064
1143
  this.draft(qb).narrow({
1065
1144
  limit: size,
1066
- offset: (page - 1) * size
1145
+ offset: (page2 - 1) * size
1067
1146
  });
1068
1147
  }
1069
1148
  /**
@@ -1290,9 +1369,9 @@ var RunFilter = class extends BaseFilter {
1290
1369
  }
1291
1370
  }
1292
1371
  applyLimit(value) {
1293
- const limit = bound(value);
1294
- if (limit !== void 0) this.$query.narrow({
1295
- limit
1372
+ const limit2 = bound(value);
1373
+ if (limit2 !== void 0) this.$query.narrow({
1374
+ limit: limit2
1296
1375
  });
1297
1376
  }
1298
1377
  applyOffset(value) {
@@ -1474,7 +1553,7 @@ var DurableApiControllerBase = class {
1474
1553
  * The persisted schedules (`engine.schedules`), soonest-due first, optionally narrowed by
1475
1554
  * `namespace`/`workflow`/`tag`/`paused`. Empty on a tenant or a store without schedules.
1476
1555
  */
1477
- schedules(namespace, workflow, tag, paused, limit) {
1556
+ schedules(namespace, workflow, tag, paused, limit2) {
1478
1557
  return this.dashboard.listSchedules({
1479
1558
  ...namespace ? {
1480
1559
  namespace
@@ -1488,7 +1567,7 @@ var DurableApiControllerBase = class {
1488
1567
  ...paused === "true" || paused === "false" ? {
1489
1568
  paused: paused === "true"
1490
1569
  } : {},
1491
- limit: Math.min(Number(limit) || 200, 1e3)
1570
+ limit: Math.min(Number(limit2) || 200, 1e3)
1492
1571
  });
1493
1572
  }
1494
1573
  pauseSchedule(id) {
@@ -1538,17 +1617,17 @@ var DurableApiControllerBase = class {
1538
1617
  return body;
1539
1618
  }
1540
1619
  /** One page of a summarized fan's members, in seq order. Versioned like {@link run}. */
1541
- async fanMembers(id, group, offset, limit, failed, ifNoneMatch, response) {
1542
- const page = await this.dashboard.getFanMembers(id, group, {
1620
+ async fanMembers(id, group, offset, limit2, failed, ifNoneMatch, response) {
1621
+ const page2 = await this.dashboard.getFanMembers(id, group, {
1543
1622
  offset: offset === void 0 ? void 0 : Number(offset),
1544
- limit: limit === void 0 ? void 0 : Number(limit),
1623
+ limit: limit2 === void 0 ? void 0 : Number(limit2),
1545
1624
  failedOnly: failed === "true",
1546
1625
  ...ifNoneMatch && {
1547
1626
  ifNoneMatch
1548
1627
  }
1549
1628
  });
1550
- if (!page) throw new NotFoundException(`fan ${group} of run ${id} not found`);
1551
- const { etag, notModified, ...body } = page;
1629
+ if (!page2) throw new NotFoundException(`fan ${group} of run ${id} not found`);
1630
+ const { etag, notModified, ...body } = page2;
1552
1631
  if (etag) {
1553
1632
  if (notModified) throw new NotModifiedException(etag);
1554
1633
  setEtag(response, etag);
@@ -1573,7 +1652,7 @@ var DurableApiControllerBase = class {
1573
1652
  * exactly the set the operator was looking at rather than a wider one.
1574
1653
  */
1575
1654
  bulk(action, draft, compensate) {
1576
- const { limit, offset, ...filter } = draft.query;
1655
+ const { limit: limit2, offset, ...filter } = draft.query;
1577
1656
  return this.dashboard.bulk(action === "cancel" ? "cancel" : "retry", filter, {
1578
1657
  compensate: compensate === "true"
1579
1658
  });
@@ -1932,7 +2011,194 @@ DurableApiController = _ts_decorate6([
1932
2011
  import "reflect-metadata";
1933
2012
  import { ApplyFilterInterceptor as ApplyFilterInterceptor2, FilterRunner as FilterRunner2 } from "@dudousxd/nestjs-filter";
1934
2013
  import { Module } from "@nestjs/common";
1935
- import { ModuleRef, RouterModule } from "@nestjs/core";
2014
+ import { ModuleRef as ModuleRef2, RouterModule } from "@nestjs/core";
2015
+
2016
+ // src/server/console-switcher.ts
2017
+ import { Controller as Controller2, Get as Get2, Header as Header2, Inject as Inject3, Injectable as Injectable5, Logger as Logger2, createParamDecorator } from "@nestjs/common";
2018
+
2019
+ // src/server/tokens.ts
2020
+ import { RunGateway as RunGateway3 } from "@dudousxd/nestjs-durable-core";
2021
+ var DASHBOARD_DEV_TOOLS = Symbol("DASHBOARD_DEV_TOOLS");
2022
+ var DASHBOARD_BASE_PATH = Symbol("DASHBOARD_BASE_PATH");
2023
+ var DASHBOARD_API_PATH = Symbol("DASHBOARD_API_PATH");
2024
+
2025
+ // src/server/console-switcher.ts
2026
+ function _ts_decorate7(decorators, target, key, desc) {
2027
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2028
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2029
+ r = Reflect.decorate(decorators, target, key, desc);
2030
+ } else {
2031
+ for (var i = decorators.length - 1; i >= 0; i--) {
2032
+ if (d = decorators[i]) {
2033
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2034
+ }
2035
+ }
2036
+ }
2037
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2038
+ }
2039
+ __name(_ts_decorate7, "_ts_decorate");
2040
+ function _ts_metadata6(metadataKey, metadataValue) {
2041
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2042
+ return Reflect.metadata(metadataKey, metadataValue);
2043
+ }
2044
+ }
2045
+ __name(_ts_metadata6, "_ts_metadata");
2046
+ function _ts_param4(paramIndex, decorator) {
2047
+ return function(target, key) {
2048
+ decorator(target, key, paramIndex);
2049
+ };
2050
+ }
2051
+ __name(_ts_param4, "_ts_param");
2052
+ var AVIARY_CONSOLES_SLOT = Symbol.for("@dudousxd/aviary:consoles");
2053
+ var ID = /^[a-z][a-z0-9-]*$/;
2054
+ var ICONS = /* @__PURE__ */ new Set([
2055
+ "workflow",
2056
+ "telescope",
2057
+ "media",
2058
+ "payments",
2059
+ "agent",
2060
+ "identity",
2061
+ "generic"
2062
+ ]);
2063
+ var CONSOLE_PROBE_TIMEOUT_MS = 300;
2064
+ function isSameOriginPath(path) {
2065
+ return typeof path === "string" && path.startsWith("/") && !path.startsWith("//");
2066
+ }
2067
+ __name(isSameOriginPath, "isSameOriginPath");
2068
+ function readAviaryConsoles() {
2069
+ const value = globalThis[AVIARY_CONSOLES_SLOT];
2070
+ if (value?.version !== 1 || !Array.isArray(value.entries)) return void 0;
2071
+ return value;
2072
+ }
2073
+ __name(readAviaryConsoles, "readAviaryConsoles");
2074
+ function registerAviaryConsole(entry, logger3) {
2075
+ if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);
2076
+ if (!isSameOriginPath(entry.path)) {
2077
+ throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single "/"`);
2078
+ }
2079
+ const holder = globalThis;
2080
+ if (holder[AVIARY_CONSOLES_SLOT] === void 0) {
2081
+ const fresh = {
2082
+ version: 1,
2083
+ entries: []
2084
+ };
2085
+ holder[AVIARY_CONSOLES_SLOT] = fresh;
2086
+ }
2087
+ const registry = readAviaryConsoles();
2088
+ if (registry === void 0) return void 0;
2089
+ if (registry.entries.some((existing) => existing.id === entry.id)) {
2090
+ logger3?.warn(`console id "${entry.id}" is already registered`);
2091
+ return void 0;
2092
+ }
2093
+ registry.entries.push(entry);
2094
+ return () => {
2095
+ const index = registry.entries.indexOf(entry);
2096
+ if (index !== -1) registry.entries.splice(index, 1);
2097
+ };
2098
+ }
2099
+ __name(registerAviaryConsole, "registerAviaryConsole");
2100
+ function consoleSwitcherConfig(selfId, endpoint) {
2101
+ const registry = readAviaryConsoles();
2102
+ if (!registry?.entries.some((entry) => entry.id === selfId)) return void 0;
2103
+ const others = registry.entries.filter((entry) => entry.id !== selfId).length;
2104
+ return others > 0 ? {
2105
+ endpoint,
2106
+ others
2107
+ } : void 0;
2108
+ }
2109
+ __name(consoleSwitcherConfig, "consoleSwitcherConfig");
2110
+ async function allows(entry, context, ms) {
2111
+ let timer;
2112
+ const timeout = new Promise((resolve2) => {
2113
+ timer = setTimeout(() => resolve2(false), ms);
2114
+ });
2115
+ try {
2116
+ return await Promise.race([
2117
+ Promise.resolve().then(() => entry.authorize(context)).then((decision) => decision?.allowed === true, () => false),
2118
+ timeout
2119
+ ]);
2120
+ } finally {
2121
+ clearTimeout(timer);
2122
+ }
2123
+ }
2124
+ __name(allows, "allows");
2125
+ async function listVisibleConsoles(registry, selfId, context, timeoutMs = CONSOLE_PROBE_TIMEOUT_MS) {
2126
+ const others = (registry?.entries ?? []).filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path)).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
2127
+ const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));
2128
+ return {
2129
+ version: 1,
2130
+ current: selfId,
2131
+ consoles: others.filter((_, index) => verdicts[index]).map((entry) => ({
2132
+ id: entry.id,
2133
+ title: entry.title,
2134
+ icon: ICONS.has(entry.icon) ? entry.icon : "generic",
2135
+ href: entry.path
2136
+ }))
2137
+ };
2138
+ }
2139
+ __name(listVisibleConsoles, "listVisibleConsoles");
2140
+ var DURABLE_CONSOLE_ID = "durable";
2141
+ var logger = new Logger2("DurableDashboardModule");
2142
+ var DurableConsoleRegistration = class {
2143
+ static {
2144
+ __name(this, "DurableConsoleRegistration");
2145
+ }
2146
+ basePath;
2147
+ decide;
2148
+ #unregister;
2149
+ constructor(basePath, decide) {
2150
+ this.basePath = basePath;
2151
+ this.decide = decide;
2152
+ }
2153
+ onModuleInit() {
2154
+ this.#unregister = registerAviaryConsole({
2155
+ id: DURABLE_CONSOLE_ID,
2156
+ title: "Workflows",
2157
+ icon: "workflow",
2158
+ order: 10,
2159
+ path: this.basePath,
2160
+ authorize: /* @__PURE__ */ __name((context) => this.decide(context), "authorize")
2161
+ }, logger);
2162
+ }
2163
+ onModuleDestroy() {
2164
+ this.#unregister?.();
2165
+ this.#unregister = void 0;
2166
+ }
2167
+ };
2168
+ DurableConsoleRegistration = _ts_decorate7([
2169
+ Injectable5(),
2170
+ _ts_param4(0, Inject3(DASHBOARD_BASE_PATH)),
2171
+ _ts_param4(1, Inject3(CONSOLE_AUTHORIZE_DECIDE)),
2172
+ _ts_metadata6("design:type", Function),
2173
+ _ts_metadata6("design:paramtypes", [
2174
+ String,
2175
+ typeof ConsoleDecide === "undefined" ? Object : ConsoleDecide
2176
+ ])
2177
+ ], DurableConsoleRegistration);
2178
+ var CurrentExecutionContext = createParamDecorator((_data, context) => ({
2179
+ context
2180
+ }));
2181
+ var DurableConsolesController = class {
2182
+ static {
2183
+ __name(this, "DurableConsolesController");
2184
+ }
2185
+ consoles(current) {
2186
+ return listVisibleConsoles(readAviaryConsoles(), DURABLE_CONSOLE_ID, current.context);
2187
+ }
2188
+ };
2189
+ _ts_decorate7([
2190
+ Get2("consoles"),
2191
+ Header2("Cache-Control", "private, no-store"),
2192
+ _ts_param4(0, CurrentExecutionContext()),
2193
+ _ts_metadata6("design:type", Function),
2194
+ _ts_metadata6("design:paramtypes", [
2195
+ typeof CurrentContext === "undefined" ? Object : CurrentContext
2196
+ ]),
2197
+ _ts_metadata6("design:returntype", typeof Promise === "undefined" ? Object : Promise)
2198
+ ], DurableConsolesController.prototype, "consoles", null);
2199
+ DurableConsolesController = _ts_decorate7([
2200
+ Controller2()
2201
+ ], DurableConsolesController);
1936
2202
 
1937
2203
  // src/server/controller-decorators.ts
1938
2204
  import "reflect-metadata";
@@ -1960,12 +2226,785 @@ function controllerFor(base, decorators) {
1960
2226
  }
1961
2227
  __name(controllerFor, "controllerFor");
1962
2228
 
2229
+ // src/server/dev-tools.ts
2230
+ import { HttpException as HttpException3, Inject as Inject4, Injectable as Injectable6, Logger as Logger3, NotFoundException as NotFoundException2 } from "@nestjs/common";
2231
+ function _ts_decorate8(decorators, target, key, desc) {
2232
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2233
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2234
+ r = Reflect.decorate(decorators, target, key, desc);
2235
+ } else {
2236
+ for (var i = decorators.length - 1; i >= 0; i--) {
2237
+ if (d = decorators[i]) {
2238
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2239
+ }
2240
+ }
2241
+ }
2242
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2243
+ }
2244
+ __name(_ts_decorate8, "_ts_decorate");
2245
+ function _ts_metadata7(metadataKey, metadataValue) {
2246
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2247
+ return Reflect.metadata(metadataKey, metadataValue);
2248
+ }
2249
+ }
2250
+ __name(_ts_metadata7, "_ts_metadata");
2251
+ function _ts_param5(paramIndex, decorator) {
2252
+ return function(target, key) {
2253
+ decorator(target, key, paramIndex);
2254
+ };
2255
+ }
2256
+ __name(_ts_param5, "_ts_param");
2257
+ var DEV_TOOLS_SLOT = Symbol.for("@dudousxd/aviary:dev-tools");
2258
+ function devToolsSlot() {
2259
+ const holder = globalThis;
2260
+ const existing = holder[DEV_TOOLS_SLOT];
2261
+ if (existing !== void 0) return existing;
2262
+ const libs = [];
2263
+ const slot = {
2264
+ version: 1,
2265
+ libs,
2266
+ register(lib) {
2267
+ if (libs.some((entry) => entry.lib === lib.lib)) {
2268
+ throw new Error(`dev tools: the lib "${lib.lib}" is already registered`);
2269
+ }
2270
+ libs.push(lib);
2271
+ return () => {
2272
+ const index = libs.indexOf(lib);
2273
+ if (index !== -1) libs.splice(index, 1);
2274
+ };
2275
+ }
2276
+ };
2277
+ holder[DEV_TOOLS_SLOT] = slot;
2278
+ return slot;
2279
+ }
2280
+ __name(devToolsSlot, "devToolsSlot");
2281
+ var DEFAULT_LIMIT = 20;
2282
+ var MAX_LIMIT = 100;
2283
+ var MAX_RUN_IDS = 100;
2284
+ var ERROR_SUMMARY_CHARS = 300;
2285
+ var RUN_STATUSES = [
2286
+ "pending",
2287
+ "running",
2288
+ "suspended",
2289
+ "completed",
2290
+ "failed",
2291
+ "cancelling",
2292
+ "cancelled",
2293
+ "dead",
2294
+ "blocked"
2295
+ ];
2296
+ var COUNT_FIELDS = [
2297
+ "status",
2298
+ "workflow",
2299
+ "origin",
2300
+ "namespace",
2301
+ "tag",
2302
+ "attributeKey",
2303
+ "attributeValue"
2304
+ ];
2305
+ var limit = {
2306
+ type: "integer",
2307
+ description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,
2308
+ minimum: 1,
2309
+ maximum: MAX_LIMIT
2310
+ };
2311
+ var cursor = {
2312
+ type: "string",
2313
+ description: "Pass the `next` of the previous page."
2314
+ };
2315
+ var runId = {
2316
+ type: "string",
2317
+ description: "Run id."
2318
+ };
2319
+ var runIds = {
2320
+ type: "array",
2321
+ items: {
2322
+ type: "string"
2323
+ },
2324
+ description: `Several run ids (1 to ${MAX_RUN_IDS}), instead of \`id\`.`
2325
+ };
2326
+ var scheduleKey = {
2327
+ type: "string",
2328
+ description: "Schedule id."
2329
+ };
2330
+ var runFilters = {
2331
+ workflow: {
2332
+ type: "string",
2333
+ description: "Workflow name."
2334
+ },
2335
+ tag: {
2336
+ type: "string",
2337
+ description: "A tag the run carries."
2338
+ },
2339
+ namespace: {
2340
+ type: "string",
2341
+ description: "Worker pool (namespace)."
2342
+ },
2343
+ attr: {
2344
+ type: "array",
2345
+ items: {
2346
+ type: "string"
2347
+ },
2348
+ description: "Search-attribute predicates `key:op:value` (ops: eq, ne, gt, gte, lt, lte, in with `|`), ANDed."
2349
+ }
2350
+ };
2351
+ var str = /* @__PURE__ */ __name((value) => typeof value === "string" && value !== "" ? value : void 0, "str");
2352
+ function limitOf(input) {
2353
+ const value = input.limit;
2354
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? Math.min(value, MAX_LIMIT) : DEFAULT_LIMIT;
2355
+ }
2356
+ __name(limitOf, "limitOf");
2357
+ function offsetOf(input) {
2358
+ const value = str(input.cursor);
2359
+ if (value === void 0) return 0;
2360
+ if (!/^\d+$/.test(value)) {
2361
+ throw new Error(`"cursor" must be the \`next\` of a previous page, got "${value}"`);
2362
+ }
2363
+ return Number(value);
2364
+ }
2365
+ __name(offsetOf, "offsetOf");
2366
+ function required(input, key) {
2367
+ const value = str(input[key]);
2368
+ if (value === void 0) throw new Error(`"${key}" is required`);
2369
+ return value;
2370
+ }
2371
+ __name(required, "required");
2372
+ function page(rows, offset, size) {
2373
+ return {
2374
+ items: rows.slice(0, size),
2375
+ next: rows.length > size ? String(offset + size) : null
2376
+ };
2377
+ }
2378
+ __name(page, "page");
2379
+ function runPredicates(input) {
2380
+ const query = {};
2381
+ const workflow = str(input.workflow);
2382
+ if (workflow !== void 0) query.workflow = workflow;
2383
+ const tag = str(input.tag);
2384
+ if (tag !== void 0) query.tag = tag;
2385
+ const namespace = str(input.namespace);
2386
+ if (namespace !== void 0) query.namespace = namespace;
2387
+ const attr = Array.isArray(input.attr) ? input.attr.filter((entry) => typeof entry === "string" && entry !== "") : [];
2388
+ if (attr.length > 0) {
2389
+ const attributes = parseAttrFilters(attr);
2390
+ if (attributes === void 0 || attributes.length !== attr.length) {
2391
+ throw new Error(`"attr" entries must read \`key:op:value\`, got ${JSON.stringify(attr)}`);
2392
+ }
2393
+ query.attributes = attributes;
2394
+ }
2395
+ return query;
2396
+ }
2397
+ __name(runPredicates, "runPredicates");
2398
+ function statusOf(input) {
2399
+ const status = str(input.status);
2400
+ if (status === void 0) return void 0;
2401
+ if (!RUN_STATUSES.includes(status)) {
2402
+ throw new Error(`"status" must be one of ${RUN_STATUSES.join(", ")}, got "${status}"`);
2403
+ }
2404
+ return status;
2405
+ }
2406
+ __name(statusOf, "statusOf");
2407
+ function listRow(run) {
2408
+ const row = {
2409
+ ...toRunListRow(run)
2410
+ };
2411
+ const message = run.error?.message;
2412
+ if (typeof message === "string" && message !== "") {
2413
+ const line = message.split("\n", 1)[0] ?? "";
2414
+ row.error = line.length > ERROR_SUMMARY_CHARS ? `${line.slice(0, ERROR_SUMMARY_CHARS)}\u2026` : line;
2415
+ }
2416
+ return row;
2417
+ }
2418
+ __name(listRow, "listRow");
2419
+ async function call(what, run) {
2420
+ try {
2421
+ return await run();
2422
+ } catch (error) {
2423
+ if (error instanceof NotFoundException2 || error instanceof Error && error.name === "ScheduleNotFoundError") {
2424
+ throw new Error(`${what()} not found`);
2425
+ }
2426
+ if (error instanceof HttpException3) throw new Error(error.message);
2427
+ throw error;
2428
+ }
2429
+ }
2430
+ __name(call, "call");
2431
+ function found(value, what) {
2432
+ if (value === null || value === void 0) throw new Error(`${what} not found`);
2433
+ return value;
2434
+ }
2435
+ __name(found, "found");
2436
+ function idsOf(input) {
2437
+ const one = str(input.id);
2438
+ const many = Array.isArray(input.ids) ? input.ids.filter((id) => typeof id === "string" && id !== "") : void 0;
2439
+ if (one !== void 0 && many !== void 0) throw new Error('pass "id" or "ids", not both');
2440
+ if (one !== void 0) return {
2441
+ ids: [
2442
+ one
2443
+ ],
2444
+ many: false
2445
+ };
2446
+ if (many === void 0 || many.length === 0) throw new Error('"id" or "ids" is required');
2447
+ if (many.length > MAX_RUN_IDS) {
2448
+ throw new Error(`"ids" lists ${many.length} runs; at most ${MAX_RUN_IDS} per call`);
2449
+ }
2450
+ return {
2451
+ ids: many,
2452
+ many: true
2453
+ };
2454
+ }
2455
+ __name(idsOf, "idsOf");
2456
+ async function each(input, act) {
2457
+ const { ids, many } = idsOf(input);
2458
+ if (!many) return act(ids[0]);
2459
+ const applied = [];
2460
+ for (const [index, id] of ids.entries()) {
2461
+ try {
2462
+ applied.push(await act(id));
2463
+ } catch (error) {
2464
+ return {
2465
+ applied,
2466
+ failed: {
2467
+ id,
2468
+ error: error instanceof Error ? error.message : String(error)
2469
+ },
2470
+ skipped: ids.slice(index + 1)
2471
+ };
2472
+ }
2473
+ }
2474
+ return {
2475
+ applied
2476
+ };
2477
+ }
2478
+ __name(each, "each");
2479
+ function starvedGroups(groups) {
2480
+ return groups.filter((group) => group.depth > 0 && group.liveWorkers.length === 0).sort((a, b) => b.depth - a.depth).map(({ group, depth, kind }) => ({
2481
+ group,
2482
+ depth,
2483
+ ...kind ? {
2484
+ kind
2485
+ } : {}
2486
+ }));
2487
+ }
2488
+ __name(starvedGroups, "starvedGroups");
2489
+ function durableDevTools(service) {
2490
+ const controlPlane = /* @__PURE__ */ __name(() => service.capabilities().controlPlane, "controlPlane");
2491
+ const schedules = /* @__PURE__ */ __name(() => service.capabilities().schedules, "schedules");
2492
+ return [
2493
+ {
2494
+ name: "durable_list_runs",
2495
+ kind: "read",
2496
+ description: "Workflow runs, newest first: id, workflow, status, what a suspended run waits on, times, tags and a one-line error summary (no input or output). Filters: status, workflow, tag, namespace, origin, attr. Returns { items, next }; pass next as cursor.",
2497
+ input: {
2498
+ type: "object",
2499
+ properties: {
2500
+ status: {
2501
+ type: "string",
2502
+ enum: RUN_STATUSES,
2503
+ description: "Run status."
2504
+ },
2505
+ ...runFilters,
2506
+ origin: {
2507
+ type: "string",
2508
+ description: "The package the run came from."
2509
+ },
2510
+ cursor,
2511
+ limit
2512
+ }
2513
+ },
2514
+ async run(input) {
2515
+ const size = limitOf(input);
2516
+ const offset = offsetOf(input);
2517
+ const status = statusOf(input);
2518
+ const origin = str(input.origin);
2519
+ const query = {
2520
+ ...runPredicates(input),
2521
+ ...status !== void 0 ? {
2522
+ status
2523
+ } : {},
2524
+ ...origin !== void 0 ? {
2525
+ origin
2526
+ } : {},
2527
+ offset,
2528
+ limit: size + 1
2529
+ };
2530
+ const runs = await call(() => "runs", () => service.listRuns(query));
2531
+ return page(runs.map(listRow), offset, size);
2532
+ }
2533
+ },
2534
+ {
2535
+ name: "durable_get_run",
2536
+ kind: "read",
2537
+ description: 'One run: its input, output, error, step timeline, children, fans (summarized over 50 members unless full) and lifecycle links. `field` returns that one field only (e.g. "steps"). An error when no run has that id.',
2538
+ input: {
2539
+ type: "object",
2540
+ properties: {
2541
+ id: runId,
2542
+ full: {
2543
+ type: "boolean",
2544
+ description: "Every fan member inline (default false)."
2545
+ },
2546
+ field: {
2547
+ type: "string",
2548
+ description: "Return only this top-level field."
2549
+ }
2550
+ },
2551
+ required: [
2552
+ "id"
2553
+ ]
2554
+ },
2555
+ async run(input) {
2556
+ const id = required(input, "id");
2557
+ const detail = found(await call(() => `run ${id}`, () => service.getRunDetail(id, input.full === true ? void 0 : {
2558
+ summarizeFansOver: 50
2559
+ })), `run ${id}`);
2560
+ const { etag: _etag, notModified: _notModified, ...body } = detail;
2561
+ const field = str(input.field);
2562
+ if (field === void 0) return body;
2563
+ if (!Object.hasOwn(body, field)) {
2564
+ throw new Error(`run ${id} has no field "${field}"; fields: ${Object.keys(body).join(", ")}`);
2565
+ }
2566
+ return {
2567
+ id,
2568
+ [field]: body[field]
2569
+ };
2570
+ }
2571
+ },
2572
+ {
2573
+ name: "durable_list_fan_members",
2574
+ kind: "read",
2575
+ description: "One fan's members (the steps or children of a fan-out group), in order. Filters: failed (only failed members). Returns { items, total, next }; pass next as cursor. An error when the run or the fan does not exist.",
2576
+ input: {
2577
+ type: "object",
2578
+ properties: {
2579
+ id: runId,
2580
+ group: {
2581
+ type: "string",
2582
+ description: "The fan group, as the run detail names it."
2583
+ },
2584
+ failed: {
2585
+ type: "boolean",
2586
+ description: "Only failed members."
2587
+ },
2588
+ cursor,
2589
+ limit
2590
+ },
2591
+ required: [
2592
+ "id",
2593
+ "group"
2594
+ ]
2595
+ },
2596
+ async run(input) {
2597
+ const id = required(input, "id");
2598
+ const group = required(input, "group");
2599
+ const size = limitOf(input);
2600
+ const offset = offsetOf(input);
2601
+ const fan = found(await call(() => `fan ${group} of run ${id}`, () => service.getFanMembers(id, group, {
2602
+ offset,
2603
+ limit: size,
2604
+ failedOnly: input.failed === true
2605
+ })), `fan ${group} of run ${id}`);
2606
+ const end = offset + fan.items.length;
2607
+ return {
2608
+ items: fan.items,
2609
+ total: fan.total,
2610
+ next: fan.items.length > 0 && end < fan.total ? String(end) : null
2611
+ };
2612
+ }
2613
+ },
2614
+ {
2615
+ name: "durable_count_runs_by",
2616
+ kind: "read",
2617
+ description: "How many runs have each value of one field (status, workflow, origin, namespace, tag, attributeKey, or attributeValue with key), most frequent first. Filters: workflow, tag, namespace, attr. Returns { items: [{ value, count }], next }; pass next as cursor. Empty when the store cannot count that field.",
2618
+ input: {
2619
+ type: "object",
2620
+ properties: {
2621
+ field: {
2622
+ type: "string",
2623
+ enum: COUNT_FIELDS,
2624
+ description: "The field to count by."
2625
+ },
2626
+ key: {
2627
+ type: "string",
2628
+ description: "The search attribute, for attributeValue."
2629
+ },
2630
+ ...runFilters,
2631
+ cursor,
2632
+ limit
2633
+ },
2634
+ required: [
2635
+ "field"
2636
+ ]
2637
+ },
2638
+ async run(input) {
2639
+ const field = required(input, "field");
2640
+ if (!COUNT_FIELDS.includes(field)) {
2641
+ throw new Error(`"field" must be one of ${COUNT_FIELDS.join(", ")}, got "${field}"`);
2642
+ }
2643
+ const size = limitOf(input);
2644
+ const offset = offsetOf(input);
2645
+ const predicates = runPredicates(input);
2646
+ if (field === "status" || field === "origin") {
2647
+ const rows2 = await call(() => "runs", () => service.runFacets(predicates));
2648
+ const counts = /* @__PURE__ */ new Map();
2649
+ for (const row of rows2) {
2650
+ const value = field === "status" ? row.status : row.origin;
2651
+ counts.set(value, (counts.get(value) ?? 0) + row.count);
2652
+ }
2653
+ const items = [
2654
+ ...counts
2655
+ ].map(([value, count]) => ({
2656
+ value,
2657
+ count
2658
+ })).sort((a, b) => b.count - a.count || String(a.value).localeCompare(String(b.value)));
2659
+ return page(items.slice(offset), offset, size);
2660
+ }
2661
+ const axis = field === "attributeValue" ? {
2662
+ field,
2663
+ key: required(input, "key")
2664
+ } : {
2665
+ field
2666
+ };
2667
+ const rows = await call(() => "runs", () => service.runValueFacets(axis, predicates, {
2668
+ offset,
2669
+ limit: size + 1
2670
+ }));
2671
+ return page(rows, offset, size);
2672
+ }
2673
+ },
2674
+ {
2675
+ name: "durable_get_health",
2676
+ kind: "read",
2677
+ description: "The fleet at a glance: run counts by status (pending backlog, dead, blocked: a run no compatible worker can take), queues with backlog and no live worker (starved), live worker count, and this deployment's role (control plane or tenant).",
2678
+ input: {
2679
+ type: "object",
2680
+ properties: {}
2681
+ },
2682
+ async run() {
2683
+ const [facets, groups] = await Promise.all([
2684
+ call(() => "runs", () => service.runFacets({})),
2685
+ call(() => "workers", () => service.workerHealth())
2686
+ ]);
2687
+ const runs = {};
2688
+ for (const row of facets) runs[row.status] = (runs[row.status] ?? 0) + row.count;
2689
+ const workers = /* @__PURE__ */ new Set();
2690
+ for (const group of groups) {
2691
+ for (const worker of group.liveWorkers) workers.add(worker.instanceId);
2692
+ }
2693
+ const starved = starvedGroups(groups);
2694
+ return {
2695
+ topology: service.topology(),
2696
+ runs,
2697
+ backlog: runs.pending ?? 0,
2698
+ dead: runs.dead ?? 0,
2699
+ blocked: runs.blocked ?? 0,
2700
+ queues: groups.length,
2701
+ liveWorkers: workers.size,
2702
+ starved,
2703
+ healthy: starved.length === 0 && (runs.blocked ?? 0) === 0
2704
+ };
2705
+ }
2706
+ },
2707
+ {
2708
+ name: "durable_list_workers",
2709
+ kind: "read",
2710
+ description: "Worker groups (queues): group, kind (workflow or step), queue depth and the live workers with their heartbeats. A group with depth > 0 and no live worker is starved. Returns { items, next: null }.",
2711
+ input: {
2712
+ type: "object",
2713
+ properties: {}
2714
+ },
2715
+ async run() {
2716
+ const groups = await call(() => "workers", () => service.workerHealth());
2717
+ return {
2718
+ items: groups,
2719
+ next: null
2720
+ };
2721
+ }
2722
+ },
2723
+ {
2724
+ name: "durable_list_schedules",
2725
+ kind: "read",
2726
+ description: "Persisted schedules, soonest due first: id, workflow, cadence (cron, every or rrule), paused, nextFireAt, lastFireAt, lastRunId, fires, lastError. Filters: workflow, tag, namespace, paused. Returns { items, next }; pass next as cursor.",
2727
+ input: {
2728
+ type: "object",
2729
+ properties: {
2730
+ workflow: {
2731
+ type: "string",
2732
+ description: "Workflow name."
2733
+ },
2734
+ tag: {
2735
+ type: "string",
2736
+ description: "A tag the schedule carries."
2737
+ },
2738
+ namespace: {
2739
+ type: "string",
2740
+ description: "Worker pool (namespace)."
2741
+ },
2742
+ paused: {
2743
+ type: "boolean",
2744
+ description: "Only paused (true) or active (false)."
2745
+ },
2746
+ cursor,
2747
+ limit
2748
+ }
2749
+ },
2750
+ available: schedules,
2751
+ async run(input) {
2752
+ const size = limitOf(input);
2753
+ const offset = offsetOf(input);
2754
+ const workflow = str(input.workflow);
2755
+ const tag = str(input.tag);
2756
+ const namespace = str(input.namespace);
2757
+ const rows = await call(() => "schedules", () => service.listSchedules({
2758
+ ...workflow !== void 0 ? {
2759
+ workflow
2760
+ } : {},
2761
+ ...tag !== void 0 ? {
2762
+ tag
2763
+ } : {},
2764
+ ...namespace !== void 0 ? {
2765
+ namespace
2766
+ } : {},
2767
+ ...typeof input.paused === "boolean" ? {
2768
+ paused: input.paused
2769
+ } : {},
2770
+ offset,
2771
+ limit: size + 1
2772
+ }));
2773
+ return page(rows, offset, size);
2774
+ }
2775
+ },
2776
+ {
2777
+ name: "durable_get_run_event",
2778
+ kind: "read",
2779
+ description: "The latest value a run published for `key` with ctx.setEvent (a live query, no side effect). Returns { id, key, value }; value is null when the run never published that key. An error when no run has that id.",
2780
+ input: {
2781
+ type: "object",
2782
+ properties: {
2783
+ id: runId,
2784
+ key: {
2785
+ type: "string",
2786
+ description: "Event key."
2787
+ }
2788
+ },
2789
+ required: [
2790
+ "id",
2791
+ "key"
2792
+ ]
2793
+ },
2794
+ available: controlPlane,
2795
+ async run(input) {
2796
+ const id = required(input, "id");
2797
+ const key = required(input, "key");
2798
+ if (!await call(() => `run ${id}`, () => service.runExists(id))) {
2799
+ throw new Error(`run ${id} not found`);
2800
+ }
2801
+ const value = await call(() => `run ${id}`, () => service.getEvent(id, key));
2802
+ return {
2803
+ id,
2804
+ key,
2805
+ value: value ?? null
2806
+ };
2807
+ }
2808
+ },
2809
+ {
2810
+ name: "durable_retry_run",
2811
+ kind: "action",
2812
+ description: `Re-runs a failed, dead or cancelled run; completed steps replay from their checkpoints. Returns { runId, status }. With ids (at most ${MAX_RUN_IDS}), runs them in order and stops at the first that fails: { applied, failed: { id, error }, skipped }. An error when no run has that id.`,
2813
+ input: {
2814
+ type: "object",
2815
+ properties: {
2816
+ id: runId,
2817
+ ids: runIds
2818
+ }
2819
+ },
2820
+ async run(input) {
2821
+ return each(input, async (id) => found(await call(() => `run ${id}`, () => service.retry(id)), `run ${id}`));
2822
+ }
2823
+ },
2824
+ {
2825
+ name: "durable_cancel_run",
2826
+ kind: "action",
2827
+ destructive: true,
2828
+ description: `Cancels a run that has not finished (with compensate: true, its completed steps' compensations run first). Returns { runId, status }. With ids (at most ${MAX_RUN_IDS}), cancels them in order and stops at the first that fails: { applied, failed: { id, error }, skipped }. An error when no run has that id.`,
2829
+ input: {
2830
+ type: "object",
2831
+ properties: {
2832
+ id: runId,
2833
+ ids: runIds,
2834
+ compensate: {
2835
+ type: "boolean",
2836
+ description: "Run compensations (default false)."
2837
+ }
2838
+ }
2839
+ },
2840
+ async run(input) {
2841
+ const compensate = input.compensate === true;
2842
+ return each(input, async (id) => found(await call(() => `run ${id}`, () => service.cancel(id, {
2843
+ compensate
2844
+ })), `run ${id}`));
2845
+ }
2846
+ },
2847
+ {
2848
+ name: "durable_continue_run",
2849
+ kind: "action",
2850
+ description: "Resumes a run paused at a ctx.breakpoint. Returns { runId, status }. An error when no run has that id, or when it is not paused at a breakpoint.",
2851
+ input: {
2852
+ type: "object",
2853
+ properties: {
2854
+ id: runId
2855
+ },
2856
+ required: [
2857
+ "id"
2858
+ ]
2859
+ },
2860
+ async run(input) {
2861
+ const id = required(input, "id");
2862
+ const what = /* @__PURE__ */ __name(() => `run ${id}`, "what");
2863
+ const result = await call(what, () => service.continue(id));
2864
+ if (result) return result;
2865
+ if (!await call(what, () => service.runExists(id))) throw new Error(`run ${id} not found`);
2866
+ throw new Error(`run ${id} is not paused at a breakpoint`);
2867
+ }
2868
+ },
2869
+ {
2870
+ name: "durable_signal_run",
2871
+ kind: "action",
2872
+ description: "Sends a signal: delivers payload (JSON text; omitted is null) to the run waiting on token (ctx.waitForSignal or a ctx.webhook token). Returns { delivered: true, runId, status }, or { delivered: false } when no run you can see waits on the token yet: the engine then buffers the payload for the next run that waits on it.",
2873
+ input: {
2874
+ type: "object",
2875
+ properties: {
2876
+ token: {
2877
+ type: "string",
2878
+ description: "The signal token the run waits on."
2879
+ },
2880
+ payload: {
2881
+ type: "string",
2882
+ description: "The payload, as JSON text."
2883
+ }
2884
+ },
2885
+ required: [
2886
+ "token"
2887
+ ]
2888
+ },
2889
+ available: controlPlane,
2890
+ async run(input) {
2891
+ const token = required(input, "token");
2892
+ let payload = null;
2893
+ if (typeof input.payload === "string" && input.payload.trim() !== "") {
2894
+ try {
2895
+ payload = JSON.parse(input.payload);
2896
+ } catch {
2897
+ throw new Error('"payload" must be JSON text (quote a plain string: "\\"ok\\"")');
2898
+ }
2899
+ }
2900
+ const result = await call(() => `token ${token}`, () => service.signal(token, payload));
2901
+ return result ? {
2902
+ delivered: true,
2903
+ runId: result.runId,
2904
+ status: result.status
2905
+ } : {
2906
+ delivered: false
2907
+ };
2908
+ }
2909
+ },
2910
+ {
2911
+ name: "durable_trigger_schedule",
2912
+ kind: "action",
2913
+ description: "Starts one run of a schedule now, outside its cadence (it does not count as a fire). Returns { runId, status }. An error when no schedule has that id.",
2914
+ input: {
2915
+ type: "object",
2916
+ properties: {
2917
+ key: scheduleKey
2918
+ },
2919
+ required: [
2920
+ "key"
2921
+ ]
2922
+ },
2923
+ available: schedules,
2924
+ async run(input) {
2925
+ const key = required(input, "key");
2926
+ return call(() => `schedule ${key}`, () => service.triggerSchedule(key));
2927
+ }
2928
+ },
2929
+ {
2930
+ name: "durable_pause_schedule",
2931
+ kind: "action",
2932
+ description: "Pauses a schedule (paused: true, the default) or resumes it from its next window (paused: false). Returns the schedule. An error when no schedule has that id.",
2933
+ input: {
2934
+ type: "object",
2935
+ properties: {
2936
+ key: scheduleKey,
2937
+ paused: {
2938
+ type: "boolean",
2939
+ description: "false resumes it (default true)."
2940
+ }
2941
+ },
2942
+ required: [
2943
+ "key"
2944
+ ]
2945
+ },
2946
+ available: schedules,
2947
+ async run(input) {
2948
+ const key = required(input, "key");
2949
+ return call(() => `schedule ${key}`, () => input.paused === false ? service.resumeSchedule(key) : service.pauseSchedule(key));
2950
+ }
2951
+ }
2952
+ ];
2953
+ }
2954
+ __name(durableDevTools, "durableDevTools");
2955
+ var logger2 = new Logger3("DurableDashboardModule");
2956
+ var DurableDevToolsRegistration = class {
2957
+ static {
2958
+ __name(this, "DurableDevToolsRegistration");
2959
+ }
2960
+ enabled;
2961
+ service;
2962
+ decide;
2963
+ #unregister;
2964
+ constructor(enabled, service, decide) {
2965
+ this.enabled = enabled;
2966
+ this.service = service;
2967
+ this.decide = decide;
2968
+ }
2969
+ onModuleInit() {
2970
+ if (!this.enabled) return;
2971
+ const slot = devToolsSlot();
2972
+ if (slot.version !== 1 || typeof slot.register !== "function") return;
2973
+ try {
2974
+ this.#unregister = slot.register({
2975
+ lib: "durable",
2976
+ title: "Durable workflows",
2977
+ authorize: /* @__PURE__ */ __name((context) => this.decide(context), "authorize"),
2978
+ tools: durableDevTools(this.service)
2979
+ });
2980
+ } catch (error) {
2981
+ logger2.warn(`durable dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
2982
+ }
2983
+ }
2984
+ onModuleDestroy() {
2985
+ this.#unregister?.();
2986
+ this.#unregister = void 0;
2987
+ }
2988
+ };
2989
+ DurableDevToolsRegistration = _ts_decorate8([
2990
+ Injectable6(),
2991
+ _ts_param5(0, Inject4(DASHBOARD_DEV_TOOLS)),
2992
+ _ts_param5(1, Inject4(DashboardService)),
2993
+ _ts_param5(2, Inject4(CONSOLE_AUTHORIZE_DECIDE)),
2994
+ _ts_metadata7("design:type", Function),
2995
+ _ts_metadata7("design:paramtypes", [
2996
+ Boolean,
2997
+ typeof DashboardService === "undefined" ? Object : DashboardService,
2998
+ typeof ConsoleDecide === "undefined" ? Object : ConsoleDecide
2999
+ ])
3000
+ ], DurableDevToolsRegistration);
3001
+
1963
3002
  // src/server/durable-ui.controller.ts
1964
3003
  import { existsSync, readFileSync } from "node:fs";
1965
3004
  import { basename, extname, join, resolve, sep } from "node:path";
1966
3005
  import { fileURLToPath } from "node:url";
1967
- import { Controller as Controller2, Get as Get2, Header as Header2, Inject as Inject3, NotFoundException as NotFoundException2, Param as Param2, StreamableFile } from "@nestjs/common";
1968
- function _ts_decorate7(decorators, target, key, desc) {
3006
+ import { Controller as Controller3, Get as Get3, Header as Header3, Inject as Inject5, NotFoundException as NotFoundException3, Param as Param2, StreamableFile } from "@nestjs/common";
3007
+ function _ts_decorate9(decorators, target, key, desc) {
1969
3008
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1970
3009
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1971
3010
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1978,21 +3017,20 @@ function _ts_decorate7(decorators, target, key, desc) {
1978
3017
  }
1979
3018
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1980
3019
  }
1981
- __name(_ts_decorate7, "_ts_decorate");
1982
- function _ts_metadata6(metadataKey, metadataValue) {
3020
+ __name(_ts_decorate9, "_ts_decorate");
3021
+ function _ts_metadata8(metadataKey, metadataValue) {
1983
3022
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1984
3023
  return Reflect.metadata(metadataKey, metadataValue);
1985
3024
  }
1986
3025
  }
1987
- __name(_ts_metadata6, "_ts_metadata");
1988
- function _ts_param4(paramIndex, decorator) {
3026
+ __name(_ts_metadata8, "_ts_metadata");
3027
+ function _ts_param6(paramIndex, decorator) {
1989
3028
  return function(target, key) {
1990
3029
  decorator(target, key, paramIndex);
1991
3030
  };
1992
3031
  }
1993
- __name(_ts_param4, "_ts_param");
1994
- var DASHBOARD_BASE_PATH = Symbol("DASHBOARD_BASE_PATH");
1995
- var DASHBOARD_API_PATH = Symbol("DASHBOARD_API_PATH");
3032
+ __name(_ts_param6, "_ts_param");
3033
+ var DURABLE_DASHBOARD_CONFIG_ID = "durable-dashboard-config";
1996
3034
  var BUILD_BASE = "/durable";
1997
3035
  function spaDir() {
1998
3036
  return fileURLToPath(new URL("../spa", import.meta.url));
@@ -2023,19 +3061,33 @@ var DurableUiControllerBase = class {
2023
3061
  index() {
2024
3062
  const indexPath = join(this.dir, "index.html");
2025
3063
  if (!existsSync(indexPath)) {
2026
- throw new NotFoundException2("Dashboard is not built. Run the package build.");
3064
+ throw new NotFoundException3("Dashboard is not built. Run the package build.");
2027
3065
  }
2028
3066
  const html = readFileSync(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
2029
- const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>`;
3067
+ const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>${this.configBlock()}`;
2030
3068
  return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
2031
3069
  }
3070
+ /**
3071
+ * The page's JSON config block (`durable-dashboard-config`, CSP-safe: data, not script). It
3072
+ * carries `switcher` only when another Aviary console is registered.
3073
+ */
3074
+ configBlock() {
3075
+ const switcher = consoleSwitcherConfig(DURABLE_CONSOLE_ID, `${this.apiBasePath}/consoles`);
3076
+ const json = JSON.stringify({
3077
+ apiBase: this.apiBasePath,
3078
+ ...switcher !== void 0 ? {
3079
+ switcher
3080
+ } : {}
3081
+ }).replaceAll("<", "\\u003c");
3082
+ return `<script type="application/json" id="${DURABLE_DASHBOARD_CONFIG_ID}">${json}</script>`;
3083
+ }
2032
3084
  asset(file) {
2033
3085
  const safe = basename(file);
2034
- if (safe !== file) throw new NotFoundException2();
3086
+ if (safe !== file) throw new NotFoundException3();
2035
3087
  const root = resolve(this.dir, "assets");
2036
3088
  const assetPath = resolve(root, safe);
2037
3089
  if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {
2038
- throw new NotFoundException2();
3090
+ throw new NotFoundException3();
2039
3091
  }
2040
3092
  const type = CONTENT_TYPES[extname(safe)] ?? "application/octet-stream";
2041
3093
  return new StreamableFile(readFileSync(assetPath), {
@@ -2043,31 +3095,31 @@ var DurableUiControllerBase = class {
2043
3095
  });
2044
3096
  }
2045
3097
  };
2046
- _ts_decorate7([
2047
- Get2(),
2048
- Header2("Content-Type", "text/html; charset=utf-8"),
2049
- Header2("Cache-Control", "no-store, must-revalidate"),
2050
- _ts_metadata6("design:type", Function),
2051
- _ts_metadata6("design:paramtypes", []),
2052
- _ts_metadata6("design:returntype", String)
3098
+ _ts_decorate9([
3099
+ Get3(),
3100
+ Header3("Content-Type", "text/html; charset=utf-8"),
3101
+ Header3("Cache-Control", "no-store, must-revalidate"),
3102
+ _ts_metadata8("design:type", Function),
3103
+ _ts_metadata8("design:paramtypes", []),
3104
+ _ts_metadata8("design:returntype", String)
2053
3105
  ], DurableUiControllerBase.prototype, "index", null);
2054
- _ts_decorate7([
2055
- Get2("assets/:file"),
2056
- Header2("Cache-Control", "public, max-age=31536000, immutable"),
2057
- _ts_param4(0, Param2("file")),
2058
- _ts_metadata6("design:type", Function),
2059
- _ts_metadata6("design:paramtypes", [
3106
+ _ts_decorate9([
3107
+ Get3("assets/:file"),
3108
+ Header3("Cache-Control", "public, max-age=31536000, immutable"),
3109
+ _ts_param6(0, Param2("file")),
3110
+ _ts_metadata8("design:type", Function),
3111
+ _ts_metadata8("design:paramtypes", [
2060
3112
  String
2061
3113
  ]),
2062
- _ts_metadata6("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
3114
+ _ts_metadata8("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
2063
3115
  ], DurableUiControllerBase.prototype, "asset", null);
2064
- DurableUiControllerBase = _ts_decorate7([
2065
- Controller2(),
3116
+ DurableUiControllerBase = _ts_decorate9([
3117
+ Controller3(),
2066
3118
  ConsolePage(),
2067
- _ts_param4(0, Inject3(DASHBOARD_BASE_PATH)),
2068
- _ts_param4(1, Inject3(DASHBOARD_API_PATH)),
2069
- _ts_metadata6("design:type", Function),
2070
- _ts_metadata6("design:paramtypes", [
3119
+ _ts_param6(0, Inject5(DASHBOARD_BASE_PATH)),
3120
+ _ts_param6(1, Inject5(DASHBOARD_API_PATH)),
3121
+ _ts_metadata8("design:type", Function),
3122
+ _ts_metadata8("design:paramtypes", [
2071
3123
  String,
2072
3124
  String
2073
3125
  ])
@@ -2077,12 +3129,12 @@ var DurableUiController = class extends DurableUiControllerBase {
2077
3129
  __name(this, "DurableUiController");
2078
3130
  }
2079
3131
  };
2080
- DurableUiController = _ts_decorate7([
2081
- Controller2()
3132
+ DurableUiController = _ts_decorate9([
3133
+ Controller3()
2082
3134
  ], DurableUiController);
2083
3135
 
2084
3136
  // src/server/durable-dashboard.module.ts
2085
- function _ts_decorate8(decorators, target, key, desc) {
3137
+ function _ts_decorate10(decorators, target, key, desc) {
2086
3138
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2087
3139
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2088
3140
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2095,9 +3147,10 @@ function _ts_decorate8(decorators, target, key, desc) {
2095
3147
  }
2096
3148
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2097
3149
  }
2098
- __name(_ts_decorate8, "_ts_decorate");
3150
+ __name(_ts_decorate10, "_ts_decorate");
2099
3151
  markOpenToAuthorization(DurableApiController);
2100
3152
  markOpenToAuthorization(DurableUiController);
3153
+ markOpenToAuthorization(DurableConsolesController);
2101
3154
  function assertNothingToDecorate(options) {
2102
3155
  if (options.decorators === void 0) return;
2103
3156
  throw new Error([
@@ -2131,10 +3184,16 @@ var DurableApiModule = class _DurableApiModule {
2131
3184
  ...options.imports ?? []
2132
3185
  ],
2133
3186
  controllers: routes ? [
2134
- consoleController(DurableApiController, options.decorators)
3187
+ consoleController(DurableApiController, options.decorators),
3188
+ consoleController(DurableConsolesController, options.decorators)
2135
3189
  ] : [],
2136
3190
  providers: [
2137
3191
  DashboardService,
3192
+ {
3193
+ provide: DASHBOARD_DEV_TOOLS,
3194
+ useValue: options.devTools ?? routes
3195
+ },
3196
+ DurableDevToolsRegistration,
2138
3197
  // Resolved at boot: a gate name without nestjs-authz, or a policy without
2139
3198
  // @nestjs/authorization, fails here rather than on the first request.
2140
3199
  {
@@ -2142,7 +3201,7 @@ var DurableApiModule = class _DurableApiModule {
2142
3201
  // Also with `routes: false`: `@UseConsoleAuthorize()` still applies the production 403.
2143
3202
  useFactory: /* @__PURE__ */ __name((moduleRef) => resolveConsoleAuthorize(options.authorize, moduleRef), "useFactory"),
2144
3203
  inject: [
2145
- ModuleRef
3204
+ ModuleRef2
2146
3205
  ]
2147
3206
  },
2148
3207
  // `@dudousxd/nestjs-filter` is wired PRIVATELY here rather than through
@@ -2166,7 +3225,7 @@ var DurableApiModule = class _DurableApiModule {
2166
3225
  validation: "off"
2167
3226
  }, null), "useFactory"),
2168
3227
  inject: [
2169
- ModuleRef
3228
+ ModuleRef2
2170
3229
  ]
2171
3230
  },
2172
3231
  ApplyFilterInterceptor2,
@@ -2192,7 +3251,7 @@ var DurableApiModule = class _DurableApiModule {
2192
3251
  };
2193
3252
  }
2194
3253
  };
2195
- DurableApiModule = _ts_decorate8([
3254
+ DurableApiModule = _ts_decorate10([
2196
3255
  Module({})
2197
3256
  ], DurableApiModule);
2198
3257
  var DurableDashboardModule = class _DurableDashboardModule {
@@ -2221,6 +3280,9 @@ var DurableDashboardModule = class _DurableDashboardModule {
2221
3280
  ...options.authorize !== void 0 ? {
2222
3281
  authorize: options.authorize
2223
3282
  } : {},
3283
+ ...options.devTools !== void 0 ? {
3284
+ devTools: options.devTools
3285
+ } : {},
2224
3286
  routes: false
2225
3287
  });
2226
3288
  return {
@@ -2257,6 +3319,9 @@ var DurableDashboardModule = class _DurableDashboardModule {
2257
3319
  } : {},
2258
3320
  ...options.authorize !== void 0 ? {
2259
3321
  authorize: options.authorize
3322
+ } : {},
3323
+ ...options.devTools !== void 0 ? {
3324
+ devTools: options.devTools
2260
3325
  } : {}
2261
3326
  }),
2262
3327
  RouterModule.register([
@@ -2273,7 +3338,11 @@ var DurableDashboardModule = class _DurableDashboardModule {
2273
3338
  controllers: [
2274
3339
  consoleController(DurableUiController, options.decorators)
2275
3340
  ],
2276
- providers: pathProviders,
3341
+ // The console switcher lists this console only where it serves its own page.
3342
+ providers: [
3343
+ ...pathProviders,
3344
+ DurableConsoleRegistration
3345
+ ],
2277
3346
  // Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).
2278
3347
  exports: [
2279
3348
  DurableApiModule
@@ -2281,14 +3350,16 @@ var DurableDashboardModule = class _DurableDashboardModule {
2281
3350
  };
2282
3351
  }
2283
3352
  };
2284
- DurableDashboardModule = _ts_decorate8([
3353
+ DurableDashboardModule = _ts_decorate10([
2285
3354
  Module({})
2286
3355
  ], DurableDashboardModule);
2287
3356
  export {
3357
+ AVIARY_CONSOLES_SLOT,
2288
3358
  CONSOLE_AUTHORIZE_DECIDE,
2289
3359
  CONSOLE_AUTHORIZE_MISSING_WARNING,
2290
3360
  CONSOLE_GATE,
2291
3361
  CONSOLE_PAGE_METADATA,
3362
+ CONSOLE_PROBE_TIMEOUT_MS,
2292
3363
  CONSOLE_PUBLIC_METADATA,
2293
3364
  ConsoleAccessDenied,
2294
3365
  ConsoleAuthorizeFilter,
@@ -2297,16 +3368,27 @@ export {
2297
3368
  ConsolePublic,
2298
3369
  DASHBOARD_API_PATH,
2299
3370
  DASHBOARD_BASE_PATH,
3371
+ DEV_TOOLS_SLOT,
3372
+ DURABLE_CONSOLE_ID,
3373
+ DURABLE_DASHBOARD_CONFIG_ID,
2300
3374
  DashboardService,
2301
3375
  DurableApiController,
2302
3376
  DurableApiControllerBase,
2303
3377
  DurableApiModule,
3378
+ DurableConsoleRegistration,
3379
+ DurableConsolesController,
2304
3380
  DurableDashboardModule,
3381
+ DurableDevToolsRegistration,
2305
3382
  DurableUiController,
2306
3383
  DurableUiControllerBase,
3384
+ MAX_RUN_IDS,
2307
3385
  UseConsoleAuthorize,
3386
+ anyAuthorize,
2308
3387
  authorizeByToken,
3388
+ durableDevTools,
3389
+ listVisibleConsoles,
2309
3390
  normalizeConsoleResult,
3391
+ registerAviaryConsole,
2310
3392
  resolveConsoleAuthorize
2311
3393
  };
2312
3394
  //# sourceMappingURL=index.js.map