@moinulmoin/eden 0.1.5 → 0.2.1

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.
@@ -34,6 +34,7 @@ const START_COMMAND = [
34
34
  const EVE_ENTRYPOINT = ".output/server/index.mjs";
35
35
  const EVE_EXCLUDED_DIRECTORY_NAMES = new Set([
36
36
  ".eden",
37
+ ".eve",
37
38
  ".git",
38
39
  ".hg",
39
40
  ".next",
@@ -98,7 +99,8 @@ function isPathExcluded(relativePath, runtimeEnvRelativePath) {
98
99
  if (excludedDirectory !== undefined) {
99
100
  return {
100
101
  excluded: true,
101
- category: excludedDirectory === ".eden" || excludedDirectory === ".git"
102
+ category: excludedDirectory === ".eden" ||
103
+ excludedDirectory === ".eve" || excludedDirectory === ".git"
102
104
  ? "generated-state"
103
105
  : excludedDirectory === "node_modules"
104
106
  ? "node_modules"
@@ -886,6 +888,34 @@ async function writeJson(path, value) {
886
888
  mode: 0o600,
887
889
  });
888
890
  }
891
+ const EVE_GENERATED_SNAPSHOT_ROOTS = {
892
+ ".dockerignore": true,
893
+ ".output": true,
894
+ "node_modules": true,
895
+ };
896
+ /**
897
+ * The authored Eve project source the runtime stage must carry so `eve start`
898
+ * can resolve the project context (agent/, agents/ workspaces, flat layouts,
899
+ * and the manifest). Returns validated top-level snapshot entries with every
900
+ * capture-time exclusion still applied; generated roots are never source.
901
+ */
902
+ export async function eveSnapshotSourceEntries(snapshotRoot) {
903
+ const entries = await readdir(snapshotRoot, { withFileTypes: true });
904
+ const names = [];
905
+ for (const entry of entries) {
906
+ if (!entry.isFile() && !entry.isDirectory())
907
+ continue;
908
+ if (entry.isSymbolicLink())
909
+ continue;
910
+ if (EVE_GENERATED_SNAPSHOT_ROOTS[entry.name] === true)
911
+ continue;
912
+ if (isPathExcluded(entry.name, undefined).excluded)
913
+ continue;
914
+ names.push(entry.name);
915
+ }
916
+ names.sort((left, right) => left.localeCompare(right));
917
+ return names;
918
+ }
889
919
  async function writeDockerBuildFiles(options) {
890
920
  if (!/^sha256:[0-9a-f]{64}$/u.test(options.nodeImage.digest) ||
891
921
  !/^node:24\.17\.0(?:-[a-z0-9][a-z0-9._-]*)?$/u.test(options.nodeImage.reference)) {
@@ -897,10 +927,18 @@ async function writeDockerBuildFiles(options) {
897
927
  });
898
928
  }
899
929
  const image = `${options.nodeImage.reference}@${options.nodeImage.digest}`;
930
+ const installSources = ["package.json", "pnpm-lock.yaml"];
931
+ if ((await lstat(join(options.snapshotRoot, "pnpm-workspace.yaml"))
932
+ .catch(() => undefined))?.isFile() === true) {
933
+ installSources.push("pnpm-workspace.yaml");
934
+ }
935
+ const runtimeSourceCopies = (await eveSnapshotSourceEntries(options.snapshotRoot))
936
+ .map((name) => `COPY --from=builder ${JSON.stringify([`/workspace/${name}`, `/app/${name}`])}`)
937
+ .join("\n");
900
938
  const dockerfile = `# syntax=docker/dockerfile:1
901
939
  FROM --platform=linux/amd64 ${image} AS builder
902
940
  WORKDIR /workspace
903
- COPY package.json pnpm-lock.yaml ./
941
+ COPY ${JSON.stringify([...installSources, "./"])}
904
942
  RUN corepack enable \\
905
943
  && corepack prepare pnpm@${options.packageManagerVersion} --activate \\
906
944
  && test "$(corepack pnpm --version)" = "${options.packageManagerVersion}" \\
@@ -924,6 +962,7 @@ ENV HOST=0.0.0.0 \\
924
962
  NITRO_PORT=8080 \\
925
963
  NODE_ENV=production
926
964
  COPY --from=builder /workspace/.output /app/.output
965
+ ${runtimeSourceCopies}
927
966
  COPY --from=runtime-deps /workspace/node_modules /app/node_modules
928
967
  EXPOSE 8080
929
968
  ENTRYPOINT ["./node_modules/.bin/eve", "start", "--host", "0.0.0.0", "--port", "8080"]
@@ -1257,6 +1296,80 @@ function safeError(error) {
1257
1296
  remediation: error.remediation,
1258
1297
  };
1259
1298
  }
1299
+ /**
1300
+ * The reported BuildKit step decides the classification: an echoed
1301
+ * `RUN corepack pnpm install --frozen-lockfile` line from an earlier cached
1302
+ * step must not relabel a failing `eve build` step as a pnpm install failure.
1303
+ */
1304
+ function failingDockerStepCommand(stderr) {
1305
+ let command;
1306
+ let position = -1;
1307
+ for (const pattern of [
1308
+ /process "([^"]+)" did not complete/gu,
1309
+ /executor failed running \[([^\]]+)\]/gu,
1310
+ /The command '([^']+)' returned a non-zero code/gu,
1311
+ ]) {
1312
+ for (const match of stderr.matchAll(pattern)) {
1313
+ if (match.index >= position) {
1314
+ position = match.index;
1315
+ command = match[1];
1316
+ }
1317
+ }
1318
+ }
1319
+ return command;
1320
+ }
1321
+ function classifyPnpmInstallFailure(stderr) {
1322
+ if (/ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION/u.test(stderr)) {
1323
+ return new EvePackagingError({
1324
+ code: "DEPENDENCY_AMBIGUITY",
1325
+ subject: "ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION",
1326
+ reason: "The isolated pinned pnpm install rejected a dependency that is newer than the project's minimumReleaseAge policy.",
1327
+ remediation: "Wait until the release ages past the minimumReleaseAge cutoff, or name the exact pin in minimumReleaseAgeExclude inside the project pnpm-workspace.yaml.",
1328
+ });
1329
+ }
1330
+ if (/ERR_PNPM_(?:OUTDATED_LOCKFILE|LOCKFILE_MISSING_DEPENDENCY)/u.test(stderr)) {
1331
+ return new EvePackagingError({
1332
+ code: "DEPENDENCY_AMBIGUITY",
1333
+ subject: "frozen pnpm install",
1334
+ reason: "The isolated pinned pnpm install could not complete without changing or bypassing the lockfile.",
1335
+ remediation: "Regenerate pnpm-lock.yaml with the declared pnpm version and retry without relaxing frozen mode.",
1336
+ });
1337
+ }
1338
+ if (/ERR_PNPM_IGNORED_BUILDS/u.test(stderr)) {
1339
+ return new EvePackagingError({
1340
+ code: "DEPENDENCY_AMBIGUITY",
1341
+ subject: "ERR_PNPM_IGNORED_BUILDS",
1342
+ reason: "The isolated pinned pnpm install refused dependency build scripts that the project pnpm policy neither allows nor ignores.",
1343
+ remediation: "Name the dependency under allowBuilds or ignoredBuiltDependencies in the project pnpm-workspace.yaml and retry.",
1344
+ });
1345
+ }
1346
+ const pnpmErrorCode = /ERR_PNPM_[A-Z0-9_]+/u.exec(stderr)?.[0];
1347
+ if (pnpmErrorCode !== undefined) {
1348
+ return new EvePackagingError({
1349
+ code: "DEPENDENCY_AMBIGUITY",
1350
+ subject: pnpmErrorCode,
1351
+ reason: `The isolated pinned pnpm install failed with ${pnpmErrorCode}.`,
1352
+ remediation: "Fix the pnpm failure reported by the isolated build without relaxing the frozen lockfile or the declared pnpm pin, then retry.",
1353
+ });
1354
+ }
1355
+ return undefined;
1356
+ }
1357
+ function classifyEveBuildFailure(stderr) {
1358
+ if (/\bjust-bash\b/u.test(stderr)) {
1359
+ return new EvePackagingError({
1360
+ code: "EVE_BUILD_FAILED",
1361
+ subject: "eve build",
1362
+ reason: "The project-local Eve build failed inside the isolated Linux/amd64 builder because Eve's default sandbox needs just-bash where no Docker daemon or /dev/kvm exists.",
1363
+ remediation: "Declare just-bash as a production dependency of the Eve project, regenerate pnpm-lock.yaml, and retry.",
1364
+ });
1365
+ }
1366
+ return new EvePackagingError({
1367
+ code: "EVE_BUILD_FAILED",
1368
+ subject: "eve build",
1369
+ reason: "The project-local Eve build failed inside the isolated Linux/amd64 builder.",
1370
+ remediation: "Fix the project-local Eve build and retry without changing the authored project during packaging.",
1371
+ });
1372
+ }
1260
1373
  function classifyDockerBuildFailure(error) {
1261
1374
  const stderr = typeof error === "object" &&
1262
1375
  error !== null &&
@@ -1264,13 +1377,38 @@ function classifyDockerBuildFailure(error) {
1264
1377
  typeof error.stderr === "string"
1265
1378
  ? error.stderr
1266
1379
  : "";
1267
- if (/ERR_PNPM_(?:OUTDATED_LOCKFILE|LOCKFILE_MISSING_DEPENDENCY)/u.test(stderr) ||
1268
- /frozen-lockfile|pnpm --version/u.test(stderr)) {
1380
+ const step = failingDockerStepCommand(stderr);
1381
+ if (step !== undefined) {
1382
+ if (/\beve\b/u.test(step) && /\bbuild\b/u.test(step)) {
1383
+ return classifyEveBuildFailure(stderr);
1384
+ }
1385
+ if (/pnpm|corepack/u.test(step)) {
1386
+ const pnpmFailure = classifyPnpmInstallFailure(stderr);
1387
+ if (pnpmFailure !== undefined)
1388
+ return pnpmFailure;
1389
+ return new EvePackagingError({
1390
+ code: "DEPENDENCY_AMBIGUITY",
1391
+ subject: "frozen pnpm install",
1392
+ reason: "The isolated pinned pnpm toolchain or install step did not complete inside the builder.",
1393
+ remediation: "Keep the declared exact pnpm version and the frozen lockfile; fix the reported toolchain or install failure and retry.",
1394
+ });
1395
+ }
1396
+ }
1397
+ const pnpmFailure = classifyPnpmInstallFailure(stderr);
1398
+ if (pnpmFailure !== undefined)
1399
+ return pnpmFailure;
1400
+ if (/\bjust-bash\b|Cannot find package/u.test(stderr)) {
1401
+ return classifyEveBuildFailure(stderr);
1402
+ }
1403
+ if (/eve build/u.test(stderr)) {
1404
+ return classifyEveBuildFailure(stderr);
1405
+ }
1406
+ if (/frozen-lockfile|pnpm --version/u.test(stderr)) {
1269
1407
  return new EvePackagingError({
1270
1408
  code: "DEPENDENCY_AMBIGUITY",
1271
1409
  subject: "frozen pnpm install",
1272
- reason: "The isolated pinned pnpm install could not complete without changing or bypassing the lockfile.",
1273
- remediation: "Regenerate pnpm-lock.yaml with the declared pnpm version and retry without relaxing frozen mode.",
1410
+ reason: "The isolated pinned pnpm toolchain or install step did not complete inside the builder.",
1411
+ remediation: "Keep the declared exact pnpm version and the frozen lockfile; fix the reported toolchain or install failure and retry.",
1274
1412
  });
1275
1413
  }
1276
1414
  if (/node_modules\/\.bin\/eve|test -x/u.test(stderr)) {
@@ -1281,14 +1419,6 @@ function classifyDockerBuildFailure(error) {
1281
1419
  remediation: "Declare Eve as a project dependency and retry with the frozen lockfile.",
1282
1420
  });
1283
1421
  }
1284
- if (/eve build/u.test(stderr)) {
1285
- return new EvePackagingError({
1286
- code: "EVE_BUILD_FAILED",
1287
- subject: "eve build",
1288
- reason: "The project-local Eve build failed inside the isolated Linux/amd64 builder.",
1289
- remediation: "Fix the project-local Eve build and retry without changing the authored project during packaging.",
1290
- });
1291
- }
1292
1422
  return new EvePackagingError({
1293
1423
  code: "DOCKER_PLATFORM_BLOCKED",
1294
1424
  subject: "Docker/OrbStack",
@@ -1,4 +1,4 @@
1
- export declare const EVE_RESERVED_HOST_VARIABLES: readonly ["HOST", "NITRO_HOST", "PORT", "NITRO_PORT", "NODE_ENV", "WORKFLOW_LOCAL_BASE_URL", "EDEN_EVE_DEPLOYMENT_ID", "EDEN_EVE_GENERATION_ID"];
1
+ export declare const EVE_RESERVED_HOST_VARIABLES: readonly ["HOST", "NITRO_HOST", "PORT", "NITRO_PORT", "NODE_ENV", "NODE_EXTRA_CA_CERTS", "WORKFLOW_LOCAL_BASE_URL", "EDEN_EVE_DEPLOYMENT_ID", "EDEN_EVE_GENERATION_ID"];
2
2
  export type EveReservedHostVariable = (typeof EVE_RESERVED_HOST_VARIABLES)[number];
3
3
  export declare const EVE_START_COMMAND: readonly ["./node_modules/.bin/eve", "start", "--host", "0.0.0.0", "--port", "8080"];
4
4
  type EveRuntimeValueMap = Readonly<Record<string, string>>;
@@ -7,6 +7,7 @@ export const EVE_RESERVED_HOST_VARIABLES = Object.freeze([
7
7
  "PORT",
8
8
  "NITRO_PORT",
9
9
  "NODE_ENV",
10
+ "NODE_EXTRA_CA_CERTS",
10
11
  "WORKFLOW_LOCAL_BASE_URL",
11
12
  "EDEN_EVE_DEPLOYMENT_ID",
12
13
  "EDEN_EVE_GENERATION_ID",
@@ -3,7 +3,7 @@ import { execFile, spawn, } from "node:child_process";
3
3
  import { cp, lstat, mkdir, readFile, readlink, readdir, realpath, rm, symlink, writeFile, } from "node:fs/promises";
4
4
  import { dirname, join, relative, resolve, } from "node:path";
5
5
  import { promisify, } from "node:util";
6
- import { EvePackagingError, } from "./eve-packaging.js";
6
+ import { EvePackagingError, eveSnapshotSourceEntries, } from "./eve-packaging.js";
7
7
  const execFileAsync = promisify(execFile);
8
8
  const START_COMMAND = [
9
9
  "./node_modules/.bin/eve",
@@ -239,7 +239,6 @@ async function captureRuntimeTree(root, snapshotRoot, label) {
239
239
  }
240
240
  };
241
241
  await visit(canonicalRoot);
242
- files.sort((left, right) => left.path.localeCompare(right.path));
243
242
  nativeModules.sort((left, right) => left.path.localeCompare(right.path));
244
243
  return {
245
244
  files,
@@ -421,8 +420,10 @@ async function writeRuntimeContext(candidate, nodeImage, closure) {
421
420
  try {
422
421
  await cp(join(candidate.snapshotRoot, ".output"), join(runtimeContextPath, ".output"), { recursive: true });
423
422
  await cp(join(candidate.snapshotRoot, "node_modules"), join(runtimeContextPath, "node_modules"), { recursive: true });
424
- await cp(join(candidate.snapshotRoot, "agent"), join(runtimeContextPath, "agent"), { recursive: true });
425
- await cp(join(candidate.snapshotRoot, "package.json"), join(runtimeContextPath, "package.json"));
423
+ const sourceEntries = await eveSnapshotSourceEntries(candidate.snapshotRoot);
424
+ for (const entry of sourceEntries) {
425
+ await cp(join(candidate.snapshotRoot, entry), join(runtimeContextPath, entry), { recursive: true });
426
+ }
426
427
  await rewriteContextLinks(join(candidate.snapshotRoot, "node_modules"), join(runtimeContextPath, "node_modules"));
427
428
  await rewriteContextLinks(join(candidate.snapshotRoot, ".output"), join(runtimeContextPath, ".output"));
428
429
  const copiedOutput = await captureRuntimeTree(join(runtimeContextPath, ".output"), runtimeContextPath, "output");
@@ -431,13 +432,15 @@ async function writeRuntimeContext(candidate, nodeImage, closure) {
431
432
  copiedDependencies.digest !== closure.dependencyDigest) {
432
433
  throw packagingError("SOURCE_RACE", "runtime context", "The copied runtime context does not match the validated immutable closure.", "Discard the mixed-generation runtime context and rebuild from a quiescent candidate.");
433
434
  }
435
+ const sourceCopyLines = sourceEntries
436
+ .map((entry) => `COPY --from=candidate ${JSON.stringify([`/candidate/${entry}`, `/workspace/${entry}`])}`)
437
+ .join("\n");
434
438
  const dockerfile = `# syntax=docker/dockerfile:1
435
439
  FROM --platform=linux/amd64 ${imageReference(nodeImage)} AS candidate
436
- WORKDIR /workspace
437
- COPY .output /workspace/.output
438
- COPY node_modules /workspace/node_modules
439
- COPY agent /workspace/agent
440
- COPY package.json /workspace/package.json
440
+ WORKDIR /candidate
441
+ COPY .output /candidate/.output
442
+ COPY node_modules /candidate/node_modules
443
+ ${sourceEntries.map((entry) => `COPY ${JSON.stringify([entry, `/candidate/${entry}`])}`).join("\n")}
441
444
 
442
445
  FROM --platform=linux/amd64 ${imageReference(nodeImage)} AS runtime
443
446
  WORKDIR /workspace
@@ -446,10 +449,9 @@ ENV HOST=0.0.0.0 \\
446
449
  PORT=8080 \\
447
450
  NITRO_PORT=8080 \\
448
451
  NODE_ENV=production
449
- COPY --from=candidate /workspace/.output /workspace/.output
450
- COPY --from=candidate /workspace/node_modules /workspace/node_modules
451
- COPY --from=candidate /workspace/agent /workspace/agent
452
- COPY --from=candidate /workspace/package.json /workspace/package.json
452
+ COPY --from=candidate /candidate/.output /workspace/.output
453
+ COPY --from=candidate /candidate/node_modules /workspace/node_modules
454
+ ${sourceCopyLines}
453
455
  EXPOSE 8080
454
456
  ENTRYPOINT ["./node_modules/.bin/eve", "start", "--host", "0.0.0.0", "--port", "8080"]
455
457
  `;
package/dist/eve.d.ts CHANGED
@@ -6,8 +6,14 @@ export interface EveCliInvocation {
6
6
  readonly command: EveCliCommand;
7
7
  readonly projectRoot: string;
8
8
  readonly environment: EveCliEnvironment;
9
- readonly name: string;
9
+ /**
10
+ * Absent only on preflight/deploy preview invocations; index.ts derives a
11
+ * deterministic Worker name from the project before execution.
12
+ */
13
+ readonly name?: string;
10
14
  readonly envFile?: string;
15
+ /** When true the command prints the machine-readable result object. */
16
+ readonly json?: boolean;
11
17
  }
12
18
  export interface EveCliHelp {
13
19
  readonly kind: "help";
@@ -25,6 +31,8 @@ export interface EveCliExecutionRequest {
25
31
  * layer is the only owner allowed to open or parse its contents.
26
32
  */
27
33
  readonly envFile?: string;
34
+ /** When true the command prints the machine-readable result object. */
35
+ readonly json?: boolean;
28
36
  }
29
37
  export type EveCliRunner = (request: EveCliExecutionRequest) => void | Promise<void>;
30
38
  export interface EveCliDiagnostic {
@@ -43,7 +51,14 @@ export declare class EveCliError extends Error {
43
51
  readonly diagnostics?: readonly EveCliDiagnostic[];
44
52
  });
45
53
  }
46
- export declare const EVE_USAGE = "Usage: eden <preflight|deploy|destroy> [options]\n\nEden Deploy commands:\n preflight Build and inspect a local Eve candidate without remote mutation\n deploy Deploy the selected Eve project to the exact named target\n destroy Remove the exact owned Eve target\n\nEvery Deploy command requires --project, --env, and --name.\n--env accepts only preview or production.\n--env-file is accepted only by preflight and deploy.\nEden Agent authoring remains available through the separate eden agent namespace.\n\nOptions:\n --help Show this help\n";
54
+ export declare const EVE_USAGE = "Usage: eden <preflight|deploy|destroy> [options]\n\nEden Deploy commands:\n preflight Build and inspect a local Eve candidate without remote mutation\n deploy Deploy the selected Eve project to the exact named target\n destroy Remove the exact owned Eve target\n\nWithout flags, preflight and deploy use the current directory, the preview\nenvironment, and a Worker name derived from the project's package.json name.\n--env accepts only preview or production; production and destroy always\nrequire an explicit --name. --env-file is accepted only by preflight and deploy.\nAll three commands print a human summary by default; --json prints the\nmachine-readable result object for scripts and CI.\n\nOptions:\n --help Show this help\n";
47
55
  export declare function eveHelpText(scope: EveCliHelp["scope"]): string;
48
56
  export declare function parseEveArguments(args: readonly string[]): ParsedEveInvocation;
57
+ /**
58
+ * Derive a stable, collision-safe Cloudflare Worker name from a raw package
59
+ * name. The sanitized slug is combined with a short digest of the raw name so
60
+ * distinct package names that collide after sanitization stay distinct. The
61
+ * result is deterministic across runs and always satisfies EVE_NAME_PATTERN.
62
+ */
63
+ export declare function deriveEveTargetName(rawPackageName: string): string;
49
64
  //# sourceMappingURL=eve.d.ts.map
package/dist/eve.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  export const EVE_CLI_COMMANDS = [
2
3
  "preflight",
3
4
  "deploy",
@@ -23,45 +24,50 @@ Eden Deploy commands:
23
24
  deploy Deploy the selected Eve project to the exact named target
24
25
  destroy Remove the exact owned Eve target
25
26
 
26
- Every Deploy command requires --project, --env, and --name.
27
- --env accepts only preview or production.
28
- --env-file is accepted only by preflight and deploy.
29
- Eden Agent authoring remains available through the separate eden agent namespace.
27
+ Without flags, preflight and deploy use the current directory, the preview
28
+ environment, and a Worker name derived from the project's package.json name.
29
+ --env accepts only preview or production; production and destroy always
30
+ require an explicit --name. --env-file is accepted only by preflight and deploy.
31
+ All three commands print a human summary by default; --json prints the
32
+ machine-readable result object for scripts and CI.
30
33
 
31
34
  Options:
32
35
  --help Show this help
33
36
  `;
34
37
  const EVE_COMMAND_USAGE = {
35
- preflight: `Usage: eden preflight --project <path> --env <preview|production> --name <name> [--env-file <path>]
38
+ preflight: `Usage: eden preflight [--project <path>] [--env <preview|production>] [--name <name>] [--env-file <path>] [--json]
36
39
 
37
40
  Build and inspect an immutable local Eve candidate. Preflight is read-only toward remote resources.
38
41
 
39
42
  Options:
40
- --project <path> Required canonical Eve project root
41
- --env <environment> Required preview or production target
42
- --name <name> Required exact target name
43
+ --project <path> Eve project root (default: current directory)
44
+ --env <environment> preview or production target (default: preview)
45
+ --name <name> Exact target name (default: derived from the project's package.json; required for production)
43
46
  --env-file <path> Optional opaque runtime environment file
47
+ --json Print the machine-readable result object (default: human summary)
44
48
  --help Show this help
45
49
  `,
46
- deploy: `Usage: eden deploy --project <path> --env <preview|production> --name <name> [--env-file <path>]
50
+ deploy: `Usage: eden deploy [--project <path>] [--env <preview|production>] [--name <name>] [--env-file <path>] [--json]
47
51
 
48
52
  Deploy the selected Eve project to one exact target after host checks pass.
49
53
 
50
54
  Options:
51
- --project <path> Required canonical Eve project root
52
- --env <environment> Required preview or production target
53
- --name <name> Required exact target name
55
+ --project <path> Eve project root (default: current directory)
56
+ --env <environment> preview or production target (default: preview)
57
+ --name <name> Exact target name (default: derived from the project's package.json; required for production)
54
58
  --env-file <path> Optional opaque runtime environment file
59
+ --json Print the machine-readable result object (default: human summary)
55
60
  --help Show this help
56
61
  `,
57
- destroy: `Usage: eden destroy --project <path> --env <preview|production> --name <name>
62
+ destroy: `Usage: eden destroy --name <name> [--project <path>] [--env <preview|production>] [--json]
58
63
 
59
64
  Destroy only the exact owned Eve target after ownership verification.
60
65
 
61
66
  Options:
62
- --project <path> Required canonical Eve project root
63
- --env <environment> Required preview or production target
64
67
  --name <name> Required exact target name
68
+ --project <path> Eve project root (default: current directory)
69
+ --env <environment> preview or production target (default: preview)
70
+ --json Print the machine-readable result object (default: human summary)
65
71
  --help Show this help
66
72
  `,
67
73
  };
@@ -126,6 +132,7 @@ export function parseEveArguments(args) {
126
132
  let name;
127
133
  let envFile;
128
134
  let help = false;
135
+ let json = false;
129
136
  for (let index = 1; index < args.length; index += 1) {
130
137
  const argument = args[index];
131
138
  if (argument === undefined) {
@@ -205,6 +212,10 @@ export function parseEveArguments(args) {
205
212
  envFile = parseEnvFileValue(argument.slice("--env-file=".length));
206
213
  continue;
207
214
  }
215
+ if (argument === "--json") {
216
+ json = true;
217
+ continue;
218
+ }
208
219
  if (argument.startsWith("-")) {
209
220
  throw eveError("EVE_OPTION_UNKNOWN", "The Eve command contains an unknown option.");
210
221
  }
@@ -213,22 +224,45 @@ export function parseEveArguments(args) {
213
224
  if (help) {
214
225
  return { kind: "help", scope: command };
215
226
  }
216
- if (projectRoot === undefined) {
217
- throw eveError("EVE_PROJECT_REQUIRED", "The --project option is required for every Eve command.");
218
- }
219
- if (environment === undefined) {
220
- throw eveError("EVE_ENV_REQUIRED", "The --env option is required for every Eve command.");
221
- }
222
- if (name === undefined) {
223
- throw eveError("EVE_NAME_REQUIRED", "The --name option is required for every Eve command.");
227
+ const resolvedEnvironment = environment ?? "preview";
228
+ const resolvedProjectRoot = projectRoot ?? ".";
229
+ if (name === undefined &&
230
+ (command === "destroy" || resolvedEnvironment === "production")) {
231
+ throw eveError("EVE_NAME_REQUIRED", "An explicit --name is required for destroy and for production targets.");
224
232
  }
225
233
  return {
226
234
  kind: "invocation",
227
235
  command,
228
- projectRoot,
229
- environment,
230
- name,
236
+ projectRoot: resolvedProjectRoot,
237
+ environment: resolvedEnvironment,
238
+ ...(name === undefined ? {} : { name }),
231
239
  ...(envFile === undefined ? {} : { envFile }),
240
+ ...(json ? { json: true } : {}),
232
241
  };
233
242
  }
243
+ const WORKER_NAME_LIMIT = 63;
244
+ /**
245
+ * Derive a stable, collision-safe Cloudflare Worker name from a raw package
246
+ * name. The sanitized slug is combined with a short digest of the raw name so
247
+ * distinct package names that collide after sanitization stay distinct. The
248
+ * result is deterministic across runs and always satisfies EVE_NAME_PATTERN.
249
+ */
250
+ export function deriveEveTargetName(rawPackageName) {
251
+ const slug = rawPackageName
252
+ .toLowerCase()
253
+ .replace(/[^a-z0-9-]+/gu, "-")
254
+ .replace(/-{2,}/gu, "-")
255
+ .replace(/^-+|-+$/gu, "");
256
+ if (slug.length === 0) {
257
+ throw eveError("EVE_NAME_DERIVATION_FAILED", "A target name could not be derived from the project package.json; pass an explicit --name.");
258
+ }
259
+ const digest = createHash("sha256")
260
+ .update(rawPackageName, "utf8")
261
+ .digest("hex")
262
+ .slice(0, 8);
263
+ const stem = slug
264
+ .slice(0, WORKER_NAME_LIMIT - digest.length - 1)
265
+ .replace(/-+$/u, "");
266
+ return `${stem}-${digest}`;
267
+ }
234
268
  //# sourceMappingURL=eve.js.map