@axlsdk/studio 0.26.0 → 0.27.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.
@@ -33,6 +33,12 @@ function redactValue(value, redact) {
33
33
  }
34
34
  function redactExecutionInfo(info, redact) {
35
35
  if (!redact) return info;
36
+ return {
37
+ ...redactExecutionFields(info),
38
+ events: info.events.map((e) => redactStreamEvent(e, true))
39
+ };
40
+ }
41
+ function redactExecutionFields(info) {
36
42
  return {
37
43
  ...info,
38
44
  ...info.result !== void 0 ? { result: REDACTED } : {},
@@ -44,13 +50,13 @@ function redactExecutionInfo(info, redact) {
44
50
  // scrubbed. Use a `{ redacted: true }` marker to keep the field
45
51
  // queryable/serializable rather than dropping it (mirrors
46
52
  // `redactPendingDecision` on `decision.metadata`).
47
- ...info.metadata !== void 0 ? { metadata: { redacted: true } } : {},
48
- events: info.events.map((e) => redactStreamEvent(e, true))
53
+ ...info.metadata !== void 0 ? { metadata: { redacted: true } } : {}
49
54
  };
50
55
  }
51
- function redactExecutionList(infos, redact) {
52
- if (!redact) return infos;
53
- return infos.map((info) => redactExecutionInfo(info, redact));
56
+ function summarizeExecution(info, redact) {
57
+ const { events, ...rest } = info;
58
+ const summary = { ...rest, eventCount: events.length };
59
+ return redact ? redactExecutionFields(summary) : summary;
54
60
  }
55
61
  function redactMemoryValue(value, redact) {
56
62
  if (!redact) return value;
@@ -661,6 +667,29 @@ function createWsHandlers(connMgr) {
661
667
  };
662
668
  }
663
669
 
670
+ // src/server/aggregates/coalesced-runner.ts
671
+ var CoalescedRunner = class {
672
+ constructor(task) {
673
+ this.task = task;
674
+ }
675
+ current;
676
+ trailing;
677
+ run() {
678
+ if (!this.current) {
679
+ const started = this.task().finally(() => {
680
+ this.current = void 0;
681
+ });
682
+ this.current = started;
683
+ return started;
684
+ }
685
+ this.trailing ??= this.current.catch(() => void 0).then(() => {
686
+ this.trailing = void 0;
687
+ return this.run();
688
+ });
689
+ return this.trailing;
690
+ }
691
+ };
692
+
664
693
  // src/server/aggregates/aggregate-snapshots.ts
665
694
  var WINDOW_MS = {
666
695
  "24h": 24 * 60 * 60 * 1e3,
@@ -755,10 +784,19 @@ var TraceAggregator = class {
755
784
  REBUILD_INTERVAL_MS
756
785
  );
757
786
  }
758
- async rebuild() {
759
- const executions = await this.options.runtime.getExecutions();
787
+ /**
788
+ * Rebuild from the store. Requests that arrive during a rebuild (a burst of
789
+ * deletes, the timer) coalesce into one trailing rebuild.
790
+ */
791
+ rebuild() {
792
+ return this.rebuilds.run();
793
+ }
794
+ rebuilds = new CoalescedRunner(() => this.rebuildFromStore());
795
+ async rebuildFromStore() {
760
796
  const cap = this.options.executionCap ?? 2e3;
761
- const capped = executions.slice(0, cap);
797
+ const capped = await this.options.runtime.getExecutions({
798
+ limit: cap
799
+ });
762
800
  const now = Date.now();
763
801
  const fresh = new Map(
764
802
  this.options.windows.map((w) => [w, this.options.emptyState()])
@@ -830,11 +868,20 @@ var ExecutionAggregator = class {
830
868
  REBUILD_INTERVAL_MS
831
869
  );
832
870
  }
833
- async rebuild() {
871
+ /**
872
+ * Rebuild from the store. Requests that arrive during a rebuild (a burst of
873
+ * deletes, the timer) coalesce into one trailing rebuild.
874
+ */
875
+ rebuild() {
876
+ return this.rebuilds.run();
877
+ }
878
+ rebuilds = new CoalescedRunner(() => this.rebuildFromStore());
879
+ async rebuildFromStore() {
834
880
  this.generation++;
835
- const executions = await this.options.runtime.getExecutions();
836
881
  const cap = this.options.executionCap ?? 2e3;
837
- const capped = executions.slice(0, cap);
882
+ const capped = await this.options.runtime.getExecutions({
883
+ limit: cap
884
+ });
838
885
  const now = Date.now();
839
886
  const fresh = new Map(
840
887
  this.options.windows.map((w) => [w, this.options.emptyState()])
@@ -1451,16 +1498,66 @@ function createWorkflowRoutes(connMgr) {
1451
1498
 
1452
1499
  // src/server/routes/executions.ts
1453
1500
  import { Hono as Hono3 } from "hono";
1501
+
1502
+ // src/server/execution-cursor.ts
1503
+ function encodeExecutionCursor(cursor) {
1504
+ return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
1505
+ }
1506
+ function decodeExecutionCursor(value) {
1507
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) return void 0;
1508
+ let parsed;
1509
+ try {
1510
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
1511
+ } catch {
1512
+ return void 0;
1513
+ }
1514
+ if (typeof parsed !== "object" || parsed === null) return void 0;
1515
+ const { startedAt, executionId } = parsed;
1516
+ if (typeof startedAt !== "number" || !Number.isFinite(startedAt)) return void 0;
1517
+ if (typeof executionId !== "string" || executionId === "") return void 0;
1518
+ return { startedAt, executionId };
1519
+ }
1520
+
1521
+ // src/server/routes/executions.ts
1522
+ var DEFAULT_PAGE_LIMIT = 100;
1523
+ var MAX_PAGE_LIMIT = 500;
1454
1524
  function createExecutionRoutes(connMgr) {
1455
1525
  const app5 = new Hono3();
1456
1526
  app5.get("/executions", async (c) => {
1457
1527
  const runtime = c.get("runtime");
1458
- const executions = await runtime.getExecutions();
1528
+ const limitParam = c.req.query("limit");
1529
+ let limit = DEFAULT_PAGE_LIMIT;
1530
+ if (limitParam !== void 0) {
1531
+ limit = Number(limitParam);
1532
+ if (limitParam.trim() === "" || !Number.isInteger(limit) || limit < 1) {
1533
+ return invalidParam("limit", `\`limit\` must be a positive integer (got "${limitParam}")`);
1534
+ }
1535
+ if (limit > MAX_PAGE_LIMIT) {
1536
+ return invalidParam("limit", `\`limit\` must be at most ${MAX_PAGE_LIMIT} (got ${limit})`);
1537
+ }
1538
+ }
1539
+ const cursorParam = c.req.query("cursor");
1540
+ let cursor;
1541
+ if (cursorParam !== void 0) {
1542
+ cursor = decodeExecutionCursor(cursorParam);
1543
+ if (!cursor) return invalidParam("cursor", "`cursor` is not a valid cursor");
1544
+ }
1545
+ const rows = await runtime.getExecutions({ limit: limit + 1, cursor });
1546
+ const page = rows.slice(0, limit);
1547
+ const last = page.at(-1);
1548
+ const redact = runtime.isRedactEnabled();
1459
1549
  return c.json({
1460
1550
  ok: true,
1461
- data: redactExecutionList(executions, runtime.isRedactEnabled())
1551
+ data: page.map((info) => summarizeExecution(info, redact)),
1552
+ nextCursor: rows.length > limit && last ? encodeExecutionCursor({ startedAt: last.startedAt, executionId: last.executionId }) : null
1462
1553
  });
1463
1554
  });
1555
+ function invalidParam(param, message) {
1556
+ return Response.json(
1557
+ { ok: false, error: { code: "INVALID_PARAM", message, param } },
1558
+ { status: 400 }
1559
+ );
1560
+ }
1464
1561
  app5.get("/executions/:id", async (c) => {
1465
1562
  const runtime = c.get("runtime");
1466
1563
  const id = c.req.param("id");
@@ -17077,4 +17174,4 @@ export {
17077
17174
  EvalAggregator,
17078
17175
  createServer
17079
17176
  };
17080
- //# sourceMappingURL=chunk-HAKTKSZS.js.map
17177
+ //# sourceMappingURL=chunk-MKL6RKQ4.js.map