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.
Files changed (26) hide show
  1. package/dist/cli/index.js +69 -37
  2. package/dist/cli/index.mjs +69 -37
  3. package/dist/index.d.mts +32 -1
  4. package/dist/index.d.ts +32 -1
  5. package/dist/index.js +7 -4
  6. package/dist/index.mjs +7 -4
  7. package/dist/repo/apps/play-runner-workers/src/child-play-await.ts +192 -0
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +1320 -1644
  9. package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +515 -648
  10. package/dist/repo/apps/play-runner-workers/src/entry.ts +896 -354
  11. package/dist/repo/apps/play-runner-workers/src/workflow-retry-state.ts +209 -0
  12. package/dist/repo/sdk/src/client.ts +9 -2
  13. package/dist/repo/sdk/src/release.ts +2 -2
  14. package/dist/repo/sdk/src/types.ts +5 -0
  15. package/dist/repo/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +231 -0
  16. package/dist/repo/shared_libs/play-runtime/governor/governor.ts +376 -0
  17. package/dist/repo/shared_libs/play-runtime/governor/policy.ts +179 -0
  18. package/dist/repo/shared_libs/play-runtime/governor/rate-state-backend.ts +87 -0
  19. package/dist/repo/shared_libs/play-runtime/run-failure.ts +12 -0
  20. package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +24 -0
  21. package/dist/repo/shared_libs/play-runtime/submit-limits.ts +35 -0
  22. package/dist/repo/shared_libs/plays/bundling/index.ts +4 -12
  23. package/dist/repo/shared_libs/plays/bundling/limits.ts +29 -0
  24. package/dist/repo/shared_libs/plays/static-pipeline.ts +314 -1
  25. package/dist/repo/shared_libs/temporal/constants.ts +38 -0
  26. 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({ id: inputField, source: 'input', field: inputField });
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {