deepline 0.2.49 → 0.2.51
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 +15 -5
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +6 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +61 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +35 -7
- package/dist/bundling-sources/shared_libs/plays/enrich-compat-adapter.ts +21 -0
- package/dist/bundling-sources/shared_libs/plays/enrich-play-compiler.ts +1555 -0
- package/dist/bundling-sources/shared_libs/plays/user-code-safety.ts +61 -0
- package/dist/cli/index.js +111 -15
- package/dist/cli/index.mjs +114 -16
- package/dist/{compiler-manifest-BPA3r-VG.d.mts → compiler-manifest-Cj3--4ZJ.d.mts} +14 -0
- package/dist/{compiler-manifest-BPA3r-VG.d.ts → compiler-manifest-Cj3--4ZJ.d.ts} +14 -0
- package/dist/index.d.mts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/dist/install-integrity.json +5 -2
- package/dist/plays/bundle-play-file.d.mts +4 -2
- package/dist/plays/bundle-play-file.d.ts +4 -2
- package/dist/plays/bundle-play-file.mjs +32 -6
- package/package.json +1 -1
|
@@ -0,0 +1,1555 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getterFromLegacyExtractJs,
|
|
3
|
+
renderExtractedValueGetterExpression,
|
|
4
|
+
renderToolPayloadExpression,
|
|
5
|
+
} from './tool-codegen';
|
|
6
|
+
import { buildEnrichCompatibilityPlan } from './enrich-compat-adapter';
|
|
7
|
+
import { assertUserCodeIsSafe } from './user-code-safety';
|
|
8
|
+
|
|
9
|
+
export type EnrichStepCommand = {
|
|
10
|
+
alias: string;
|
|
11
|
+
tool: string;
|
|
12
|
+
operation?: string;
|
|
13
|
+
play?: {
|
|
14
|
+
ref: string;
|
|
15
|
+
mode?: 'scalar';
|
|
16
|
+
execution?: 'child' | 'inline';
|
|
17
|
+
inline?: {
|
|
18
|
+
exportName: string;
|
|
19
|
+
functionExpressionSource: string;
|
|
20
|
+
sourceHash: string;
|
|
21
|
+
typeImports: string[];
|
|
22
|
+
runtimeImports?: string[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
payload: Record<string, unknown>;
|
|
26
|
+
extract_js?: string;
|
|
27
|
+
run_if_js?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type EnrichWaterfallCommand = {
|
|
33
|
+
with_waterfall: string;
|
|
34
|
+
min_results?: number;
|
|
35
|
+
commands: EnrichCommand[];
|
|
36
|
+
description?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type EnrichCommand = EnrichStepCommand | EnrichWaterfallCommand;
|
|
40
|
+
|
|
41
|
+
export type EnrichCompiledConfig = {
|
|
42
|
+
version: 1;
|
|
43
|
+
commands: EnrichCommand[];
|
|
44
|
+
cost_cap_usd_per_run?: number;
|
|
45
|
+
_comments?: Array<{ path: string; lines: string[] }>;
|
|
46
|
+
_expansion_preview?: {
|
|
47
|
+
plays: Array<{
|
|
48
|
+
alias: string;
|
|
49
|
+
tool_id: string;
|
|
50
|
+
template_group: string;
|
|
51
|
+
runtime_group: string;
|
|
52
|
+
estimated_credits_range: string;
|
|
53
|
+
steps: Array<Record<string, unknown>>;
|
|
54
|
+
}>;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type EnrichPlaySourceOptions = {
|
|
59
|
+
playName?: string;
|
|
60
|
+
mapName?: string;
|
|
61
|
+
forceAliases?: Iterable<string>;
|
|
62
|
+
/** Hard Deepline-credit ceiling enforced by the play runtime. */
|
|
63
|
+
maxCreditsPerRun?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Inline `run_javascript` steps as code instead of emitting a (runtime-
|
|
66
|
+
* rejected) `run_javascript` tool call, and emit the play with `// @ts-nocheck`.
|
|
67
|
+
* Off by default — used by the workflows→plays migration, not enrich-compat.
|
|
68
|
+
*/
|
|
69
|
+
inlineRunJavascript?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Emit clean, idiomatic step code — a direct `ctx.tools.execute(...)` call
|
|
72
|
+
* plus native `result.extractedValues.<field>?.get()` extraction — instead of
|
|
73
|
+
* the `__dlRunCommand`/`__dlExtract` wrappers. A localized `as any` at the
|
|
74
|
+
* loose V1-payload boundary keeps it typechecking. Off by default (enrich-
|
|
75
|
+
* compat keeps the wrappers + its tests); the workflows→plays migration opts in.
|
|
76
|
+
*/
|
|
77
|
+
idiomaticGetters?: boolean;
|
|
78
|
+
failFast?: boolean;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function isWaterfall(
|
|
82
|
+
command: EnrichCommand,
|
|
83
|
+
): command is EnrichWaterfallCommand {
|
|
84
|
+
return 'with_waterfall' in command;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* True if the config contains any `run_javascript` step (including inside
|
|
89
|
+
* waterfalls). Those steps are inlined as arbitrary customer code, which cannot
|
|
90
|
+
* be strictly typechecked, so plays that contain them are emitted with
|
|
91
|
+
* `// @ts-nocheck` (they still bundle + run; non-JS plays stay strictly typed).
|
|
92
|
+
*/
|
|
93
|
+
function configHasRunJavascript(config: EnrichCompiledConfig): boolean {
|
|
94
|
+
const walk = (commands: EnrichCommand[]): boolean =>
|
|
95
|
+
commands.some((command) =>
|
|
96
|
+
isWaterfall(command)
|
|
97
|
+
? walk(command.commands)
|
|
98
|
+
: command.tool === 'run_javascript',
|
|
99
|
+
);
|
|
100
|
+
return walk(config.commands ?? []);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function stringLiteral(value: string): string {
|
|
104
|
+
return JSON.stringify(value);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stableJson(value: unknown): string {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
110
|
+
}
|
|
111
|
+
if (value && typeof value === 'object') {
|
|
112
|
+
const entries = Object.entries(value as Record<string, unknown>).sort(
|
|
113
|
+
([left], [right]) => left.localeCompare(right),
|
|
114
|
+
);
|
|
115
|
+
return `{${entries
|
|
116
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
117
|
+
.join(',')}}`;
|
|
118
|
+
}
|
|
119
|
+
return JSON.stringify(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function indent(source: string, spaces: number): string {
|
|
123
|
+
const pad = ' '.repeat(spaces);
|
|
124
|
+
return source
|
|
125
|
+
.split('\n')
|
|
126
|
+
.map((line) => (line ? `${pad}${line}` : line))
|
|
127
|
+
.join('\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function commandCallId(command: EnrichStepCommand): string {
|
|
131
|
+
return `${command.alias}__${command.tool}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function normalizeAlias(value: string): string {
|
|
135
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function renderExecuteStep(
|
|
139
|
+
command: EnrichStepCommand,
|
|
140
|
+
options: {
|
|
141
|
+
force: boolean;
|
|
142
|
+
precheck?: string;
|
|
143
|
+
legacyEnvelope?: boolean;
|
|
144
|
+
inlineRunJavascript?: boolean;
|
|
145
|
+
idiomaticGetters?: boolean;
|
|
146
|
+
nativeRunIf?: boolean;
|
|
147
|
+
} = {
|
|
148
|
+
force: false,
|
|
149
|
+
},
|
|
150
|
+
): string {
|
|
151
|
+
if (
|
|
152
|
+
command.tool === 'run_javascript' &&
|
|
153
|
+
options.inlineRunJavascript &&
|
|
154
|
+
canInlineRunJavascript(command)
|
|
155
|
+
) {
|
|
156
|
+
return renderInlineJavascriptStep(command, options);
|
|
157
|
+
}
|
|
158
|
+
if (command.play) {
|
|
159
|
+
return renderPlayStep(command, options);
|
|
160
|
+
}
|
|
161
|
+
if (options.idiomaticGetters) {
|
|
162
|
+
return renderIdiomaticExecuteStep(command, options);
|
|
163
|
+
}
|
|
164
|
+
const alias = stringLiteral(command.alias);
|
|
165
|
+
const callId = stringLiteral(commandCallId(command));
|
|
166
|
+
const tool = stringLiteral(command.tool);
|
|
167
|
+
const payload = stableJson(command.payload ?? {});
|
|
168
|
+
const extractJs = renderExtractFunction(command, 6);
|
|
169
|
+
const runIfJs = options.nativeRunIf
|
|
170
|
+
? 'null'
|
|
171
|
+
: (renderRunIfFunction(command) ?? 'null');
|
|
172
|
+
const description = command.description
|
|
173
|
+
? `,\n description: ${stringLiteral(command.description)}`
|
|
174
|
+
: '';
|
|
175
|
+
const force = options.force ? `,\n force: true` : '';
|
|
176
|
+
const legacyEnvelope = options.legacyEnvelope
|
|
177
|
+
? `,\n legacyEnvelope: true`
|
|
178
|
+
: '';
|
|
179
|
+
|
|
180
|
+
return [
|
|
181
|
+
`async (row, stepCtx) => {`,
|
|
182
|
+
...(options.precheck ? [` if (${options.precheck}) return null;`] : []),
|
|
183
|
+
` return __dlRunCommand({`,
|
|
184
|
+
` alias: ${alias},`,
|
|
185
|
+
` callId: ${callId},`,
|
|
186
|
+
` tool: ${tool},`,
|
|
187
|
+
` payload: ${payload},`,
|
|
188
|
+
` extract: ${extractJs},`,
|
|
189
|
+
` runIf: ${runIfJs},`,
|
|
190
|
+
` row,`,
|
|
191
|
+
` stepCtx${description}${force}${legacyEnvelope}`,
|
|
192
|
+
` });`,
|
|
193
|
+
`}`,
|
|
194
|
+
].join('\n');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function canInlineRunJavascript(command: EnrichStepCommand): boolean {
|
|
198
|
+
const code =
|
|
199
|
+
typeof command.payload?.code === 'string' ? command.payload.code : '';
|
|
200
|
+
if (/\btriggerWorkflow\b/.test(code)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
assertUserCodeIsSafe(code, `run_javascript step "${command.alias}"`);
|
|
205
|
+
} catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function renderExtractFunction(
|
|
212
|
+
command: EnrichStepCommand,
|
|
213
|
+
indentSpaces: number,
|
|
214
|
+
): string {
|
|
215
|
+
return command.extract_js
|
|
216
|
+
? `({ row, result, data, raw, pick, extract, extractList, target, get }) => { const input = row; const context = row; const output_data = __dlLegacyOutputData(result, raw);\n${indent(renderJavascriptBody(command.extract_js), indentSpaces)}\n }`
|
|
217
|
+
: 'null';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function renderRunIfFunction(command: EnrichStepCommand): string | null {
|
|
221
|
+
return command.run_if_js
|
|
222
|
+
? `(row) => { const input = row; const context = row;\n${indent(renderJavascriptBody(command.run_if_js), 6)}\n }`
|
|
223
|
+
: null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function renderColumnRunIfFunction(command: EnrichStepCommand): string | null {
|
|
227
|
+
if (!command.run_if_js) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
// Runtime-sheet rows keep source CSV values in the projected-value envelope.
|
|
231
|
+
// Gate every column against the same prepared row used by templating and
|
|
232
|
+
// extraction, regardless of whether the column invokes a play or a tool.
|
|
233
|
+
return `(__dlRawRow) => { const row = __dlPrepareEnrichRow(__dlRawRow, [${stringLiteral(command.alias)}]); const input = row; const context = row;\n${indent(renderJavascriptBody(command.run_if_js), 6)}\n }`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function renderCombinedRunIfFunction(
|
|
237
|
+
precheck: string | null | undefined,
|
|
238
|
+
runIfSource: string | null,
|
|
239
|
+
): string | null {
|
|
240
|
+
if (!runIfSource) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return precheck
|
|
244
|
+
? `(row) => { if (${precheck}) return false; return (${runIfSource})(row); }`
|
|
245
|
+
: runIfSource;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Emit a clean, idiomatic step: a direct `ctx.tools.execute(...)` call plus a
|
|
250
|
+
* native `result.extractedValues.<field>?.get()` extraction — no
|
|
251
|
+
* `__dlRunCommand`/`__dlExtract` wrappers. The execute argument is cast `as any`
|
|
252
|
+
* (V1 payloads are loose/templated and can't satisfy each tool's strict
|
|
253
|
+
* per-tool input type) and `result` is `any`, so it typechecks cleanly.
|
|
254
|
+
*/
|
|
255
|
+
function renderIdiomaticExecuteStep(
|
|
256
|
+
command: EnrichStepCommand,
|
|
257
|
+
options: {
|
|
258
|
+
force: boolean;
|
|
259
|
+
precheck?: string;
|
|
260
|
+
nativeRunIf?: boolean;
|
|
261
|
+
},
|
|
262
|
+
): string {
|
|
263
|
+
const callId = stringLiteral(commandCallId(command));
|
|
264
|
+
const tool = stringLiteral(command.tool);
|
|
265
|
+
const input = renderToolPayloadExpression(command.payload ?? {});
|
|
266
|
+
const getter = getterFromLegacyExtractJs(command.extract_js, command.alias);
|
|
267
|
+
const extraction = getter
|
|
268
|
+
? `${renderExtractedValueGetterExpression('result', getter)} ?? null`
|
|
269
|
+
: 'result';
|
|
270
|
+
const runIfLines =
|
|
271
|
+
command.run_if_js && !options.nativeRunIf
|
|
272
|
+
? [
|
|
273
|
+
` if (`,
|
|
274
|
+
` !((row: Record<string, any>) => {`,
|
|
275
|
+
` const input = row;`,
|
|
276
|
+
` const context = row;`,
|
|
277
|
+
indent(renderJavascriptBody(command.run_if_js), 6),
|
|
278
|
+
` })(row as Record<string, any>)`,
|
|
279
|
+
` ) return null;`,
|
|
280
|
+
]
|
|
281
|
+
: [];
|
|
282
|
+
return [
|
|
283
|
+
`async (row, ctx) => {`,
|
|
284
|
+
...(options.precheck ? [` if (${options.precheck}) return null;`] : []),
|
|
285
|
+
...runIfLines,
|
|
286
|
+
` const result: any = await ctx.tools.execute({`,
|
|
287
|
+
` id: ${callId},`,
|
|
288
|
+
` tool: ${tool},`,
|
|
289
|
+
` input: ${input} as any,`,
|
|
290
|
+
` description: ${stringLiteral((command.description ?? '').trim() || `Run ${command.alias} via ${command.tool}.`)},`,
|
|
291
|
+
...(options.force ? [` force: true,`] : []),
|
|
292
|
+
` });`,
|
|
293
|
+
` __dlAssertSuccessfulToolResult(result);`,
|
|
294
|
+
` return ${extraction};`,
|
|
295
|
+
`}`,
|
|
296
|
+
].join('\n');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function renderPlayStep(
|
|
300
|
+
command: EnrichStepCommand,
|
|
301
|
+
options: {
|
|
302
|
+
force: boolean;
|
|
303
|
+
precheck?: string;
|
|
304
|
+
legacyEnvelope?: boolean;
|
|
305
|
+
nativeRunIf?: boolean;
|
|
306
|
+
},
|
|
307
|
+
): string {
|
|
308
|
+
const alias = stringLiteral(command.alias);
|
|
309
|
+
const callId = stringLiteral(commandCallId(command));
|
|
310
|
+
const playRef = stringLiteral(command.play?.ref ?? command.tool);
|
|
311
|
+
const payload = stableJson(command.payload ?? {});
|
|
312
|
+
const runIfJs = renderRunIfFunction(command) ?? 'null';
|
|
313
|
+
const runIfLines =
|
|
314
|
+
command.run_if_js && !options.nativeRunIf
|
|
315
|
+
? [
|
|
316
|
+
` const __dlRunIf = ${runIfJs};`,
|
|
317
|
+
` if (!__dlRunIf(templateRow)) return null;`,
|
|
318
|
+
]
|
|
319
|
+
: [];
|
|
320
|
+
const inline = command.play?.inline;
|
|
321
|
+
const inlineHandler = inline
|
|
322
|
+
? `__dlInlinePlay_${inline.sourceHash.slice(0, 16)}`
|
|
323
|
+
: null;
|
|
324
|
+
return [
|
|
325
|
+
`async (row, stepCtx) => {`,
|
|
326
|
+
...(options.precheck ? [` if (${options.precheck}) return null;`] : []),
|
|
327
|
+
` const templateRow = __dlPrepareEnrichRow(row, [${alias}]);`,
|
|
328
|
+
...runIfLines,
|
|
329
|
+
` const payload = __dlTemplate(${payload}, templateRow) as Record<string, unknown>;`,
|
|
330
|
+
` if (__dlShouldSkipBlankPlayPayload(payload)) return null;`,
|
|
331
|
+
` const result: unknown = `,
|
|
332
|
+
...(inlineHandler
|
|
333
|
+
? [
|
|
334
|
+
// Enrich validates and normalizes this payload against the certified
|
|
335
|
+
// prebuilt contract before code generation. The attachment retains
|
|
336
|
+
// its concrete input type, while generated payloads are records.
|
|
337
|
+
` await __dlRunInlinePlay(${inlineHandler}, stepCtx, payload as never, ${options.force});`,
|
|
338
|
+
]
|
|
339
|
+
: [
|
|
340
|
+
` await stepCtx.runPlay(${callId}, ${playRef}, payload, {`,
|
|
341
|
+
` description: ${stringLiteral(command.description ?? command.alias)}`,
|
|
342
|
+
` });`,
|
|
343
|
+
]),
|
|
344
|
+
``,
|
|
345
|
+
` __dlAssertSuccessfulToolResult(result);`,
|
|
346
|
+
` return __dlPlayResultValue(${alias}, result);`,
|
|
347
|
+
`}`,
|
|
348
|
+
].join('\n');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function collectInlinePlayHandlers(
|
|
352
|
+
commands: readonly EnrichCommand[],
|
|
353
|
+
): Array<NonNullable<NonNullable<EnrichStepCommand['play']>['inline']>> {
|
|
354
|
+
const handlers = new Map<
|
|
355
|
+
string,
|
|
356
|
+
NonNullable<NonNullable<EnrichStepCommand['play']>['inline']>
|
|
357
|
+
>();
|
|
358
|
+
const visit = (command: EnrichCommand): void => {
|
|
359
|
+
if (isWaterfall(command)) {
|
|
360
|
+
command.commands.forEach(visit);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const inline = command.play?.inline;
|
|
364
|
+
if (inline) handlers.set(inline.sourceHash, inline);
|
|
365
|
+
};
|
|
366
|
+
commands.forEach(visit);
|
|
367
|
+
return [...handlers.values()];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function renderInlineJavascriptStep(
|
|
371
|
+
command: EnrichStepCommand,
|
|
372
|
+
options: {
|
|
373
|
+
force: boolean;
|
|
374
|
+
precheck?: string;
|
|
375
|
+
legacyEnvelope?: boolean;
|
|
376
|
+
nativeRunIf?: boolean;
|
|
377
|
+
},
|
|
378
|
+
): string {
|
|
379
|
+
const alias = stringLiteral(command.alias);
|
|
380
|
+
const extractJs = renderExtractFunction(command, 4);
|
|
381
|
+
const legacyEnvelope = options.legacyEnvelope ? 'true' : 'false';
|
|
382
|
+
const resultExpression = command.extract_js
|
|
383
|
+
? `__dlExtract(${alias}, result, row as Record<string, unknown>, ${extractJs}, ${legacyEnvelope})`
|
|
384
|
+
: 'result';
|
|
385
|
+
const payload = stableJson(command.payload ?? {});
|
|
386
|
+
const code =
|
|
387
|
+
typeof command.payload?.code === 'string'
|
|
388
|
+
? command.payload.code
|
|
389
|
+
: 'return null;';
|
|
390
|
+
const runIfLines =
|
|
391
|
+
command.run_if_js && !options.nativeRunIf
|
|
392
|
+
? [
|
|
393
|
+
` if (!((row: Record<string, any>) => { const input = row; const context = row;`,
|
|
394
|
+
indent(renderJavascriptBody(command.run_if_js), 4),
|
|
395
|
+
` })(row as Record<string, any>)) return null;`,
|
|
396
|
+
]
|
|
397
|
+
: [];
|
|
398
|
+
return [
|
|
399
|
+
`async (row) => {`,
|
|
400
|
+
...(options.precheck ? [` if (${options.precheck}) return null;`] : []),
|
|
401
|
+
...runIfLines,
|
|
402
|
+
` const __dlPayload = __dlRuntimePayload('run_javascript', __dlTemplate(${payload}, row as Record<string, unknown>) as Record<string, unknown>, row as Record<string, unknown>);`,
|
|
403
|
+
` const rawResult = ((row: Record<string, any>, input: Record<string, any>, context: Record<string, any>) => {`,
|
|
404
|
+
` const payload = __dlPayload as Record<string, any>;`,
|
|
405
|
+
` const extract = __dlInlineExtract;`,
|
|
406
|
+
` const extractList = __dlInlineExtractList;`,
|
|
407
|
+
` return (() => {`,
|
|
408
|
+
indent(renderJavascriptBody(code), 4),
|
|
409
|
+
` })();`,
|
|
410
|
+
` })(row as Record<string, any>, row as Record<string, any>, row as Record<string, any>);`,
|
|
411
|
+
` const result = await Promise.resolve(rawResult);`,
|
|
412
|
+
` return ${resultExpression};`,
|
|
413
|
+
`}`,
|
|
414
|
+
].join('\n');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function renderJavascriptBody(source: string): string {
|
|
418
|
+
// Single chokepoint for all user-authored play code (extract_js, run_if_js,
|
|
419
|
+
// run_javascript). Reject non-deterministic / sandbox-escaping constructs.
|
|
420
|
+
assertUserCodeIsSafe(source, 'play step code');
|
|
421
|
+
const trimmed = source.trim();
|
|
422
|
+
if (/^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed)) {
|
|
423
|
+
return `return (${trimmed});`;
|
|
424
|
+
}
|
|
425
|
+
if (
|
|
426
|
+
trimmed &&
|
|
427
|
+
!trimmed.includes('\n') &&
|
|
428
|
+
!trimmed.includes(';') &&
|
|
429
|
+
!/\breturn\b/.test(trimmed) &&
|
|
430
|
+
!/^(?:throw|if|for|while|switch|try|catch|const|let|var|class|function)\b/.test(
|
|
431
|
+
trimmed,
|
|
432
|
+
)
|
|
433
|
+
) {
|
|
434
|
+
return `return (${trimmed});`;
|
|
435
|
+
}
|
|
436
|
+
return source;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function renderColumnStep(
|
|
440
|
+
alias: string,
|
|
441
|
+
resolverSource: string,
|
|
442
|
+
options: {
|
|
443
|
+
runIfSource?: string | null;
|
|
444
|
+
} = {},
|
|
445
|
+
): string {
|
|
446
|
+
const resolver = indent(resolverSource, 8);
|
|
447
|
+
const optionFields = options.runIfSource
|
|
448
|
+
? [`runIf: ${options.runIfSource}`]
|
|
449
|
+
: [];
|
|
450
|
+
const optionSource =
|
|
451
|
+
optionFields.length > 0 ? `{ ${optionFields.join(', ')} }` : null;
|
|
452
|
+
return [
|
|
453
|
+
` .withColumn(${stringLiteral(alias)},`,
|
|
454
|
+
`${resolver}${optionSource ? ',' : ''}`,
|
|
455
|
+
...(optionSource ? [` ${optionSource},`] : []),
|
|
456
|
+
` )`,
|
|
457
|
+
].join('\n');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function metadataMode(command: EnrichStepCommand): 'list' | 'scalar' {
|
|
461
|
+
return /\bextractList\s*\(/.test(command.extract_js ?? '')
|
|
462
|
+
? 'list'
|
|
463
|
+
: 'scalar';
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function metadataEntryForCommand(
|
|
467
|
+
command: EnrichStepCommand,
|
|
468
|
+
waterfallGroupId?: string,
|
|
469
|
+
): Record<string, unknown> {
|
|
470
|
+
const entry: Record<string, unknown> = {
|
|
471
|
+
tool_id: command.tool,
|
|
472
|
+
};
|
|
473
|
+
if (command.play?.ref) {
|
|
474
|
+
entry.play_ref = command.play.ref;
|
|
475
|
+
entry.kind = 'play_call';
|
|
476
|
+
}
|
|
477
|
+
if (command.operation) {
|
|
478
|
+
entry.operation = command.operation;
|
|
479
|
+
}
|
|
480
|
+
if (command.extract_js?.trim()) {
|
|
481
|
+
entry.extract_js = command.extract_js.trim();
|
|
482
|
+
}
|
|
483
|
+
if (waterfallGroupId) {
|
|
484
|
+
entry.waterfall = {
|
|
485
|
+
group_id: waterfallGroupId,
|
|
486
|
+
mode: metadataMode(command),
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return entry;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function collectMetadataColumns(
|
|
493
|
+
commands: EnrichCommand[],
|
|
494
|
+
waterfallGroupId?: string,
|
|
495
|
+
): Record<string, unknown> {
|
|
496
|
+
const columns: Record<string, unknown> = {};
|
|
497
|
+
for (const command of commands) {
|
|
498
|
+
if (isWaterfall(command)) {
|
|
499
|
+
Object.assign(
|
|
500
|
+
columns,
|
|
501
|
+
collectMetadataColumns(command.commands, command.with_waterfall),
|
|
502
|
+
);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (command.disabled) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
columns[normalizeAlias(command.alias)] = metadataEntryForCommand(
|
|
509
|
+
command,
|
|
510
|
+
waterfallGroupId,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
return columns;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function collectGeneratedAliases(commands: EnrichCommand[]): string[] {
|
|
517
|
+
const aliases: string[] = [];
|
|
518
|
+
const addAlias = (alias: string): void => {
|
|
519
|
+
if (alias && !aliases.includes(alias)) {
|
|
520
|
+
aliases.push(alias);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
for (const command of commands) {
|
|
524
|
+
if (isWaterfall(command)) {
|
|
525
|
+
const before = aliases.length;
|
|
526
|
+
collectGeneratedAliases(command.commands).forEach(addAlias);
|
|
527
|
+
if (aliases.length > before) {
|
|
528
|
+
addAlias(command.with_waterfall);
|
|
529
|
+
addAlias(`${command.with_waterfall}_source`);
|
|
530
|
+
}
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (!command.disabled) {
|
|
534
|
+
addAlias(command.alias);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return aliases;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function renderMetadataColumnStep(config: EnrichCompiledConfig): string {
|
|
541
|
+
const columns = collectMetadataColumns(config.commands);
|
|
542
|
+
if (Object.keys(columns).length === 0) {
|
|
543
|
+
return '';
|
|
544
|
+
}
|
|
545
|
+
return [
|
|
546
|
+
` .withColumn('_metadata',`,
|
|
547
|
+
` (row) => __dlMergeMetadata(__dlMetadataFromRow(row), ${stableJson({ columns })}),`,
|
|
548
|
+
` )`,
|
|
549
|
+
].join('\n');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function renderWaterfallColumns(
|
|
553
|
+
command: EnrichWaterfallCommand,
|
|
554
|
+
forceAliases: Set<string>,
|
|
555
|
+
inlineRunJavascript: boolean,
|
|
556
|
+
idiomaticGetters: boolean,
|
|
557
|
+
): string[] {
|
|
558
|
+
const activeChildren = command.commands.filter(
|
|
559
|
+
(nested): nested is EnrichStepCommand =>
|
|
560
|
+
!isWaterfall(nested) && !nested.disabled,
|
|
561
|
+
);
|
|
562
|
+
const minResults =
|
|
563
|
+
typeof command.min_results === 'number'
|
|
564
|
+
? Math.max(1, Math.trunc(command.min_results))
|
|
565
|
+
: 1;
|
|
566
|
+
const columnSteps = activeChildren
|
|
567
|
+
.map((nested, stepIndex) => {
|
|
568
|
+
if (isWaterfall(nested)) {
|
|
569
|
+
throw new Error('Nested with_waterfall blocks are not supported.');
|
|
570
|
+
}
|
|
571
|
+
if (nested.disabled) {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
const priorAliases = activeChildren
|
|
575
|
+
.slice(0, stepIndex)
|
|
576
|
+
.map((prior) => prior.alias);
|
|
577
|
+
const force = forceAliases.has(normalizeAlias(nested.alias));
|
|
578
|
+
const precheck =
|
|
579
|
+
priorAliases.length > 0
|
|
580
|
+
? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})`
|
|
581
|
+
: undefined;
|
|
582
|
+
const runIfSource = renderCombinedRunIfFunction(
|
|
583
|
+
precheck,
|
|
584
|
+
renderColumnRunIfFunction(nested),
|
|
585
|
+
);
|
|
586
|
+
return renderColumnStep(
|
|
587
|
+
nested.alias,
|
|
588
|
+
renderExecuteStep(nested, {
|
|
589
|
+
force,
|
|
590
|
+
precheck,
|
|
591
|
+
legacyEnvelope: Boolean(nested.extract_js),
|
|
592
|
+
inlineRunJavascript,
|
|
593
|
+
idiomaticGetters,
|
|
594
|
+
nativeRunIf: Boolean(runIfSource),
|
|
595
|
+
}),
|
|
596
|
+
{
|
|
597
|
+
runIfSource,
|
|
598
|
+
},
|
|
599
|
+
);
|
|
600
|
+
})
|
|
601
|
+
.filter((line): line is string => line !== null);
|
|
602
|
+
const aliases = activeChildren.map((nested) => nested.alias);
|
|
603
|
+
const returnExpr =
|
|
604
|
+
typeof command.min_results === 'number'
|
|
605
|
+
? `__dlFirstMinResults(row, ${stableJson(aliases)}, ${Math.max(
|
|
606
|
+
1,
|
|
607
|
+
Math.trunc(command.min_results),
|
|
608
|
+
)})`
|
|
609
|
+
: `__dlFirstMeaningful(row, ${stableJson(aliases)})`;
|
|
610
|
+
if (columnSteps.length === 0) {
|
|
611
|
+
return [];
|
|
612
|
+
}
|
|
613
|
+
return [
|
|
614
|
+
...columnSteps,
|
|
615
|
+
renderColumnStep(command.with_waterfall, `(row) => ${returnExpr}`),
|
|
616
|
+
renderColumnStep(
|
|
617
|
+
`${command.with_waterfall}_source`,
|
|
618
|
+
typeof command.min_results === 'number'
|
|
619
|
+
? `(row) => __dlContributingAliases(row, ${stableJson(aliases)})`
|
|
620
|
+
: `(row) => __dlFirstMeaningfulAlias(row, ${stableJson(aliases)})`,
|
|
621
|
+
),
|
|
622
|
+
];
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export function compileEnrichConfigToPlaySource(
|
|
626
|
+
config: EnrichCompiledConfig,
|
|
627
|
+
options: EnrichPlaySourceOptions = {},
|
|
628
|
+
): string {
|
|
629
|
+
if (
|
|
630
|
+
options.maxCreditsPerRun !== undefined &&
|
|
631
|
+
(!Number.isFinite(options.maxCreditsPerRun) ||
|
|
632
|
+
options.maxCreditsPerRun <= 0)
|
|
633
|
+
) {
|
|
634
|
+
throw new Error('maxCreditsPerRun must be a number greater than 0.');
|
|
635
|
+
}
|
|
636
|
+
const compatibility = buildEnrichCompatibilityPlan(options);
|
|
637
|
+
const { playName, mapName } = compatibility;
|
|
638
|
+
const inlineRunJavascript = options.inlineRunJavascript ?? false;
|
|
639
|
+
const idiomaticGetters = options.idiomaticGetters ?? false;
|
|
640
|
+
const forceAliases = new Set(
|
|
641
|
+
[...(options.forceAliases ?? [])].map((alias) => normalizeAlias(alias)),
|
|
642
|
+
);
|
|
643
|
+
const columnSteps: string[] = [];
|
|
644
|
+
const inlineHandlers = collectInlinePlayHandlers(config.commands);
|
|
645
|
+
const inlineTypeImports = [
|
|
646
|
+
...new Set(inlineHandlers.flatMap((inline) => inline.typeImports ?? [])),
|
|
647
|
+
].sort();
|
|
648
|
+
const inlineRuntimeImports = [
|
|
649
|
+
...new Set(inlineHandlers.flatMap((inline) => inline.runtimeImports ?? [])),
|
|
650
|
+
].sort();
|
|
651
|
+
|
|
652
|
+
config.commands.forEach((command) => {
|
|
653
|
+
if (isWaterfall(command)) {
|
|
654
|
+
columnSteps.push(
|
|
655
|
+
...renderWaterfallColumns(
|
|
656
|
+
command,
|
|
657
|
+
forceAliases,
|
|
658
|
+
inlineRunJavascript,
|
|
659
|
+
idiomaticGetters,
|
|
660
|
+
),
|
|
661
|
+
);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (command.disabled) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const force = forceAliases.has(normalizeAlias(command.alias));
|
|
668
|
+
const runIfSource = renderColumnRunIfFunction(command);
|
|
669
|
+
columnSteps.push(
|
|
670
|
+
renderColumnStep(
|
|
671
|
+
command.alias,
|
|
672
|
+
renderExecuteStep(command, {
|
|
673
|
+
force,
|
|
674
|
+
inlineRunJavascript,
|
|
675
|
+
idiomaticGetters,
|
|
676
|
+
nativeRunIf: Boolean(runIfSource),
|
|
677
|
+
}),
|
|
678
|
+
{
|
|
679
|
+
runIfSource,
|
|
680
|
+
},
|
|
681
|
+
),
|
|
682
|
+
);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
const columnStepSource =
|
|
686
|
+
columnSteps.length > 0
|
|
687
|
+
? columnSteps.join('\n')
|
|
688
|
+
: ` .withColumn('noop', () => null)`;
|
|
689
|
+
const metadataColumnSource = renderMetadataColumnStep(config);
|
|
690
|
+
const generatedAliases = collectGeneratedAliases(config.commands);
|
|
691
|
+
const runOptionsSource = options.failFast
|
|
692
|
+
? `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart), onRowError: 'fail' as const }`
|
|
693
|
+
: `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart) }`;
|
|
694
|
+
const playOptionsSource = [
|
|
695
|
+
`description: ${stringLiteral(
|
|
696
|
+
'Read a CSV file, run the configured Deepline enrich commands, and return enriched rows.',
|
|
697
|
+
)}`,
|
|
698
|
+
...(options.maxCreditsPerRun === undefined
|
|
699
|
+
? []
|
|
700
|
+
: [`billing: { maxCreditsPerRun: ${String(options.maxCreditsPerRun)} }`]),
|
|
701
|
+
].join(', ');
|
|
702
|
+
|
|
703
|
+
const body = [
|
|
704
|
+
`function __dlRunInlinePlay<TContext, TInput, TOutput>(handler: (ctx: TContext, input: TInput) => Promise<TOutput>, scope: TContext, payload: TInput, force: boolean): Promise<TOutput> {`,
|
|
705
|
+
` if (!force) return handler(scope, payload);`,
|
|
706
|
+
` const runtimeScope = scope as TContext & { __deeplineRunWithForcedTools?: <T>(run: () => Promise<T>) => Promise<T> };`,
|
|
707
|
+
` if (typeof runtimeScope.__deeplineRunWithForcedTools !== 'function') {`,
|
|
708
|
+
` throw new Error('This runtime does not support forced certified inline play execution.');`,
|
|
709
|
+
` }`,
|
|
710
|
+
` return runtimeScope.__deeplineRunWithForcedTools(() => handler(scope, payload));`,
|
|
711
|
+
`}`,
|
|
712
|
+
``,
|
|
713
|
+
...inlineHandlers.flatMap((inline) => [
|
|
714
|
+
`const __dlInlinePlay_${inline.sourceHash.slice(0, 16)} = ${inline.functionExpressionSource};`,
|
|
715
|
+
``,
|
|
716
|
+
]),
|
|
717
|
+
`export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
|
|
718
|
+
` const sourceRows = await ctx.csv<Record<string, unknown>>(input.file);`,
|
|
719
|
+
` const rowStart = __dlNonNegativeInteger(input.rowStart, 0);`,
|
|
720
|
+
` const rowEndExclusive = Number.isFinite(input.rowEnd) ? Math.max(rowStart, __dlWholeNumber(input.rowEnd, rowStart) + 1) : undefined;`,
|
|
721
|
+
` const rows: Array<Record<string, unknown>> = [];`,
|
|
722
|
+
` let sourceRowIndex = 0;`,
|
|
723
|
+
` for await (const row of sourceRows) {`,
|
|
724
|
+
` if (rowEndExclusive !== undefined && sourceRowIndex >= rowEndExclusive) break;`,
|
|
725
|
+
` if (sourceRowIndex >= rowStart) {`,
|
|
726
|
+
` rows.push(__dlPrepareEnrichRow(row, ${stableJson(generatedAliases)}, sourceRowIndex));`,
|
|
727
|
+
` }`,
|
|
728
|
+
` sourceRowIndex += 1;`,
|
|
729
|
+
` }`,
|
|
730
|
+
` const enriched = await ctx`,
|
|
731
|
+
` .dataset(${stringLiteral(mapName)}, rows)`,
|
|
732
|
+
columnStepSource,
|
|
733
|
+
...(metadataColumnSource ? [metadataColumnSource] : []),
|
|
734
|
+
` .run(${runOptionsSource});`,
|
|
735
|
+
` return { rows: enriched, count: await enriched.count() };`,
|
|
736
|
+
`}, { ${playOptionsSource} });`,
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
// Idiomatic plays call `ctx.tools.execute` directly and read results via
|
|
740
|
+
// `extractedValues.<field>.get()`, so they reference almost none of the
|
|
741
|
+
// legacy extraction preamble. Tree-shake it to only the helpers the body
|
|
742
|
+
// actually uses (plus their transitive deps) so the emitted play is clean.
|
|
743
|
+
const helpers = idiomaticGetters
|
|
744
|
+
? selectUsedHelpers(helperSource(), body.join('\n'))
|
|
745
|
+
: helperSource();
|
|
746
|
+
const typeImports = idiomaticGetters
|
|
747
|
+
? inlineTypeImports
|
|
748
|
+
: [...new Set([...inlineTypeImports, 'DeeplinePlayRuntimeContext'])].sort();
|
|
749
|
+
|
|
750
|
+
return [
|
|
751
|
+
...(inlineRunJavascript && configHasRunJavascript(config)
|
|
752
|
+
? ['// @ts-nocheck', '/* eslint-disable */', '']
|
|
753
|
+
: []),
|
|
754
|
+
`import { ${['definePlay', 'steps', ...inlineRuntimeImports].join(', ')} } from 'deepline';`,
|
|
755
|
+
...(typeImports.length > 0
|
|
756
|
+
? [`import type { ${typeImports.join(', ')} } from 'deepline';`]
|
|
757
|
+
: []),
|
|
758
|
+
``,
|
|
759
|
+
`type EnrichInput = { file: string; rowStart?: number | null; rowEnd?: number | null };`,
|
|
760
|
+
``,
|
|
761
|
+
helpers,
|
|
762
|
+
``,
|
|
763
|
+
...body,
|
|
764
|
+
``,
|
|
765
|
+
].join('\n');
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Tree-shakes the `__dl*` helper preamble down to only the helpers referenced
|
|
770
|
+
* by `referenceSource` (the generated play body) plus their transitive helper
|
|
771
|
+
* dependencies. Helpers are blank-line-separated function/type/const blocks in
|
|
772
|
+
* `helperSource()`; a block is kept if its name appears in the body or in any
|
|
773
|
+
* already-kept helper's source. Behavior-preserving: an unreferenced helper is
|
|
774
|
+
* dead code, and `bundlePlayFile` would flag any helper dropped while still in
|
|
775
|
+
* use.
|
|
776
|
+
*/
|
|
777
|
+
function selectUsedHelpers(
|
|
778
|
+
helperBlock: string,
|
|
779
|
+
referenceSource: string,
|
|
780
|
+
): string {
|
|
781
|
+
const referencesSymbol = (source: string, symbol: string): boolean =>
|
|
782
|
+
new RegExp(`\\b${symbol}\\b`).test(source);
|
|
783
|
+
|
|
784
|
+
const blocks = helperBlock
|
|
785
|
+
.split('\n\n')
|
|
786
|
+
.map((source) => source.trim())
|
|
787
|
+
.filter(Boolean)
|
|
788
|
+
.map((source) => ({
|
|
789
|
+
name:
|
|
790
|
+
source.match(/(?:function|type|const)\s+(__[A-Za-z]\w*)/)?.[1] ?? '',
|
|
791
|
+
source,
|
|
792
|
+
}));
|
|
793
|
+
|
|
794
|
+
const used = new Set<string>();
|
|
795
|
+
for (const { name } of blocks) {
|
|
796
|
+
if (name && referencesSymbol(referenceSource, name)) used.add(name);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
let changed = true;
|
|
800
|
+
while (changed) {
|
|
801
|
+
changed = false;
|
|
802
|
+
const usedSource = blocks
|
|
803
|
+
.filter((block) => used.has(block.name))
|
|
804
|
+
.map((block) => block.source)
|
|
805
|
+
.join('\n');
|
|
806
|
+
for (const { name } of blocks) {
|
|
807
|
+
if (!name || used.has(name)) continue;
|
|
808
|
+
if (referencesSymbol(usedSource, name)) {
|
|
809
|
+
used.add(name);
|
|
810
|
+
changed = true;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return blocks
|
|
816
|
+
.filter((block) => used.has(block.name))
|
|
817
|
+
.map((block) => block.source)
|
|
818
|
+
.join('\n\n');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function helperSource(): string {
|
|
822
|
+
return [
|
|
823
|
+
`function __dlWholeNumber(value: unknown, fallback: number): number {`,
|
|
824
|
+
` const numeric = Number(value);`,
|
|
825
|
+
` if (!Number.isFinite(numeric)) return fallback;`,
|
|
826
|
+
` return Math.floor(numeric);`,
|
|
827
|
+
`}`,
|
|
828
|
+
``,
|
|
829
|
+
`function __dlNonNegativeInteger(value: unknown, fallback: number): number {`,
|
|
830
|
+
` return Math.max(0, __dlWholeNumber(value, fallback));`,
|
|
831
|
+
`}`,
|
|
832
|
+
``,
|
|
833
|
+
`function __dlAtLeastOneInteger(value: number): number {`,
|
|
834
|
+
` return Math.max(1, Math.floor(Number(value)));`,
|
|
835
|
+
`}`,
|
|
836
|
+
``,
|
|
837
|
+
`function __dlRecord(value: unknown): value is Record<string, unknown> {`,
|
|
838
|
+
` return Boolean(value && typeof value === 'object' && !Array.isArray(value));`,
|
|
839
|
+
`}`,
|
|
840
|
+
``,
|
|
841
|
+
`function __dlParseMetadata(value: unknown): Record<string, unknown> | null {`,
|
|
842
|
+
` if (__dlRecord(value)) return value;`,
|
|
843
|
+
` if (typeof value !== 'string') return null;`,
|
|
844
|
+
` const trimmed = value.trim();`,
|
|
845
|
+
` if (!trimmed) return null;`,
|
|
846
|
+
` const candidates = [trimmed];`,
|
|
847
|
+
` if (trimmed.includes('\\\\\"')) candidates.push(trimmed.replace(/\\\\\"/g, '"'));`,
|
|
848
|
+
` for (const candidate of candidates) {`,
|
|
849
|
+
` try {`,
|
|
850
|
+
` const parsed = JSON.parse(candidate);`,
|
|
851
|
+
` if (__dlRecord(parsed)) return parsed;`,
|
|
852
|
+
` if (typeof parsed === 'string') {`,
|
|
853
|
+
` const nested = JSON.parse(parsed);`,
|
|
854
|
+
` if (__dlRecord(nested)) return nested;`,
|
|
855
|
+
` }`,
|
|
856
|
+
` } catch {}`,
|
|
857
|
+
` }`,
|
|
858
|
+
` return null;`,
|
|
859
|
+
`}`,
|
|
860
|
+
``,
|
|
861
|
+
`function __dlMetadataFromRow(row: Record<string, unknown>): unknown {`,
|
|
862
|
+
` const direct = __dlParseMetadata(row._metadata);`,
|
|
863
|
+
` if (direct) return direct;`,
|
|
864
|
+
` const relocated = __dlParseMetadata(row.metadata);`,
|
|
865
|
+
` if (relocated) return relocated;`,
|
|
866
|
+
` const dotted = __dlParseMetadata(row['metadata.columns']);`,
|
|
867
|
+
` if (dotted) return { columns: dotted };`,
|
|
868
|
+
` const underscored = __dlParseMetadata(row.metadata__columns);`,
|
|
869
|
+
` if (underscored) return { columns: underscored };`,
|
|
870
|
+
` return row._metadata;`,
|
|
871
|
+
`}`,
|
|
872
|
+
``,
|
|
873
|
+
`function __dlMergeMetadata(existing: unknown, patch: Record<string, unknown>): Record<string, unknown> {`,
|
|
874
|
+
` const base = __dlParseMetadata(existing) ?? {};`,
|
|
875
|
+
` const baseColumns = __dlRecord(base.columns) ? base.columns : {};`,
|
|
876
|
+
` const patchColumns = __dlRecord(patch.columns) ? patch.columns : {};`,
|
|
877
|
+
` return {`,
|
|
878
|
+
` ...base,`,
|
|
879
|
+
` ...patch,`,
|
|
880
|
+
` columns: {`,
|
|
881
|
+
` ...baseColumns,`,
|
|
882
|
+
` ...patchColumns,`,
|
|
883
|
+
` },`,
|
|
884
|
+
` };`,
|
|
885
|
+
`}`,
|
|
886
|
+
``,
|
|
887
|
+
`function __dlPathParts(path: string): string[] {`,
|
|
888
|
+
` const source = String(path || '');`,
|
|
889
|
+
` const parts: string[] = [];`,
|
|
890
|
+
` let current = '';`,
|
|
891
|
+
` for (let index = 0; index < source.length; index += 1) {`,
|
|
892
|
+
` const char = source.slice(index, index + 1);`,
|
|
893
|
+
` if (char === '.' || char === '[' || char === ']') {`,
|
|
894
|
+
` const trimmed = current.trim();`,
|
|
895
|
+
` if (trimmed) parts.push(trimmed);`,
|
|
896
|
+
` current = '';`,
|
|
897
|
+
` continue;`,
|
|
898
|
+
` }`,
|
|
899
|
+
` current += char;`,
|
|
900
|
+
` }`,
|
|
901
|
+
` const trimmed = current.trim();`,
|
|
902
|
+
` if (trimmed) parts.push(trimmed);`,
|
|
903
|
+
` return parts;`,
|
|
904
|
+
`}`,
|
|
905
|
+
``,
|
|
906
|
+
`function __dlNormalizeHeader(value: string): string {`,
|
|
907
|
+
` return value.trim().replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');`,
|
|
908
|
+
`}`,
|
|
909
|
+
``,
|
|
910
|
+
`function __dlOwnStringKeys(record: Record<string, unknown>): string[] {`,
|
|
911
|
+
` return Object.keys(record);`,
|
|
912
|
+
`}`,
|
|
913
|
+
``,
|
|
914
|
+
`function __dlReadOwnField(record: Record<string, unknown>, key: string): unknown {`,
|
|
915
|
+
` return record[key];`,
|
|
916
|
+
`}`,
|
|
917
|
+
``,
|
|
918
|
+
`function __dlGetNormalizedKey(record: Record<string, unknown>, part: string): string | null {`,
|
|
919
|
+
` const normalized = __dlNormalizeHeader(part);`,
|
|
920
|
+
` for (const key of __dlOwnStringKeys(record)) {`,
|
|
921
|
+
` if (__dlNormalizeHeader(key) === normalized) return key;`,
|
|
922
|
+
` }`,
|
|
923
|
+
` return null;`,
|
|
924
|
+
`}`,
|
|
925
|
+
``,
|
|
926
|
+
`function __dlGetRecordField(record: Record<string, unknown>, part: string): { found: boolean; value: unknown } {`,
|
|
927
|
+
` if (part in record) return { found: true, value: record[part] };`,
|
|
928
|
+
` const normalizedKey = __dlGetNormalizedKey(record, part);`,
|
|
929
|
+
` if (normalizedKey) return { found: true, value: __dlReadOwnField(record, normalizedKey) };`,
|
|
930
|
+
` const projectedValues = record.__deeplineCsvProjectedValues;`,
|
|
931
|
+
` if (projectedValues && typeof projectedValues === 'object' && !Array.isArray(projectedValues)) {`,
|
|
932
|
+
` const projectedRecord = projectedValues as Record<string, unknown>;`,
|
|
933
|
+
` if (part in projectedRecord) return { found: true, value: projectedRecord[part] };`,
|
|
934
|
+
` const projectedKey = __dlGetNormalizedKey(projectedRecord, part);`,
|
|
935
|
+
` if (projectedKey) return { found: true, value: __dlReadOwnField(projectedRecord, projectedKey) };`,
|
|
936
|
+
` }`,
|
|
937
|
+
` const data = record.data;`,
|
|
938
|
+
` if (data && typeof data === 'object' && !Array.isArray(data)) {`,
|
|
939
|
+
` const dataRecord = data as Record<string, unknown>;`,
|
|
940
|
+
` if (part in dataRecord) return { found: true, value: dataRecord[part] };`,
|
|
941
|
+
` const dataKey = __dlGetNormalizedKey(dataRecord, part);`,
|
|
942
|
+
` if (dataKey) return { found: true, value: __dlReadOwnField(dataRecord, dataKey) };`,
|
|
943
|
+
` }`,
|
|
944
|
+
` return { found: false, value: undefined };`,
|
|
945
|
+
`}`,
|
|
946
|
+
``,
|
|
947
|
+
`function __dlGetByPath(root: unknown, path: string): unknown {`,
|
|
948
|
+
` let cursor = root;`,
|
|
949
|
+
` const parts = __dlPathParts(path);`,
|
|
950
|
+
` for (let index = 0; index < parts.length; index += 1) {`,
|
|
951
|
+
` const part = parts[index] || '';`,
|
|
952
|
+
` cursor = __dlParseJsonContainer(cursor);`,
|
|
953
|
+
` if (!cursor || typeof cursor !== 'object') return undefined;`,
|
|
954
|
+
` const record = cursor as Record<string, unknown>;`,
|
|
955
|
+
` let field = { found: false, value: undefined as unknown };`,
|
|
956
|
+
` for (let end = parts.length; end > index + 1; end -= 1) {`,
|
|
957
|
+
` const dottedPart = parts.slice(index, end).join('.');`,
|
|
958
|
+
` field = __dlGetRecordField(record, dottedPart);`,
|
|
959
|
+
` if (field.found) {`,
|
|
960
|
+
` index = end - 1;`,
|
|
961
|
+
` break;`,
|
|
962
|
+
` }`,
|
|
963
|
+
` }`,
|
|
964
|
+
` if (!field.found) field = __dlGetRecordField(record, part);`,
|
|
965
|
+
` if (!field.found) return undefined;`,
|
|
966
|
+
` cursor = field.value;`,
|
|
967
|
+
` }`,
|
|
968
|
+
` return cursor;`,
|
|
969
|
+
`}`,
|
|
970
|
+
``,
|
|
971
|
+
`function __dlParseJsonContainer(value: unknown): unknown {`,
|
|
972
|
+
` if (typeof value !== 'string') return value;`,
|
|
973
|
+
` const trimmed = value.trim();`,
|
|
974
|
+
` if ((!trimmed.startsWith('{') || !trimmed.endsWith('}')) && (!trimmed.startsWith('[') || !trimmed.endsWith(']'))) return value;`,
|
|
975
|
+
` try {`,
|
|
976
|
+
` return JSON.parse(trimmed);`,
|
|
977
|
+
` } catch {`,
|
|
978
|
+
` return value;`,
|
|
979
|
+
` }`,
|
|
980
|
+
`}`,
|
|
981
|
+
``,
|
|
982
|
+
`function __dlMeaningful(value: unknown): boolean {`,
|
|
983
|
+
` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
|
|
984
|
+
` const record = value as Record<string, unknown>;`,
|
|
985
|
+
` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
|
|
986
|
+
` if (status === 'error' || status === 'failed') return false;`,
|
|
987
|
+
` if (typeof record.error === 'string' && record.error.trim()) return false;`,
|
|
988
|
+
` const result = record.result;`,
|
|
989
|
+
` if (result && typeof result === 'object' && !Array.isArray(result)) {`,
|
|
990
|
+
` const resultRecord = result as Record<string, unknown>;`,
|
|
991
|
+
` if (typeof resultRecord.error === 'string' && resultRecord.error.trim()) return false;`,
|
|
992
|
+
` if (typeof resultRecord.message === 'string' && resultRecord.message.trim()) return false;`,
|
|
993
|
+
` }`,
|
|
994
|
+
` if ('matched_result' in record) return __dlMeaningful(record.matched_result);`,
|
|
995
|
+
` }`,
|
|
996
|
+
` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
|
|
997
|
+
`}`,
|
|
998
|
+
``,
|
|
999
|
+
`function __dlErrorPayload(value: unknown): boolean {`,
|
|
1000
|
+
` if (!value || typeof value !== 'object' || Array.isArray(value)) return false;`,
|
|
1001
|
+
` const record = value as Record<string, unknown>;`,
|
|
1002
|
+
` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
|
|
1003
|
+
` const result = record.result;`,
|
|
1004
|
+
` const resultError = result && typeof result === 'object' && !Array.isArray(result) ? (result as Record<string, unknown>).error : null;`,
|
|
1005
|
+
` return status === 'error' || status === 'failed' || (typeof record.error === 'string' && record.error.trim() !== '') || (typeof resultError === 'string' && resultError.trim() !== '');`,
|
|
1006
|
+
`}`,
|
|
1007
|
+
``,
|
|
1008
|
+
`function __dlAssertSuccessfulToolResult(value: unknown): void {`,
|
|
1009
|
+
` if (!__dlErrorPayload(value)) return;`,
|
|
1010
|
+
` const record = value as Record<string, unknown>;`,
|
|
1011
|
+
` const result = record.result && typeof record.result === 'object' && !Array.isArray(record.result) ? record.result as Record<string, unknown> : null;`,
|
|
1012
|
+
` const message = typeof record.error === 'string' ? record.error : typeof result?.error === 'string' ? result.error : typeof result?.message === 'string' ? result.message : 'Tool returned an error-shaped result.';`,
|
|
1013
|
+
` throw new Error(message);`,
|
|
1014
|
+
`}`,
|
|
1015
|
+
``,
|
|
1016
|
+
`function __dlGeneratedCellError(value: unknown): boolean {`,
|
|
1017
|
+
` if (typeof value === 'string') {`,
|
|
1018
|
+
` const lower = value.trim().toLowerCase();`,
|
|
1019
|
+
` if (!lower) return false;`,
|
|
1020
|
+
` if (/^column\\s+.+\\s+failed\\b/.test(lower)) return true;`,
|
|
1021
|
+
` if (lower.startsWith('error:') || lower.includes(': error:') || lower.includes('"error"')) return true;`,
|
|
1022
|
+
` try {`,
|
|
1023
|
+
` return __dlGeneratedCellError(JSON.parse(value));`,
|
|
1024
|
+
` } catch {`,
|
|
1025
|
+
` return false;`,
|
|
1026
|
+
` }`,
|
|
1027
|
+
` }`,
|
|
1028
|
+
` if (!__dlRecord(value)) return false;`,
|
|
1029
|
+
` const status = typeof value.status === 'string' ? value.status.toLowerCase() : '';`,
|
|
1030
|
+
` if (status === 'error' || status === 'failed') return true;`,
|
|
1031
|
+
` if (typeof value.error === 'string' && value.error.trim() !== '') return true;`,
|
|
1032
|
+
` const raw = __dlGetByPath(value, 'toolResponse.raw') ?? __dlGetByPath(value, 'toolOutput.raw');`,
|
|
1033
|
+
` if (raw !== undefined && raw !== value && __dlGeneratedCellError(raw)) return true;`,
|
|
1034
|
+
` const result = value.result;`,
|
|
1035
|
+
` return result !== undefined && result !== value && __dlGeneratedCellError(result);`,
|
|
1036
|
+
`}`,
|
|
1037
|
+
``,
|
|
1038
|
+
`function __dlStripErroredGeneratedColumns(row: Record<string, unknown>, aliases: string[]): Record<string, unknown> {`,
|
|
1039
|
+
` let next: Record<string, unknown> | null = null;`,
|
|
1040
|
+
` for (const alias of aliases) {`,
|
|
1041
|
+
` for (const key of __dlAliasCandidates(alias)) {`,
|
|
1042
|
+
` if (key in row && __dlGeneratedCellError(row[key])) {`,
|
|
1043
|
+
` if (next === null) next = { ...row };`,
|
|
1044
|
+
` delete next[key];`,
|
|
1045
|
+
` }`,
|
|
1046
|
+
` }`,
|
|
1047
|
+
` }`,
|
|
1048
|
+
` return next ?? row;`,
|
|
1049
|
+
`}`,
|
|
1050
|
+
``,
|
|
1051
|
+
`function __dlTemplateContext(row: Record<string, unknown>): Record<string, unknown> {`,
|
|
1052
|
+
` const context: Record<string, unknown> = {};`,
|
|
1053
|
+
` const projectedValues = row.__deeplineCsvProjectedValues;`,
|
|
1054
|
+
` if (__dlRecord(projectedValues)) {`,
|
|
1055
|
+
` for (const [key, value] of Object.entries(projectedValues)) context[key] = value;`,
|
|
1056
|
+
` }`,
|
|
1057
|
+
` for (const [key, value] of Object.entries(row)) {`,
|
|
1058
|
+
` if (key.startsWith('__deepline')) continue;`,
|
|
1059
|
+
` context[key] = value;`,
|
|
1060
|
+
` }`,
|
|
1061
|
+
` return context;`,
|
|
1062
|
+
`}`,
|
|
1063
|
+
``,
|
|
1064
|
+
`function __dlPrepareEnrichRow(row: Record<string, unknown>, aliases: string[], sourceRowIndex?: number): Record<string, unknown> {`,
|
|
1065
|
+
` const cleaned = __dlStripErroredGeneratedColumns(row, aliases);`,
|
|
1066
|
+
` const templateContext = __dlTemplateContext(cleaned);`,
|
|
1067
|
+
` return {`,
|
|
1068
|
+
` ...templateContext,`,
|
|
1069
|
+
` ...cleaned,`,
|
|
1070
|
+
` ...(typeof sourceRowIndex === 'number' && '__deeplineSourceRowIndex' in cleaned ? { __deeplineOriginalSourceRowIndex: cleaned.__deeplineSourceRowIndex } : {}),`,
|
|
1071
|
+
` ...(typeof sourceRowIndex === 'number' ? { __deeplineSourceRowIndex: sourceRowIndex } : {}),`,
|
|
1072
|
+
` __deeplineCsvProjectedFields: Object.keys(templateContext),`,
|
|
1073
|
+
` __deeplineCsvProjectedValues: templateContext,`,
|
|
1074
|
+
` };`,
|
|
1075
|
+
`}`,
|
|
1076
|
+
``,
|
|
1077
|
+
`function __dlRawToolOutput(result: unknown): unknown {`,
|
|
1078
|
+
` if (!result || typeof result !== 'object') return result;`,
|
|
1079
|
+
` const record = result as Record<string, unknown>;`,
|
|
1080
|
+
` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
|
|
1081
|
+
`}`,
|
|
1082
|
+
``,
|
|
1083
|
+
`function __dlPushCandidate(candidates: unknown[], value: unknown): void {`,
|
|
1084
|
+
` if (value === null || value === undefined) return;`,
|
|
1085
|
+
` if (!candidates.includes(value)) candidates.push(value);`,
|
|
1086
|
+
`}`,
|
|
1087
|
+
``,
|
|
1088
|
+
`function __dlRawToolCandidates(raw: unknown): unknown[] {`,
|
|
1089
|
+
` const candidates: unknown[] = [raw];`,
|
|
1090
|
+
` if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return candidates;`,
|
|
1091
|
+
` const record = raw as Record<string, unknown>;`,
|
|
1092
|
+
` __dlPushCandidate(candidates, record.data);`,
|
|
1093
|
+
` __dlPushCandidate(candidates, record.result);`,
|
|
1094
|
+
` __dlPushCandidate(candidates, record.output);`,
|
|
1095
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'data.data'));`,
|
|
1096
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'result.data'));`,
|
|
1097
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'result.data.data'));`,
|
|
1098
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.data'));`,
|
|
1099
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.data.data'));`,
|
|
1100
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.body'));`,
|
|
1101
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.body.data'));`,
|
|
1102
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolResponse.raw'));`,
|
|
1103
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolOutput.raw'));`,
|
|
1104
|
+
` return candidates;`,
|
|
1105
|
+
`}`,
|
|
1106
|
+
``,
|
|
1107
|
+
`function __dlLegacyResultData(value: unknown): unknown {`,
|
|
1108
|
+
` if (!value || typeof value !== 'object' || Array.isArray(value)) return value;`,
|
|
1109
|
+
` const record = value as Record<string, unknown>;`,
|
|
1110
|
+
` return 'data' in record ? record.data : value;`,
|
|
1111
|
+
`}`,
|
|
1112
|
+
``,
|
|
1113
|
+
`function __dlLegacyOutputData(result: unknown, raw: unknown): unknown {`,
|
|
1114
|
+
` const rawRecord = raw && typeof raw === 'object' && !Array.isArray(raw) ? (raw as Record<string, unknown>) : null;`,
|
|
1115
|
+
` const data = rawRecord && 'data' in rawRecord ? rawRecord.data : raw;`,
|
|
1116
|
+
` const existingResult = rawRecord && rawRecord.result && typeof rawRecord.result === 'object' && !Array.isArray(rawRecord.result) ? (rawRecord.result as Record<string, unknown>) : null;`,
|
|
1117
|
+
` const resultData = existingResult && 'data' in existingResult ? __dlLegacyResultData(existingResult.data) : __dlLegacyResultData(data);`,
|
|
1118
|
+
` const resultObject = resultData && typeof resultData === 'object' && !Array.isArray(resultData) ? (resultData as Record<string, unknown>) : {};`,
|
|
1119
|
+
` return {`,
|
|
1120
|
+
` ...(rawRecord ?? {}),`,
|
|
1121
|
+
` data,`,
|
|
1122
|
+
` result: { ...resultObject, ...(existingResult ?? {}), data: resultData },`,
|
|
1123
|
+
` raw,`,
|
|
1124
|
+
` toolResponse: { raw },`,
|
|
1125
|
+
` originalResult: result,`,
|
|
1126
|
+
` };`,
|
|
1127
|
+
`}`,
|
|
1128
|
+
``,
|
|
1129
|
+
`function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
|
|
1130
|
+
` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
|
|
1131
|
+
` if (value && typeof value === 'object') {`,
|
|
1132
|
+
` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
|
|
1133
|
+
` }`,
|
|
1134
|
+
` if (typeof value !== 'string') return value;`,
|
|
1135
|
+
` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
|
|
1136
|
+
` if (exact) return __dlGetByPath(row, exact[1] || '');`,
|
|
1137
|
+
` let rendered = '';`,
|
|
1138
|
+
` let cursor = 0;`,
|
|
1139
|
+
` while (cursor < value.length) {`,
|
|
1140
|
+
` const open = value.indexOf('{{', cursor);`,
|
|
1141
|
+
` if (open < 0) {`,
|
|
1142
|
+
` rendered += value.slice(cursor);`,
|
|
1143
|
+
` break;`,
|
|
1144
|
+
` }`,
|
|
1145
|
+
` const close = value.indexOf('}}', open + 2);`,
|
|
1146
|
+
` if (close < 0) {`,
|
|
1147
|
+
` rendered += value.slice(cursor);`,
|
|
1148
|
+
` break;`,
|
|
1149
|
+
` }`,
|
|
1150
|
+
` rendered += value.slice(cursor, open);`,
|
|
1151
|
+
` const path = value.slice(open + 2, close).trim();`,
|
|
1152
|
+
` const replacement = __dlGetByPath(row, path);`,
|
|
1153
|
+
` rendered += replacement === null || replacement === undefined ? '' : String(replacement);`,
|
|
1154
|
+
` cursor = close + 2;`,
|
|
1155
|
+
` }`,
|
|
1156
|
+
` return rendered;`,
|
|
1157
|
+
`}`,
|
|
1158
|
+
``,
|
|
1159
|
+
`function __dlStableRowKey(row: Record<string, unknown>, index: number): string {`,
|
|
1160
|
+
` for (const key of ['id', 'ID', 'row_key', 'ROW_KEY', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN', 'name', 'Name']) {`,
|
|
1161
|
+
` const value = row[key];`,
|
|
1162
|
+
` if (__dlMeaningful(value)) return String(value);`,
|
|
1163
|
+
` }`,
|
|
1164
|
+
` return String(index);`,
|
|
1165
|
+
`}`,
|
|
1166
|
+
``,
|
|
1167
|
+
`function __dlEnrichRowKey(row: Record<string, unknown>, index: number): string {`,
|
|
1168
|
+
' return `${__dlStableRowKey(row, index)}:${index}`;',
|
|
1169
|
+
`}`,
|
|
1170
|
+
``,
|
|
1171
|
+
`function __dlScalarValue(value: unknown): unknown {`,
|
|
1172
|
+
` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
|
|
1173
|
+
` const record = value as Record<string, unknown>;`,
|
|
1174
|
+
` if ('matched_result' in record) return __dlScalarValue(record.matched_result);`,
|
|
1175
|
+
` for (const key of ['value', 'email', 'output', 'result', 'data']) {`,
|
|
1176
|
+
` const nested = record[key];`,
|
|
1177
|
+
` if (__dlMeaningful(nested)) return __dlScalarValue(nested);`,
|
|
1178
|
+
` }`,
|
|
1179
|
+
` }`,
|
|
1180
|
+
` return value;`,
|
|
1181
|
+
`}`,
|
|
1182
|
+
``,
|
|
1183
|
+
`function __dlFirstMeaningful(row: Record<string, unknown>, aliases: string[]): unknown {`,
|
|
1184
|
+
` for (const alias of aliases) {`,
|
|
1185
|
+
` const value = row[alias];`,
|
|
1186
|
+
` if (__dlMeaningful(value)) return __dlScalarValue(value);`,
|
|
1187
|
+
` }`,
|
|
1188
|
+
` return null;`,
|
|
1189
|
+
`}`,
|
|
1190
|
+
``,
|
|
1191
|
+
`function __dlFirstMeaningfulAlias(row: Record<string, unknown>, aliases: string[]): string | null {`,
|
|
1192
|
+
` for (const alias of aliases) {`,
|
|
1193
|
+
` if (__dlMeaningful(row[alias])) return alias;`,
|
|
1194
|
+
` }`,
|
|
1195
|
+
` return null;`,
|
|
1196
|
+
`}`,
|
|
1197
|
+
``,
|
|
1198
|
+
`function __dlListValue(value: unknown): unknown[] {`,
|
|
1199
|
+
` if (Array.isArray(value)) return value.filter(__dlMeaningful);`,
|
|
1200
|
+
` if (value && typeof value === 'object') {`,
|
|
1201
|
+
` const record = value as Record<string, unknown>;`,
|
|
1202
|
+
` if ('matched_result' in record) return __dlListValue(record.matched_result);`,
|
|
1203
|
+
` for (const key of ['result', 'value', 'data']) {`,
|
|
1204
|
+
` const nested = record[key];`,
|
|
1205
|
+
` const nestedList = __dlListValue(nested);`,
|
|
1206
|
+
` if (nestedList.length > 0) return nestedList;`,
|
|
1207
|
+
` }`,
|
|
1208
|
+
` return __dlMeaningful(value) ? [value] : [];`,
|
|
1209
|
+
` }`,
|
|
1210
|
+
` return __dlMeaningful(value) ? [value] : [];`,
|
|
1211
|
+
`}`,
|
|
1212
|
+
``,
|
|
1213
|
+
`function __dlFirstMinResults(row: Record<string, unknown>, aliases: string[], minResults: number): unknown {`,
|
|
1214
|
+
` const values: unknown[] = [];`,
|
|
1215
|
+
` for (const alias of aliases) {`,
|
|
1216
|
+
` values.push(...__dlListValue(row[alias]));`,
|
|
1217
|
+
` if (values.length >= minResults) return values;`,
|
|
1218
|
+
` }`,
|
|
1219
|
+
` return values.length > 0 ? values : null;`,
|
|
1220
|
+
`}`,
|
|
1221
|
+
``,
|
|
1222
|
+
`function __dlContributingAliases(row: Record<string, unknown>, aliases: string[]): string[] | null {`,
|
|
1223
|
+
` const sources: string[] = [];`,
|
|
1224
|
+
` for (const alias of aliases) {`,
|
|
1225
|
+
` if (__dlListValue(row[alias]).length > 0) sources.push(alias);`,
|
|
1226
|
+
` }`,
|
|
1227
|
+
` return sources.length > 0 ? sources : null;`,
|
|
1228
|
+
`}`,
|
|
1229
|
+
``,
|
|
1230
|
+
`function __dlWaterfallSatisfied(row: Record<string, unknown>, aliases: string[], minResults: number): boolean {`,
|
|
1231
|
+
` let count = 0;`,
|
|
1232
|
+
` for (const alias of aliases) {`,
|
|
1233
|
+
` count += __dlListValue(row[alias]).length;`,
|
|
1234
|
+
` if (count >= __dlAtLeastOneInteger(minResults)) return true;`,
|
|
1235
|
+
` }`,
|
|
1236
|
+
` return false;`,
|
|
1237
|
+
`}`,
|
|
1238
|
+
``,
|
|
1239
|
+
`function __dlKeyPaths(key: string): string[] {`,
|
|
1240
|
+
` const normalized = String(key || '').trim();`,
|
|
1241
|
+
` if (normalized === 'email') return ['email', 'email_address', 'person.email.email', 'data.person.email.email', 'result.person.email.email', 'result.data.person.email.email', 'person.email', 'contact.email', 'data.email', 'result.data.email'];`,
|
|
1242
|
+
` if (normalized === 'personal_email') return ['personal_email', 'email', 'email_address', 'data.personal_email', 'data.email'];`,
|
|
1243
|
+
` if (normalized === 'phone') return ['phone', 'phone_number', 'mobile_phone', 'mobile_phone_number', 'data.phone'];`,
|
|
1244
|
+
` if (normalized === 'linkedin') return ['linkedin', 'linkedin_url', 'linkedin_profile', 'profile_url', 'person.linkedin', 'person.linkedin_url'];`,
|
|
1245
|
+
` if (normalized === 'full_name') return ['full_name', 'name', 'person.full_name', 'person.name'];`,
|
|
1246
|
+
` if (normalized === 'first_name') return ['first_name', 'person.first_name'];`,
|
|
1247
|
+
` if (normalized === 'last_name') return ['last_name', 'person.last_name'];`,
|
|
1248
|
+
` if (normalized === 'title') return ['title', 'job_title', 'current_title', 'headline', 'person.title'];`,
|
|
1249
|
+
` if (normalized === 'company_name') return ['company_name', 'company.name', 'organization.name'];`,
|
|
1250
|
+
` if (normalized === 'company_domain') return ['company_domain', 'domain', 'company.domain', 'organization.domain'];`,
|
|
1251
|
+
` if (normalized === 'status') return ['status', 'verdict', 'state'];`,
|
|
1252
|
+
` if (normalized === 'email_status') return ['email_status', 'status', 'verdict', 'data.email_status'];`,
|
|
1253
|
+
` return [normalized];`,
|
|
1254
|
+
`}`,
|
|
1255
|
+
``,
|
|
1256
|
+
`function __dlSelectorKeys(selector: unknown): string[] | null {`,
|
|
1257
|
+
` if (!selector || typeof selector !== 'object' || Array.isArray(selector)) return null;`,
|
|
1258
|
+
` const keys = (selector as { keys?: unknown }).keys;`,
|
|
1259
|
+
` if (!Array.isArray(keys) || keys.length === 0) return null;`,
|
|
1260
|
+
` const out: string[] = [];`,
|
|
1261
|
+
` for (const key of keys) {`,
|
|
1262
|
+
` if (typeof key === 'string' && key.trim()) out.push(key.trim());`,
|
|
1263
|
+
` }`,
|
|
1264
|
+
` return out.length > 0 ? out : null;`,
|
|
1265
|
+
`}`,
|
|
1266
|
+
``,
|
|
1267
|
+
`function __dlFirstByPaths(payload: unknown, paths: string[] | string): unknown {`,
|
|
1268
|
+
` const candidates = Array.isArray(paths) ? paths : [paths];`,
|
|
1269
|
+
` for (const path of candidates) {`,
|
|
1270
|
+
` for (const candidate of __dlRawToolCandidates(payload)) {`,
|
|
1271
|
+
` const value = __dlGetByPath(candidate, String(path));`,
|
|
1272
|
+
` if (__dlMeaningful(value)) return value;`,
|
|
1273
|
+
` }`,
|
|
1274
|
+
` }`,
|
|
1275
|
+
` return null;`,
|
|
1276
|
+
`}`,
|
|
1277
|
+
``,
|
|
1278
|
+
`function __dlExtractTarget(payload: unknown, key: string): unknown {`,
|
|
1279
|
+
` return __dlFirstByPaths(payload, __dlKeyPaths(key));`,
|
|
1280
|
+
`}`,
|
|
1281
|
+
``,
|
|
1282
|
+
`function __dlExtractedValue(result: unknown, key: string): unknown {`,
|
|
1283
|
+
` if (!result || typeof result !== 'object' || Array.isArray(result)) return undefined;`,
|
|
1284
|
+
` const extractedValues = (result as Record<string, unknown>).extractedValues;`,
|
|
1285
|
+
` if (!extractedValues || typeof extractedValues !== 'object' || Array.isArray(extractedValues)) return undefined;`,
|
|
1286
|
+
` const accessor = (extractedValues as Record<string, unknown>)[key];`,
|
|
1287
|
+
` if (!accessor || typeof accessor !== 'object' || Array.isArray(accessor)) return undefined;`,
|
|
1288
|
+
` const record = accessor as Record<string, unknown>;`,
|
|
1289
|
+
` const get = record.get;`,
|
|
1290
|
+
` if (typeof get === 'function') {`,
|
|
1291
|
+
` try {`,
|
|
1292
|
+
` return (get as () => unknown)();`,
|
|
1293
|
+
` } catch {`,
|
|
1294
|
+
` return undefined;`,
|
|
1295
|
+
` }`,
|
|
1296
|
+
` }`,
|
|
1297
|
+
` return record.value;`,
|
|
1298
|
+
`}`,
|
|
1299
|
+
``,
|
|
1300
|
+
`function __dlLegacyExtractorPayload(payload: unknown, raw: unknown): unknown {`,
|
|
1301
|
+
` if (payload === undefined || payload === null) return raw;`,
|
|
1302
|
+
` if (payload && typeof payload === 'object' && !Array.isArray(payload) && Object.keys(payload as Record<string, unknown>).length === 0) return raw;`,
|
|
1303
|
+
` return payload;`,
|
|
1304
|
+
`}`,
|
|
1305
|
+
``,
|
|
1306
|
+
`function __dlLegacyMatchedEnvelope(value: unknown, raw: unknown): unknown {`,
|
|
1307
|
+
` if (value && typeof value === 'object' && !Array.isArray(value) && 'matched_result' in (value as Record<string, unknown>)) return value;`,
|
|
1308
|
+
` const legacyOutput = __dlLegacyOutputData(undefined, raw) as Record<string, unknown>;`,
|
|
1309
|
+
` const legacyResult = legacyOutput.result && typeof legacyOutput.result === 'object' && !Array.isArray(legacyOutput.result) ? legacyOutput.result : raw;`,
|
|
1310
|
+
` return { matched_result: value, result: legacyResult };`,
|
|
1311
|
+
`}`,
|
|
1312
|
+
``,
|
|
1313
|
+
`function __dlString(value: unknown): string | null {`,
|
|
1314
|
+
` return typeof value === 'string' && value.trim() ? value.trim() : null;`,
|
|
1315
|
+
`}`,
|
|
1316
|
+
``,
|
|
1317
|
+
`// NOTE: email_status is NOT normalized here. It is materialized once by the`,
|
|
1318
|
+
`// provider's emailStatus({...}) contract via buildEmailStatus and read back`,
|
|
1319
|
+
`// through __dlExtractedValue(result, 'email_status') below. There is no`,
|
|
1320
|
+
`// legacy string coarsening — see CONTEXT.md "Provider Email Status Contract".`,
|
|
1321
|
+
``,
|
|
1322
|
+
`function __dlListPathCandidates(selector: unknown): string[] {`,
|
|
1323
|
+
` const paths: string[] = [];`,
|
|
1324
|
+
` const push = (path: string) => { if (path && !paths.includes(path)) paths.push(path); };`,
|
|
1325
|
+
` if (Array.isArray(selector)) {`,
|
|
1326
|
+
` for (const path of selector) push(String(path));`,
|
|
1327
|
+
` } else if (typeof selector === 'string') {`,
|
|
1328
|
+
` push(selector);`,
|
|
1329
|
+
` }`,
|
|
1330
|
+
` for (const path of ['data', 'data.people', 'data.persons', 'data.contacts', 'data.results', 'data.leads', 'data.items', 'result.data', 'result.people', 'result.persons', 'result.contacts', 'result.results', 'result.leads', 'result.items', 'people', 'persons', 'contacts', 'results', 'leads', 'items', 'output.body', 'body']) push(path);`,
|
|
1331
|
+
` return paths;`,
|
|
1332
|
+
`}`,
|
|
1333
|
+
``,
|
|
1334
|
+
`function __dlFindList(payload: unknown, selector: unknown): unknown[] {`,
|
|
1335
|
+
` if (Array.isArray(payload)) return payload;`,
|
|
1336
|
+
` for (const path of __dlListPathCandidates(selector)) {`,
|
|
1337
|
+
` for (const candidate of __dlRawToolCandidates(payload)) {`,
|
|
1338
|
+
` const value = __dlGetByPath(candidate, path);`,
|
|
1339
|
+
` if (Array.isArray(value)) return value;`,
|
|
1340
|
+
` }`,
|
|
1341
|
+
` }`,
|
|
1342
|
+
` const queue = __dlRawToolCandidates(payload);`,
|
|
1343
|
+
` const seen: unknown[] = [];`,
|
|
1344
|
+
` for (let index = 0; index < queue.length; index += 1) {`,
|
|
1345
|
+
` const current = queue[index];`,
|
|
1346
|
+
` if (!current || typeof current !== 'object' || seen.includes(current)) continue;`,
|
|
1347
|
+
` seen.push(current);`,
|
|
1348
|
+
` for (const value of Object.values(current as Record<string, unknown>)) {`,
|
|
1349
|
+
` if (Array.isArray(value)) return value;`,
|
|
1350
|
+
` if (value && typeof value === 'object') queue.push(value);`,
|
|
1351
|
+
` }`,
|
|
1352
|
+
` }`,
|
|
1353
|
+
` return [];`,
|
|
1354
|
+
`}`,
|
|
1355
|
+
``,
|
|
1356
|
+
`function __dlDeriveFullName(row: Record<string, unknown>): string | null {`,
|
|
1357
|
+
` const existing = __dlFirstByPaths(row, ['full_name', 'name']);`,
|
|
1358
|
+
` if (typeof existing === 'string' && existing.trim()) return existing.trim();`,
|
|
1359
|
+
` const first = __dlFirstByPaths(row, ['first_name', 'firstName']);`,
|
|
1360
|
+
` const last = __dlFirstByPaths(row, ['last_name', 'lastName']);`,
|
|
1361
|
+
` const parts = [first, last].filter((part): part is string => typeof part === 'string').map((part) => part.trim()).filter(Boolean);`,
|
|
1362
|
+
` return parts.length > 0 ? parts.join(' ') : null;`,
|
|
1363
|
+
`}`,
|
|
1364
|
+
``,
|
|
1365
|
+
`function __dlProjectListRows(rows: unknown[], keys: string[], payload: unknown): Array<Record<string, unknown>> {`,
|
|
1366
|
+
` const projected: Array<Record<string, unknown>> = [];`,
|
|
1367
|
+
` for (const row of rows) {`,
|
|
1368
|
+
` const record = row && typeof row === 'object' && !Array.isArray(row) ? (row as Record<string, unknown>) : { value: row };`,
|
|
1369
|
+
` const out: Record<string, unknown> = {};`,
|
|
1370
|
+
` for (const key of keys) {`,
|
|
1371
|
+
` let value = __dlExtractTarget(record, key);`,
|
|
1372
|
+
` if (!__dlMeaningful(value) && key === 'full_name') value = __dlDeriveFullName(record);`,
|
|
1373
|
+
` if (!__dlMeaningful(value)) value = __dlExtractTarget(payload, key);`,
|
|
1374
|
+
` out[key] = value === undefined ? null : value;`,
|
|
1375
|
+
` }`,
|
|
1376
|
+
` if (Object.values(out).some(__dlMeaningful)) projected.push(out);`,
|
|
1377
|
+
` }`,
|
|
1378
|
+
` return projected;`,
|
|
1379
|
+
`}`,
|
|
1380
|
+
``,
|
|
1381
|
+
`type __DlExtractorHelpers = { row: Record<string, unknown>; result: unknown; data: unknown; raw: unknown; pick: (paths: string[] | string) => unknown; extract: (...args: unknown[]) => unknown; extractList: (...args: unknown[]) => unknown[]; target: (paths: string[] | string) => unknown; get: (path: string) => unknown };`,
|
|
1382
|
+
``,
|
|
1383
|
+
`function __dlInlineExtractorArgs(args: unknown[]): { payload: unknown; selector: unknown } {`,
|
|
1384
|
+
` if (args.length >= 3) return { payload: args[1], selector: args[2] };`,
|
|
1385
|
+
` if (args.length >= 2) return { payload: args[0], selector: args[1] };`,
|
|
1386
|
+
` return { payload: args[0], selector: undefined };`,
|
|
1387
|
+
`}`,
|
|
1388
|
+
``,
|
|
1389
|
+
`function __dlInlineExtract(...args: unknown[]): unknown {`,
|
|
1390
|
+
` const { payload, selector } = __dlInlineExtractorArgs(args);`,
|
|
1391
|
+
` if (selector === undefined) return payload;`,
|
|
1392
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
1393
|
+
` if (keys) return Object.fromEntries(keys.map((key) => [key, __dlExtractTarget(payload, key) ?? null]));`,
|
|
1394
|
+
` if (Array.isArray(selector)) return __dlFirstByPaths(payload, selector.map(String));`,
|
|
1395
|
+
` if (typeof selector === 'string') return __dlExtractTarget(payload, selector) ?? __dlFirstByPaths(payload, selector);`,
|
|
1396
|
+
` return selector;`,
|
|
1397
|
+
`}`,
|
|
1398
|
+
``,
|
|
1399
|
+
`function __dlInlineExtractList(...args: unknown[]): unknown[] {`,
|
|
1400
|
+
` const { payload, selector } = __dlInlineExtractorArgs(args);`,
|
|
1401
|
+
` const rows = __dlFindList(payload, selector);`,
|
|
1402
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
1403
|
+
` return keys ? __dlProjectListRows(rows, keys, payload) : rows;`,
|
|
1404
|
+
`}`,
|
|
1405
|
+
``,
|
|
1406
|
+
`function __dlExtract(alias: string, result: unknown, row: Record<string, unknown>, extractor: ((args: __DlExtractorHelpers) => unknown) | null, legacyEnvelope = false): unknown {`,
|
|
1407
|
+
` const raw = __dlRawToolOutput(result);`,
|
|
1408
|
+
` if (!extractor) return raw;`,
|
|
1409
|
+
` const pick = (paths: string[] | string) => {`,
|
|
1410
|
+
` const requestedPaths = Array.isArray(paths) ? paths : [paths];`,
|
|
1411
|
+
` for (const requestedPath of requestedPaths) {`,
|
|
1412
|
+
` for (const path of __dlKeyPaths(String(requestedPath))) {`,
|
|
1413
|
+
` const extractedValue = __dlExtractedValue(result, path);`,
|
|
1414
|
+
` if (__dlMeaningful(extractedValue)) return extractedValue;`,
|
|
1415
|
+
` for (const candidate of __dlRawToolCandidates(raw)) {`,
|
|
1416
|
+
` const value = __dlGetByPath(candidate, path);`,
|
|
1417
|
+
` if (__dlMeaningful(value)) return value;`,
|
|
1418
|
+
` }`,
|
|
1419
|
+
` }`,
|
|
1420
|
+
` }`,
|
|
1421
|
+
` return null;`,
|
|
1422
|
+
` };`,
|
|
1423
|
+
` const extract = (...args: unknown[]): unknown => {`,
|
|
1424
|
+
` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
|
|
1425
|
+
` const selector = args.length >= 3 ? args[2] : args[0];`,
|
|
1426
|
+
` if (selector === undefined) return payload;`,
|
|
1427
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
1428
|
+
` if (keys) return Object.fromEntries(keys.map((key) => [key, __dlExtractTarget(payload, key) ?? null]));`,
|
|
1429
|
+
` if (Array.isArray(selector)) return __dlFirstByPaths(payload, selector.map(String));`,
|
|
1430
|
+
` if (typeof selector === 'string') {`,
|
|
1431
|
+
` const extractedValue = __dlExtractedValue(result, selector);`,
|
|
1432
|
+
` if (__dlMeaningful(extractedValue)) return __dlLegacyMatchedEnvelope(extractedValue, payload);`,
|
|
1433
|
+
` return __dlExtractTarget(payload, selector) ?? __dlFirstByPaths(payload, selector);`,
|
|
1434
|
+
` }`,
|
|
1435
|
+
` return selector;`,
|
|
1436
|
+
` };`,
|
|
1437
|
+
` const extractList = (...args: unknown[]): unknown[] => {`,
|
|
1438
|
+
` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
|
|
1439
|
+
` const selector = args.length >= 3 ? args[2] : args[0];`,
|
|
1440
|
+
` const rows = __dlFindList(payload, selector);`,
|
|
1441
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
1442
|
+
` return keys ? __dlProjectListRows(rows, keys, payload) : rows;`,
|
|
1443
|
+
` };`,
|
|
1444
|
+
` const get = (path: string): unknown => __dlExtractedValue(result, path) ?? __dlFirstByPaths(raw, path);`,
|
|
1445
|
+
` let resolved: unknown;`,
|
|
1446
|
+
` const extracted = extractor({ row, result, data: raw, raw, pick, extract, extractList, target: pick, get });`,
|
|
1447
|
+
` resolved = typeof extracted === 'function' ? (extracted as (outputData: unknown) => unknown)(__dlLegacyOutputData(result, raw)) : extracted;`,
|
|
1448
|
+
` if (resolved && typeof resolved === 'object' && !Array.isArray(resolved) && alias in (resolved as Record<string, unknown>)) {`,
|
|
1449
|
+
` const aliasValue = (resolved as Record<string, unknown>)[alias];`,
|
|
1450
|
+
` return legacyEnvelope && __dlMeaningful(aliasValue) ? __dlLegacyMatchedEnvelope(aliasValue, raw) : aliasValue;`,
|
|
1451
|
+
` }`,
|
|
1452
|
+
` if (Array.isArray(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
|
|
1453
|
+
` if ((resolved === null || resolved === undefined) && __dlErrorPayload(raw)) return raw;`,
|
|
1454
|
+
` if (legacyEnvelope && __dlMeaningful(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
|
|
1455
|
+
` return resolved === undefined ? raw : resolved;`,
|
|
1456
|
+
`}`,
|
|
1457
|
+
``,
|
|
1458
|
+
`function __dlMergeContextRecord(existing: unknown, defaults: Record<string, unknown>): Record<string, unknown> {`,
|
|
1459
|
+
` if (!existing || typeof existing !== 'object' || Array.isArray(existing)) return { ...defaults };`,
|
|
1460
|
+
` return { ...defaults, ...(existing as Record<string, unknown>) };`,
|
|
1461
|
+
`}`,
|
|
1462
|
+
``,
|
|
1463
|
+
`function __dlCompactJavascriptContext(value: unknown, depth = 0): unknown {`,
|
|
1464
|
+
` if (depth > 8) return '[Object]';`,
|
|
1465
|
+
` if (typeof value === 'string') return value.length > 20000 ? value.slice(0, 20000) + '...[truncated]' : value;`,
|
|
1466
|
+
` if (!value || typeof value !== 'object') return value;`,
|
|
1467
|
+
` if (Array.isArray(value)) {`,
|
|
1468
|
+
` const limit = depth <= 2 ? 25 : 10;`,
|
|
1469
|
+
` const items = value.slice(0, limit).map((entry) => __dlCompactJavascriptContext(entry, depth + 1));`,
|
|
1470
|
+
` if (value.length > limit) items.push({ __deepline_truncated_items: value.length - limit });`,
|
|
1471
|
+
` return items;`,
|
|
1472
|
+
` }`,
|
|
1473
|
+
` const out: Record<string, unknown> = {};`,
|
|
1474
|
+
` let count = 0;`,
|
|
1475
|
+
` for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {`,
|
|
1476
|
+
` count += 1;`,
|
|
1477
|
+
` if (count > 80) {`,
|
|
1478
|
+
` out.__deepline_truncated_fields = count - 80;`,
|
|
1479
|
+
` break;`,
|
|
1480
|
+
` }`,
|
|
1481
|
+
` out[key] = __dlCompactJavascriptContext(entry, depth + 1);`,
|
|
1482
|
+
` }`,
|
|
1483
|
+
` return out;`,
|
|
1484
|
+
`}`,
|
|
1485
|
+
``,
|
|
1486
|
+
`function __dlRuntimePayload(tool: string, payload: Record<string, unknown>, row: Record<string, unknown>): Record<string, unknown> {`,
|
|
1487
|
+
` if (tool !== 'run_javascript') return payload;`,
|
|
1488
|
+
` const compactRow = __dlCompactJavascriptContext(row) as Record<string, unknown>;`,
|
|
1489
|
+
` return {`,
|
|
1490
|
+
` ...payload,`,
|
|
1491
|
+
` row: __dlMergeContextRecord(payload.row, compactRow),`,
|
|
1492
|
+
` input: __dlMergeContextRecord(payload.input, compactRow),`,
|
|
1493
|
+
` context: __dlMergeContextRecord(payload.context, compactRow),`,
|
|
1494
|
+
` };`,
|
|
1495
|
+
`}`,
|
|
1496
|
+
``,
|
|
1497
|
+
`function __dlBlankPayloadValue(value: unknown): boolean {`,
|
|
1498
|
+
` return value === null || value === undefined || (typeof value === 'string' && value.trim() === '');`,
|
|
1499
|
+
`}`,
|
|
1500
|
+
``,
|
|
1501
|
+
`function __dlShouldSkipEmptyPayload(tool: string, payload: Record<string, unknown>): boolean {`,
|
|
1502
|
+
` if (tool === 'leadmagic_email_validation') return __dlBlankPayloadValue(payload.email);`,
|
|
1503
|
+
` return false;`,
|
|
1504
|
+
`}`,
|
|
1505
|
+
``,
|
|
1506
|
+
`function __dlPayloadHasMeaningfulValue(value: unknown): boolean {`,
|
|
1507
|
+
` if (__dlBlankPayloadValue(value)) return false;`,
|
|
1508
|
+
` if (Array.isArray(value)) return value.some(__dlPayloadHasMeaningfulValue);`,
|
|
1509
|
+
` if (value && typeof value === 'object') return Object.values(value as Record<string, unknown>).some(__dlPayloadHasMeaningfulValue);`,
|
|
1510
|
+
` return true;`,
|
|
1511
|
+
`}`,
|
|
1512
|
+
``,
|
|
1513
|
+
`function __dlShouldSkipBlankPlayPayload(payload: Record<string, unknown>): boolean {`,
|
|
1514
|
+
` return Object.keys(payload).length > 0 && !__dlPayloadHasMeaningfulValue(payload);`,
|
|
1515
|
+
`}`,
|
|
1516
|
+
``,
|
|
1517
|
+
`function __dlAliasCandidates(alias: string): string[] {`,
|
|
1518
|
+
` const aliases: string[] = [];`,
|
|
1519
|
+
` for (const candidate of [alias, alias.replace(/-/g, '_'), alias.replace(/_/g, '-')]) {`,
|
|
1520
|
+
` if (candidate && !aliases.includes(candidate)) aliases.push(candidate);`,
|
|
1521
|
+
` }`,
|
|
1522
|
+
` return aliases;`,
|
|
1523
|
+
`}`,
|
|
1524
|
+
``,
|
|
1525
|
+
`function __dlPlayResultValue(alias: string, result: unknown): unknown {`,
|
|
1526
|
+
` if (!result || typeof result !== 'object' || Array.isArray(result)) return result;`,
|
|
1527
|
+
` const record = result as Record<string, unknown>;`,
|
|
1528
|
+
` const aliases = __dlAliasCandidates(alias);`,
|
|
1529
|
+
` for (const key of aliases) {`,
|
|
1530
|
+
` if (key in record && __dlMeaningful(record[key])) return __dlScalarValue(record[key]);`,
|
|
1531
|
+
` }`,
|
|
1532
|
+
` const values = Object.values(record).filter(__dlMeaningful);`,
|
|
1533
|
+
` if (values.length === 1) return __dlScalarValue(values[0]);`,
|
|
1534
|
+
` return result;`,
|
|
1535
|
+
`}`,
|
|
1536
|
+
``,
|
|
1537
|
+
`async function __dlRunCommand(input: { alias: string; callId: string; tool: string; payload: Record<string, unknown>; extract: ((args: __DlExtractorHelpers) => unknown) | null; runIf: ((row: Record<string, unknown>) => unknown) | null; row: Record<string, unknown>; stepCtx: DeeplinePlayRuntimeContext; description?: string; force?: boolean; legacyEnvelope?: boolean }): Promise<unknown> {`,
|
|
1538
|
+
` if (input.runIf) {`,
|
|
1539
|
+
` const shouldRun = input.runIf(input.row);`,
|
|
1540
|
+
` if (!shouldRun) return null;`,
|
|
1541
|
+
` }`,
|
|
1542
|
+
` const payload = __dlRuntimePayload(input.tool, __dlTemplate(input.payload, input.row) as Record<string, unknown>, input.row);`,
|
|
1543
|
+
` if (__dlShouldSkipEmptyPayload(input.tool, payload)) return null;`,
|
|
1544
|
+
` const result = await input.stepCtx.tools.execute({`,
|
|
1545
|
+
` id: input.callId,`,
|
|
1546
|
+
` tool: input.tool,`,
|
|
1547
|
+
` input: payload,`,
|
|
1548
|
+
` ...(input.description ? { description: input.description } : {}),`,
|
|
1549
|
+
` ...(input.force ? { force: true } : {}),`,
|
|
1550
|
+
` });`,
|
|
1551
|
+
` __dlAssertSuccessfulToolResult(result);`,
|
|
1552
|
+
` return __dlExtract(input.alias, result, input.row, input.extract, Boolean(input.legacyEnvelope));`,
|
|
1553
|
+
`}`,
|
|
1554
|
+
].join('\n');
|
|
1555
|
+
}
|