deepline 0.1.78 → 0.1.80
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/cli/index.js +69 -37
- package/dist/cli/index.mjs +69 -37
- package/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +7 -4
- package/dist/index.mjs +7 -4
- package/dist/repo/apps/play-runner-workers/src/child-play-await.ts +192 -0
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +1320 -1644
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +515 -648
- package/dist/repo/apps/play-runner-workers/src/entry.ts +896 -354
- package/dist/repo/apps/play-runner-workers/src/workflow-retry-state.ts +209 -0
- package/dist/repo/sdk/src/client.ts +9 -2
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/sdk/src/types.ts +5 -0
- package/dist/repo/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +231 -0
- package/dist/repo/shared_libs/play-runtime/governor/governor.ts +376 -0
- package/dist/repo/shared_libs/play-runtime/governor/policy.ts +179 -0
- package/dist/repo/shared_libs/play-runtime/governor/rate-state-backend.ts +87 -0
- package/dist/repo/shared_libs/play-runtime/run-failure.ts +12 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +24 -0
- package/dist/repo/shared_libs/play-runtime/submit-limits.ts +35 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +4 -12
- package/dist/repo/shared_libs/plays/bundling/limits.ts +29 -0
- package/dist/repo/shared_libs/plays/static-pipeline.ts +314 -1
- package/dist/repo/shared_libs/temporal/constants.ts +38 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for play submit-path size/wait limits.
|
|
3
|
+
*
|
|
4
|
+
* Dependency-free leaf shared by the Next.js run route
|
|
5
|
+
* (`POST /api/v2/plays/run`), the Cloudflare coordinator's workflow retry-state
|
|
6
|
+
* codec, and the limits documentation generator. These bound what a single
|
|
7
|
+
* submission may carry inline, the hard ceiling on submitted input, and how
|
|
8
|
+
* long the API will block for a synchronous result.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Max size of a CSV/file input that may be inlined into the submit payload
|
|
13
|
+
* (and the workflow event history). Larger inputs must be staged via
|
|
14
|
+
* `POST /api/v2/plays/files/stage` and referenced by handle.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_TEMPORAL_INLINE_INPUT_FILE_BYTES = 64 * 1024;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Max time the run route will block waiting for a play to finish before
|
|
20
|
+
* returning a pending handle the caller can poll/stream.
|
|
21
|
+
*/
|
|
22
|
+
export const MAX_WAIT_FOR_COMPLETION_MS = 15_000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Submitted input/retry-state at or below this size stays inline in the
|
|
26
|
+
* coordinator Durable Object. Above it, the params are externalized to a
|
|
27
|
+
* short-lived play artifact (up to {@link PLAY_SUBMIT_INPUT_MAX_BYTES}).
|
|
28
|
+
*/
|
|
29
|
+
export const PLAY_SUBMIT_INPUT_INLINE_MAX_BYTES = 100_000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Absolute ceiling for submitted input/retry-state. A submission larger than
|
|
33
|
+
* this is rejected with guidance to use staged files or `ctx.csv` inputs.
|
|
34
|
+
*/
|
|
35
|
+
export const PLAY_SUBMIT_INPUT_MAX_BYTES = 1024 * 1024;
|
|
@@ -26,20 +26,12 @@ import type {
|
|
|
26
26
|
} from '../artifact-types';
|
|
27
27
|
import { buildPlayContractCompatibility } from '../contracts';
|
|
28
28
|
import { validatePlaySourceFilesHaveNoInlineSecrets } from '../secret-guardrails';
|
|
29
|
+
import {
|
|
30
|
+
MAX_ESM_WORKERS_BUNDLE_BYTES,
|
|
31
|
+
MAX_PLAY_BUNDLE_BYTES,
|
|
32
|
+
} from './limits';
|
|
29
33
|
|
|
30
34
|
const PLAY_BUNDLE_CACHE_VERSION = 24;
|
|
31
|
-
const MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
|
|
32
|
-
// workerd local-mode (`wrangler dev` Worker Loader) silently fails to
|
|
33
|
-
// instantiate per-graphHash play Workers when the bundled code passes a
|
|
34
|
-
// threshold somewhere between 1.04 MiB (44-package-imports — works) and
|
|
35
|
-
// 1.18 MiB (the same play with date-fns added — hangs forever). The
|
|
36
|
-
// workflow body never runs, no error is logged anywhere, and the run
|
|
37
|
-
// hangs indefinitely. We surface this as a hard bundle failure so the
|
|
38
|
-
// user gets an actionable message at submit time instead of a 5-minute
|
|
39
|
-
// silent timeout. Real CF (workers.dev) accepts much larger bundles, but
|
|
40
|
-
// `dev:v2 cloudflare` is the regression entrypoint so the local limit is
|
|
41
|
-
// the binding one.
|
|
42
|
-
const MAX_ESM_WORKERS_BUNDLE_BYTES = 1_150_000;
|
|
43
35
|
const PLAY_ARTIFACT_CACHE_DIR = join(
|
|
44
36
|
tmpdir(),
|
|
45
37
|
`deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for play bundle/compile size limits.
|
|
3
|
+
*
|
|
4
|
+
* Kept in a dependency-free leaf module (no esbuild, no Node-only imports) so
|
|
5
|
+
* both the bundler (`./index.ts`) and the limits documentation generator
|
|
6
|
+
* (`scripts/generate-limits-docs.ts`) can import the numbers without pulling in
|
|
7
|
+
* the compile toolchain. Do not restate these values in docs — the public page
|
|
8
|
+
* is generated from here.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Absolute hard ceiling for a compiled play bundle, across every artifact kind.
|
|
13
|
+
* A bundle larger than this is rejected at submit time.
|
|
14
|
+
*/
|
|
15
|
+
export const MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Tighter ceiling for the `esm_workers` artifact kind. workerd local-mode
|
|
19
|
+
* (`wrangler dev` Worker Loader) silently fails to instantiate per-graphHash
|
|
20
|
+
* play Workers when the bundled code passes a threshold somewhere between
|
|
21
|
+
* 1.04 MiB (44-package-imports — works) and 1.18 MiB (the same play with
|
|
22
|
+
* date-fns added — hangs forever). The workflow body never runs, no error is
|
|
23
|
+
* logged anywhere, and the run hangs indefinitely. We surface this as a hard
|
|
24
|
+
* bundle failure so the user gets an actionable message at submit time instead
|
|
25
|
+
* of a 5-minute silent timeout. Real CF (workers.dev) accepts much larger
|
|
26
|
+
* bundles, but `dev:v2 cloudflare` is the regression entrypoint so the local
|
|
27
|
+
* limit is the binding one.
|
|
28
|
+
*/
|
|
29
|
+
export const MAX_ESM_WORKERS_BUNDLE_BYTES = 1_150_000;
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import { normalizeTableNamespace } from './row-identity';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A top-level key the play's function literally `return`s. Derived from the
|
|
5
|
+
* `return { ... }` object literal — NOT from dataset `.withColumn(...)` names —
|
|
6
|
+
* so the "Returns" graph node mirrors the function's real output shape.
|
|
7
|
+
* `isDataset` is true when the key's value is a `PlayDataset` handle (a table).
|
|
8
|
+
*/
|
|
9
|
+
export interface PlayStaticReturnField {
|
|
10
|
+
name: string;
|
|
11
|
+
isDataset: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
export interface PlayStaticPipeline {
|
|
4
15
|
tableNamespace?: string;
|
|
5
16
|
inputFields?: string[];
|
|
17
|
+
rowKeyFields?: string[];
|
|
6
18
|
csvArg?: string;
|
|
7
19
|
hasInlineData?: boolean;
|
|
8
20
|
csvDescription?: string;
|
|
9
21
|
datasetDescription?: string;
|
|
10
22
|
fields: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Top-level keys of the play's `return { ... }` object literal, in source
|
|
25
|
+
* order. Undefined when the terminal return isn't a statically-known object
|
|
26
|
+
* literal (bare value, dataset handle, conditional returns, etc.).
|
|
27
|
+
*/
|
|
28
|
+
returnFields?: PlayStaticReturnField[];
|
|
11
29
|
stages?: PlayStaticSubstep[];
|
|
12
30
|
substeps: PlayStaticSubstep[];
|
|
13
31
|
sheetContract?: PlaySheetContract | null;
|
|
@@ -32,6 +50,7 @@ export interface PlaySheetColumnContract {
|
|
|
32
50
|
outputSqlName?: string;
|
|
33
51
|
stepId?: string;
|
|
34
52
|
toolId?: string;
|
|
53
|
+
isRowKey?: boolean;
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
export interface PlaySheetContract {
|
|
@@ -39,6 +58,42 @@ export interface PlaySheetContract {
|
|
|
39
58
|
columns: PlaySheetColumnContract[];
|
|
40
59
|
}
|
|
41
60
|
|
|
61
|
+
export type PlayStaticColumnProducerKind =
|
|
62
|
+
| 'tool'
|
|
63
|
+
| 'waterfall'
|
|
64
|
+
| 'stepProgram'
|
|
65
|
+
| 'playCall'
|
|
66
|
+
| 'transform';
|
|
67
|
+
|
|
68
|
+
export interface PlayStaticColumnProducer {
|
|
69
|
+
id: string;
|
|
70
|
+
kind: PlayStaticColumnProducerKind;
|
|
71
|
+
field: string;
|
|
72
|
+
toolId?: string;
|
|
73
|
+
playId?: string;
|
|
74
|
+
staleAfterSeconds?: number;
|
|
75
|
+
conditional?: boolean;
|
|
76
|
+
sourceRange?: PlayStaticSourceRange;
|
|
77
|
+
steps?: PlayStaticColumnProducer[];
|
|
78
|
+
substep: PlayStaticSubstep;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface PlayStaticDatasetColumn {
|
|
82
|
+
id: string;
|
|
83
|
+
source: PlaySheetColumnSource;
|
|
84
|
+
sqlName?: string;
|
|
85
|
+
staleAfterSeconds?: number;
|
|
86
|
+
producers: PlayStaticColumnProducer[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface PlayCompiledStaticGraph {
|
|
90
|
+
topLevel: PlayStaticSubstep[];
|
|
91
|
+
datasets: Array<{
|
|
92
|
+
tableNamespace: string;
|
|
93
|
+
columns: PlayStaticDatasetColumn[];
|
|
94
|
+
}>;
|
|
95
|
+
}
|
|
96
|
+
|
|
42
97
|
export function ensureCompiledSheetContract(
|
|
43
98
|
pipeline: PlayStaticPipeline | null | undefined,
|
|
44
99
|
): PlayStaticPipeline | null | undefined {
|
|
@@ -92,8 +147,27 @@ function truncateStaticSubstepsForStorage(
|
|
|
92
147
|
return {
|
|
93
148
|
...base,
|
|
94
149
|
inputFields: base.inputFields ? [...base.inputFields] : undefined,
|
|
150
|
+
rowKeyFields: base.rowKeyFields ? [...base.rowKeyFields] : undefined,
|
|
95
151
|
outputFields: base.outputFields ? [...base.outputFields] : undefined,
|
|
152
|
+
columns: base.columns
|
|
153
|
+
? base.columns.map((column) => ({
|
|
154
|
+
...column,
|
|
155
|
+
producers: column.producers.map((producer) => ({
|
|
156
|
+
...producer,
|
|
157
|
+
sourceRange: cloneStorageSafeSourceRange(producer.sourceRange),
|
|
158
|
+
steps: producer.steps
|
|
159
|
+
? producer.steps.map((stepProducer) => ({
|
|
160
|
+
...stepProducer,
|
|
161
|
+
sourceRange: cloneStorageSafeSourceRange(
|
|
162
|
+
stepProducer.sourceRange,
|
|
163
|
+
),
|
|
164
|
+
}))
|
|
165
|
+
: undefined,
|
|
166
|
+
})),
|
|
167
|
+
}))
|
|
168
|
+
: undefined,
|
|
96
169
|
waterfallIds: base.waterfallIds ? [...base.waterfallIds] : undefined,
|
|
170
|
+
steps: truncateStaticSubstepsForStorage(base.steps, input),
|
|
97
171
|
sheetContract: cloneStorageSafeSheetContract(base.sheetContract),
|
|
98
172
|
};
|
|
99
173
|
}
|
|
@@ -153,7 +227,13 @@ export function truncateStaticPipelineForStorage(
|
|
|
153
227
|
return {
|
|
154
228
|
...pipeline,
|
|
155
229
|
inputFields: pipeline.inputFields ? [...pipeline.inputFields] : undefined,
|
|
230
|
+
rowKeyFields: pipeline.rowKeyFields
|
|
231
|
+
? [...pipeline.rowKeyFields]
|
|
232
|
+
: undefined,
|
|
156
233
|
fields: [...(pipeline.fields ?? [])],
|
|
234
|
+
returnFields: pipeline.returnFields
|
|
235
|
+
? pipeline.returnFields.map((field) => ({ ...field }))
|
|
236
|
+
: undefined,
|
|
157
237
|
stages: truncateStaticSubstepsForStorage(pipeline.stages, {
|
|
158
238
|
embeddedPlayCallPipelineDepth,
|
|
159
239
|
maxEmbeddedPlayCallPipelineDepth,
|
|
@@ -179,6 +259,7 @@ export interface PlayStaticSourceRange {
|
|
|
179
259
|
|
|
180
260
|
type PlayStaticSubstepMetadata = {
|
|
181
261
|
conditional?: boolean;
|
|
262
|
+
staleAfterSeconds?: number;
|
|
182
263
|
};
|
|
183
264
|
|
|
184
265
|
export type PlayStaticSubstep = PlayStaticSubstepMetadata &
|
|
@@ -198,8 +279,11 @@ export type PlayStaticSubstep = PlayStaticSubstepMetadata &
|
|
|
198
279
|
name?: string;
|
|
199
280
|
tableNamespace?: string;
|
|
200
281
|
inputFields?: string[];
|
|
282
|
+
rowKeyFields?: string[];
|
|
201
283
|
outputFields?: string[];
|
|
284
|
+
columns?: PlayStaticDatasetColumn[];
|
|
202
285
|
waterfallIds?: string[];
|
|
286
|
+
steps?: PlayStaticSubstep[];
|
|
203
287
|
sheetContract?: PlaySheetContract | null;
|
|
204
288
|
description?: string;
|
|
205
289
|
sourceRange?: PlayStaticSourceRange;
|
|
@@ -210,6 +294,8 @@ export type PlayStaticSubstep = PlayStaticSubstepMetadata &
|
|
|
210
294
|
type: 'tool';
|
|
211
295
|
toolId: string;
|
|
212
296
|
field: string;
|
|
297
|
+
paramsSource?: string;
|
|
298
|
+
sourceText?: string;
|
|
213
299
|
description?: string;
|
|
214
300
|
inLoop?: boolean;
|
|
215
301
|
isEventWait?: boolean;
|
|
@@ -263,6 +349,7 @@ export type PlayStaticSubstep = PlayStaticSubstepMetadata &
|
|
|
263
349
|
| {
|
|
264
350
|
type: 'run_javascript';
|
|
265
351
|
alias: string;
|
|
352
|
+
sourceText?: string;
|
|
266
353
|
description?: string;
|
|
267
354
|
sourceRange?: PlayStaticSourceRange;
|
|
268
355
|
callDepth?: number;
|
|
@@ -271,6 +358,7 @@ export type PlayStaticSubstep = PlayStaticSubstepMetadata &
|
|
|
271
358
|
| {
|
|
272
359
|
type: 'code';
|
|
273
360
|
field: string;
|
|
361
|
+
sourceText?: string;
|
|
274
362
|
description?: string;
|
|
275
363
|
sourceRange?: PlayStaticSourceRange;
|
|
276
364
|
callDepth?: number;
|
|
@@ -289,6 +377,12 @@ export function getCompiledPipelineSubsteps(
|
|
|
289
377
|
|
|
290
378
|
export function getTopLevelPipelineSubsteps(
|
|
291
379
|
pipeline: PlayStaticPipeline | null | undefined,
|
|
380
|
+
): PlayStaticSubstep[] {
|
|
381
|
+
return compileStaticGraph(pipeline).topLevel;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function getRawTopLevelPipelineSubsteps(
|
|
385
|
+
pipeline: PlayStaticPipeline | null | undefined,
|
|
292
386
|
): PlayStaticSubstep[] {
|
|
293
387
|
if (!pipeline) {
|
|
294
388
|
return [];
|
|
@@ -328,6 +422,14 @@ export function flattenStaticSubsteps(
|
|
|
328
422
|
|
|
329
423
|
for (const substep of substeps) {
|
|
330
424
|
flattened.push(substep);
|
|
425
|
+
if (substep.type === 'dataset' && substep.steps?.length) {
|
|
426
|
+
flattened.push(...flattenStaticSubsteps(substep.steps));
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (substep.type === 'step_suite') {
|
|
430
|
+
flattened.push(...flattenStaticSubsteps(substep.steps));
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
331
433
|
if (substep.type === 'play_call' && substep.pipeline) {
|
|
332
434
|
const nestedSubsteps = getCompiledPipelineSubsteps(substep.pipeline);
|
|
333
435
|
if (nestedSubsteps.length > 0) {
|
|
@@ -345,6 +447,167 @@ export function flattenStaticPipeline(
|
|
|
345
447
|
return flattenStaticSubsteps(getCompiledPipelineSubsteps(pipeline));
|
|
346
448
|
}
|
|
347
449
|
|
|
450
|
+
export function compileStaticGraph(
|
|
451
|
+
pipeline: PlayStaticPipeline | null | undefined,
|
|
452
|
+
): PlayCompiledStaticGraph {
|
|
453
|
+
const rawTopLevel = getRawTopLevelPipelineSubsteps(pipeline);
|
|
454
|
+
const datasets: PlayCompiledStaticGraph['datasets'] = [];
|
|
455
|
+
const topLevel = rawTopLevel.map((substep) => {
|
|
456
|
+
if (substep.type !== 'dataset') {
|
|
457
|
+
return substep;
|
|
458
|
+
}
|
|
459
|
+
const columns = compileDatasetColumns(substep, pipeline?.substeps ?? []);
|
|
460
|
+
const tableNamespace = (substep.tableNamespace ?? substep.field).trim();
|
|
461
|
+
if (tableNamespace) {
|
|
462
|
+
datasets.push({ tableNamespace, columns });
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
...substep,
|
|
466
|
+
columns,
|
|
467
|
+
} satisfies PlayStaticSubstep;
|
|
468
|
+
});
|
|
469
|
+
return { topLevel, datasets };
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function compileDatasetColumns(
|
|
473
|
+
dataset: Extract<PlayStaticSubstep, { type: 'dataset' }>,
|
|
474
|
+
pipelineSubsteps: PlayStaticSubstep[] = [],
|
|
475
|
+
): PlayStaticDatasetColumn[] {
|
|
476
|
+
const columnsById = new Map<string, PlayStaticDatasetColumn>();
|
|
477
|
+
const ensureColumn = (
|
|
478
|
+
id: string,
|
|
479
|
+
source: PlaySheetColumnSource,
|
|
480
|
+
sqlName?: string,
|
|
481
|
+
) => {
|
|
482
|
+
const trimmed = id.trim();
|
|
483
|
+
if (!trimmed) return null;
|
|
484
|
+
const existing = columnsById.get(trimmed);
|
|
485
|
+
if (existing) {
|
|
486
|
+
if (!existing.sqlName && sqlName) existing.sqlName = sqlName;
|
|
487
|
+
return existing;
|
|
488
|
+
}
|
|
489
|
+
const column: PlayStaticDatasetColumn = {
|
|
490
|
+
id: trimmed,
|
|
491
|
+
source,
|
|
492
|
+
...(sqlName ? { sqlName } : {}),
|
|
493
|
+
producers: [],
|
|
494
|
+
};
|
|
495
|
+
columnsById.set(trimmed, column);
|
|
496
|
+
return column;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
for (const column of dataset.sheetContract?.columns ?? []) {
|
|
500
|
+
ensureColumn(column.id, column.source, column.sqlName);
|
|
501
|
+
}
|
|
502
|
+
for (const field of dataset.inputFields ?? []) {
|
|
503
|
+
ensureColumn(field, 'input', sqlSafePlayColumnName(field));
|
|
504
|
+
}
|
|
505
|
+
for (const field of dataset.outputFields ?? []) {
|
|
506
|
+
ensureColumn(field, 'datasetColumn', sqlSafePlayColumnName(field));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const datasetProducerSteps =
|
|
510
|
+
dataset.steps && dataset.steps.length > 0
|
|
511
|
+
? dataset.steps
|
|
512
|
+
: pipelineSubsteps.filter((substep) => {
|
|
513
|
+
const field = fieldForColumnProducer(substep);
|
|
514
|
+
return field ? (dataset.outputFields ?? []).includes(field) : false;
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
for (const substep of datasetProducerSteps) {
|
|
518
|
+
const field = fieldForColumnProducer(substep);
|
|
519
|
+
if (!field) continue;
|
|
520
|
+
const column = ensureColumn(
|
|
521
|
+
field,
|
|
522
|
+
'datasetColumn',
|
|
523
|
+
sqlSafePlayColumnName(field),
|
|
524
|
+
);
|
|
525
|
+
if (!column) continue;
|
|
526
|
+
const pipelineSubstep =
|
|
527
|
+
substep.staleAfterSeconds === undefined
|
|
528
|
+
? pipelineSubsteps.find(
|
|
529
|
+
(candidate) => fieldForColumnProducer(candidate) === field,
|
|
530
|
+
)
|
|
531
|
+
: undefined;
|
|
532
|
+
const producer = columnProducerFromSubstep(
|
|
533
|
+
pipelineSubstep && pipelineSubstep.staleAfterSeconds !== undefined
|
|
534
|
+
? pipelineSubstep
|
|
535
|
+
: substep,
|
|
536
|
+
field,
|
|
537
|
+
);
|
|
538
|
+
column.producers.push(producer);
|
|
539
|
+
if (
|
|
540
|
+
column.staleAfterSeconds === undefined &&
|
|
541
|
+
producer.staleAfterSeconds !== undefined
|
|
542
|
+
) {
|
|
543
|
+
column.staleAfterSeconds = producer.staleAfterSeconds;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return [...columnsById.values()];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function fieldForColumnProducer(substep: PlayStaticSubstep): string | null {
|
|
551
|
+
if ('field' in substep && typeof substep.field === 'string') {
|
|
552
|
+
return substep.field.trim() || null;
|
|
553
|
+
}
|
|
554
|
+
if (substep.type === 'run_javascript') {
|
|
555
|
+
return substep.alias.trim() || null;
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function columnProducerFromSubstep(
|
|
561
|
+
substep: PlayStaticSubstep,
|
|
562
|
+
field: string,
|
|
563
|
+
): PlayStaticColumnProducer {
|
|
564
|
+
const steps =
|
|
565
|
+
substep.type === 'step_suite'
|
|
566
|
+
? substep.steps
|
|
567
|
+
.map((step) => {
|
|
568
|
+
const stepField = fieldForColumnProducer(step) ?? field;
|
|
569
|
+
return columnProducerFromSubstep(step, stepField);
|
|
570
|
+
})
|
|
571
|
+
.filter((producer) => producer.field.trim())
|
|
572
|
+
: undefined;
|
|
573
|
+
const kind: PlayStaticColumnProducerKind =
|
|
574
|
+
substep.type === 'tool'
|
|
575
|
+
? 'tool'
|
|
576
|
+
: substep.type === 'waterfall'
|
|
577
|
+
? 'waterfall'
|
|
578
|
+
: substep.type === 'step_suite'
|
|
579
|
+
? 'stepProgram'
|
|
580
|
+
: substep.type === 'play_call'
|
|
581
|
+
? 'playCall'
|
|
582
|
+
: 'transform';
|
|
583
|
+
return {
|
|
584
|
+
id: producerId(substep, field),
|
|
585
|
+
kind,
|
|
586
|
+
field,
|
|
587
|
+
...(substep.type === 'tool' ? { toolId: substep.toolId } : {}),
|
|
588
|
+
...(substep.type === 'play_call' ? { playId: substep.playId } : {}),
|
|
589
|
+
...(substep.staleAfterSeconds !== undefined
|
|
590
|
+
? { staleAfterSeconds: substep.staleAfterSeconds }
|
|
591
|
+
: {}),
|
|
592
|
+
...(substep.conditional ? { conditional: true } : {}),
|
|
593
|
+
...(substep.sourceRange ? { sourceRange: substep.sourceRange } : {}),
|
|
594
|
+
...(steps && steps.length > 0 ? { steps } : {}),
|
|
595
|
+
substep,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function producerId(substep: PlayStaticSubstep, field: string): string {
|
|
600
|
+
if (substep.type === 'tool') return `${field}:tool:${substep.toolId}`;
|
|
601
|
+
if (substep.type === 'waterfall') {
|
|
602
|
+
return `${field}:waterfall:${substep.id ?? substep.tool ?? 'unknown'}`;
|
|
603
|
+
}
|
|
604
|
+
if (substep.type === 'play_call') return `${field}:play:${substep.playId}`;
|
|
605
|
+
if (substep.type === 'step_suite') return `${field}:steps`;
|
|
606
|
+
if (substep.type === 'run_javascript')
|
|
607
|
+
return `${field}:transform:${substep.alias}`;
|
|
608
|
+
return `${field}:transform`;
|
|
609
|
+
}
|
|
610
|
+
|
|
348
611
|
export function resolveSheetContractForTableNamespace(
|
|
349
612
|
pipeline: PlayStaticPipeline | null | undefined,
|
|
350
613
|
tableNamespace: string | null | undefined,
|
|
@@ -400,6 +663,50 @@ export function resolveSheetContractForTableNamespace(
|
|
|
400
663
|
return resolveFromPipeline(pipeline);
|
|
401
664
|
}
|
|
402
665
|
|
|
666
|
+
export function resolveStaticDatasetColumnsForTableNamespace(
|
|
667
|
+
pipeline: PlayStaticPipeline | null | undefined,
|
|
668
|
+
tableNamespace: string | null | undefined,
|
|
669
|
+
): PlayStaticDatasetColumn[] {
|
|
670
|
+
const requestedNamespace = tableNamespace?.trim();
|
|
671
|
+
if (!pipeline || !requestedNamespace) {
|
|
672
|
+
return [];
|
|
673
|
+
}
|
|
674
|
+
const normalizedNamespace = normalizeTableNamespace(requestedNamespace);
|
|
675
|
+
const seen = new Set<PlayStaticPipeline>();
|
|
676
|
+
|
|
677
|
+
const resolveFromPipeline = (
|
|
678
|
+
currentPipeline: PlayStaticPipeline | null | undefined,
|
|
679
|
+
): PlayStaticDatasetColumn[] | null => {
|
|
680
|
+
if (!currentPipeline || seen.has(currentPipeline)) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
seen.add(currentPipeline);
|
|
684
|
+
|
|
685
|
+
const compiled = compileStaticGraph(currentPipeline);
|
|
686
|
+
const matchingDataset = compiled.datasets.find(
|
|
687
|
+
(dataset) =>
|
|
688
|
+
normalizeTableNamespace(dataset.tableNamespace) ===
|
|
689
|
+
normalizedNamespace,
|
|
690
|
+
);
|
|
691
|
+
if (matchingDataset) {
|
|
692
|
+
return matchingDataset.columns;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
for (const substep of getCompiledPipelineSubsteps(currentPipeline)) {
|
|
696
|
+
if (substep.type === 'play_call') {
|
|
697
|
+
const nestedColumns = resolveFromPipeline(substep.pipeline);
|
|
698
|
+
if (nestedColumns) {
|
|
699
|
+
return nestedColumns;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return null;
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
return resolveFromPipeline(pipeline) ?? [];
|
|
708
|
+
}
|
|
709
|
+
|
|
403
710
|
export function sqlSafePlayColumnName(id: string): string {
|
|
404
711
|
const normalized = id
|
|
405
712
|
.trim()
|
|
@@ -441,8 +748,14 @@ export function compileSheetContract(pipeline: PlayStaticPipeline): {
|
|
|
441
748
|
const inputFields = pipeline.inputFields?.length
|
|
442
749
|
? pipeline.inputFields
|
|
443
750
|
: [tableNamespace];
|
|
751
|
+
const rowKeyFieldSet = new Set(pipeline.rowKeyFields ?? []);
|
|
444
752
|
for (const inputField of inputFields) {
|
|
445
|
-
addColumn({
|
|
753
|
+
addColumn({
|
|
754
|
+
id: inputField,
|
|
755
|
+
source: 'input',
|
|
756
|
+
field: inputField,
|
|
757
|
+
...(rowKeyFieldSet.has(inputField) ? { isRowKey: true } : {}),
|
|
758
|
+
});
|
|
446
759
|
}
|
|
447
760
|
|
|
448
761
|
for (const field of pipeline.fields) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Temporal execution constants.
|
|
3
|
+
*
|
|
4
|
+
* Keep values that both the API/auth surface and the worker need here so the
|
|
5
|
+
* API never imports from worker-only modules.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Local Temporal dev defaults.
|
|
10
|
+
*
|
|
11
|
+
* These match the host ports exposed by docker-compose.yml and the env files
|
|
12
|
+
* used by the local dev flows (`.env.local`, `.env.worktree`).
|
|
13
|
+
*/
|
|
14
|
+
export const LOCAL_TEMPORAL_FRONTEND_PORT = 17233;
|
|
15
|
+
export const LOCAL_TEMPORAL_UI_PORT = 18233;
|
|
16
|
+
export const LOCAL_TEMPORAL_NAMESPACE = 'default';
|
|
17
|
+
export const LOCAL_TEMPORAL_ADDRESS = `127.0.0.1:${LOCAL_TEMPORAL_FRONTEND_PORT}`;
|
|
18
|
+
export const LOCAL_TEMPORAL_UI_URL = `http://127.0.0.1:${LOCAL_TEMPORAL_UI_PORT}`;
|
|
19
|
+
|
|
20
|
+
/** Maximum active user-code runtime for a standard play, in seconds. */
|
|
21
|
+
export const STANDARD_PLAY_RUNTIME_LIMIT_SECONDS = 10 * 60; // 10 minutes
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Activity timeout includes cleanup/billing headroom after the 10 minute
|
|
25
|
+
* user-code runtime cap. Keep this higher than STANDARD_PLAY_RUNTIME_LIMIT_SECONDS.
|
|
26
|
+
*/
|
|
27
|
+
export const PLAY_ACTIVITY_TIMEOUT_SECONDS = 12 * 60; // 12 minutes
|
|
28
|
+
|
|
29
|
+
/** Heartbeat cadence for the long-running play execution activity. */
|
|
30
|
+
export const PLAY_EXECUTE_ACTIVITY_HEARTBEAT_INTERVAL_SECONDS = 15;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* TTL for workflow executor tokens, in seconds.
|
|
34
|
+
* Matches the activity timeout so tokens expire when the activity would
|
|
35
|
+
* time out anyway.
|
|
36
|
+
*/
|
|
37
|
+
export const WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS =
|
|
38
|
+
PLAY_ACTIVITY_TIMEOUT_SECONDS;
|