deepline 0.2.53 → 0.2.55

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 (44) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +17 -1
  2. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
  3. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  4. package/dist/bundling-sources/sdk/src/types.ts +43 -1
  5. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +30 -1
  6. package/dist/bundling-sources/shared_libs/play-runtime/cell-provenance.ts +231 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1094 -128
  8. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +178 -9
  9. package/dist/bundling-sources/shared_libs/play-runtime/docflow-node-io.ts +634 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/docflow-observation.ts +64 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +1 -1
  12. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +18 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +33 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +251 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/play-node-scope.ts +160 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +6 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +27 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +43 -5
  19. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +12 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +26 -4
  21. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +6 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +83 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +3 -0
  24. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +49 -1
  25. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +375 -29
  26. package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +636 -0
  27. package/dist/bundling-sources/shared_libs/plays/docflow-binding.ts +598 -0
  28. package/dist/bundling-sources/shared_libs/plays/docflow.ts +1645 -0
  29. package/dist/bundling-sources/shared_libs/plays/play-exports.ts +202 -0
  30. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +16 -1
  31. package/dist/bundling-sources/shared_libs/plays/ts-ast.ts +48 -0
  32. package/dist/cli/index.js +994 -312
  33. package/dist/cli/index.mjs +994 -312
  34. package/dist/{compiler-manifest-Cj3--4ZJ.d.mts → compiler-manifest-Bl8kmLx9.d.mts} +118 -0
  35. package/dist/{compiler-manifest-Cj3--4ZJ.d.ts → compiler-manifest-Bl8kmLx9.d.ts} +118 -0
  36. package/dist/index.d.mts +47 -2
  37. package/dist/index.d.ts +47 -2
  38. package/dist/index.js +419 -59
  39. package/dist/index.mjs +419 -59
  40. package/dist/install-integrity.json +12 -2
  41. package/dist/plays/bundle-play-file.d.mts +2 -2
  42. package/dist/plays/bundle-play-file.d.ts +2 -2
  43. package/dist/plays/bundle-play-file.mjs +1361 -45
  44. package/package.json +1 -1
@@ -26,8 +26,7 @@ import {
26
26
  sep
27
27
  } from "path";
28
28
  import { builtinModules } from "module";
29
- import { Parser } from "acorn";
30
- import { tsPlugin } from "acorn-typescript";
29
+ import { Parser as Parser2 } from "acorn";
31
30
  import { build, transformSync } from "esbuild";
32
31
 
33
32
  // ../shared_libs/play-runtime/backend.ts
@@ -3449,6 +3448,22 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
3449
3448
  description: "Whether the dataset returns all rows or only newly admitted rows.",
3450
3449
  errorMessage: 'ctx.dataset run mode must be "upsert" or "net_new".'
3451
3450
  },
3451
+ "ctx.dataset.run.undrawnColumns": {
3452
+ schema: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
3453
+ fixtures: {
3454
+ valid: ["miss_reason"],
3455
+ invalid: [],
3456
+ absent: void 0,
3457
+ unresolved: { expression: "undrawnColumns" },
3458
+ edition1: ["miss_reason"]
3459
+ },
3460
+ referenceType: "readonly string[]",
3461
+ required: false,
3462
+ resolution: "static-when-present",
3463
+ issueCode: "play_authoring_dataset_option_invalid",
3464
+ description: "Computed columns deliberately left out of the authored @mermaid diagram.",
3465
+ errorMessage: "ctx.dataset run undrawnColumns must be a non-empty array of static column-name strings."
3466
+ },
3452
3467
  "ctx.step.id": {
3453
3468
  schema: Type.String({ minLength: 1 }),
3454
3469
  fixtures: {
@@ -3936,7 +3951,7 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3936
3951
  "export type StepProgramOutput<TProgram> = TProgram extends StepProgram<unknown, infer Output, unknown> ? Output : never;",
3937
3952
  "export type DatasetRowKey<InputRow extends object> = (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);",
3938
3953
  "export type DatasetDefinitionOptions<InputRow extends object> = { key?: DatasetRowKey<InputRow> };",
3939
- `export type DatasetRunOptions<InputRow extends object> = { description?: ${cloudReferenceType("ctx.dataset.run.description")}; key?: DatasetRowKey<InputRow>; onRowError?: ${cloudReferenceType("ctx.dataset.run.onRowError")}; mode?: ${cloudReferenceType("ctx.dataset.run.mode")} };`,
3954
+ `export type DatasetRunOptions<InputRow extends object> = { description?: ${cloudReferenceType("ctx.dataset.run.description")}; key?: DatasetRowKey<InputRow>; onRowError?: ${cloudReferenceType("ctx.dataset.run.onRowError")}; mode?: ${cloudReferenceType("ctx.dataset.run.mode")}; undrawnColumns?: ${cloudReferenceType("ctx.dataset.run.undrawnColumns")} };`,
3940
3955
  "export type DatasetBuilder<InputRow extends object, OutputRow extends object> = {",
3941
3956
  " withColumn<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;",
3942
3957
  " withColumn<Name extends string, Value>(name: Name, definition: DatasetColumnDefinition<OutputRow, Value> & { readonly runIf: (row: OutputRow, index: number) => boolean | Promise<boolean> }): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;",
@@ -3989,6 +4004,1123 @@ function buildPlayContractCompatibility(input) {
3989
4004
  };
3990
4005
  }
3991
4006
 
4007
+ // ../shared_libs/plays/ts-ast.ts
4008
+ import { Parser } from "acorn";
4009
+ import { tsPlugin } from "acorn-typescript";
4010
+ var TypeScriptParser = Parser.extend(
4011
+ tsPlugin({
4012
+ allowSatisfies: true,
4013
+ jsx: {
4014
+ allowNamespaces: true,
4015
+ allowNamespacedObjects: true
4016
+ }
4017
+ })
4018
+ );
4019
+ function isAstNode(value) {
4020
+ return value !== null && typeof value === "object" && "type" in value;
4021
+ }
4022
+ function astArray(value) {
4023
+ return Array.isArray(value) ? value.filter(isAstNode) : [];
4024
+ }
4025
+ function parsePlaySourceForAnalysis(sourceCode) {
4026
+ try {
4027
+ return TypeScriptParser.parse(sourceCode, {
4028
+ ecmaVersion: "latest",
4029
+ sourceType: "module",
4030
+ allowHashBang: true
4031
+ });
4032
+ } catch {
4033
+ return null;
4034
+ }
4035
+ }
4036
+
4037
+ // ../shared_libs/plays/play-exports.ts
4038
+ var PLAY_DEFAULT_EXPORT = "default";
4039
+ function getIdentifierName(node) {
4040
+ return isAstNode(node) && node.type === "Identifier" ? typeof node.name === "string" ? node.name : null : null;
4041
+ }
4042
+ function unwrapStaticExpression(node) {
4043
+ let current = node;
4044
+ while (current && (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "ParenthesizedExpression")) {
4045
+ current = isAstNode(current.expression) ? current.expression : null;
4046
+ }
4047
+ return current;
4048
+ }
4049
+ function isDefinePlayCall(node) {
4050
+ const expression = unwrapStaticExpression(node);
4051
+ if (!expression || expression.type !== "CallExpression") return false;
4052
+ const callee = isAstNode(expression.callee) ? expression.callee : null;
4053
+ if (!callee) return false;
4054
+ if (callee.type === "Identifier") {
4055
+ return callee.name === "definePlay" || callee.name === "defineWorkflow";
4056
+ }
4057
+ if (callee.type === "MemberExpression" && !callee.computed && isAstNode(callee.property) && callee.property.type === "Identifier") {
4058
+ return callee.property.name === "definePlay" || callee.property.name === "defineWorkflow";
4059
+ }
4060
+ return false;
4061
+ }
4062
+ function listPlayFileExports(sourceCode) {
4063
+ const ast = parsePlaySourceForAnalysis(sourceCode);
4064
+ if (!ast) return null;
4065
+ const declarations = /* @__PURE__ */ new Map();
4066
+ const namedExports = /* @__PURE__ */ new Map();
4067
+ let defaultExpression = null;
4068
+ const recordDeclarations = (declaration, exported) => {
4069
+ for (const declarator of astArray(declaration.declarations)) {
4070
+ const name = getIdentifierName(declarator.id);
4071
+ if (!name) continue;
4072
+ declarations.set(
4073
+ name,
4074
+ isAstNode(declarator.init) ? declarator.init : null
4075
+ );
4076
+ if (exported) namedExports.set(name, name);
4077
+ }
4078
+ };
4079
+ for (const statement of astArray(ast.body)) {
4080
+ if (statement.type === "VariableDeclaration") {
4081
+ recordDeclarations(statement, false);
4082
+ continue;
4083
+ }
4084
+ if (statement.type === "ExportDefaultDeclaration") {
4085
+ defaultExpression = isAstNode(statement.declaration) ? statement.declaration : null;
4086
+ continue;
4087
+ }
4088
+ if (statement.type === "TSExportAssignment") {
4089
+ defaultExpression = isAstNode(statement.expression) ? statement.expression : null;
4090
+ continue;
4091
+ }
4092
+ if (statement.type !== "ExportNamedDeclaration") continue;
4093
+ if (isAstNode(statement.declaration) && statement.declaration.type === "VariableDeclaration") {
4094
+ recordDeclarations(statement.declaration, true);
4095
+ }
4096
+ for (const specifier of astArray(statement.specifiers)) {
4097
+ const localName = getIdentifierName(specifier.local);
4098
+ const exportedName = getIdentifierName(specifier.exported);
4099
+ if (localName && exportedName) namedExports.set(exportedName, localName);
4100
+ }
4101
+ }
4102
+ const defaultLocalName = getIdentifierName(
4103
+ unwrapStaticExpression(defaultExpression)
4104
+ );
4105
+ const defaultIsPlay = defaultLocalName ? isDefinePlayCall(declarations.get(defaultLocalName) ?? null) : isDefinePlayCall(defaultExpression);
4106
+ const exports = [];
4107
+ const aliasedLocals = /* @__PURE__ */ new Set();
4108
+ if (defaultIsPlay) {
4109
+ const aliases = defaultLocalName ? [...namedExports.entries()].filter(([, local]) => local === defaultLocalName).map(([exported]) => exported) : [];
4110
+ if (defaultLocalName) aliasedLocals.add(defaultLocalName);
4111
+ exports.push({ name: PLAY_DEFAULT_EXPORT, aliases });
4112
+ }
4113
+ for (const [exportedName, localName] of namedExports) {
4114
+ if (exportedName === PLAY_DEFAULT_EXPORT) continue;
4115
+ if (aliasedLocals.has(localName)) continue;
4116
+ if (!isDefinePlayCall(declarations.get(localName) ?? null)) continue;
4117
+ exports.push({ name: exportedName, aliases: [] });
4118
+ }
4119
+ return exports;
4120
+ }
4121
+ function canonicalPlayExportName(exportName, exports) {
4122
+ const requested = exportName?.trim() || PLAY_DEFAULT_EXPORT;
4123
+ for (const entry of exports) {
4124
+ if (entry.name === requested || entry.aliases.includes(requested)) {
4125
+ return entry.name;
4126
+ }
4127
+ }
4128
+ return null;
4129
+ }
4130
+ function playExportNamesForMessage(exports) {
4131
+ return exports.flatMap((entry) => [entry.name, ...entry.aliases]);
4132
+ }
4133
+
4134
+ // ../shared_libs/plays/docflow.ts
4135
+ var PLAY_DOCFLOW_NODE_KINDS = [
4136
+ "action",
4137
+ "decision",
4138
+ "dataset",
4139
+ "play",
4140
+ "conceptual"
4141
+ ];
4142
+ var PLAY_DOCFLOW_ARMS = [
4143
+ "run",
4144
+ "else"
4145
+ ];
4146
+ var FLOW_START = /^\s*(?:flowchart|graph)\s+(LR|RL|TB|TD|BT)\s*$/i;
4147
+ var PUT = /^\s*\/\/\s*put\s+(.+)$/;
4148
+ var MERMAID_NODE = /^\s*\/\/\s*@mermaid-node\s+([A-Za-z][\w-]*)(?:\s+(.*))?$/;
4149
+ var ATTRIBUTE = /([A-Za-z][\w-]*)\s*:\s*"([^"\n]*)"/y;
4150
+ var MERMAID_NODE_ATTRIBUTES = ["label", "type", "in", "out", "arm"];
4151
+ var LEGACY_DOCFLOW_ATTRIBUTES = ["id", ...MERMAID_NODE_ATTRIBUTES];
4152
+ var MERMAID_NODE_ID = /^[A-Za-z][\w-]*/;
4153
+ var MERMAID_NODE_SHAPES = [
4154
+ ["[[", "]]"],
4155
+ ["[(", ")]"],
4156
+ ["([", "])"],
4157
+ ["{{", "}}"],
4158
+ ["((", "))"],
4159
+ ["[/", "/]"],
4160
+ ["[\\", "\\]"],
4161
+ ["[/", "\\]"],
4162
+ ["[\\", "/]"],
4163
+ ["[", "]"],
4164
+ ["{", "}"],
4165
+ ["(", ")"],
4166
+ [">", "]"]
4167
+ ];
4168
+ var SUBGRAPH_OPEN = /^\s*subgraph\s+([A-Za-z][\w-]*)\s*(?:\[(.*)\])?\s*$/i;
4169
+ var SUBGRAPH_END = /^\s*end\s*$/i;
4170
+ var DOCFLOW_EDGE_CONNECTOR = /^\s*-->(?:\|([^|]+)\|)?/;
4171
+ var MERMAID_EDGE_CONNECTOR = /^\s*(?:-->|-\.->|==>|---)(?:\|([^|]+)\|)?/;
4172
+ var DOCFLOW_PATH = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/;
4173
+ var IDENTIFIER = /[A-Za-z_$][\w$]*/g;
4174
+ var JS_WORDS = /* @__PURE__ */ new Set([
4175
+ "async",
4176
+ "await",
4177
+ "const",
4178
+ "else",
4179
+ "false",
4180
+ "if",
4181
+ "let",
4182
+ "new",
4183
+ "null",
4184
+ "return",
4185
+ "true",
4186
+ "undefined",
4187
+ "var"
4188
+ ]);
4189
+ function parseAttributes(source, input) {
4190
+ const attributes = {};
4191
+ const validAttributes = input.annotation === "@mermaid-node" ? MERMAID_NODE_ATTRIBUTES : LEGACY_DOCFLOW_ATTRIBUTES;
4192
+ let cursor = 0;
4193
+ while (cursor < source.length) {
4194
+ cursor += /^\s*/.exec(source.slice(cursor))?.[0].length ?? 0;
4195
+ if (cursor >= source.length) break;
4196
+ ATTRIBUTE.lastIndex = cursor;
4197
+ const match = ATTRIBUTE.exec(source);
4198
+ if (!match) {
4199
+ const fragment = source.slice(cursor).split(/\s+/)[0] ?? source.slice(cursor);
4200
+ input.errors.push(
4201
+ `Docflow annotation on line ${input.line} has malformed attribute ${JSON.stringify(fragment)}. Use key:"value" pairs. Valid attributes: ${validAttributes.map((name2) => `"${name2}"`).join(", ")}.`
4202
+ );
4203
+ return null;
4204
+ }
4205
+ const name = match[1];
4206
+ if (!validAttributes.includes(name)) {
4207
+ input.errors.push(
4208
+ `Docflow annotation on line ${input.line} has unknown attribute "${name}". Valid attributes: ${validAttributes.map((option) => `"${option}"`).join(", ")}.`
4209
+ );
4210
+ return null;
4211
+ }
4212
+ if (Object.prototype.hasOwnProperty.call(attributes, name)) {
4213
+ input.errors.push(
4214
+ `Docflow annotation on line ${input.line} repeats attribute "${name}". Write each attribute once.`
4215
+ );
4216
+ return null;
4217
+ }
4218
+ attributes[name] = match[2];
4219
+ cursor = ATTRIBUTE.lastIndex;
4220
+ }
4221
+ return attributes;
4222
+ }
4223
+ function nodeKind(value) {
4224
+ if (!value) return void 0;
4225
+ return PLAY_DOCFLOW_NODE_KINDS.includes(value) ? value : null;
4226
+ }
4227
+ function nodeArm(value) {
4228
+ if (!value) return void 0;
4229
+ return PLAY_DOCFLOW_ARMS.includes(value) ? value : null;
4230
+ }
4231
+ var SHAPE_KINDS = {
4232
+ "{": "decision",
4233
+ "[[": "play"
4234
+ };
4235
+ var MERMAID_LABEL_LOOKS_LIKE_SOURCE = /^(?:\[|\{|\(|>)/;
4236
+ function parseMermaidNodeAtStart(source) {
4237
+ const leadingWhitespace = /^\s*/.exec(source)?.[0].length ?? 0;
4238
+ const idMatch = MERMAID_NODE_ID.exec(source.slice(leadingWhitespace));
4239
+ if (!idMatch) return null;
4240
+ const id = idMatch[0];
4241
+ let cursor = leadingWhitespace + id.length;
4242
+ cursor += /^\s*/.exec(source.slice(cursor))?.[0].length ?? 0;
4243
+ const shape = MERMAID_NODE_SHAPES.find(
4244
+ ([open2]) => source.startsWith(open2, cursor)
4245
+ );
4246
+ if (!shape) {
4247
+ return { id, label: id, kind: "action", length: cursor, shaped: false };
4248
+ }
4249
+ const [open, close] = shape;
4250
+ const labelStart = cursor + open.length;
4251
+ const openingQuote = source[labelStart];
4252
+ let labelEnd = -1;
4253
+ if (openingQuote === '"' || openingQuote === "'") {
4254
+ for (let index = labelStart + 1; index < source.length; index += 1) {
4255
+ if (source[index] === "\\") {
4256
+ index += 1;
4257
+ continue;
4258
+ }
4259
+ if (source[index] !== openingQuote) continue;
4260
+ const whitespace = /^\s*/.exec(source.slice(index + 1))?.[0].length ?? 0;
4261
+ const candidate = index + 1 + whitespace;
4262
+ if (source.startsWith(close, candidate)) {
4263
+ labelEnd = candidate;
4264
+ break;
4265
+ }
4266
+ }
4267
+ } else {
4268
+ labelEnd = source.indexOf(close, labelStart);
4269
+ }
4270
+ if (labelEnd < 0) {
4271
+ return {
4272
+ id,
4273
+ label: id,
4274
+ kind: "action",
4275
+ length: cursor + open.length,
4276
+ shaped: false,
4277
+ unterminated: { open, close }
4278
+ };
4279
+ }
4280
+ const rawLabel = source.slice(labelStart, labelEnd).trim();
4281
+ const label = rawLabel.length >= 2 && (rawLabel.startsWith('"') && rawLabel.endsWith('"') || rawLabel.startsWith("'") && rawLabel.endsWith("'")) ? rawLabel.slice(1, -1) : rawLabel;
4282
+ return {
4283
+ id,
4284
+ label: label || id,
4285
+ kind: SHAPE_KINDS[open] ?? "action",
4286
+ length: labelEnd + close.length,
4287
+ shaped: true
4288
+ };
4289
+ }
4290
+ function parseShapedMermaidNodes(fragment) {
4291
+ const nodes = [];
4292
+ let cursor = 0;
4293
+ while (cursor < fragment.length) {
4294
+ const identifier = /[A-Za-z][\w-]*/.exec(fragment.slice(cursor));
4295
+ if (!identifier) break;
4296
+ const start = cursor + (identifier.index ?? 0);
4297
+ const parsed = parseMermaidNodeAtStart(fragment.slice(start));
4298
+ if (parsed?.shaped || parsed?.unterminated) nodes.push(parsed);
4299
+ cursor = start + Math.max(parsed?.length ?? identifier[0].length, 1);
4300
+ }
4301
+ return nodes;
4302
+ }
4303
+ function parseEdgeChain(line, syntax) {
4304
+ let remaining = line;
4305
+ const first = parseMermaidNodeAtStart(remaining);
4306
+ if (!first) return null;
4307
+ const nodes = [first];
4308
+ const labels = [];
4309
+ remaining = remaining.slice(first.length);
4310
+ while (remaining.trim()) {
4311
+ const connector = (syntax === "mermaid" ? MERMAID_EDGE_CONNECTOR : DOCFLOW_EDGE_CONNECTOR).exec(remaining);
4312
+ if (!connector) return null;
4313
+ remaining = remaining.slice(connector[0].length);
4314
+ const next = parseMermaidNodeAtStart(remaining);
4315
+ if (!next) return null;
4316
+ labels.push(connector[1]?.trim() || void 0);
4317
+ nodes.push(next);
4318
+ remaining = remaining.slice(next.length);
4319
+ }
4320
+ return nodes.length > 1 ? { nodes, labels } : null;
4321
+ }
4322
+ function parseContractPaths(value, attribute, nodeId, line, errors) {
4323
+ if (value === void 0) return void 0;
4324
+ const paths = value.split(",").map((path) => path.trim()).filter(Boolean);
4325
+ if (paths.some((path) => !DOCFLOW_PATH.test(path))) {
4326
+ errors.push(
4327
+ `Docflow annotation "${nodeId}" on line ${line} has an invalid ${attribute}:"\u2026" contract. Use comma-separated identifiers or property paths only.`
4328
+ );
4329
+ return void 0;
4330
+ }
4331
+ return [...new Set(paths)];
4332
+ }
4333
+ function inferBindingIo(statement) {
4334
+ const returned = /^return\s+(.+?);?\s*$/.exec(statement.trim());
4335
+ if (returned) {
4336
+ const inputs2 = /* @__PURE__ */ new Set();
4337
+ const expression2 = returned[1].replace(/(['"`])(?:\\.|(?!\1).)*\1/g, "");
4338
+ IDENTIFIER.lastIndex = 0;
4339
+ for (const match of expression2.matchAll(IDENTIFIER)) {
4340
+ const identifier = match[0];
4341
+ const index = match.index ?? 0;
4342
+ if (JS_WORDS.has(identifier) || expression2[index - 1] === ".") continue;
4343
+ inputs2.add(identifier);
4344
+ }
4345
+ return {
4346
+ inputs: [...inputs2],
4347
+ ioConfidence: "inferred"
4348
+ };
4349
+ }
4350
+ const assignment = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?\s*$/.exec(
4351
+ statement.trim()
4352
+ );
4353
+ if (!assignment || assignment[2].includes("await")) {
4354
+ return { ioConfidence: "ambiguous" };
4355
+ }
4356
+ const output = assignment[1];
4357
+ const expression = assignment[2];
4358
+ const inputs = /* @__PURE__ */ new Set();
4359
+ IDENTIFIER.lastIndex = 0;
4360
+ for (const match of expression.matchAll(IDENTIFIER)) {
4361
+ const identifier = match[0];
4362
+ const index = match.index ?? 0;
4363
+ const previous = expression[index - 1] ?? "";
4364
+ const next = expression[index + identifier.length] ?? "";
4365
+ if (JS_WORDS.has(identifier) || identifier === output || previous === "." || next === ":" || /^\s*\(/.test(expression.slice(index + identifier.length))) {
4366
+ continue;
4367
+ }
4368
+ inputs.add(identifier);
4369
+ }
4370
+ return {
4371
+ inputs: [...inputs],
4372
+ outputs: [output],
4373
+ ioConfidence: "inferred"
4374
+ };
4375
+ }
4376
+ var MERMAID_BLOCK = /\/\*\*\s*@mermaid(?:\s|\r?\n)([\s\S]*?)\*\//g;
4377
+ var LEGACY_BLOCK = /\/\*\*\s*@docflow(?:\s|\r?\n)([\s\S]*?)\*\//;
4378
+ var BLOCK_EXPORT_HEADER = /^[A-Za-z_$][\w$]*$/;
4379
+ function cleanBlockBody(raw) {
4380
+ return raw.split(/\r?\n/).map((line) => /^\s*\*/.test(line) ? line.replace(/^\s*\* ?/, "") : line).join("\n").trim();
4381
+ }
4382
+ function splitBlockExportHeader(body) {
4383
+ const newlineIndex = body.indexOf("\n");
4384
+ const firstLine = (newlineIndex < 0 ? body : body.slice(0, newlineIndex)).trim();
4385
+ if (!BLOCK_EXPORT_HEADER.test(firstLine) || /^(?:flowchart|graph)$/i.test(firstLine)) {
4386
+ return { exportName: null, diagram: body };
4387
+ }
4388
+ return {
4389
+ exportName: firstLine,
4390
+ diagram: newlineIndex < 0 ? "" : body.slice(newlineIndex + 1).trim()
4391
+ };
4392
+ }
4393
+ function blockLabel(block) {
4394
+ const tag = block.syntax === "mermaid" ? "@mermaid" : "@docflow";
4395
+ return block.authoredExportName ? `${tag} ${block.authoredExportName}` : tag;
4396
+ }
4397
+ function parseDocflowBlockGraph(mermaidSource, syntax, errors) {
4398
+ const docLines = mermaidSource.split(/\r?\n/).filter((line) => line.trim() && !line.trim().startsWith("%%"));
4399
+ const direction = FLOW_START.exec(docLines[0] ?? "")?.[1]?.toUpperCase();
4400
+ if (!direction) {
4401
+ errors.push(
4402
+ "Docflow must start with `flowchart` or `graph` and direction `LR`, `RL`, `TB`, `TD`, or `BT`."
4403
+ );
4404
+ return null;
4405
+ }
4406
+ const nodes = /* @__PURE__ */ new Map();
4407
+ const edges = [];
4408
+ const subgraphs = /* @__PURE__ */ new Map();
4409
+ const ignoredDirectives = [];
4410
+ const subgraphStack = [];
4411
+ const subgraphIds = /* @__PURE__ */ new Set();
4412
+ if (syntax === "mermaid") {
4413
+ for (const line of docLines.slice(1)) {
4414
+ const opened = SUBGRAPH_OPEN.exec(line);
4415
+ if (opened) subgraphIds.add(opened[1]);
4416
+ }
4417
+ }
4418
+ const declaredIds = /* @__PURE__ */ new Set();
4419
+ const addNodes = (fragment, line) => {
4420
+ let sawDeclaration = false;
4421
+ for (const node of parseShapedMermaidNodes(fragment)) {
4422
+ const { id, label, kind } = node;
4423
+ if (node.unterminated) {
4424
+ sawDeclaration = true;
4425
+ errors.push(
4426
+ `Docflow node "${id}" opens with \`${node.unterminated.open}\` but never closes with \`${node.unterminated.close}\`: ${line.trim()}`
4427
+ );
4428
+ continue;
4429
+ }
4430
+ sawDeclaration = true;
4431
+ if (subgraphIds.has(id)) {
4432
+ errors.push(
4433
+ `Docflow subgraph "${id}" collides with a node of the same id.`
4434
+ );
4435
+ continue;
4436
+ }
4437
+ if (MERMAID_LABEL_LOOKS_LIKE_SOURCE.test(label)) {
4438
+ errors.push(
4439
+ `Docflow node "${id}" has an unreadable label \`${label}\` \u2014 the shape around it is not one this parser knows, so its own syntax became the label. Supported shapes: ${MERMAID_NODE_SHAPES.map(([open, close]) => `${open}\u2026${close}`).join(", ")}. Line: ${line.trim()}`
4440
+ );
4441
+ continue;
4442
+ }
4443
+ declaredIds.add(id);
4444
+ const existing = nodes.get(id);
4445
+ if (existing && existing.label === existing.id && label !== id) {
4446
+ existing.label = label;
4447
+ existing.kind = kind;
4448
+ } else if (existing && existing.label !== label) {
4449
+ errors.push(`Docflow node "${id}" has conflicting labels.`);
4450
+ } else if (!existing) {
4451
+ nodes.set(id, { id, label, kind });
4452
+ subgraphStack[subgraphStack.length - 1]?.memberIds.push(id);
4453
+ }
4454
+ }
4455
+ return sawDeclaration;
4456
+ };
4457
+ for (const line of docLines.slice(1)) {
4458
+ if (syntax === "mermaid") {
4459
+ const opened = SUBGRAPH_OPEN.exec(line);
4460
+ if (opened) {
4461
+ const id = opened[1];
4462
+ const rawLabel = opened[2]?.trim() ?? "";
4463
+ const label = rawLabel.length >= 2 && (rawLabel.startsWith('"') && rawLabel.endsWith('"') || rawLabel.startsWith("'") && rawLabel.endsWith("'")) ? rawLabel.slice(1, -1) : rawLabel;
4464
+ const subgraph = {
4465
+ id,
4466
+ label: label || id,
4467
+ memberIds: []
4468
+ };
4469
+ subgraphs.set(id, subgraph);
4470
+ subgraphStack.push(subgraph);
4471
+ continue;
4472
+ }
4473
+ if (SUBGRAPH_END.test(line)) {
4474
+ if (subgraphStack.length === 0) {
4475
+ errors.push(
4476
+ "Docflow has an `end` with no open `subgraph` above it. Every `end` closes exactly one `subgraph`."
4477
+ );
4478
+ }
4479
+ subgraphStack.pop();
4480
+ continue;
4481
+ }
4482
+ }
4483
+ let declaredOnThisLine = false;
4484
+ const isDirective = /^\s*(?:direction|classDef|class|style|linkStyle|click)\b/i.test(line);
4485
+ if (syntax === "mermaid" && isDirective) {
4486
+ ignoredDirectives.push(line.trim());
4487
+ }
4488
+ if (syntax === "mermaid" && !isDirective) {
4489
+ declaredOnThisLine = addNodes(line, line);
4490
+ }
4491
+ const chain = parseEdgeChain(line, syntax);
4492
+ if (!chain) {
4493
+ if (syntax === "docflow") {
4494
+ errors.push(`Unsupported docflow line: ${line.trim()}`);
4495
+ } else if (!isDirective && !declaredOnThisLine) {
4496
+ errors.push(
4497
+ `Docflow line is not an edge, a node declaration, or a directive this parser understands: ${line.trim()}`
4498
+ );
4499
+ }
4500
+ continue;
4501
+ }
4502
+ if (syntax === "docflow") addNodes(line, line);
4503
+ for (const node of chain.nodes) {
4504
+ if (subgraphIds.has(node.id)) continue;
4505
+ const existing = nodes.get(node.id);
4506
+ if (!existing) {
4507
+ nodes.set(node.id, node);
4508
+ subgraphStack[subgraphStack.length - 1]?.memberIds.push(node.id);
4509
+ } else if (existing.label === existing.id && node.label !== node.id) {
4510
+ existing.label = node.label;
4511
+ existing.kind = node.kind;
4512
+ }
4513
+ }
4514
+ for (let index = 0; index < chain.nodes.length - 1; index += 1) {
4515
+ edges.push({
4516
+ from: chain.nodes[index].id,
4517
+ to: chain.nodes[index + 1].id,
4518
+ ...chain.labels[index] ? { label: chain.labels[index] } : {}
4519
+ });
4520
+ }
4521
+ }
4522
+ if (subgraphStack.length > 0) {
4523
+ errors.push(
4524
+ `Docflow subgraph${subgraphStack.length === 1 ? "" : "s"} ${subgraphStack.map((subgraph) => `"${subgraph.id}"`).join(
4525
+ ", "
4526
+ )} ${subgraphStack.length === 1 ? "is" : "are"} never closed with \`end\`. Everything below an unclosed subgraph is silently drawn inside it.`
4527
+ );
4528
+ }
4529
+ for (const node of nodes.values()) {
4530
+ if (declaredIds.has(node.id) || subgraphIds.has(node.id)) continue;
4531
+ errors.push(
4532
+ `Docflow node "${node.id}" is referenced by an edge but never declared with a shape and label, so the canvas would show its id. Declare it once, e.g. \`${node.id}["What this step does"]\`.`
4533
+ );
4534
+ }
4535
+ return { direction, nodes, edges, subgraphs, ignoredDirectives };
4536
+ }
4537
+ function materializeDocflow(block) {
4538
+ return {
4539
+ direction: block.direction,
4540
+ nodes: [...block.nodes.values()],
4541
+ edges: block.edges,
4542
+ bindings: block.bindings,
4543
+ syntax: block.syntax,
4544
+ mermaidSource: block.mermaidSource,
4545
+ ...block.subgraphs.size ? { subgraphs: [...block.subgraphs.values()] } : {},
4546
+ ...block.ignoredDirectives.length ? { ignoredDirectives: block.ignoredDirectives } : {}
4547
+ };
4548
+ }
4549
+ function parsePlayDocflowFile(sourceCode) {
4550
+ const errors = [];
4551
+ const bindings = parseDocflowBindings(sourceCode, errors);
4552
+ const rawBlocks = [];
4553
+ MERMAID_BLOCK.lastIndex = 0;
4554
+ for (const match of sourceCode.matchAll(MERMAID_BLOCK)) {
4555
+ rawBlocks.push({ syntax: "mermaid", body: cleanBlockBody(match[1]) });
4556
+ }
4557
+ if (rawBlocks.length === 0) {
4558
+ const legacy = LEGACY_BLOCK.exec(sourceCode);
4559
+ if (legacy) {
4560
+ rawBlocks.push({ syntax: "docflow", body: cleanBlockBody(legacy[1]) });
4561
+ }
4562
+ }
4563
+ if (rawBlocks.length === 0) return { blocks: [], bindings, errors };
4564
+ const parsedBlocks = [];
4565
+ for (const raw of rawBlocks) {
4566
+ const { exportName, diagram } = splitBlockExportHeader(raw.body);
4567
+ const graph = parseDocflowBlockGraph(diagram, raw.syntax, errors);
4568
+ if (!graph) continue;
4569
+ parsedBlocks.push({
4570
+ ...graph,
4571
+ authoredExportName: exportName,
4572
+ exportName: null,
4573
+ syntax: raw.syntax,
4574
+ mermaidSource: diagram,
4575
+ bindings: []
4576
+ });
4577
+ }
4578
+ resolveBlockExports(sourceCode, parsedBlocks, errors);
4579
+ attachBindingsToBlocks(sourceCode, parsedBlocks, bindings, errors);
4580
+ return {
4581
+ blocks: parsedBlocks.filter((block) => block.exportName !== null).map((block) => ({
4582
+ exportName: block.exportName,
4583
+ docflow: materializeDocflow(block)
4584
+ })),
4585
+ bindings,
4586
+ errors
4587
+ };
4588
+ }
4589
+ function resolveBlockExports(sourceCode, blocks, errors) {
4590
+ const fileExports = blocks.some((block) => block.authoredExportName !== null) ? listPlayFileExports(sourceCode) : null;
4591
+ const claimed = /* @__PURE__ */ new Map();
4592
+ for (const block of blocks) {
4593
+ if (block.authoredExportName === null) {
4594
+ block.exportName = PLAY_DEFAULT_EXPORT;
4595
+ } else if (fileExports === null) {
4596
+ block.exportName = block.authoredExportName;
4597
+ } else {
4598
+ const canonical = canonicalPlayExportName(
4599
+ block.authoredExportName,
4600
+ fileExports
4601
+ );
4602
+ if (!canonical) {
4603
+ const available = playExportNamesForMessage(fileExports);
4604
+ errors.push(
4605
+ `Docflow block \`${blockLabel(block)}\` names an export this file does not define. ` + (available.length ? `Exported plays: ${available.map((name) => `"${name}"`).join(", ")}.` : "This file exports no play.")
4606
+ );
4607
+ continue;
4608
+ }
4609
+ block.exportName = canonical;
4610
+ }
4611
+ const existing = claimed.get(block.exportName);
4612
+ if (existing) {
4613
+ errors.push(
4614
+ `Docflow blocks \`${blockLabel(existing)}\` and \`${blockLabel(block)}\` both describe export "${block.exportName}". One diagram per export.`
4615
+ );
4616
+ block.exportName = null;
4617
+ continue;
4618
+ }
4619
+ claimed.set(block.exportName, block);
4620
+ }
4621
+ }
4622
+ function unwrapExportExpression(node) {
4623
+ let current = node;
4624
+ while (current && (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "ParenthesizedExpression")) {
4625
+ current = isAstNode(current.expression) ? current.expression : null;
4626
+ }
4627
+ return current;
4628
+ }
4629
+ function playExportSourceRanges(sourceCode) {
4630
+ const ast = parsePlaySourceForAnalysis(sourceCode);
4631
+ const fileExports = listPlayFileExports(sourceCode);
4632
+ if (!ast || !fileExports) return [];
4633
+ const declarations = /* @__PURE__ */ new Map();
4634
+ const namedExports = /* @__PURE__ */ new Map();
4635
+ let defaultExpression = null;
4636
+ for (const statement of astArray(ast.body)) {
4637
+ const recordDeclarations = (declaration, exported) => {
4638
+ for (const declarator of astArray(declaration.declarations)) {
4639
+ const id = isAstNode(declarator.id) ? declarator.id : null;
4640
+ const name = id?.type === "Identifier" && typeof id.name === "string" ? id.name : null;
4641
+ if (!name) continue;
4642
+ declarations.set(
4643
+ name,
4644
+ isAstNode(declarator.init) ? declarator.init : null
4645
+ );
4646
+ if (exported) namedExports.set(name, name);
4647
+ }
4648
+ };
4649
+ if (statement.type === "VariableDeclaration") {
4650
+ recordDeclarations(statement, false);
4651
+ } else if (statement.type === "ExportDefaultDeclaration") {
4652
+ defaultExpression = isAstNode(statement.declaration) ? statement.declaration : null;
4653
+ } else if (statement.type === "TSExportAssignment") {
4654
+ defaultExpression = isAstNode(statement.expression) ? statement.expression : null;
4655
+ } else if (statement.type === "ExportNamedDeclaration") {
4656
+ if (isAstNode(statement.declaration) && statement.declaration.type === "VariableDeclaration") {
4657
+ recordDeclarations(statement.declaration, true);
4658
+ }
4659
+ for (const specifier of astArray(statement.specifiers)) {
4660
+ const local = isAstNode(specifier.local) ? specifier.local : null;
4661
+ const exported = isAstNode(specifier.exported) ? specifier.exported : null;
4662
+ if (local?.type === "Identifier" && typeof local.name === "string" && exported?.type === "Identifier" && typeof exported.name === "string") {
4663
+ namedExports.set(exported.name, local.name);
4664
+ }
4665
+ }
4666
+ }
4667
+ }
4668
+ const resolveCall = (expression, seen = /* @__PURE__ */ new Set()) => {
4669
+ const unwrapped = unwrapExportExpression(expression);
4670
+ if (!unwrapped) return null;
4671
+ if (isDefinePlayCall(unwrapped)) return unwrapped;
4672
+ if (unwrapped.type !== "Identifier" || typeof unwrapped.name !== "string" || seen.has(unwrapped.name)) {
4673
+ return null;
4674
+ }
4675
+ seen.add(unwrapped.name);
4676
+ return resolveCall(declarations.get(unwrapped.name) ?? null, seen);
4677
+ };
4678
+ const defaultCall = resolveCall(defaultExpression);
4679
+ const ranges = [];
4680
+ for (const fileExport of fileExports) {
4681
+ const call = fileExport.name === PLAY_DEFAULT_EXPORT ? defaultCall : resolveCall(
4682
+ declarations.get(namedExports.get(fileExport.name) ?? "") ?? null
4683
+ );
4684
+ if (call && typeof call.start === "number" && typeof call.end === "number") {
4685
+ ranges.push({
4686
+ exportName: fileExport.name,
4687
+ start: call.start,
4688
+ end: call.end
4689
+ });
4690
+ }
4691
+ }
4692
+ return ranges;
4693
+ }
4694
+ function projectRecordedArms(block, errors) {
4695
+ const declaredBy = /* @__PURE__ */ new Map();
4696
+ for (const binding of block.bindings) {
4697
+ if (!binding.arm) continue;
4698
+ const incoming = block.edges.filter(
4699
+ (edge2) => edge2.to === binding.nodeId && block.nodes.get(edge2.from)?.kind === "decision"
4700
+ );
4701
+ if (incoming.length === 0) {
4702
+ errors.push(
4703
+ `Docflow annotation "${binding.nodeId}" declares arm:"${binding.arm}" but no decision points at it. Put arm:"\u2026" on the node a decision's labelled edge leads to.`
4704
+ );
4705
+ continue;
4706
+ }
4707
+ if (incoming.length > 1) {
4708
+ errors.push(
4709
+ `Docflow annotation "${binding.nodeId}" declares arm:"${binding.arm}" but ${incoming.length} decisions point at it, so the arm names no single edge. Give each decision its own arm node.`
4710
+ );
4711
+ continue;
4712
+ }
4713
+ const edge = incoming[0];
4714
+ const key = `${edge.from}\0${binding.arm}`;
4715
+ const already = declaredBy.get(key);
4716
+ if (already !== void 0) {
4717
+ errors.push(
4718
+ `Decision "${edge.from}" has two arms declaring arm:"${binding.arm}" ("${already}" and "${binding.nodeId}"). A conditional has one run arm and one else arm.`
4719
+ );
4720
+ continue;
4721
+ }
4722
+ declaredBy.set(key, binding.nodeId);
4723
+ edge.arm = binding.arm;
4724
+ }
4725
+ }
4726
+ function attachBindingsToBlocks(sourceCode, blocks, bindings, errors) {
4727
+ const lineStarts = sourceLineStartsForDocflow(sourceCode);
4728
+ const exportRanges = playExportSourceRanges(sourceCode);
4729
+ for (const binding of bindings) {
4730
+ let owners = blocks.filter((block) => block.nodes.has(binding.nodeId));
4731
+ if (owners.length === 0) {
4732
+ errors.push(
4733
+ `Docflow annotation "${binding.nodeId}" does not name a graph node.`
4734
+ );
4735
+ continue;
4736
+ }
4737
+ if (owners.length > 1) {
4738
+ const lineStart = lineStarts[binding.line - 1];
4739
+ if (lineStart !== void 0) {
4740
+ const scoped = owners.filter(
4741
+ (owner2) => exportRanges.some(
4742
+ (range) => range.exportName === owner2.exportName && lineStart >= range.start && lineStart < range.end
4743
+ )
4744
+ );
4745
+ if (scoped.length === 1) owners = scoped;
4746
+ }
4747
+ }
4748
+ if (owners.length > 1) {
4749
+ errors.push(
4750
+ `Docflow annotation "${binding.nodeId}" names a node in ${owners.map((owner2) => `\`${blockLabel(owner2)}\``).join(" and ")}, and its enclosing play could not identify one owner. Put the annotation inside the definePlay handler for the export it describes.`
4751
+ );
4752
+ continue;
4753
+ }
4754
+ const owner = owners[0];
4755
+ if (owner.bindings.some((candidate) => candidate.nodeId === binding.nodeId)) {
4756
+ errors.push(
4757
+ `Docflow node "${binding.nodeId}" has more than one code binding.`
4758
+ );
4759
+ continue;
4760
+ }
4761
+ owner.bindings.push(binding);
4762
+ }
4763
+ for (const block of blocks) {
4764
+ for (const binding of block.bindings) {
4765
+ const node = block.nodes.get(binding.nodeId);
4766
+ if (binding.label) node.label = binding.label;
4767
+ if (binding.kind) node.kind = binding.kind;
4768
+ }
4769
+ projectRecordedArms(block, errors);
4770
+ if (block.syntax !== "docflow") continue;
4771
+ const bound = new Set(block.bindings.map((binding) => binding.nodeId));
4772
+ for (const node of block.nodes.values()) {
4773
+ if (node.kind !== "conceptual" && !bound.has(node.id)) {
4774
+ errors.push(
4775
+ `Docflow node "${node.id}" has no code binding; mark it type:"conceptual" or bind it.`
4776
+ );
4777
+ }
4778
+ }
4779
+ }
4780
+ }
4781
+ function sourceLineStartsForDocflow(sourceCode) {
4782
+ const starts = [0];
4783
+ for (let index = 0; index < sourceCode.length; index += 1) {
4784
+ if (sourceCode[index] === "\n") starts.push(index + 1);
4785
+ }
4786
+ return starts;
4787
+ }
4788
+ function parseDocflowBindings(sourceCode, errors) {
4789
+ const lines = sourceCode.split(/\r?\n/);
4790
+ const bindings = [];
4791
+ for (let index = 0; index < lines.length; index += 1) {
4792
+ PUT.lastIndex = 0;
4793
+ MERMAID_NODE.lastIndex = 0;
4794
+ const legacyMatch = PUT.exec(lines[index]);
4795
+ const mermaidMatch = MERMAID_NODE.exec(lines[index]);
4796
+ if (!legacyMatch && !mermaidMatch) continue;
4797
+ const attributes = parseAttributes(
4798
+ mermaidMatch ? mermaidMatch[2] ?? "" : legacyMatch[1],
4799
+ {
4800
+ line: index + 1,
4801
+ annotation: mermaidMatch ? "@mermaid-node" : "put",
4802
+ errors
4803
+ }
4804
+ );
4805
+ if (!attributes) continue;
4806
+ const id = (mermaidMatch ? mermaidMatch[1] : attributes.id)?.trim();
4807
+ if (!id) {
4808
+ errors.push(`Docflow annotation on line ${index + 1} requires id:"\u2026".`);
4809
+ continue;
4810
+ }
4811
+ const kind = nodeKind(attributes.type);
4812
+ if (attributes.type && !kind) {
4813
+ errors.push(
4814
+ `Docflow annotation "${id}" has unsupported type "${attributes.type}". Valid types: ${PLAY_DOCFLOW_NODE_KINDS.map((nodeType) => `"${nodeType}"`).join(", ")}.`
4815
+ );
4816
+ continue;
4817
+ }
4818
+ const arm = nodeArm(attributes.arm);
4819
+ if (attributes.arm && !arm) {
4820
+ errors.push(
4821
+ `Docflow annotation "${id}" has unsupported arm "${attributes.arm}". Valid arms: ${PLAY_DOCFLOW_ARMS.map((token) => `"${token}"`).join(", ")}. The arm names which side of the conditional this node is, not what the edge is labelled.`
4822
+ );
4823
+ continue;
4824
+ }
4825
+ let nextLine = index + 1;
4826
+ while (nextLine < lines.length && (!lines[nextLine].trim() || lines[nextLine].trim().startsWith("//")))
4827
+ nextLine += 1;
4828
+ if (nextLine === lines.length) {
4829
+ errors.push(
4830
+ `Docflow annotation "${id}" must be followed by executable code.`
4831
+ );
4832
+ continue;
4833
+ }
4834
+ const inputs = parseContractPaths(
4835
+ attributes.in,
4836
+ "in",
4837
+ id,
4838
+ index + 1,
4839
+ errors
4840
+ );
4841
+ const outputs = parseContractPaths(
4842
+ attributes.out,
4843
+ "out",
4844
+ id,
4845
+ index + 1,
4846
+ errors
4847
+ );
4848
+ if (attributes.in !== void 0 && !inputs || attributes.out !== void 0 && !outputs) {
4849
+ continue;
4850
+ }
4851
+ const inferred = inputs || outputs ? {
4852
+ ...inputs ? { inputs } : {},
4853
+ ...outputs ? { outputs } : {},
4854
+ ioConfidence: "explicit"
4855
+ } : inferBindingIo(lines[nextLine]);
4856
+ bindings.push({
4857
+ nodeId: id,
4858
+ line: nextLine + 1,
4859
+ ...attributes.label ? { label: attributes.label } : {},
4860
+ ...kind ? { kind } : {},
4861
+ // Spread, never `arm: arm ?? undefined` — see `PlayDocflowEdge.arm`. An
4862
+ // explicit `undefined` key survives `JSON.stringify` round-trips in some
4863
+ // callers and would reintroduce the hash drift this omission avoids.
4864
+ ...arm ? { arm } : {},
4865
+ ...inferred
4866
+ });
4867
+ }
4868
+ return bindings;
4869
+ }
4870
+
4871
+ // ../shared_libs/plays/docflow-binding.ts
4872
+ function sourceLineStarts(sourceCode) {
4873
+ const starts = [0];
4874
+ for (let index = 0; index < sourceCode.length; index += 1) {
4875
+ if (sourceCode[index] === "\n") starts.push(index + 1);
4876
+ }
4877
+ return starts;
4878
+ }
4879
+ function astNodeBounds(node) {
4880
+ return typeof node.start === "number" && typeof node.end === "number" ? { start: node.start, end: node.end } : null;
4881
+ }
4882
+ function findDocflowBoundStatement(ast, line, lineStarts) {
4883
+ const lineStart = lineStarts[line - 1];
4884
+ const lineEnd = lineStarts[line] ?? Number.POSITIVE_INFINITY;
4885
+ if (lineStart === void 0) return null;
4886
+ let match = null;
4887
+ const pending = [ast];
4888
+ const statementTypes = /* @__PURE__ */ new Set([
4889
+ "ExpressionStatement",
4890
+ "IfStatement",
4891
+ "ReturnStatement",
4892
+ "ThrowStatement",
4893
+ "VariableDeclaration"
4894
+ ]);
4895
+ while (pending.length > 0) {
4896
+ const node = pending.pop();
4897
+ const bounds = astNodeBounds(node);
4898
+ if (bounds && bounds.start < lineEnd && bounds.end > lineStart && statementTypes.has(node.type)) {
4899
+ if (!match || bounds.end - bounds.start < (astNodeBounds(match)?.end ?? 0) - (astNodeBounds(match)?.start ?? 0)) {
4900
+ match = node;
4901
+ }
4902
+ }
4903
+ for (const child of Object.values(node)) {
4904
+ if (Array.isArray(child)) pending.push(...child.filter(isAstNode));
4905
+ else if (isAstNode(child)) pending.push(child);
4906
+ }
4907
+ }
4908
+ return match;
4909
+ }
4910
+ var DOCFLOW_STATEMENT_TYPES = /* @__PURE__ */ new Set([
4911
+ "ExpressionStatement",
4912
+ "IfStatement",
4913
+ "ReturnStatement",
4914
+ "ThrowStatement",
4915
+ "VariableDeclaration"
4916
+ ]);
4917
+ function docflowOutputRoot(binding) {
4918
+ const first = binding.outputs?.[0];
4919
+ if (!first) return null;
4920
+ const root = first.split(".")[0];
4921
+ if (!root || root === "$output") return null;
4922
+ return root;
4923
+ }
4924
+ function buildDocflowParentIndex(ast) {
4925
+ const parents = /* @__PURE__ */ new Map();
4926
+ const pending = [ast];
4927
+ while (pending.length > 0) {
4928
+ const node = pending.pop();
4929
+ for (const child of Object.values(node)) {
4930
+ const children = Array.isArray(child) ? child.filter(isAstNode) : isAstNode(child) ? [child] : [];
4931
+ for (const nested of children) {
4932
+ parents.set(nested, node);
4933
+ pending.push(nested);
4934
+ }
4935
+ }
4936
+ }
4937
+ return parents;
4938
+ }
4939
+ function enclosingDocflowStatement(node, parents) {
4940
+ let current = node;
4941
+ while (current) {
4942
+ if (DOCFLOW_STATEMENT_TYPES.has(current.type)) return current;
4943
+ current = parents.get(current) ?? null;
4944
+ }
4945
+ return null;
4946
+ }
4947
+ function isWithColumnCallForName(node, columnName) {
4948
+ if (node.type !== "CallExpression") return false;
4949
+ const callee = isAstNode(node.callee) ? node.callee : null;
4950
+ if (!callee || callee.type !== "MemberExpression" || callee.computed || !isAstNode(callee.property) || callee.property.type !== "Identifier" || callee.property.name !== "withColumn") {
4951
+ return false;
4952
+ }
4953
+ const firstArg = astArray(node.arguments)[0];
4954
+ return Boolean(
4955
+ firstArg && firstArg.type === "Literal" && typeof firstArg.value === "string" && firstArg.value === columnName
4956
+ );
4957
+ }
4958
+ function isDocflowStepCallForName(node, stepName) {
4959
+ if (node.type !== "CallExpression") return false;
4960
+ const callee = isAstNode(node.callee) ? node.callee : null;
4961
+ if (!callee || callee.type !== "MemberExpression" || callee.computed || !isAstNode(callee.property) || callee.property.type !== "Identifier" || callee.property.name !== "step") {
4962
+ return false;
4963
+ }
4964
+ const firstArg = astArray(node.arguments)[0];
4965
+ return Boolean(
4966
+ firstArg && firstArg.type === "Literal" && typeof firstArg.value === "string" && firstArg.value === stepName
4967
+ );
4968
+ }
4969
+ function docflowExpressionWrapsStepBuilder(expression, stepName) {
4970
+ const unwrapped = expression.type === "AwaitExpression" && isAstNode(expression.argument) ? expression.argument : expression;
4971
+ if (isDocflowStepCallForName(unwrapped, stepName)) return false;
4972
+ const pending = [unwrapped];
4973
+ while (pending.length > 0) {
4974
+ const node = pending.pop();
4975
+ if (node !== unwrapped && isDocflowStepCallForName(node, stepName)) {
4976
+ return true;
4977
+ }
4978
+ for (const child of Object.values(node)) {
4979
+ if (Array.isArray(child)) pending.push(...child.filter(isAstNode));
4980
+ else if (isAstNode(child)) pending.push(child);
4981
+ }
4982
+ }
4983
+ return false;
4984
+ }
4985
+ function getIdentifierName2(node) {
4986
+ return isAstNode(node) && node.type === "Identifier" ? typeof node.name === "string" ? node.name : null : null;
4987
+ }
4988
+ function enclosingDefinePlayCall(ast, line, lineStarts) {
4989
+ const lineStart = lineStarts[line - 1];
4990
+ const lineEnd = lineStarts[line] ?? Number.POSITIVE_INFINITY;
4991
+ if (lineStart === void 0) return null;
4992
+ let match = null;
4993
+ const pending = [ast];
4994
+ while (pending.length > 0) {
4995
+ const node = pending.pop();
4996
+ const bounds = astNodeBounds(node);
4997
+ if (bounds && bounds.start < lineEnd && bounds.end > lineStart && isDefinePlayCall(node)) {
4998
+ const matchBounds = match ? astNodeBounds(match) : null;
4999
+ if (!match || !matchBounds || bounds.end - bounds.start < matchBounds.end - matchBounds.start) {
5000
+ match = node;
5001
+ }
5002
+ }
5003
+ for (const child of Object.values(node)) {
5004
+ if (Array.isArray(child)) pending.push(...child.filter(isAstNode));
5005
+ else if (isAstNode(child)) pending.push(child);
5006
+ }
5007
+ }
5008
+ return match;
5009
+ }
5010
+ function isVariableDeclarationForName(node, name) {
5011
+ if (node.type !== "VariableDeclaration") return false;
5012
+ return astArray(node.declarations).some(
5013
+ (declarator) => getIdentifierName2(declarator.id) === name
5014
+ );
5015
+ }
5016
+ function resolveDocflowBindingSymbol(ast, binding, lineStarts, parents) {
5017
+ const root = docflowOutputRoot(binding);
5018
+ if (!root) return { kind: "abstain" };
5019
+ const parentIndex = parents ?? buildDocflowParentIndex(ast);
5020
+ const scope = enclosingDefinePlayCall(ast, binding.line, lineStarts) ?? ast;
5021
+ const inScope = (node) => {
5022
+ if (scope === ast) return true;
5023
+ const bounds = astNodeBounds(node);
5024
+ const scopeBounds = astNodeBounds(scope);
5025
+ return Boolean(
5026
+ bounds && scopeBounds && bounds.start >= scopeBounds.start && bounds.end <= scopeBounds.end
5027
+ );
5028
+ };
5029
+ const matchesColumn = (node) => isWithColumnCallForName(node, root) && inScope(node);
5030
+ const matchesStep = (node) => isDocflowStepCallForName(node, root) && inScope(node);
5031
+ const matchesVariable = (node) => isVariableDeclarationForName(node, root) && inScope(node);
5032
+ const collectStatements = (predicate) => {
5033
+ const statements = /* @__PURE__ */ new Map();
5034
+ const pending = [scope];
5035
+ while (pending.length > 0) {
5036
+ const node = pending.pop();
5037
+ if (predicate(node)) {
5038
+ const statement = enclosingDocflowStatement(node, parentIndex);
5039
+ const kind = matchesColumn(node) ? "declaredColumn" : matchesStep(node) ? "declaredStep" : "variable";
5040
+ if (statement && (!statements.has(statement) || kind === "declaredColumn")) {
5041
+ statements.set(statement, kind);
5042
+ }
5043
+ }
5044
+ for (const child of Object.values(node)) {
5045
+ if (Array.isArray(child)) pending.push(...child.filter(isAstNode));
5046
+ else if (isAstNode(child)) pending.push(child);
5047
+ }
5048
+ }
5049
+ return statements;
5050
+ };
5051
+ let candidates = collectStatements(
5052
+ (node) => matchesColumn(node) || matchesStep(node)
5053
+ );
5054
+ if (candidates.size === 0) candidates = collectStatements(matchesVariable);
5055
+ if (candidates.size === 0) return { kind: "abstain" };
5056
+ const resolved = (statement) => ({
5057
+ kind: "resolved",
5058
+ statement,
5059
+ symbolKind: candidates.get(statement) ?? "variable"
5060
+ });
5061
+ const lineStart = lineStarts[binding.line - 1];
5062
+ const lineEnd = lineStarts[binding.line] ?? Number.POSITIVE_INFINITY;
5063
+ if (lineStart !== void 0) {
5064
+ const containing = [...candidates.keys()].filter((statement) => {
5065
+ const bounds = astNodeBounds(statement);
5066
+ return bounds && bounds.start < lineEnd && bounds.end > lineStart;
5067
+ });
5068
+ if (containing.length === 1) return resolved(containing[0]);
5069
+ if (containing.length > 1) {
5070
+ return { kind: "ambiguous", candidates: containing };
5071
+ }
5072
+ }
5073
+ if (candidates.size === 1) {
5074
+ return resolved([...candidates.keys()][0]);
5075
+ }
5076
+ return { kind: "ambiguous", candidates: [...candidates.keys()] };
5077
+ }
5078
+ function statementLine(statement, lineStarts) {
5079
+ const bounds = statement ? astNodeBounds(statement) : null;
5080
+ if (!bounds) return null;
5081
+ let line = 1;
5082
+ for (let index = 0; index < lineStarts.length; index += 1) {
5083
+ if (lineStarts[index] > bounds.start) break;
5084
+ line = index + 1;
5085
+ }
5086
+ return line;
5087
+ }
5088
+ function resolveDocflowBinding(ast, binding, lineStarts, parents) {
5089
+ const positional = findDocflowBoundStatement(ast, binding.line, lineStarts);
5090
+ const symbol = resolveDocflowBindingSymbol(ast, binding, lineStarts, parents);
5091
+ const root = docflowOutputRoot(binding);
5092
+ if (symbol.kind === "ambiguous") {
5093
+ return {
5094
+ binding,
5095
+ statement: positional,
5096
+ symbolKind: null,
5097
+ drift: {
5098
+ binding,
5099
+ symbol: root ?? "",
5100
+ annotatedLine: statementLine(positional, lineStarts),
5101
+ symbolLine: statementLine(symbol.candidates[0] ?? null, lineStarts),
5102
+ ambiguous: true
5103
+ }
5104
+ };
5105
+ }
5106
+ if (symbol.kind === "resolved") {
5107
+ const drift = positional && positional !== symbol.statement ? {
5108
+ binding,
5109
+ symbol: root ?? "",
5110
+ annotatedLine: statementLine(positional, lineStarts),
5111
+ symbolLine: statementLine(symbol.statement, lineStarts),
5112
+ ambiguous: false
5113
+ } : null;
5114
+ return {
5115
+ binding,
5116
+ statement: symbol.statement,
5117
+ symbolKind: symbol.symbolKind,
5118
+ drift
5119
+ };
5120
+ }
5121
+ return { binding, statement: positional, symbolKind: null, drift: null };
5122
+ }
5123
+
3992
5124
  // ../shared_libs/plays/secret-guardrails.ts
3993
5125
  var SECRET_ENV_PATTERN = /\bprocess(?:\.env|\[['"]env['"]\])(?:\.|\[['"])([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|PRIVATE[_-]?KEY|ACCESS[_-]?KEY)[A-Z0-9_]*)(?:['"]\])?/g;
3994
5126
  var PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----/;
@@ -4029,7 +5161,7 @@ function validatePlaySourceFilesHaveNoInlineSecrets(sourceFiles) {
4029
5161
  var MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
4030
5162
 
4031
5163
  // ../shared_libs/plays/bundling/index.ts
4032
- var PLAY_BUNDLE_CACHE_VERSION = 28;
5164
+ var PLAY_BUNDLE_CACHE_VERSION = 33;
4033
5165
  var PLAY_ARTIFACT_CACHE_DIR = join(
4034
5166
  tmpdir(),
4035
5167
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`
@@ -4188,15 +5320,6 @@ function findSourceImportReferences(sourceCode) {
4188
5320
  function extractDefinedPlayName(sourceCode) {
4189
5321
  return extractDefinedPlayMetadata(sourceCode, null)?.name ?? null;
4190
5322
  }
4191
- var TypeScriptParser = Parser.extend(
4192
- tsPlugin({
4193
- allowSatisfies: true,
4194
- jsx: {
4195
- allowNamespaces: true,
4196
- allowNamespacedObjects: true
4197
- }
4198
- })
4199
- );
4200
5323
  function parsePlaySourceAst(sourceCode) {
4201
5324
  try {
4202
5325
  return TypeScriptParser.parse(sourceCode, {
@@ -4213,7 +5336,7 @@ function parsePlaySourceAst(sourceCode) {
4213
5336
  legalComments: "none",
4214
5337
  sourcemap: false
4215
5338
  }).code;
4216
- return Parser.parse(transformed, {
5339
+ return Parser2.parse(transformed, {
4217
5340
  ecmaVersion: "latest",
4218
5341
  sourceType: "module",
4219
5342
  allowHashBang: true
@@ -4223,17 +5346,11 @@ function parsePlaySourceAst(sourceCode) {
4223
5346
  }
4224
5347
  }
4225
5348
  }
4226
- function getIdentifierName(node) {
5349
+ function getIdentifierName3(node) {
4227
5350
  return isAstNode(node) && node.type === "Identifier" ? typeof node.name === "string" ? node.name : null : null;
4228
5351
  }
4229
- function isAstNode(value) {
4230
- return value !== null && typeof value === "object" && "type" in value;
4231
- }
4232
- function astArray(value) {
4233
- return Array.isArray(value) ? value.filter(isAstNode) : [];
4234
- }
4235
5352
  function memberExpressionPath(node) {
4236
- const expression = unwrapStaticExpression(node);
5353
+ const expression = unwrapStaticExpression2(node);
4237
5354
  if (!expression) return null;
4238
5355
  if (expression.type === "Identifier" && typeof expression.name === "string") {
4239
5356
  return [expression.name];
@@ -4291,7 +5408,7 @@ function buildPlayMetadataContext(ast) {
4291
5408
  for (const statement of astArray(ast.body)) {
4292
5409
  if (statement.type === "VariableDeclaration") {
4293
5410
  for (const declaration of astArray(statement.declarations)) {
4294
- const name = getIdentifierName(declaration.id);
5411
+ const name = getIdentifierName3(declaration.id);
4295
5412
  if (!name) continue;
4296
5413
  const initializer = isAstNode(declaration.init) ? declaration.init : null;
4297
5414
  declarations.set(
@@ -4314,7 +5431,7 @@ function buildPlayMetadataContext(ast) {
4314
5431
  for (const declaration of astArray(
4315
5432
  statement.declaration.declarations
4316
5433
  )) {
4317
- const name = getIdentifierName(declaration.id);
5434
+ const name = getIdentifierName3(declaration.id);
4318
5435
  if (!name) continue;
4319
5436
  const initializer = isAstNode(declaration.init) ? declaration.init : null;
4320
5437
  declarations.set(
@@ -4328,8 +5445,8 @@ function buildPlayMetadataContext(ast) {
4328
5445
  }
4329
5446
  }
4330
5447
  for (const specifier of astArray(statement.specifiers)) {
4331
- const localName = getIdentifierName(specifier.local);
4332
- const exportedName = getIdentifierName(specifier.exported) ?? (isAstNode(specifier.exported) && specifier.exported.type === "Literal" && typeof specifier.exported.value === "string" ? specifier.exported.value : null);
5448
+ const localName = getIdentifierName3(specifier.local);
5449
+ const exportedName = getIdentifierName3(specifier.exported) ?? (isAstNode(specifier.exported) && specifier.exported.type === "Literal" && typeof specifier.exported.value === "string" ? specifier.exported.value : null);
4333
5450
  if (localName && exportedName) {
4334
5451
  namedExports.set(exportedName, localName);
4335
5452
  }
@@ -4346,7 +5463,7 @@ function buildPlayMetadataContext(ast) {
4346
5463
  }
4347
5464
  return { declarations, namedExports, commonJsExports, defaultExport };
4348
5465
  }
4349
- function unwrapStaticExpression(node) {
5466
+ function unwrapStaticExpression2(node) {
4350
5467
  let current = node ?? null;
4351
5468
  while (current && (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "ParenthesizedExpression")) {
4352
5469
  current = isAstNode(current.expression) ? current.expression : null;
@@ -4354,7 +5471,7 @@ function unwrapStaticExpression(node) {
4354
5471
  return current;
4355
5472
  }
4356
5473
  function staticStringFromExpression(node, context, seen = /* @__PURE__ */ new Set(), trimResult = true) {
4357
- const expression = unwrapStaticExpression(node);
5474
+ const expression = unwrapStaticExpression2(node);
4358
5475
  if (!expression) return null;
4359
5476
  if (expression.type === "Literal") {
4360
5477
  const value = expression.value;
@@ -4384,7 +5501,7 @@ function staticStringFromExpression(node, context, seen = /* @__PURE__ */ new Se
4384
5501
  if (!value?.trim()) return null;
4385
5502
  return trimResult ? value.trim() : value;
4386
5503
  }
4387
- const identifier = getIdentifierName(expression);
5504
+ const identifier = getIdentifierName3(expression);
4388
5505
  if (!identifier || seen.has(identifier)) return null;
4389
5506
  seen.add(identifier);
4390
5507
  return staticStringFromExpression(
@@ -4409,10 +5526,10 @@ function propertyNameFromKey(property) {
4409
5526
  return null;
4410
5527
  }
4411
5528
  function objectExpressionFromNode(node, context, seen = /* @__PURE__ */ new Set()) {
4412
- const expression = unwrapStaticExpression(node);
5529
+ const expression = unwrapStaticExpression2(node);
4413
5530
  if (!expression) return null;
4414
5531
  if (expression.type === "ObjectExpression") return expression;
4415
- const identifier = getIdentifierName(expression);
5532
+ const identifier = getIdentifierName3(expression);
4416
5533
  if (!identifier || seen.has(identifier)) return null;
4417
5534
  seen.add(identifier);
4418
5535
  return objectExpressionFromNode(
@@ -4479,12 +5596,12 @@ function staticPropertyNames(node, context, ancestors = /* @__PURE__ */ new Set(
4479
5596
  return names;
4480
5597
  }
4481
5598
  function staticNumberFromExpression(node, context, seen = /* @__PURE__ */ new Set()) {
4482
- const expression = unwrapStaticExpression(node);
5599
+ const expression = unwrapStaticExpression2(node);
4483
5600
  if (!expression) return null;
4484
5601
  if (expression.type === "Literal" && typeof expression.value === "number") {
4485
5602
  return expression.value;
4486
5603
  }
4487
- const identifier = getIdentifierName(expression);
5604
+ const identifier = getIdentifierName3(expression);
4488
5605
  if (!identifier || seen.has(identifier)) return null;
4489
5606
  seen.add(identifier);
4490
5607
  return staticNumberFromExpression(
@@ -4573,7 +5690,7 @@ function stringPropertyFromObjectExpression(node, propertyName, context, seenObj
4573
5690
  return value;
4574
5691
  }
4575
5692
  function isDefinePlayCallee(node) {
4576
- const callee = unwrapStaticExpression(node);
5693
+ const callee = unwrapStaticExpression2(node);
4577
5694
  if (!callee) return false;
4578
5695
  if (callee.type === "Identifier" && (callee.name === "definePlay" || callee.name === "defineWorkflow")) {
4579
5696
  return true;
@@ -4585,7 +5702,7 @@ function isDefinePlayCallee(node) {
4585
5702
  return false;
4586
5703
  }
4587
5704
  function playMetadataFromDefinePlayCall(node, context) {
4588
- const expression = unwrapStaticExpression(node);
5705
+ const expression = unwrapStaticExpression2(node);
4589
5706
  if (!expression || expression.type !== "CallExpression" || !isDefinePlayCallee(isAstNode(expression.callee) ? expression.callee : null)) {
4590
5707
  return null;
4591
5708
  }
@@ -4631,13 +5748,13 @@ function resolveExportExpression(exportName, context) {
4631
5748
  if (exportName === "default") {
4632
5749
  const commonJsDefault = context.commonJsExports.get("default");
4633
5750
  if (commonJsDefault) {
4634
- const commonJsDefaultIdentifier = getIdentifierName(
4635
- unwrapStaticExpression(commonJsDefault)
5751
+ const commonJsDefaultIdentifier = getIdentifierName3(
5752
+ unwrapStaticExpression2(commonJsDefault)
4636
5753
  );
4637
5754
  return commonJsDefaultIdentifier ? context.declarations.get(commonJsDefaultIdentifier) ?? commonJsDefault : commonJsDefault;
4638
5755
  }
4639
- const defaultExpression = unwrapStaticExpression(context.defaultExport);
4640
- const defaultIdentifier = getIdentifierName(defaultExpression);
5756
+ const defaultExpression = unwrapStaticExpression2(context.defaultExport);
5757
+ const defaultIdentifier = getIdentifierName3(defaultExpression);
4641
5758
  if (!defaultIdentifier) {
4642
5759
  const defaultExportName = context.namedExports.get("default");
4643
5760
  return defaultExportName ? context.declarations.get(defaultExportName) ?? null : defaultExpression;
@@ -4647,8 +5764,8 @@ function resolveExportExpression(exportName, context) {
4647
5764
  if (exportName) {
4648
5765
  const commonJsExport = context.commonJsExports.get(exportName);
4649
5766
  if (commonJsExport) {
4650
- const commonJsExportIdentifier = getIdentifierName(
4651
- unwrapStaticExpression(commonJsExport)
5767
+ const commonJsExportIdentifier = getIdentifierName3(
5768
+ unwrapStaticExpression2(commonJsExport)
4652
5769
  );
4653
5770
  return commonJsExportIdentifier ? context.declarations.get(commonJsExportIdentifier) ?? commonJsExport : commonJsExport;
4654
5771
  }
@@ -4674,7 +5791,7 @@ function extractDefinedPlayMetadata(sourceCode, exportName) {
4674
5791
  context
4675
5792
  );
4676
5793
  if (directDefaultMetadata) return directDefaultMetadata;
4677
- const defaultIdentifier = getIdentifierName(context.defaultExport);
5794
+ const defaultIdentifier = getIdentifierName3(context.defaultExport);
4678
5795
  if (defaultIdentifier) {
4679
5796
  const defaultMetadata = playMetadataFromDefinePlayCall(
4680
5797
  context.declarations.get(defaultIdentifier),
@@ -4765,6 +5882,201 @@ function localSdkAliasPlugin(adapter) {
4765
5882
  }
4766
5883
  };
4767
5884
  }
5885
+ function sourceLoaderForPath(path) {
5886
+ const extension = extname(path).toLowerCase();
5887
+ if (extension === ".tsx") return "tsx";
5888
+ if (extension === ".jsx") return "jsx";
5889
+ if (extension === ".js" || extension === ".mjs" || extension === ".cjs") {
5890
+ return "js";
5891
+ }
5892
+ return "ts";
5893
+ }
5894
+ function docflowContextIdentifierFromCall(call) {
5895
+ if (!isDefinePlayCallExpression(call)) return null;
5896
+ const args = astArray(call.arguments);
5897
+ const callback = args.find(
5898
+ (argument) => argument.type === "ArrowFunctionExpression" || argument.type === "FunctionExpression"
5899
+ );
5900
+ if (!callback) return null;
5901
+ return getIdentifierName3(astArray(callback.params)[0]);
5902
+ }
5903
+ function findDocflowContextIdentifier(input) {
5904
+ let owner = input.statement;
5905
+ while (owner) {
5906
+ const contextName = docflowContextIdentifierFromCall(owner);
5907
+ if (contextName) return contextName;
5908
+ owner = input.parents.get(owner) ?? null;
5909
+ }
5910
+ const lineStart = input.lineStarts[input.bindingLine - 1];
5911
+ const lineEnd = input.lineStarts[input.bindingLine] ?? Number.POSITIVE_INFINITY;
5912
+ if (lineStart === void 0) return null;
5913
+ let best = null;
5914
+ const pending = [input.ast];
5915
+ while (pending.length > 0) {
5916
+ const value = pending.pop();
5917
+ if (!isAstNode(value)) continue;
5918
+ if (isDefinePlayCallExpression(value)) {
5919
+ const bounds = astNodeBounds(value);
5920
+ if (bounds && bounds.start < lineEnd && bounds.end > lineStart && (!best || bounds.end - bounds.start < (astNodeBounds(best)?.end ?? 0) - (astNodeBounds(best)?.start ?? 0))) {
5921
+ best = value;
5922
+ }
5923
+ }
5924
+ for (const child of Object.values(value)) {
5925
+ if (Array.isArray(child)) pending.push(...child);
5926
+ else if (isAstNode(child)) pending.push(child);
5927
+ }
5928
+ }
5929
+ return best ? docflowContextIdentifierFromCall(best) : null;
5930
+ }
5931
+ function docflowInputCaptureSource(paths) {
5932
+ return `[${paths.map((path) => {
5933
+ const [root, ...properties] = path.split(".");
5934
+ return `{path:${JSON.stringify(path)},readRoot:()=>${root},properties:${JSON.stringify(properties)}}`;
5935
+ }).join(",")}]`;
5936
+ }
5937
+ function docflowObservationTarget(statement) {
5938
+ if (!statement) return null;
5939
+ let expression = null;
5940
+ if (statement.type === "VariableDeclaration") {
5941
+ const declarations = astArray(statement.declarations);
5942
+ if (declarations.length === 1 && isAstNode(declarations[0].init)) {
5943
+ expression = declarations[0].init;
5944
+ }
5945
+ } else if (statement.type === "ReturnStatement" && isAstNode(statement.argument)) {
5946
+ expression = statement.argument;
5947
+ } else if (statement.type === "ExpressionStatement" && isAstNode(statement.expression)) {
5948
+ expression = statement.expression;
5949
+ } else if (statement.type === "IfStatement" && isAstNode(statement.test)) {
5950
+ expression = statement.test;
5951
+ }
5952
+ if (!expression) return null;
5953
+ const bounds = astNodeBounds(expression);
5954
+ if (!bounds) return null;
5955
+ return {
5956
+ ...bounds,
5957
+ awaitsResult: expressionContainsTopLevelAwait(expression),
5958
+ expression
5959
+ };
5960
+ }
5961
+ function docflowObservedExpression(input) {
5962
+ const callback = input.awaitsResult ? `async()=>(${input.expression})` : `()=>(${input.expression})`;
5963
+ const observation = `${input.contextName}.__deeplineObserveDocflowNode(${JSON.stringify(input.nodeId)},${docflowInputCaptureSource(input.inputs)},${JSON.stringify(input.outputs)},${callback})`;
5964
+ return input.awaitsResult ? `await ${observation}` : observation;
5965
+ }
5966
+ function expressionContainsTopLevelAwait(expression) {
5967
+ const pending = [expression];
5968
+ while (pending.length > 0) {
5969
+ const node = pending.pop();
5970
+ if (node.type === "AwaitExpression") return true;
5971
+ if (node !== expression && (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration")) {
5972
+ continue;
5973
+ }
5974
+ for (const child of Object.values(node)) {
5975
+ if (Array.isArray(child)) pending.push(...child.filter(isAstNode));
5976
+ else if (isAstNode(child)) pending.push(child);
5977
+ }
5978
+ }
5979
+ return false;
5980
+ }
5981
+ function instrumentPlayDocflowRuntimeHits(sourceCode) {
5982
+ const parsed = parsePlayDocflowFile(sourceCode);
5983
+ if (parsed.blocks.length === 0 || parsed.errors.length > 0) return sourceCode;
5984
+ const ast = parsePlaySourceAst(sourceCode);
5985
+ if (!ast) return sourceCode;
5986
+ const lineStarts = sourceLineStarts(sourceCode);
5987
+ const replacements = [];
5988
+ const targets = [];
5989
+ const parents = buildDocflowParentIndex(ast);
5990
+ for (const binding of parsed.bindings) {
5991
+ const resolution = resolveDocflowBinding(ast, binding, lineStarts, parents);
5992
+ const contextName = findDocflowContextIdentifier({
5993
+ ast,
5994
+ statement: resolution.statement,
5995
+ bindingLine: binding.line,
5996
+ lineStarts,
5997
+ parents
5998
+ });
5999
+ if (!contextName) continue;
6000
+ const target = docflowObservationTarget(resolution.statement);
6001
+ const observedRoot = docflowOutputRoot(binding);
6002
+ if (target && observedRoot && docflowExpressionWrapsStepBuilder(target.expression, observedRoot)) {
6003
+ continue;
6004
+ }
6005
+ if (target) {
6006
+ targets.push({ ...target, contextName, binding });
6007
+ continue;
6008
+ }
6009
+ const lineStart = lineStarts[binding.line - 1];
6010
+ if (lineStart !== void 0) {
6011
+ const indentationLength = /^\s*/.exec(sourceCode.slice(lineStart))?.[0].length ?? 0;
6012
+ replacements.push({
6013
+ start: lineStart + indentationLength,
6014
+ end: lineStart + indentationLength,
6015
+ value: `await ${contextName}.__deeplineDocflowHit(${JSON.stringify(binding.nodeId)}); `
6016
+ });
6017
+ }
6018
+ }
6019
+ targets.sort(
6020
+ (left, right) => left.start - right.start || right.end - left.end
6021
+ );
6022
+ const groups = [];
6023
+ for (const target of targets) {
6024
+ const open = groups[groups.length - 1];
6025
+ if (open && target.start < open.end) {
6026
+ open.bindings.push(target.binding);
6027
+ } else {
6028
+ groups.push({
6029
+ start: target.start,
6030
+ end: target.end,
6031
+ awaitsResult: target.awaitsResult,
6032
+ contextName: target.contextName,
6033
+ bindings: [target.binding]
6034
+ });
6035
+ }
6036
+ }
6037
+ for (const group of groups) {
6038
+ let expression = sourceCode.slice(group.start, group.end);
6039
+ for (const binding of [...group.bindings].reverse()) {
6040
+ expression = docflowObservedExpression({
6041
+ contextName: group.contextName,
6042
+ nodeId: binding.nodeId,
6043
+ inputs: binding.inputs ?? [],
6044
+ outputs: binding.outputs ?? [],
6045
+ expression,
6046
+ awaitsResult: group.awaitsResult
6047
+ });
6048
+ }
6049
+ replacements.push({
6050
+ start: group.start,
6051
+ end: group.end,
6052
+ value: expression
6053
+ });
6054
+ }
6055
+ return replacements.sort((left, right) => right.start - left.start).reduce(
6056
+ (source, replacement) => `${source.slice(0, replacement.start)}${replacement.value}${source.slice(replacement.end)}`,
6057
+ sourceCode
6058
+ );
6059
+ }
6060
+ function docflowRuntimeInstrumentationPlugin(customerSourceFilePaths) {
6061
+ const customerSourceFiles = new Set(
6062
+ customerSourceFilePaths.filter((path) => extname(path).toLowerCase() !== ".json").map((path) => resolve(path))
6063
+ );
6064
+ return {
6065
+ name: "deepline-docflow-runtime-instrumentation",
6066
+ setup(buildContext) {
6067
+ buildContext.onLoad({ filter: /./ }, (args) => {
6068
+ if (!customerSourceFiles.has(resolve(args.path))) return void 0;
6069
+ return {
6070
+ contents: instrumentPlayDocflowRuntimeHits(
6071
+ readFileSync(args.path, "utf8")
6072
+ ),
6073
+ loader: sourceLoaderForPath(args.path),
6074
+ resolveDir: dirname(args.path)
6075
+ };
6076
+ });
6077
+ }
6078
+ };
6079
+ }
4768
6080
  function buildImportedPlayProxyModule(playName) {
4769
6081
  const serializedName = JSON.stringify(playName);
4770
6082
  return `
@@ -5102,9 +6414,13 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
5102
6414
  artifactKind
5103
6415
  );
5104
6416
  }
5105
- async function runEsbuildForCjsNode(entryFile, _customerSourceFilePaths, importedPlayDependencies, adapter, exportName) {
6417
+ async function runEsbuildForCjsNode(entryFile, customerSourceFilePaths, importedPlayDependencies, adapter, exportName) {
5106
6418
  const sdkAliasPlugin = localSdkAliasPlugin(adapter);
5107
6419
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
6420
+ const docflowPlugin = docflowRuntimeInstrumentationPlugin([
6421
+ entryFile,
6422
+ ...customerSourceFilePaths
6423
+ ]);
5108
6424
  const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
5109
6425
  `;
5110
6426
  const result = await build({
@@ -5128,7 +6444,7 @@ async function runEsbuildForCjsNode(entryFile, _customerSourceFilePaths, importe
5128
6444
  sourcesContent: false,
5129
6445
  logLevel: "silent",
5130
6446
  legalComments: "none",
5131
- plugins: [sdkAliasPlugin, playProxyPlugin].filter(
6447
+ plugins: [docflowPlugin, sdkAliasPlugin, playProxyPlugin].filter(
5132
6448
  (plugin) => plugin != null
5133
6449
  )
5134
6450
  });
@@ -5718,7 +7034,7 @@ async function discoverPackagedLocalFiles(entryFile) {
5718
7034
  }
5719
7035
 
5720
7036
  // src/plays/bundle-play-file.ts
5721
- var PLAY_BUNDLE_CACHE_VERSION2 = 31;
7037
+ var PLAY_BUNDLE_CACHE_VERSION2 = 33;
5722
7038
  var MODULE_DIR = dirname3(fileURLToPath(import.meta.url));
5723
7039
  var SDK_PACKAGE_ROOT = resolve3(MODULE_DIR, "..", "..");
5724
7040
  var SOURCE_REPO_ROOT = resolve3(SDK_PACKAGE_ROOT, "..");