@edge-base/cli 0.2.7 → 0.2.9

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 (53) hide show
  1. package/dist/commands/build-app.d.ts +3 -0
  2. package/dist/commands/build-app.d.ts.map +1 -0
  3. package/dist/commands/build-app.js +45 -0
  4. package/dist/commands/build-app.js.map +1 -0
  5. package/dist/commands/deploy.d.ts.map +1 -1
  6. package/dist/commands/deploy.js +14 -14
  7. package/dist/commands/deploy.js.map +1 -1
  8. package/dist/commands/dev.d.ts.map +1 -1
  9. package/dist/commands/dev.js +43 -36
  10. package/dist/commands/dev.js.map +1 -1
  11. package/dist/commands/docker.d.ts +5 -0
  12. package/dist/commands/docker.d.ts.map +1 -1
  13. package/dist/commands/docker.js +83 -5
  14. package/dist/commands/docker.js.map +1 -1
  15. package/dist/commands/pack.d.ts +3 -0
  16. package/dist/commands/pack.d.ts.map +1 -0
  17. package/dist/commands/pack.js +117 -0
  18. package/dist/commands/pack.js.map +1 -0
  19. package/dist/index.js +6 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/lib/app-bundle.d.ts +48 -0
  22. package/dist/lib/app-bundle.d.ts.map +1 -0
  23. package/dist/lib/app-bundle.js +240 -0
  24. package/dist/lib/app-bundle.js.map +1 -0
  25. package/dist/lib/deploy-shared.d.ts +4 -0
  26. package/dist/lib/deploy-shared.d.ts.map +1 -1
  27. package/dist/lib/deploy-shared.js +40 -8
  28. package/dist/lib/deploy-shared.js.map +1 -1
  29. package/dist/lib/frontend-config.d.ts +7 -0
  30. package/dist/lib/frontend-config.d.ts.map +1 -0
  31. package/dist/lib/frontend-config.js +8 -0
  32. package/dist/lib/frontend-config.js.map +1 -0
  33. package/dist/lib/function-registry.d.ts +1 -0
  34. package/dist/lib/function-registry.d.ts.map +1 -1
  35. package/dist/lib/function-registry.js +3 -1
  36. package/dist/lib/function-registry.js.map +1 -1
  37. package/dist/lib/managed-resource-names.d.ts +2 -0
  38. package/dist/lib/managed-resource-names.d.ts.map +1 -1
  39. package/dist/lib/managed-resource-names.js +19 -1
  40. package/dist/lib/managed-resource-names.js.map +1 -1
  41. package/dist/lib/node-tools.d.ts +3 -1
  42. package/dist/lib/node-tools.d.ts.map +1 -1
  43. package/dist/lib/node-tools.js +2 -2
  44. package/dist/lib/node-tools.js.map +1 -1
  45. package/dist/lib/pack.d.ts +102 -0
  46. package/dist/lib/pack.d.ts.map +1 -0
  47. package/dist/lib/pack.js +1047 -0
  48. package/dist/lib/pack.js.map +1 -0
  49. package/dist/lib/runtime-scaffold.d.ts +43 -3
  50. package/dist/lib/runtime-scaffold.d.ts.map +1 -1
  51. package/dist/lib/runtime-scaffold.js +455 -38
  52. package/dist/lib/runtime-scaffold.js.map +1 -1
  53. package/package.json +7 -4
@@ -1,7 +1,9 @@
1
- import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, unlinkSync, symlinkSync, writeFileSync, } from 'node:fs';
2
- import { basename, dirname, join, resolve } from 'node:path';
1
+ import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, realpathSync, readdirSync, readFileSync, readlinkSync, rmSync, unlinkSync, symlinkSync, writeFileSync, } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { basename, dirname, join, posix, resolve } from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
- import { buildManagedD1DatabaseName } from './managed-resource-names.js';
5
+ import { normalizeFrontendMountPath } from './frontend-config.js';
6
+ import { buildManagedD1DatabaseName, buildManagedR2BucketName, buildManagedWorkerName, } from './managed-resource-names.js';
5
7
  import { deriveProjectSlug, INTERNAL_D1_BINDINGS } from './project-runtime.js';
6
8
  import { resolveWranglerTool } from './wrangler.js';
7
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -14,7 +16,9 @@ const MONOREPO_ADMIN_BUILD_SOURCES = [
14
16
  const CLI_NODE_MODULES_SOURCE = resolve(__dirname, '../../node_modules');
15
17
  const WORKSPACE_NODE_MODULES_SOURCE = resolve(WORKSPACE_ROOT, 'node_modules');
16
18
  const SERVER_NODE_MODULES_SOURCE = resolve(__dirname, '../../../server/node_modules');
19
+ const MONOREPO_SERVER_PACKAGE_MANIFEST_SOURCE = resolve(__dirname, '../../../server/package.json');
17
20
  const MONOREPO_SHARED_SOURCE = resolve(__dirname, '../../../shared');
21
+ const EDGEBASE_ASSETS_DIRNAME = 'app-assets';
18
22
  export { deriveProjectSlug, INTERNAL_D1_BINDINGS } from './project-runtime.js';
19
23
  export function getRuntimeRoot(projectDir) {
20
24
  return join(projectDir, '.edgebase', 'runtime', 'server');
@@ -25,17 +29,21 @@ export function getRuntimeServerSrcDir(projectDir) {
25
29
  export function getRuntimeAdminBuildDir(projectDir) {
26
30
  return join(getRuntimeRoot(projectDir), 'admin-build');
27
31
  }
28
- export function ensureRuntimeScaffold(projectDir) {
32
+ export function getRuntimeAssetsDir(projectDir) {
33
+ return join(getRuntimeRoot(projectDir), EDGEBASE_ASSETS_DIRNAME);
34
+ }
35
+ export function ensureRuntimeScaffold(projectDir, options = {}) {
29
36
  const runtimeRoot = getRuntimeRoot(projectDir);
30
37
  mkdirSync(runtimeRoot, { recursive: true });
31
38
  copyRuntimeDir(resolveServerRuntimeSource(projectDir), getRuntimeServerSrcDir(projectDir));
32
39
  const adminBuildDir = getRuntimeAdminBuildDir(projectDir);
33
40
  copyRuntimeDir(resolveAdminBuildSource(projectDir), adminBuildDir);
34
41
  normalizeAdminBuildBase(adminBuildDir);
35
- ensureRuntimeNodeModulesLink(projectDir, runtimeRoot);
36
- ensureProjectSharedPackageLink(projectDir);
37
- writeRuntimeConfigShim(projectDir);
38
- writeRuntimeTestConfigShim(projectDir);
42
+ prepareRuntimeAssets(projectDir, options.frontendProjectDir ?? projectDir, adminBuildDir, options.frontend ?? undefined);
43
+ ensureRuntimeNodeModules(projectDir, runtimeRoot, options.dependencyMode ?? 'symlink', options.dependencyProfile ?? 'portable');
44
+ ensureProjectSharedPackageLink(projectDir, options.dependencyMode ?? 'symlink');
45
+ writeRuntimeConfigShim(projectDir, undefined, { importPath: options.configImportPath });
46
+ writeRuntimeTestConfigShim(projectDir, { importPath: options.testConfigImportPath });
39
47
  }
40
48
  export function ensureLocalWranglerToml(projectDir) {
41
49
  const wranglerPath = join(projectDir, 'wrangler.toml');
@@ -45,14 +53,15 @@ export function ensureLocalWranglerToml(projectDir) {
45
53
  return wranglerPath;
46
54
  }
47
55
  export function buildDefaultWranglerToml(accountId, projectName = 'edgebase') {
56
+ const workerName = buildManagedWorkerName(projectName);
48
57
  const accountLine = accountId ? `account_id = "${accountId}"\n` : '';
49
58
  const internalD1Blocks = INTERNAL_D1_BINDINGS.map(({ name, binding }) => `[[d1_databases]]
50
59
  binding = "${binding}"
51
- database_name = "${buildManagedD1DatabaseName(projectName, name)}"
60
+ database_name = "${buildManagedD1DatabaseName(workerName, name)}"
52
61
  database_id = "local"
53
62
  `).join('\n');
54
63
  return `# Auto-generated by edgebase
55
- name = "${projectName}"
64
+ name = "${workerName}"
56
65
  ${accountLine}main = ".edgebase/runtime/server/src/index.ts"
57
66
  compatibility_date = "2025-02-10"
58
67
  compatibility_flags = ["nodejs_compat"]
@@ -76,7 +85,7 @@ new_sqlite_classes = ["LogsDO"]
76
85
 
77
86
  [[r2_buckets]]
78
87
  binding = "STORAGE"
79
- bucket_name = "${projectName}-storage"
88
+ bucket_name = "${buildManagedR2BucketName(workerName)}"
80
89
 
81
90
  [[kv_namespaces]]
82
91
  binding = "KV"
@@ -85,7 +94,7 @@ id = "local"
85
94
  ${internalD1Blocks}
86
95
 
87
96
  [assets]
88
- directory = ".edgebase/runtime/server/admin-build"
97
+ directory = ".edgebase/runtime/server/app-assets"
89
98
  binding = "ASSETS"
90
99
  run_worker_first = true
91
100
  `;
@@ -106,10 +115,10 @@ function copyRuntimeDir(source, target) {
106
115
  }
107
116
  function resolveServerRuntimeSource(projectDir) {
108
117
  const candidates = dedupeCandidates([
118
+ MONOREPO_SERVER_RUNTIME_SOURCE,
109
119
  join(projectDir, 'node_modules', '@edge-base', 'server', 'src'),
110
120
  resolve(CLI_NODE_MODULES_SOURCE, '@edge-base/server', 'src'),
111
121
  resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base/server', 'src'),
112
- MONOREPO_SERVER_RUNTIME_SOURCE,
113
122
  ]);
114
123
  for (const candidate of candidates) {
115
124
  if (existsSync(join(candidate, 'index.ts'))) {
@@ -120,10 +129,10 @@ function resolveServerRuntimeSource(projectDir) {
120
129
  }
121
130
  function resolveAdminBuildSource(projectDir) {
122
131
  const candidates = dedupeCandidates([
132
+ ...MONOREPO_ADMIN_BUILD_SOURCES,
123
133
  join(projectDir, 'node_modules', '@edge-base', 'server', 'admin-build'),
124
134
  resolve(CLI_NODE_MODULES_SOURCE, '@edge-base/server', 'admin-build'),
125
135
  resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base/server', 'admin-build'),
126
- ...MONOREPO_ADMIN_BUILD_SOURCES,
127
136
  ]);
128
137
  for (const candidate of candidates) {
129
138
  if (existsSync(join(candidate, 'index.html'))) {
@@ -132,6 +141,31 @@ function resolveAdminBuildSource(projectDir) {
132
141
  }
133
142
  throw new Error(`Admin build source is missing. Checked: ${candidates.join(', ')}`);
134
143
  }
144
+ function prepareRuntimeAssets(projectDir, sourceProjectDir, adminBuildDir, frontend) {
145
+ const assetsDir = getRuntimeAssetsDir(projectDir);
146
+ rmSync(assetsDir, { recursive: true, force: true });
147
+ mkdirSync(assetsDir, { recursive: true });
148
+ copyRuntimeDir(adminBuildDir, join(assetsDir, 'admin'));
149
+ copyFrontendBuild(sourceProjectDir, assetsDir, frontend);
150
+ }
151
+ function copyFrontendBuild(projectDir, assetsDir, frontend) {
152
+ if (!frontend?.directory)
153
+ return;
154
+ const source = resolve(projectDir, frontend.directory);
155
+ if (!existsSync(source)) {
156
+ throw new Error(`Frontend build directory is missing: ${source}`);
157
+ }
158
+ const mountPath = normalizeFrontendMountPath(frontend.mountPath);
159
+ if (mountPath === '/') {
160
+ const conflicts = readdirSync(source).filter((entry) => entry === 'admin');
161
+ if (conflicts.length > 0) {
162
+ throw new Error(`Frontend build directory '${frontend.directory}' contains reserved top-level entries: ${conflicts.join(', ')}.`);
163
+ }
164
+ }
165
+ const target = mountPath === '/' ? assetsDir : join(assetsDir, mountPath.slice(1));
166
+ mkdirSync(target, { recursive: true });
167
+ copyRuntimeDir(source, target);
168
+ }
135
169
  function normalizeAdminBuildBase(adminBuildDir) {
136
170
  const indexPath = join(adminBuildDir, 'index.html');
137
171
  if (!existsSync(indexPath))
@@ -146,11 +180,15 @@ function normalizeAdminBuildBase(adminBuildDir) {
146
180
  writeFileSync(indexPath, normalized, 'utf-8');
147
181
  }
148
182
  }
149
- function ensureRuntimeNodeModulesLink(projectDir, runtimeRoot) {
150
- const source = resolveRuntimeNodeModulesSource(projectDir);
183
+ function ensureRuntimeNodeModules(projectDir, runtimeRoot, mode, profile) {
184
+ const source = resolveRuntimeNodeModulesSource(projectDir, mode);
151
185
  if (!source)
152
186
  return;
153
187
  const target = join(runtimeRoot, 'node_modules');
188
+ if (mode === 'copy') {
189
+ materializeNodeModulesTree(source, target, getRuntimeNodeModulesCandidates(projectDir, mode), resolveRuntimeDependencySelection(projectDir, profile));
190
+ return;
191
+ }
154
192
  const currentLink = readExistingSymlinkTarget(target);
155
193
  if (currentLink === source)
156
194
  return;
@@ -159,16 +197,16 @@ function ensureRuntimeNodeModulesLink(projectDir, runtimeRoot) {
159
197
  }
160
198
  symlinkSync(source, target, process.platform === 'win32' ? 'junction' : 'dir');
161
199
  }
162
- export function ensureProjectSharedPackageLink(projectDir) {
200
+ export function ensureProjectSharedPackageLink(projectDir, mode = 'symlink') {
163
201
  const source = resolveSharedPackageSource(projectDir);
164
202
  if (!source)
165
203
  return;
166
204
  for (const root of resolveSharedPackageLinkRoots(projectDir)) {
167
- ensureSharedPackageLinkAtRoot(root, source);
205
+ ensureSharedPackageLinkAtRoot(root, source, mode);
168
206
  }
169
207
  }
170
- function resolveRuntimeNodeModulesSource(projectDir) {
171
- return resolveRuntimeNodeModulesSourceFromCandidates(getRuntimeNodeModulesCandidates(projectDir));
208
+ function resolveRuntimeNodeModulesSource(projectDir, mode = 'symlink') {
209
+ return resolveRuntimeNodeModulesSourceFromCandidates(getRuntimeNodeModulesCandidates(projectDir, mode));
172
210
  }
173
211
  export function resolveRuntimeNodeModulesSourceFromCandidates(candidates) {
174
212
  for (const candidate of candidates) {
@@ -189,7 +227,15 @@ export function resolveSharedPackageSourceFromCandidates(candidates) {
189
227
  }
190
228
  return null;
191
229
  }
192
- function getRuntimeNodeModulesCandidates(projectDir) {
230
+ function getRuntimeNodeModulesCandidates(projectDir, mode = 'symlink') {
231
+ if (mode === 'copy') {
232
+ return dedupeCandidates([
233
+ SERVER_NODE_MODULES_SOURCE,
234
+ CLI_NODE_MODULES_SOURCE,
235
+ join(projectDir, 'node_modules'),
236
+ WORKSPACE_NODE_MODULES_SOURCE,
237
+ ]);
238
+ }
193
239
  return dedupeCandidates([
194
240
  SERVER_NODE_MODULES_SOURCE,
195
241
  CLI_NODE_MODULES_SOURCE,
@@ -197,6 +243,174 @@ function getRuntimeNodeModulesCandidates(projectDir) {
197
243
  join(projectDir, 'node_modules'),
198
244
  ]);
199
245
  }
246
+ function materializeNodeModulesTree(sourceRoot, targetRoot, candidateRoots, selectedPackages) {
247
+ rmSync(targetRoot, { recursive: true, force: true });
248
+ mkdirSync(targetRoot, { recursive: true });
249
+ const normalizedRoots = dedupeCandidates(candidateRoots)
250
+ .filter((candidate) => existsSync(candidate))
251
+ .map((candidate) => resolve(candidate))
252
+ .sort((left, right) => right.length - left.length);
253
+ const copiedTargets = new Set();
254
+ if (selectedPackages && selectedPackages.length > 0) {
255
+ for (const selection of selectedPackages) {
256
+ materializeNodeModulesEntry(selection.packageDir, join(targetRoot, ...selection.packageName.split('/')), targetRoot, normalizedRoots, copiedTargets);
257
+ }
258
+ return;
259
+ }
260
+ for (const entry of readdirSync(sourceRoot)) {
261
+ materializeNodeModulesEntry(join(sourceRoot, entry), join(targetRoot, entry), targetRoot, normalizedRoots, copiedTargets);
262
+ }
263
+ }
264
+ function resolvePackageDirectoryPath(packageName, candidateRoots) {
265
+ const topLevelEntry = resolveTopLevelPackageEntry(packageName, candidateRoots);
266
+ if (topLevelEntry) {
267
+ return topLevelEntry;
268
+ }
269
+ const packageSegments = packageName.split('/');
270
+ for (const candidateRoot of dedupeCandidates(candidateRoots)) {
271
+ const pnpmDir = join(candidateRoot, '.pnpm');
272
+ if (!existsSync(pnpmDir))
273
+ continue;
274
+ for (const entry of readdirSync(pnpmDir)) {
275
+ const candidatePath = join(pnpmDir, entry, 'node_modules', ...packageSegments);
276
+ if (existsSync(candidatePath)) {
277
+ return candidatePath;
278
+ }
279
+ }
280
+ }
281
+ return null;
282
+ }
283
+ function resolveTopLevelPackageEntry(packageName, candidateRoots) {
284
+ const packagePath = packageName.split('/');
285
+ for (const candidateRoot of candidateRoots) {
286
+ const candidatePath = join(candidateRoot, ...packagePath);
287
+ if (existsSync(candidatePath)) {
288
+ return candidatePath;
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+ function materializeNodeModulesEntry(sourceEntry, targetEntry, targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules = false) {
294
+ const stat = lstatSync(sourceEntry);
295
+ if (stat.isSymbolicLink()) {
296
+ const sourceLinkTarget = readlinkSync(sourceEntry);
297
+ const resolvedSourceTarget = resolve(dirname(sourceEntry), sourceLinkTarget);
298
+ const sourceContainerRoot = findContainingRoot(resolvedSourceTarget, candidateRoots);
299
+ if (!existsSync(resolvedSourceTarget)) {
300
+ rmSync(targetEntry, { recursive: true, force: true });
301
+ mkdirSync(dirname(targetEntry), { recursive: true });
302
+ symlinkSync(buildDirectoryLinkTarget(targetEntry, resolvedSourceTarget), targetEntry, process.platform === 'win32' ? 'junction' : 'dir');
303
+ return;
304
+ }
305
+ if (!sourceContainerRoot) {
306
+ const targetKey = `${resolvedSourceTarget}=>${targetEntry}`;
307
+ if (copiedTargets.has(targetKey)) {
308
+ return;
309
+ }
310
+ copiedTargets.add(targetKey);
311
+ materializeNodeModulesEntry(resolvedSourceTarget, targetEntry, targetRoot, candidateRoots, copiedTargets, true);
312
+ return;
313
+ }
314
+ const materialization = getNodeModulesMaterialization(resolvedSourceTarget, sourceContainerRoot, targetRoot);
315
+ const targetKey = `${materialization.sourceRoot}=>${materialization.targetRoot}`;
316
+ if (!copiedTargets.has(targetKey)) {
317
+ copiedTargets.add(targetKey);
318
+ materializeNodeModulesEntry(materialization.sourceRoot, materialization.targetRoot, targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules);
319
+ }
320
+ rmSync(targetEntry, { recursive: true, force: true });
321
+ mkdirSync(dirname(targetEntry), { recursive: true });
322
+ symlinkSync(buildDirectoryLinkTarget(targetEntry, materialization.targetPath), targetEntry, process.platform === 'win32' ? 'junction' : 'dir');
323
+ return;
324
+ }
325
+ if (stat.isDirectory()) {
326
+ mkdirSync(targetEntry, { recursive: true });
327
+ for (const entry of readdirSync(sourceEntry)) {
328
+ if (skipNestedNodeModules && entry === 'node_modules') {
329
+ continue;
330
+ }
331
+ materializeNodeModulesEntry(join(sourceEntry, entry), join(targetEntry, entry), targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules);
332
+ }
333
+ return;
334
+ }
335
+ mkdirSync(dirname(targetEntry), { recursive: true });
336
+ copyFileSync(sourceEntry, targetEntry);
337
+ }
338
+ function findContainingRoot(targetPath, candidateRoots) {
339
+ for (const candidateRoot of candidateRoots) {
340
+ if (getRelativePathSegmentsWithinRoot(candidateRoot, targetPath) !== null) {
341
+ return candidateRoot;
342
+ }
343
+ }
344
+ return null;
345
+ }
346
+ function getNodeModulesMaterialization(sourcePath, sourceContainerRoot, targetRoot) {
347
+ const relativeSegments = getRelativePathSegmentsWithinRoot(sourceContainerRoot, sourcePath);
348
+ if (!relativeSegments) {
349
+ throw new Error(`Could not resolve '${sourcePath}' inside runtime dependency root '${sourceContainerRoot}'.`);
350
+ }
351
+ const targetPath = join(targetRoot, ...relativeSegments);
352
+ if (relativeSegments[0] === '.pnpm' && relativeSegments.length >= 2) {
353
+ const packageRoot = join(sourceContainerRoot, '.pnpm', relativeSegments[1]);
354
+ return {
355
+ sourceRoot: packageRoot,
356
+ targetRoot: join(targetRoot, '.pnpm', relativeSegments[1]),
357
+ targetPath,
358
+ };
359
+ }
360
+ return {
361
+ sourceRoot: sourcePath,
362
+ targetRoot: targetPath,
363
+ targetPath,
364
+ };
365
+ }
366
+ function getRelativePathSegmentsWithinRoot(rootPath, targetPath) {
367
+ const rootSegments = splitCrossPlatformPathSegments(rootPath);
368
+ const targetSegments = splitCrossPlatformPathSegments(targetPath);
369
+ if (rootSegments.length > targetSegments.length) {
370
+ return null;
371
+ }
372
+ for (let index = 0; index < rootSegments.length; index += 1) {
373
+ if (rootSegments[index] !== targetSegments[index]) {
374
+ return null;
375
+ }
376
+ }
377
+ return targetSegments.slice(rootSegments.length);
378
+ }
379
+ function splitCrossPlatformPathSegments(pathValue) {
380
+ const normalized = normalizeCrossPlatformPath(pathValue);
381
+ return normalized.split('/').filter(Boolean);
382
+ }
383
+ function normalizeCrossPlatformPath(pathValue) {
384
+ let normalized = pathValue.replace(/\\/g, '/').replace(/\/+/g, '/');
385
+ if (normalized.length > 1 && normalized.endsWith('/')) {
386
+ normalized = normalized.slice(0, -1);
387
+ }
388
+ if (/^[A-Z]:($|\/)/.test(normalized)) {
389
+ normalized = `${normalized[0].toLowerCase()}${normalized.slice(1)}`;
390
+ }
391
+ return normalized;
392
+ }
393
+ function buildDirectoryLinkTarget(linkPath, destinationPath, platform = process.platform) {
394
+ if (platform === 'win32') {
395
+ if (/^[A-Za-z]:[\\/]/.test(destinationPath)) {
396
+ return destinationPath.replace(/\//g, '\\');
397
+ }
398
+ return resolve(destinationPath);
399
+ }
400
+ const sourceDir = normalizeCrossPlatformPath(dirname(linkPath));
401
+ const targetPath = normalizeCrossPlatformPath(destinationPath);
402
+ return posix.relative(sourceDir, targetPath) || '.';
403
+ }
404
+ export const __runtimeScaffoldTestUtils = {
405
+ buildDirectoryLinkTarget,
406
+ findContainingRoot,
407
+ getNodeModulesMaterialization,
408
+ getRelativePathSegmentsWithinRoot,
409
+ resolvePackageSelectionFromManifestCandidates,
410
+ resolvePackageDirectoryPath,
411
+ resolveRuntimePackageSelections,
412
+ resolveRuntimePackageSelectionsFromInitialSelections,
413
+ };
200
414
  function getSharedPackageSourceCandidates(projectDir) {
201
415
  return dedupeCandidates([
202
416
  join(projectDir, 'node_modules', '@edge-base', 'shared'),
@@ -206,6 +420,169 @@ function getSharedPackageSourceCandidates(projectDir) {
206
420
  MONOREPO_SHARED_SOURCE,
207
421
  ]);
208
422
  }
423
+ function resolveRuntimeDependencySelection(projectDir, profile) {
424
+ const candidateRoots = getRuntimeNodeModulesCandidates(projectDir, 'copy');
425
+ const serverDependencies = readPackageDependencyNamesFromCandidates(getServerPackageManifestCandidates(projectDir));
426
+ if (!serverDependencies) {
427
+ return null;
428
+ }
429
+ const initialSelections = [];
430
+ for (const packageName of dedupeCandidates(serverDependencies)) {
431
+ const selection = resolveRuntimePackageSelection(packageName, candidateRoots);
432
+ if (selection) {
433
+ initialSelections.push(selection);
434
+ }
435
+ }
436
+ if (profile === 'portable') {
437
+ const wranglerSelection = resolvePackageSelectionFromManifestCandidates('wrangler', getPortableWranglerManifestCandidates(projectDir)) ?? resolveRuntimePackageSelection('wrangler', candidateRoots);
438
+ if (wranglerSelection) {
439
+ initialSelections.push(wranglerSelection);
440
+ }
441
+ }
442
+ return resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots);
443
+ }
444
+ function getPortableWranglerManifestCandidates(projectDir) {
445
+ return dedupeCandidates([
446
+ join(projectDir, 'node_modules', 'wrangler', 'package.json'),
447
+ resolve(CLI_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
448
+ resolve(WORKSPACE_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
449
+ resolve(SERVER_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
450
+ ]);
451
+ }
452
+ function getServerPackageManifestCandidates(projectDir) {
453
+ return dedupeCandidates([
454
+ MONOREPO_SERVER_PACKAGE_MANIFEST_SOURCE,
455
+ join(projectDir, 'node_modules', '@edge-base', 'server', 'package.json'),
456
+ resolve(CLI_NODE_MODULES_SOURCE, '@edge-base', 'server', 'package.json'),
457
+ resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base', 'server', 'package.json'),
458
+ ]);
459
+ }
460
+ function readPackageDependencyNamesFromCandidates(candidates) {
461
+ for (const candidate of dedupeCandidates(candidates)) {
462
+ if (!existsSync(candidate))
463
+ continue;
464
+ try {
465
+ return readPackageDependencyNames(candidate);
466
+ }
467
+ catch {
468
+ continue;
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ function resolveRuntimePackageSelections(initialPackages, candidateRoots) {
474
+ const initialSelections = [];
475
+ for (const packageName of dedupeCandidates(initialPackages)) {
476
+ const selection = resolveRuntimePackageSelection(packageName, candidateRoots);
477
+ if (!selection) {
478
+ continue;
479
+ }
480
+ initialSelections.push(selection);
481
+ }
482
+ return resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots);
483
+ }
484
+ function resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots) {
485
+ const selections = new Map();
486
+ const visitedManifestPaths = new Set();
487
+ const queue = [];
488
+ for (const selection of initialSelections) {
489
+ if (selections.has(selection.packageName)) {
490
+ continue;
491
+ }
492
+ selections.set(selection.packageName, selection);
493
+ queue.push(selection);
494
+ }
495
+ while (queue.length > 0) {
496
+ const selection = queue.shift();
497
+ if (!selection || visitedManifestPaths.has(selection.manifestPath)) {
498
+ continue;
499
+ }
500
+ visitedManifestPaths.add(selection.manifestPath);
501
+ const packageRequire = createRequire(selection.manifestPath);
502
+ for (const dependencyName of readPackageDependencyNames(selection.manifestPath)) {
503
+ const dependencySelection = resolveRuntimePackageSelection(dependencyName, candidateRoots, packageRequire);
504
+ if (!dependencySelection) {
505
+ continue;
506
+ }
507
+ queue.push(dependencySelection);
508
+ if (!selections.has(dependencyName)) {
509
+ selections.set(dependencyName, dependencySelection);
510
+ }
511
+ }
512
+ }
513
+ return Array.from(selections.values());
514
+ }
515
+ function resolvePackageSelectionFromManifestCandidates(packageName, manifestCandidates) {
516
+ for (const manifestCandidate of dedupeCandidates(manifestCandidates)) {
517
+ if (!existsSync(manifestCandidate)) {
518
+ continue;
519
+ }
520
+ try {
521
+ const rawManifestPath = resolve(manifestCandidate);
522
+ const resolvedManifestPath = realpathSync(manifestCandidate);
523
+ const packageJson = JSON.parse(readFileSync(resolvedManifestPath, 'utf-8'));
524
+ if (packageJson.name !== packageName) {
525
+ continue;
526
+ }
527
+ return {
528
+ packageName,
529
+ packageDir: dirname(rawManifestPath),
530
+ manifestPath: resolvedManifestPath,
531
+ };
532
+ }
533
+ catch {
534
+ continue;
535
+ }
536
+ }
537
+ return null;
538
+ }
539
+ function resolveRuntimePackageSelection(packageName, candidateRoots, packageRequire) {
540
+ const location = resolvePackageManifestLocation(packageName, candidateRoots, packageRequire);
541
+ if (!location) {
542
+ return null;
543
+ }
544
+ return {
545
+ packageName,
546
+ packageDir: location.packageDir,
547
+ manifestPath: location.manifestPath,
548
+ };
549
+ }
550
+ function resolvePackageManifestLocation(packageName, candidateRoots, packageRequire) {
551
+ if (packageRequire) {
552
+ try {
553
+ const rawManifestPath = packageRequire.resolve(`${packageName}/package.json`);
554
+ if (existsSync(rawManifestPath)) {
555
+ return {
556
+ packageDir: dirname(resolve(rawManifestPath)),
557
+ manifestPath: realpathSync(rawManifestPath),
558
+ };
559
+ }
560
+ }
561
+ catch {
562
+ // Fall back to the candidate-root scan below so workspace shims and
563
+ // fixture tests can still resolve packages outside the package graph.
564
+ }
565
+ }
566
+ const packageDir = resolvePackageDirectoryPath(packageName, candidateRoots);
567
+ if (!packageDir) {
568
+ return null;
569
+ }
570
+ const rawManifestPath = join(packageDir, 'package.json');
571
+ if (!existsSync(rawManifestPath)) {
572
+ return null;
573
+ }
574
+ return {
575
+ packageDir,
576
+ manifestPath: realpathSync(rawManifestPath),
577
+ };
578
+ }
579
+ function readPackageDependencyNames(packageJsonPath) {
580
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
581
+ return [
582
+ ...Object.keys(packageJson.dependencies ?? {}),
583
+ ...Object.keys(packageJson.optionalDependencies ?? {}),
584
+ ];
585
+ }
209
586
  function dedupeCandidates(candidates) {
210
587
  return Array.from(new Set(candidates));
211
588
  }
@@ -217,7 +594,7 @@ export function resolveSharedPackageLinkRoots(projectDir) {
217
594
  }
218
595
  return Array.from(roots);
219
596
  }
220
- function ensureSharedPackageLinkAtRoot(rootDir, source) {
597
+ function ensureSharedPackageLinkAtRoot(rootDir, source, mode) {
221
598
  const target = join(rootDir, 'node_modules', '@edge-base', 'shared');
222
599
  const markerPath = join(target, '.edgebase-shim');
223
600
  const normalizedSource = resolve(source);
@@ -242,7 +619,12 @@ function ensureSharedPackageLinkAtRoot(rootDir, source) {
242
619
  mkdirSync(target, { recursive: true });
243
620
  writeFileSync(join(target, 'package.json'), buildSharedShimPackageJson(), 'utf-8');
244
621
  writeFileSync(markerPath, 'edgebase-shared-shim\n', 'utf-8');
245
- ensureDirectorySymlink(join(source, 'src'), join(target, 'src'));
622
+ if (mode === 'copy') {
623
+ replaceDirectoryContents(join(source, 'src'), join(target, 'src'));
624
+ }
625
+ else {
626
+ ensureDirectorySymlink(join(source, 'src'), join(target, 'src'));
627
+ }
246
628
  }
247
629
  function findWorkspaceRoot(startDir) {
248
630
  let currentDir = resolve(startDir);
@@ -288,6 +670,39 @@ function ensureDirectorySymlink(source, target) {
288
670
  symlinkSync(normalizedSource, target, process.platform === 'win32' ? 'junction' : 'dir');
289
671
  }
290
672
  }
673
+ function replaceDirectoryContents(source, target, options = {}) {
674
+ rmSync(target, { recursive: true, force: true });
675
+ mkdirSync(dirname(target), { recursive: true });
676
+ if ((options.dereference ?? true) === false && (options.verbatimSymlinks ?? false) === true) {
677
+ copyDirectoryTreePreservingSymlinks(source, target);
678
+ return;
679
+ }
680
+ cpSync(source, target, {
681
+ recursive: true,
682
+ force: true,
683
+ dereference: options.dereference ?? true,
684
+ verbatimSymlinks: options.verbatimSymlinks ?? false,
685
+ });
686
+ }
687
+ function copyDirectoryTreePreservingSymlinks(source, target) {
688
+ const stat = lstatSync(source);
689
+ if (stat.isSymbolicLink()) {
690
+ rmSync(target, { recursive: true, force: true });
691
+ mkdirSync(dirname(target), { recursive: true });
692
+ const linkTarget = readlinkSync(source);
693
+ symlinkSync(linkTarget, target, process.platform === 'win32' ? 'junction' : 'dir');
694
+ return;
695
+ }
696
+ if (stat.isDirectory()) {
697
+ mkdirSync(target, { recursive: true });
698
+ for (const entry of readdirSync(source)) {
699
+ copyDirectoryTreePreservingSymlinks(join(source, entry), join(target, entry));
700
+ }
701
+ return;
702
+ }
703
+ mkdirSync(dirname(target), { recursive: true });
704
+ copyFileSync(source, target);
705
+ }
291
706
  function readExistingSymlinkTarget(target) {
292
707
  try {
293
708
  if (!lstatSync(target).isSymbolicLink()) {
@@ -316,16 +731,16 @@ function buildSharedShimPackageJson() {
316
731
  },
317
732
  }, null, 2)}\n`;
318
733
  }
319
- function buildRuntimeConfigShimContents(injectedEnv) {
734
+ function buildRuntimeConfigShimContents(injectedEnv, importPath = '../../../../edgebase.config.ts') {
320
735
  const normalizedEnv = Object.fromEntries(Object.entries(injectedEnv ?? {}).filter(([, value]) => typeof value === 'string'));
321
736
  if (Object.keys(normalizedEnv).length === 0) {
322
- return `// Auto-generated by edgebase CLI. Re-export the user project config with a static path so Wrangler can bundle it.
323
- import config from '../../../../edgebase.config.ts';
737
+ return `// Auto-generated by edgebase CLI. Re-export the runtime config module with a static path so Wrangler can bundle it.
738
+ import config from '${importPath}';
324
739
 
325
740
  export default config;
326
741
  `;
327
742
  }
328
- return `// Auto-generated by edgebase CLI. Inject local config env before evaluating the user project config.
743
+ return `// Auto-generated by edgebase CLI. Inject local config env before evaluating the runtime config module.
329
744
  const injectedEnv = ${JSON.stringify(normalizedEnv, null, 2)} as Record<string, string>;
330
745
 
331
746
  if (typeof process !== 'undefined' && process?.env) {
@@ -336,25 +751,27 @@ if (typeof process !== 'undefined' && process?.env) {
336
751
  }
337
752
  }
338
753
 
339
- const mod = await import('../../../../edgebase.config.ts');
754
+ const mod = await import('${importPath}');
340
755
  const config = (mod as { default?: unknown }).default ?? mod;
341
756
 
342
757
  export default config;
343
758
  `;
344
759
  }
345
- export function writeRuntimeConfigShim(projectDir, injectedEnv) {
760
+ export function writeRuntimeConfigShim(projectDir, injectedEnv, options) {
346
761
  const shimPath = join(getRuntimeServerSrcDir(projectDir), 'generated-config.ts');
347
- writeFileSync(shimPath, buildRuntimeConfigShimContents(injectedEnv), 'utf-8');
762
+ writeFileSync(shimPath, buildRuntimeConfigShimContents(injectedEnv, options?.importPath), 'utf-8');
348
763
  }
349
- function writeRuntimeTestConfigShim(projectDir) {
764
+ function writeRuntimeTestConfigShim(projectDir, options) {
350
765
  const shimPath = join(getRuntimeRoot(projectDir), 'edgebase.test.config.ts');
351
- const tsConfigPath = join(projectDir, 'edgebase.test.config.ts');
352
- const jsConfigPath = join(projectDir, 'edgebase.test.config.js');
353
- const importPath = existsSync(tsConfigPath)
354
- ? '../../../edgebase.test.config.ts'
355
- : existsSync(jsConfigPath)
356
- ? '../../../edgebase.test.config.js'
357
- : './src/generated-config.ts';
766
+ const importPath = options?.importPath ?? (() => {
767
+ const tsConfigPath = join(projectDir, 'edgebase.test.config.ts');
768
+ const jsConfigPath = join(projectDir, 'edgebase.test.config.js');
769
+ return existsSync(tsConfigPath)
770
+ ? '../../../edgebase.test.config.ts'
771
+ : existsSync(jsConfigPath)
772
+ ? '../../../edgebase.test.config.js'
773
+ : './src/generated-config.ts';
774
+ })();
358
775
  writeFileSync(shimPath, `// Auto-generated by edgebase CLI. Keep the runtime test-config import resolvable in scaffolded dev environments.
359
776
  // If the project does not define a dedicated test config, fall back to the generated runtime config shim.
360
777
  import config from '${importPath}';