@workflow/next 5.0.0-beta.2 → 5.0.0-beta.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"builder-deferred.d.ts","sourceRoot":"","sources":["../src/builder-deferred.ts"],"names":[],"mappings":"AAkDA,wBAAsB,sBAAsB,iBAosE3C"}
1
+ {"version":3,"file":"builder-deferred.d.ts","sourceRoot":"","sources":["../src/builder-deferred.ts"],"names":[],"mappings":"AA6CA,wBAAsB,sBAAsB,iBAo6D3C"}
@@ -9,9 +9,8 @@ const node_fs_1 = require("node:fs");
9
9
  const promises_1 = require("node:fs/promises");
10
10
  const node_os_1 = __importDefault(require("node:os"));
11
11
  const node_path_1 = require("node:path");
12
- const enhanced_resolve_1 = __importDefault(require("enhanced-resolve"));
13
12
  const socket_server_js_1 = require("./socket-server.js");
14
- const step_copy_utils_js_1 = require("./step-copy-utils.js");
13
+ const DEFERRED_STEP_COPY_DIR_NAME = '__workflow_step_files__';
15
14
  const ROUTE_STUB_FILE_MARKER = 'WORKFLOW_ROUTE_STUB_FILE';
16
15
  const ROUTE_STUB_MARKER_SCAN_BYTES = 4 * 1024;
17
16
  let CachedNextBuilderDeferred;
@@ -25,43 +24,6 @@ async function getNextBuilderDeferred() {
25
24
  const { BaseBuilder: BaseBuilderClass, STEP_QUEUE_TRIGGER, WORKFLOW_QUEUE_TRIGGER, applySwcTransform, detectWorkflowPatterns, getImportPath, resolveWorkflowAliasRelativePath,
26
25
  // biome-ignore lint/security/noGlobalEval: Need to use eval here to avoid TypeScript from transpiling the import statement into `require()`
27
26
  } = (await eval('import("@workflow/builders")'));
28
- // Shared resolve options matching the configuration used by the SWC
29
- // esbuild plugin (swc-esbuild-plugin.ts) for consistent resolution
30
- // semantics across the toolchain.
31
- const NODE_RESOLVE_OPTIONS = {
32
- dependencyType: 'commonjs',
33
- modules: ['node_modules'],
34
- exportsFields: ['exports'],
35
- importsFields: ['imports'],
36
- conditionNames: ['node', 'require'],
37
- descriptionFiles: ['package.json'],
38
- extensions: [
39
- '.ts',
40
- '.tsx',
41
- '.mts',
42
- '.cts',
43
- '.cjs',
44
- '.mjs',
45
- '.js',
46
- '.jsx',
47
- '.json',
48
- '.node',
49
- ],
50
- enforceExtensions: false,
51
- symlinks: true,
52
- mainFields: ['main'],
53
- mainFiles: ['index'],
54
- roots: [],
55
- fullySpecified: false,
56
- preferRelative: false,
57
- preferAbsolute: false,
58
- restrictions: [],
59
- };
60
- const NODE_ESM_RESOLVE_OPTIONS = {
61
- ...NODE_RESOLVE_OPTIONS,
62
- dependencyType: 'esm',
63
- conditionNames: ['node', 'import'],
64
- };
65
27
  class NextDeferredBuilder extends BaseBuilderClass {
66
28
  socketIO;
67
29
  discoveredWorkflowFiles = new Set();
@@ -73,10 +35,6 @@ async function getNextBuilderDeferred() {
73
35
  cacheWriteTimer = null;
74
36
  deferredRebuildTimer = null;
75
37
  lastDeferredBuildSignature = null;
76
- // Lazily initialized resolvers for bare specifier rewriting.
77
- // Cached to avoid re-creating on every import rewrite.
78
- esmSyncResolver;
79
- cjsSyncResolver;
80
38
  manifestStepResolveBaseDirs = null;
81
39
  async build() {
82
40
  const outputDir = await this.findAppDirectory();
@@ -477,7 +435,7 @@ async function getNextBuilderDeferred() {
477
435
  (0, node_path_1.join)(stepRouteDir, 'route.js.temp'),
478
436
  (0, node_path_1.join)(stepRouteDir, 'route.js.temp.debug.json'),
479
437
  (0, node_path_1.join)(stepRouteDir, 'route.js.debug.json'),
480
- (0, node_path_1.join)(stepRouteDir, step_copy_utils_js_1.DEFERRED_STEP_COPY_DIR_NAME),
438
+ (0, node_path_1.join)(stepRouteDir, DEFERRED_STEP_COPY_DIR_NAME),
481
439
  (0, node_path_1.join)(webhookRouteDir, 'route.js.temp'),
482
440
  (0, node_path_1.join)(workflowGeneratedDir, 'manifest.json'),
483
441
  ];
@@ -955,91 +913,7 @@ async function getNextBuilderDeferred() {
955
913
  }
956
914
  return relativePath;
957
915
  }
958
- getStepCopyFileName(filePath) {
959
- const normalizedPath = filePath.replace(/\\/g, '/');
960
- const hash = (0, node_crypto_1.createHash)('sha256').update(normalizedPath).digest('hex');
961
- const extension = (0, node_path_1.extname)(normalizedPath);
962
- return `${hash.slice(0, 16)}${extension || '.js'}`;
963
- }
964
- rewriteCopiedStepImportSpecifier(specifier, sourceFilePath, copiedFilePath, copiedStepFileBySourcePath) {
965
- if (!specifier.startsWith('.')) {
966
- // Bare specifiers (e.g. '@workflow/serde') that are transitive
967
- // dependencies of SDK packages can't be resolved by the bundler
968
- // from the copied file's location (__workflow_step_files__/ inside
969
- // the app dir) because the app doesn't directly depend on them.
970
- //
971
- // Only rewrite when the specifier can't be resolved from the app
972
- // directory. If the package is a direct dependency of the app,
973
- // the bare specifier will resolve normally and should be left as-is.
974
- const appResolvable = this.resolveBareCopiedStepSpecifier(specifier, copiedFilePath);
975
- if (!appResolvable) {
976
- const resolved = this.resolveBareCopiedStepSpecifier(specifier, sourceFilePath);
977
- if (!resolved)
978
- return specifier;
979
- let rewrittenPath = (0, node_path_1.relative)((0, node_path_1.dirname)(copiedFilePath), resolved).replace(/\\/g, '/');
980
- if (!rewrittenPath.startsWith('.')) {
981
- rewrittenPath = `./${rewrittenPath}`;
982
- }
983
- return rewrittenPath;
984
- }
985
- return specifier;
986
- }
987
- const specifierMatch = specifier.match(/^([^?#]+)(.*)$/);
988
- const importPath = specifierMatch?.[1] ?? specifier;
989
- const suffix = specifierMatch?.[2] ?? '';
990
- const absoluteTargetPath = (0, node_path_1.resolve)((0, node_path_1.dirname)(sourceFilePath), importPath);
991
- const resolvedTargetPath = this.resolveCopiedStepImportTargetPath(absoluteTargetPath);
992
- const normalizedTargetPath = resolvedTargetPath.replace(/\\/g, '/');
993
- const copiedTargetPath = copiedStepFileBySourcePath.get(normalizedTargetPath) ||
994
- (() => {
995
- try {
996
- const realTargetPath = (0, node_fs_1.realpathSync)(resolvedTargetPath).replace(/\\/g, '/');
997
- return copiedStepFileBySourcePath.get(realTargetPath);
998
- }
999
- catch {
1000
- return undefined;
1001
- }
1002
- })();
1003
- let rewrittenPath = (0, node_path_1.relative)((0, node_path_1.dirname)(copiedFilePath), copiedTargetPath || resolvedTargetPath).replace(/\\/g, '/');
1004
- if (!rewrittenPath.startsWith('.')) {
1005
- rewrittenPath = `./${rewrittenPath}`;
1006
- }
1007
- return `${rewrittenPath}${suffix}`;
1008
- }
1009
- /**
1010
- * Resolves a bare specifier (e.g. '@workflow/serde', 'workflow') to an
1011
- * absolute file path using ESM-compatible resolution semantics via
1012
- * `enhanced-resolve`. Tries ESM conditions first (`node`, `import`),
1013
- * falling back to CJS resolution if ESM fails.
1014
- */
1015
- resolveBareCopiedStepSpecifier(specifier, sourceFilePath) {
1016
- if (!this.esmSyncResolver) {
1017
- this.esmSyncResolver = enhanced_resolve_1.default.create.sync(NODE_ESM_RESOLVE_OPTIONS);
1018
- }
1019
- if (!this.cjsSyncResolver) {
1020
- this.cjsSyncResolver =
1021
- enhanced_resolve_1.default.create.sync(NODE_RESOLVE_OPTIONS);
1022
- }
1023
- const context = (0, node_path_1.dirname)(sourceFilePath);
1024
- try {
1025
- const resolved = this.esmSyncResolver(context, specifier);
1026
- if (resolved)
1027
- return resolved;
1028
- }
1029
- catch {
1030
- // ESM resolution failed, try CJS
1031
- }
1032
- try {
1033
- const resolved = this.cjsSyncResolver(context, specifier);
1034
- if (resolved)
1035
- return resolved;
1036
- }
1037
- catch {
1038
- // CJS resolution also failed
1039
- }
1040
- return undefined;
1041
- }
1042
- resolveCopiedStepImportTargetPath(targetPath) {
916
+ resolveImportTargetWithExtensionFallbacks(targetPath) {
1043
917
  if ((0, node_fs_1.existsSync)(targetPath)) {
1044
918
  return targetPath;
1045
919
  }
@@ -1069,16 +943,6 @@ async function getNextBuilderDeferred() {
1069
943
  }
1070
944
  return targetPath;
1071
945
  }
1072
- rewriteRelativeImportsForCopiedStep(source, sourceFilePath, copiedFilePath, copiedStepFileBySourcePath) {
1073
- const rewriteSpecifier = (specifier) => this.rewriteCopiedStepImportSpecifier(specifier, sourceFilePath, copiedFilePath, copiedStepFileBySourcePath);
1074
- const rewritePattern = (currentSource, pattern) => currentSource.replace(pattern, (_match, prefix, specifier, suffix) => `${prefix}${rewriteSpecifier(specifier)}${suffix}`);
1075
- let rewrittenSource = source;
1076
- rewrittenSource = rewritePattern(rewrittenSource, /(from\s+['"])([^'"]+)(['"])/g);
1077
- rewrittenSource = rewritePattern(rewrittenSource, /(import\s+['"])([^'"]+)(['"])/g);
1078
- rewrittenSource = rewritePattern(rewrittenSource, /(import\(\s*['"])([^'"]+)(['"]\s*\))/g);
1079
- rewrittenSource = rewritePattern(rewrittenSource, /(require\(\s*['"])([^'"]+)(['"]\s*\))/g);
1080
- return rewrittenSource;
1081
- }
1082
946
  extractRelativeImportSpecifiers(source) {
1083
947
  const relativeSpecifiers = new Set();
1084
948
  const importPatterns = [
@@ -1113,7 +977,7 @@ async function getNextBuilderDeferred() {
1113
977
  const importPath = specifierMatch?.[1] ?? specifier;
1114
978
  const absoluteTargetPath = (0, node_path_1.resolve)((0, node_path_1.dirname)(sourceFilePath), importPath);
1115
979
  const candidatePaths = new Set([
1116
- this.resolveCopiedStepImportTargetPath(absoluteTargetPath),
980
+ this.resolveImportTargetWithExtensionFallbacks(absoluteTargetPath),
1117
981
  ]);
1118
982
  if (!(0, node_path_1.extname)(absoluteTargetPath)) {
1119
983
  const extensionCandidates = [
@@ -1132,7 +996,7 @@ async function getNextBuilderDeferred() {
1132
996
  }
1133
997
  }
1134
998
  for (const candidatePath of candidatePaths) {
1135
- const resolvedPath = this.resolveCopiedStepImportTargetPath(candidatePath);
999
+ const resolvedPath = this.resolveImportTargetWithExtensionFallbacks(candidatePath);
1136
1000
  const normalizedResolvedPath = this.normalizeDiscoveredFilePath(resolvedPath);
1137
1001
  if (this.shouldSkipTransitiveStepFile(normalizedResolvedPath)) {
1138
1002
  continue;
@@ -1313,85 +1177,6 @@ async function getNextBuilderDeferred() {
1313
1177
  }));
1314
1178
  return verifiedSerdeFiles.sort();
1315
1179
  }
1316
- async createResponseBuiltinsStepFile({ stepsRouteDir, }) {
1317
- const copiedStepsDir = (0, node_path_1.join)(stepsRouteDir, step_copy_utils_js_1.DEFERRED_STEP_COPY_DIR_NAME);
1318
- await (0, promises_1.mkdir)(copiedStepsDir, { recursive: true });
1319
- const responseBuiltinsFilePath = (0, node_path_1.join)(copiedStepsDir, 'workflow-response-builtins.ts');
1320
- const source = [
1321
- 'export async function __builtin_response_array_buffer(this: Request | Response) {',
1322
- " 'use step';",
1323
- ' return this.arrayBuffer();',
1324
- '}',
1325
- '',
1326
- 'export async function __builtin_response_json(this: Request | Response) {',
1327
- " 'use step';",
1328
- ' return this.json();',
1329
- '}',
1330
- '',
1331
- 'export async function __builtin_response_text(this: Request | Response) {',
1332
- " 'use step';",
1333
- ' return this.text();',
1334
- '}',
1335
- ].join('\n');
1336
- const sourceMapComment = (0, step_copy_utils_js_1.createDeferredStepCopyInlineSourceMapComment)({
1337
- sourcePath: responseBuiltinsFilePath,
1338
- sourceContent: source,
1339
- });
1340
- await this.writeFileIfChanged(responseBuiltinsFilePath, `${source}\n${sourceMapComment}\n`);
1341
- return responseBuiltinsFilePath;
1342
- }
1343
- async copyDiscoveredStepFiles({ stepFiles, stepsRouteDir, preserveFileNames = [], }) {
1344
- const copiedStepsDir = (0, node_path_1.join)(stepsRouteDir, step_copy_utils_js_1.DEFERRED_STEP_COPY_DIR_NAME);
1345
- await (0, promises_1.mkdir)(copiedStepsDir, { recursive: true });
1346
- const normalizedStepFiles = Array.from(new Set(stepFiles.map((stepFile) => this.normalizeDiscoveredFilePath(stepFile)))).sort();
1347
- const copiedStepFileBySourcePath = new Map();
1348
- const expectedFileNames = new Set(preserveFileNames);
1349
- const copiedStepFiles = [];
1350
- for (const normalizedStepFile of normalizedStepFiles) {
1351
- const copiedFileName = this.getStepCopyFileName(normalizedStepFile);
1352
- const copiedFilePath = (0, node_path_1.join)(copiedStepsDir, copiedFileName);
1353
- const normalizedPathKey = normalizedStepFile.replace(/\\/g, '/');
1354
- copiedStepFileBySourcePath.set(normalizedPathKey, copiedFilePath);
1355
- try {
1356
- const realPathKey = (0, node_fs_1.realpathSync)(normalizedStepFile).replace(/\\/g, '/');
1357
- copiedStepFileBySourcePath.set(realPathKey, copiedFilePath);
1358
- }
1359
- catch {
1360
- // Keep best-effort mapping when source cannot be realpath-resolved.
1361
- }
1362
- expectedFileNames.add(copiedFileName);
1363
- copiedStepFiles.push(copiedFilePath);
1364
- }
1365
- for (const normalizedStepFile of normalizedStepFiles) {
1366
- const source = await (0, promises_1.readFile)(normalizedStepFile, 'utf-8');
1367
- const copiedFilePath = copiedStepFileBySourcePath.get(normalizedStepFile.replace(/\\/g, '/'));
1368
- if (!copiedFilePath) {
1369
- continue;
1370
- }
1371
- const rewrittenSource = this.rewriteRelativeImportsForCopiedStep(source, normalizedStepFile, copiedFilePath, copiedStepFileBySourcePath);
1372
- const metadataComment = (0, step_copy_utils_js_1.createDeferredStepSourceMetadataComment)({
1373
- relativeFilename: await this.getRelativeFilenameForSwc(normalizedStepFile),
1374
- absolutePath: normalizedStepFile.replace(/\\/g, '/'),
1375
- });
1376
- const sourceMapComment = (0, step_copy_utils_js_1.createDeferredStepCopyInlineSourceMapComment)({
1377
- sourcePath: normalizedStepFile,
1378
- sourceContent: source,
1379
- generatedContent: rewrittenSource,
1380
- });
1381
- const copiedSource = `${metadataComment}\n${rewrittenSource}\n${sourceMapComment}`;
1382
- await this.writeFileIfChanged(copiedFilePath, copiedSource);
1383
- }
1384
- const existingEntries = await (0, promises_1.readdir)(copiedStepsDir, {
1385
- withFileTypes: true,
1386
- }).catch(() => []);
1387
- await Promise.all(existingEntries
1388
- .filter((entry) => !expectedFileNames.has(entry.name))
1389
- .map((entry) => (0, promises_1.rm)((0, node_path_1.join)(copiedStepsDir, entry.name), {
1390
- recursive: true,
1391
- force: true,
1392
- })));
1393
- return copiedStepFiles;
1394
- }
1395
1180
  async createDeferredStepsManifest({ stepFiles, workflowFiles, serdeOnlyFiles, }) {
1396
1181
  const workflowManifest = {};
1397
1182
  const filesForStepTransform = Array.from(new Set([...stepFiles, ...serdeOnlyFiles])).sort();
@@ -1438,6 +1223,41 @@ async function getNextBuilderDeferred() {
1438
1223
  const existingCandidates = await this.filterExistingFiles(candidateFiles);
1439
1224
  return Array.from(new Set(existingCandidates)).sort();
1440
1225
  }
1226
+ /**
1227
+ * Resolves the path to the workflow SDK's internal response builtins
1228
+ * step file. Returns the file as a pair of (absolute path, import
1229
+ * specifier) so the caller can include it in both the manifest
1230
+ * (absolute path) and the generated step/route.js (as an import).
1231
+ */
1232
+ resolveResponseBuiltinsStepSource() {
1233
+ let resolved;
1234
+ try {
1235
+ resolved = require.resolve('workflow/internal/builtins', {
1236
+ paths: [this.config.workingDir],
1237
+ });
1238
+ }
1239
+ catch {
1240
+ return null;
1241
+ }
1242
+ return {
1243
+ absolutePath: this.normalizeDiscoveredFilePath(resolved),
1244
+ importPath: 'workflow/internal/builtins',
1245
+ };
1246
+ }
1247
+ /**
1248
+ * Builds the import specifier for a given step/serde source file as it
1249
+ * should appear in the generated step/route.js. Package-provided files
1250
+ * are imported via their package specifier so the bundler resolves them
1251
+ * from the app's dependency graph. Local files are imported via a
1252
+ * relative path from the generated route.
1253
+ */
1254
+ getStepRouteImportSpecifier(stepRouteFile, sourceFilePath) {
1255
+ const { importPath, isPackage } = getImportPath(sourceFilePath, this.config.workingDir);
1256
+ if (isPackage) {
1257
+ return importPath;
1258
+ }
1259
+ return this.getRelativeImportSpecifier(stepRouteFile, sourceFilePath);
1260
+ }
1441
1261
  async buildStepsFunction({ workflowGeneratedDir, routeFileName = 'route.js', discoveredEntries, additionalStepSourceManifest, }) {
1442
1262
  const stepsRouteDir = (0, node_path_1.join)(workflowGeneratedDir, 'step');
1443
1263
  await (0, promises_1.mkdir)(stepsRouteDir, { recursive: true });
@@ -1456,54 +1276,46 @@ async function getNextBuilderDeferred() {
1456
1276
  ? await this.collectManifestStepSourceFiles(additionalStepSourceManifest)
1457
1277
  : [];
1458
1278
  const stepFilesWithManifestSources = Array.from(new Set([...stepFiles, ...additionalManifestStepFiles])).sort();
1459
- const responseBuiltinsStepFilePath = await this.createResponseBuiltinsStepFile({
1460
- stepsRouteDir,
1461
- });
1462
- const manifestStepFiles = Array.from(new Set([...stepFilesWithManifestSources, responseBuiltinsStepFilePath])).sort();
1279
+ const responseBuiltins = this.resolveResponseBuiltinsStepSource();
1280
+ const manifestStepFiles = Array.from(new Set([
1281
+ ...stepFilesWithManifestSources,
1282
+ ...(responseBuiltins ? [responseBuiltins.absolutePath] : []),
1283
+ ])).sort();
1463
1284
  const manifest = await this.createDeferredStepsManifest({
1464
1285
  stepFiles: manifestStepFiles,
1465
1286
  workflowFiles,
1466
1287
  serdeOnlyFiles,
1467
1288
  });
1468
1289
  const manifestDiscoveredStepFiles = await this.collectManifestStepSourceFiles(manifest);
1469
- // Copy all discovered step sources so they are transformed in step mode.
1470
- // Importing raw node_modules files directly can bypass loader transforms,
1471
- // which prevents step registrars from being emitted.
1472
- const copiedStepSourceFiles = Array.from(new Set([
1290
+ // Step source files are imported directly from their original
1291
+ // locations. The workflow loader transforms every file it sees in
1292
+ // step mode, so package-provided sources register their step
1293
+ // functions as module side effects just like local sources do.
1294
+ const stepSourceFiles = Array.from(new Set([
1473
1295
  ...stepFilesWithManifestSources,
1474
1296
  ...manifestDiscoveredStepFiles,
1475
1297
  ])).sort();
1476
- const copiedDiscoveredStepFiles = await this.copyDiscoveredStepFiles({
1477
- stepFiles: copiedStepSourceFiles,
1478
- stepsRouteDir,
1479
- preserveFileNames: [(0, node_path_1.basename)(responseBuiltinsStepFilePath)],
1480
- });
1481
- const copiedStepFiles = [
1482
- responseBuiltinsStepFilePath,
1483
- ...copiedDiscoveredStepFiles,
1484
- ];
1485
1298
  const stepRouteFile = (0, node_path_1.join)(stepsRouteDir, routeFileName);
1486
- const copiedStepImports = copiedStepFiles
1487
- .map((copiedStepFile) => {
1488
- const importSpecifier = this.getRelativeImportSpecifier(stepRouteFile, copiedStepFile);
1489
- return `import '${importSpecifier}';`;
1490
- })
1299
+ const stepImportSpecifiers = new Set();
1300
+ if (responseBuiltins) {
1301
+ stepImportSpecifiers.add(responseBuiltins.importPath);
1302
+ }
1303
+ for (const stepSourceFile of stepSourceFiles) {
1304
+ stepImportSpecifiers.add(this.getStepRouteImportSpecifier(stepRouteFile, stepSourceFile));
1305
+ }
1306
+ const stepImports = Array.from(stepImportSpecifiers)
1307
+ .map((specifier) => `import '${specifier}';`)
1491
1308
  .join('\n');
1492
1309
  const serdeImports = serdeOnlyFiles
1493
1310
  .map((serdeFile) => {
1494
1311
  const normalizedSerdeFile = this.normalizeDiscoveredFilePath(serdeFile);
1495
- const { importPath, isPackage } = getImportPath(normalizedSerdeFile, this.config.workingDir);
1496
- if (isPackage) {
1497
- return `import '${importPath}';`;
1498
- }
1499
- const importSpecifier = this.getRelativeImportSpecifier(stepRouteFile, normalizedSerdeFile);
1500
- return `import '${importSpecifier}';`;
1312
+ return `import '${this.getStepRouteImportSpecifier(stepRouteFile, normalizedSerdeFile)}';`;
1501
1313
  })
1502
1314
  .join('\n');
1503
1315
  const routeContents = [
1504
1316
  '// biome-ignore-all lint: generated file',
1505
1317
  '/* eslint-disable */',
1506
- copiedStepImports,
1318
+ stepImports,
1507
1319
  serdeImports
1508
1320
  ? `// Serde files for cross-context class registration\n${serdeImports}`
1509
1321
  : '',