deepspace 0.17.0 → 0.18.0
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.
- package/CHANGELOG.md +145 -1
- package/README.md +18 -41
- package/dist/cli.js +1176 -1327
- package/dist/cli.js.map +1 -1
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/worker.js.map +1 -1
- package/features/documents/src/LibrarySidebar.tsx +6 -8
- package/features/documents/src/editor/useDocEditor.ts +3 -8
- package/features/landing/feature.json +1 -1
- package/features/messaging/src/components/chat/MessageItem.tsx +83 -34
- package/features/messaging/src/hooks/useChatChannel.ts +2 -8
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
defineCommand as defineCommand14,
|
|
6
|
+
runMain,
|
|
7
|
+
runCommand,
|
|
8
|
+
renderUsage
|
|
9
|
+
} from "citty";
|
|
5
10
|
import { sync as spawnSync8 } from "cross-spawn";
|
|
6
|
-
import { readFileSync as
|
|
11
|
+
import { readFileSync as readFileSync28, existsSync as existsSync25 } from "fs";
|
|
7
12
|
import { dirname as dirname11, join as join25 } from "path";
|
|
8
13
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9
14
|
|
|
@@ -260,6 +265,146 @@ function wrapCommandErrors(cmd) {
|
|
|
260
265
|
return cmd;
|
|
261
266
|
}
|
|
262
267
|
|
|
268
|
+
// src/cli/lib/command.ts
|
|
269
|
+
import { defineCommand, parseArgs } from "citty";
|
|
270
|
+
import * as p3 from "@clack/prompts";
|
|
271
|
+
var Refusal = class extends Error {
|
|
272
|
+
code;
|
|
273
|
+
action;
|
|
274
|
+
actionRequired;
|
|
275
|
+
extra;
|
|
276
|
+
constructor(message, code, opts = {}) {
|
|
277
|
+
super(message);
|
|
278
|
+
this.name = "Refusal";
|
|
279
|
+
this.code = code;
|
|
280
|
+
this.action = opts.action ? executableAction(opts.action) : void 0;
|
|
281
|
+
this.actionRequired = opts.actionRequired ?? false;
|
|
282
|
+
this.extra = opts.extra ?? {};
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
function apiErrorDetails(err) {
|
|
286
|
+
return err instanceof ApiError ? err.details : void 0;
|
|
287
|
+
}
|
|
288
|
+
function withoutReservedKeys(payload) {
|
|
289
|
+
return Object.fromEntries(
|
|
290
|
+
Object.entries(payload).filter(
|
|
291
|
+
([key]) => ![
|
|
292
|
+
"ok",
|
|
293
|
+
"code",
|
|
294
|
+
"error",
|
|
295
|
+
"action",
|
|
296
|
+
"actionRequired",
|
|
297
|
+
"next",
|
|
298
|
+
"nextAction",
|
|
299
|
+
"resume"
|
|
300
|
+
].includes(key)
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function exitWhenFlushed(code) {
|
|
305
|
+
if (process.stdout.writableLength === 0) process.exit(code);
|
|
306
|
+
process.stdout.write("", () => process.exit(code));
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
function nameVariants(name) {
|
|
310
|
+
return [
|
|
311
|
+
name,
|
|
312
|
+
name.replace(/-([a-z])/g, (_, c) => c.toUpperCase()),
|
|
313
|
+
name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
function unknownOptionNames(parsed, args) {
|
|
317
|
+
const known = /* @__PURE__ */ new Set(["_"]);
|
|
318
|
+
for (const [name, def] of Object.entries(args)) {
|
|
319
|
+
for (const variant of nameVariants(name)) known.add(variant);
|
|
320
|
+
const alias = def.alias;
|
|
321
|
+
for (const a of Array.isArray(alias) ? alias : alias ? [alias] : []) {
|
|
322
|
+
for (const variant of nameVariants(a)) known.add(variant);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return Object.keys(parsed).filter((key) => !known.has(key));
|
|
326
|
+
}
|
|
327
|
+
function unknownOptionNamesFromRaw(rawArgs2, args) {
|
|
328
|
+
const relaxedArgs = Object.fromEntries(
|
|
329
|
+
Object.entries(args).map(([name, definition]) => [
|
|
330
|
+
name,
|
|
331
|
+
definition.type === "enum" ? { ...definition, type: "string", required: false, options: void 0 } : { ...definition, required: false }
|
|
332
|
+
])
|
|
333
|
+
);
|
|
334
|
+
return unknownOptionNames(parseArgs(rawArgs2, relaxedArgs), args);
|
|
335
|
+
}
|
|
336
|
+
function unknownOptionMessage(commandName, unknown, args) {
|
|
337
|
+
const valid = Object.entries(args).filter(([, definition]) => definition.type !== "positional").map(([name]) => name).sort().join(", ");
|
|
338
|
+
return `Unknown ${unknown.length === 1 ? "option" : "options"}: ${unknown.map((name) => `--${name}`).join(", ")}. \`${commandName}\` accepts: ${valid || "(none)"}.`;
|
|
339
|
+
}
|
|
340
|
+
function defineDeepspaceCommand(def) {
|
|
341
|
+
const declaredArgs = {
|
|
342
|
+
...def.args ?? {},
|
|
343
|
+
json: {
|
|
344
|
+
type: "boolean",
|
|
345
|
+
description: "Emit a single-line JSON result for scripts/agents",
|
|
346
|
+
default: false
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
return defineCommand({
|
|
350
|
+
meta: def.meta,
|
|
351
|
+
args: declaredArgs,
|
|
352
|
+
async run({ args }) {
|
|
353
|
+
const json = Boolean(args.json);
|
|
354
|
+
let succeeded = false;
|
|
355
|
+
try {
|
|
356
|
+
const unknown = unknownOptionNames(args, declaredArgs);
|
|
357
|
+
if (unknown.length > 0) {
|
|
358
|
+
throw new Refusal(
|
|
359
|
+
unknownOptionMessage(def.meta.name, unknown, declaredArgs),
|
|
360
|
+
"unknown_option"
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
const out = await def.run({
|
|
364
|
+
args
|
|
365
|
+
}) ?? {};
|
|
366
|
+
if (out.action) out.action = executableAction(out.action);
|
|
367
|
+
if (json) {
|
|
368
|
+
console.log(
|
|
369
|
+
JSON.stringify({
|
|
370
|
+
ok: true,
|
|
371
|
+
...withoutReservedKeys(out.data ?? {}),
|
|
372
|
+
...out.action ? { action: out.action } : {}
|
|
373
|
+
})
|
|
374
|
+
);
|
|
375
|
+
} else if (out.action) {
|
|
376
|
+
printAction(out.action);
|
|
377
|
+
}
|
|
378
|
+
succeeded = true;
|
|
379
|
+
} catch (err) {
|
|
380
|
+
stopActiveSpinner();
|
|
381
|
+
const refusal = err instanceof Refusal ? err : null;
|
|
382
|
+
const code = refusal?.code ?? errorCode(err);
|
|
383
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
384
|
+
if (json) {
|
|
385
|
+
console.log(
|
|
386
|
+
JSON.stringify({
|
|
387
|
+
ok: false,
|
|
388
|
+
...code ? { code } : {},
|
|
389
|
+
...refusal?.actionRequired ? { actionRequired: true } : {},
|
|
390
|
+
error: message,
|
|
391
|
+
...refusal?.action ? { action: refusal.action } : {},
|
|
392
|
+
...withoutReservedKeys(refusal?.extra ?? apiErrorDetails(err) ?? {})
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
} else {
|
|
396
|
+
const line = code ? withSlug(message, code) : message;
|
|
397
|
+
if (refusal?.actionRequired) p3.log.warn(line);
|
|
398
|
+
else p3.log.error(line);
|
|
399
|
+
if (refusal?.action) printAction(refusal.action);
|
|
400
|
+
}
|
|
401
|
+
exitWhenFlushed(refusal?.actionRequired ? 2 : 1);
|
|
402
|
+
}
|
|
403
|
+
if (succeeded) exitWhenFlushed(0);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
263
408
|
// src/cli/lib/command-suggestions.ts
|
|
264
409
|
function splitTokenPath(token, table) {
|
|
265
410
|
const parts = token.trim().split(/\s+/);
|
|
@@ -398,105 +543,6 @@ async function exchangeSession(authUrl, sessionToken) {
|
|
|
398
543
|
return data.token ?? null;
|
|
399
544
|
}
|
|
400
545
|
|
|
401
|
-
// src/cli/lib/command.ts
|
|
402
|
-
import { defineCommand } from "citty";
|
|
403
|
-
import * as p3 from "@clack/prompts";
|
|
404
|
-
var Refusal = class extends Error {
|
|
405
|
-
code;
|
|
406
|
-
action;
|
|
407
|
-
actionRequired;
|
|
408
|
-
extra;
|
|
409
|
-
constructor(message, code, opts = {}) {
|
|
410
|
-
super(message);
|
|
411
|
-
this.name = "Refusal";
|
|
412
|
-
this.code = code;
|
|
413
|
-
this.action = opts.action ? executableAction(opts.action) : void 0;
|
|
414
|
-
this.actionRequired = opts.actionRequired ?? false;
|
|
415
|
-
this.extra = opts.extra ?? {};
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
function apiErrorDetails(err) {
|
|
419
|
-
return err instanceof ApiError ? err.details : void 0;
|
|
420
|
-
}
|
|
421
|
-
function withoutReservedKeys(payload) {
|
|
422
|
-
return Object.fromEntries(
|
|
423
|
-
Object.entries(payload).filter(
|
|
424
|
-
([key]) => ![
|
|
425
|
-
"ok",
|
|
426
|
-
"code",
|
|
427
|
-
"error",
|
|
428
|
-
"action",
|
|
429
|
-
"actionRequired",
|
|
430
|
-
"next",
|
|
431
|
-
"nextAction",
|
|
432
|
-
"resume"
|
|
433
|
-
].includes(key)
|
|
434
|
-
)
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
function exitWhenFlushed(code) {
|
|
438
|
-
if (process.stdout.writableLength === 0) process.exit(code);
|
|
439
|
-
process.stdout.write("", () => process.exit(code));
|
|
440
|
-
return void 0;
|
|
441
|
-
}
|
|
442
|
-
function defineDeepspaceCommand(def) {
|
|
443
|
-
return defineCommand({
|
|
444
|
-
meta: def.meta,
|
|
445
|
-
args: {
|
|
446
|
-
...def.args ?? {},
|
|
447
|
-
json: {
|
|
448
|
-
type: "boolean",
|
|
449
|
-
description: "Emit a single-line JSON result for scripts/agents",
|
|
450
|
-
default: false
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
async run({ args }) {
|
|
454
|
-
const json = Boolean(args.json);
|
|
455
|
-
let succeeded = false;
|
|
456
|
-
try {
|
|
457
|
-
const out = await def.run({ args }) ?? {};
|
|
458
|
-
if (out.action) out.action = executableAction(out.action);
|
|
459
|
-
if (json) {
|
|
460
|
-
console.log(
|
|
461
|
-
JSON.stringify({
|
|
462
|
-
ok: true,
|
|
463
|
-
...withoutReservedKeys(out.data ?? {}),
|
|
464
|
-
...out.action ? { action: out.action } : {}
|
|
465
|
-
})
|
|
466
|
-
);
|
|
467
|
-
} else if (out.action) {
|
|
468
|
-
printAction(out.action);
|
|
469
|
-
}
|
|
470
|
-
succeeded = true;
|
|
471
|
-
} catch (err) {
|
|
472
|
-
stopActiveSpinner();
|
|
473
|
-
const refusal = err instanceof Refusal ? err : null;
|
|
474
|
-
const code = refusal?.code ?? errorCode(err);
|
|
475
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
476
|
-
if (json) {
|
|
477
|
-
console.log(
|
|
478
|
-
JSON.stringify({
|
|
479
|
-
ok: false,
|
|
480
|
-
...code ? { code } : {},
|
|
481
|
-
...refusal?.actionRequired ? { actionRequired: true } : {},
|
|
482
|
-
error: message,
|
|
483
|
-
...refusal?.action ? { action: refusal.action } : {},
|
|
484
|
-
...withoutReservedKeys(refusal?.extra ?? apiErrorDetails(err) ?? {})
|
|
485
|
-
})
|
|
486
|
-
);
|
|
487
|
-
} else {
|
|
488
|
-
const line = code ? withSlug(message, code) : message;
|
|
489
|
-
if (refusal?.actionRequired) p3.log.warn(line);
|
|
490
|
-
else p3.log.error(line);
|
|
491
|
-
if (refusal?.action) printAction(refusal.action);
|
|
492
|
-
}
|
|
493
|
-
exitWhenFlushed(refusal?.actionRequired ? 2 : 1);
|
|
494
|
-
}
|
|
495
|
-
if (succeeded) exitWhenFlushed(0);
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
546
|
// src/cli/auth.ts
|
|
501
547
|
var AUTH_URL = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
502
548
|
var PROD_AUTH_URL = "https://auth.deep.space";
|
|
@@ -1377,9 +1423,6 @@ function resolveWorktreePort(appDir) {
|
|
|
1377
1423
|
return deriveWorktreePort(linked.worktreeRoot);
|
|
1378
1424
|
}
|
|
1379
1425
|
|
|
1380
|
-
// src/cli/lib/dev-vars.ts
|
|
1381
|
-
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
1382
|
-
|
|
1383
1426
|
// src/cli/lib/app-tokens.ts
|
|
1384
1427
|
async function fetchPublicKey(authUrl) {
|
|
1385
1428
|
const response = await fetch(`${authUrl}/api/auth/jwks`);
|
|
@@ -1429,1069 +1472,907 @@ async function fetchAppIdentityToken(deployUrl, callerJwt, appId) {
|
|
|
1429
1472
|
return body.token;
|
|
1430
1473
|
}
|
|
1431
1474
|
|
|
1432
|
-
// src/
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
"kv_namespace",
|
|
1440
|
-
"d1",
|
|
1441
|
-
"queue",
|
|
1442
|
-
"browser_rendering",
|
|
1443
|
-
"analytics_engine",
|
|
1444
|
-
"hyperdrive",
|
|
1445
|
-
"ratelimit"
|
|
1446
|
-
]);
|
|
1447
|
-
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
1448
|
-
"ASSETS",
|
|
1449
|
-
"PLATFORM_WORKER",
|
|
1450
|
-
"API_WORKER",
|
|
1451
|
-
"DEEPSPACE_APP_ID",
|
|
1452
|
-
"DEEPSPACE_RESOURCE_ID",
|
|
1453
|
-
"DEEPSPACE_RELEASE_OPERATION_ID",
|
|
1454
|
-
"APP_NAME",
|
|
1455
|
-
"OWNER_USER_ID",
|
|
1456
|
-
"AUTH_JWT_PUBLIC_KEY",
|
|
1457
|
-
"AUTH_JWT_ISSUER",
|
|
1458
|
-
"AUTH_WORKER_URL",
|
|
1459
|
-
"APP_IDENTITY_TOKEN",
|
|
1460
|
-
"APP_OWNER_JWT",
|
|
1461
|
-
"USAGE_EVENTS"
|
|
1462
|
-
]);
|
|
1463
|
-
function validateBindingManifest(manifest) {
|
|
1464
|
-
if (!Array.isArray(manifest)) {
|
|
1465
|
-
return { valid: false, errors: [{ reason: "Manifest must be an array" }] };
|
|
1475
|
+
// src/cli/lib/secure-file.ts
|
|
1476
|
+
import { chmodSync, writeFileSync as writeFileSync5 } from "fs";
|
|
1477
|
+
function writeSecretFileSync(path, content) {
|
|
1478
|
+
writeFileSync5(path, content, { mode: 384 });
|
|
1479
|
+
try {
|
|
1480
|
+
chmodSync(path, 384);
|
|
1481
|
+
} catch {
|
|
1466
1482
|
}
|
|
1467
|
-
const errors = [];
|
|
1468
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
1469
|
-
for (const entry of manifest) {
|
|
1470
|
-
if (!entry || typeof entry !== "object") {
|
|
1471
|
-
errors.push({
|
|
1472
|
-
reason: `Entry must be an object (got ${entry === null ? "null" : typeof entry})`
|
|
1473
|
-
});
|
|
1474
|
-
continue;
|
|
1475
|
-
}
|
|
1476
|
-
const e = entry;
|
|
1477
|
-
const type = e.type;
|
|
1478
|
-
const name = e.name;
|
|
1479
|
-
if (typeof type !== "string" || !ALLOWED_BINDING_TYPES.has(type)) {
|
|
1480
|
-
errors.push({
|
|
1481
|
-
reason: `Disallowed binding type: ${typeof type === "string" ? type : `(${typeof type})`}. Allowed: ${[...ALLOWED_BINDING_TYPES].sort().join(", ")}`
|
|
1482
|
-
});
|
|
1483
|
-
continue;
|
|
1484
|
-
}
|
|
1485
|
-
if (typeof name !== "string" || !name) {
|
|
1486
|
-
errors.push({ reason: `Binding of type "${type}" missing 'name' (string)` });
|
|
1487
|
-
continue;
|
|
1488
|
-
}
|
|
1489
|
-
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
1490
|
-
errors.push({ reason: `Binding name "${name}" is reserved by the SDK` });
|
|
1491
|
-
continue;
|
|
1492
|
-
}
|
|
1493
|
-
if (seenNames.has(name)) {
|
|
1494
|
-
errors.push({ reason: `Duplicate binding name "${name}"` });
|
|
1495
|
-
continue;
|
|
1496
|
-
}
|
|
1497
|
-
seenNames.add(name);
|
|
1498
|
-
const binding = entry;
|
|
1499
|
-
const fieldErr = requiredFieldError(binding);
|
|
1500
|
-
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
1501
|
-
}
|
|
1502
|
-
if (errors.length) return { valid: false, errors };
|
|
1503
|
-
return { valid: true, bindings: manifest };
|
|
1504
1483
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
case "kv_namespace": {
|
|
1522
|
-
if (!b.namespace_id)
|
|
1523
|
-
return `kv_namespace binding "${b.name}" missing namespace_id (or "auto")`;
|
|
1524
|
-
if (b.namespace_id === AUTO_PROVISION_SENTINEL && !b.title) {
|
|
1525
|
-
return `kv_namespace binding "${b.name}" with namespace_id="auto" requires "title"`;
|
|
1526
|
-
}
|
|
1527
|
-
return null;
|
|
1528
|
-
}
|
|
1529
|
-
case "d1": {
|
|
1530
|
-
if (!b.id) return `d1 binding "${b.name}" missing id (or "auto")`;
|
|
1531
|
-
if (b.id === AUTO_PROVISION_SENTINEL && !b.database_name) {
|
|
1532
|
-
return `d1 binding "${b.name}" with id="auto" requires "database_name"`;
|
|
1533
|
-
}
|
|
1534
|
-
return null;
|
|
1535
|
-
}
|
|
1536
|
-
case "queue":
|
|
1537
|
-
return b.queue_name ? null : `queue binding "${b.name}" missing queue_name (or "auto")`;
|
|
1538
|
-
case "hyperdrive":
|
|
1539
|
-
if (!b.id) return `hyperdrive binding "${b.name}" missing id`;
|
|
1540
|
-
if (b.id === AUTO_PROVISION_SENTINEL) {
|
|
1541
|
-
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
1542
|
-
}
|
|
1543
|
-
return null;
|
|
1544
|
-
case "ratelimit":
|
|
1545
|
-
if (!/^\d+$/u.test(b.namespace_id) || Number(b.namespace_id) <= 0) {
|
|
1546
|
-
return `ratelimit binding "${b.name}" requires a positive integer namespace_id string`;
|
|
1547
|
-
}
|
|
1548
|
-
if (!Number.isInteger(b.simple?.limit) || b.simple.limit <= 0) {
|
|
1549
|
-
return `ratelimit binding "${b.name}" requires simple.limit (positive integer)`;
|
|
1550
|
-
}
|
|
1551
|
-
if (b.simple.period !== 10 && b.simple.period !== 60) {
|
|
1552
|
-
return `ratelimit binding "${b.name}" requires simple.period (10 or 60)`;
|
|
1553
|
-
}
|
|
1554
|
-
return null;
|
|
1555
|
-
case "ai":
|
|
1556
|
-
case "browser_rendering":
|
|
1557
|
-
case "analytics_engine":
|
|
1558
|
-
return null;
|
|
1559
|
-
// no required fields beyond `name`
|
|
1560
|
-
default: {
|
|
1561
|
-
const _exhaustive = b;
|
|
1562
|
-
void _exhaustive;
|
|
1563
|
-
return null;
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
function bindingManifestFromOutputConfig(outputConfig) {
|
|
1568
|
-
const out = [];
|
|
1569
|
-
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
1570
|
-
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
1571
|
-
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
1572
|
-
const name = pluckString(v, "binding");
|
|
1573
|
-
const index_name = pluckString(v, "index_name");
|
|
1574
|
-
if (!name || !index_name) continue;
|
|
1575
|
-
const dimensions = pluckNumber(v, "dimensions");
|
|
1576
|
-
const metric = pluckString(v, "metric");
|
|
1577
|
-
out.push({
|
|
1578
|
-
type: "vectorize",
|
|
1579
|
-
name,
|
|
1580
|
-
index_name,
|
|
1581
|
-
...dimensions != null && { dimensions },
|
|
1582
|
-
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
1583
|
-
});
|
|
1584
|
-
}
|
|
1585
|
-
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
1586
|
-
const name = pluckString(r, "binding");
|
|
1587
|
-
const bucket_name = pluckString(r, "bucket_name");
|
|
1588
|
-
if (name && bucket_name) out.push({ type: "r2_bucket", name, bucket_name });
|
|
1589
|
-
}
|
|
1590
|
-
for (const k of asObjectArray(outputConfig.kv_namespaces)) {
|
|
1591
|
-
const name = pluckString(k, "binding");
|
|
1592
|
-
const namespace_id = pluckString(k, "id");
|
|
1593
|
-
if (!name || !namespace_id) continue;
|
|
1594
|
-
const title = pluckString(k, "title");
|
|
1595
|
-
out.push({
|
|
1596
|
-
type: "kv_namespace",
|
|
1597
|
-
name,
|
|
1598
|
-
namespace_id,
|
|
1599
|
-
...title && { title }
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
|
-
for (const d of asObjectArray(outputConfig.d1_databases)) {
|
|
1603
|
-
const name = pluckString(d, "binding");
|
|
1604
|
-
const id = pluckString(d, "database_id");
|
|
1605
|
-
if (!name || !id) continue;
|
|
1606
|
-
const database_name = pluckString(d, "database_name");
|
|
1607
|
-
out.push({
|
|
1608
|
-
type: "d1",
|
|
1609
|
-
name,
|
|
1610
|
-
id,
|
|
1611
|
-
...database_name && { database_name }
|
|
1612
|
-
});
|
|
1484
|
+
|
|
1485
|
+
// src/cli/lib/wrangler-env.ts
|
|
1486
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
1487
|
+
import { join as join7 } from "path";
|
|
1488
|
+
import { parse as parseToml3, stringify as stringifyToml } from "smol-toml";
|
|
1489
|
+
|
|
1490
|
+
// src/server/rooms/app-name.ts
|
|
1491
|
+
var APP_NAME_RULES = {
|
|
1492
|
+
/** ^[a-z0-9](-?[a-z0-9])+$ — RFC 1035 host label, no consecutive dashes. */
|
|
1493
|
+
pattern: /^[a-z0-9](?:-?[a-z0-9])+$/,
|
|
1494
|
+
minLength: 2,
|
|
1495
|
+
maxLength: 63
|
|
1496
|
+
};
|
|
1497
|
+
function validateAppName(raw) {
|
|
1498
|
+
if (typeof raw !== "string") {
|
|
1499
|
+
return { valid: false, reason: "App name must be a string" };
|
|
1613
1500
|
}
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const queue_name = pluckString(q, "queue");
|
|
1618
|
-
if (name && queue_name) out.push({ type: "queue", name, queue_name });
|
|
1501
|
+
const name = raw.trim();
|
|
1502
|
+
if (!name) {
|
|
1503
|
+
return { valid: false, reason: "App name is required" };
|
|
1619
1504
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
for (const a of asObjectArray(outputConfig.analytics_engine_datasets)) {
|
|
1623
|
-
const name = pluckString(a, "binding");
|
|
1624
|
-
const dataset = pluckString(a, "dataset");
|
|
1625
|
-
if (name) out.push({ type: "analytics_engine", name, dataset: dataset ?? void 0 });
|
|
1505
|
+
if (name.length < APP_NAME_RULES.minLength) {
|
|
1506
|
+
return { valid: false, reason: `App name "${name}" is too short (min ${APP_NAME_RULES.minLength} chars)` };
|
|
1626
1507
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
const id = pluckString(h, "id");
|
|
1630
|
-
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
1508
|
+
if (name.length > APP_NAME_RULES.maxLength) {
|
|
1509
|
+
return { valid: false, reason: `App name "${name}" is too long (max ${APP_NAME_RULES.maxLength} chars)` };
|
|
1631
1510
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const period = pluckNumber(simple, "period");
|
|
1638
|
-
if (!name || !namespace_id || limit === null || period !== 10 && period !== 60) continue;
|
|
1639
|
-
out.push({ type: "ratelimit", name, namespace_id, simple: { limit, period } });
|
|
1511
|
+
if (!APP_NAME_RULES.pattern.test(name)) {
|
|
1512
|
+
return {
|
|
1513
|
+
valid: false,
|
|
1514
|
+
reason: `App name "${name}" is invalid. Use lowercase letters, digits, and single hyphens only (must start + end with alphanumeric). Examples: my-app, search-v2, unison-search.`
|
|
1515
|
+
};
|
|
1640
1516
|
}
|
|
1641
|
-
return
|
|
1642
|
-
}
|
|
1643
|
-
function isObject(v) {
|
|
1644
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1645
|
-
}
|
|
1646
|
-
function pluckString(v, key) {
|
|
1647
|
-
if (!isObject(v)) return null;
|
|
1648
|
-
const val = v[key];
|
|
1649
|
-
return typeof val === "string" && val.length > 0 ? val : null;
|
|
1517
|
+
return { valid: true, name };
|
|
1650
1518
|
}
|
|
1651
|
-
function
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1519
|
+
function resolveAppName(raw) {
|
|
1520
|
+
const strict = validateAppName(raw);
|
|
1521
|
+
if (strict.valid) return { ok: true, name: strict.name };
|
|
1522
|
+
if (typeof raw !== "string") return { ok: false, reason: strict.reason };
|
|
1523
|
+
const trimmed = raw.trim();
|
|
1524
|
+
if (!trimmed) return { ok: false, reason: "App name is required" };
|
|
1525
|
+
const sanitized = sanitizeAppName(trimmed);
|
|
1526
|
+
const sanitizedStrict = validateAppName(sanitized);
|
|
1527
|
+
if (!sanitizedStrict.valid) {
|
|
1528
|
+
return {
|
|
1529
|
+
ok: false,
|
|
1530
|
+
reason: `App name "${trimmed}" can't be sanitized into a valid name (${sanitizedStrict.reason}). Pick something matching: lowercase letters, digits, single hyphens, ${APP_NAME_RULES.minLength}-${APP_NAME_RULES.maxLength} chars.`
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
return {
|
|
1534
|
+
ok: true,
|
|
1535
|
+
name: sanitized,
|
|
1536
|
+
warning: `App name "${trimmed}" doesn't conform to deploy rules \u2014 using "${sanitized}" instead. Update wrangler.toml's \`name\` to silence this warning.`
|
|
1537
|
+
};
|
|
1655
1538
|
}
|
|
1656
|
-
function
|
|
1657
|
-
|
|
1658
|
-
return v.filter(isObject);
|
|
1539
|
+
function sanitizeAppName(raw) {
|
|
1540
|
+
return raw.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
1659
1541
|
}
|
|
1660
1542
|
|
|
1661
|
-
// src/cli/lib/
|
|
1662
|
-
var
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
"
|
|
1666
|
-
"
|
|
1543
|
+
// src/cli/lib/wrangler-env.ts
|
|
1544
|
+
var NON_INHERITABLE_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
1545
|
+
"define",
|
|
1546
|
+
"vars",
|
|
1547
|
+
"durable_objects",
|
|
1548
|
+
"kv_namespaces",
|
|
1549
|
+
"r2_buckets",
|
|
1550
|
+
"ai_search_namespaces",
|
|
1551
|
+
"ai_search",
|
|
1552
|
+
"ratelimits",
|
|
1553
|
+
"vectorize",
|
|
1554
|
+
"services",
|
|
1555
|
+
"queues",
|
|
1556
|
+
"workflows",
|
|
1557
|
+
"tail_consumers",
|
|
1558
|
+
"secrets",
|
|
1559
|
+
"secrets_store_secrets"
|
|
1667
1560
|
]);
|
|
1668
|
-
var
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
if (!CONFIG_NAME_RE.test(configName)) {
|
|
1673
|
-
throw new Error(
|
|
1674
|
-
`Invalid config "${configName}". Use letters, numbers, underscores, or hyphens; start with a letter or number.`
|
|
1675
|
-
);
|
|
1676
|
-
}
|
|
1677
|
-
return configName;
|
|
1678
|
-
}
|
|
1679
|
-
function validateSecretName(name) {
|
|
1680
|
-
if (!SECRET_NAME_RE.test(name)) {
|
|
1681
|
-
throw new Error(
|
|
1682
|
-
`Invalid secret name "${name}". Use letters, numbers, and underscores; start with a letter or underscore.`
|
|
1683
|
-
);
|
|
1561
|
+
var generatedConfigSeq = 0;
|
|
1562
|
+
function resolveAppNameForEnv(config, envName) {
|
|
1563
|
+
if (!envName) {
|
|
1564
|
+
return resolveAppName(config.name);
|
|
1684
1565
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1566
|
+
const envBlock = config.env?.[envName];
|
|
1567
|
+
if (!envBlock) {
|
|
1568
|
+
return {
|
|
1569
|
+
ok: false,
|
|
1570
|
+
reason: `wrangler.toml: no [env.${envName}] block found. Add one with a distinct \`name\` to deploy this environment.`
|
|
1571
|
+
};
|
|
1689
1572
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1573
|
+
if (typeof envBlock.name !== "string" || envBlock.name.trim() === "") {
|
|
1574
|
+
return {
|
|
1575
|
+
ok: false,
|
|
1576
|
+
reason: `wrangler.toml: [env.${envName}].name is missing. Set a distinct app name (e.g. "${typeof config.name === "string" ? config.name : "myapp"}-${envName}") so this environment doesn't overwrite the default.`
|
|
1577
|
+
};
|
|
1695
1578
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
const res = await fetch(`${deployUrl}/api/secrets${path}`, {
|
|
1702
|
-
...init,
|
|
1703
|
-
headers: {
|
|
1704
|
-
Authorization: `Bearer ${token}`,
|
|
1705
|
-
"Content-Type": "application/json",
|
|
1706
|
-
...init.headers ?? {}
|
|
1707
|
-
}
|
|
1708
|
-
});
|
|
1709
|
-
const body = await res.json().catch(() => ({}));
|
|
1710
|
-
if (!res.ok) {
|
|
1711
|
-
const err = new Error(body.error ?? `Secrets request failed (${res.status})`);
|
|
1712
|
-
err.status = res.status;
|
|
1713
|
-
err.apiPath = path;
|
|
1714
|
-
throw err;
|
|
1579
|
+
if (envBlock.name === config.name) {
|
|
1580
|
+
return {
|
|
1581
|
+
ok: false,
|
|
1582
|
+
reason: `wrangler.toml: [env.${envName}].name "${envBlock.name}" is the same as the top-level name. Pick a distinct name (e.g. "${config.name}-${envName}") so deploys don't collide.`
|
|
1583
|
+
};
|
|
1715
1584
|
}
|
|
1716
|
-
return
|
|
1717
|
-
}
|
|
1718
|
-
function listSecrets(deployUrl, token, appId, configName) {
|
|
1719
|
-
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`);
|
|
1720
|
-
}
|
|
1721
|
-
function fetchSecretsValues(deployUrl, token, appId, configName) {
|
|
1722
|
-
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}/values`);
|
|
1585
|
+
return resolveAppName(envBlock.name);
|
|
1723
1586
|
}
|
|
1724
|
-
function
|
|
1725
|
-
return
|
|
1726
|
-
deployUrl,
|
|
1727
|
-
token,
|
|
1728
|
-
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}?plain=true`
|
|
1729
|
-
);
|
|
1587
|
+
function devVarsPathFor(appDir) {
|
|
1588
|
+
return join7(appDir, ".dev.vars");
|
|
1730
1589
|
}
|
|
1731
|
-
function
|
|
1732
|
-
return
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1590
|
+
function prepareWranglerEnvConfig(appDir, envName) {
|
|
1591
|
+
if (!envName) return { cleanup() {
|
|
1592
|
+
} };
|
|
1593
|
+
const config = readWranglerConfig(appDir);
|
|
1594
|
+
const resolved = resolveAppNameForEnv(config, envName);
|
|
1595
|
+
if (!resolved.ok) throw new Error(resolved.reason);
|
|
1596
|
+
const flattened = flattenWranglerEnvConfig(config, envName);
|
|
1597
|
+
const configPath = join7(
|
|
1598
|
+
appDir,
|
|
1599
|
+
`.wrangler.deepspace.${safeGeneratedConfigSegment(envName)}.${process.pid}.${++generatedConfigSeq}.toml`
|
|
1737
1600
|
);
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1601
|
+
writeFileSync6(
|
|
1602
|
+
configPath,
|
|
1603
|
+
[
|
|
1604
|
+
"# Generated by DeepSpace for this CLI run.",
|
|
1605
|
+
"# Do not edit; wrangler.toml remains the source of truth.",
|
|
1606
|
+
stringifyToml(flattened)
|
|
1607
|
+
].join("\n").replace(/\s*$/u, "\n")
|
|
1745
1608
|
);
|
|
1609
|
+
return {
|
|
1610
|
+
configPath,
|
|
1611
|
+
cleanup() {
|
|
1612
|
+
rmSync2(configPath, { force: true });
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1746
1615
|
}
|
|
1747
|
-
function
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1616
|
+
function wranglerViteEnv(baseEnv, prepared, extraEnv = {}) {
|
|
1617
|
+
const env = { ...baseEnv, ...extraEnv };
|
|
1618
|
+
if (prepared.configPath) {
|
|
1619
|
+
delete env.CLOUDFLARE_ENV;
|
|
1620
|
+
env.CLOUDFLARE_VITE_WRANGLER_CONFIG_PATH = prepared.configPath;
|
|
1621
|
+
}
|
|
1622
|
+
return env;
|
|
1754
1623
|
}
|
|
1755
|
-
function
|
|
1756
|
-
|
|
1624
|
+
function flattenWranglerEnvConfig(config, envName) {
|
|
1625
|
+
const envRoot = asTomlRecord(config.env);
|
|
1626
|
+
const envBlock = asTomlRecord(envRoot?.[envName]);
|
|
1627
|
+
if (!envBlock) {
|
|
1628
|
+
throw new Error(`wrangler.toml: no [env.${envName}] block found.`);
|
|
1629
|
+
}
|
|
1630
|
+
const flattened = {};
|
|
1631
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1632
|
+
if (key === "env" || NON_INHERITABLE_ENV_KEYS.has(key)) continue;
|
|
1633
|
+
flattened[key] = value;
|
|
1634
|
+
}
|
|
1635
|
+
for (const [key, value] of Object.entries(envBlock)) {
|
|
1636
|
+
flattened[key] = value;
|
|
1637
|
+
}
|
|
1638
|
+
if (!("durable_objects" in envBlock) && config.durable_objects) {
|
|
1639
|
+
flattened.durable_objects = config.durable_objects;
|
|
1640
|
+
}
|
|
1641
|
+
const envVars = asTomlRecord(envBlock.vars) ?? {};
|
|
1642
|
+
if (!("APP_NAME" in envVars) && typeof envBlock.name === "string") {
|
|
1643
|
+
flattened.vars = { ...envVars, APP_NAME: envBlock.name };
|
|
1644
|
+
}
|
|
1645
|
+
return flattened;
|
|
1757
1646
|
}
|
|
1758
|
-
function
|
|
1759
|
-
return
|
|
1760
|
-
method: "POST",
|
|
1761
|
-
body: JSON.stringify({ name: configName, ...copyFrom ? { copyFrom } : {} })
|
|
1762
|
-
});
|
|
1647
|
+
function asTomlRecord(value) {
|
|
1648
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1763
1649
|
}
|
|
1764
|
-
function
|
|
1765
|
-
return
|
|
1766
|
-
method: "DELETE"
|
|
1767
|
-
});
|
|
1650
|
+
function safeGeneratedConfigSegment(value) {
|
|
1651
|
+
return value.replace(/[^A-Za-z0-9._-]/gu, "_");
|
|
1768
1652
|
}
|
|
1769
|
-
|
|
1653
|
+
var WranglerConfigError = class extends Error {
|
|
1654
|
+
constructor(path, cause) {
|
|
1655
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1656
|
+
super(`wrangler.toml: malformed TOML at ${path}: ${detail}`);
|
|
1657
|
+
this.path = path;
|
|
1658
|
+
this.cause = cause;
|
|
1659
|
+
this.name = "WranglerConfigError";
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
function readWranglerConfig(appDir) {
|
|
1663
|
+
const path = join7(appDir, "wrangler.toml");
|
|
1664
|
+
const raw = readFileSync9(path, "utf-8");
|
|
1770
1665
|
try {
|
|
1771
|
-
|
|
1772
|
-
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
1773
|
-
throw new ApiError(
|
|
1774
|
-
`The secrets service at ${deployUrl} returned an unexpected response shape \u2014 check DEEPSPACE_DEPLOY_URL (is this the service the app lives on?).`,
|
|
1775
|
-
200,
|
|
1776
|
-
"invalid_response",
|
|
1777
|
-
`/${appId}/configs/${configName}/values`
|
|
1778
|
-
);
|
|
1779
|
-
}
|
|
1780
|
-
return { appId, configName, values: secrets };
|
|
1666
|
+
return parseToml3(raw);
|
|
1781
1667
|
} catch (err) {
|
|
1782
|
-
|
|
1783
|
-
throw err;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
function quoteDotenvValue(value) {
|
|
1787
|
-
if (/^[A-Za-z0-9_./:@+-]*$/.test(value) && value !== "") return value;
|
|
1788
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
1789
|
-
return `"${escaped}"`;
|
|
1790
|
-
}
|
|
1791
|
-
function renderSecretsCache(secrets, source) {
|
|
1792
|
-
const keys = Object.keys(secrets).sort();
|
|
1793
|
-
const lines = [
|
|
1794
|
-
GENERATED_SECRETS_DIVIDER,
|
|
1795
|
-
`# app ${source.appId} \xB7 config ${source.configName}`,
|
|
1796
|
-
"# Use `npx deepspace secrets` to change these values."
|
|
1797
|
-
];
|
|
1798
|
-
for (const key of keys) {
|
|
1799
|
-
validateSecretValue(key, secrets[key]);
|
|
1800
|
-
lines.push(`${key}=${quoteDotenvValue(secrets[key])}`);
|
|
1668
|
+
throw new WranglerConfigError(path, err);
|
|
1801
1669
|
}
|
|
1802
|
-
return lines.join("\n");
|
|
1803
1670
|
}
|
|
1804
|
-
function
|
|
1805
|
-
|
|
1806
|
-
if (idx === -1) return content;
|
|
1807
|
-
return content.slice(0, idx).trimEnd() + "\n";
|
|
1671
|
+
function hasWranglerConfig(appDir) {
|
|
1672
|
+
return existsSync6(join7(appDir, "wrangler.toml"));
|
|
1808
1673
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
out2[key] = value;
|
|
1823
|
-
}
|
|
1824
|
-
return out2;
|
|
1825
|
-
}
|
|
1826
|
-
const out = {};
|
|
1827
|
-
const lines = trimmed.split(/\r?\n/);
|
|
1828
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1829
|
-
const line = lines[i].trim();
|
|
1830
|
-
if (!line || line.startsWith("#")) continue;
|
|
1831
|
-
const eq = line.indexOf("=");
|
|
1832
|
-
if (eq <= 0) continue;
|
|
1833
|
-
const key = line.slice(0, eq).trim();
|
|
1834
|
-
if (!SECRET_NAME_RE.test(key)) continue;
|
|
1835
|
-
validateSecretName(key);
|
|
1836
|
-
const raw = line.slice(eq + 1).trim();
|
|
1837
|
-
const quote = raw[0] === '"' || raw[0] === "'" ? raw[0] : null;
|
|
1838
|
-
let value;
|
|
1839
|
-
if (!quote) {
|
|
1840
|
-
value = raw;
|
|
1841
|
-
} else if (endsWithUnescapedQuote(raw.slice(1), quote)) {
|
|
1842
|
-
const inner = raw.slice(1, -1);
|
|
1843
|
-
value = quote === '"' ? unescapeDotenv(inner) : inner;
|
|
1844
|
-
} else {
|
|
1845
|
-
const startLine = i + 1;
|
|
1846
|
-
const parts = [raw.slice(1)];
|
|
1847
|
-
let closed = false;
|
|
1848
|
-
while (++i < lines.length) {
|
|
1849
|
-
const cont = lines[i].trimEnd();
|
|
1850
|
-
if (endsWithUnescapedQuote(cont, quote)) {
|
|
1851
|
-
parts.push(cont.slice(0, -1));
|
|
1852
|
-
closed = true;
|
|
1853
|
-
break;
|
|
1854
|
-
}
|
|
1855
|
-
parts.push(lines[i]);
|
|
1856
|
-
}
|
|
1857
|
-
if (!closed) {
|
|
1858
|
-
throw new Error(
|
|
1859
|
-
`Unterminated quoted value for "${key}" (opens on line ${startLine}). Close the ${quote === '"' ? "double" : "single"} quote, or escape newlines as \\n on one line.`
|
|
1860
|
-
);
|
|
1861
|
-
}
|
|
1862
|
-
value = parts.join("\n");
|
|
1863
|
-
if (quote === '"') value = unescapeDotenv(value);
|
|
1864
|
-
}
|
|
1865
|
-
validateSecretValue(key, value);
|
|
1866
|
-
out[key] = value;
|
|
1867
|
-
}
|
|
1868
|
-
return out;
|
|
1674
|
+
|
|
1675
|
+
// src/cli/lib/dev-vars.ts
|
|
1676
|
+
var HEADER = [
|
|
1677
|
+
"# Generated by deepspace \u2014 do not edit; this file is rewritten on every",
|
|
1678
|
+
"# dev, test, deploy, and secrets pull run. App values live in the secret store:",
|
|
1679
|
+
"# npx deepspace secrets set KEY=value"
|
|
1680
|
+
].join("\n");
|
|
1681
|
+
function requireAppIdFor(appDir, wranglerEnv) {
|
|
1682
|
+
const id = readAppId(appDir, wranglerEnv);
|
|
1683
|
+
if (id) return id;
|
|
1684
|
+
throw new Error(
|
|
1685
|
+
"wrangler.toml has no DEEPSPACE_APP_ID" + (wranglerEnv ? ` for [env.${wranglerEnv}]` : "") + ". Run `deepspace app init` (or `deepspace app init --env <name>`) and retry."
|
|
1686
|
+
);
|
|
1869
1687
|
}
|
|
1870
|
-
function
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1688
|
+
async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {}) {
|
|
1689
|
+
const urls = PLATFORM_URLS;
|
|
1690
|
+
const publicKey = await fetchPublicKey(urls.auth);
|
|
1691
|
+
const appId = opts.appId ?? requireAppIdFor(appDir, wranglerEnv);
|
|
1692
|
+
const appOwnerJwt = await mintAppOwnerJwt(urls.auth, callerJwt, appId);
|
|
1693
|
+
const appIdentityToken = await fetchAppIdentityToken(urls.deploy, callerJwt, appId);
|
|
1694
|
+
const sdkVars = [
|
|
1695
|
+
`AUTH_JWT_PUBLIC_KEY="${publicKey}"`,
|
|
1696
|
+
`AUTH_JWT_ISSUER=${urls.auth}/api/auth`,
|
|
1697
|
+
`AUTH_WORKER_URL=${urls.auth}`,
|
|
1698
|
+
`API_WORKER_URL=${urls.api}`,
|
|
1699
|
+
`PLATFORM_WORKER_URL=${urls.platform}`,
|
|
1700
|
+
`OWNER_USER_ID=${ownerId}`,
|
|
1701
|
+
`APP_OWNER_JWT=${appOwnerJwt}`,
|
|
1702
|
+
...appIdentityToken ? [`APP_IDENTITY_TOKEN=${appIdentityToken}`] : [],
|
|
1703
|
+
`ALLOW_DEBUG_ROUTES=true`
|
|
1704
|
+
].join("\n");
|
|
1705
|
+
const devVarsPath = devVarsPathFor(appDir);
|
|
1706
|
+
const cache = opts.generatedSecretsCache;
|
|
1707
|
+
writeSecretFileSync(
|
|
1708
|
+
devVarsPath,
|
|
1709
|
+
[HEADER, "", sdkVars, ...cache ? ["", cache] : [], ""].join("\n")
|
|
1710
|
+
);
|
|
1876
1711
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1712
|
+
|
|
1713
|
+
// src/cli/lib/install-status.ts
|
|
1714
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, realpathSync as realpathSync3 } from "fs";
|
|
1715
|
+
import { dirname as dirname5, join as join9, resolve as resolve6 } from "path";
|
|
1716
|
+
|
|
1717
|
+
// src/cli/lib/package-manager.ts
|
|
1718
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
1719
|
+
import { join as join8 } from "path";
|
|
1720
|
+
function detectPackageManager(appDir) {
|
|
1721
|
+
try {
|
|
1722
|
+
const pkg2 = JSON.parse(readFileSync10(join8(appDir, "package.json"), "utf-8"));
|
|
1723
|
+
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
1724
|
+
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
1725
|
+
} catch {
|
|
1726
|
+
}
|
|
1727
|
+
if (existsSync7(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
1728
|
+
if (existsSync7(join8(appDir, "yarn.lock"))) return "yarn";
|
|
1729
|
+
if (existsSync7(join8(appDir, "bun.lock")) || existsSync7(join8(appDir, "bun.lockb"))) return "bun";
|
|
1730
|
+
return "npm";
|
|
1731
|
+
}
|
|
1732
|
+
function installCommand(appDir) {
|
|
1733
|
+
return `${detectPackageManager(appDir)} install`;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// src/cli/lib/install-status.ts
|
|
1737
|
+
function installState(appDir) {
|
|
1738
|
+
if (resolvesDeepspace(appDir)) return "ready";
|
|
1739
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
1740
|
+
if (existsSync8(join9(sentinelDir, "install.err"))) return "failed";
|
|
1741
|
+
if (existsSync8(join9(sentinelDir, "install.started"))) {
|
|
1742
|
+
if (!existsSync8(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
|
|
1743
|
+
return "installing";
|
|
1885
1744
|
}
|
|
1745
|
+
return "failed";
|
|
1886
1746
|
}
|
|
1887
|
-
return
|
|
1747
|
+
return "missing";
|
|
1888
1748
|
}
|
|
1889
|
-
function
|
|
1890
|
-
return `
|
|
1749
|
+
function tailHint(logPath, platform = process.platform) {
|
|
1750
|
+
return platform === "win32" ? `Get-Content -Wait ${logPath}` : `tail -f ${logPath}`;
|
|
1891
1751
|
}
|
|
1892
|
-
function
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1752
|
+
function ensureInstallReady(appDir) {
|
|
1753
|
+
if (resolvesDeepspace(appDir)) return;
|
|
1754
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
1755
|
+
const errPath = join9(sentinelDir, "install.err");
|
|
1756
|
+
const startedPath = join9(sentinelDir, "install.started");
|
|
1757
|
+
const donePath = join9(sentinelDir, "install.done");
|
|
1758
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
1759
|
+
if (existsSync8(errPath)) {
|
|
1760
|
+
const detail = readFileSync11(errPath, "utf-8").trimEnd();
|
|
1761
|
+
const log29 = existsSync8(logPath) ? ` Full log: ${logPath}.` : "";
|
|
1762
|
+
throw new Refusal(
|
|
1763
|
+
`Dependency install failed: ${detail}.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
1764
|
+
"install_failed",
|
|
1765
|
+
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
1766
|
+
);
|
|
1896
1767
|
}
|
|
1897
|
-
if (
|
|
1898
|
-
|
|
1768
|
+
if (existsSync8(startedPath) && !existsSync8(donePath)) {
|
|
1769
|
+
if (!installWorkerAlive(sentinelDir)) {
|
|
1770
|
+
const log29 = existsSync8(logPath) ? ` See what happened: ${logPath}.` : "";
|
|
1771
|
+
throw new Refusal(
|
|
1772
|
+
`The dependency install is no longer running and never finished.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
1773
|
+
"install_failed",
|
|
1774
|
+
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
const tail = existsSync8(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
1778
|
+
throw new Refusal(
|
|
1779
|
+
`Dependencies are still installing.${tail} Retry once it finishes.`,
|
|
1780
|
+
"install_in_progress"
|
|
1781
|
+
);
|
|
1899
1782
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
return {
|
|
1908
|
-
pulled,
|
|
1909
|
-
rendered: renderSecretsCache(pulled.values, pulled),
|
|
1910
|
-
// An empty store is the common case and not worth a status line.
|
|
1911
|
-
summary: count ? `Secrets: ${pulled.configName} (${count} secret${count === 1 ? "" : "s"})` : null
|
|
1912
|
-
};
|
|
1783
|
+
throw new Refusal(
|
|
1784
|
+
"Dependencies not installed. Run `npm install` (or `bun install`) first.",
|
|
1785
|
+
"deps_missing",
|
|
1786
|
+
{
|
|
1787
|
+
action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] }
|
|
1788
|
+
}
|
|
1789
|
+
);
|
|
1913
1790
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
function writeSecretFileSync(path, content) {
|
|
1918
|
-
writeFileSync5(path, content, { mode: 384 });
|
|
1791
|
+
function resolvesDeepspace(fromDir) {
|
|
1792
|
+
let dir = canonicalPath2(fromDir);
|
|
1793
|
+
let checkoutRoot = null;
|
|
1919
1794
|
try {
|
|
1920
|
-
|
|
1795
|
+
checkoutRoot = canonicalPath2(repoToplevel(dir));
|
|
1921
1796
|
} catch {
|
|
1922
1797
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
// src/server/rooms/app-name.ts
|
|
1931
|
-
var APP_NAME_RULES = {
|
|
1932
|
-
/** ^[a-z0-9](-?[a-z0-9])+$ — RFC 1035 host label, no consecutive dashes. */
|
|
1933
|
-
pattern: /^[a-z0-9](?:-?[a-z0-9])+$/,
|
|
1934
|
-
minLength: 2,
|
|
1935
|
-
maxLength: 63
|
|
1936
|
-
};
|
|
1937
|
-
function validateAppName(raw) {
|
|
1938
|
-
if (typeof raw !== "string") {
|
|
1939
|
-
return { valid: false, reason: "App name must be a string" };
|
|
1940
|
-
}
|
|
1941
|
-
const name = raw.trim();
|
|
1942
|
-
if (!name) {
|
|
1943
|
-
return { valid: false, reason: "App name is required" };
|
|
1944
|
-
}
|
|
1945
|
-
if (name.length < APP_NAME_RULES.minLength) {
|
|
1946
|
-
return { valid: false, reason: `App name "${name}" is too short (min ${APP_NAME_RULES.minLength} chars)` };
|
|
1798
|
+
for (; ; ) {
|
|
1799
|
+
if (existsSync8(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
|
|
1800
|
+
if (dir === checkoutRoot) return false;
|
|
1801
|
+
const parent = dirname5(dir);
|
|
1802
|
+
if (parent === dir) return false;
|
|
1803
|
+
dir = parent;
|
|
1947
1804
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1805
|
+
}
|
|
1806
|
+
function canonicalPath2(path) {
|
|
1807
|
+
try {
|
|
1808
|
+
return realpathSync3(path);
|
|
1809
|
+
} catch {
|
|
1810
|
+
return resolve6(path);
|
|
1950
1811
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1812
|
+
}
|
|
1813
|
+
function installWorkerAlive(sentinelDir) {
|
|
1814
|
+
const pidPath = join9(sentinelDir, "install.pid");
|
|
1815
|
+
if (!existsSync8(pidPath)) return false;
|
|
1816
|
+
const pid = Number(readFileSync11(pidPath, "utf-8").trim());
|
|
1817
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
1818
|
+
try {
|
|
1819
|
+
process.kill(pid, 0);
|
|
1820
|
+
return true;
|
|
1821
|
+
} catch (err) {
|
|
1822
|
+
return err.code === "EPERM";
|
|
1956
1823
|
}
|
|
1957
|
-
return { valid: true, name };
|
|
1958
1824
|
}
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1825
|
+
|
|
1826
|
+
// src/cli/lib/preflight.ts
|
|
1827
|
+
import { execFileSync } from "child_process";
|
|
1828
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1829
|
+
import * as nodeModule from "module";
|
|
1830
|
+
import { resolve as resolve7 } from "path";
|
|
1831
|
+
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function", version = process.versions.node) {
|
|
1832
|
+
const [major, minor] = version.split(".").map(Number);
|
|
1833
|
+
const supportedLine = major === 22 && minor >= 15 || major === 24 || major === 26;
|
|
1834
|
+
if (hasRegisterHooks && supportedLine) return;
|
|
1835
|
+
throw new Refusal(
|
|
1836
|
+
`deepspace ${command} requires a supported Node release: 22.15+, 24, or 26 (found v${version}). Install the current LTS from https://nodejs.org and re-run.`,
|
|
1837
|
+
supportedLine ? "node_capability_missing" : "node_unsupported"
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
var DLL_NOT_FOUND = -1073741515;
|
|
1841
|
+
function preflightWindowsWorkerd(appDir) {
|
|
1842
|
+
if (process.platform !== "win32") return;
|
|
1843
|
+
const workerdExe = resolve7(
|
|
1844
|
+
appDir,
|
|
1845
|
+
"node_modules",
|
|
1846
|
+
"@cloudflare",
|
|
1847
|
+
"workerd-windows-64",
|
|
1848
|
+
"bin",
|
|
1849
|
+
"workerd.exe"
|
|
1850
|
+
);
|
|
1851
|
+
if (!existsSync9(workerdExe)) return;
|
|
1852
|
+
try {
|
|
1853
|
+
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
1854
|
+
return;
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
const status3 = err.status;
|
|
1857
|
+
if (status3 !== DLL_NOT_FOUND) return;
|
|
1858
|
+
throw new Refusal(
|
|
1859
|
+
"workerd (Cloudflare Workers runtime) cannot start on this system: it needs the Microsoft Visual C++ Redistributable, which is not installed. Install it with `winget install Microsoft.VCRedist.2015+.x64` (or choco install -y vcredist140, or https://aka.ms/vs/17/release/vc_redist.x64.exe), then restart this terminal and re-run.",
|
|
1860
|
+
"vcredist_missing"
|
|
1861
|
+
);
|
|
1972
1862
|
}
|
|
1973
|
-
return {
|
|
1974
|
-
ok: true,
|
|
1975
|
-
name: sanitized,
|
|
1976
|
-
warning: `App name "${trimmed}" doesn't conform to deploy rules \u2014 using "${sanitized}" instead. Update wrangler.toml's \`name\` to silence this warning.`
|
|
1977
|
-
};
|
|
1978
1863
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1864
|
+
|
|
1865
|
+
// src/cli/lib/macos-junk.ts
|
|
1866
|
+
import { readdirSync as readdirSync2, rmSync as rmSync3 } from "fs";
|
|
1867
|
+
import { join as join10 } from "path";
|
|
1868
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
|
|
1869
|
+
function removeMacosJunk(dir) {
|
|
1870
|
+
let removed = 0;
|
|
1871
|
+
let entries;
|
|
1872
|
+
try {
|
|
1873
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
1874
|
+
} catch {
|
|
1875
|
+
return removed;
|
|
1876
|
+
}
|
|
1877
|
+
for (const entry of entries) {
|
|
1878
|
+
const path = join10(dir, entry.name);
|
|
1879
|
+
if (entry.isDirectory()) {
|
|
1880
|
+
if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
if (entry.name === ".DS_Store" || entry.name.startsWith("._")) {
|
|
1884
|
+
try {
|
|
1885
|
+
rmSync3(path, { force: true });
|
|
1886
|
+
removed++;
|
|
1887
|
+
} catch {
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return removed;
|
|
1981
1892
|
}
|
|
1982
1893
|
|
|
1983
|
-
// src/
|
|
1984
|
-
var
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
"durable_objects",
|
|
1988
|
-
"kv_namespaces",
|
|
1989
|
-
"r2_buckets",
|
|
1990
|
-
"ai_search_namespaces",
|
|
1991
|
-
"ai_search",
|
|
1992
|
-
"ratelimits",
|
|
1894
|
+
// src/server/rooms/binding-manifest.ts
|
|
1895
|
+
var AUTO_PROVISION_SENTINEL = "auto";
|
|
1896
|
+
var VECTORIZE_METRICS = /* @__PURE__ */ new Set(["cosine", "euclidean", "dot-product"]);
|
|
1897
|
+
var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
1993
1898
|
"vectorize",
|
|
1994
|
-
"
|
|
1995
|
-
"
|
|
1996
|
-
"
|
|
1997
|
-
"
|
|
1998
|
-
"
|
|
1999
|
-
"
|
|
1899
|
+
"ai",
|
|
1900
|
+
"r2_bucket",
|
|
1901
|
+
"kv_namespace",
|
|
1902
|
+
"d1",
|
|
1903
|
+
"queue",
|
|
1904
|
+
"browser_rendering",
|
|
1905
|
+
"analytics_engine",
|
|
1906
|
+
"hyperdrive",
|
|
1907
|
+
"ratelimit"
|
|
2000
1908
|
]);
|
|
2001
|
-
var
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
1909
|
+
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
1910
|
+
"ASSETS",
|
|
1911
|
+
"PLATFORM_WORKER",
|
|
1912
|
+
"API_WORKER",
|
|
1913
|
+
"DEEPSPACE_APP_ID",
|
|
1914
|
+
"DEEPSPACE_RESOURCE_ID",
|
|
1915
|
+
"DEEPSPACE_RELEASE_OPERATION_ID",
|
|
1916
|
+
"APP_NAME",
|
|
1917
|
+
"OWNER_USER_ID",
|
|
1918
|
+
"AUTH_JWT_PUBLIC_KEY",
|
|
1919
|
+
"AUTH_JWT_ISSUER",
|
|
1920
|
+
"AUTH_WORKER_URL",
|
|
1921
|
+
"APP_IDENTITY_TOKEN",
|
|
1922
|
+
"APP_OWNER_JWT",
|
|
1923
|
+
"USAGE_EVENTS"
|
|
1924
|
+
]);
|
|
1925
|
+
function validateBindingManifest(manifest) {
|
|
1926
|
+
if (!Array.isArray(manifest)) {
|
|
1927
|
+
return { valid: false, errors: [{ reason: "Manifest must be an array" }] };
|
|
2018
1928
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
1929
|
+
const errors = [];
|
|
1930
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1931
|
+
for (const entry of manifest) {
|
|
1932
|
+
if (!entry || typeof entry !== "object") {
|
|
1933
|
+
errors.push({
|
|
1934
|
+
reason: `Entry must be an object (got ${entry === null ? "null" : typeof entry})`
|
|
1935
|
+
});
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
const e = entry;
|
|
1939
|
+
const type = e.type;
|
|
1940
|
+
const name = e.name;
|
|
1941
|
+
if (typeof type !== "string" || !ALLOWED_BINDING_TYPES.has(type)) {
|
|
1942
|
+
errors.push({
|
|
1943
|
+
reason: `Disallowed binding type: ${typeof type === "string" ? type : `(${typeof type})`}. Allowed: ${[...ALLOWED_BINDING_TYPES].sort().join(", ")}`
|
|
1944
|
+
});
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (typeof name !== "string" || !name) {
|
|
1948
|
+
errors.push({ reason: `Binding of type "${type}" missing 'name' (string)` });
|
|
1949
|
+
continue;
|
|
1950
|
+
}
|
|
1951
|
+
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
1952
|
+
errors.push({ reason: `Binding name "${name}" is reserved by the SDK` });
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
if (seenNames.has(name)) {
|
|
1956
|
+
errors.push({ reason: `Duplicate binding name "${name}"` });
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
seenNames.add(name);
|
|
1960
|
+
const binding = entry;
|
|
1961
|
+
const fieldErr = requiredFieldError(binding);
|
|
1962
|
+
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
2024
1963
|
}
|
|
2025
|
-
|
|
2026
|
-
}
|
|
2027
|
-
function devVarsPathFor(appDir, envName, opts = {}) {
|
|
2028
|
-
if (opts.sharedDevVarsCache || !envName) return join7(appDir, ".dev.vars");
|
|
2029
|
-
return join7(appDir, `.dev.vars.${envName}`);
|
|
2030
|
-
}
|
|
2031
|
-
function legacyEnvDevVarsPathFor(appDir, envName) {
|
|
2032
|
-
return envName ? join7(appDir, `.dev.vars.${envName}`) : void 0;
|
|
2033
|
-
}
|
|
2034
|
-
function warnIfLegacyEnvDevVarsExists(appDir, envName, warn = console.warn) {
|
|
2035
|
-
const legacyPath = legacyEnvDevVarsPathFor(appDir, envName);
|
|
2036
|
-
if (!legacyPath || !existsSync6(legacyPath)) return;
|
|
2037
|
-
warn(
|
|
2038
|
-
`Warning: ${legacyPath} exists, but DeepSpace now uses ${devVarsPathFor(appDir, envName, { sharedDevVarsCache: true })} for every environment. This run will ignore ${legacyPath}; move any secrets you still need into .dev.vars, then remove the env-specific file when you're ready.`
|
|
2039
|
-
);
|
|
1964
|
+
if (errors.length) return { valid: false, errors };
|
|
1965
|
+
return { valid: true, bindings: manifest };
|
|
2040
1966
|
}
|
|
2041
|
-
function
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
1967
|
+
function requiredFieldError(b) {
|
|
1968
|
+
switch (b.type) {
|
|
1969
|
+
case "vectorize": {
|
|
1970
|
+
if (!b.index_name) return `vectorize binding "${b.name}" missing index_name (or "auto")`;
|
|
1971
|
+
if (b.index_name === AUTO_PROVISION_SENTINEL) {
|
|
1972
|
+
if (typeof b.dimensions !== "number" || b.dimensions <= 0) {
|
|
1973
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "dimensions" (positive number)`;
|
|
1974
|
+
}
|
|
1975
|
+
if (!b.metric || !VECTORIZE_METRICS.has(b.metric)) {
|
|
1976
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "metric" (one of: ${[...VECTORIZE_METRICS].join(", ")})`;
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
return null;
|
|
1980
|
+
}
|
|
1981
|
+
case "r2_bucket":
|
|
1982
|
+
return b.bucket_name ? null : `r2_bucket binding "${b.name}" missing bucket_name (or "auto")`;
|
|
1983
|
+
case "kv_namespace": {
|
|
1984
|
+
if (!b.namespace_id)
|
|
1985
|
+
return `kv_namespace binding "${b.name}" missing namespace_id (or "auto")`;
|
|
1986
|
+
if (b.namespace_id === AUTO_PROVISION_SENTINEL && !b.title) {
|
|
1987
|
+
return `kv_namespace binding "${b.name}" with namespace_id="auto" requires "title"`;
|
|
1988
|
+
}
|
|
1989
|
+
return null;
|
|
1990
|
+
}
|
|
1991
|
+
case "d1": {
|
|
1992
|
+
if (!b.id) return `d1 binding "${b.name}" missing id (or "auto")`;
|
|
1993
|
+
if (b.id === AUTO_PROVISION_SENTINEL && !b.database_name) {
|
|
1994
|
+
return `d1 binding "${b.name}" with id="auto" requires "database_name"`;
|
|
1995
|
+
}
|
|
1996
|
+
return null;
|
|
1997
|
+
}
|
|
1998
|
+
case "queue":
|
|
1999
|
+
return b.queue_name ? null : `queue binding "${b.name}" missing queue_name (or "auto")`;
|
|
2000
|
+
case "hyperdrive":
|
|
2001
|
+
if (!b.id) return `hyperdrive binding "${b.name}" missing id`;
|
|
2002
|
+
if (b.id === AUTO_PROVISION_SENTINEL) {
|
|
2003
|
+
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
2004
|
+
}
|
|
2005
|
+
return null;
|
|
2006
|
+
case "ratelimit":
|
|
2007
|
+
if (!/^\d+$/u.test(b.namespace_id) || Number(b.namespace_id) <= 0) {
|
|
2008
|
+
return `ratelimit binding "${b.name}" requires a positive integer namespace_id string`;
|
|
2009
|
+
}
|
|
2010
|
+
if (!Number.isInteger(b.simple?.limit) || b.simple.limit <= 0) {
|
|
2011
|
+
return `ratelimit binding "${b.name}" requires simple.limit (positive integer)`;
|
|
2012
|
+
}
|
|
2013
|
+
if (b.simple.period !== 10 && b.simple.period !== 60) {
|
|
2014
|
+
return `ratelimit binding "${b.name}" requires simple.period (10 or 60)`;
|
|
2015
|
+
}
|
|
2016
|
+
return null;
|
|
2017
|
+
case "ai":
|
|
2018
|
+
case "browser_rendering":
|
|
2019
|
+
case "analytics_engine":
|
|
2020
|
+
return null;
|
|
2021
|
+
// no required fields beyond `name`
|
|
2022
|
+
default: {
|
|
2023
|
+
const _exhaustive = b;
|
|
2024
|
+
void _exhaustive;
|
|
2025
|
+
return null;
|
|
2067
2026
|
}
|
|
2068
|
-
};
|
|
2069
|
-
}
|
|
2070
|
-
function wranglerViteEnv(baseEnv, prepared, extraEnv = {}) {
|
|
2071
|
-
const env = { ...baseEnv, ...extraEnv };
|
|
2072
|
-
if (prepared.configPath) {
|
|
2073
|
-
delete env.CLOUDFLARE_ENV;
|
|
2074
|
-
env.CLOUDFLARE_VITE_WRANGLER_CONFIG_PATH = prepared.configPath;
|
|
2075
|
-
} else if (prepared.wranglerEnv) {
|
|
2076
|
-
env.CLOUDFLARE_ENV = prepared.wranglerEnv;
|
|
2077
2027
|
}
|
|
2078
|
-
return env;
|
|
2079
2028
|
}
|
|
2080
|
-
function
|
|
2081
|
-
const
|
|
2082
|
-
const
|
|
2083
|
-
if (
|
|
2084
|
-
|
|
2029
|
+
function bindingManifestFromOutputConfig(outputConfig) {
|
|
2030
|
+
const out = [];
|
|
2031
|
+
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
2032
|
+
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
2033
|
+
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
2034
|
+
const name = pluckString(v, "binding");
|
|
2035
|
+
const index_name = pluckString(v, "index_name");
|
|
2036
|
+
if (!name || !index_name) continue;
|
|
2037
|
+
const dimensions = pluckNumber(v, "dimensions");
|
|
2038
|
+
const metric = pluckString(v, "metric");
|
|
2039
|
+
out.push({
|
|
2040
|
+
type: "vectorize",
|
|
2041
|
+
name,
|
|
2042
|
+
index_name,
|
|
2043
|
+
...dimensions != null && { dimensions },
|
|
2044
|
+
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
2045
|
+
});
|
|
2085
2046
|
}
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2047
|
+
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
2048
|
+
const name = pluckString(r, "binding");
|
|
2049
|
+
const bucket_name = pluckString(r, "bucket_name");
|
|
2050
|
+
if (name && bucket_name) out.push({ type: "r2_bucket", name, bucket_name });
|
|
2090
2051
|
}
|
|
2091
|
-
for (const
|
|
2092
|
-
|
|
2052
|
+
for (const k of asObjectArray(outputConfig.kv_namespaces)) {
|
|
2053
|
+
const name = pluckString(k, "binding");
|
|
2054
|
+
const namespace_id = pluckString(k, "id");
|
|
2055
|
+
if (!name || !namespace_id) continue;
|
|
2056
|
+
const title = pluckString(k, "title");
|
|
2057
|
+
out.push({
|
|
2058
|
+
type: "kv_namespace",
|
|
2059
|
+
name,
|
|
2060
|
+
namespace_id,
|
|
2061
|
+
...title && { title }
|
|
2062
|
+
});
|
|
2093
2063
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2064
|
+
for (const d of asObjectArray(outputConfig.d1_databases)) {
|
|
2065
|
+
const name = pluckString(d, "binding");
|
|
2066
|
+
const id = pluckString(d, "database_id");
|
|
2067
|
+
if (!name || !id) continue;
|
|
2068
|
+
const database_name = pluckString(d, "database_name");
|
|
2069
|
+
out.push({
|
|
2070
|
+
type: "d1",
|
|
2071
|
+
name,
|
|
2072
|
+
id,
|
|
2073
|
+
...database_name && { database_name }
|
|
2074
|
+
});
|
|
2096
2075
|
}
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2099
|
-
|
|
2076
|
+
const queues = isObject(outputConfig.queues) ? outputConfig.queues : null;
|
|
2077
|
+
for (const q of asObjectArray(queues?.producers)) {
|
|
2078
|
+
const name = pluckString(q, "binding");
|
|
2079
|
+
const queue_name = pluckString(q, "queue");
|
|
2080
|
+
if (name && queue_name) out.push({ type: "queue", name, queue_name });
|
|
2100
2081
|
}
|
|
2101
|
-
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
return value.replace(/[^A-Za-z0-9._-]/gu, "_");
|
|
2108
|
-
}
|
|
2109
|
-
var WranglerConfigError = class extends Error {
|
|
2110
|
-
constructor(path, cause) {
|
|
2111
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
2112
|
-
super(`wrangler.toml: malformed TOML at ${path}: ${detail}`);
|
|
2113
|
-
this.path = path;
|
|
2114
|
-
this.cause = cause;
|
|
2115
|
-
this.name = "WranglerConfigError";
|
|
2082
|
+
const browserBinding = pluckString(outputConfig.browser, "binding");
|
|
2083
|
+
if (browserBinding) out.push({ type: "browser_rendering", name: browserBinding });
|
|
2084
|
+
for (const a of asObjectArray(outputConfig.analytics_engine_datasets)) {
|
|
2085
|
+
const name = pluckString(a, "binding");
|
|
2086
|
+
const dataset = pluckString(a, "dataset");
|
|
2087
|
+
if (name) out.push({ type: "analytics_engine", name, dataset: dataset ?? void 0 });
|
|
2116
2088
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2089
|
+
for (const h of asObjectArray(outputConfig.hyperdrive)) {
|
|
2090
|
+
const name = pluckString(h, "binding");
|
|
2091
|
+
const id = pluckString(h, "id");
|
|
2092
|
+
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
2093
|
+
}
|
|
2094
|
+
for (const r of asObjectArray(outputConfig.ratelimits)) {
|
|
2095
|
+
const name = pluckString(r, "name");
|
|
2096
|
+
const namespace_id = pluckString(r, "namespace_id");
|
|
2097
|
+
const simple = isObject(r.simple) ? r.simple : null;
|
|
2098
|
+
const limit = pluckNumber(simple, "limit");
|
|
2099
|
+
const period = pluckNumber(simple, "period");
|
|
2100
|
+
if (!name || !namespace_id || limit === null || period !== 10 && period !== 60) continue;
|
|
2101
|
+
out.push({ type: "ratelimit", name, namespace_id, simple: { limit, period } });
|
|
2125
2102
|
}
|
|
2103
|
+
return out;
|
|
2104
|
+
}
|
|
2105
|
+
function isObject(v) {
|
|
2106
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2126
2107
|
}
|
|
2127
|
-
function
|
|
2128
|
-
|
|
2108
|
+
function pluckString(v, key) {
|
|
2109
|
+
if (!isObject(v)) return null;
|
|
2110
|
+
const val = v[key];
|
|
2111
|
+
return typeof val === "string" && val.length > 0 ? val : null;
|
|
2112
|
+
}
|
|
2113
|
+
function pluckNumber(v, key) {
|
|
2114
|
+
if (!isObject(v)) return null;
|
|
2115
|
+
const val = v[key];
|
|
2116
|
+
return typeof val === "number" && Number.isFinite(val) ? val : null;
|
|
2117
|
+
}
|
|
2118
|
+
function asObjectArray(v) {
|
|
2119
|
+
if (!Array.isArray(v)) return [];
|
|
2120
|
+
return v.filter(isObject);
|
|
2129
2121
|
}
|
|
2130
2122
|
|
|
2131
|
-
// src/cli/lib/
|
|
2132
|
-
var
|
|
2133
|
-
|
|
2134
|
-
"AUTH_JWT_ISSUER",
|
|
2135
|
-
"AUTH_WORKER_URL",
|
|
2123
|
+
// src/cli/lib/secrets.ts
|
|
2124
|
+
var RESERVED_SECRET_NAMES = /* @__PURE__ */ new Set([
|
|
2125
|
+
...RESERVED_BINDING_NAMES,
|
|
2136
2126
|
"API_WORKER_URL",
|
|
2137
|
-
"PLATFORM_WORKER_URL"
|
|
2138
|
-
"OWNER_USER_ID",
|
|
2139
|
-
"APP_OWNER_JWT",
|
|
2140
|
-
"APP_IDENTITY_TOKEN",
|
|
2141
|
-
"ALLOW_DEBUG_ROUTES"
|
|
2127
|
+
"PLATFORM_WORKER_URL"
|
|
2142
2128
|
]);
|
|
2143
|
-
var
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
if (line.trim() === DEV_VARS_DIVIDER.trim()) {
|
|
2152
|
-
belowDivider = true;
|
|
2153
|
-
i++;
|
|
2154
|
-
continue;
|
|
2155
|
-
}
|
|
2156
|
-
const eq = line.indexOf("=");
|
|
2157
|
-
const keyMatch = eq >= 0 ? line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/) : null;
|
|
2158
|
-
if (!keyMatch) {
|
|
2159
|
-
out.push(line);
|
|
2160
|
-
i++;
|
|
2161
|
-
continue;
|
|
2162
|
-
}
|
|
2163
|
-
const key = keyMatch[1];
|
|
2164
|
-
const valueStart = line.slice(eq + 1);
|
|
2165
|
-
const block = [line];
|
|
2166
|
-
if (valueStart.startsWith('"') && !hasUnescapedQuote(valueStart.slice(1))) {
|
|
2167
|
-
i++;
|
|
2168
|
-
while (i < lines.length) {
|
|
2169
|
-
block.push(lines[i]);
|
|
2170
|
-
const closed = hasUnescapedQuote(lines[i]);
|
|
2171
|
-
i++;
|
|
2172
|
-
if (closed) break;
|
|
2173
|
-
}
|
|
2174
|
-
} else {
|
|
2175
|
-
i++;
|
|
2176
|
-
}
|
|
2177
|
-
if (belowDivider || !SDK_MANAGED_KEYS.has(key)) out.push(...block);
|
|
2129
|
+
var SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2130
|
+
var CONFIG_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
2131
|
+
var MAX_SECRET_VALUE_BYTES = 32 * 1024;
|
|
2132
|
+
function validateConfigName(configName) {
|
|
2133
|
+
if (!CONFIG_NAME_RE.test(configName)) {
|
|
2134
|
+
throw new Error(
|
|
2135
|
+
`Invalid config "${configName}". Use letters, numbers, underscores, or hyphens; start with a letter or number.`
|
|
2136
|
+
);
|
|
2178
2137
|
}
|
|
2179
|
-
|
|
2180
|
-
return out.join("\n");
|
|
2138
|
+
return configName;
|
|
2181
2139
|
}
|
|
2182
|
-
function
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
continue;
|
|
2188
|
-
}
|
|
2189
|
-
if (char === "\\") {
|
|
2190
|
-
escaped = true;
|
|
2191
|
-
continue;
|
|
2192
|
-
}
|
|
2193
|
-
if (char === '"') return true;
|
|
2140
|
+
function validateSecretName(name) {
|
|
2141
|
+
if (!SECRET_NAME_RE.test(name)) {
|
|
2142
|
+
throw new Error(
|
|
2143
|
+
`Invalid secret name "${name}". Use letters, numbers, and underscores; start with a letter or underscore.`
|
|
2144
|
+
);
|
|
2194
2145
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
const lines = content.split("\n");
|
|
2200
|
-
let i = 0;
|
|
2201
|
-
while (i < lines.length) {
|
|
2202
|
-
const line = lines[i];
|
|
2203
|
-
const trimmed = line.trim();
|
|
2204
|
-
if (!trimmed || trimmed.startsWith("#")) {
|
|
2205
|
-
i++;
|
|
2206
|
-
continue;
|
|
2207
|
-
}
|
|
2208
|
-
const eq = line.indexOf("=");
|
|
2209
|
-
if (eq < 0) {
|
|
2210
|
-
i++;
|
|
2211
|
-
continue;
|
|
2212
|
-
}
|
|
2213
|
-
const keyMatch = line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
2214
|
-
if (!keyMatch) {
|
|
2215
|
-
i++;
|
|
2216
|
-
continue;
|
|
2217
|
-
}
|
|
2218
|
-
const key = keyMatch[1];
|
|
2219
|
-
let value = line.slice(eq + 1);
|
|
2220
|
-
let closed = !value.startsWith('"') || hasUnescapedQuote(value.slice(1));
|
|
2221
|
-
if (!closed) {
|
|
2222
|
-
i++;
|
|
2223
|
-
while (i < lines.length) {
|
|
2224
|
-
value += "\n" + lines[i];
|
|
2225
|
-
if (hasUnescapedQuote(lines[i])) {
|
|
2226
|
-
closed = true;
|
|
2227
|
-
i++;
|
|
2228
|
-
break;
|
|
2229
|
-
}
|
|
2230
|
-
i++;
|
|
2231
|
-
}
|
|
2232
|
-
} else {
|
|
2233
|
-
i++;
|
|
2234
|
-
}
|
|
2235
|
-
if (value.startsWith('"') && !closed) {
|
|
2236
|
-
throw new Error(
|
|
2237
|
-
`parseDevVars: unterminated quoted value for key "${key}" \u2014 check your .dev.vars file for a missing closing quote.`
|
|
2238
|
-
);
|
|
2239
|
-
}
|
|
2240
|
-
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
2241
|
-
value = unescapeQuotedDevVar(value.slice(1, -1));
|
|
2242
|
-
}
|
|
2243
|
-
result[key] = value;
|
|
2146
|
+
if (RESERVED_SECRET_NAMES.has(name)) {
|
|
2147
|
+
throw new Error(
|
|
2148
|
+
`"${name}" is managed by the DeepSpace SDK and can't be set as an app secret \u2014 the platform injects it at deploy time.`
|
|
2149
|
+
);
|
|
2244
2150
|
}
|
|
2245
|
-
return
|
|
2151
|
+
return name;
|
|
2246
2152
|
}
|
|
2247
|
-
function
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
const char = value[i];
|
|
2251
|
-
if (char !== "\\" || i === value.length - 1) {
|
|
2252
|
-
out += char;
|
|
2253
|
-
continue;
|
|
2254
|
-
}
|
|
2255
|
-
const next = value[i + 1];
|
|
2256
|
-
if (next === "\\" || next === '"') {
|
|
2257
|
-
out += next;
|
|
2258
|
-
i++;
|
|
2259
|
-
continue;
|
|
2260
|
-
}
|
|
2261
|
-
out += char;
|
|
2153
|
+
function validateSecretValue(key, value) {
|
|
2154
|
+
if (new TextEncoder().encode(value).byteLength > MAX_SECRET_VALUE_BYTES) {
|
|
2155
|
+
throw new Error(`Secret "${key}" exceeds ${MAX_SECRET_VALUE_BYTES} bytes`);
|
|
2262
2156
|
}
|
|
2263
|
-
return out;
|
|
2264
2157
|
}
|
|
2265
|
-
function
|
|
2266
|
-
|
|
2267
|
-
if (id) return id;
|
|
2268
|
-
throw new Error(
|
|
2269
|
-
"wrangler.toml has no DEEPSPACE_APP_ID" + (wranglerEnv ? ` for [env.${wranglerEnv}]` : "") + ". Run `deepspace app init` (or `deepspace app init --env <name>`) and retry."
|
|
2270
|
-
);
|
|
2158
|
+
function defaultConfigNameForEnv(wranglerEnv) {
|
|
2159
|
+
return wranglerEnv ?? "prd";
|
|
2271
2160
|
}
|
|
2272
|
-
async function
|
|
2273
|
-
const
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
`AUTH_JWT_ISSUER=${urls.auth}/api/auth`,
|
|
2281
|
-
`AUTH_WORKER_URL=${urls.auth}`,
|
|
2282
|
-
`API_WORKER_URL=${urls.api}`,
|
|
2283
|
-
`PLATFORM_WORKER_URL=${urls.platform}`,
|
|
2284
|
-
`OWNER_USER_ID=${ownerId}`,
|
|
2285
|
-
`APP_OWNER_JWT=${appOwnerJwt}`,
|
|
2286
|
-
...appIdentityToken ? [`APP_IDENTITY_TOKEN=${appIdentityToken}`] : [],
|
|
2287
|
-
`ALLOW_DEBUG_ROUTES=true`
|
|
2288
|
-
].join("\n");
|
|
2289
|
-
const useSharedDevVarsCache = opts.sharedDevVarsCache ?? opts.generatedSecretsCache !== void 0;
|
|
2290
|
-
const devVarsPath = devVarsPathFor(appDir, wranglerEnv, {
|
|
2291
|
-
sharedDevVarsCache: useSharedDevVarsCache
|
|
2161
|
+
async function secretsApi(deployUrl, token, path, init = {}) {
|
|
2162
|
+
const res = await fetch(`${deployUrl}/api/secrets${path}`, {
|
|
2163
|
+
...init,
|
|
2164
|
+
headers: {
|
|
2165
|
+
Authorization: `Bearer ${token}`,
|
|
2166
|
+
"Content-Type": "application/json",
|
|
2167
|
+
...init.headers ?? {}
|
|
2168
|
+
}
|
|
2292
2169
|
});
|
|
2293
|
-
const
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
} else if (opts.generatedSecretsCache) {
|
|
2300
|
-
body = `${sdkVars}
|
|
2301
|
-
|
|
2302
|
-
${custom}
|
|
2303
|
-
`;
|
|
2304
|
-
} else {
|
|
2305
|
-
body = `${sdkVars}
|
|
2306
|
-
|
|
2307
|
-
${DEV_VARS_DIVIDER}
|
|
2308
|
-
${custom}
|
|
2309
|
-
`;
|
|
2170
|
+
const body = await res.json().catch(() => ({}));
|
|
2171
|
+
if (!res.ok) {
|
|
2172
|
+
const err = new Error(body.error ?? `Secrets request failed (${res.status})`);
|
|
2173
|
+
err.status = res.status;
|
|
2174
|
+
err.apiPath = path;
|
|
2175
|
+
throw err;
|
|
2310
2176
|
}
|
|
2311
|
-
|
|
2177
|
+
return body;
|
|
2312
2178
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
import { existsSync as existsSync9, readFileSync as readFileSync12, realpathSync as realpathSync3 } from "fs";
|
|
2316
|
-
import { dirname as dirname5, join as join9, resolve as resolve6 } from "path";
|
|
2317
|
-
|
|
2318
|
-
// src/cli/lib/package-manager.ts
|
|
2319
|
-
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
|
|
2320
|
-
import { join as join8 } from "path";
|
|
2321
|
-
function detectPackageManager(appDir) {
|
|
2322
|
-
try {
|
|
2323
|
-
const pkg2 = JSON.parse(readFileSync11(join8(appDir, "package.json"), "utf-8"));
|
|
2324
|
-
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
2325
|
-
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
2326
|
-
} catch {
|
|
2327
|
-
}
|
|
2328
|
-
if (existsSync8(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
2329
|
-
if (existsSync8(join8(appDir, "yarn.lock"))) return "yarn";
|
|
2330
|
-
if (existsSync8(join8(appDir, "bun.lock")) || existsSync8(join8(appDir, "bun.lockb"))) return "bun";
|
|
2331
|
-
return "npm";
|
|
2179
|
+
function listSecrets(deployUrl, token, appId, configName) {
|
|
2180
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`);
|
|
2332
2181
|
}
|
|
2333
|
-
function
|
|
2334
|
-
return
|
|
2182
|
+
function fetchSecretsValues(deployUrl, token, appId, configName) {
|
|
2183
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}/values`);
|
|
2335
2184
|
}
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
if (existsSync9(join9(sentinelDir, "install.started"))) {
|
|
2343
|
-
if (!existsSync9(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
|
|
2344
|
-
return "installing";
|
|
2345
|
-
}
|
|
2346
|
-
return "failed";
|
|
2347
|
-
}
|
|
2348
|
-
return "missing";
|
|
2185
|
+
function getSecretPlain(deployUrl, token, appId, configName, key) {
|
|
2186
|
+
return secretsApi(
|
|
2187
|
+
deployUrl,
|
|
2188
|
+
token,
|
|
2189
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}?plain=true`
|
|
2190
|
+
);
|
|
2349
2191
|
}
|
|
2350
|
-
function
|
|
2351
|
-
return
|
|
2192
|
+
function setSecret(deployUrl, token, appId, configName, key, value) {
|
|
2193
|
+
return secretsApi(
|
|
2194
|
+
deployUrl,
|
|
2195
|
+
token,
|
|
2196
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
2197
|
+
{ method: "PUT", body: JSON.stringify({ value }) }
|
|
2198
|
+
);
|
|
2352
2199
|
}
|
|
2353
|
-
function
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
const logPath = join9(sentinelDir, "install.log");
|
|
2360
|
-
if (existsSync9(errPath)) {
|
|
2361
|
-
const detail = readFileSync12(errPath, "utf-8").trimEnd();
|
|
2362
|
-
const log29 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
|
|
2363
|
-
throw new Refusal(
|
|
2364
|
-
`Dependency install failed: ${detail}.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
2365
|
-
"install_failed",
|
|
2366
|
-
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2367
|
-
);
|
|
2368
|
-
}
|
|
2369
|
-
if (existsSync9(startedPath) && !existsSync9(donePath)) {
|
|
2370
|
-
if (!installWorkerAlive(sentinelDir)) {
|
|
2371
|
-
const log29 = existsSync9(logPath) ? ` See what happened: ${logPath}.` : "";
|
|
2372
|
-
throw new Refusal(
|
|
2373
|
-
`The dependency install is no longer running and never finished.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
2374
|
-
"install_failed",
|
|
2375
|
-
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2376
|
-
);
|
|
2377
|
-
}
|
|
2378
|
-
const tail = existsSync9(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
2379
|
-
throw new Refusal(
|
|
2380
|
-
`Dependencies are still installing.${tail} Retry once it finishes.`,
|
|
2381
|
-
"install_in_progress"
|
|
2382
|
-
);
|
|
2383
|
-
}
|
|
2384
|
-
throw new Refusal(
|
|
2385
|
-
"Dependencies not installed. Run `npm install` (or `bun install`) first.",
|
|
2386
|
-
"deps_missing",
|
|
2387
|
-
{
|
|
2388
|
-
action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] }
|
|
2389
|
-
}
|
|
2200
|
+
function deleteSecret(deployUrl, token, appId, configName, key) {
|
|
2201
|
+
return secretsApi(
|
|
2202
|
+
deployUrl,
|
|
2203
|
+
token,
|
|
2204
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
2205
|
+
{ method: "DELETE" }
|
|
2390
2206
|
);
|
|
2391
2207
|
}
|
|
2392
|
-
function
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
for (; ; ) {
|
|
2400
|
-
if (existsSync9(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
|
|
2401
|
-
if (dir === checkoutRoot) return false;
|
|
2402
|
-
const parent = dirname5(dir);
|
|
2403
|
-
if (parent === dir) return false;
|
|
2404
|
-
dir = parent;
|
|
2405
|
-
}
|
|
2208
|
+
function uploadSecrets(deployUrl, token, appId, configName, secrets, replace) {
|
|
2209
|
+
return secretsApi(
|
|
2210
|
+
deployUrl,
|
|
2211
|
+
token,
|
|
2212
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets`,
|
|
2213
|
+
{ method: "PUT", body: JSON.stringify({ secrets, replace }) }
|
|
2214
|
+
);
|
|
2406
2215
|
}
|
|
2407
|
-
function
|
|
2408
|
-
|
|
2409
|
-
return realpathSync3(path);
|
|
2410
|
-
} catch {
|
|
2411
|
-
return resolve6(path);
|
|
2412
|
-
}
|
|
2216
|
+
function listConfigs(deployUrl, token, appId) {
|
|
2217
|
+
return secretsApi(deployUrl, token, `/${appId}/configs`);
|
|
2413
2218
|
}
|
|
2414
|
-
function
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2219
|
+
function createConfig(deployUrl, token, appId, configName, copyFrom) {
|
|
2220
|
+
return secretsApi(deployUrl, token, `/${appId}/configs`, {
|
|
2221
|
+
method: "POST",
|
|
2222
|
+
body: JSON.stringify({ name: configName, ...copyFrom ? { copyFrom } : {} })
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
function deleteConfig(deployUrl, token, appId, configName) {
|
|
2226
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`, {
|
|
2227
|
+
method: "DELETE"
|
|
2228
|
+
});
|
|
2229
|
+
}
|
|
2230
|
+
async function pullAppSecretsCache(deployUrl, token, appId, configName) {
|
|
2419
2231
|
try {
|
|
2420
|
-
|
|
2421
|
-
|
|
2232
|
+
const { secrets } = await fetchSecretsValues(deployUrl, token, appId, configName);
|
|
2233
|
+
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
2234
|
+
throw new ApiError(
|
|
2235
|
+
`The secrets service at ${deployUrl} returned an unexpected response shape \u2014 check DEEPSPACE_DEPLOY_URL (is this the service the app lives on?).`,
|
|
2236
|
+
200,
|
|
2237
|
+
"invalid_response",
|
|
2238
|
+
`/${appId}/configs/${configName}/values`
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
return { appId, configName, values: secrets };
|
|
2422
2242
|
} catch (err) {
|
|
2423
|
-
|
|
2243
|
+
if (err?.status === 404) return null;
|
|
2244
|
+
throw err;
|
|
2424
2245
|
}
|
|
2425
2246
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
import * as nodeModule from "module";
|
|
2431
|
-
import { resolve as resolve7 } from "path";
|
|
2432
|
-
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function") {
|
|
2433
|
-
if (hasRegisterHooks) return;
|
|
2434
|
-
throw new Refusal(
|
|
2435
|
-
`deepspace ${command} requires Node 22.15 or newer (found v${process.versions.node}). Install the current LTS from https://nodejs.org and re-run.`,
|
|
2436
|
-
"node_too_old"
|
|
2437
|
-
);
|
|
2247
|
+
function quoteDotenvValue(value) {
|
|
2248
|
+
if (/^[A-Za-z0-9_./:@+-]*$/.test(value) && value !== "") return value;
|
|
2249
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
2250
|
+
return `"${escaped}"`;
|
|
2438
2251
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
const
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
"@cloudflare",
|
|
2446
|
-
"workerd-windows-64",
|
|
2447
|
-
"bin",
|
|
2448
|
-
"workerd.exe"
|
|
2449
|
-
);
|
|
2450
|
-
if (!existsSync10(workerdExe)) return;
|
|
2451
|
-
try {
|
|
2452
|
-
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
2453
|
-
return;
|
|
2454
|
-
} catch (err) {
|
|
2455
|
-
const status3 = err.status;
|
|
2456
|
-
if (status3 !== DLL_NOT_FOUND) return;
|
|
2457
|
-
throw new Refusal(
|
|
2458
|
-
"workerd (Cloudflare Workers runtime) cannot start on this system: it needs the Microsoft Visual C++ Redistributable, which is not installed. Install it with `winget install Microsoft.VCRedist.2015+.x64` (or choco install -y vcredist140, or https://aka.ms/vs/17/release/vc_redist.x64.exe), then restart this terminal and re-run.",
|
|
2459
|
-
"vcredist_missing"
|
|
2460
|
-
);
|
|
2252
|
+
function renderSecretsCache(secrets, source) {
|
|
2253
|
+
const keys = Object.keys(secrets).sort();
|
|
2254
|
+
const lines = [`# App secrets \xB7 config ${source.configName} \xB7 app ${source.appId}`];
|
|
2255
|
+
for (const key of keys) {
|
|
2256
|
+
validateSecretValue(key, secrets[key]);
|
|
2257
|
+
lines.push(`${key}=${quoteDotenvValue(secrets[key])}`);
|
|
2461
2258
|
}
|
|
2259
|
+
return lines.join("\n");
|
|
2462
2260
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2261
|
+
function parseSecretsUpload(content) {
|
|
2262
|
+
const trimmed = content.trim();
|
|
2263
|
+
if (!trimmed) return {};
|
|
2264
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
2265
|
+
const parsed = JSON.parse(trimmed);
|
|
2266
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2267
|
+
throw new Error("JSON upload must be an object of KEY=value strings.");
|
|
2268
|
+
}
|
|
2269
|
+
const out2 = {};
|
|
2270
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
2271
|
+
validateSecretName(key);
|
|
2272
|
+
if (typeof value !== "string") throw new Error(`Secret "${key}" must be a string`);
|
|
2273
|
+
validateSecretValue(key, value);
|
|
2274
|
+
out2[key] = value;
|
|
2275
|
+
}
|
|
2276
|
+
return out2;
|
|
2475
2277
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2278
|
+
const out = {};
|
|
2279
|
+
const lines = trimmed.split(/\r?\n/);
|
|
2280
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2281
|
+
const line = lines[i].trim();
|
|
2282
|
+
if (!line || line.startsWith("#")) continue;
|
|
2283
|
+
const lineNumber = i + 1;
|
|
2284
|
+
const eq = line.indexOf("=");
|
|
2285
|
+
if (eq <= 0) {
|
|
2286
|
+
throw new Error(`Invalid dotenv assignment on line ${lineNumber}: expected KEY=value.`);
|
|
2481
2287
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2288
|
+
const key = line.slice(0, eq).trim();
|
|
2289
|
+
if (!SECRET_NAME_RE.test(key)) {
|
|
2290
|
+
throw new Error(
|
|
2291
|
+
`Invalid secret name on line ${lineNumber}: use letters, numbers, and underscores; start with a letter or underscore.`
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
validateSecretName(key);
|
|
2295
|
+
const raw = line.slice(eq + 1).trim();
|
|
2296
|
+
const quote = raw[0] === '"' || raw[0] === "'" ? raw[0] : null;
|
|
2297
|
+
let value;
|
|
2298
|
+
if (!quote) {
|
|
2299
|
+
value = raw;
|
|
2300
|
+
} else if (endsWithUnescapedQuote(raw.slice(1), quote)) {
|
|
2301
|
+
const inner = raw.slice(1, -1);
|
|
2302
|
+
value = quote === '"' ? unescapeDotenv(inner) : inner;
|
|
2303
|
+
} else {
|
|
2304
|
+
const startLine = i + 1;
|
|
2305
|
+
const parts = [raw.slice(1)];
|
|
2306
|
+
let closed = false;
|
|
2307
|
+
while (++i < lines.length) {
|
|
2308
|
+
const cont = lines[i].trimEnd();
|
|
2309
|
+
if (endsWithUnescapedQuote(cont, quote)) {
|
|
2310
|
+
parts.push(cont.slice(0, -1));
|
|
2311
|
+
closed = true;
|
|
2312
|
+
break;
|
|
2313
|
+
}
|
|
2314
|
+
parts.push(lines[i]);
|
|
2315
|
+
}
|
|
2316
|
+
if (!closed) {
|
|
2317
|
+
throw new Error(
|
|
2318
|
+
`Unterminated quoted value for "${key}" (opens on line ${startLine}). Close the ${quote === '"' ? "double" : "single"} quote, or escape newlines as \\n on one line.`
|
|
2319
|
+
);
|
|
2487
2320
|
}
|
|
2321
|
+
value = parts.join("\n");
|
|
2322
|
+
if (quote === '"') value = unescapeDotenv(value);
|
|
2488
2323
|
}
|
|
2324
|
+
validateSecretValue(key, value);
|
|
2325
|
+
out[key] = value;
|
|
2489
2326
|
}
|
|
2490
|
-
return
|
|
2327
|
+
return out;
|
|
2328
|
+
}
|
|
2329
|
+
function endsWithUnescapedQuote(s, q) {
|
|
2330
|
+
if (!s.endsWith(q)) return false;
|
|
2331
|
+
if (q === "'") return true;
|
|
2332
|
+
let backslashes = 0;
|
|
2333
|
+
for (let j = s.length - 2; j >= 0 && s[j] === "\\"; j--) backslashes++;
|
|
2334
|
+
return backslashes % 2 === 0;
|
|
2335
|
+
}
|
|
2336
|
+
function unescapeDotenv(v) {
|
|
2337
|
+
let out = "";
|
|
2338
|
+
for (let i = 0; i < v.length; i++) {
|
|
2339
|
+
if (v[i] === "\\" && i + 1 < v.length) {
|
|
2340
|
+
const c = v[++i];
|
|
2341
|
+
out += c === "n" ? "\n" : c === "r" ? "\r" : c;
|
|
2342
|
+
} else {
|
|
2343
|
+
out += v[i];
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return out;
|
|
2347
|
+
}
|
|
2348
|
+
function shellSingleQuote(value) {
|
|
2349
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2350
|
+
}
|
|
2351
|
+
function formatSecretsDownload(secrets, format) {
|
|
2352
|
+
const entries = Object.entries(secrets).sort(([a], [b]) => a.localeCompare(b));
|
|
2353
|
+
if (format === "json") {
|
|
2354
|
+
return JSON.stringify(Object.fromEntries(entries), null, 2) + "\n";
|
|
2355
|
+
}
|
|
2356
|
+
if (format === "shell") {
|
|
2357
|
+
return entries.map(([k, v]) => `export ${k}=${shellSingleQuote(v)}`).join("\n") + "\n";
|
|
2358
|
+
}
|
|
2359
|
+
return entries.map(([k, v]) => `${k}=${quoteDotenvValue(v)}`).join("\n") + "\n";
|
|
2360
|
+
}
|
|
2361
|
+
async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv, selectedConfigName = defaultConfigNameForEnv(wranglerEnv)) {
|
|
2362
|
+
const configName = selectedConfigName;
|
|
2363
|
+
const pulled = await pullAppSecretsCache(deployUrl, token, appId, configName);
|
|
2364
|
+
const values = pulled?.values ?? {};
|
|
2365
|
+
const count = Object.keys(values).length;
|
|
2366
|
+
return {
|
|
2367
|
+
pulled,
|
|
2368
|
+
rendered: renderSecretsCache(values, { appId, configName }),
|
|
2369
|
+
// An empty store is the common case and not worth a status line.
|
|
2370
|
+
summary: count ? `Secrets: ${configName} (${count} secret${count === 1 ? "" : "s"})` : null
|
|
2371
|
+
};
|
|
2491
2372
|
}
|
|
2492
2373
|
|
|
2493
2374
|
// src/cli/lib/schema-lint.ts
|
|
2494
|
-
import { existsSync as
|
|
2375
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2495
2376
|
import { join as join11 } from "path";
|
|
2496
2377
|
import { pathToFileURL } from "url";
|
|
2497
2378
|
|
|
@@ -2552,7 +2433,7 @@ function lintSchema(schema) {
|
|
|
2552
2433
|
// src/cli/lib/schema-lint.ts
|
|
2553
2434
|
async function lintProjectSchemas(appDir) {
|
|
2554
2435
|
const schemasPath = join11(appDir, "src", "schemas.ts");
|
|
2555
|
-
if (!
|
|
2436
|
+
if (!existsSync10(schemasPath)) return null;
|
|
2556
2437
|
let schemas;
|
|
2557
2438
|
try {
|
|
2558
2439
|
const esbuild = await import("esbuild");
|
|
@@ -2756,10 +2637,8 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2756
2637
|
if (appIdForSecrets) {
|
|
2757
2638
|
try {
|
|
2758
2639
|
const refreshed = await refreshSecretsCache(DEPLOY_URL, token, appIdForSecrets, wranglerEnv);
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
if (refreshed.summary) say(refreshed.summary);
|
|
2762
|
-
}
|
|
2640
|
+
generatedSecretsCache = refreshed.rendered;
|
|
2641
|
+
if (refreshed.summary) say(refreshed.summary);
|
|
2763
2642
|
} catch (err) {
|
|
2764
2643
|
throw new Refusal(
|
|
2765
2644
|
`Failed to refresh app secrets: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -2767,16 +2646,10 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2767
2646
|
);
|
|
2768
2647
|
}
|
|
2769
2648
|
}
|
|
2770
|
-
const sharedDevVarsCache = generatedSecretsCache !== void 0;
|
|
2771
2649
|
await writeDevVars(appDir, payload.sub, token, wranglerEnv, {
|
|
2772
|
-
generatedSecretsCache
|
|
2773
|
-
sharedDevVarsCache
|
|
2650
|
+
generatedSecretsCache
|
|
2774
2651
|
});
|
|
2775
|
-
const wranglerConfig = prepareWranglerEnvConfig(
|
|
2776
|
-
appDir,
|
|
2777
|
-
wranglerEnv,
|
|
2778
|
-
{ sharedDevVarsCache }
|
|
2779
|
-
);
|
|
2652
|
+
const wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv);
|
|
2780
2653
|
preflightWindowsWorkerd(appDir);
|
|
2781
2654
|
if (await isPortListening(port, host)) {
|
|
2782
2655
|
const killArgv = port === DEFAULT_PORT ? ["deepspace", "dev", "kill"] : ["deepspace", "dev", "kill", "--port", String(port)];
|
|
@@ -2791,15 +2664,11 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2791
2664
|
}
|
|
2792
2665
|
const url = `http://${host === DEFAULT_DEV_HOST ? "localhost" : host}:${port}`;
|
|
2793
2666
|
say("Starting dev server...\n");
|
|
2794
|
-
const vite = spawn(
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
{
|
|
2798
|
-
|
|
2799
|
-
stdio: "inherit",
|
|
2800
|
-
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2801
|
-
}
|
|
2802
|
-
);
|
|
2667
|
+
const vite = spawn("npx", ["vite", "--port", String(port), "--strictPort", "--host", host], {
|
|
2668
|
+
cwd: appDir,
|
|
2669
|
+
stdio: "inherit",
|
|
2670
|
+
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2671
|
+
});
|
|
2803
2672
|
if (args.json) {
|
|
2804
2673
|
void waitForPortListening(port, host, READINESS_TIMEOUT_MS).then((ready) => {
|
|
2805
2674
|
console.log(
|
|
@@ -2844,7 +2713,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2844
2713
|
|
|
2845
2714
|
// src/cli/commands/kill.ts
|
|
2846
2715
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2847
|
-
import { readFileSync as
|
|
2716
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
2848
2717
|
import { setTimeout as wait } from "timers/promises";
|
|
2849
2718
|
var SIGTERM_GRACE_MS = 1500;
|
|
2850
2719
|
var IS_WIN = process.platform === "win32";
|
|
@@ -2995,7 +2864,7 @@ function listenerPidsViaProc(port) {
|
|
|
2995
2864
|
for (const f of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
2996
2865
|
let data;
|
|
2997
2866
|
try {
|
|
2998
|
-
data =
|
|
2867
|
+
data = readFileSync12(f, "utf-8");
|
|
2999
2868
|
} catch {
|
|
3000
2869
|
continue;
|
|
3001
2870
|
}
|
|
@@ -3154,10 +3023,10 @@ function ensurePlaywright(appDir) {
|
|
|
3154
3023
|
}
|
|
3155
3024
|
|
|
3156
3025
|
// src/cli/lib/test-account-service.ts
|
|
3157
|
-
import { readFileSync as
|
|
3026
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
3158
3027
|
|
|
3159
3028
|
// src/testing/accounts.ts
|
|
3160
|
-
import { existsSync as
|
|
3029
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
3161
3030
|
import { homedir as homedir3 } from "os";
|
|
3162
3031
|
import { join as join13 } from "path";
|
|
3163
3032
|
var TEST_ACCOUNTS_PATH = join13(homedir3(), ".deepspace", "test-accounts.json");
|
|
@@ -3231,9 +3100,9 @@ function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope())
|
|
|
3231
3100
|
});
|
|
3232
3101
|
}
|
|
3233
3102
|
function readTestAccountStore() {
|
|
3234
|
-
if (!
|
|
3103
|
+
if (!existsSync11(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {}, unscoped: [] };
|
|
3235
3104
|
try {
|
|
3236
|
-
const raw = JSON.parse(
|
|
3105
|
+
const raw = JSON.parse(readFileSync13(TEST_ACCOUNTS_PATH, "utf-8"));
|
|
3237
3106
|
if (Array.isArray(raw)) return { version: 2, scopes: {}, unscoped: validTestAccounts(raw) };
|
|
3238
3107
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3239
3108
|
return { version: 2, scopes: {}, unscoped: [] };
|
|
@@ -3271,7 +3140,7 @@ function saveTestAccountStore(store) {
|
|
|
3271
3140
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
3272
3141
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
3273
3142
|
function sessionCookie() {
|
|
3274
|
-
const token =
|
|
3143
|
+
const token = readFileSync14(SESSION_PATH, "utf-8").trim();
|
|
3275
3144
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
3276
3145
|
}
|
|
3277
3146
|
async function fetchRemoteTestAccounts() {
|
|
@@ -3375,10 +3244,8 @@ var test_default = defineDeepspaceCommand({
|
|
|
3375
3244
|
if (appIdForSecrets) {
|
|
3376
3245
|
try {
|
|
3377
3246
|
const refreshed = await refreshSecretsCache(DEPLOY_URL2, token, appIdForSecrets, wranglerEnv);
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
if (refreshed.summary) say(refreshed.summary);
|
|
3381
|
-
}
|
|
3247
|
+
generatedSecretsCache = refreshed.rendered;
|
|
3248
|
+
if (refreshed.summary) say(refreshed.summary);
|
|
3382
3249
|
} catch (err) {
|
|
3383
3250
|
throw new Refusal(
|
|
3384
3251
|
`Failed to refresh app secrets: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -3386,17 +3253,17 @@ var test_default = defineDeepspaceCommand({
|
|
|
3386
3253
|
);
|
|
3387
3254
|
}
|
|
3388
3255
|
}
|
|
3389
|
-
const sharedDevVarsCache = generatedSecretsCache !== void 0;
|
|
3390
3256
|
await writeDevVars(appDir, ownerId, token, wranglerEnv, {
|
|
3391
|
-
generatedSecretsCache
|
|
3392
|
-
sharedDevVarsCache
|
|
3257
|
+
generatedSecretsCache
|
|
3393
3258
|
});
|
|
3394
3259
|
if (suite !== "unit") {
|
|
3395
3260
|
try {
|
|
3396
3261
|
const { removed } = await syncTestAccountStore();
|
|
3397
3262
|
if (removed > 0) say(`Removed ${removed} stale test account credential(s).`);
|
|
3398
3263
|
} catch (error) {
|
|
3399
|
-
say(
|
|
3264
|
+
say(
|
|
3265
|
+
`Warning: could not reconcile test accounts: ${error instanceof Error ? error.message : String(error)}`
|
|
3266
|
+
);
|
|
3400
3267
|
}
|
|
3401
3268
|
}
|
|
3402
3269
|
if (suite !== "unit") {
|
|
@@ -3406,46 +3273,32 @@ var test_default = defineDeepspaceCommand({
|
|
|
3406
3273
|
let exitCode = 0;
|
|
3407
3274
|
switch (suite) {
|
|
3408
3275
|
case "smoke":
|
|
3409
|
-
exitCode = runPlaywright(
|
|
3410
|
-
appDir,
|
|
3411
|
-
["tests/smoke.spec.ts"],
|
|
3412
|
-
port,
|
|
3413
|
-
wranglerEnv,
|
|
3414
|
-
sharedDevVarsCache
|
|
3415
|
-
);
|
|
3276
|
+
exitCode = runPlaywright(appDir, ["tests/smoke.spec.ts"], port, wranglerEnv);
|
|
3416
3277
|
break;
|
|
3417
3278
|
case "api":
|
|
3418
|
-
exitCode = runPlaywright(
|
|
3419
|
-
appDir,
|
|
3420
|
-
["tests/api.spec.ts"],
|
|
3421
|
-
port,
|
|
3422
|
-
wranglerEnv,
|
|
3423
|
-
sharedDevVarsCache
|
|
3424
|
-
);
|
|
3279
|
+
exitCode = runPlaywright(appDir, ["tests/api.spec.ts"], port, wranglerEnv);
|
|
3425
3280
|
break;
|
|
3426
3281
|
case "e2e":
|
|
3427
|
-
exitCode = runPlaywright(appDir, [], port, wranglerEnv
|
|
3282
|
+
exitCode = runPlaywright(appDir, [], port, wranglerEnv);
|
|
3428
3283
|
break;
|
|
3429
3284
|
case "unit":
|
|
3430
3285
|
exitCode = runVitest(appDir);
|
|
3431
3286
|
break;
|
|
3432
3287
|
case "all":
|
|
3433
3288
|
exitCode = runVitest(appDir);
|
|
3434
|
-
if (exitCode === 0)
|
|
3435
|
-
exitCode = runPlaywright(appDir, [], port, wranglerEnv, sharedDevVarsCache);
|
|
3289
|
+
if (exitCode === 0) exitCode = runPlaywright(appDir, [], port, wranglerEnv);
|
|
3436
3290
|
break;
|
|
3437
3291
|
case "default":
|
|
3438
3292
|
exitCode = runPlaywright(
|
|
3439
3293
|
appDir,
|
|
3440
3294
|
["tests/smoke.spec.ts", "tests/api.spec.ts"],
|
|
3441
3295
|
port,
|
|
3442
|
-
wranglerEnv
|
|
3443
|
-
sharedDevVarsCache
|
|
3296
|
+
wranglerEnv
|
|
3444
3297
|
);
|
|
3445
3298
|
break;
|
|
3446
3299
|
default:
|
|
3447
3300
|
if (suite.endsWith(".spec.ts")) {
|
|
3448
|
-
exitCode = runPlaywright(appDir, [suite], port, wranglerEnv
|
|
3301
|
+
exitCode = runPlaywright(appDir, [suite], port, wranglerEnv);
|
|
3449
3302
|
} else {
|
|
3450
3303
|
throw new Refusal(
|
|
3451
3304
|
`Unknown test suite: ${suite}
|
|
@@ -3462,10 +3315,10 @@ Available: smoke, api, e2e, unit, all`,
|
|
|
3462
3315
|
return { data: { suite, port, appDir, wranglerEnv: wranglerEnv ?? null } };
|
|
3463
3316
|
}
|
|
3464
3317
|
});
|
|
3465
|
-
function runPlaywright(appDir, testFiles, port, wranglerEnv
|
|
3318
|
+
function runPlaywright(appDir, testFiles, port, wranglerEnv) {
|
|
3466
3319
|
let wranglerConfig;
|
|
3467
3320
|
try {
|
|
3468
|
-
wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv
|
|
3321
|
+
wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv);
|
|
3469
3322
|
} catch (err) {
|
|
3470
3323
|
console.error(err instanceof Error ? err.message : String(err));
|
|
3471
3324
|
return 1;
|
|
@@ -3997,33 +3850,33 @@ function describeFilesFailure(status3, body) {
|
|
|
3997
3850
|
import * as p6 from "@clack/prompts";
|
|
3998
3851
|
import { execSync as execSync2 } from "child_process";
|
|
3999
3852
|
import { createHash as createHash2 } from "crypto";
|
|
4000
|
-
import { existsSync as
|
|
3853
|
+
import { existsSync as existsSync14, readFileSync as readFileSync17, readdirSync as readdirSync4 } from "fs";
|
|
4001
3854
|
import { dirname as dirname6, join as join16, resolve as resolve11 } from "path";
|
|
4002
3855
|
|
|
4003
3856
|
// src/documentation/deploy.ts
|
|
4004
|
-
import { existsSync as
|
|
3857
|
+
import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
|
|
4005
3858
|
import { join as join14 } from "path";
|
|
4006
3859
|
function readDocumentationDeployManifest(appDir, clientDir) {
|
|
4007
|
-
if (!
|
|
3860
|
+
if (!existsSync12(join14(appDir, "documentation.json"))) return void 0;
|
|
4008
3861
|
const manifestPath = join14(clientDir, "_documentation", "manifest.json");
|
|
4009
|
-
if (!
|
|
3862
|
+
if (!existsSync12(manifestPath)) {
|
|
4010
3863
|
throw new Error(
|
|
4011
3864
|
"documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
|
|
4012
3865
|
);
|
|
4013
3866
|
}
|
|
4014
|
-
return JSON.parse(
|
|
3867
|
+
return JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
4015
3868
|
}
|
|
4016
3869
|
|
|
4017
3870
|
// src/cli/commands/update/app-migrations.ts
|
|
4018
3871
|
import {
|
|
4019
3872
|
closeSync,
|
|
4020
3873
|
constants,
|
|
4021
|
-
existsSync as
|
|
3874
|
+
existsSync as existsSync13,
|
|
4022
3875
|
fstatSync,
|
|
4023
3876
|
ftruncateSync,
|
|
4024
3877
|
lstatSync,
|
|
4025
3878
|
openSync,
|
|
4026
|
-
readFileSync as
|
|
3879
|
+
readFileSync as readFileSync16,
|
|
4027
3880
|
writeSync
|
|
4028
3881
|
} from "fs";
|
|
4029
3882
|
import { join as join15 } from "path";
|
|
@@ -4072,7 +3925,7 @@ var APP_MIGRATION_DEFINITIONS = SOURCE_MIGRATIONS.map(
|
|
|
4072
3925
|
);
|
|
4073
3926
|
function readAppliedAppMigrations(appDir) {
|
|
4074
3927
|
const path = join15(appDir, APP_MIGRATIONS_MANIFEST);
|
|
4075
|
-
if (!
|
|
3928
|
+
if (!existsSync13(path)) return [];
|
|
4076
3929
|
const source = readRegularFile(path);
|
|
4077
3930
|
let parsed;
|
|
4078
3931
|
try {
|
|
@@ -4133,7 +3986,7 @@ function planAppMigrations(appDir) {
|
|
|
4133
3986
|
if (pending.length > 0 && blockers.length === 0) {
|
|
4134
3987
|
const manifestPath = `${appPrefix}${APP_MIGRATIONS_MANIFEST}`;
|
|
4135
3988
|
const absoluteManifestPath = join15(repo, manifestPath);
|
|
4136
|
-
const before =
|
|
3989
|
+
const before = existsSync13(absoluteManifestPath) ? readRegularFile(absoluteManifestPath) : null;
|
|
4137
3990
|
const after = `${JSON.stringify([...appliedIds, ...pending.map(({ id }) => id)], null, 2)}
|
|
4138
3991
|
`;
|
|
4139
3992
|
if (before !== after) edits.push({ path: manifestPath, before, after });
|
|
@@ -4149,7 +4002,7 @@ function applyAppMigrationPlan(appDir, plan) {
|
|
|
4149
4002
|
const repo = repoToplevel(appDir);
|
|
4150
4003
|
for (const edit of plan.edits) {
|
|
4151
4004
|
const absolutePath = join15(repo, edit.path);
|
|
4152
|
-
const current =
|
|
4005
|
+
const current = existsSync13(absolutePath) ? readRegularFile(absolutePath) : null;
|
|
4153
4006
|
if (current !== edit.before) {
|
|
4154
4007
|
throw new Error(`Migration input changed after planning: ${edit.path}`);
|
|
4155
4008
|
}
|
|
@@ -4168,7 +4021,7 @@ function readRegularFile(path) {
|
|
|
4168
4021
|
if (!opened.isFile() || opened.dev !== after.dev || opened.ino !== after.ino) {
|
|
4169
4022
|
throw new Error(`Migration input changed while opening: ${path}`);
|
|
4170
4023
|
}
|
|
4171
|
-
return
|
|
4024
|
+
return readFileSync16(fd, "utf-8");
|
|
4172
4025
|
} finally {
|
|
4173
4026
|
closeSync(fd);
|
|
4174
4027
|
}
|
|
@@ -4194,7 +4047,7 @@ function writeRegularFile(path, before, after) {
|
|
|
4194
4047
|
if (!opened.isFile() || opened.dev !== currentPath.dev || opened.ino !== currentPath.ino) {
|
|
4195
4048
|
throw new Error(`Migration input changed while opening: ${path}`);
|
|
4196
4049
|
}
|
|
4197
|
-
if (
|
|
4050
|
+
if (readFileSync16(fd, "utf-8") !== before) {
|
|
4198
4051
|
throw new Error(`Migration input changed after planning: ${path}`);
|
|
4199
4052
|
}
|
|
4200
4053
|
ftruncateSync(fd, 0);
|
|
@@ -4278,7 +4131,7 @@ function declaredCompatibilityDate(config) {
|
|
|
4278
4131
|
return typeof declared === "string" && /^\d{4}-\d{2}-\d{2}$/.test(declared) ? declared : null;
|
|
4279
4132
|
}
|
|
4280
4133
|
async function buildDeployBundle(options) {
|
|
4281
|
-
const { appDir, appName, envName,
|
|
4134
|
+
const { appDir, appName, envName, output, spinner: spinner2 } = options;
|
|
4282
4135
|
const junk = removeMacosJunk(appDir);
|
|
4283
4136
|
if (junk > 0) p6.log.info(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
4284
4137
|
const lintFindings = await lintProjectSchemas(appDir);
|
|
@@ -4288,9 +4141,7 @@ async function buildDeployBundle(options) {
|
|
|
4288
4141
|
spinner2.start("Building...");
|
|
4289
4142
|
let preparedWranglerConfig;
|
|
4290
4143
|
try {
|
|
4291
|
-
preparedWranglerConfig = prepareWranglerEnvConfig(appDir, envName
|
|
4292
|
-
sharedDevVarsCache
|
|
4293
|
-
});
|
|
4144
|
+
preparedWranglerConfig = prepareWranglerEnvConfig(appDir, envName);
|
|
4294
4145
|
const appDomain = DEEPSPACE_ENV === "staging" ? "spacestest.com" : "app.space";
|
|
4295
4146
|
execSync2("npx vite build", {
|
|
4296
4147
|
cwd: appDir,
|
|
@@ -4311,18 +4162,18 @@ async function buildDeployBundle(options) {
|
|
|
4311
4162
|
}
|
|
4312
4163
|
spinner2.stop("Built");
|
|
4313
4164
|
const deployConfigPath = join16(appDir, ".wrangler", "deploy", "config.json");
|
|
4314
|
-
if (!
|
|
4165
|
+
if (!existsSync14(deployConfigPath)) {
|
|
4315
4166
|
output.die(
|
|
4316
4167
|
"Build output config not found at .wrangler/deploy/config.json",
|
|
4317
4168
|
"build_output_missing"
|
|
4318
4169
|
);
|
|
4319
4170
|
}
|
|
4320
|
-
const deployConfig = JSON.parse(
|
|
4171
|
+
const deployConfig = JSON.parse(readFileSync17(deployConfigPath, "utf-8"));
|
|
4321
4172
|
const outputWranglerPath = resolve11(dirname6(deployConfigPath), deployConfig.configPath);
|
|
4322
|
-
if (!
|
|
4173
|
+
if (!existsSync14(outputWranglerPath)) {
|
|
4323
4174
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
4324
4175
|
}
|
|
4325
|
-
const outputConfig = JSON.parse(
|
|
4176
|
+
const outputConfig = JSON.parse(readFileSync17(outputWranglerPath, "utf-8"));
|
|
4326
4177
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
4327
4178
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
4328
4179
|
output.die(
|
|
@@ -4333,10 +4184,10 @@ async function buildDeployBundle(options) {
|
|
|
4333
4184
|
const workerDir = dirname6(outputWranglerPath);
|
|
4334
4185
|
const workerBundlePath = join16(workerDir, outputConfig.main);
|
|
4335
4186
|
const clientDir = outputConfig.assets?.directory ? resolve11(workerDir, outputConfig.assets.directory) : null;
|
|
4336
|
-
if (!
|
|
4187
|
+
if (!existsSync14(workerBundlePath)) {
|
|
4337
4188
|
output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
|
|
4338
4189
|
}
|
|
4339
|
-
if (!clientDir || !
|
|
4190
|
+
if (!clientDir || !existsSync14(clientDir)) {
|
|
4340
4191
|
return output.die(`Client assets not found at ${clientDir}`, "build_output_missing");
|
|
4341
4192
|
}
|
|
4342
4193
|
try {
|
|
@@ -4349,7 +4200,7 @@ async function buildDeployBundle(options) {
|
|
|
4349
4200
|
const assets = collectAssets(clientDir);
|
|
4350
4201
|
const assetConfig = readDeployAssetConfig(clientDir);
|
|
4351
4202
|
spinner2.stop(`Collected ${assets.length} assets`);
|
|
4352
|
-
const workerJs =
|
|
4203
|
+
const workerJs = readFileSync17(workerBundlePath, "utf-8");
|
|
4353
4204
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
4354
4205
|
const sqliteClasses = new Set(
|
|
4355
4206
|
outputConfig.migrations?.flatMap((migration) => migration.new_sqlite_classes ?? []) ?? []
|
|
@@ -4428,7 +4279,7 @@ function collectAssets(dir) {
|
|
|
4428
4279
|
if (entry.isDirectory()) {
|
|
4429
4280
|
walk(fullPath, relativePath);
|
|
4430
4281
|
} else {
|
|
4431
|
-
const bytes =
|
|
4282
|
+
const bytes = readFileSync17(fullPath);
|
|
4432
4283
|
assets.push({
|
|
4433
4284
|
path: `/${relativePath}`,
|
|
4434
4285
|
hash: createHash2("sha256").update(bytes).digest("hex"),
|
|
@@ -4443,10 +4294,13 @@ function collectAssets(dir) {
|
|
|
4443
4294
|
}
|
|
4444
4295
|
function readDeployAssetConfig(clientDir) {
|
|
4445
4296
|
const config = {};
|
|
4446
|
-
for (const [file, key] of [
|
|
4297
|
+
for (const [file, key] of [
|
|
4298
|
+
["_headers", "_headers"],
|
|
4299
|
+
["_redirects", "_redirects"]
|
|
4300
|
+
]) {
|
|
4447
4301
|
const filePath = join16(clientDir, file);
|
|
4448
|
-
if (!
|
|
4449
|
-
const content =
|
|
4302
|
+
if (!existsSync14(filePath)) continue;
|
|
4303
|
+
const content = readFileSync17(filePath, "utf8");
|
|
4450
4304
|
if (content.trim()) config[key] = content;
|
|
4451
4305
|
}
|
|
4452
4306
|
return config;
|
|
@@ -4460,7 +4314,7 @@ function errorMessage(error) {
|
|
|
4460
4314
|
|
|
4461
4315
|
// src/cli/commands/deploy/commerce.ts
|
|
4462
4316
|
import * as p7 from "@clack/prompts";
|
|
4463
|
-
import { existsSync as
|
|
4317
|
+
import { existsSync as existsSync15 } from "fs";
|
|
4464
4318
|
import { join as join17 } from "path";
|
|
4465
4319
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4466
4320
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -4469,7 +4323,7 @@ function errorMessage2(error) {
|
|
|
4469
4323
|
}
|
|
4470
4324
|
async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
|
|
4471
4325
|
const sourcePath = join17(appDir, "src", sourceFile);
|
|
4472
|
-
if (!
|
|
4326
|
+
if (!existsSync15(sourcePath)) return void 0;
|
|
4473
4327
|
const esbuild = await import("esbuild");
|
|
4474
4328
|
const outputPath = join17(appDir, ".wrangler", "deploy", outputFile);
|
|
4475
4329
|
await esbuild.build({
|
|
@@ -4564,7 +4418,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
4564
4418
|
}
|
|
4565
4419
|
}
|
|
4566
4420
|
async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
4567
|
-
if (!
|
|
4421
|
+
if (!existsSync15(join17(appDir, "src", "subscriptions.ts"))) return;
|
|
4568
4422
|
let plans;
|
|
4569
4423
|
try {
|
|
4570
4424
|
plans = await loadDeclaration(
|
|
@@ -4619,7 +4473,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
4619
4473
|
}
|
|
4620
4474
|
}
|
|
4621
4475
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
4622
|
-
if (!
|
|
4476
|
+
if (!existsSync15(join17(appDir, "src", "products.ts"))) return;
|
|
4623
4477
|
let products;
|
|
4624
4478
|
try {
|
|
4625
4479
|
products = await loadDeclaration(
|
|
@@ -4721,7 +4575,7 @@ function createDeployOutput(json) {
|
|
|
4721
4575
|
|
|
4722
4576
|
// src/cli/commands/deploy/request.ts
|
|
4723
4577
|
import * as p10 from "@clack/prompts";
|
|
4724
|
-
import { readFileSync as
|
|
4578
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
4725
4579
|
|
|
4726
4580
|
// src/cli/commands/deploy/repository.ts
|
|
4727
4581
|
import * as p9 from "@clack/prompts";
|
|
@@ -4912,7 +4766,7 @@ function repoApi(deployUrl, token, appId) {
|
|
|
4912
4766
|
}
|
|
4913
4767
|
|
|
4914
4768
|
// src/cli/lib/vc-remote.ts
|
|
4915
|
-
import { existsSync as
|
|
4769
|
+
import { existsSync as existsSync16 } from "fs";
|
|
4916
4770
|
import { tmpdir } from "os";
|
|
4917
4771
|
import { isAbsolute as isAbsolute3, relative, resolve as resolve12 } from "path";
|
|
4918
4772
|
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
@@ -4967,7 +4821,7 @@ function helperEntryUsable(command) {
|
|
|
4967
4821
|
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
4968
4822
|
const expectedEnvironment = DEEPSPACE_ENV === "production" ? void 0 : DEEPSPACE_ENV;
|
|
4969
4823
|
if (!match || match[1] !== expectedEnvironment) return false;
|
|
4970
|
-
return
|
|
4824
|
+
return existsSync16(match[2]) && existsSync16(match[3]);
|
|
4971
4825
|
}
|
|
4972
4826
|
function credentialHelperCommand(environment = DEEPSPACE_ENV) {
|
|
4973
4827
|
const entry = process.argv[1];
|
|
@@ -5554,7 +5408,7 @@ async function deployBuiltBundle(options) {
|
|
|
5554
5408
|
if (bundle.customBindings.length) {
|
|
5555
5409
|
form.append("bindingManifest", JSON.stringify(bundle.customBindings));
|
|
5556
5410
|
}
|
|
5557
|
-
if (secrets.
|
|
5411
|
+
if (secrets.authoritative) form.append("userSecrets", JSON.stringify(secrets.values));
|
|
5558
5412
|
if (bundle.extraRoutes === true || bundle.extraRoutes.length) {
|
|
5559
5413
|
form.append("extraRunWorkerFirst", JSON.stringify(bundle.extraRoutes));
|
|
5560
5414
|
}
|
|
@@ -5746,7 +5600,7 @@ async function uploadDeployAssets(options) {
|
|
|
5746
5600
|
// against the plan before it touches storage, and a stream body
|
|
5747
5601
|
// would be sent chunked with no length at all; a retry also needs a
|
|
5748
5602
|
// body it can send again.
|
|
5749
|
-
body:
|
|
5603
|
+
body: readFileSync18(asset.sourcePath)
|
|
5750
5604
|
})
|
|
5751
5605
|
);
|
|
5752
5606
|
if (!response.ok) {
|
|
@@ -5762,9 +5616,7 @@ async function uploadDeployAssets(options) {
|
|
|
5762
5616
|
}
|
|
5763
5617
|
};
|
|
5764
5618
|
try {
|
|
5765
|
-
await Promise.all(
|
|
5766
|
-
Array.from({ length: Math.min(UPLOAD_CONCURRENCY, missing.length) }, worker)
|
|
5767
|
-
);
|
|
5619
|
+
await Promise.all(Array.from({ length: Math.min(UPLOAD_CONCURRENCY, missing.length) }, worker));
|
|
5768
5620
|
} catch (error) {
|
|
5769
5621
|
spinner2.stop("Deploy failed");
|
|
5770
5622
|
if (error instanceof AssetUploadError) {
|
|
@@ -5833,35 +5685,17 @@ async function postWithRetry(url, makeInit, {
|
|
|
5833
5685
|
|
|
5834
5686
|
// src/cli/commands/deploy/secrets.ts
|
|
5835
5687
|
import * as p11 from "@clack/prompts";
|
|
5836
|
-
import { existsSync as existsSync18, readFileSync as readFileSync20 } from "fs";
|
|
5837
|
-
function classifyDevVarsSecrets(options) {
|
|
5838
|
-
const store = new Set(options.storeSecretNames);
|
|
5839
|
-
const strayKeys = options.handEditedDevVarKeys.filter((key) => !store.has(key));
|
|
5840
|
-
if (strayKeys.length === 0) return { kind: "ok" };
|
|
5841
|
-
if (options.storeSecretNames.length === 0 && !options.allowMissing) {
|
|
5842
|
-
return { kind: "block", strayKeys };
|
|
5843
|
-
}
|
|
5844
|
-
return { kind: "warn", strayKeys };
|
|
5845
|
-
}
|
|
5846
5688
|
async function loadDeploySecrets(options) {
|
|
5847
5689
|
const { deployUrl, appDir, appId, envName, ownerId, token, output } = options;
|
|
5848
5690
|
let linked = null;
|
|
5849
|
-
let localOnlyDevVars = {};
|
|
5850
5691
|
try {
|
|
5851
5692
|
const refreshed = await refreshSecretsCache(deployUrl, token, appId, envName);
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
await writeDevVars(appDir, ownerId, token, envName, {
|
|
5859
|
-
appId,
|
|
5860
|
-
generatedSecretsCache: refreshed.rendered,
|
|
5861
|
-
sharedDevVarsCache: true
|
|
5862
|
-
});
|
|
5863
|
-
if (refreshed.summary) p11.log.info(refreshed.summary);
|
|
5864
|
-
}
|
|
5693
|
+
linked = refreshed.pulled;
|
|
5694
|
+
await writeDevVars(appDir, ownerId, token, envName, {
|
|
5695
|
+
appId,
|
|
5696
|
+
generatedSecretsCache: refreshed.rendered
|
|
5697
|
+
});
|
|
5698
|
+
if (refreshed.summary) p11.log.info(refreshed.summary);
|
|
5865
5699
|
} catch (error) {
|
|
5866
5700
|
if (error?.status === 403) {
|
|
5867
5701
|
output.die(
|
|
@@ -5874,40 +5708,31 @@ async function loadDeploySecrets(options) {
|
|
|
5874
5708
|
"secrets_refresh_failed"
|
|
5875
5709
|
);
|
|
5876
5710
|
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
function prepareDeploySecrets(options) {
|
|
5880
|
-
const { appDir, envName, cache, allowMissing, customBindings, doManifest, output } = options;
|
|
5881
|
-
const values = cache.linked ? { ...cache.linked.values } : {};
|
|
5882
|
-
const devVarsPath = devVarsPathFor(appDir, envName, {
|
|
5883
|
-
sharedDevVarsCache: cache.linked !== null
|
|
5884
|
-
});
|
|
5885
|
-
const handEditedDevVars = cache.linked ? cache.localOnlyDevVars : readCustomDevVars(devVarsPath, output);
|
|
5886
|
-
const decision = classifyDevVarsSecrets({
|
|
5887
|
-
storeSecretNames: Object.keys(values),
|
|
5888
|
-
handEditedDevVarKeys: Object.keys(handEditedDevVars),
|
|
5889
|
-
allowMissing
|
|
5890
|
-
});
|
|
5891
|
-
if (decision.kind === "block") {
|
|
5892
|
-
const uploadArgv = [
|
|
5893
|
-
"deepspace",
|
|
5894
|
-
"secrets",
|
|
5895
|
-
"upload",
|
|
5896
|
-
".dev.vars",
|
|
5897
|
-
...envName ? ["-e", envName] : []
|
|
5898
|
-
];
|
|
5711
|
+
if (!linked) {
|
|
5712
|
+
const configName = defaultConfigNameForEnv(envName);
|
|
5899
5713
|
output.die(
|
|
5900
|
-
`
|
|
5901
|
-
"
|
|
5902
|
-
{
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5714
|
+
`Secrets config "${configName}" does not exist. Create it explicitly before deploying; this distinguishes an intentional empty config from an uninitialized app and protects live Worker secrets during the platform rollout.`,
|
|
5715
|
+
"secrets_config_missing",
|
|
5716
|
+
{
|
|
5717
|
+
action: {
|
|
5718
|
+
cwd: appDir,
|
|
5719
|
+
argv: [
|
|
5720
|
+
"deepspace",
|
|
5721
|
+
"secrets",
|
|
5722
|
+
"configs",
|
|
5723
|
+
"create",
|
|
5724
|
+
configName,
|
|
5725
|
+
...envName ? ["--env", envName] : []
|
|
5726
|
+
]
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5909
5729
|
);
|
|
5910
5730
|
}
|
|
5731
|
+
return { linked };
|
|
5732
|
+
}
|
|
5733
|
+
function prepareDeploySecrets(options) {
|
|
5734
|
+
const { cache, customBindings, doManifest, output } = options;
|
|
5735
|
+
const values = cache.linked ? { ...cache.linked.values } : {};
|
|
5911
5736
|
const names = Object.keys(values);
|
|
5912
5737
|
const declaredBindingNames = /* @__PURE__ */ new Set([
|
|
5913
5738
|
...customBindings.map((binding) => binding.name),
|
|
@@ -5928,17 +5753,7 @@ function prepareDeploySecrets(options) {
|
|
|
5928
5753
|
}
|
|
5929
5754
|
}
|
|
5930
5755
|
if (names.length) p11.log.info(`App secrets: ${names.join(", ")}`);
|
|
5931
|
-
return { values, names };
|
|
5932
|
-
}
|
|
5933
|
-
function readCustomDevVars(path, output) {
|
|
5934
|
-
if (!existsSync18(path)) return {};
|
|
5935
|
-
const section = extractCustomDevVars(stripGeneratedSecretsCache(readFileSync20(path, "utf-8")));
|
|
5936
|
-
if (!section) return {};
|
|
5937
|
-
try {
|
|
5938
|
-
return parseDevVars(section);
|
|
5939
|
-
} catch (error) {
|
|
5940
|
-
output.die(`.dev.vars: ${errorMessage5(error)}`, "invalid_dev_vars");
|
|
5941
|
-
}
|
|
5756
|
+
return { values, names, authoritative: cache.linked !== null };
|
|
5942
5757
|
}
|
|
5943
5758
|
function errorMessage5(error) {
|
|
5944
5759
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -5977,11 +5792,6 @@ var deploy_default = defineCommand3({
|
|
|
5977
5792
|
description: "Platform admins only: claim an app name still inside its 30-day release cooldown. The previous owner permanently loses their reserved reclaim. Non-admin accounts are refused.",
|
|
5978
5793
|
default: false
|
|
5979
5794
|
},
|
|
5980
|
-
"allow-missing-secrets": {
|
|
5981
|
-
type: "boolean",
|
|
5982
|
-
description: "Deploy even though hand-edited .dev.vars secrets are absent from the store (they will NOT be deployed, and any a previous deploy set are dropped).",
|
|
5983
|
-
default: false
|
|
5984
|
-
},
|
|
5985
5795
|
// Citty maps --no-push to the negation of an argument named `push`.
|
|
5986
5796
|
push: {
|
|
5987
5797
|
type: "boolean",
|
|
@@ -6055,7 +5865,6 @@ var deploy_default = defineCommand3({
|
|
|
6055
5865
|
appDir,
|
|
6056
5866
|
appName,
|
|
6057
5867
|
envName,
|
|
6058
|
-
sharedDevVarsCache: secretsCache.linked !== null,
|
|
6059
5868
|
output,
|
|
6060
5869
|
spinner: spinner2
|
|
6061
5870
|
});
|
|
@@ -6075,10 +5884,7 @@ var deploy_default = defineCommand3({
|
|
|
6075
5884
|
sourceState
|
|
6076
5885
|
});
|
|
6077
5886
|
const secrets = prepareDeploySecrets({
|
|
6078
|
-
appDir,
|
|
6079
|
-
envName,
|
|
6080
5887
|
cache: secretsCache,
|
|
6081
|
-
allowMissing: Boolean(args["allow-missing-secrets"]),
|
|
6082
5888
|
customBindings: bundle.customBindings,
|
|
6083
5889
|
doManifest: bundle.doManifest,
|
|
6084
5890
|
output
|
|
@@ -6818,7 +6624,7 @@ var usage_default = defineDeepspaceCommand({
|
|
|
6818
6624
|
});
|
|
6819
6625
|
|
|
6820
6626
|
// src/cli/commands/add.ts
|
|
6821
|
-
import { existsSync as
|
|
6627
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, realpathSync as realpathSync4 } from "fs";
|
|
6822
6628
|
import { dirname as dirname8, join as join19, resolve as resolve15 } from "path";
|
|
6823
6629
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
6824
6630
|
|
|
@@ -6826,12 +6632,12 @@ import { pathToFileURL as pathToFileURL3 } from "url";
|
|
|
6826
6632
|
import {
|
|
6827
6633
|
closeSync as closeSync2,
|
|
6828
6634
|
copyFileSync,
|
|
6829
|
-
existsSync as
|
|
6635
|
+
existsSync as existsSync17,
|
|
6830
6636
|
fstatSync as fstatSync2,
|
|
6831
6637
|
mkdirSync as mkdirSync6,
|
|
6832
6638
|
mkdtempSync,
|
|
6833
6639
|
openSync as openSync2,
|
|
6834
|
-
readFileSync as
|
|
6640
|
+
readFileSync as readFileSync19,
|
|
6835
6641
|
readSync,
|
|
6836
6642
|
readdirSync as readdirSync5,
|
|
6837
6643
|
rmSync as rmSync4,
|
|
@@ -6846,9 +6652,9 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
6846
6652
|
let dir = dirname7(fileURLToPath(moduleUrl));
|
|
6847
6653
|
for (; ; ) {
|
|
6848
6654
|
const packagePath = join18(dir, "package.json");
|
|
6849
|
-
if (
|
|
6655
|
+
if (existsSync17(packagePath)) {
|
|
6850
6656
|
try {
|
|
6851
|
-
const pkg2 = JSON.parse(
|
|
6657
|
+
const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
6852
6658
|
if (pkg2.name === "deepspace") return dir;
|
|
6853
6659
|
} catch {
|
|
6854
6660
|
}
|
|
@@ -6859,13 +6665,13 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
6859
6665
|
}
|
|
6860
6666
|
}
|
|
6861
6667
|
function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
|
|
6862
|
-
return
|
|
6668
|
+
return existsSync17(join18(packageRoot, "features"));
|
|
6863
6669
|
}
|
|
6864
6670
|
function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
6865
6671
|
const configPath = join18(packageRoot, "features", id, "feature.json");
|
|
6866
|
-
if (!
|
|
6672
|
+
if (!existsSync17(configPath)) return null;
|
|
6867
6673
|
try {
|
|
6868
|
-
const config = JSON.parse(
|
|
6674
|
+
const config = JSON.parse(readFileSync19(configPath, "utf-8"));
|
|
6869
6675
|
if (typeof config.id !== "string" || typeof config.name !== "string" || !Array.isArray(config.files)) {
|
|
6870
6676
|
return null;
|
|
6871
6677
|
}
|
|
@@ -6876,7 +6682,7 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
|
6876
6682
|
}
|
|
6877
6683
|
function listFeatureConfigs(packageRoot = featurePackageRoot()) {
|
|
6878
6684
|
const featuresDir = join18(packageRoot, "features");
|
|
6879
|
-
if (!
|
|
6685
|
+
if (!existsSync17(featuresDir)) return [];
|
|
6880
6686
|
return readdirSync5(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readFeatureConfig(entry.name, packageRoot)).filter((config) => config !== null && !config.internal).sort((a, b) => a.id.localeCompare(b.id));
|
|
6881
6687
|
}
|
|
6882
6688
|
function featureCatalog(packageRoot = featurePackageRoot()) {
|
|
@@ -6930,7 +6736,7 @@ function copyFile(src, dest) {
|
|
|
6930
6736
|
function copyFileWithImportShift(src, dest, levels) {
|
|
6931
6737
|
mkdirSync6(dirname7(dest), { recursive: true });
|
|
6932
6738
|
const prefix = "../".repeat(levels);
|
|
6933
|
-
const shifted =
|
|
6739
|
+
const shifted = readFileSync19(src, "utf-8").replace(
|
|
6934
6740
|
/(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
|
|
6935
6741
|
`$1$2${prefix}$3`
|
|
6936
6742
|
);
|
|
@@ -6946,9 +6752,9 @@ function installFiles(packageRoot, config, targetDir, emit) {
|
|
|
6946
6752
|
const srcPath = join18(packageRoot, "features", config.id, file.src);
|
|
6947
6753
|
const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
|
|
6948
6754
|
const destPath = join18(targetDir, resolvedDest);
|
|
6949
|
-
const existed =
|
|
6755
|
+
const existed = existsSync17(destPath);
|
|
6950
6756
|
if (existed) preexisting.push(resolvedDest);
|
|
6951
|
-
if (!
|
|
6757
|
+
if (!existsSync17(srcPath)) {
|
|
6952
6758
|
emit(` Warning: source not found: ${file.src}`, "stderr");
|
|
6953
6759
|
if (!existed) missing.push(resolvedDest);
|
|
6954
6760
|
continue;
|
|
@@ -6986,10 +6792,10 @@ function unresolvedSchema(schema, reason, emit) {
|
|
|
6986
6792
|
function integrateSchema(config, targetDir, emit) {
|
|
6987
6793
|
if (!config.schema) return [];
|
|
6988
6794
|
const schemasPath = join18(targetDir, "src", "schemas.ts");
|
|
6989
|
-
if (!
|
|
6795
|
+
if (!existsSync17(schemasPath)) {
|
|
6990
6796
|
return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
|
|
6991
6797
|
}
|
|
6992
|
-
let content =
|
|
6798
|
+
let content = readFileSync19(schemasPath, "utf-8");
|
|
6993
6799
|
const { exportName, importPath, spreadOperator } = config.schema;
|
|
6994
6800
|
const importPattern = new RegExp(`^import\\s+\\{[^}]*\\b${exportName}\\b`, "m");
|
|
6995
6801
|
if (importPattern.test(content)) {
|
|
@@ -7043,10 +6849,10 @@ function unresolvedActions(actions, reason, emit) {
|
|
|
7043
6849
|
function integrateActions(config, targetDir, emit) {
|
|
7044
6850
|
if (!config.actions?.length) return [];
|
|
7045
6851
|
const actionsPath = join18(targetDir, "src", "actions", "index.ts");
|
|
7046
|
-
if (!
|
|
6852
|
+
if (!existsSync17(actionsPath)) {
|
|
7047
6853
|
return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
|
|
7048
6854
|
}
|
|
7049
|
-
let content =
|
|
6855
|
+
let content = readFileSync19(actionsPath, "utf-8");
|
|
7050
6856
|
const registryPattern = /export const actions:\s*Record<string,\s*ActionHandler(?:<[^>]*>)?>\s*=\s*\{/;
|
|
7051
6857
|
if (!registryPattern.test(content)) {
|
|
7052
6858
|
const reason = "src/actions/index.ts has an unexpected structure.";
|
|
@@ -7093,7 +6899,7 @@ function integrateCode(config, targetDir, emit) {
|
|
|
7093
6899
|
const unresolved = [];
|
|
7094
6900
|
for (const insertion of config.code ?? []) {
|
|
7095
6901
|
const targetPath = join18(targetDir, insertion.file);
|
|
7096
|
-
if (!
|
|
6902
|
+
if (!existsSync17(targetPath)) {
|
|
7097
6903
|
unresolved.push({
|
|
7098
6904
|
area: "code",
|
|
7099
6905
|
reason: `${insertion.file} was not found.`,
|
|
@@ -7101,7 +6907,7 @@ function integrateCode(config, targetDir, emit) {
|
|
|
7101
6907
|
});
|
|
7102
6908
|
continue;
|
|
7103
6909
|
}
|
|
7104
|
-
const original =
|
|
6910
|
+
const original = readFileSync19(targetPath, "utf-8");
|
|
7105
6911
|
if (original.includes(insertion.insert.trim())) {
|
|
7106
6912
|
emit(` Integration already present: ${insertion.file}`);
|
|
7107
6913
|
continue;
|
|
@@ -7137,7 +6943,7 @@ ${content.slice(importIndex < 0 ? 0 : importIndex)}`;
|
|
|
7137
6943
|
function integrateIgnore(config, targetDir) {
|
|
7138
6944
|
if (!config.ignore?.length) return;
|
|
7139
6945
|
const targetPath = join18(targetDir, ".gitignore");
|
|
7140
|
-
const current =
|
|
6946
|
+
const current = existsSync17(targetPath) ? readFileSync19(targetPath, "utf-8") : "";
|
|
7141
6947
|
const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
7142
6948
|
const missing = config.ignore.filter((pattern) => !present.has(pattern));
|
|
7143
6949
|
if (missing.length === 0) return;
|
|
@@ -7154,10 +6960,10 @@ function integrateWrangler(config, targetDir, emit) {
|
|
|
7154
6960
|
...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
|
|
7155
6961
|
...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
|
|
7156
6962
|
];
|
|
7157
|
-
if (!
|
|
6963
|
+
if (!existsSync17(targetPath)) {
|
|
7158
6964
|
return [{ area: "wrangler", reason: "wrangler.toml was not found.", steps }];
|
|
7159
6965
|
}
|
|
7160
|
-
let source =
|
|
6966
|
+
let source = readFileSync19(targetPath, "utf-8");
|
|
7161
6967
|
if (runWorkerFirst && !/^\[assets\]\s*$/m.test(source)) {
|
|
7162
6968
|
return [{ area: "wrangler", reason: "wrangler.toml has no [assets] section.", steps }];
|
|
7163
6969
|
}
|
|
@@ -7234,7 +7040,7 @@ function rateLimitBindingBlock(scope, binding) {
|
|
|
7234
7040
|
function integrateCss(packageRoot, config, targetDir, emit) {
|
|
7235
7041
|
if (!config.css?.length) return [];
|
|
7236
7042
|
const stylesPath = join18(targetDir, "src", "styles.css");
|
|
7237
|
-
if (!
|
|
7043
|
+
if (!existsSync17(stylesPath)) {
|
|
7238
7044
|
emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
|
|
7239
7045
|
return [
|
|
7240
7046
|
{
|
|
@@ -7246,12 +7052,12 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
7246
7052
|
}
|
|
7247
7053
|
];
|
|
7248
7054
|
}
|
|
7249
|
-
let styles =
|
|
7055
|
+
let styles = readFileSync19(stylesPath, "utf-8");
|
|
7250
7056
|
let integrated = 0;
|
|
7251
7057
|
const unresolved = [];
|
|
7252
7058
|
for (const cssFile of config.css) {
|
|
7253
7059
|
const srcPath = join18(packageRoot, "features", config.id, cssFile);
|
|
7254
|
-
if (!
|
|
7060
|
+
if (!existsSync17(srcPath)) {
|
|
7255
7061
|
emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
|
|
7256
7062
|
unresolved.push({
|
|
7257
7063
|
area: "css",
|
|
@@ -7260,7 +7066,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
7260
7066
|
});
|
|
7261
7067
|
continue;
|
|
7262
7068
|
}
|
|
7263
|
-
const css =
|
|
7069
|
+
const css = readFileSync19(srcPath, "utf-8");
|
|
7264
7070
|
const fingerprint = css.match(/\.([\w-]+)\s*\{|@keyframes\s+([\w-]+)/);
|
|
7265
7071
|
const marker = fingerprint?.[1] ?? fingerprint?.[2];
|
|
7266
7072
|
if (marker && styles.includes(marker)) {
|
|
@@ -7288,10 +7094,10 @@ function unresolvedRoute(route, config, reason, emit) {
|
|
|
7288
7094
|
function integrateRoute(config, targetDir, emit) {
|
|
7289
7095
|
if (!config.route) return [];
|
|
7290
7096
|
const navPath = join18(targetDir, "src", "nav.ts");
|
|
7291
|
-
if (!
|
|
7097
|
+
if (!existsSync17(navPath)) {
|
|
7292
7098
|
return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
|
|
7293
7099
|
}
|
|
7294
|
-
let content =
|
|
7100
|
+
let content = readFileSync19(navPath, "utf-8");
|
|
7295
7101
|
const routePath = config.route.path;
|
|
7296
7102
|
if (content.includes(`'${routePath}'`)) {
|
|
7297
7103
|
emit(` Nav already present: ${routePath}`);
|
|
@@ -7366,13 +7172,13 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
7366
7172
|
return { added: [], installRan: false, packageManager };
|
|
7367
7173
|
}
|
|
7368
7174
|
const packagePath = join18(targetDir, "package.json");
|
|
7369
|
-
if (!
|
|
7175
|
+
if (!existsSync17(packagePath)) {
|
|
7370
7176
|
emit(" Could not patch package.json (file not found)");
|
|
7371
7177
|
return { added: [], installRan: false, packageManager };
|
|
7372
7178
|
}
|
|
7373
7179
|
let pkg2;
|
|
7374
7180
|
try {
|
|
7375
|
-
pkg2 = JSON.parse(
|
|
7181
|
+
pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
7376
7182
|
} catch (error) {
|
|
7377
7183
|
emit(
|
|
7378
7184
|
` Could not patch package.json (parse error: ${error instanceof Error ? error.message : String(error)})`
|
|
@@ -7432,7 +7238,7 @@ ${child.diagnosticTail}` : "";
|
|
|
7432
7238
|
}
|
|
7433
7239
|
function dependencySnapshot(targetDir) {
|
|
7434
7240
|
try {
|
|
7435
|
-
const pkg2 = JSON.parse(
|
|
7241
|
+
const pkg2 = JSON.parse(readFileSync19(join18(targetDir, "package.json"), "utf-8"));
|
|
7436
7242
|
return {
|
|
7437
7243
|
dependencies: pkg2.dependencies ?? {},
|
|
7438
7244
|
devDependencies: pkg2.devDependencies ?? {}
|
|
@@ -7444,7 +7250,7 @@ function dependencySnapshot(targetDir) {
|
|
|
7444
7250
|
function installedNavFeatures(packageRoot, targetDir) {
|
|
7445
7251
|
return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
|
|
7446
7252
|
const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
|
|
7447
|
-
return
|
|
7253
|
+
return existsSync17(join18(targetDir, signature));
|
|
7448
7254
|
}).map((feature) => feature.id);
|
|
7449
7255
|
}
|
|
7450
7256
|
function warnNavConflicts(packageRoot, config, targetDir, emit) {
|
|
@@ -7677,11 +7483,11 @@ function renderFeatureInfo(config) {
|
|
|
7677
7483
|
return lines.join("\n");
|
|
7678
7484
|
}
|
|
7679
7485
|
function isDeepSpaceApp(dir) {
|
|
7680
|
-
if (
|
|
7486
|
+
if (existsSync18(join19(dir, "wrangler.toml"))) return true;
|
|
7681
7487
|
const packagePath = join19(dir, "package.json");
|
|
7682
|
-
if (!
|
|
7488
|
+
if (!existsSync18(packagePath)) return false;
|
|
7683
7489
|
try {
|
|
7684
|
-
const pkg2 = JSON.parse(
|
|
7490
|
+
const pkg2 = JSON.parse(readFileSync20(packagePath, "utf-8"));
|
|
7685
7491
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
7686
7492
|
} catch {
|
|
7687
7493
|
return false;
|
|
@@ -7691,7 +7497,7 @@ function installedPackageRoot(fromDir) {
|
|
|
7691
7497
|
let dir = fromDir;
|
|
7692
7498
|
for (; ; ) {
|
|
7693
7499
|
const candidate = join19(dir, "node_modules", "deepspace");
|
|
7694
|
-
if (
|
|
7500
|
+
if (existsSync18(join19(candidate, "package.json"))) return candidate;
|
|
7695
7501
|
const parent = dirname8(dir);
|
|
7696
7502
|
if (parent === dir) return null;
|
|
7697
7503
|
dir = parent;
|
|
@@ -7710,7 +7516,7 @@ async function targetInstaller(appDir) {
|
|
|
7710
7516
|
}
|
|
7711
7517
|
const entry = join19(installedRoot, "dist", "feature-installer.js");
|
|
7712
7518
|
const catalog = join19(installedRoot, "features");
|
|
7713
|
-
if (!
|
|
7519
|
+
if (!existsSync18(entry) || !existsSync18(catalog)) {
|
|
7714
7520
|
throw new Refusal(
|
|
7715
7521
|
`The app's installed deepspace package does not contain the typed feature installer.
|
|
7716
7522
|
Expected: ${entry}
|
|
@@ -7803,7 +7609,7 @@ var add_default = defineDeepspaceCommand({
|
|
|
7803
7609
|
return { data: { features: featureCatalog(root) } };
|
|
7804
7610
|
}
|
|
7805
7611
|
const appDir = resolve15(args.dir ?? ".");
|
|
7806
|
-
if (!
|
|
7612
|
+
if (!existsSync18(appDir)) {
|
|
7807
7613
|
throw new Refusal(
|
|
7808
7614
|
`Target directory not found: ${appDir}
|
|
7809
7615
|
Create a new app first: deepspace app create <name>`,
|
|
@@ -8179,7 +7985,7 @@ var domain_default = defineCommand5({
|
|
|
8179
7985
|
// src/cli/commands/app-files.ts
|
|
8180
7986
|
import { defineCommand as defineCommand6 } from "citty";
|
|
8181
7987
|
import { basename as basename4, resolve as resolve16 } from "path";
|
|
8182
|
-
import { existsSync as
|
|
7988
|
+
import { existsSync as existsSync19, statSync as statSync2 } from "fs";
|
|
8183
7989
|
|
|
8184
7990
|
// src/cli/lib/app-files-api.ts
|
|
8185
7991
|
import { createWriteStream, openAsBlob, statSync } from "fs";
|
|
@@ -8450,7 +8256,7 @@ var put = defineDeepspaceCommand({
|
|
|
8450
8256
|
},
|
|
8451
8257
|
async run({ args }) {
|
|
8452
8258
|
const localPath = resolve16(String(args.file));
|
|
8453
|
-
if (!
|
|
8259
|
+
if (!existsSync19(localPath) || !statSync2(localPath).isFile()) {
|
|
8454
8260
|
throw new Refusal(`No such file: ${args.file}`, "file_not_found");
|
|
8455
8261
|
}
|
|
8456
8262
|
const key = requireKey(args.key ?? basename4(localPath));
|
|
@@ -8920,7 +8726,7 @@ var transfer_default = defineCommand8({
|
|
|
8920
8726
|
import { defineCommand as defineCommand9 } from "citty";
|
|
8921
8727
|
|
|
8922
8728
|
// src/cli/commands/_invoke-impl.ts
|
|
8923
|
-
import { readFileSync as
|
|
8729
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
8924
8730
|
import * as p15 from "@clack/prompts";
|
|
8925
8731
|
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
8926
8732
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -8961,7 +8767,7 @@ function resolveBody(opts) {
|
|
|
8961
8767
|
if (opts.body != null) {
|
|
8962
8768
|
raw = opts.body;
|
|
8963
8769
|
} else if (opts.bodyFile != null) {
|
|
8964
|
-
raw = opts.bodyFile === "-" ?
|
|
8770
|
+
raw = opts.bodyFile === "-" ? readFileSync21(0, "utf-8") : readFileSync21(opts.bodyFile, "utf-8");
|
|
8965
8771
|
} else {
|
|
8966
8772
|
return "{}";
|
|
8967
8773
|
}
|
|
@@ -9240,7 +9046,7 @@ var integrations_default = defineCommand9({
|
|
|
9240
9046
|
|
|
9241
9047
|
// src/cli/commands/feedback.ts
|
|
9242
9048
|
import * as p16 from "@clack/prompts";
|
|
9243
|
-
import { existsSync as
|
|
9049
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
|
|
9244
9050
|
import { dirname as dirname9, join as join20 } from "path";
|
|
9245
9051
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9246
9052
|
import { release } from "os";
|
|
@@ -9268,8 +9074,8 @@ function readCliVersion() {
|
|
|
9268
9074
|
join20(here, "..", "..", "..", "package.json")
|
|
9269
9075
|
];
|
|
9270
9076
|
for (const path of candidates) {
|
|
9271
|
-
if (!
|
|
9272
|
-
const pkg2 = JSON.parse(
|
|
9077
|
+
if (!existsSync20(path)) continue;
|
|
9078
|
+
const pkg2 = JSON.parse(readFileSync22(path, "utf-8"));
|
|
9273
9079
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
9274
9080
|
}
|
|
9275
9081
|
} catch {
|
|
@@ -9465,7 +9271,7 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
9465
9271
|
|
|
9466
9272
|
// src/cli/commands/secrets.ts
|
|
9467
9273
|
import { defineCommand as defineCommand10 } from "citty";
|
|
9468
|
-
import { readFileSync as
|
|
9274
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
9469
9275
|
|
|
9470
9276
|
// src/cli/lib/citty-args.ts
|
|
9471
9277
|
function dedupePositionals(bound, rest) {
|
|
@@ -9735,7 +9541,7 @@ var upload = defineCommand10({
|
|
|
9735
9541
|
async run({ args }) {
|
|
9736
9542
|
try {
|
|
9737
9543
|
const t = await resolveTarget(args);
|
|
9738
|
-
const content = args.file === "-" ?
|
|
9544
|
+
const content = args.file === "-" ? readFileSync23(0, "utf-8") : readFileSync23(args.file, "utf-8");
|
|
9739
9545
|
const secrets = parseSecretsUpload(content);
|
|
9740
9546
|
if (Object.keys(secrets).length === 0)
|
|
9741
9547
|
fail(new InputError("No secrets found in the input.", "empty_input"));
|
|
@@ -9799,27 +9605,24 @@ var pull = defineCommand10({
|
|
|
9799
9605
|
);
|
|
9800
9606
|
const t = await resolveTarget(args);
|
|
9801
9607
|
const ownerId = decodeJwtPayload(t.token).sub;
|
|
9802
|
-
const
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
)
|
|
9810
|
-
);
|
|
9811
|
-
return;
|
|
9812
|
-
}
|
|
9608
|
+
const refreshed = await refreshSecretsCache(
|
|
9609
|
+
DEPLOY_URL11,
|
|
9610
|
+
t.token,
|
|
9611
|
+
t.appId,
|
|
9612
|
+
wranglerEnv,
|
|
9613
|
+
t.configName
|
|
9614
|
+
);
|
|
9813
9615
|
await writeDevVars(appDir, ownerId, t.token, wranglerEnv, {
|
|
9814
9616
|
appId: t.appId,
|
|
9815
|
-
generatedSecretsCache:
|
|
9816
|
-
sharedDevVarsCache: true
|
|
9617
|
+
generatedSecretsCache: refreshed.rendered
|
|
9817
9618
|
});
|
|
9818
|
-
const count = Object.keys(pulled
|
|
9619
|
+
const count = Object.keys(refreshed.pulled?.values ?? {}).length;
|
|
9819
9620
|
ok(
|
|
9820
9621
|
args.json === true,
|
|
9821
9622
|
{ appId: t.appId, config: t.configName, pulled: count },
|
|
9822
|
-
() => console.log(
|
|
9623
|
+
() => console.log(
|
|
9624
|
+
refreshed.pulled ? `Pulled ${count} secrets (${t.configName}) into .dev.vars.` : `Config ${t.configName} does not exist yet; regenerated .dev.vars without app secrets.`
|
|
9625
|
+
)
|
|
9823
9626
|
);
|
|
9824
9627
|
} catch (err) {
|
|
9825
9628
|
fail(err);
|
|
@@ -9991,7 +9794,7 @@ var init_default = defineDeepspaceCommand({
|
|
|
9991
9794
|
// src/cli/commands/status.ts
|
|
9992
9795
|
import { defineCommand as defineCommand11 } from "citty";
|
|
9993
9796
|
import * as p17 from "@clack/prompts";
|
|
9994
|
-
import { existsSync as
|
|
9797
|
+
import { existsSync as existsSync21, readFileSync as readFileSync24 } from "fs";
|
|
9995
9798
|
function timeAgo(iso) {
|
|
9996
9799
|
const ms = Date.now() - Date.parse(iso);
|
|
9997
9800
|
if (!Number.isFinite(ms) || ms < 0) return iso;
|
|
@@ -10076,7 +9879,7 @@ var status_default = defineCommand11({
|
|
|
10076
9879
|
json.services = urls;
|
|
10077
9880
|
if (urlOverrides.length > 0) json.urlOverrides = urlOverrides;
|
|
10078
9881
|
}
|
|
10079
|
-
facts.loggedIn =
|
|
9882
|
+
facts.loggedIn = existsSync21(SESSION_PATH);
|
|
10080
9883
|
let userId = null;
|
|
10081
9884
|
if (!facts.loggedIn) {
|
|
10082
9885
|
lines.push(["Session", "not logged in"]);
|
|
@@ -10085,7 +9888,7 @@ var status_default = defineCommand11({
|
|
|
10085
9888
|
let who = "logged in";
|
|
10086
9889
|
try {
|
|
10087
9890
|
const payload = decodeJwtPayload(
|
|
10088
|
-
|
|
9891
|
+
readFileSync24(TOKEN_PATH, "utf-8").trim()
|
|
10089
9892
|
);
|
|
10090
9893
|
if (payload.email) who = payload.email;
|
|
10091
9894
|
userId = payload.sub ?? null;
|
|
@@ -10634,12 +10437,7 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10634
10437
|
}
|
|
10635
10438
|
spinner2?.message(`Fetching ${branch} from the cloud repo\u2026`);
|
|
10636
10439
|
const trackingRef = spaceTrackingRef(branch);
|
|
10637
|
-
runGitRemote(appDir, token, [
|
|
10638
|
-
"fetch",
|
|
10639
|
-
"--quiet",
|
|
10640
|
-
SPACE_REMOTE,
|
|
10641
|
-
`+${refName}:${trackingRef}`
|
|
10642
|
-
]);
|
|
10440
|
+
runGitRemote(appDir, token, ["fetch", "--quiet", SPACE_REMOTE, `+${refName}:${trackingRef}`]);
|
|
10643
10441
|
const remoteOid = resolveCommit(appDir, trackingRef);
|
|
10644
10442
|
if (!remoteOid) {
|
|
10645
10443
|
spinner2?.stop("Fetch failed.");
|
|
@@ -10677,6 +10475,8 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10677
10475
|
integration = "fast_forwarded";
|
|
10678
10476
|
}
|
|
10679
10477
|
}
|
|
10478
|
+
} else if (isAncestor(appDir, remoteOid, localOid)) {
|
|
10479
|
+
integration = "local_ahead";
|
|
10680
10480
|
} else {
|
|
10681
10481
|
integration = "fetched_only_diverged";
|
|
10682
10482
|
}
|
|
@@ -10690,10 +10490,15 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10690
10490
|
let line;
|
|
10691
10491
|
let codeForState;
|
|
10692
10492
|
let recoveryAction;
|
|
10493
|
+
let successAction;
|
|
10693
10494
|
switch (integration) {
|
|
10694
10495
|
case "up_to_date":
|
|
10695
10496
|
line = `${branch} is already up to date.`;
|
|
10696
10497
|
break;
|
|
10498
|
+
case "local_ahead":
|
|
10499
|
+
line = `${branch} already contains ${SPACE_REMOTE}/${branch} at ${remoteOid.slice(0, 10)} \u2014 nothing to pull. Local work is not on the cloud repo yet: run \`${pushCommand}\`.`;
|
|
10500
|
+
successAction = targetedVcAction("push", appDir, appId, branch);
|
|
10501
|
+
break;
|
|
10697
10502
|
case "fast_forwarded":
|
|
10698
10503
|
line = `Fast-forwarded ${branch} to ${remoteOid.slice(0, 10)}.`;
|
|
10699
10504
|
break;
|
|
@@ -10742,7 +10547,8 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10742
10547
|
}
|
|
10743
10548
|
const actionRequired = recoveryAction !== void 0;
|
|
10744
10549
|
spinner2?.stop(codeForState ? `Fetched ${SPACE_REMOTE}/${branch}.` : line);
|
|
10745
|
-
if (!args.json)
|
|
10550
|
+
if (!args.json)
|
|
10551
|
+
p19.log.info(`App: ${appId} \xB7 ${SPACE_REMOTE}/${branch} at ${remoteOid.slice(0, 10)}`);
|
|
10746
10552
|
if (codeForState) {
|
|
10747
10553
|
throw new Refusal(line, codeForState, {
|
|
10748
10554
|
actionRequired,
|
|
@@ -10756,20 +10562,20 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10756
10562
|
}
|
|
10757
10563
|
});
|
|
10758
10564
|
}
|
|
10759
|
-
return { data: { status: integration, appId, branch, remoteOid } };
|
|
10565
|
+
return { data: { status: integration, appId, branch, remoteOid }, action: successAction };
|
|
10760
10566
|
}
|
|
10761
10567
|
});
|
|
10762
10568
|
|
|
10763
10569
|
// src/cli/commands/clone.ts
|
|
10764
10570
|
import * as p20 from "@clack/prompts";
|
|
10765
|
-
import { existsSync as
|
|
10571
|
+
import { existsSync as existsSync22, readdirSync as readdirSync6, rmSync as rmSync5 } from "fs";
|
|
10766
10572
|
import { resolve as resolve17, basename as basename5, join as join21 } from "path";
|
|
10767
10573
|
function cleanFailedCloneDir(dir) {
|
|
10768
10574
|
let entries;
|
|
10769
10575
|
try {
|
|
10770
10576
|
entries = readdirSync6(dir);
|
|
10771
10577
|
} catch {
|
|
10772
|
-
return
|
|
10578
|
+
return existsSync22(dir) ? ["<unreadable>"] : [];
|
|
10773
10579
|
}
|
|
10774
10580
|
if (entries.length === 1 && entries[0] === ".git") {
|
|
10775
10581
|
try {
|
|
@@ -10782,7 +10588,7 @@ function cleanFailedCloneDir(dir) {
|
|
|
10782
10588
|
return entries;
|
|
10783
10589
|
}
|
|
10784
10590
|
function validateCloneTarget(dir) {
|
|
10785
|
-
if (!
|
|
10591
|
+
if (!existsSync22(dir)) return false;
|
|
10786
10592
|
let entries;
|
|
10787
10593
|
try {
|
|
10788
10594
|
entries = readdirSync6(dir);
|
|
@@ -11258,7 +11064,7 @@ function printOverlaps(overlaps, json) {
|
|
|
11258
11064
|
|
|
11259
11065
|
// src/cli/commands/workspace/local.ts
|
|
11260
11066
|
import * as p24 from "@clack/prompts";
|
|
11261
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
11067
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync25, realpathSync as realpathSync6, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
11262
11068
|
import { dirname as dirname10, isAbsolute as isAbsolute5, join as join22, relative as relative2, sep as sep2 } from "path";
|
|
11263
11069
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
11264
11070
|
function excludeWorktreeDir(appDir, dir) {
|
|
@@ -11316,7 +11122,7 @@ function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
|
|
|
11316
11122
|
const markerPath = worktreeMarkerPath(worktreeRoot);
|
|
11317
11123
|
if (!markerPath) return false;
|
|
11318
11124
|
try {
|
|
11319
|
-
const marker = JSON.parse(
|
|
11125
|
+
const marker = JSON.parse(readFileSync25(markerPath, "utf-8"));
|
|
11320
11126
|
return marker.version === 1 && marker.workspaceId === workspaceId && marker.mode === "managed-linked";
|
|
11321
11127
|
} catch {
|
|
11322
11128
|
return false;
|
|
@@ -11756,7 +11562,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11756
11562
|
|
|
11757
11563
|
// src/cli/commands/workspace/attach.ts
|
|
11758
11564
|
import * as p26 from "@clack/prompts";
|
|
11759
|
-
import { existsSync as
|
|
11565
|
+
import { existsSync as existsSync23, readdirSync as readdirSync7, realpathSync as realpathSync7, rmSync as rmSync6 } from "fs";
|
|
11760
11566
|
import { isAbsolute as isAbsolute7, resolve as resolve19 } from "path";
|
|
11761
11567
|
function samePath(a, b) {
|
|
11762
11568
|
const real = (p35) => {
|
|
@@ -11776,7 +11582,7 @@ function finishedWorkspaceMessage(id, view) {
|
|
|
11776
11582
|
return `Workspace ${id} is ${workspace.status} \u2014 its ref is retained briefly (fetch ${workspace.ref} to inspect), but attach only works on active workspaces.`;
|
|
11777
11583
|
}
|
|
11778
11584
|
function cleanFailedFreshAttachDir(dir) {
|
|
11779
|
-
if (!
|
|
11585
|
+
if (!existsSync23(dir)) return [];
|
|
11780
11586
|
let entries;
|
|
11781
11587
|
try {
|
|
11782
11588
|
entries = readdirSync7(dir);
|
|
@@ -11858,7 +11664,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11858
11664
|
const localTip = resolveCommit(appDir, `refs/heads/${branch}`);
|
|
11859
11665
|
if (localTip) {
|
|
11860
11666
|
let existing = resolveWorkspaceWorktree(listWorktrees(appDir), id);
|
|
11861
|
-
if (existing && !
|
|
11667
|
+
if (existing && !existsSync23(existing)) {
|
|
11862
11668
|
runGit(appDir, ["worktree", "prune"], { allowFail: true });
|
|
11863
11669
|
existing = null;
|
|
11864
11670
|
}
|
|
@@ -11976,7 +11782,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11976
11782
|
process.cwd(),
|
|
11977
11783
|
(typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
|
|
11978
11784
|
);
|
|
11979
|
-
if (
|
|
11785
|
+
if (existsSync23(dir)) {
|
|
11980
11786
|
spinner2?.stop("Target exists.");
|
|
11981
11787
|
throw new Refusal(`${dir} already exists \u2014 pass a different directory.`, "dir_exists");
|
|
11982
11788
|
}
|
|
@@ -12313,13 +12119,13 @@ import * as p30 from "@clack/prompts";
|
|
|
12313
12119
|
|
|
12314
12120
|
// src/cli/lib/validation.ts
|
|
12315
12121
|
import { sync as spawnSync7 } from "cross-spawn";
|
|
12316
|
-
import { readFileSync as
|
|
12122
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
12317
12123
|
import { join as join23 } from "path";
|
|
12318
12124
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
12319
12125
|
var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
|
|
12320
12126
|
function configuredCommand(appDir) {
|
|
12321
12127
|
try {
|
|
12322
|
-
const pkg2 = JSON.parse(
|
|
12128
|
+
const pkg2 = JSON.parse(readFileSync26(join23(appDir, "package.json"), "utf-8"));
|
|
12323
12129
|
if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
|
|
12324
12130
|
} catch {
|
|
12325
12131
|
}
|
|
@@ -13384,7 +13190,7 @@ function sameRefs(left, right) {
|
|
|
13384
13190
|
|
|
13385
13191
|
// src/cli/commands/update.ts
|
|
13386
13192
|
import * as p34 from "@clack/prompts";
|
|
13387
|
-
import { existsSync as
|
|
13193
|
+
import { existsSync as existsSync24, readFileSync as readFileSync27, writeFileSync as writeFileSync10 } from "fs";
|
|
13388
13194
|
import { join as join24, resolve as resolve20 } from "path";
|
|
13389
13195
|
var REGISTRY = "https://registry.npmjs.org/deepspace/latest";
|
|
13390
13196
|
var DOCS_URL = "https://documentation.deep.space/cli-reference/commands";
|
|
@@ -13400,18 +13206,18 @@ function gapIsTooWide(from, to) {
|
|
|
13400
13206
|
}
|
|
13401
13207
|
function readAppSdkVersion(appDir) {
|
|
13402
13208
|
const installed = join24(appDir, "node_modules", "deepspace", "package.json");
|
|
13403
|
-
if (
|
|
13404
|
-
const parsed2 = JSON.parse(
|
|
13209
|
+
if (existsSync24(installed)) {
|
|
13210
|
+
const parsed2 = JSON.parse(readFileSync27(installed, "utf8"));
|
|
13405
13211
|
if (typeof parsed2.version === "string") return parsed2.version;
|
|
13406
13212
|
}
|
|
13407
13213
|
const manifest = join24(appDir, "package.json");
|
|
13408
|
-
if (!
|
|
13409
|
-
const parsed = JSON.parse(
|
|
13214
|
+
if (!existsSync24(manifest)) return null;
|
|
13215
|
+
const parsed = JSON.parse(readFileSync27(manifest, "utf8"));
|
|
13410
13216
|
return parsed.dependencies?.deepspace ?? null;
|
|
13411
13217
|
}
|
|
13412
13218
|
function pinSdkVersion(appDir, version) {
|
|
13413
13219
|
const path = join24(appDir, "package.json");
|
|
13414
|
-
const source =
|
|
13220
|
+
const source = readFileSync27(path, "utf8");
|
|
13415
13221
|
const updated = source.replace(
|
|
13416
13222
|
/("deepspace"\s*:\s*")[^"]+(")/,
|
|
13417
13223
|
(_match, head, tail) => `${head}^${version}${tail}`
|
|
@@ -13557,8 +13363,8 @@ var update_default = defineDeepspaceCommand({
|
|
|
13557
13363
|
|
|
13558
13364
|
// src/cli/cli.ts
|
|
13559
13365
|
var __dirname = dirname11(fileURLToPath3(import.meta.url));
|
|
13560
|
-
var pkg = JSON.parse(
|
|
13561
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
13366
|
+
var pkg = JSON.parse(readFileSync28(join25(__dirname, "..", "package.json"), "utf-8"));
|
|
13367
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync25("/etc/ssl/certs/ca-certificates.crt")) {
|
|
13562
13368
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
13563
13369
|
}
|
|
13564
13370
|
var create2 = defineCommand14({
|
|
@@ -13689,6 +13495,46 @@ Run \`${unknown.helpPath.join(" ")} --help\` for the command list.`
|
|
|
13689
13495
|
}
|
|
13690
13496
|
process.exit(1);
|
|
13691
13497
|
}
|
|
13498
|
+
function selectedLeaf(argv) {
|
|
13499
|
+
let command = main;
|
|
13500
|
+
let remaining = argv;
|
|
13501
|
+
const path = [];
|
|
13502
|
+
const commandPrefixArgs = [];
|
|
13503
|
+
for (; ; ) {
|
|
13504
|
+
const subCommands = command.subCommands;
|
|
13505
|
+
if (!subCommands || Object.keys(subCommands).length === 0) {
|
|
13506
|
+
return { command, rawArgs: [...commandPrefixArgs, ...remaining], path };
|
|
13507
|
+
}
|
|
13508
|
+
const index = remaining.findIndex((argument) => !argument.startsWith("-"));
|
|
13509
|
+
if (index < 0) return { command, rawArgs: [...commandPrefixArgs, ...remaining], path };
|
|
13510
|
+
const name = remaining[index];
|
|
13511
|
+
const entry = Object.entries(subCommands).find(([key, child]) => {
|
|
13512
|
+
if (key === name) return true;
|
|
13513
|
+
const alias = child.meta?.alias;
|
|
13514
|
+
return (Array.isArray(alias) ? alias : alias ? [alias] : []).includes(name);
|
|
13515
|
+
});
|
|
13516
|
+
if (!entry) return null;
|
|
13517
|
+
commandPrefixArgs.push(...remaining.slice(0, index));
|
|
13518
|
+
path.push(entry[0]);
|
|
13519
|
+
command = entry[1];
|
|
13520
|
+
remaining = remaining.slice(index + 1);
|
|
13521
|
+
}
|
|
13522
|
+
}
|
|
13523
|
+
function assertKnownOptions(argv) {
|
|
13524
|
+
const selected = selectedLeaf(argv);
|
|
13525
|
+
if (!selected || selected.path.join(" ") === "app create") return;
|
|
13526
|
+
const args = selected.command.args ?? {};
|
|
13527
|
+
const unknown = unknownOptionNamesFromRaw(selected.rawArgs, args);
|
|
13528
|
+
if (unknown.length === 0) return;
|
|
13529
|
+
const commandName = selected.path.join(" ") || "deepspace";
|
|
13530
|
+
const message = unknownOptionMessage(commandName, unknown, args);
|
|
13531
|
+
if (argv.includes("--json")) {
|
|
13532
|
+
console.log(JSON.stringify({ ok: false, code: "unknown_option", error: message }));
|
|
13533
|
+
} else {
|
|
13534
|
+
console.error(withSlug(message, "unknown_option"));
|
|
13535
|
+
}
|
|
13536
|
+
process.exit(1);
|
|
13537
|
+
}
|
|
13692
13538
|
var rawArgs = process.argv.slice(2);
|
|
13693
13539
|
var wantsHelp = rawArgs.some((arg) => arg === "--help" || arg === "-h");
|
|
13694
13540
|
if (DEEPSPACE_ENV === "invalid") {
|
|
@@ -13700,6 +13546,9 @@ if (DEEPSPACE_ENV === "invalid") {
|
|
|
13700
13546
|
}
|
|
13701
13547
|
process.exit(1);
|
|
13702
13548
|
}
|
|
13549
|
+
if (!wantsHelp && !(rawArgs.length === 1 && ["--version", "-v"].includes(rawArgs[0]))) {
|
|
13550
|
+
assertKnownOptions(rawArgs);
|
|
13551
|
+
}
|
|
13703
13552
|
if (rawArgs.includes("--json") && !wantsHelp) {
|
|
13704
13553
|
const unknown = findUnknownCommand(rawArgs, commandTree());
|
|
13705
13554
|
if (unknown) {
|