deepline 0.1.243 → 0.1.244

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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +5 -25
  3. package/dist/bundling-sources/sdk/src/release.ts +23 -21
  4. package/dist/bundling-sources/sdk/src/types.ts +1 -1
  5. package/dist/bundling-sources/shared_libs/play-runtime/backend.ts +2 -21
  6. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -6
  7. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +6 -13
  8. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +9 -26
  9. package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +1 -2
  10. package/dist/bundling-sources/shared_libs/play-runtime/map-row-identity.ts +1 -1
  11. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +191 -29
  12. package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +1 -1
  13. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +1 -36
  14. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
  15. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +0 -7
  16. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +1 -12
  17. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +2 -3
  18. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +5 -15
  19. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +6 -21
  20. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +3 -4
  21. package/dist/bundling-sources/shared_libs/plays/artifact-kind-guard.ts +16 -29
  22. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -8
  23. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +21 -712
  24. package/dist/bundling-sources/shared_libs/plays/bundling/limits.ts +1 -18
  25. package/dist/cli/index.js +29 -71
  26. package/dist/cli/index.mjs +29 -71
  27. package/dist/{compiler-manifest-CYcwzSOJ.d.ts → compiler-manifest-DlE7dnRm.d.mts} +2 -8
  28. package/dist/{compiler-manifest-CYcwzSOJ.d.mts → compiler-manifest-DlE7dnRm.d.ts} +2 -8
  29. package/dist/index.d.mts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +24 -22
  32. package/dist/index.mjs +24 -22
  33. package/dist/plays/bundle-play-file.d.mts +7 -22
  34. package/dist/plays/bundle-play-file.d.ts +7 -22
  35. package/dist/plays/bundle-play-file.mjs +26 -614
  36. package/package.json +2 -2
  37. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +0 -4504
  38. package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +0 -1386
  39. package/dist/bundling-sources/apps/play-runner-workers/src/durable-object-deploy-handoff.ts +0 -30
  40. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +0 -8385
  41. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +0 -21
  42. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +0 -251
  43. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/coordinator-progress.ts +0 -289
  44. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/csv-rows.ts +0 -85
  45. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/customer-console.ts +0 -23
  46. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/dataset-handles.ts +0 -528
  47. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +0 -229
  48. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/ledger-event-batches.ts +0 -81
  49. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/live-progress.ts +0 -22
  50. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +0 -920
  51. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-latency-profile.ts +0 -90
  52. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/output-datasets.ts +0 -475
  53. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +0 -570
  54. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/result-dataset-persistence.ts +0 -50
  55. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/row-isolation.ts +0 -5
  56. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +0 -519
  57. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -107
  58. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +0 -2541
  59. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-http-errors.ts +0 -7
  60. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +0 -227
  61. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +0 -130
  62. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +0 -39
  63. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/workflow-preview.ts +0 -26
  64. package/dist/bundling-sources/apps/play-runner-workers/src/runtime-api-retry.ts +0 -23
  65. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +0 -47
  66. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +0 -167
  67. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +0 -91
  68. package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +0 -329
  69. package/dist/bundling-sources/sdk/src/worker-play-entry.ts +0 -240
  70. package/dist/bundling-sources/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +0 -252
  71. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -217
@@ -29,7 +29,7 @@ import type {
29
29
  } from '../artifact-types';
30
30
  import { buildPlayContractCompatibility } from '../contracts';
31
31
  import { validatePlaySourceFilesHaveNoInlineSecrets } from '../secret-guardrails';
32
- import { MAX_ESM_WORKERS_BUNDLE_BYTES, MAX_PLAY_BUNDLE_BYTES } from './limits';
32
+ import { MAX_PLAY_BUNDLE_BYTES } from './limits';
33
33
 
34
34
  const PLAY_BUNDLE_CACHE_VERSION = 25;
35
35
  const PLAY_ARTIFACT_CACHE_DIR = join(
@@ -48,7 +48,6 @@ const SOURCE_EXTENSIONS = [
48
48
  '.cjs',
49
49
  '.json',
50
50
  ];
51
- const WORKERS_PLAY_ENTRY_VIRTUAL = 'deepline-play-entry';
52
51
  const PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
53
52
  const NODE_BUILTIN_SET = new Set(
54
53
  builtinModules.flatMap((name) =>
@@ -96,10 +95,6 @@ export type PlayBundlingAdapter = {
96
95
  sdkPackageJson: string;
97
96
  sdkEntryFile: string;
98
97
  sdkTypesEntryFile?: string;
99
- sdkWorkersEntryFile: string;
100
- workersHarnessEntryFile: string;
101
- workersHarnessFilesDir: string;
102
- workersRuntimeFingerprintDirs?: string[];
103
98
  discoverPackagedLocalFiles(
104
99
  filePath: string,
105
100
  ): Promise<PlayLocalFileDiscoveryResult>;
@@ -1014,106 +1009,6 @@ function extractDefinedPlayMetadata(
1014
1009
  return null;
1015
1010
  }
1016
1011
 
1017
- function canonicalizeRootPlayNameForWorkersRuntimeHash(
1018
- sourceCode: string,
1019
- ): string {
1020
- const source = stripCommentsToSpaces(sourceCode);
1021
- const callPattern =
1022
- /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
1023
- const match = callPattern.exec(source);
1024
- if (!match) return sourceCode;
1025
-
1026
- const openParen = match.index + match[0].length - 1;
1027
- const firstArgStart = openParen + 1;
1028
- const firstNonSpace = source.slice(firstArgStart).search(/\S/);
1029
- if (firstNonSpace < 0) return sourceCode;
1030
-
1031
- const argIndex = firstArgStart + firstNonSpace;
1032
- const quote = source[argIndex];
1033
- if (quote === '"' || quote === "'") {
1034
- const literalMatch = source
1035
- .slice(argIndex)
1036
- .match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
1037
- if (!literalMatch) return sourceCode;
1038
- const replacement = `${quote}__deepline_runtime_play_name__${quote}`;
1039
- return `${sourceCode.slice(0, argIndex)}${replacement}${sourceCode.slice(argIndex + literalMatch[0].length)}`;
1040
- }
1041
-
1042
- if (quote !== '{') return sourceCode;
1043
- const closeBrace = findMatchingBrace(source, argIndex);
1044
- if (closeBrace < 0) return sourceCode;
1045
- const objectSource = source.slice(argIndex + 1, closeBrace);
1046
- const idMatch = objectSource.match(
1047
- /(^|[,{\s])((?:id|['"]id['"])\s*:\s*)(['"])([\s\S]*?)\3/,
1048
- );
1049
- if (!idMatch || idMatch.index === undefined) return sourceCode;
1050
- const idValueStart =
1051
- argIndex +
1052
- 1 +
1053
- idMatch.index +
1054
- idMatch[1]!.length +
1055
- idMatch[2]!.length +
1056
- idMatch[3]!.length;
1057
- return `${sourceCode.slice(0, idValueStart)}__deepline_runtime_play_name__${sourceCode.slice(idValueStart + idMatch[4]!.length)}`;
1058
- }
1059
-
1060
- function workersRuntimeGraphFilePath(input: {
1061
- entryFile: string;
1062
- filePath: string;
1063
- adapter: PlayBundlingAdapter;
1064
- }): string {
1065
- if (input.filePath === input.entryFile) return '<entry>';
1066
- const entryRelative = relative(dirname(input.entryFile), input.filePath);
1067
- if (entryRelative && !entryRelative.startsWith('..')) {
1068
- return entryRelative.replaceAll('\\', '/');
1069
- }
1070
- return `<project>/${relative(input.adapter.projectRoot, input.filePath).replaceAll('\\', '/')}`;
1071
- }
1072
-
1073
- function buildWorkersRuntimeGraphHash(input: {
1074
- analysis: SourceGraphAnalysis;
1075
- entryFile: string;
1076
- adapter: PlayBundlingAdapter;
1077
- exportName: string;
1078
- }): string {
1079
- const sourceFiles = Object.entries(input.analysis.sourceFiles)
1080
- .map(([filePath, contents]) => ({
1081
- filePath: workersRuntimeGraphFilePath({
1082
- entryFile: input.entryFile,
1083
- filePath,
1084
- adapter: input.adapter,
1085
- }),
1086
- hash: sha256(
1087
- filePath === input.entryFile
1088
- ? canonicalizeRootPlayNameForWorkersRuntimeHash(contents)
1089
- : contents,
1090
- ),
1091
- }))
1092
- .sort((left, right) => left.filePath.localeCompare(right.filePath));
1093
-
1094
- return sha256(
1095
- JSON.stringify({
1096
- entryFile: '<entry>',
1097
- entryExport: input.exportName,
1098
- localFiles: sourceFiles,
1099
- nodeBuiltins: [...input.analysis.importPolicy.nodeBuiltins].sort(),
1100
- packages: input.analysis.importPolicy.packages
1101
- .map(({ name, version }) => ({ name, version }))
1102
- .sort((left, right) => left.name.localeCompare(right.name)),
1103
- importedPlayDependencies: input.analysis.importedPlayDependencies
1104
- .map((dependency) => ({
1105
- filePath: workersRuntimeGraphFilePath({
1106
- entryFile: input.entryFile,
1107
- filePath: dependency.filePath,
1108
- adapter: input.adapter,
1109
- }),
1110
- playName: dependency.playName,
1111
- }))
1112
- .sort((left, right) => left.filePath.localeCompare(right.filePath)),
1113
- }),
1114
- );
1115
- }
1116
-
1117
1012
  function readPackageVersionFromPackageJson(
1118
1013
  packageJsonPath: string,
1119
1014
  packageName: string,
@@ -1193,13 +1088,8 @@ function findPackageJsonPath(
1193
1088
  : null;
1194
1089
  }
1195
1090
 
1196
- function localSdkAliasPlugin(
1197
- adapter: PlayBundlingAdapter,
1198
- options?: { workersRuntime?: boolean },
1199
- ): Plugin | null {
1200
- const entryFile = options?.workersRuntime
1201
- ? adapter.sdkWorkersEntryFile
1202
- : adapter.sdkEntryFile;
1091
+ function localSdkAliasPlugin(adapter: PlayBundlingAdapter): Plugin | null {
1092
+ const entryFile = adapter.sdkEntryFile;
1203
1093
  if (!existsSync(entryFile)) {
1204
1094
  return null;
1205
1095
  }
@@ -1217,308 +1107,6 @@ function localSdkAliasPlugin(
1217
1107
  };
1218
1108
  }
1219
1109
 
1220
- /**
1221
- * Resolves the virtual `deepline-play-entry` import (used by the Workers
1222
- * harness) to the user's play source file. Lets the harness statically import
1223
- * the play without the bundler needing to know its path ahead of time.
1224
- */
1225
- function workersPlayEntryAliasPlugin(playFilePath: string): Plugin {
1226
- return {
1227
- name: 'deepline-workers-play-entry-alias',
1228
- setup(buildContext) {
1229
- buildContext.onResolve(
1230
- { filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
1231
- () => ({ path: playFilePath }),
1232
- );
1233
- },
1234
- };
1235
- }
1236
-
1237
- const CUSTOMER_CONSOLE_CAPTURE_GLOBAL = '__deeplineCaptureCustomerConsole';
1238
-
1239
- function sourceLoaderForPath(path: string): 'ts' | 'tsx' | 'js' | 'jsx' {
1240
- const extension = extname(path).toLowerCase();
1241
- if (extension === '.tsx') return 'tsx';
1242
- if (extension === '.jsx') return 'jsx';
1243
- if (extension === '.js' || extension === '.mjs' || extension === '.cjs') {
1244
- return 'js';
1245
- }
1246
- return 'ts';
1247
- }
1248
-
1249
- /**
1250
- * Give the customer's entry module a lexical console without replacing the
1251
- * Worker's process-wide console. Runtime diagnostics therefore keep flowing
1252
- * to Cloudflare while console.* calls authored in the play join its Run Log
1253
- * Stream. The lazy global lookup is required because ESM dependencies are
1254
- * evaluated before the harness installs the request-scoped capture function.
1255
- */
1256
- function workersCustomerConsolePlugin(
1257
- customerSourceFilePaths: readonly string[],
1258
- ): Plugin {
1259
- const customerSourceFiles = new Set(
1260
- customerSourceFilePaths
1261
- .filter((path) => extname(path).toLowerCase() !== '.json')
1262
- .map((path) => resolve(path)),
1263
- );
1264
- return {
1265
- name: 'deepline-workers-customer-console',
1266
- setup(buildContext) {
1267
- buildContext.onLoad({ filter: /./ }, (args) => {
1268
- if (!customerSourceFiles.has(resolve(args.path))) return undefined;
1269
- const source = readFileSync(args.path, 'utf8');
1270
- const declaresOwnConsole =
1271
- /(?:^|\n)\s*(?:(?:const|let|var|class|function)\s+console\b|import[^\n]*\bconsole\b)/m.test(
1272
- source,
1273
- );
1274
- if (declaresOwnConsole) {
1275
- return {
1276
- contents: source,
1277
- loader: sourceLoaderForPath(args.path),
1278
- resolveDir: dirname(args.path),
1279
- };
1280
- }
1281
- const prelude = `
1282
- const console = new Proxy(globalThis.console, {
1283
- get(target, property) {
1284
- const capture = globalThis.${CUSTOMER_CONSOLE_CAPTURE_GLOBAL};
1285
- if (typeof capture === 'function') {
1286
- return (...args) => capture(String(property), args);
1287
- }
1288
- const value = Reflect.get(target, property);
1289
- return typeof value === 'function' ? value.bind(target) : value;
1290
- },
1291
- });
1292
- `;
1293
- return {
1294
- contents: `${prelude}\n${source}`,
1295
- loader: sourceLoaderForPath(args.path),
1296
- resolveDir: dirname(args.path),
1297
- };
1298
- });
1299
- },
1300
- };
1301
- }
1302
-
1303
- function workersNamedPlayEntryAliasPlugin(
1304
- playFilePath: string,
1305
- exportName: string,
1306
- ): Plugin {
1307
- return {
1308
- name: 'deepline-workers-named-play-entry-alias',
1309
- setup(buildContext) {
1310
- buildContext.onResolve(
1311
- { filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
1312
- () => ({
1313
- path: `${playFilePath}.${exportName}.entry.ts`,
1314
- namespace: 'deepline-named-play-entry',
1315
- }),
1316
- );
1317
- buildContext.onLoad(
1318
- { filter: /.*/, namespace: 'deepline-named-play-entry' },
1319
- () => ({
1320
- contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};\n`,
1321
- loader: 'ts',
1322
- resolveDir: dirname(playFilePath),
1323
- }),
1324
- );
1325
- },
1326
- };
1327
- }
1328
-
1329
- /**
1330
- * Cloudflare Workers' `nodejs_compat` flag covers most node builtins (path,
1331
- * crypto, buffer, async_hooks, ...) but NOT `node:fs` / `node:fs/promises` /
1332
- * `node:os` (no filesystem, no OS in a V8 isolate). The SDK's config-loading
1333
- * and tool-output utilities reference these even when the play never invokes
1334
- * them — which is the case for plays that don't read CLI config at runtime.
1335
- *
1336
- * This plugin substitutes those specific imports with a tiny stub module that
1337
- * has no top-level side effects and throws clearly if anything actually calls
1338
- * into them. That lets the deploy validation pass while still surfacing a
1339
- * loud error if a play unexpectedly depends on a node-only API.
1340
- */
1341
- function workersNodeBuiltinStubPlugin(): Plugin {
1342
- // These are the node builtins NOT exposed by Cloudflare's nodejs_compat.
1343
- // Keep this list narrow — anything supported (path, crypto, etc.) should
1344
- // pass through as `external: ['node:*']` in the build config.
1345
- const UNSUPPORTED = new Set([
1346
- 'node:fs',
1347
- 'node:fs/promises',
1348
- 'node:os',
1349
- 'node:child_process',
1350
- ]);
1351
- return {
1352
- name: 'deepline-workers-node-builtin-stub',
1353
- setup(buildContext) {
1354
- buildContext.onResolve({ filter: /^node:/ }, (args) => {
1355
- if (!UNSUPPORTED.has(args.path)) return null;
1356
- return { path: args.path, namespace: 'deepline-workers-node-stub' };
1357
- });
1358
- buildContext.onLoad(
1359
- { filter: /.*/, namespace: 'deepline-workers-node-stub' },
1360
- (args) => {
1361
- const builtinName = args.path;
1362
- // CommonJS module.exports = Proxy. esbuild's CJS↔ESM interop maps
1363
- // any named import (e.g. `import { readFileSync } from 'node:fs'`)
1364
- // to a property access on this Proxy. Property access returns a
1365
- // function-like Proxy that throws on call. So:
1366
- // - import resolves at bundle time (deploy validation passes)
1367
- // - import binding evaluates to a no-arg-throw function
1368
- // - calling it gives a clear error
1369
- const stubSource = `
1370
- const message = ${JSON.stringify(
1371
- `Workers backend: ${builtinName} is not available in Cloudflare Workers ` +
1372
- '(no filesystem / no OS). Run this play on Daytona or local-process backend ' +
1373
- 'if it needs node builtins.',
1374
- )};
1375
- function makeThrower(name) {
1376
- return new Proxy(function() { throw new Error(message + ' (called: ' + name + ')'); }, {
1377
- get(_t, prop) {
1378
- if (prop === '__esModule') return true;
1379
- if (typeof prop === 'symbol') return undefined;
1380
- return makeThrower(name + '.' + String(prop));
1381
- },
1382
- apply() { throw new Error(message + ' (called: ' + name + ')'); },
1383
- construct() { throw new Error(message + ' (constructed: ' + name + ')'); },
1384
- });
1385
- }
1386
- module.exports = makeThrower(${JSON.stringify(builtinName)});
1387
- `;
1388
- return { contents: stubSource, loader: 'js' };
1389
- },
1390
- );
1391
- },
1392
- };
1393
- }
1394
-
1395
- /**
1396
- * Stub every `zod/v4/locales/<lang>.js` module EXCEPT English with an
1397
- * empty default export so non-English locale strings don't get bundled
1398
- * into per-play Workers.
1399
- *
1400
- * Why this exists:
1401
- * `zod/v4/core/index.js` does `export * as locales from "../locales/index.js"`
1402
- * and `zod/v4/locales/index.js` re-exports every individual locale
1403
- * module (he, ru, ta, th, be, km, ka, uk, hy, bg, ur, ar, mk, fa, ps,
1404
- * lt, …). esbuild follows the static re-exports and bundles every
1405
- * locale, ~5–9 KB each. A bundle audit (`scripts/audit-play-bundle.ts`)
1406
- * on `03-tool-basic.play.ts` showed ~22 of these locale modules
1407
- * present, totaling ~150–200 KB of dead weight.
1408
- *
1409
- * Plays only ever surface English error messages to user/CLI. The
1410
- * non-English locale data is unreachable in our codepath, but
1411
- * esbuild's tree-shaking can't prove that across `export * as`
1412
- * re-exports. So we stub each non-English locale at load time with
1413
- * an empty default export. zod's runtime falls back to its built-in
1414
- * English messages when a requested locale's data is missing or
1415
- * empty.
1416
- *
1417
- * What this saves (measured, fresh per-play bundle):
1418
- * ~150–200 KB → cold V8 compile time drops proportionally
1419
- * (~50–70 ms on CF edge per cold play).
1420
- *
1421
- * Safety:
1422
- * - English (`en.js`) is NEVER stubbed — that's the language zod
1423
- * and our error surfaces actually use.
1424
- * - The locales/index.js wrapper is left intact; its re-exports
1425
- * resolve to empty `{}` modules but the import shape stays
1426
- * stable for any code that does `import { locales } from "zod"`.
1427
- * - If a future codepath needs a non-English locale, this plugin
1428
- * would silently swallow the data; that would be a real bug, so
1429
- * we log a one-time warning at bundle time when a non-English
1430
- * locale is stubbed (only emitted to stderr in dev to keep CI
1431
- * output clean).
1432
- */
1433
- function zodNonEnglishLocaleStubPlugin(): Plugin {
1434
- // Match any zod v4 locale module path EXCEPT en + en variants.
1435
- // The filter uses a coarse regex; the namespace handler then
1436
- // double-checks with a precise basename test before stubbing.
1437
- const LOCALE_PATH_FILTER =
1438
- /[\\/]zod[\\/]v4[\\/]locales[\\/][^\\/]+\.(?:c?js|mjs)$/;
1439
- const NAMESPACE = 'deepline-zod-locale-stub';
1440
- return {
1441
- name: 'deepline-zod-non-english-locale-stub',
1442
- setup(buildContext) {
1443
- buildContext.onResolve({ filter: LOCALE_PATH_FILTER }, (args) => {
1444
- // Resolve relatively first (esbuild needs an absolute path), then
1445
- // decide whether to stub. We do this in onResolve so we can check
1446
- // the filename and let `en` flow through unmodified.
1447
- const norm = args.path.replace(/\\/g, '/');
1448
- const file = norm.slice(norm.lastIndexOf('/') + 1);
1449
- // Allow English variants — `en.js`, `en.cjs`, future `en-US.js`, etc.
1450
- if (/^en(?:[-_][A-Za-z]+)?\.(?:c?js|mjs)$/.test(file)) {
1451
- return null;
1452
- }
1453
- // Anything else: stub with an empty default export.
1454
- return { path: args.path, namespace: NAMESPACE };
1455
- });
1456
- buildContext.onLoad({ filter: /.*/, namespace: NAMESPACE }, () => ({
1457
- // zod locales export a default object literal. Empty object is
1458
- // structurally compatible — accessing any locale key returns
1459
- // undefined and zod falls back to its built-in English.
1460
- contents: 'export default {};',
1461
- loader: 'js',
1462
- }));
1463
- },
1464
- };
1465
- }
1466
-
1467
- /**
1468
- * Hard banlist of `node:*` modules whose presence in a workers_edge bundle
1469
- * would either escape the V8 isolate boundary (filesystem, network sockets,
1470
- * subprocess control) or open up confused-deputy paths (vm/repl/worker
1471
- * threads). These are blocked at bundle time so the per-graphHash CF Worker
1472
- * never sees code that could attempt to call them.
1473
- *
1474
- * `nodejs_compat` covers the supported subset (crypto, stream, buffer, util,
1475
- * path, url, http, https) — those imports continue to pass through and
1476
- * resolve to workerd-shimmed implementations.
1477
- */
1478
- const WORKERS_BANNED_NODE_MODULES = new Set([
1479
- 'node:fs',
1480
- 'node:fs/promises',
1481
- 'node:net',
1482
- 'node:child_process',
1483
- 'node:cluster',
1484
- 'node:tls',
1485
- 'node:dgram',
1486
- 'node:dns',
1487
- 'node:dns/promises',
1488
- 'node:repl',
1489
- 'node:vm',
1490
- 'node:worker_threads',
1491
- ]);
1492
-
1493
- function workersNodeImportBanlistPlugin(adapter: PlayBundlingAdapter): Plugin {
1494
- return {
1495
- name: 'deepline-workers-node-import-banlist',
1496
- setup(buildContext) {
1497
- buildContext.onResolve({ filter: /^node:/ }, (args) => {
1498
- if (!WORKERS_BANNED_NODE_MODULES.has(args.path)) return null;
1499
- // SDK internals (sdk/src/*.ts) intentionally reference a small set
1500
- // of node builtins (e.g. config loaders, tool-output writers) that
1501
- // never run in the V8 isolate path; they're substituted by the
1502
- // workersNodeBuiltinStubPlugin's throwing stub. Only reject when
1503
- // the import comes from outside the SDK source tree -- i.e. user
1504
- // play code or third-party deps that the play pulls in.
1505
- const importer = args.importer ?? '';
1506
- if (importer.startsWith(adapter.sdkSourceRoot)) {
1507
- return null;
1508
- }
1509
- return {
1510
- errors: [
1511
- {
1512
- text: `Module "${args.path}" is not allowed in workers_edge plays. See AGENTS.md for the V8 isolate boundary.`,
1513
- detail: { importer: args.importer, kind: args.kind },
1514
- },
1515
- ],
1516
- };
1517
- });
1518
- },
1519
- };
1520
- }
1521
-
1522
1110
  function buildImportedPlayProxyModule(playName: string): string {
1523
1111
  const serializedName = JSON.stringify(playName);
1524
1112
  return `
@@ -1842,99 +1430,11 @@ async function analyzeSourceGraph(
1842
1430
  };
1843
1431
  }
1844
1432
 
1845
- /**
1846
- * Fingerprint of Worker runtime sources bundled into every esm_workers play
1847
- * artifact. Harness, shared runtime, or provider-owned batching edits must
1848
- * invalidate the bundle cache and force a fresh CF deploy. Computed fresh on
1849
- * every bundle call so dev edits are picked up on the next `play run` without
1850
- * restarting the dev server. No caching: that's deliberate — caching the
1851
- * fingerprint is exactly the stale-state bug this exists to prevent.
1852
- */
1853
- async function computeWorkersHarnessFingerprintWithAdapter(
1854
- adapter: PlayBundlingAdapter,
1855
- ): Promise<string> {
1856
- const { readdir } = await import('node:fs/promises');
1857
- const addFilePart = async (
1858
- parts: Array<{ name: string; hash: string }>,
1859
- rootDir: string,
1860
- filePath: string,
1861
- ) => {
1862
- const contents = await readFile(filePath, 'utf-8');
1863
- parts.push({
1864
- name: `${basename(rootDir)}:${filePath.slice(rootDir.length + 1)}`,
1865
- hash: sha256(contents),
1866
- });
1867
- };
1868
- const collectTsFilesRecursive = async (
1869
- rootDir: string,
1870
- parts: Array<{ name: string; hash: string }>,
1871
- ) => {
1872
- if (!(await fileExists(rootDir))) return;
1873
- const filePaths: string[] = [];
1874
- const visitDir = async (dir: string) => {
1875
- const entries = await readdir(dir, { withFileTypes: true });
1876
- for (const entry of entries) {
1877
- const childPath = join(dir, entry.name);
1878
- if (entry.isDirectory()) {
1879
- await visitDir(childPath);
1880
- continue;
1881
- }
1882
- if (entry.isFile() && /\.[cm]?ts$/.test(entry.name)) {
1883
- filePaths.push(childPath);
1884
- }
1885
- }
1886
- };
1887
- await visitDir(rootDir);
1888
- for (const filePath of filePaths.sort()) {
1889
- await addFilePart(parts, rootDir, filePath);
1890
- }
1891
- };
1892
- const collectIntegrationBatchingFiles = async (
1893
- rootDir: string,
1894
- parts: Array<{ name: string; hash: string }>,
1895
- ) => {
1896
- if (!(await fileExists(rootDir))) return;
1897
- const entries = await readdir(rootDir, { withFileTypes: true });
1898
- const filePaths: string[] = [];
1899
- for (const entry of entries) {
1900
- if (
1901
- entry.isFile() &&
1902
- (entry.name === 'play-runtime-batching-registry.ts' ||
1903
- /^batching.*\.ts$/.test(entry.name))
1904
- ) {
1905
- filePaths.push(join(rootDir, entry.name));
1906
- }
1907
- if (entry.isDirectory()) {
1908
- const batchingFile = join(rootDir, entry.name, 'batching.ts');
1909
- if (await fileExists(batchingFile)) {
1910
- filePaths.push(batchingFile);
1911
- }
1912
- }
1913
- }
1914
- for (const filePath of filePaths.sort()) {
1915
- await addFilePart(parts, rootDir, filePath);
1916
- }
1917
- };
1918
-
1919
- const parts: Array<{ name: string; hash: string }> = [];
1920
- await collectTsFilesRecursive(adapter.workersHarnessFilesDir, parts);
1921
- for (const dir of adapter.workersRuntimeFingerprintDirs ?? []) {
1922
- if (basename(dir) === 'integrations') {
1923
- await collectIntegrationBatchingFiles(dir, parts);
1924
- } else {
1925
- await collectTsFilesRecursive(dir, parts);
1926
- }
1927
- }
1928
- return sha256(JSON.stringify(parts));
1929
- }
1930
-
1931
1433
  function artifactCachePath(
1932
1434
  graphHash: string,
1933
1435
  artifactKind: PlayArtifactKind,
1934
1436
  adapter: PlayBundlingAdapter,
1935
1437
  ): string {
1936
- // Cache key includes artifactKind so a play can have both cjs_node20 and
1937
- // esm_workers builds cached side-by-side without one stomping on the other.
1938
1438
  return join(
1939
1439
  adapter.cacheDir ?? PLAY_ARTIFACT_CACHE_DIR,
1940
1440
  `${graphHash}.${artifactKind}.json`,
@@ -2003,25 +1503,11 @@ function normalizeSourceMapForRuntime(
2003
1503
  export function getBundleSizeErrorForBytes(
2004
1504
  filePath: string,
2005
1505
  bundleBytes: number,
2006
- artifactKind: PlayArtifactKind,
1506
+ _artifactKind: PlayArtifactKind,
2007
1507
  ): string | null {
2008
1508
  if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
2009
1509
  return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
2010
1510
  }
2011
- if (
2012
- artifactKind === PLAY_ARTIFACT_KINDS.esmWorkers &&
2013
- bundleBytes > MAX_ESM_WORKERS_BUNDLE_BYTES
2014
- ) {
2015
- const mib = (bundleBytes / 1024 / 1024).toFixed(2);
2016
- const limitMib = (MAX_ESM_WORKERS_BUNDLE_BYTES / 1024 / 1024).toFixed(2);
2017
- return (
2018
- `${filePath} Cloudflare Workers bundle is ${mib} MiB, above the workerd ` +
2019
- `local-mode threshold of ${limitMib} MiB. Bundles past this size silently ` +
2020
- `hang without executing the workflow body. Reduce dependencies (top ` +
2021
- `offenders are usually date-fns, lodash, large schema libs) or split the ` +
2022
- `play into smaller pieces with ctx.runPlay.`
2023
- );
2024
- }
2025
1511
  return null;
2026
1512
  }
2027
1513
 
@@ -2038,14 +1524,7 @@ function getBundleSizeError(
2038
1524
  }
2039
1525
 
2040
1526
  export type BundlePlayFileOptions = {
2041
- /**
2042
- * Which artifact to produce. Defaults to `cjs_node20` (Daytona / local-process
2043
- * runner). Pass `esm_workers` to produce a Cloudflare Worker bundle that
2044
- * combines the play with the Workers harness.
2045
- *
2046
- * The CLI selects this from the execution profile or an explicit flag. Each
2047
- * kind is cached independently on disk.
2048
- */
1527
+ /** Must be `cjs_node20`; retained as an explicit compile-contract assertion. */
2049
1528
  target?: PlayArtifactKind;
2050
1529
  exportName?: string;
2051
1530
  };
@@ -2057,20 +1536,7 @@ export type BundlePlayFileCoreOptions = BundlePlayFileOptions & {
2057
1536
  type EsbuildBundleOutput = {
2058
1537
  bundledCode: string;
2059
1538
  sourceMapText: string;
2060
- outputExtension: 'cjs' | 'mjs';
2061
- };
2062
-
2063
- type PlayArtifactTargetAdapter = {
2064
- artifactKind: PlayArtifactKind;
2065
- codeFormat: PlayBundleArtifact['codeFormat'];
2066
- includeWorkersHarnessInGraphHash: boolean;
2067
- runEsbuild(input: {
2068
- entryFile: string;
2069
- customerSourceFilePaths: string[];
2070
- importedPlayDependencies: ImportedPlayDependency[];
2071
- adapter: PlayBundlingAdapter;
2072
- exportName: string;
2073
- }): Promise<EsbuildBundleOutput | string[]>;
1539
+ outputExtension: 'cjs';
2074
1540
  };
2075
1541
 
2076
1542
  async function runEsbuildForCjsNode(
@@ -2125,141 +1591,6 @@ async function runEsbuildForCjsNode(
2125
1591
  };
2126
1592
  }
2127
1593
 
2128
- async function runEsbuildForEsmWorkers(
2129
- playEntryFile: string,
2130
- customerSourceFilePaths: string[],
2131
- importedPlayDependencies: ImportedPlayDependency[],
2132
- adapter: PlayBundlingAdapter,
2133
- exportName: string,
2134
- ): Promise<EsbuildBundleOutput | string[]> {
2135
- const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
2136
- const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
2137
- const playEntryAlias =
2138
- exportName === 'default'
2139
- ? workersPlayEntryAliasPlugin(playEntryFile)
2140
- : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
2141
- const customerConsolePlugin = workersCustomerConsolePlugin(
2142
- customerSourceFilePaths,
2143
- );
2144
- const result = await build({
2145
- // Entry is the Workers harness; it imports the play via the virtual
2146
- // `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
2147
- entryPoints: [adapter.workersHarnessEntryFile],
2148
- absWorkingDir: adapter.projectRoot,
2149
- bundle: true,
2150
- format: 'esm',
2151
- nodePaths: [adapter.nodeModulesDir],
2152
- // Browser platform with workerd + worker conditions matches Cloudflare's
2153
- // V8-isolate runtime. Avoids accidentally pulling node-only branches of
2154
- // dual-publish packages.
2155
- platform: 'browser',
2156
- conditions: ['workerd', 'worker', 'browser', 'import', 'default'],
2157
- mainFields: ['module', 'main'],
2158
- target: ['es2022'],
2159
- outfile: 'play-worker.mjs',
2160
- write: false,
2161
- sourcemap: 'external',
2162
- sourcesContent: false,
2163
- logLevel: 'silent',
2164
- legalComments: 'none',
2165
- // Aggressive minify + treeshake: a tiny play (<1KB source) was producing
2166
- // an ~870KB bundle, dominated by zod's locale files (~260KB unused) and
2167
- // dead-code branches across `shared_libs/play-runtime/*` and the SDK.
2168
- // Both DCE on with these flags. workerd compiles each unique-graphHash
2169
- // bundle on first dispatch, so bundle bytes translate ~linearly into
2170
- // per-play cold latency on workers_edge. External sourcemap remains
2171
- // unchanged (`sourcemap: 'external'`), so debugging is unaffected.
2172
- minify: true,
2173
- treeShaking: true,
2174
- // Most node:* builtins are provided by Cloudflare's `nodejs_compat` flag.
2175
- // The unsupported subset (fs, fs/promises, os, child_process) gets
2176
- // replaced with throwing stubs by workersNodeBuiltinStubPlugin so deploy
2177
- // validation passes; anything supported (path, crypto, buffer, etc.) is
2178
- // marked external and resolved at runtime by the Workers runtime.
2179
- external: ['node:*', 'cloudflare:workers'],
2180
- plugins: [
2181
- // Banlist runs first so a forbidden import errors out before any other
2182
- // resolver (esp. the workersNodeBuiltinStubPlugin which would silently
2183
- // turn it into a throwing-stub).
2184
- workersNodeImportBanlistPlugin(adapter),
2185
- sdkAliasPlugin,
2186
- playProxyPlugin,
2187
- playEntryAlias,
2188
- customerConsolePlugin,
2189
- workersNodeBuiltinStubPlugin(),
2190
- // Strip non-English zod locale data from the bundle. zod's locales
2191
- // are re-exported as a static namespace from `zod/v4/core`, so
2192
- // tree-shaking can't drop them; this plugin replaces each
2193
- // non-English locale module with an empty default export at bundle
2194
- // time. ~150–200 KB savings on every per-play bundle, with no
2195
- // behavior change (we only surface English messages anyway).
2196
- // If the bundle suddenly grows back, check whether a new dep added
2197
- // its own zod copy or whether zod's locale layout changed.
2198
- zodNonEnglishLocaleStubPlugin(),
2199
- ].filter((plugin): plugin is Plugin => plugin != null),
2200
- });
2201
- const codeFile = result.outputFiles?.find((f) => f.path.endsWith('.mjs'));
2202
- const mapFile = result.outputFiles?.find((f) => f.path.endsWith('.mjs.map'));
2203
- if (!codeFile?.text || !mapFile?.text) {
2204
- return ['Workers play bundling produced incomplete output.'];
2205
- }
2206
- return {
2207
- bundledCode: codeFile.text,
2208
- sourceMapText: mapFile.text,
2209
- outputExtension: 'mjs',
2210
- };
2211
- }
2212
-
2213
- const PLAY_ARTIFACT_TARGET_ADAPTERS: Record<
2214
- PlayArtifactKind,
2215
- PlayArtifactTargetAdapter
2216
- > = {
2217
- [PLAY_ARTIFACT_KINDS.cjsNode20]: {
2218
- artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
2219
- codeFormat: 'cjs_module',
2220
- includeWorkersHarnessInGraphHash: false,
2221
- runEsbuild: ({
2222
- entryFile,
2223
- customerSourceFilePaths,
2224
- importedPlayDependencies,
2225
- adapter,
2226
- exportName,
2227
- }) =>
2228
- runEsbuildForCjsNode(
2229
- entryFile,
2230
- customerSourceFilePaths,
2231
- importedPlayDependencies,
2232
- adapter,
2233
- exportName,
2234
- ),
2235
- },
2236
- [PLAY_ARTIFACT_KINDS.esmWorkers]: {
2237
- artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
2238
- codeFormat: 'esm_module',
2239
- includeWorkersHarnessInGraphHash: true,
2240
- runEsbuild: ({
2241
- entryFile,
2242
- customerSourceFilePaths,
2243
- importedPlayDependencies,
2244
- adapter,
2245
- exportName,
2246
- }) =>
2247
- runEsbuildForEsmWorkers(
2248
- entryFile,
2249
- customerSourceFilePaths,
2250
- importedPlayDependencies,
2251
- adapter,
2252
- exportName,
2253
- ),
2254
- },
2255
- };
2256
-
2257
- function resolvePlayArtifactTargetAdapter(
2258
- artifactKind: PlayArtifactKind,
2259
- ): PlayArtifactTargetAdapter {
2260
- return PLAY_ARTIFACT_TARGET_ADAPTERS[artifactKind];
2261
- }
2262
-
2263
1594
  export async function bundlePlayFile(
2264
1595
  filePath: string,
2265
1596
  options: BundlePlayFileCoreOptions,
@@ -2267,7 +1598,6 @@ export async function bundlePlayFile(
2267
1598
  const adapter = options.adapter;
2268
1599
  const target: PlayArtifactKind =
2269
1600
  options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
2270
- const targetAdapter = resolvePlayArtifactTargetAdapter(target);
2271
1601
  const exportName = options.exportName?.trim() || 'default';
2272
1602
  assertValidExportName(exportName);
2273
1603
  const absolutePath = await normalizeLocalPath(filePath);
@@ -2279,29 +1609,9 @@ export async function bundlePlayFile(
2279
1609
  adapter,
2280
1610
  exportName,
2281
1611
  );
2282
- analysis.graphHash =
2283
- target === PLAY_ARTIFACT_KINDS.esmWorkers
2284
- ? buildWorkersRuntimeGraphHash({
2285
- analysis,
2286
- entryFile: absolutePath,
2287
- adapter,
2288
- exportName,
2289
- })
2290
- : sha256(`${analysis.graphHash}\nentry-export:${exportName}`);
2291
- // For esm_workers builds, the harness source files (entry.ts +
2292
- // peer DO/coordinator types it imports) are bundled INTO every play
2293
- // artifact. So any harness edit must produce a different graphHash so
2294
- // (a) the bundle cache misses, (b) the per-graphHash CF Worker name
2295
- // changes and gets a fresh deploy. This is the file-watcher-equivalent
2296
- // for hot-reload of the harness: editing entry.ts → next play run
2297
- // re-bundles + redeploys automatically.
2298
- if (targetAdapter.includeWorkersHarnessInGraphHash) {
2299
- const harnessFingerprint =
2300
- await computeWorkersHarnessFingerprintWithAdapter(adapter);
2301
- analysis.graphHash = sha256(
2302
- `${analysis.graphHash}\nworkers-harness:${harnessFingerprint}`,
2303
- );
2304
- }
1612
+ analysis.graphHash = sha256(
1613
+ `${analysis.graphHash}\nentry-export:${exportName}`,
1614
+ );
2305
1615
  try {
2306
1616
  validatePlaySourceFilesHaveNoInlineSecrets(analysis.sourceFiles);
2307
1617
  } catch (error) {
@@ -2334,10 +1644,11 @@ export async function bundlePlayFile(
2334
1644
  // Cache lookup happens after validation because a bundle cache hit is keyed
2335
1645
  // by source and target, while cloud descriptor typecheck results also depend
2336
1646
  // on generated tool metadata.
2337
- const canUseArtifactCache = target !== PLAY_ARTIFACT_KINDS.esmWorkers;
2338
- const cachedArtifact = canUseArtifactCache
2339
- ? await readArtifactCache(analysis.graphHash, target, adapter)
2340
- : null;
1647
+ const cachedArtifact = await readArtifactCache(
1648
+ analysis.graphHash,
1649
+ target,
1650
+ adapter,
1651
+ );
2341
1652
  const discoveredFiles =
2342
1653
  await adapter.discoverPackagedLocalFiles(absolutePath);
2343
1654
  if (cachedArtifact) {
@@ -2374,13 +1685,13 @@ export async function bundlePlayFile(
2374
1685
  };
2375
1686
  }
2376
1687
 
2377
- const buildOutcome = await targetAdapter.runEsbuild({
2378
- entryFile: absolutePath,
2379
- customerSourceFilePaths: analysis.importPolicy.localFiles,
2380
- importedPlayDependencies: analysis.importedPlayDependencies,
1688
+ const buildOutcome = await runEsbuildForCjsNode(
1689
+ absolutePath,
1690
+ analysis.importPolicy.localFiles,
1691
+ analysis.importedPlayDependencies,
2381
1692
  adapter,
2382
1693
  exportName,
2383
- });
1694
+ );
2384
1695
  if (Array.isArray(buildOutcome)) {
2385
1696
  return {
2386
1697
  success: false,
@@ -2414,7 +1725,7 @@ export async function bundlePlayFile(
2414
1725
  }
2415
1726
 
2416
1727
  const artifact: PlayBundleArtifact = {
2417
- codeFormat: targetAdapter.codeFormat,
1728
+ codeFormat: 'cjs_module',
2418
1729
  artifactKind: target,
2419
1730
  entryFile: absolutePath,
2420
1731
  virtualFilename,
@@ -2430,9 +1741,7 @@ export async function bundlePlayFile(
2430
1741
  cacheHit: false,
2431
1742
  };
2432
1743
 
2433
- if (canUseArtifactCache) {
2434
- await writeArtifactCache(artifact, adapter);
2435
- }
1744
+ await writeArtifactCache(artifact, adapter);
2436
1745
 
2437
1746
  return {
2438
1747
  success: true,