deepline 0.2.49 → 0.2.50

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,61 @@
1
+ // Compile-time safety gate for user-authored play code (enrich `extract_js`,
2
+ // `run_if_js`, and `run_javascript` step `code`).
3
+ //
4
+ // Plays replay deterministically under Worker Loader, and the step code runs
5
+ // with provider data in scope. So user code may NOT be non-deterministic
6
+ // (Math.random, Date.now, ...) — replay would diverge — and may NOT reach out
7
+ // of the sandbox (fetch, require, process, ...). We reject those at compile
8
+ // time with a clear, named error (fail loud, per the repo non-negotiables).
9
+ //
10
+ // This is a fast static scan, not a security boundary on its own: the play
11
+ // runtime is the real boundary (it executes step code in an isolate that does
12
+ // not expose these globals). The scan exists to fail authoring early with a
13
+ // readable message instead of producing a play that silently misbehaves at run
14
+ // time. The `(?<!\.)` guards keep it from flagging harmless property access on
15
+ // user objects (e.g. `row.process_date`, `data.fetch`).
16
+
17
+ type ForbiddenRule = { readonly pattern: RegExp; readonly reason: string };
18
+
19
+ const FORBIDDEN: readonly ForbiddenRule[] = [
20
+ // Non-deterministic — breaks replay.
21
+ { pattern: /\bMath\s*\.\s*random\b/, reason: 'Math.random()' },
22
+ { pattern: /\bDate\s*\.\s*now\b/, reason: 'Date.now()' },
23
+ { pattern: /\bnew\s+Date\s*\(\s*\)/, reason: 'new Date() with no argument' },
24
+ { pattern: /\bperformance\s*\.\s*now\b/, reason: 'performance.now()' },
25
+ {
26
+ pattern: /\bcrypto\s*\.\s*(?:randomUUID|getRandomValues)\b/,
27
+ reason: 'crypto random',
28
+ },
29
+ // Sandbox escape / I/O.
30
+ { pattern: /(?<!\.)\bfetch\s*\(/, reason: 'fetch()' },
31
+ { pattern: /(?<!\.)\bimport\s*\(/, reason: 'dynamic import()' },
32
+ { pattern: /(?<!\.)\brequire\s*\(/, reason: 'require()' },
33
+ { pattern: /(?<!\.)\beval\s*\(/, reason: 'eval()' },
34
+ {
35
+ pattern: /(?<!\.)\bnew\s+Function\b|(?<!\.)\bFunction\s*\(/,
36
+ reason: 'the Function constructor',
37
+ },
38
+ { pattern: /(?<!\.)\bprocess\b/, reason: 'process' },
39
+ { pattern: /(?<!\.)\bglobalThis\b/, reason: 'globalThis' },
40
+ { pattern: /(?<!\.)\b(?:window|self)\b/, reason: 'window/self' },
41
+ { pattern: /(?<!\.)\bXMLHttpRequest\b/, reason: 'XMLHttpRequest' },
42
+ { pattern: /(?<!\.)\bWebAssembly\b/, reason: 'WebAssembly' },
43
+ ];
44
+
45
+ /**
46
+ * Throw if `code` references a forbidden (non-deterministic or sandbox-escaping)
47
+ * construct. `label` describes where the code came from for the error message,
48
+ * e.g. `extract_js for "email"` or `run_javascript step "enrich"`.
49
+ */
50
+ export function assertUserCodeIsSafe(code: string, label: string): void {
51
+ if (typeof code !== 'string' || !code.trim()) return;
52
+ for (const { pattern, reason } of FORBIDDEN) {
53
+ if (pattern.test(code)) {
54
+ throw new Error(
55
+ `${label} uses ${reason}, which is not allowed in play code: it ` +
56
+ `breaks deterministic replay or escapes the sandbox. Remove it and ` +
57
+ `compute the value from the row/result instead.`,
58
+ );
59
+ }
60
+ }
61
+ }
package/dist/cli/index.js CHANGED
@@ -1044,7 +1044,7 @@ var SDK_RELEASE = {
1044
1044
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1045
1045
  // exposed storage-dependent synchronous access. This deliberate minor
1046
1046
  // release keeps lazy paging semantics independent of row residency.
1047
- version: "0.2.49",
1047
+ version: "0.2.50",
1048
1048
  contracts: {
1049
1049
  api: {
1050
1050
  name: "sdk-http-api",
@@ -24663,7 +24663,7 @@ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
24663
24663
  return null;
24664
24664
  }
24665
24665
 
24666
- // src/cli/enrich-compat-adapter.ts
24666
+ // ../shared_libs/plays/enrich-compat-adapter.ts
24667
24667
  var ENRICH_COMPAT_DEFAULT_PLAY_NAME = "deepline-enrich-v1-compat";
24668
24668
  var ENRICH_COMPAT_DEFAULT_MAP_NAME = "deepline_enrich_rows";
24669
24669
  function buildEnrichCompatibilityPlan(options = {}) {
@@ -24673,7 +24673,7 @@ function buildEnrichCompatibilityPlan(options = {}) {
24673
24673
  };
24674
24674
  }
24675
24675
 
24676
- // src/cli/user-code-safety.ts
24676
+ // ../shared_libs/plays/user-code-safety.ts
24677
24677
  var FORBIDDEN = [
24678
24678
  // Non-deterministic — breaks replay.
24679
24679
  { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
@@ -24710,7 +24710,7 @@ function assertUserCodeIsSafe(code, label) {
24710
24710
  }
24711
24711
  }
24712
24712
 
24713
- // src/cli/enrich-play-compiler.ts
24713
+ // ../shared_libs/plays/enrich-play-compiler.ts
24714
24714
  function isWaterfall(command) {
24715
24715
  return "with_waterfall" in command;
24716
24716
  }
@@ -25141,7 +25141,9 @@ function compileEnrichConfigToPlaySource(config, options = {}) {
25141
25141
  const generatedAliases = collectGeneratedAliases(config.commands);
25142
25142
  const runOptionsSource = options.failFast ? `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart), onRowError: 'fail' as const }` : `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart) }`;
25143
25143
  const playOptionsSource = [
25144
- `description: ${stringLiteral("Read a CSV file, run the configured Deepline enrich commands, and return enriched rows.")}`,
25144
+ `description: ${stringLiteral(
25145
+ "Read a CSV file, run the configured Deepline enrich commands, and return enriched rows."
25146
+ )}`,
25145
25147
  ...options.maxCreditsPerRun === void 0 ? [] : [`billing: { maxCreditsPerRun: ${String(options.maxCreditsPerRun)} }`]
25146
25148
  ].join(", ");
25147
25149
  const body = [
@@ -1030,7 +1030,7 @@ var SDK_RELEASE = {
1030
1030
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1031
1031
  // exposed storage-dependent synchronous access. This deliberate minor
1032
1032
  // release keeps lazy paging semantics independent of row residency.
1033
- version: "0.2.49",
1033
+ version: "0.2.50",
1034
1034
  contracts: {
1035
1035
  api: {
1036
1036
  name: "sdk-http-api",
@@ -12313,7 +12313,9 @@ import {
12313
12313
  extname,
12314
12314
  isAbsolute as isAbsolute3,
12315
12315
  join as join7,
12316
- resolve as resolve8
12316
+ relative as relative2,
12317
+ resolve as resolve8,
12318
+ sep
12317
12319
  } from "path";
12318
12320
  import { builtinModules } from "module";
12319
12321
  import { Parser } from "acorn";
@@ -24707,7 +24709,7 @@ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
24707
24709
  return null;
24708
24710
  }
24709
24711
 
24710
- // src/cli/enrich-compat-adapter.ts
24712
+ // ../shared_libs/plays/enrich-compat-adapter.ts
24711
24713
  var ENRICH_COMPAT_DEFAULT_PLAY_NAME = "deepline-enrich-v1-compat";
24712
24714
  var ENRICH_COMPAT_DEFAULT_MAP_NAME = "deepline_enrich_rows";
24713
24715
  function buildEnrichCompatibilityPlan(options = {}) {
@@ -24717,7 +24719,7 @@ function buildEnrichCompatibilityPlan(options = {}) {
24717
24719
  };
24718
24720
  }
24719
24721
 
24720
- // src/cli/user-code-safety.ts
24722
+ // ../shared_libs/plays/user-code-safety.ts
24721
24723
  var FORBIDDEN = [
24722
24724
  // Non-deterministic — breaks replay.
24723
24725
  { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
@@ -24754,7 +24756,7 @@ function assertUserCodeIsSafe(code, label) {
24754
24756
  }
24755
24757
  }
24756
24758
 
24757
- // src/cli/enrich-play-compiler.ts
24759
+ // ../shared_libs/plays/enrich-play-compiler.ts
24758
24760
  function isWaterfall(command) {
24759
24761
  return "with_waterfall" in command;
24760
24762
  }
@@ -25185,7 +25187,9 @@ function compileEnrichConfigToPlaySource(config, options = {}) {
25185
25187
  const generatedAliases = collectGeneratedAliases(config.commands);
25186
25188
  const runOptionsSource = options.failFast ? `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart), onRowError: 'fail' as const }` : `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart) }`;
25187
25189
  const playOptionsSource = [
25188
- `description: ${stringLiteral("Read a CSV file, run the configured Deepline enrich commands, and return enriched rows.")}`,
25190
+ `description: ${stringLiteral(
25191
+ "Read a CSV file, run the configured Deepline enrich commands, and return enriched rows."
25192
+ )}`,
25189
25193
  ...options.maxCreditsPerRun === void 0 ? [] : [`billing: { maxCreditsPerRun: ${String(options.maxCreditsPerRun)} }`]
25190
25194
  ].join(", ");
25191
25195
  const body = [
package/dist/index.js CHANGED
@@ -763,7 +763,7 @@ var SDK_RELEASE = {
763
763
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
764
764
  // exposed storage-dependent synchronous access. This deliberate minor
765
765
  // release keeps lazy paging semantics independent of row residency.
766
- version: "0.2.49",
766
+ version: "0.2.50",
767
767
  contracts: {
768
768
  api: {
769
769
  name: "sdk-http-api",
package/dist/index.mjs CHANGED
@@ -689,7 +689,7 @@ var SDK_RELEASE = {
689
689
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
690
690
  // exposed storage-dependent synchronous access. This deliberate minor
691
691
  // release keeps lazy paging semantics independent of row residency.
692
- version: "0.2.49",
692
+ version: "0.2.50",
693
693
  contracts: {
694
694
  api: {
695
695
  name: "sdk-http-api",
@@ -191,6 +191,8 @@
191
191
  "dist/bundling-sources/shared_libs/plays/dataset-summary.ts",
192
192
  "dist/bundling-sources/shared_libs/plays/dataset.ts",
193
193
  "dist/bundling-sources/shared_libs/plays/definition.ts",
194
+ "dist/bundling-sources/shared_libs/plays/enrich-compat-adapter.ts",
195
+ "dist/bundling-sources/shared_libs/plays/enrich-play-compiler.ts",
194
196
  "dist/bundling-sources/shared_libs/plays/file-refs.ts",
195
197
  "dist/bundling-sources/shared_libs/plays/input-contract-definition.ts",
196
198
  "dist/bundling-sources/shared_libs/plays/input-contract.ts",
@@ -202,6 +204,7 @@
202
204
  "dist/bundling-sources/shared_libs/plays/static-pipeline.ts",
203
205
  "dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts",
204
206
  "dist/bundling-sources/shared_libs/plays/tool-codegen.ts",
207
+ "dist/bundling-sources/shared_libs/plays/user-code-safety.ts",
205
208
  "dist/bundling-sources/shared_libs/product-notifications/contract.ts",
206
209
  "dist/bundling-sources/shared_libs/runtime-env.ts",
207
210
  "dist/bundling-sources/shared_libs/security/outbound-url-policy.ts",
@@ -65,6 +65,8 @@ type PlayLocalFileDiscoveryResult = {
65
65
  };
66
66
  type PlayBundlingAdapter = {
67
67
  projectRoot: string;
68
+ /** Optional root used only to make source-graph identity independent of temporary absolute paths. */
69
+ sourceIdentityRoot?: string;
68
70
  nodeModulesDir: string;
69
71
  cacheDir?: string;
70
72
  sdkSourceRoot: string;
@@ -65,6 +65,8 @@ type PlayLocalFileDiscoveryResult = {
65
65
  };
66
66
  type PlayBundlingAdapter = {
67
67
  projectRoot: string;
68
+ /** Optional root used only to make source-graph identity independent of temporary absolute paths. */
69
+ sourceIdentityRoot?: string;
68
70
  nodeModulesDir: string;
69
71
  cacheDir?: string;
70
72
  sdkSourceRoot: string;
@@ -21,7 +21,9 @@ import {
21
21
  extname,
22
22
  isAbsolute,
23
23
  join,
24
- resolve
24
+ relative,
25
+ resolve,
26
+ sep
25
27
  } from "path";
26
28
  import { builtinModules } from "module";
27
29
  import { Parser } from "acorn";
@@ -4060,6 +4062,15 @@ function assertValidExportName(exportName) {
4060
4062
  function sha256(value) {
4061
4063
  return createHash("sha256").update(value).digest("hex");
4062
4064
  }
4065
+ function sourceIdentityPath(filePath, adapter) {
4066
+ if (!adapter.sourceIdentityRoot) return filePath;
4067
+ const identityRoot = resolve(adapter.sourceIdentityRoot);
4068
+ const logicalPath = relative(identityRoot, resolve(filePath));
4069
+ if (!logicalPath || logicalPath === ".." || logicalPath.startsWith(`..${sep}`) || isAbsolute(logicalPath)) {
4070
+ return filePath;
4071
+ }
4072
+ return logicalPath.split(/[\\/]+/).join("/");
4073
+ }
4063
4074
  function formatEsbuildMessage(message) {
4064
4075
  const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
4065
4076
  return location ? `${location} ${message.text}` : message.text;
@@ -4980,12 +4991,15 @@ async function analyzeSourceGraph(entryFile, adapter, exportName) {
4980
4991
  const sourceHash = sha256(sourceCode);
4981
4992
  const graphHash = sha256(
4982
4993
  JSON.stringify({
4983
- entryFile: absoluteEntryFile,
4984
- localFiles: [...localFiles.entries()].map(([filePath, contents]) => ({ filePath, hash: sha256(contents) })).sort((left, right) => left.filePath.localeCompare(right.filePath)),
4994
+ entryFile: sourceIdentityPath(absoluteEntryFile, adapter),
4995
+ localFiles: [...localFiles.entries()].map(([filePath, contents]) => ({
4996
+ filePath: sourceIdentityPath(filePath, adapter),
4997
+ hash: sha256(contents)
4998
+ })).sort((left, right) => left.filePath.localeCompare(right.filePath)),
4985
4999
  nodeBuiltins: [...nodeBuiltins].sort(),
4986
5000
  packages: [...packages.entries()].map(([name, version]) => ({ name, version })).sort((left, right) => left.name.localeCompare(right.name)),
4987
5001
  importedPlayDependencies: [...importedPlayDependencies.values()].map((dependency) => ({
4988
- filePath: dependency.filePath,
5002
+ filePath: sourceIdentityPath(dependency.filePath, adapter),
4989
5003
  playName: dependency.playName
4990
5004
  })).sort((left, right) => left.filePath.localeCompare(right.filePath))
4991
5005
  })
@@ -5024,6 +5038,7 @@ async function analyzeSourceGraph(entryFile, adapter, exportName) {
5024
5038
  }
5025
5039
  function artifactCachePath(graphHash, artifactKind, adapter) {
5026
5040
  return join(
5041
+ /* turbopackIgnore: true */
5027
5042
  adapter.cacheDir ?? PLAY_ARTIFACT_CACHE_DIR,
5028
5043
  `${graphHash}.${artifactKind}.json`
5029
5044
  );
@@ -5031,7 +5046,12 @@ function artifactCachePath(graphHash, artifactKind, adapter) {
5031
5046
  async function readArtifactCache(graphHash, artifactKind, adapter) {
5032
5047
  try {
5033
5048
  const serialized = await readFile(
5034
- artifactCachePath(graphHash, artifactKind, adapter),
5049
+ /* turbopackIgnore: true */
5050
+ artifactCachePath(
5051
+ graphHash,
5052
+ artifactKind,
5053
+ adapter
5054
+ ),
5035
5055
  "utf-8"
5036
5056
  );
5037
5057
  return JSON.parse(serialized);
@@ -5041,8 +5061,13 @@ async function readArtifactCache(graphHash, artifactKind, adapter) {
5041
5061
  }
5042
5062
  async function writeArtifactCache(artifact, adapter) {
5043
5063
  const cacheDir = adapter.cacheDir ?? PLAY_ARTIFACT_CACHE_DIR;
5044
- await mkdir(cacheDir, { recursive: true });
5064
+ await mkdir(
5065
+ /* turbopackIgnore: true */
5066
+ cacheDir,
5067
+ { recursive: true }
5068
+ );
5045
5069
  await writeFile(
5070
+ /* turbopackIgnore: true */
5046
5071
  artifactCachePath(
5047
5072
  artifact.graphHash,
5048
5073
  artifact.artifactKind ?? PLAY_ARTIFACT_KINDS.cjsNode20,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {