deepline 0.3.258 → 0.3.259

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 (67) hide show
  1. package/dist/bundling-sources/sdk/src/api-error.ts +45 -0
  2. package/dist/bundling-sources/sdk/src/client.ts +98 -459
  3. package/dist/bundling-sources/sdk/src/http.ts +1 -34
  4. package/dist/bundling-sources/sdk/src/index.ts +8 -0
  5. package/dist/bundling-sources/sdk/src/plays/csv-data-input.ts +139 -0
  6. package/dist/bundling-sources/sdk/src/plays/run-request.ts +855 -0
  7. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  8. package/dist/bundling-sources/sdk/src/runs/overview.ts +45 -2
  9. package/dist/bundling-sources/sdk/src/types.ts +5 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +8 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +42 -10
  12. package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +3 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +23 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +3 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/play-admission-outcome.ts +23 -5
  16. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +1 -1
  17. package/dist/bundling-sources/shared_libs/play-runtime/run-execution-state.ts +845 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +106 -6
  19. package/dist/bundling-sources/shared_libs/play-runtime/run-observation.ts +440 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +35 -2
  21. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +66 -7
  22. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +0 -29
  23. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-transition.ts +47 -2
  24. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +6 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +475 -36
  26. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +78 -11
  27. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +550 -42
  28. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +181 -8
  29. package/dist/bundling-sources/shared_libs/play-runtime/step-lifecycle-tracker.ts +0 -18
  30. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +80 -7
  31. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +37 -2
  32. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +148 -18
  33. package/dist/bundling-sources/shared_libs/plays/cascade-falloff.ts +86 -0
  34. package/dist/bundling-sources/shared_libs/plays/docflow-unreached-reason.ts +611 -0
  35. package/dist/bundling-sources/shared_libs/plays/docflow.ts +21 -0
  36. package/dist/bundling-sources/shared_libs/plays/play-exports.ts +28 -5
  37. package/dist/bundling-sources/shared_libs/plays/settings-contract.ts +92 -10
  38. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +5 -0
  39. package/dist/bundling-sources/shared_libs/plays/trigger-config-contract.ts +222 -0
  40. package/dist/bundling-sources/shared_libs/plays/trigger-config-route.ts +15 -0
  41. package/dist/cli/index.js +4957 -771
  42. package/dist/cli/index.mjs +4957 -771
  43. package/dist/client-B5XbKrrs.d.mts +6564 -0
  44. package/dist/client-C9Y49yya.d.ts +6564 -0
  45. package/dist/{compiler-manifest-C4mdRJOP.d.mts → compiler-manifest-CUuh8C3b.d.mts} +14 -1
  46. package/dist/{compiler-manifest-C4mdRJOP.d.ts → compiler-manifest-CUuh8C3b.d.ts} +14 -1
  47. package/dist/index.d.mts +7 -6275
  48. package/dist/index.d.ts +7 -6275
  49. package/dist/index.js +360 -326
  50. package/dist/index.mjs +360 -326
  51. package/dist/install-integrity.json +21 -2
  52. package/dist/plays/bundle-play-file.d.mts +2 -2
  53. package/dist/plays/bundle-play-file.d.ts +2 -2
  54. package/dist/plays/bundle-play-file.mjs +117 -18
  55. package/dist/plays/csv-data-input.d.mts +37 -0
  56. package/dist/plays/csv-data-input.d.ts +37 -0
  57. package/dist/plays/csv-data-input.js +3135 -0
  58. package/dist/plays/csv-data-input.mjs +3108 -0
  59. package/dist/plays/run-request.d.mts +6 -0
  60. package/dist/plays/run-request.d.ts +6 -0
  61. package/dist/plays/run-request.js +664 -0
  62. package/dist/plays/run-request.mjs +614 -0
  63. package/dist/release.d.mts +1 -1
  64. package/dist/release.d.ts +1 -1
  65. package/dist/release.js +1 -1
  66. package/dist/release.mjs +1 -1
  67. package/package.json +12 -1
@@ -1444,34 +1444,116 @@ function sourceLoaderForPath(path: string): Loader {
1444
1444
  return 'ts';
1445
1445
  }
1446
1446
 
1447
- function docflowContextIdentifierFromCall(call: AstNode): string | null {
1447
+ function isFunctionNode(node: AstNode | null | undefined): node is AstNode {
1448
+ return (
1449
+ !!node &&
1450
+ (node.type === 'ArrowFunctionExpression' ||
1451
+ node.type === 'FunctionExpression')
1452
+ );
1453
+ }
1454
+
1455
+ /**
1456
+ * The run function a definePlay call declares inline: the positional callback
1457
+ * of `definePlay(name, run, options?)`, or the `run` property (arrow, function
1458
+ * expression, or method shorthand) of the object form `definePlay({ id, run })`.
1459
+ */
1460
+ function definePlayRunFunction(call: AstNode): AstNode | null {
1448
1461
  if (!isDefinePlayCallExpression(call)) return null;
1449
1462
  const args = astArray(call.arguments);
1450
- const callback = args.find(
1451
- (argument) =>
1452
- argument.type === 'ArrowFunctionExpression' ||
1453
- argument.type === 'FunctionExpression',
1454
- );
1455
- if (!callback) return null;
1456
- return getIdentifierName(astArray(callback.params)[0]);
1463
+ const positional = args.find((argument) => isFunctionNode(argument));
1464
+ if (positional) return positional;
1465
+ const config = unwrapStaticExpression(args[0] ?? null);
1466
+ if (!config || config.type !== 'ObjectExpression') return null;
1467
+ let run: AstNode | null = null;
1468
+ for (const property of astArray(config.properties)) {
1469
+ if (property.type !== 'Property') continue;
1470
+ if (propertyNameFromKey(property) !== 'run') continue;
1471
+ const value = unwrapStaticExpression(
1472
+ isAstNode(property.value) ? property.value : null,
1473
+ );
1474
+ run = isFunctionNode(value) ? value : null;
1475
+ }
1476
+ return run;
1477
+ }
1478
+
1479
+ /** Context parameter name injected into a run function that declares none. */
1480
+ const DOCFLOW_INJECTED_CONTEXT_NAME = '__deeplineDocflowContext';
1481
+
1482
+ type DocflowContextResolution =
1483
+ /** The run function's first parameter is a plain identifier. */
1484
+ | { kind: 'named'; name: string }
1485
+ /** The run function declares no parameters; one is injected at `at`. */
1486
+ | { kind: 'inject'; name: string; at: number }
1487
+ /** The run function exists but its context cannot be addressed. */
1488
+ | { kind: 'unsupported'; reason: string };
1489
+
1490
+ function docflowContextFromCall(
1491
+ call: AstNode,
1492
+ sourceCode: string,
1493
+ ): DocflowContextResolution | null {
1494
+ if (!isDefinePlayCallExpression(call)) return null;
1495
+ const run = definePlayRunFunction(call);
1496
+ if (!run) {
1497
+ return {
1498
+ kind: 'unsupported',
1499
+ reason:
1500
+ 'its definePlay call does not declare the run function inline (pass `definePlay(name, async (ctx, input) => …)` or `definePlay({ id, run: async (ctx, input) => … })`)',
1501
+ };
1502
+ }
1503
+ const params = astArray(run.params);
1504
+ const first = params[0];
1505
+ if (first) {
1506
+ const name = getIdentifierName(first);
1507
+ return name
1508
+ ? { kind: 'named', name }
1509
+ : {
1510
+ kind: 'unsupported',
1511
+ reason: `its run function's first parameter is not a plain identifier (found ${first.type}; declare the context as \`ctx\` and destructure inside the body)`,
1512
+ };
1513
+ }
1514
+ const runBounds = astNodeBounds(run);
1515
+ const body = isAstNode(run.body) ? astNodeBounds(run.body) : null;
1516
+ if (!runBounds || !body) {
1517
+ return {
1518
+ kind: 'unsupported',
1519
+ reason: 'its run function has no source span',
1520
+ };
1521
+ }
1522
+ const signature = sourceCode.slice(runBounds.start, body.start);
1523
+ const emptyParams = /\(\s*\)/.exec(signature);
1524
+ if (!emptyParams) {
1525
+ return {
1526
+ kind: 'unsupported',
1527
+ reason: 'its parameterless run function has no `()` parameter list',
1528
+ };
1529
+ }
1530
+ return {
1531
+ kind: 'inject',
1532
+ name: DOCFLOW_INJECTED_CONTEXT_NAME,
1533
+ at: runBounds.start + emptyParams.index + 1,
1534
+ };
1457
1535
  }
1458
1536
 
1459
1537
  /**
1460
1538
  * Finds the context parameter for the definePlay call that owns one binding.
1461
1539
  * A file may export several plays with different parameter names, so a single
1462
1540
  * file-global context identifier is not safe for instrumentation.
1541
+ *
1542
+ * `null` means no definePlay call owns the binding: it sits in a module-level
1543
+ * helper the run function calls, which has no context in scope.
1463
1544
  */
1464
- function findDocflowContextIdentifier(input: {
1545
+ function findDocflowContext(input: {
1465
1546
  ast: AstNode;
1547
+ sourceCode: string;
1466
1548
  statement: AstNode | null;
1467
1549
  bindingLine: number;
1468
1550
  lineStarts: readonly number[];
1469
1551
  parents: ReadonlyMap<AstNode, AstNode>;
1470
- }): string | null {
1552
+ }): DocflowContextResolution | null {
1471
1553
  let owner: AstNode | null = input.statement;
1472
1554
  while (owner) {
1473
- const contextName = docflowContextIdentifierFromCall(owner);
1474
- if (contextName) return contextName;
1555
+ const context = docflowContextFromCall(owner, input.sourceCode);
1556
+ if (context) return context;
1475
1557
  owner = input.parents.get(owner) ?? null;
1476
1558
  }
1477
1559
 
@@ -1504,7 +1586,7 @@ function findDocflowContextIdentifier(input: {
1504
1586
  else if (isAstNode(child)) pending.push(child);
1505
1587
  }
1506
1588
  }
1507
- return best ? docflowContextIdentifierFromCall(best) : null;
1589
+ return best ? docflowContextFromCall(best, input.sourceCode) : null;
1508
1590
  }
1509
1591
 
1510
1592
  type DocflowInstrumentationReplacement = {
@@ -1530,6 +1612,14 @@ function docflowObservationTarget(
1530
1612
  end: number;
1531
1613
  awaitsResult: boolean;
1532
1614
  expression: AstNode;
1615
+ /**
1616
+ * The statement is a `throw`: its node is observed FAILING, not completing.
1617
+ * The observed span is the thrown value, and the observation itself throws
1618
+ * it — so the node settles as failed with the authored error, and the outer
1619
+ * `throw` it replaces is never reached. A hit marker before the statement
1620
+ * used to record the node completed an instant before the run failed there.
1621
+ */
1622
+ throws: boolean;
1533
1623
  } | null {
1534
1624
  if (!statement) return null;
1535
1625
  let expression: AstNode | null = null;
@@ -1550,6 +1640,11 @@ function docflowObservationTarget(
1550
1640
  expression = statement.expression;
1551
1641
  } else if (statement.type === 'IfStatement' && isAstNode(statement.test)) {
1552
1642
  expression = statement.test;
1643
+ } else if (
1644
+ statement.type === 'ThrowStatement' &&
1645
+ isAstNode(statement.argument)
1646
+ ) {
1647
+ expression = statement.argument;
1553
1648
  }
1554
1649
  if (!expression) return null;
1555
1650
  const bounds = astNodeBounds(expression);
@@ -1558,6 +1653,7 @@ function docflowObservationTarget(
1558
1653
  ...bounds,
1559
1654
  awaitsResult: expressionContainsTopLevelAwait(expression),
1560
1655
  expression,
1656
+ throws: statement.type === 'ThrowStatement',
1561
1657
  };
1562
1658
  }
1563
1659
 
@@ -1568,10 +1664,13 @@ function docflowObservedExpression(input: {
1568
1664
  outputs: readonly string[];
1569
1665
  expression: string;
1570
1666
  awaitsResult: boolean;
1667
+ /** Observe the expression as the value a `throw` raises (see `throws`). */
1668
+ throws?: boolean;
1571
1669
  }): string {
1572
- const callback = input.awaitsResult
1573
- ? `async()=>(${input.expression})`
1574
- : `()=>(${input.expression})`;
1670
+ const body = input.throws
1671
+ ? `{throw (${input.expression})}`
1672
+ : `(${input.expression})`;
1673
+ const callback = input.awaitsResult ? `async()=>${body}` : `()=>${body}`;
1575
1674
  const observation = `((runtimeContext,inputCaptures,execute)=>typeof runtimeContext.__deeplineObserveDocflowNode==="function"?runtimeContext.__deeplineObserveDocflowNode(${JSON.stringify(input.nodeId)},inputCaptures,${JSON.stringify(input.outputs)},execute):execute())(${input.contextName},${docflowInputCaptureSource(input.inputs)},${callback})`;
1576
1675
  // Parenthesize the awaited form so it is self-delimiting at every source
1577
1676
  // boundary, including compact `return{...}` syntax. This avoids relying on
@@ -1627,10 +1726,13 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1627
1726
  start: number;
1628
1727
  end: number;
1629
1728
  awaitsResult: boolean;
1729
+ throws: boolean;
1630
1730
  contextName: string;
1631
1731
  binding: PlayDocflowBinding;
1632
1732
  };
1633
1733
  const targets: ObservationTarget[] = [];
1734
+ /** Parameterless run functions that receive an injected context param. */
1735
+ const injectedContextAt = new Set<number>();
1634
1736
 
1635
1737
  // Build the parent index once so every binding's symbol resolution reuses it.
1636
1738
  const parents = buildDocflowParentIndex(ast);
@@ -1639,14 +1741,27 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1639
1741
  // to positional resolution. Instrumentation and `plays check` share this
1640
1742
  // resolver, so the observed statement and the lint agree.
1641
1743
  const resolution = resolveDocflowBinding(ast, binding, lineStarts, parents);
1642
- const contextName = findDocflowContextIdentifier({
1744
+ const context = findDocflowContext({
1643
1745
  ast,
1746
+ sourceCode,
1644
1747
  statement: resolution.statement,
1645
1748
  bindingLine: binding.line,
1646
1749
  lineStarts,
1647
1750
  parents,
1648
1751
  });
1649
- if (!contextName) continue;
1752
+ // No owning definePlay: the binding is in a module-level helper with no
1753
+ // context in scope, so it is observed through its caller's bindings.
1754
+ if (!context) continue;
1755
+ if (context.kind === 'unsupported') {
1756
+ throw new Error(
1757
+ `@mermaid-node ${binding.nodeId} (line ${binding.line}) cannot be observed at runtime: ${context.reason}.`,
1758
+ );
1759
+ }
1760
+ if (context.kind === 'inject') injectedContextAt.add(context.at);
1761
+ // An injected parameter is undefined when the run function is invoked
1762
+ // directly (outside the runtime), so its reads stay null-safe.
1763
+ const contextName =
1764
+ context.kind === 'inject' ? `(${context.name}??{})` : context.name;
1650
1765
  const target = docflowObservationTarget(resolution.statement);
1651
1766
  // A binding naming a `.step('<leg>', …)` leg of a waterfall is NOT
1652
1767
  // instrumented, and the omission is deliberate rather than a gap.
@@ -1706,6 +1821,7 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1706
1821
  start: number;
1707
1822
  end: number;
1708
1823
  awaitsResult: boolean;
1824
+ throws: boolean;
1709
1825
  contextName: string;
1710
1826
  bindings: PlayDocflowBinding[];
1711
1827
  };
@@ -1719,6 +1835,7 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1719
1835
  start: target.start,
1720
1836
  end: target.end,
1721
1837
  awaitsResult: target.awaitsResult,
1838
+ throws: target.throws,
1722
1839
  contextName: target.contextName,
1723
1840
  bindings: [target.binding],
1724
1841
  });
@@ -1728,6 +1845,9 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1728
1845
  for (const group of groups) {
1729
1846
  // Wrap in reverse order so the first-listed annotation observes outermost.
1730
1847
  let expression = sourceCode.slice(group.start, group.end);
1848
+ // Only the innermost observation raises a thrown value; every annotation
1849
+ // wrapped around it sees that raise as its own failure.
1850
+ let innermost = true;
1731
1851
  for (const binding of [...group.bindings].reverse()) {
1732
1852
  expression = docflowObservedExpression({
1733
1853
  contextName: group.contextName,
@@ -1736,7 +1856,9 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1736
1856
  outputs: binding.outputs ?? [],
1737
1857
  expression,
1738
1858
  awaitsResult: group.awaitsResult,
1859
+ throws: group.throws && innermost,
1739
1860
  });
1861
+ innermost = false;
1740
1862
  }
1741
1863
  replacements.push({
1742
1864
  start: group.start,
@@ -1745,6 +1867,14 @@ export function instrumentPlayDocflowRuntimeHits(sourceCode: string): string {
1745
1867
  });
1746
1868
  }
1747
1869
 
1870
+ for (const at of injectedContextAt) {
1871
+ replacements.push({
1872
+ start: at,
1873
+ end: at,
1874
+ value: DOCFLOW_INJECTED_CONTEXT_NAME,
1875
+ });
1876
+ }
1877
+
1748
1878
  return replacements
1749
1879
  .sort((left, right) => right.start - left.start)
1750
1880
  .reduce(
@@ -0,0 +1,86 @@
1
+ // A cascade's falloff on one run: how many rows reached each attempt, how many
2
+ // it answered, and how many were left for the next — derived from the run
3
+ // summary's leg-hit patterns (`SheetSummary.cascades`) and the cascade's
4
+ // declared attempt order.
5
+ //
6
+ // A row is answered by the FIRST attempt, in declared order, whose leg holds a
7
+ // value. That is the waterfall's meaning even when later attempts also ran
8
+ // (a `steps()` chain without `runIf` runs every leg), so a later attempt never
9
+ // gets credit for a row an earlier one already answered.
10
+ //
11
+ // PURE — shared by the dashboard and the CLI so both state the same numbers.
12
+
13
+ export type CascadeFillPattern = {
14
+ /** The leg columns that held a value on these rows. */
15
+ filled: readonly string[];
16
+ rows: number;
17
+ };
18
+
19
+ export type CascadeFalloffStep = {
20
+ leg: string;
21
+ /** Rows no earlier attempt answered. */
22
+ entering: number;
23
+ /** Rows this attempt answered first. */
24
+ hits: number;
25
+ /** Rows still unanswered after this attempt. */
26
+ remaining: number;
27
+ };
28
+
29
+ export type CascadeFalloff = {
30
+ /** The rows the cascade worked on. */
31
+ total: number;
32
+ steps: CascadeFalloffStep[];
33
+ /** Rows no attempt answered. */
34
+ noResult: number;
35
+ };
36
+
37
+ export function cascadeFalloff(input: {
38
+ /** Leg columns in the cascade's declared order. */
39
+ legs: readonly string[];
40
+ patterns: readonly CascadeFillPattern[];
41
+ /** Rows the cascade worked on (the run's rows in its table). */
42
+ total: number;
43
+ }): CascadeFalloff {
44
+ const order = new Map(input.legs.map((leg, index) => [leg, index]));
45
+ const firstHits = input.legs.map(() => 0);
46
+ for (const pattern of input.patterns) {
47
+ let first = -1;
48
+ for (const leg of pattern.filled) {
49
+ const index = order.get(leg);
50
+ if (index !== undefined && (first === -1 || index < first)) first = index;
51
+ }
52
+ if (first !== -1) firstHits[first] += pattern.rows;
53
+ }
54
+ const answered = firstHits.reduce((sum, hits) => sum + hits, 0);
55
+ const total = Math.max(input.total, answered);
56
+ let entering = total;
57
+ const steps = input.legs.map((leg, index) => {
58
+ const hits = firstHits[index]!;
59
+ const step = { leg, entering, hits, remaining: entering - hits };
60
+ entering -= hits;
61
+ return step;
62
+ });
63
+ return { total, steps, noResult: total - answered };
64
+ }
65
+
66
+ /** `x%` — a count's share of `total`, never rounding a partial share up to a
67
+ * whole one or down to nothing. */
68
+ export function formatHitRate(count: number, total: number): string {
69
+ if (total <= 0) return '—';
70
+ const share = (count / total) * 100;
71
+ const percent =
72
+ share === 0 || share === 100
73
+ ? share.toString()
74
+ : share >= 10
75
+ ? Math.min(99, Math.round(share)).toString()
76
+ : share < 1
77
+ ? '<1'
78
+ : share.toFixed(1).replace(/\.0$/, '');
79
+ return `${percent}%`;
80
+ }
81
+
82
+ /** `n · x%` — a count and its share of `total`, for a dense surface. */
83
+ export function formatHitShare(count: number, total: number): string {
84
+ if (total <= 0) return count.toLocaleString('en-US');
85
+ return `${count.toLocaleString('en-US')} · ${formatHitRate(count, total)}`;
86
+ }