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.
- package/dist/bundling-sources/sdk/src/client.ts +17 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +43 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +30 -1
- package/dist/bundling-sources/shared_libs/play-runtime/cell-provenance.ts +231 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1094 -128
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +178 -9
- package/dist/bundling-sources/shared_libs/play-runtime/docflow-node-io.ts +634 -0
- package/dist/bundling-sources/shared_libs/play-runtime/docflow-observation.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +18 -0
- package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +33 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +251 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-node-scope.ts +160 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +43 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +26 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +83 -0
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +3 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +49 -1
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +375 -29
- package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +636 -0
- package/dist/bundling-sources/shared_libs/plays/docflow-binding.ts +598 -0
- package/dist/bundling-sources/shared_libs/plays/docflow.ts +1645 -0
- package/dist/bundling-sources/shared_libs/plays/play-exports.ts +202 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +16 -1
- package/dist/bundling-sources/shared_libs/plays/ts-ast.ts +48 -0
- package/dist/cli/index.js +994 -312
- package/dist/cli/index.mjs +994 -312
- package/dist/{compiler-manifest-Cj3--4ZJ.d.mts → compiler-manifest-Bl8kmLx9.d.mts} +118 -0
- package/dist/{compiler-manifest-Cj3--4ZJ.d.ts → compiler-manifest-Bl8kmLx9.d.ts} +118 -0
- package/dist/index.d.mts +47 -2
- package/dist/index.d.ts +47 -2
- package/dist/index.js +419 -59
- package/dist/index.mjs +419 -59
- package/dist/install-integrity.json +12 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +1361 -45
- 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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4332
|
-
const exportedName =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5529
|
+
const expression = unwrapStaticExpression2(node);
|
|
4413
5530
|
if (!expression) return null;
|
|
4414
5531
|
if (expression.type === "ObjectExpression") return expression;
|
|
4415
|
-
const identifier =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4635
|
-
|
|
5751
|
+
const commonJsDefaultIdentifier = getIdentifierName3(
|
|
5752
|
+
unwrapStaticExpression2(commonJsDefault)
|
|
4636
5753
|
);
|
|
4637
5754
|
return commonJsDefaultIdentifier ? context.declarations.get(commonJsDefaultIdentifier) ?? commonJsDefault : commonJsDefault;
|
|
4638
5755
|
}
|
|
4639
|
-
const defaultExpression =
|
|
4640
|
-
const defaultIdentifier =
|
|
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 =
|
|
4651
|
-
|
|
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 =
|
|
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,
|
|
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 =
|
|
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, "..");
|