deepline 0.1.24 → 0.1.26

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.
@@ -0,0 +1,228 @@
1
+ export type PlayStepLifecycleNode = {
2
+ nodeId: string;
3
+ type: string;
4
+ };
5
+
6
+ export type PlayStepLifecycleProgress = {
7
+ startedAt?: number | null;
8
+ completedAt?: number | null;
9
+ };
10
+
11
+ export type PlayStepLifecycleEvent = {
12
+ nodeId: string;
13
+ type: string;
14
+ transition: 'started' | 'completed' | 'failed';
15
+ at: number;
16
+ };
17
+
18
+ function isAutoStartedSetupNode(type: string): boolean {
19
+ return type === 'csv' || type === 'code' || type === 'run_javascript';
20
+ }
21
+
22
+ export class PlayStepLifecycleTracker {
23
+ private failedNodeIds = new Set<string>();
24
+
25
+ constructor(
26
+ private readonly nodes: readonly PlayStepLifecycleNode[],
27
+ private readonly getProgress: () => Record<string, PlayStepLifecycleProgress>,
28
+ private readonly emit: (event: PlayStepLifecycleEvent) => void,
29
+ private readonly now: () => number = Date.now,
30
+ ) {}
31
+
32
+ markPreMapStepsStarted(at = this.now()): void {
33
+ for (const node of this.nodes) {
34
+ if (!isAutoStartedSetupNode(node.type)) break;
35
+ if (!this.getProgress()[node.nodeId]?.startedAt) {
36
+ this.emit({
37
+ nodeId: node.nodeId,
38
+ type: node.type,
39
+ transition: 'started',
40
+ at,
41
+ });
42
+ }
43
+ }
44
+ }
45
+
46
+ onToolCalled(toolId: string, at = this.now()): void {
47
+ const normalizedToolId = toolId.trim();
48
+ if (!normalizedToolId) {
49
+ return;
50
+ }
51
+ this.completeStartedNonMapNodes(at);
52
+ const node = this.nodes.find((candidate) => {
53
+ if (candidate.type !== 'tool') {
54
+ return false;
55
+ }
56
+ if (this.getProgress()[candidate.nodeId]?.startedAt) {
57
+ return false;
58
+ }
59
+ return candidate.nodeId.endsWith(`:${normalizedToolId}`);
60
+ });
61
+ if (!node) {
62
+ return;
63
+ }
64
+ this.emit({
65
+ nodeId: node.nodeId,
66
+ type: node.type,
67
+ transition: 'started',
68
+ at,
69
+ });
70
+ }
71
+
72
+ onToolFailed(toolId: string, at = this.now()): void {
73
+ const normalizedToolId = toolId.trim();
74
+ if (!normalizedToolId) {
75
+ return;
76
+ }
77
+ const node = [...this.nodes].reverse().find((candidate) => {
78
+ if (candidate.type !== 'tool') {
79
+ return false;
80
+ }
81
+ if (!candidate.nodeId.endsWith(`:${normalizedToolId}`)) {
82
+ return false;
83
+ }
84
+ const existing = this.getProgress()[candidate.nodeId];
85
+ return Boolean(existing?.startedAt) && !existing?.completedAt;
86
+ });
87
+ if (!node) {
88
+ return;
89
+ }
90
+ this.failedNodeIds.add(node.nodeId);
91
+ this.emit({
92
+ nodeId: node.nodeId,
93
+ type: node.type,
94
+ transition: 'failed',
95
+ at,
96
+ });
97
+ }
98
+
99
+ onMapStarted(mapNodeId: string, at = this.now()): void {
100
+ const mapIndex = this.nodes.findIndex((node) => node.nodeId === mapNodeId);
101
+ if (mapIndex < 0) return;
102
+ const mapNode = this.nodes[mapIndex]!;
103
+ for (let index = 0; index < mapIndex; index += 1) {
104
+ const node = this.nodes[index]!;
105
+ if (
106
+ node.type !== 'map' &&
107
+ this.getProgress()[node.nodeId]?.startedAt &&
108
+ !this.getProgress()[node.nodeId]?.completedAt &&
109
+ !this.failedNodeIds.has(node.nodeId)
110
+ ) {
111
+ this.emit({
112
+ nodeId: node.nodeId,
113
+ type: node.type,
114
+ transition: 'completed',
115
+ at,
116
+ });
117
+ }
118
+ }
119
+ if (!this.getProgress()[mapNodeId]?.startedAt) {
120
+ this.emit({
121
+ nodeId: mapNode.nodeId,
122
+ type: mapNode.type,
123
+ transition: 'started',
124
+ at,
125
+ });
126
+ }
127
+ }
128
+
129
+ onMapCompleted(mapNodeId: string, at = this.now()): void {
130
+ const mapIndex = this.nodes.findIndex((node) => node.nodeId === mapNodeId);
131
+ if (mapIndex < 0) return;
132
+ const mapNode = this.nodes[mapIndex]!;
133
+ if (!this.getProgress()[mapNodeId]?.completedAt) {
134
+ this.emit({
135
+ nodeId: mapNode.nodeId,
136
+ type: mapNode.type,
137
+ transition: 'completed',
138
+ at,
139
+ });
140
+ }
141
+ for (let index = mapIndex + 1; index < this.nodes.length; index += 1) {
142
+ const node = this.nodes[index]!;
143
+ if (node.type === 'map') break;
144
+ if (
145
+ isAutoStartedSetupNode(node.type) &&
146
+ !this.getProgress()[node.nodeId]?.startedAt
147
+ ) {
148
+ this.emit({
149
+ nodeId: node.nodeId,
150
+ type: node.type,
151
+ transition: 'started',
152
+ at,
153
+ });
154
+ }
155
+ }
156
+ }
157
+
158
+ onMapFailed(mapNodeId: string, at = this.now()): void {
159
+ const mapIndex = this.nodes.findIndex((node) => node.nodeId === mapNodeId);
160
+ if (mapIndex < 0) return;
161
+ const mapNode = this.nodes[mapIndex]!;
162
+ if (!this.getProgress()[mapNodeId]?.completedAt) {
163
+ this.failedNodeIds.add(mapNode.nodeId);
164
+ this.emit({
165
+ nodeId: mapNode.nodeId,
166
+ type: mapNode.type,
167
+ transition: 'failed',
168
+ at,
169
+ });
170
+ }
171
+ }
172
+
173
+ markAllTerminal(at = this.now()): void {
174
+ for (const node of this.nodes) {
175
+ const existing = this.getProgress()[node.nodeId];
176
+ if (
177
+ existing?.startedAt &&
178
+ !existing.completedAt &&
179
+ !this.failedNodeIds.has(node.nodeId)
180
+ ) {
181
+ this.emit({
182
+ nodeId: node.nodeId,
183
+ type: node.type,
184
+ transition: 'completed',
185
+ at,
186
+ });
187
+ }
188
+ }
189
+ }
190
+
191
+ markStartedFailed(at = this.now()): void {
192
+ for (let index = this.nodes.length - 1; index >= 0; index -= 1) {
193
+ const node = this.nodes[index]!;
194
+ const existing = this.getProgress()[node.nodeId];
195
+ if (existing?.startedAt && !existing.completedAt) {
196
+ this.failedNodeIds.add(node.nodeId);
197
+ this.emit({
198
+ nodeId: node.nodeId,
199
+ type: node.type,
200
+ transition: 'failed',
201
+ at,
202
+ });
203
+ return;
204
+ }
205
+ }
206
+ }
207
+
208
+ private completeStartedNonMapNodes(at: number): void {
209
+ for (const node of this.nodes) {
210
+ if (node.type === 'map') {
211
+ continue;
212
+ }
213
+ const existing = this.getProgress()[node.nodeId];
214
+ if (
215
+ existing?.startedAt &&
216
+ !existing.completedAt &&
217
+ !this.failedNodeIds.has(node.nodeId)
218
+ ) {
219
+ this.emit({
220
+ nodeId: node.nodeId,
221
+ type: node.type,
222
+ transition: 'completed',
223
+ at,
224
+ });
225
+ }
226
+ }
227
+ }
228
+ }
@@ -1,24 +1,24 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
2
3
  import { mkdir, readFile, realpath, stat, writeFile } from 'node:fs/promises';
3
4
  import { tmpdir } from 'node:os';
4
5
  import { basename, dirname, extname, isAbsolute, join, resolve } from 'node:path';
5
- import { builtinModules, createRequire } from 'node:module';
6
+ import { builtinModules } from 'node:module';
6
7
  import { build, type Message, type Plugin } from 'esbuild';
7
8
  import {
8
9
  PLAY_ARTIFACT_KINDS,
9
10
  type PlayArtifactKind,
10
- } from '../../play-runtime/backend.js';
11
- import type { PlayCompilerManifest } from '../compiler-manifest.js';
11
+ } from '../../play-runtime/backend';
12
+ import type { PlayCompilerManifest } from '../compiler-manifest';
12
13
  import type {
13
14
  PlayArtifactCompatibility,
14
15
  PlayBundleArtifact,
15
16
  PlayImportPolicy,
16
17
  PlayPackageImport,
17
18
  PlayRuntimeFeature,
18
- } from '../artifact-types.js';
19
- import { buildPlayContractCompatibility } from '../contracts.js';
19
+ } from '../artifact-types';
20
+ import { buildPlayContractCompatibility } from '../contracts';
20
21
 
21
- const playArtifactRequire = createRequire(import.meta.url);
22
22
  const PLAY_BUNDLE_CACHE_VERSION = 24;
23
23
  const MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
24
24
  // workerd local-mode (`wrangler dev` Worker Loader) silently fails to
@@ -343,10 +343,7 @@ function findMatchingBrace(source: string, openIndex: number): number {
343
343
  return -1;
344
344
  }
345
345
 
346
- export function extractDefinedPlayName(
347
- sourceCode: string,
348
- _filePath: string,
349
- ): string | null {
346
+ export function extractDefinedPlayName(sourceCode: string): string | null {
350
347
  const source = stripCommentsToSpaces(sourceCode);
351
348
  const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
352
349
  for (const match of source.matchAll(callPattern)) {
@@ -374,13 +371,79 @@ export function extractDefinedPlayName(
374
371
  return null;
375
372
  }
376
373
 
377
- function getPackageRequireCandidates(fromFile: string) {
378
- const candidates = [
379
- createRequire(fromFile),
380
- createRequire(join(process.cwd(), 'package.json')),
381
- playArtifactRequire,
374
+ function readPackageVersionFromPackageJson(
375
+ packageJsonPath: string,
376
+ packageName: string,
377
+ ): string | null {
378
+ try {
379
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
380
+ name?: unknown;
381
+ version?: unknown;
382
+ };
383
+ if (
384
+ packageJson.name === packageName &&
385
+ typeof packageJson.version === 'string'
386
+ ) {
387
+ return packageJson.version;
388
+ }
389
+ } catch {
390
+ return null;
391
+ }
392
+ return null;
393
+ }
394
+
395
+ function findPackageJsonPathFrom(
396
+ startDir: string,
397
+ packageName: string,
398
+ ): string | null {
399
+ let current = resolve(startDir);
400
+ while (true) {
401
+ const packageJsonPath = join(
402
+ current,
403
+ 'node_modules',
404
+ packageName,
405
+ 'package.json',
406
+ );
407
+ if (existsSync(packageJsonPath)) {
408
+ return packageJsonPath;
409
+ }
410
+
411
+ const parent = dirname(current);
412
+ if (parent === current) {
413
+ return null;
414
+ }
415
+ current = parent;
416
+ }
417
+ }
418
+
419
+ function findPackageJsonPath(
420
+ packageName: string,
421
+ fromFile: string,
422
+ adapter: PlayBundlingAdapter,
423
+ ): string | null {
424
+ const startDirs = [
425
+ dirname(fromFile),
426
+ adapter.projectRoot,
427
+ dirname(adapter.sdkPackageJson),
428
+ process.cwd(),
382
429
  ];
383
- return candidates;
430
+ const seen = new Set<string>();
431
+ for (const startDir of startDirs) {
432
+ const normalized = resolve(startDir);
433
+ if (seen.has(normalized)) continue;
434
+ seen.add(normalized);
435
+ const packageJsonPath = findPackageJsonPathFrom(normalized, packageName);
436
+ if (packageJsonPath) return packageJsonPath;
437
+ }
438
+
439
+ const adapterNodeModulesPackageJson = join(
440
+ adapter.nodeModulesDir,
441
+ packageName,
442
+ 'package.json',
443
+ );
444
+ return existsSync(adapterNodeModulesPackageJson)
445
+ ? adapterNodeModulesPackageJson
446
+ : null;
384
447
  }
385
448
 
386
449
  function localSdkAliasPlugin(
@@ -390,7 +453,7 @@ function localSdkAliasPlugin(
390
453
  const entryFile = options?.workersRuntime
391
454
  ? adapter.sdkWorkersEntryFile
392
455
  : adapter.sdkEntryFile;
393
- if (!playArtifactRequire('node:fs').existsSync(entryFile)) {
456
+ if (!existsSync(entryFile)) {
394
457
  return null;
395
458
  }
396
459
 
@@ -754,48 +817,24 @@ function resolvePackageImport(
754
817
  adapter: PlayBundlingAdapter,
755
818
  ): PackageResolution {
756
819
  const packageName = getPackageName(specifier);
757
- if (packageName === 'deepline' && playArtifactRequire('node:fs').existsSync(adapter.sdkPackageJson)) {
820
+ if (packageName === 'deepline' && existsSync(adapter.sdkPackageJson)) {
758
821
  const packageJson = JSON.parse(
759
- playArtifactRequire('node:fs').readFileSync(adapter.sdkPackageJson, 'utf-8'),
822
+ readFileSync(adapter.sdkPackageJson, 'utf-8'),
760
823
  ) as { version?: string };
761
824
  return {
762
825
  name: 'deepline',
763
826
  version: packageJson.version ?? null,
764
827
  };
765
828
  }
766
- const candidateRequires = getPackageRequireCandidates(fromFile);
767
- let resolved = false;
768
-
769
- for (const candidateRequire of candidateRequires) {
770
- try {
771
- candidateRequire.resolve(specifier);
772
- resolved = true;
773
- break;
774
- } catch {
775
- continue;
776
- }
777
- }
778
-
779
- if (!resolved) {
829
+ const packageJsonPath = findPackageJsonPath(packageName, fromFile, adapter);
830
+ if (!packageJsonPath) {
780
831
  throw new Error(`Could not resolve "${specifier}" from ${fromFile}`);
781
832
  }
782
833
 
783
- let version: string | null = null;
784
-
785
- for (const candidateRequire of candidateRequires) {
786
- try {
787
- const packageJsonPath = candidateRequire.resolve(`${packageName}/package.json`);
788
- const packageJson = JSON.parse(
789
- playArtifactRequire('node:fs').readFileSync(packageJsonPath, 'utf-8'),
790
- ) as { version?: string };
791
- version = packageJson.version ?? null;
792
- break;
793
- } catch {
794
- continue;
795
- }
796
- }
797
-
798
- return { name: packageName, version };
834
+ return {
835
+ name: packageName,
836
+ version: readPackageVersionFromPackageJson(packageJsonPath, packageName),
837
+ };
799
838
  }
800
839
 
801
840
  async function analyzeSourceGraph(
@@ -853,7 +892,7 @@ async function analyzeSourceGraph(
853
892
  });
854
893
  if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
855
894
  const importedSource = await readFile(resolved, 'utf-8');
856
- const importedPlayName = extractDefinedPlayName(importedSource, resolved);
895
+ const importedPlayName = extractDefinedPlayName(importedSource);
857
896
  if (!importedPlayName) {
858
897
  throw new Error(
859
898
  `${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`,
@@ -918,7 +957,7 @@ async function analyzeSourceGraph(
918
957
  .sort((left, right) => left.filePath.localeCompare(right.filePath)),
919
958
  }),
920
959
  );
921
- const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
960
+ const playName = extractDefinedPlayName(sourceCode);
922
961
 
923
962
  return {
924
963
  sourceCode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,208 +0,0 @@
1
- import type { ComputeBillingItem } from './worker-api-types';
2
- import type { DbSessionOperation } from './db-session';
3
- import type { PlayRowUpdate } from './ctx-types';
4
- import type { PlaySheetContract } from '../plays/static-pipeline';
5
-
6
- export type RuntimeRunStatusAction = {
7
- action: 'update_run_status';
8
- playId: string;
9
- status: string;
10
- error?: string | null;
11
- runtimeBackend?: string | null;
12
- artifactHash?: string | null;
13
- graphHash?: string | null;
14
- waitKind?: string | null;
15
- waitUntil?: number | null;
16
- activeBoundaryId?: string | null;
17
- lastCheckpointAt?: number | null;
18
- liveLogs?: string[];
19
- liveTimeline?: unknown;
20
- liveNodeProgress?: unknown;
21
- result?: Record<string, unknown> | null;
22
- };
23
-
24
- export type RuntimeSheetAction =
25
- | {
26
- action: 'ensure_sheet';
27
- playName: string;
28
- tableNamespace: string;
29
- sheetContract?: PlaySheetContract | null;
30
- userEmail?: string | null;
31
- }
32
- | {
33
- action: 'create_db_session';
34
- playName: string;
35
- target: {
36
- tableNamespace: string;
37
- logicalTable: 'sheet_rows' | 'dataset_rows' | 'play_output';
38
- };
39
- operations: DbSessionOperation[];
40
- limits?: {
41
- maxRows?: number;
42
- maxBytes?: number;
43
- maxRequests?: number;
44
- };
45
- sheetContract?: PlaySheetContract | null;
46
- ttlSeconds?: number;
47
- userEmail?: string | null;
48
- }
49
- | {
50
- action: 'apply_row_updates';
51
- playName: string;
52
- tableNamespace: string;
53
- sheetContract?: PlaySheetContract | null;
54
- contractSnapshot?: unknown;
55
- runId: string;
56
- userEmail?: string | null;
57
- updates: Array<Omit<PlayRowUpdate, 'rowId'> & { runId?: string }>;
58
- }
59
- | {
60
- action: 'start_sheet_dataset';
61
- playName: string;
62
- tableNamespace: string;
63
- sheetContract?: PlaySheetContract | null;
64
- contractSnapshot?: unknown;
65
- rows: unknown;
66
- runId: string;
67
- userEmail?: string | null;
68
- }
69
- | {
70
- action: 'persist_completed_sheet_rows';
71
- playName: string;
72
- tableNamespace: string;
73
- sheetContract?: PlaySheetContract | null;
74
- contractSnapshot?: unknown;
75
- rows: unknown;
76
- outputFields: string[];
77
- runId: string;
78
- userEmail?: string | null;
79
- };
80
-
81
- export type RuntimeArtifactAction =
82
- | {
83
- action: 'create_signed_artifact_url';
84
- storageKey: string;
85
- }
86
- | {
87
- action: 'create_signed_staged_file_url';
88
- file: { storageKey: string };
89
- };
90
-
91
- export type RuntimeBillingAction =
92
- | {
93
- action: 'compute_billing_upsert';
94
- sessionId: string;
95
- orgId: string;
96
- userId?: string | null;
97
- operation: string;
98
- workflowId?: string;
99
- runId?: string;
100
- }
101
- | {
102
- action: 'compute_billing_record_item';
103
- sessionId: string;
104
- orgId: string;
105
- userId?: string | null;
106
- operation: string;
107
- item: ComputeBillingItem;
108
- }
109
- | {
110
- action: 'compute_billing_finalize';
111
- sessionId: string;
112
- orgId: string;
113
- userId?: string | null;
114
- operation: string;
115
- status: 'completed' | 'error';
116
- workflowId?: string;
117
- runId?: string;
118
- maxCreditsPerRun?: number | null;
119
- finalItem: ComputeBillingItem;
120
- };
121
-
122
- export type RuntimeReceiptAction =
123
- | {
124
- action: 'get_runtime_step_receipt';
125
- playName: string;
126
- runId: string;
127
- key: string;
128
- }
129
- | {
130
- action: 'claim_runtime_step_receipt';
131
- playName: string;
132
- runId: string;
133
- key: string;
134
- }
135
- | {
136
- action: 'complete_runtime_step_receipt';
137
- playName: string;
138
- runId: string;
139
- key: string;
140
- output: unknown;
141
- }
142
- | {
143
- action: 'fail_runtime_step_receipt';
144
- playName: string;
145
- runId: string;
146
- key: string;
147
- error: string;
148
- }
149
- | {
150
- action: 'skip_runtime_step_receipt';
151
- playName: string;
152
- runId: string;
153
- key: string;
154
- output?: unknown;
155
- };
156
-
157
- export type RuntimePlayResolutionAction = {
158
- action: 'resolve_play';
159
- playRef: string;
160
- };
161
-
162
- export type RuntimeResultsAction = {
163
- action: 'save_results';
164
- playId: string;
165
- userId?: string | null;
166
- result: {
167
- success: boolean;
168
- error?: string;
169
- publicResult?: Record<string, unknown> | null;
170
- maxCreditsPerRun?: number | null;
171
- temporalActionEstimate?: Record<string, unknown> | null;
172
- };
173
- };
174
-
175
- export type RuntimeApiAction =
176
- | RuntimePlayResolutionAction
177
- | RuntimeSheetAction
178
- | RuntimeArtifactAction
179
- | RuntimeRunStatusAction
180
- | RuntimeResultsAction
181
- | RuntimeBillingAction
182
- | RuntimeReceiptAction;
183
-
184
- export type RuntimeBatchAction = {
185
- action: 'batch';
186
- events: RuntimeApiAction[];
187
- };
188
-
189
- export const runtimeRunActions = {
190
- updateStatus(input: Omit<RuntimeRunStatusAction, 'action'>): RuntimeRunStatusAction {
191
- return { action: 'update_run_status', ...input };
192
- },
193
- } as const;
194
-
195
- export const runtimeSheetActions = {
196
- startDataset(input: Omit<Extract<RuntimeSheetAction, { action: 'start_sheet_dataset' }>, 'action'>) {
197
- return { action: 'start_sheet_dataset', ...input } satisfies RuntimeSheetAction;
198
- },
199
- persistCompletedRows(input: Omit<Extract<RuntimeSheetAction, { action: 'persist_completed_sheet_rows' }>, 'action'>) {
200
- return { action: 'persist_completed_sheet_rows', ...input } satisfies RuntimeSheetAction;
201
- },
202
- } as const;
203
-
204
- export const runtimeBillingActions = {
205
- finalize(input: Omit<Extract<RuntimeBillingAction, { action: 'compute_billing_finalize' }>, 'action'>) {
206
- return { action: 'compute_billing_finalize', ...input } satisfies RuntimeBillingAction;
207
- },
208
- } as const;