deepline 0.1.69 → 0.1.71
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/README.md +7 -7
- package/dist/cli/index.js +95 -30
- package/dist/cli/index.mjs +95 -30
- package/dist/index.d.mts +60 -37
- package/dist/index.d.ts +60 -37
- package/dist/index.js +13 -3
- package/dist/index.mjs +12 -3
- package/dist/repo/apps/play-runner-workers/src/entry.ts +34 -21
- package/dist/repo/sdk/src/client.ts +3 -0
- package/dist/repo/sdk/src/index.ts +3 -2
- package/dist/repo/sdk/src/play.ts +70 -30
- package/dist/repo/sdk/src/release.ts +3 -3
- package/dist/repo/sdk/src/types.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +13 -2
- package/dist/repo/shared_libs/play-runtime/db-session-plan.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/execution-plan.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/step-lifecycle-tracker.ts +4 -4
- package/dist/repo/shared_libs/plays/dataset.ts +2 -2
- package/dist/repo/shared_libs/plays/row-identity.ts +3 -3
- package/dist/repo/shared_libs/plays/static-pipeline.ts +12 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,8 +212,8 @@ export default definePlay('rate-limit-repro', async (ctx) => {
|
|
|
212
212
|
row_number: i + 1,
|
|
213
213
|
}));
|
|
214
214
|
|
|
215
|
-
const results = await ctx.
|
|
216
|
-
.
|
|
215
|
+
const results = await ctx.dataset('rate_limit_probes', items)
|
|
216
|
+
.withColumn("result", (row, ctx) =>
|
|
217
217
|
ctx.tools.execute({
|
|
218
218
|
id: 'rate_limit_probe',
|
|
219
219
|
tool: 'test_rate_limit',
|
|
@@ -242,12 +242,12 @@ deepline play run --file rate-limit-repro.play.ts --watch
|
|
|
242
242
|
|
|
243
243
|
### 4. Fallback variant (multi-step with rate limits)
|
|
244
244
|
|
|
245
|
-
Use `steps()` and `
|
|
245
|
+
Use `steps()` and `runIf()` to express row-level fallback sequences. Skipped
|
|
246
246
|
conditional steps yield `null`.
|
|
247
247
|
|
|
248
248
|
```bash
|
|
249
249
|
cat > rate-limit-waterfall.play.ts << 'TS'
|
|
250
|
-
import { definePlay,
|
|
250
|
+
import { definePlay, runIf, steps } from 'deepline';
|
|
251
251
|
|
|
252
252
|
export default definePlay('rate-limit-waterfall', async (ctx) => {
|
|
253
253
|
const leads = Array.from({ length: 24 }, (_, i) => ({
|
|
@@ -267,7 +267,7 @@ export default definePlay('rate-limit-waterfall', async (ctx) => {
|
|
|
267
267
|
},
|
|
268
268
|
description: 'Exercise primary rate-limit behavior.',
|
|
269
269
|
}))
|
|
270
|
-
.step("backup_probe",
|
|
270
|
+
.step("backup_probe", runIf(
|
|
271
271
|
(row) => !row.primary_probe,
|
|
272
272
|
(row, ctx) =>
|
|
273
273
|
ctx.tools.execute({
|
|
@@ -284,8 +284,8 @@ export default definePlay('rate-limit-waterfall', async (ctx) => {
|
|
|
284
284
|
.return((row) => row.primary_probe ?? row.backup_probe ?? null);
|
|
285
285
|
|
|
286
286
|
// Fan out over leads — each gets a row-level fallback sequence
|
|
287
|
-
const results = await ctx.
|
|
288
|
-
.
|
|
287
|
+
const results = await ctx.dataset('leads', leads)
|
|
288
|
+
.withColumn("probe", probeFallback)
|
|
289
289
|
.run();
|
|
290
290
|
|
|
291
291
|
return results;
|
package/dist/cli/index.js
CHANGED
|
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
|
|
|
229
229
|
|
|
230
230
|
// src/release.ts
|
|
231
231
|
var SDK_RELEASE = {
|
|
232
|
-
version: "0.1.
|
|
233
|
-
apiContract: "2026-
|
|
232
|
+
version: "0.1.71",
|
|
233
|
+
apiContract: "2026-06-dataset-column-syntax-cutover",
|
|
234
234
|
supportPolicy: {
|
|
235
|
-
latest: "0.1.
|
|
235
|
+
latest: "0.1.71",
|
|
236
236
|
minimumSupported: "0.1.53",
|
|
237
237
|
deprecatedBelow: "0.1.53"
|
|
238
238
|
}
|
|
@@ -1037,6 +1037,7 @@ var DeeplineClient = class {
|
|
|
1037
1037
|
...request.inputFile ? { inputFile: request.inputFile } : {},
|
|
1038
1038
|
...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
|
|
1039
1039
|
...request.force ? { force: true } : {},
|
|
1040
|
+
...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
|
|
1040
1041
|
...request.profile ? { profile: request.profile } : {}
|
|
1041
1042
|
};
|
|
1042
1043
|
for await (const event of this.http.streamSse(
|
|
@@ -6884,7 +6885,7 @@ function playInspectionComments(playRef, indent) {
|
|
|
6884
6885
|
function accessorExpression(base, field) {
|
|
6885
6886
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field) ? `${base}.${field}` : `${base}[${jsString(field)}]`;
|
|
6886
6887
|
}
|
|
6887
|
-
function
|
|
6888
|
+
function needsRunIfImport(options) {
|
|
6888
6889
|
return stageProviders(options.email).length > 1 || stageProviders(options.phone).length > 1;
|
|
6889
6890
|
}
|
|
6890
6891
|
function sourceCollectionTypeName(entity) {
|
|
@@ -7186,7 +7187,7 @@ function generateFinderPlayStep(input2) {
|
|
|
7186
7187
|
" ",
|
|
7187
7188
|
input2.stage.value
|
|
7188
7189
|
);
|
|
7189
|
-
return `.
|
|
7190
|
+
return `.withColumn(${jsString(outputField)}, async (row, rowCtx) => {
|
|
7190
7191
|
const ${input2.aggregateStepName}Input = ${payload};
|
|
7191
7192
|
throw new Error(${jsString(`TODO: map ${input2.aggregateStepName}Input for ${input2.stage.value}, then delete this throw.`)});
|
|
7192
7193
|
const ${input2.aggregateStepName}Result = await rowCtx.runPlay<${resultTypeName}>(
|
|
@@ -7232,12 +7233,12 @@ function generateFinderProviderStep(input2) {
|
|
|
7232
7233
|
const stepName = input2.stepNames[input2.index];
|
|
7233
7234
|
switch (input2.index) {
|
|
7234
7235
|
case 0:
|
|
7235
|
-
return `.
|
|
7236
|
+
return `.withColumn(${jsString(stepName)}, ${input2.resolver})`;
|
|
7236
7237
|
default: {
|
|
7237
7238
|
const priorCandidates = input2.stepNames.slice(0, input2.index).map((name) => `row.${name}`).join(", ");
|
|
7238
|
-
return `.
|
|
7239
|
+
return `.withColumn(
|
|
7239
7240
|
${jsString(stepName)},
|
|
7240
|
-
|
|
7241
|
+
runIf(
|
|
7241
7242
|
(row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
|
|
7242
7243
|
${input2.resolver},
|
|
7243
7244
|
),
|
|
@@ -7271,9 +7272,9 @@ function generateFinderProviderWaterfall(input2) {
|
|
|
7271
7272
|
);
|
|
7272
7273
|
const candidateNames = stepNames.map((name) => `row.${name}`).join(", ");
|
|
7273
7274
|
return `// ${input2.aggregateStepName} provider waterfall. Each provider leg is active once its TODO throw is removed;
|
|
7274
|
-
// delete or comment out legs you do not want before running. Later legs are gated with
|
|
7275
|
+
// delete or comment out legs you do not want before running. Later legs are gated with runIf(...).
|
|
7275
7276
|
${providerSteps.join("\n ")}
|
|
7276
|
-
.
|
|
7277
|
+
.withColumn(${jsString(input2.aggregateStepName)}, (row) => {
|
|
7277
7278
|
const candidates = [${candidateNames}];
|
|
7278
7279
|
const match = candidates.find((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)});
|
|
7279
7280
|
return ${optionalFinderValueExpression("match", input2.outputField)} ?? null;
|
|
@@ -7328,7 +7329,7 @@ function generateBootstrapPlaySource(input2) {
|
|
|
7328
7329
|
rowTypeDefinitions,
|
|
7329
7330
|
finderPlayResultTypes
|
|
7330
7331
|
].filter((definition) => definition.trim().length > 0).join("\n\n");
|
|
7331
|
-
const importNames =
|
|
7332
|
+
const importNames = needsRunIfImport(input2.options) ? "definePlay, runIf" : "definePlay";
|
|
7332
7333
|
return `import { ${importNames} } from 'deepline';
|
|
7333
7334
|
|
|
7334
7335
|
${typeDefinitions}
|
|
@@ -7347,7 +7348,7 @@ export default definePlay(${jsString(input2.options.name)}, async (ctx, input: I
|
|
|
7347
7348
|
}
|
|
7348
7349
|
|
|
7349
7350
|
const rows = await ctx
|
|
7350
|
-
.
|
|
7351
|
+
.dataset('bootstrap_rows', rowsToProcess)${mapSteps}
|
|
7351
7352
|
.run({
|
|
7352
7353
|
key: (_row, index) => index,
|
|
7353
7354
|
description: ${jsString(`Bootstrap ${input2.options.template}: seed source rows, run requested stages, then return the mapped rows.`)},
|
|
@@ -7857,7 +7858,7 @@ function normalizePlayNameForSheet(value) {
|
|
|
7857
7858
|
function normalizeTableNamespace(value) {
|
|
7858
7859
|
return validateIdentifierPart(
|
|
7859
7860
|
value,
|
|
7860
|
-
"ctx.
|
|
7861
|
+
"ctx.dataset() key",
|
|
7861
7862
|
MAP_KEY_NAMESPACE_MAX_LENGTH
|
|
7862
7863
|
);
|
|
7863
7864
|
}
|
|
@@ -7867,7 +7868,7 @@ function validatePlaySheetTableName(playName, tableNamespace) {
|
|
|
7867
7868
|
const resolved = `${playSegment}_${keySegment}`;
|
|
7868
7869
|
if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
|
|
7869
7870
|
throw new Error(
|
|
7870
|
-
`Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.
|
|
7871
|
+
`Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.dataset() key. Resolved table name: "${resolved}".`
|
|
7871
7872
|
);
|
|
7872
7873
|
}
|
|
7873
7874
|
return resolved;
|
|
@@ -7947,6 +7948,7 @@ ${EXTRACTED_GETTER_ERROR_HINT}`;
|
|
|
7947
7948
|
}
|
|
7948
7949
|
|
|
7949
7950
|
// src/cli/commands/play.ts
|
|
7951
|
+
var PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS = 2500;
|
|
7950
7952
|
var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
7951
7953
|
"--json",
|
|
7952
7954
|
"--wait",
|
|
@@ -8889,6 +8891,10 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8889
8891
|
let eventCount = 0;
|
|
8890
8892
|
let firstRunIdMs = null;
|
|
8891
8893
|
let lastPhase = null;
|
|
8894
|
+
const startRequest = {
|
|
8895
|
+
...input2.request,
|
|
8896
|
+
waitForCompletionMs: typeof input2.request.waitForCompletionMs === "number" ? input2.request.waitForCompletionMs : PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS
|
|
8897
|
+
};
|
|
8892
8898
|
const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
|
|
8893
8899
|
() => {
|
|
8894
8900
|
timedOut = true;
|
|
@@ -8897,7 +8903,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8897
8903
|
Math.max(1, input2.waitTimeoutMs)
|
|
8898
8904
|
);
|
|
8899
8905
|
try {
|
|
8900
|
-
for await (const event of input2.client.startPlayRunStream(
|
|
8906
|
+
for await (const event of input2.client.startPlayRunStream(startRequest, {
|
|
8901
8907
|
signal: controller.signal
|
|
8902
8908
|
})) {
|
|
8903
8909
|
eventCount += 1;
|
|
@@ -10105,10 +10111,66 @@ function formatActiveRunConflictError(input2) {
|
|
|
10105
10111
|
lines.push(` rerun: add --force to the same deepline plays run command`);
|
|
10106
10112
|
return lines.join("\n");
|
|
10107
10113
|
}
|
|
10114
|
+
var PLAY_SYNTAX_MIGRATION_ERROR_MARKERS = [
|
|
10115
|
+
"ctx.map(...) has been replaced by ctx.dataset(...)",
|
|
10116
|
+
"Dataset .step(...) has been replaced by .withColumn(...)"
|
|
10117
|
+
];
|
|
10118
|
+
function stringArrayField(record, key) {
|
|
10119
|
+
const value = record[key];
|
|
10120
|
+
if (!Array.isArray(value)) return [];
|
|
10121
|
+
return value.filter((entry) => typeof entry === "string");
|
|
10122
|
+
}
|
|
10123
|
+
function extractPlayValidationErrors(value) {
|
|
10124
|
+
if (value instanceof DeeplineError) {
|
|
10125
|
+
return extractPlayValidationErrors(value.details);
|
|
10126
|
+
}
|
|
10127
|
+
if (!isRecord4(value)) {
|
|
10128
|
+
return [];
|
|
10129
|
+
}
|
|
10130
|
+
const directErrors = stringArrayField(value, "errors");
|
|
10131
|
+
if (directErrors.length > 0) {
|
|
10132
|
+
return directErrors;
|
|
10133
|
+
}
|
|
10134
|
+
for (const key of ["response", "error", "details"]) {
|
|
10135
|
+
const nestedErrors = extractPlayValidationErrors(value[key]);
|
|
10136
|
+
if (nestedErrors.length > 0) {
|
|
10137
|
+
return nestedErrors;
|
|
10138
|
+
}
|
|
10139
|
+
}
|
|
10140
|
+
return [];
|
|
10141
|
+
}
|
|
10142
|
+
function orderPlayValidationErrors(errors) {
|
|
10143
|
+
const uniqueErrors = [...new Set(errors)];
|
|
10144
|
+
const migrationErrors = uniqueErrors.filter(
|
|
10145
|
+
(error) => PLAY_SYNTAX_MIGRATION_ERROR_MARKERS.some(
|
|
10146
|
+
(marker) => error.includes(marker)
|
|
10147
|
+
)
|
|
10148
|
+
);
|
|
10149
|
+
const remainingErrors = uniqueErrors.filter(
|
|
10150
|
+
(error) => !migrationErrors.includes(error)
|
|
10151
|
+
);
|
|
10152
|
+
return [...migrationErrors, ...remainingErrors];
|
|
10153
|
+
}
|
|
10154
|
+
function normalizePlayValidationError(error) {
|
|
10155
|
+
const validationErrors = extractPlayValidationErrors(error);
|
|
10156
|
+
if (validationErrors.length === 0) {
|
|
10157
|
+
return error;
|
|
10158
|
+
}
|
|
10159
|
+
const message = orderPlayValidationErrors(validationErrors).join("\n");
|
|
10160
|
+
if (!message.trim()) {
|
|
10161
|
+
return error;
|
|
10162
|
+
}
|
|
10163
|
+
return new DeeplineError(
|
|
10164
|
+
message,
|
|
10165
|
+
error instanceof DeeplineError ? error.statusCode : void 0,
|
|
10166
|
+
error instanceof DeeplineError ? error.code : "PLAY_VALIDATION_ERROR",
|
|
10167
|
+
error instanceof DeeplineError ? error.details : void 0
|
|
10168
|
+
);
|
|
10169
|
+
}
|
|
10108
10170
|
function normalizePlayStartError(error, playName) {
|
|
10109
10171
|
const activeRuns = extractActiveRunsFromError(error);
|
|
10110
10172
|
if (activeRuns.length === 0) {
|
|
10111
|
-
return error;
|
|
10173
|
+
return normalizePlayValidationError(error);
|
|
10112
10174
|
}
|
|
10113
10175
|
return new DeeplineError(
|
|
10114
10176
|
formatActiveRunConflictError({ playName, activeRuns }),
|
|
@@ -10271,7 +10333,7 @@ function writeStartedPlayRun(input2) {
|
|
|
10271
10333
|
);
|
|
10272
10334
|
}
|
|
10273
10335
|
function parsePlayRunOptions(args) {
|
|
10274
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.
|
|
10336
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.dataset guidance.";
|
|
10275
10337
|
let filePath = null;
|
|
10276
10338
|
let playName = null;
|
|
10277
10339
|
let input2 = null;
|
|
@@ -10634,7 +10696,10 @@ async function handleFileBackedRun(options) {
|
|
|
10634
10696
|
progress.phase("compiled play");
|
|
10635
10697
|
} catch (error) {
|
|
10636
10698
|
progress.fail();
|
|
10637
|
-
|
|
10699
|
+
const normalizedError = normalizePlayValidationError(error);
|
|
10700
|
+
console.error(
|
|
10701
|
+
normalizedError instanceof Error ? normalizedError.message : String(normalizedError)
|
|
10702
|
+
);
|
|
10638
10703
|
return 1;
|
|
10639
10704
|
}
|
|
10640
10705
|
const bundleResult = graph.root;
|
|
@@ -11777,7 +11842,7 @@ function registerPlayCommands(program) {
|
|
|
11777
11842
|
Concepts:
|
|
11778
11843
|
Plays are durable cloud workflows.
|
|
11779
11844
|
Stable ctx.tools.execute({ id, tool, input }) calls are replay-safe.
|
|
11780
|
-
ctx.
|
|
11845
|
+
ctx.dataset adds row keys and row progress.
|
|
11781
11846
|
Named play runs use the live revision unless --latest or --revision-id is set.
|
|
11782
11847
|
Running a local file does not make it live; use set-live/publish explicitly.
|
|
11783
11848
|
|
|
@@ -11830,19 +11895,19 @@ Idempotent execution:
|
|
|
11830
11895
|
|
|
11831
11896
|
await ctx.tools.execute({ id: 'company_lookup', tool, input });
|
|
11832
11897
|
|
|
11833
|
-
For rows, use ctx.
|
|
11898
|
+
For rows, use ctx.dataset plus a stable row key:
|
|
11834
11899
|
|
|
11835
11900
|
const rows = await ctx
|
|
11836
|
-
.
|
|
11837
|
-
.
|
|
11901
|
+
.dataset('companies_v1', companies)
|
|
11902
|
+
.withColumn('cto', (row, ctx) => ctx.tools.execute({
|
|
11838
11903
|
id: 'find_cto',
|
|
11839
11904
|
tool: 'apollo_search_people_with_match',
|
|
11840
11905
|
input: { q_organization_domains_list: [row.domain], per_page: 1 },
|
|
11841
11906
|
}))
|
|
11842
11907
|
.run({ key: 'domain' });
|
|
11843
11908
|
|
|
11844
|
-
Reuse needs the same play, tool id,
|
|
11845
|
-
To refresh, change the id/
|
|
11909
|
+
Reuse needs the same play, tool id, dataset name, row key, and compatible logic.
|
|
11910
|
+
To refresh, change the id/dataset key or set staleAfterSeconds:
|
|
11846
11911
|
|
|
11847
11912
|
.run({ key: 'domain', staleAfterSeconds: 86400 })
|
|
11848
11913
|
|
|
@@ -13997,11 +14062,11 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
13997
14062
|
const rows = (list?.get() ?? []).slice(0, 100);
|
|
13998
14063
|
// ${sampleRows}
|
|
13999
14064
|
// columns: ${columns}
|
|
14000
|
-
// .
|
|
14001
|
-
// .
|
|
14002
|
-
// ctx.
|
|
14065
|
+
// .withColumn('email_waterfall', (row, rowCtx) => rowCtx.runPlay('name_domain_email', 'name-and-domain-to-email', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), domain: String(row.domain ?? '') }, { description: 'Resolve email.' }))
|
|
14066
|
+
// .withColumn('phone_waterfall', (row, rowCtx) => rowCtx.runPlay('contact_phone', 'contact-to-phone', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), email: String(row.email ?? '') }, { description: 'Resolve phone.' }))
|
|
14067
|
+
// ctx.dataset is idempotent by dataset key + row key; reruns reuse completed rows.
|
|
14003
14068
|
const enrichedData = await ctx
|
|
14004
|
-
.
|
|
14069
|
+
.dataset('enriched_data', rows)
|
|
14005
14070
|
.run({
|
|
14006
14071
|
key: ${rowKey},
|
|
14007
14072
|
description: 'Enrich seeded rows.',
|
|
@@ -14815,8 +14880,8 @@ async function runPlayRunnerHealthCheck() {
|
|
|
14815
14880
|
"",
|
|
14816
14881
|
"export default definePlay('health-check', async (ctx) => {",
|
|
14817
14882
|
" const rows = await ctx",
|
|
14818
|
-
" .
|
|
14819
|
-
" .
|
|
14883
|
+
" .dataset('health_rows', [{ id: 'a' }, { id: 'b' }])",
|
|
14884
|
+
" .withColumn('echo', (row) => ({ ok: true, id: row.id }))",
|
|
14820
14885
|
" .run({ key: 'id' });",
|
|
14821
14886
|
" return { ok: true, rows, source: 'deepline health --play-runner' };",
|
|
14822
14887
|
"});",
|
package/dist/cli/index.mjs
CHANGED
|
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
|
|
|
206
206
|
|
|
207
207
|
// src/release.ts
|
|
208
208
|
var SDK_RELEASE = {
|
|
209
|
-
version: "0.1.
|
|
210
|
-
apiContract: "2026-
|
|
209
|
+
version: "0.1.71",
|
|
210
|
+
apiContract: "2026-06-dataset-column-syntax-cutover",
|
|
211
211
|
supportPolicy: {
|
|
212
|
-
latest: "0.1.
|
|
212
|
+
latest: "0.1.71",
|
|
213
213
|
minimumSupported: "0.1.53",
|
|
214
214
|
deprecatedBelow: "0.1.53"
|
|
215
215
|
}
|
|
@@ -1014,6 +1014,7 @@ var DeeplineClient = class {
|
|
|
1014
1014
|
...request.inputFile ? { inputFile: request.inputFile } : {},
|
|
1015
1015
|
...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
|
|
1016
1016
|
...request.force ? { force: true } : {},
|
|
1017
|
+
...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
|
|
1017
1018
|
...request.profile ? { profile: request.profile } : {}
|
|
1018
1019
|
};
|
|
1019
1020
|
for await (const event of this.http.streamSse(
|
|
@@ -6887,7 +6888,7 @@ function playInspectionComments(playRef, indent) {
|
|
|
6887
6888
|
function accessorExpression(base, field) {
|
|
6888
6889
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field) ? `${base}.${field}` : `${base}[${jsString(field)}]`;
|
|
6889
6890
|
}
|
|
6890
|
-
function
|
|
6891
|
+
function needsRunIfImport(options) {
|
|
6891
6892
|
return stageProviders(options.email).length > 1 || stageProviders(options.phone).length > 1;
|
|
6892
6893
|
}
|
|
6893
6894
|
function sourceCollectionTypeName(entity) {
|
|
@@ -7189,7 +7190,7 @@ function generateFinderPlayStep(input2) {
|
|
|
7189
7190
|
" ",
|
|
7190
7191
|
input2.stage.value
|
|
7191
7192
|
);
|
|
7192
|
-
return `.
|
|
7193
|
+
return `.withColumn(${jsString(outputField)}, async (row, rowCtx) => {
|
|
7193
7194
|
const ${input2.aggregateStepName}Input = ${payload};
|
|
7194
7195
|
throw new Error(${jsString(`TODO: map ${input2.aggregateStepName}Input for ${input2.stage.value}, then delete this throw.`)});
|
|
7195
7196
|
const ${input2.aggregateStepName}Result = await rowCtx.runPlay<${resultTypeName}>(
|
|
@@ -7235,12 +7236,12 @@ function generateFinderProviderStep(input2) {
|
|
|
7235
7236
|
const stepName = input2.stepNames[input2.index];
|
|
7236
7237
|
switch (input2.index) {
|
|
7237
7238
|
case 0:
|
|
7238
|
-
return `.
|
|
7239
|
+
return `.withColumn(${jsString(stepName)}, ${input2.resolver})`;
|
|
7239
7240
|
default: {
|
|
7240
7241
|
const priorCandidates = input2.stepNames.slice(0, input2.index).map((name) => `row.${name}`).join(", ");
|
|
7241
|
-
return `.
|
|
7242
|
+
return `.withColumn(
|
|
7242
7243
|
${jsString(stepName)},
|
|
7243
|
-
|
|
7244
|
+
runIf(
|
|
7244
7245
|
(row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
|
|
7245
7246
|
${input2.resolver},
|
|
7246
7247
|
),
|
|
@@ -7274,9 +7275,9 @@ function generateFinderProviderWaterfall(input2) {
|
|
|
7274
7275
|
);
|
|
7275
7276
|
const candidateNames = stepNames.map((name) => `row.${name}`).join(", ");
|
|
7276
7277
|
return `// ${input2.aggregateStepName} provider waterfall. Each provider leg is active once its TODO throw is removed;
|
|
7277
|
-
// delete or comment out legs you do not want before running. Later legs are gated with
|
|
7278
|
+
// delete or comment out legs you do not want before running. Later legs are gated with runIf(...).
|
|
7278
7279
|
${providerSteps.join("\n ")}
|
|
7279
|
-
.
|
|
7280
|
+
.withColumn(${jsString(input2.aggregateStepName)}, (row) => {
|
|
7280
7281
|
const candidates = [${candidateNames}];
|
|
7281
7282
|
const match = candidates.find((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)});
|
|
7282
7283
|
return ${optionalFinderValueExpression("match", input2.outputField)} ?? null;
|
|
@@ -7331,7 +7332,7 @@ function generateBootstrapPlaySource(input2) {
|
|
|
7331
7332
|
rowTypeDefinitions,
|
|
7332
7333
|
finderPlayResultTypes
|
|
7333
7334
|
].filter((definition) => definition.trim().length > 0).join("\n\n");
|
|
7334
|
-
const importNames =
|
|
7335
|
+
const importNames = needsRunIfImport(input2.options) ? "definePlay, runIf" : "definePlay";
|
|
7335
7336
|
return `import { ${importNames} } from 'deepline';
|
|
7336
7337
|
|
|
7337
7338
|
${typeDefinitions}
|
|
@@ -7350,7 +7351,7 @@ export default definePlay(${jsString(input2.options.name)}, async (ctx, input: I
|
|
|
7350
7351
|
}
|
|
7351
7352
|
|
|
7352
7353
|
const rows = await ctx
|
|
7353
|
-
.
|
|
7354
|
+
.dataset('bootstrap_rows', rowsToProcess)${mapSteps}
|
|
7354
7355
|
.run({
|
|
7355
7356
|
key: (_row, index) => index,
|
|
7356
7357
|
description: ${jsString(`Bootstrap ${input2.options.template}: seed source rows, run requested stages, then return the mapped rows.`)},
|
|
@@ -7860,7 +7861,7 @@ function normalizePlayNameForSheet(value) {
|
|
|
7860
7861
|
function normalizeTableNamespace(value) {
|
|
7861
7862
|
return validateIdentifierPart(
|
|
7862
7863
|
value,
|
|
7863
|
-
"ctx.
|
|
7864
|
+
"ctx.dataset() key",
|
|
7864
7865
|
MAP_KEY_NAMESPACE_MAX_LENGTH
|
|
7865
7866
|
);
|
|
7866
7867
|
}
|
|
@@ -7870,7 +7871,7 @@ function validatePlaySheetTableName(playName, tableNamespace) {
|
|
|
7870
7871
|
const resolved = `${playSegment}_${keySegment}`;
|
|
7871
7872
|
if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
|
|
7872
7873
|
throw new Error(
|
|
7873
|
-
`Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.
|
|
7874
|
+
`Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.dataset() key. Resolved table name: "${resolved}".`
|
|
7874
7875
|
);
|
|
7875
7876
|
}
|
|
7876
7877
|
return resolved;
|
|
@@ -7950,6 +7951,7 @@ ${EXTRACTED_GETTER_ERROR_HINT}`;
|
|
|
7950
7951
|
}
|
|
7951
7952
|
|
|
7952
7953
|
// src/cli/commands/play.ts
|
|
7954
|
+
var PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS = 2500;
|
|
7953
7955
|
var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
7954
7956
|
"--json",
|
|
7955
7957
|
"--wait",
|
|
@@ -8892,6 +8894,10 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8892
8894
|
let eventCount = 0;
|
|
8893
8895
|
let firstRunIdMs = null;
|
|
8894
8896
|
let lastPhase = null;
|
|
8897
|
+
const startRequest = {
|
|
8898
|
+
...input2.request,
|
|
8899
|
+
waitForCompletionMs: typeof input2.request.waitForCompletionMs === "number" ? input2.request.waitForCompletionMs : PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS
|
|
8900
|
+
};
|
|
8895
8901
|
const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
|
|
8896
8902
|
() => {
|
|
8897
8903
|
timedOut = true;
|
|
@@ -8900,7 +8906,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8900
8906
|
Math.max(1, input2.waitTimeoutMs)
|
|
8901
8907
|
);
|
|
8902
8908
|
try {
|
|
8903
|
-
for await (const event of input2.client.startPlayRunStream(
|
|
8909
|
+
for await (const event of input2.client.startPlayRunStream(startRequest, {
|
|
8904
8910
|
signal: controller.signal
|
|
8905
8911
|
})) {
|
|
8906
8912
|
eventCount += 1;
|
|
@@ -10108,10 +10114,66 @@ function formatActiveRunConflictError(input2) {
|
|
|
10108
10114
|
lines.push(` rerun: add --force to the same deepline plays run command`);
|
|
10109
10115
|
return lines.join("\n");
|
|
10110
10116
|
}
|
|
10117
|
+
var PLAY_SYNTAX_MIGRATION_ERROR_MARKERS = [
|
|
10118
|
+
"ctx.map(...) has been replaced by ctx.dataset(...)",
|
|
10119
|
+
"Dataset .step(...) has been replaced by .withColumn(...)"
|
|
10120
|
+
];
|
|
10121
|
+
function stringArrayField(record, key) {
|
|
10122
|
+
const value = record[key];
|
|
10123
|
+
if (!Array.isArray(value)) return [];
|
|
10124
|
+
return value.filter((entry) => typeof entry === "string");
|
|
10125
|
+
}
|
|
10126
|
+
function extractPlayValidationErrors(value) {
|
|
10127
|
+
if (value instanceof DeeplineError) {
|
|
10128
|
+
return extractPlayValidationErrors(value.details);
|
|
10129
|
+
}
|
|
10130
|
+
if (!isRecord4(value)) {
|
|
10131
|
+
return [];
|
|
10132
|
+
}
|
|
10133
|
+
const directErrors = stringArrayField(value, "errors");
|
|
10134
|
+
if (directErrors.length > 0) {
|
|
10135
|
+
return directErrors;
|
|
10136
|
+
}
|
|
10137
|
+
for (const key of ["response", "error", "details"]) {
|
|
10138
|
+
const nestedErrors = extractPlayValidationErrors(value[key]);
|
|
10139
|
+
if (nestedErrors.length > 0) {
|
|
10140
|
+
return nestedErrors;
|
|
10141
|
+
}
|
|
10142
|
+
}
|
|
10143
|
+
return [];
|
|
10144
|
+
}
|
|
10145
|
+
function orderPlayValidationErrors(errors) {
|
|
10146
|
+
const uniqueErrors = [...new Set(errors)];
|
|
10147
|
+
const migrationErrors = uniqueErrors.filter(
|
|
10148
|
+
(error) => PLAY_SYNTAX_MIGRATION_ERROR_MARKERS.some(
|
|
10149
|
+
(marker) => error.includes(marker)
|
|
10150
|
+
)
|
|
10151
|
+
);
|
|
10152
|
+
const remainingErrors = uniqueErrors.filter(
|
|
10153
|
+
(error) => !migrationErrors.includes(error)
|
|
10154
|
+
);
|
|
10155
|
+
return [...migrationErrors, ...remainingErrors];
|
|
10156
|
+
}
|
|
10157
|
+
function normalizePlayValidationError(error) {
|
|
10158
|
+
const validationErrors = extractPlayValidationErrors(error);
|
|
10159
|
+
if (validationErrors.length === 0) {
|
|
10160
|
+
return error;
|
|
10161
|
+
}
|
|
10162
|
+
const message = orderPlayValidationErrors(validationErrors).join("\n");
|
|
10163
|
+
if (!message.trim()) {
|
|
10164
|
+
return error;
|
|
10165
|
+
}
|
|
10166
|
+
return new DeeplineError(
|
|
10167
|
+
message,
|
|
10168
|
+
error instanceof DeeplineError ? error.statusCode : void 0,
|
|
10169
|
+
error instanceof DeeplineError ? error.code : "PLAY_VALIDATION_ERROR",
|
|
10170
|
+
error instanceof DeeplineError ? error.details : void 0
|
|
10171
|
+
);
|
|
10172
|
+
}
|
|
10111
10173
|
function normalizePlayStartError(error, playName) {
|
|
10112
10174
|
const activeRuns = extractActiveRunsFromError(error);
|
|
10113
10175
|
if (activeRuns.length === 0) {
|
|
10114
|
-
return error;
|
|
10176
|
+
return normalizePlayValidationError(error);
|
|
10115
10177
|
}
|
|
10116
10178
|
return new DeeplineError(
|
|
10117
10179
|
formatActiveRunConflictError({ playName, activeRuns }),
|
|
@@ -10274,7 +10336,7 @@ function writeStartedPlayRun(input2) {
|
|
|
10274
10336
|
);
|
|
10275
10337
|
}
|
|
10276
10338
|
function parsePlayRunOptions(args) {
|
|
10277
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.
|
|
10339
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.dataset guidance.";
|
|
10278
10340
|
let filePath = null;
|
|
10279
10341
|
let playName = null;
|
|
10280
10342
|
let input2 = null;
|
|
@@ -10637,7 +10699,10 @@ async function handleFileBackedRun(options) {
|
|
|
10637
10699
|
progress.phase("compiled play");
|
|
10638
10700
|
} catch (error) {
|
|
10639
10701
|
progress.fail();
|
|
10640
|
-
|
|
10702
|
+
const normalizedError = normalizePlayValidationError(error);
|
|
10703
|
+
console.error(
|
|
10704
|
+
normalizedError instanceof Error ? normalizedError.message : String(normalizedError)
|
|
10705
|
+
);
|
|
10641
10706
|
return 1;
|
|
10642
10707
|
}
|
|
10643
10708
|
const bundleResult = graph.root;
|
|
@@ -11780,7 +11845,7 @@ function registerPlayCommands(program) {
|
|
|
11780
11845
|
Concepts:
|
|
11781
11846
|
Plays are durable cloud workflows.
|
|
11782
11847
|
Stable ctx.tools.execute({ id, tool, input }) calls are replay-safe.
|
|
11783
|
-
ctx.
|
|
11848
|
+
ctx.dataset adds row keys and row progress.
|
|
11784
11849
|
Named play runs use the live revision unless --latest or --revision-id is set.
|
|
11785
11850
|
Running a local file does not make it live; use set-live/publish explicitly.
|
|
11786
11851
|
|
|
@@ -11833,19 +11898,19 @@ Idempotent execution:
|
|
|
11833
11898
|
|
|
11834
11899
|
await ctx.tools.execute({ id: 'company_lookup', tool, input });
|
|
11835
11900
|
|
|
11836
|
-
For rows, use ctx.
|
|
11901
|
+
For rows, use ctx.dataset plus a stable row key:
|
|
11837
11902
|
|
|
11838
11903
|
const rows = await ctx
|
|
11839
|
-
.
|
|
11840
|
-
.
|
|
11904
|
+
.dataset('companies_v1', companies)
|
|
11905
|
+
.withColumn('cto', (row, ctx) => ctx.tools.execute({
|
|
11841
11906
|
id: 'find_cto',
|
|
11842
11907
|
tool: 'apollo_search_people_with_match',
|
|
11843
11908
|
input: { q_organization_domains_list: [row.domain], per_page: 1 },
|
|
11844
11909
|
}))
|
|
11845
11910
|
.run({ key: 'domain' });
|
|
11846
11911
|
|
|
11847
|
-
Reuse needs the same play, tool id,
|
|
11848
|
-
To refresh, change the id/
|
|
11912
|
+
Reuse needs the same play, tool id, dataset name, row key, and compatible logic.
|
|
11913
|
+
To refresh, change the id/dataset key or set staleAfterSeconds:
|
|
11849
11914
|
|
|
11850
11915
|
.run({ key: 'domain', staleAfterSeconds: 86400 })
|
|
11851
11916
|
|
|
@@ -14000,11 +14065,11 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
14000
14065
|
const rows = (list?.get() ?? []).slice(0, 100);
|
|
14001
14066
|
// ${sampleRows}
|
|
14002
14067
|
// columns: ${columns}
|
|
14003
|
-
// .
|
|
14004
|
-
// .
|
|
14005
|
-
// ctx.
|
|
14068
|
+
// .withColumn('email_waterfall', (row, rowCtx) => rowCtx.runPlay('name_domain_email', 'name-and-domain-to-email', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), domain: String(row.domain ?? '') }, { description: 'Resolve email.' }))
|
|
14069
|
+
// .withColumn('phone_waterfall', (row, rowCtx) => rowCtx.runPlay('contact_phone', 'contact-to-phone', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), email: String(row.email ?? '') }, { description: 'Resolve phone.' }))
|
|
14070
|
+
// ctx.dataset is idempotent by dataset key + row key; reruns reuse completed rows.
|
|
14006
14071
|
const enrichedData = await ctx
|
|
14007
|
-
.
|
|
14072
|
+
.dataset('enriched_data', rows)
|
|
14008
14073
|
.run({
|
|
14009
14074
|
key: ${rowKey},
|
|
14010
14075
|
description: 'Enrich seeded rows.',
|
|
@@ -14825,8 +14890,8 @@ async function runPlayRunnerHealthCheck() {
|
|
|
14825
14890
|
"",
|
|
14826
14891
|
"export default definePlay('health-check', async (ctx) => {",
|
|
14827
14892
|
" const rows = await ctx",
|
|
14828
|
-
" .
|
|
14829
|
-
" .
|
|
14893
|
+
" .dataset('health_rows', [{ id: 'a' }, { id: 'b' }])",
|
|
14894
|
+
" .withColumn('echo', (row) => ({ ok: true, id: row.id }))",
|
|
14830
14895
|
" .run({ key: 'id' });",
|
|
14831
14896
|
" return { ok: true, rows, source: 'deepline health --play-runner' };",
|
|
14832
14897
|
"});",
|