naro-cli 0.21.0 → 0.22.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/README.md +53 -2
- package/dist/{chunk-FFPOVA7I.js → chunk-MBSO3K6L.js} +566 -40
- package/dist/deploy.js +1 -1
- package/dist/index.js +603 -175
- package/package.json +4 -4
|
@@ -326,6 +326,162 @@ var STALL_WINDOW_MS = 10 * 6e4;
|
|
|
326
326
|
// ../../packages/core/src/deployment-strategy.ts
|
|
327
327
|
var MIN_SUPPORTED_NEXT_MAJOR = 15;
|
|
328
328
|
|
|
329
|
+
// ../../packages/core/src/environment-guidance.ts
|
|
330
|
+
var DATA_COMMANDS = /* @__PURE__ */ new Set([
|
|
331
|
+
"db",
|
|
332
|
+
"migration",
|
|
333
|
+
"gen",
|
|
334
|
+
"backend",
|
|
335
|
+
"policy",
|
|
336
|
+
"storage",
|
|
337
|
+
"realtime"
|
|
338
|
+
]);
|
|
339
|
+
var BACKEND_SWITCHES = /* @__PURE__ */ new Set([
|
|
340
|
+
"enable",
|
|
341
|
+
"provision",
|
|
342
|
+
"disable"
|
|
343
|
+
]);
|
|
344
|
+
var PRODUCTION_SWITCH_NOTE = "production's switch, which every environment follows";
|
|
345
|
+
var SWITCH_CONTEXT = /\bturned off\b|\bturn(?:s|ing)? (?:it|them) (?:back )?on\b|\bbackend is on\b/i;
|
|
346
|
+
var SUBCOMMANDS = {
|
|
347
|
+
db: /* @__PURE__ */ new Set([
|
|
348
|
+
"info",
|
|
349
|
+
"enable",
|
|
350
|
+
"query",
|
|
351
|
+
"push",
|
|
352
|
+
"dump",
|
|
353
|
+
"restore",
|
|
354
|
+
"pull",
|
|
355
|
+
"reset",
|
|
356
|
+
"lint",
|
|
357
|
+
"table",
|
|
358
|
+
"index",
|
|
359
|
+
"create",
|
|
360
|
+
"add-column",
|
|
361
|
+
"rename-column",
|
|
362
|
+
"rename",
|
|
363
|
+
"drop-column",
|
|
364
|
+
"drop"
|
|
365
|
+
]),
|
|
366
|
+
migration: /* @__PURE__ */ new Set(["list", "ls", "new", "up", "repair"]),
|
|
367
|
+
gen: /* @__PURE__ */ new Set(["types"]),
|
|
368
|
+
backend: /* @__PURE__ */ new Set([
|
|
369
|
+
"info",
|
|
370
|
+
"status",
|
|
371
|
+
"enable",
|
|
372
|
+
"provision",
|
|
373
|
+
"disable",
|
|
374
|
+
"keys",
|
|
375
|
+
"rotate",
|
|
376
|
+
"auth",
|
|
377
|
+
"email",
|
|
378
|
+
"oauth",
|
|
379
|
+
"redirects",
|
|
380
|
+
"require-verification",
|
|
381
|
+
"signups",
|
|
382
|
+
"password",
|
|
383
|
+
"set",
|
|
384
|
+
"add",
|
|
385
|
+
"remove",
|
|
386
|
+
"users",
|
|
387
|
+
"create",
|
|
388
|
+
"delete"
|
|
389
|
+
]),
|
|
390
|
+
policy: /* @__PURE__ */ new Set(["list", "ls", "set", "create", "drop", "remove", "rm"]),
|
|
391
|
+
storage: /* @__PURE__ */ new Set([
|
|
392
|
+
"enable",
|
|
393
|
+
"disable",
|
|
394
|
+
"bucket",
|
|
395
|
+
"list",
|
|
396
|
+
"create",
|
|
397
|
+
"update",
|
|
398
|
+
"empty",
|
|
399
|
+
"remove",
|
|
400
|
+
"cp",
|
|
401
|
+
"mv",
|
|
402
|
+
"ls",
|
|
403
|
+
"rm"
|
|
404
|
+
]),
|
|
405
|
+
realtime: /* @__PURE__ */ new Set(["enable", "disable", "tables", "add", "remove"])
|
|
406
|
+
};
|
|
407
|
+
function kindOf(words) {
|
|
408
|
+
const command = words[1] ?? "";
|
|
409
|
+
if (!DATA_COMMANDS.has(command)) return "leave";
|
|
410
|
+
if (words.some((word) => word === "--env" || word.startsWith("--env=")))
|
|
411
|
+
return "leave";
|
|
412
|
+
if (command === "migration" && words[2] === "new") return "leave";
|
|
413
|
+
if (command === "backend" && BACKEND_SWITCHES.has(words[2] ?? ""))
|
|
414
|
+
return words.length === 3 ? "switch" : "leave";
|
|
415
|
+
return "env";
|
|
416
|
+
}
|
|
417
|
+
function quoted(span, slug, switchContext) {
|
|
418
|
+
const words = span.split(" ");
|
|
419
|
+
switch (kindOf(words)) {
|
|
420
|
+
case "env":
|
|
421
|
+
return `\`${span} --env ${slug}\``;
|
|
422
|
+
case "switch":
|
|
423
|
+
return words[2] !== "disable" && !switchContext ? `\`naro environments provision ${slug}\`` : `\`${span}\` (${PRODUCTION_SWITCH_NOTE})`;
|
|
424
|
+
default:
|
|
425
|
+
return `\`${span}\``;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
var QUOTED = /`(naro [^`\n]+)`/g;
|
|
429
|
+
var BARE = /(?<![`\w/-])naro ([a-z]+)((?: [a-z][a-z-]*)*)/g;
|
|
430
|
+
function bare(match, command, rest, after, slug, switchContext) {
|
|
431
|
+
const vocabulary = SUBCOMMANDS[command];
|
|
432
|
+
if (vocabulary === void 0) return match;
|
|
433
|
+
const words = rest.split(" ").filter((word) => word !== "");
|
|
434
|
+
let count2 = 0;
|
|
435
|
+
while (count2 < words.length && vocabulary.has(words[count2] ?? "")) count2++;
|
|
436
|
+
if (count2 === 0) return match;
|
|
437
|
+
const head = ["naro", command, ...words.slice(0, count2)];
|
|
438
|
+
const tail = words.slice(count2);
|
|
439
|
+
const following = after.split(/\n|[.;,)](?:\s|$)/, 1)[0] ?? "";
|
|
440
|
+
if (/(?:^|\s)--env[\s=]/.test(following) || after.startsWith(` \u2014 ${PRODUCTION_SWITCH_NOTE}`))
|
|
441
|
+
return match;
|
|
442
|
+
const shown = head.join(" ");
|
|
443
|
+
const remainder = tail.length > 0 ? ` ${tail.join(" ")}` : "";
|
|
444
|
+
switch (kindOf(head)) {
|
|
445
|
+
case "env":
|
|
446
|
+
return `${shown} --env ${slug}${remainder}`;
|
|
447
|
+
case "switch":
|
|
448
|
+
return head[2] !== "disable" && !switchContext ? `naro environments provision ${slug}${remainder}` : `${shown} \u2014 ${PRODUCTION_SWITCH_NOTE}${remainder}`;
|
|
449
|
+
default:
|
|
450
|
+
return match;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function environmentGuidance(text2, slug) {
|
|
454
|
+
if (slug === null || !text2.includes("naro ")) return text2;
|
|
455
|
+
const switchContext = SWITCH_CONTEXT.test(text2);
|
|
456
|
+
const spans = [];
|
|
457
|
+
const marked = text2.replaceAll(
|
|
458
|
+
QUOTED,
|
|
459
|
+
(match, span, offset) => {
|
|
460
|
+
const noted = text2.startsWith(
|
|
461
|
+
` (${PRODUCTION_SWITCH_NOTE})`,
|
|
462
|
+
offset + match.length
|
|
463
|
+
);
|
|
464
|
+
spans.push(noted ? match : quoted(span, slug, switchContext));
|
|
465
|
+
return `\uE000${String(spans.length - 1)}\uE000`;
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
const rewritten = marked.replaceAll(
|
|
469
|
+
BARE,
|
|
470
|
+
(match, command, rest, offset) => bare(
|
|
471
|
+
match,
|
|
472
|
+
command,
|
|
473
|
+
rest,
|
|
474
|
+
marked.slice(offset + match.length),
|
|
475
|
+
slug,
|
|
476
|
+
switchContext
|
|
477
|
+
)
|
|
478
|
+
);
|
|
479
|
+
return rewritten.replaceAll(
|
|
480
|
+
/\uE000(\d+)\uE000/g,
|
|
481
|
+
(_match, index) => spans[Number(index)] ?? ""
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
329
485
|
// ../../packages/core/src/errors.ts
|
|
330
486
|
var DEPLOYMENT_ERROR_CODES = [
|
|
331
487
|
"CLONE_FAILED",
|
|
@@ -436,7 +592,11 @@ var DEPLOYMENT_ERROR_CODES = [
|
|
|
436
592
|
"ENVIRONMENT_BRANCH_IS_PRODUCTION",
|
|
437
593
|
/** The plan's environment count is full. */
|
|
438
594
|
"ENVIRONMENT_LIMIT_REACHED",
|
|
439
|
-
/**
|
|
595
|
+
/**
|
|
596
|
+
* The project uses a Naro database, which environments did not yet support
|
|
597
|
+
* (stages 1 and 2 only). No route answers it since named environments 3c;
|
|
598
|
+
* kept for clients of a control plane from before.
|
|
599
|
+
*/
|
|
440
600
|
"ENVIRONMENT_DATABASE_UNSUPPORTED",
|
|
441
601
|
"ENVIRONMENT_NOT_FOUND",
|
|
442
602
|
/** The project's new default branch is the one an environment deploys. */
|
|
@@ -460,6 +620,9 @@ var DeploymentError = class extends Error {
|
|
|
460
620
|
}
|
|
461
621
|
};
|
|
462
622
|
|
|
623
|
+
// ../../packages/core/src/functions.ts
|
|
624
|
+
var FUNCTION_WORKER_NAME_PREFIX = "naro-fn-";
|
|
625
|
+
|
|
463
626
|
// ../../packages/core/src/git-remote.ts
|
|
464
627
|
var SCP_LIKE = /^[^@\s/]+@([^:\s/]+):(.+)$/;
|
|
465
628
|
function parseGitHubRemote(url2) {
|
|
@@ -15064,6 +15227,8 @@ function subdomainProblem(value) {
|
|
|
15064
15227
|
if (value.endsWith("-preview")) return "reserved";
|
|
15065
15228
|
if (value === BACKEND_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(BACKEND_WORKER_NAME_PREFIX))
|
|
15066
15229
|
return "reserved";
|
|
15230
|
+
if (value === FUNCTION_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(FUNCTION_WORKER_NAME_PREFIX))
|
|
15231
|
+
return "reserved";
|
|
15067
15232
|
if (RESERVED_SUBDOMAINS.includes(value))
|
|
15068
15233
|
return "reserved";
|
|
15069
15234
|
return null;
|
|
@@ -15399,6 +15564,72 @@ function workerNameForSurface(subdomain, surface) {
|
|
|
15399
15564
|
}
|
|
15400
15565
|
}
|
|
15401
15566
|
|
|
15567
|
+
// ../../packages/core/src/usage-alert-mail.ts
|
|
15568
|
+
var REQUESTS = {
|
|
15569
|
+
en: {
|
|
15570
|
+
subject80: (team) => `${team} has used 80% of its monthly requests`,
|
|
15571
|
+
subject100: (team) => `${team} has reached its monthly request limit`,
|
|
15572
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} requests the Hobby plan includes for ${month}.`,
|
|
15573
|
+
warning: "When it reaches the limit, requests past it may be blocked until the month ends (UTC).",
|
|
15574
|
+
blocked: "Sites on this team answer 503 until the month ends (UTC).",
|
|
15575
|
+
running: "Sites keep running for now.",
|
|
15576
|
+
button: "View usage",
|
|
15577
|
+
footer: (team) => `You're receiving this because you're an owner or billing member of ${team} on naro.`
|
|
15578
|
+
},
|
|
15579
|
+
ko: {
|
|
15580
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15581
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15582
|
+
usage: (team, used, limit, month) => `${team} \uD300\uC774 ${month} Hobby \uD50C\uB79C\uC5D0 \uD3EC\uD568\uB41C \uC694\uCCAD ${limit}\uAC74 \uC911 ${used}\uAC74\uC744 \uC37C\uC2B5\uB2C8\uB2E4.`,
|
|
15583
|
+
warning: "\uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uADF8 \uB4A4 \uC694\uCCAD\uC774 \uB9C9\uD790 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
15584
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0AC\uC774\uD2B8\uB294 503\uC73C\uB85C \uC751\uB2F5\uD569\uB2C8\uB2E4.",
|
|
15585
|
+
running: "\uC9C0\uAE08\uC740 \uC0AC\uC774\uD2B8\uAC00 \uACC4\uC18D \uB3D9\uC791\uD569\uB2C8\uB2E4.",
|
|
15586
|
+
button: "\uC0AC\uC6A9\uB7C9 \uBCF4\uAE30",
|
|
15587
|
+
footer: (team) => `naro\uC5D0\uC11C ${team} \uD300\uC758 owner \uB610\uB294 billing \uC5ED\uD560\uC774\uB77C \uC774 \uBA54\uC77C\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4.`
|
|
15588
|
+
},
|
|
15589
|
+
ja: {
|
|
15590
|
+
subject80: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306E80%\u3092\u4F7F\u7528\u3057\u307E\u3057\u305F`,
|
|
15591
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15592
|
+
usage: (team, used, limit, month) => `${team} \u30C1\u30FC\u30E0\u306F ${month} \u306E Hobby \u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B ${limit} \u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u3046\u3061 ${used} \u3092\u4F7F\u7528\u3057\u307E\u3057\u305F\u3002`,
|
|
15593
|
+
warning: "\u4E0A\u9650\u306B\u9054\u3059\u308B\u3068\u3001\u6708\u672B(UTC)\u307E\u3067\u4EE5\u964D\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
15594
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u30B5\u30A4\u30C8\u306F503\u3092\u8FD4\u3057\u307E\u3059\u3002",
|
|
15595
|
+
running: "\u73FE\u5728\u3001\u30B5\u30A4\u30C8\u306F\u5F15\u304D\u7D9A\u304D\u52D5\u4F5C\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
15596
|
+
button: "\u4F7F\u7528\u91CF\u3092\u898B\u308B",
|
|
15597
|
+
footer: (team) => `naro \u3067 ${team} \u30C1\u30FC\u30E0\u306E owner \u307E\u305F\u306F billing \u30ED\u30FC\u30EB\u306E\u305F\u3081\u3001\u3053\u306E\u30E1\u30FC\u30EB\u3092\u304A\u9001\u308A\u3057\u3066\u3044\u307E\u3059\u3002`
|
|
15598
|
+
}
|
|
15599
|
+
};
|
|
15600
|
+
var BUILDS = {
|
|
15601
|
+
en: {
|
|
15602
|
+
subject80: (team) => `${team} has used 80% of its monthly builds`,
|
|
15603
|
+
subject100: (team) => `${team} has reached its monthly build limit`,
|
|
15604
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} builds the Hobby plan includes for ${month}.`,
|
|
15605
|
+
warning: "When it reaches the limit, new deploys may be refused until the month ends (UTC).",
|
|
15606
|
+
blocked: "New deploys on this team are refused until the month ends (UTC).",
|
|
15607
|
+
running: "Deploys keep working for now.",
|
|
15608
|
+
button: REQUESTS.en.button,
|
|
15609
|
+
footer: REQUESTS.en.footer
|
|
15610
|
+
},
|
|
15611
|
+
ko: {
|
|
15612
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15613
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15614
|
+
usage: (team, used, limit, month) => `${team} \uD300\uC774 ${month} Hobby \uD50C\uB79C\uC5D0 \uD3EC\uD568\uB41C \uBE4C\uB4DC ${limit}\uAC74 \uC911 ${used}\uAC74\uC744 \uC37C\uC2B5\uB2C8\uB2E4.`,
|
|
15615
|
+
warning: "\uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC0C8 \uBC30\uD3EC\uAC00 \uAC70\uC808\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
15616
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0C8 \uBC30\uD3EC\uB294 \uAC70\uC808\uB429\uB2C8\uB2E4.",
|
|
15617
|
+
running: "\uC9C0\uAE08\uC740 \uBC30\uD3EC\uAC00 \uACC4\uC18D \uB429\uB2C8\uB2E4.",
|
|
15618
|
+
button: REQUESTS.ko.button,
|
|
15619
|
+
footer: REQUESTS.ko.footer
|
|
15620
|
+
},
|
|
15621
|
+
ja: {
|
|
15622
|
+
subject80: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306E80%\u3092\u4F7F\u7528\u3057\u307E\u3057\u305F`,
|
|
15623
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15624
|
+
usage: (team, used, limit, month) => `${team} \u30C1\u30FC\u30E0\u306F ${month} \u306E Hobby \u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B ${limit} \u30D3\u30EB\u30C9\u306E\u3046\u3061 ${used} \u3092\u4F7F\u7528\u3057\u307E\u3057\u305F\u3002`,
|
|
15625
|
+
warning: "\u4E0A\u9650\u306B\u9054\u3059\u308B\u3068\u3001\u6708\u672B(UTC)\u307E\u3067\u65B0\u3057\u3044\u30C7\u30D7\u30ED\u30A4\u304C\u62D2\u5426\u3055\u308C\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
15626
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u65B0\u3057\u3044\u30C7\u30D7\u30ED\u30A4\u306F\u62D2\u5426\u3055\u308C\u307E\u3059\u3002",
|
|
15627
|
+
running: "\u73FE\u5728\u3001\u30C7\u30D7\u30ED\u30A4\u306F\u5F15\u304D\u7D9A\u304D\u884C\u3048\u307E\u3059\u3002",
|
|
15628
|
+
button: REQUESTS.ja.button,
|
|
15629
|
+
footer: REQUESTS.ja.footer
|
|
15630
|
+
}
|
|
15631
|
+
};
|
|
15632
|
+
|
|
15402
15633
|
// ../../packages/build/src/next-version.ts
|
|
15403
15634
|
var NEXT_RELEASE_DATES = {
|
|
15404
15635
|
16: "2025-10-21",
|
|
@@ -19244,7 +19475,7 @@ async function errorFromResponse(response, context) {
|
|
|
19244
19475
|
if (typeof serverDetails === "object" && serverDetails !== null && !Array.isArray(serverDetails))
|
|
19245
19476
|
Object.assign(details, serverDetails);
|
|
19246
19477
|
const retryAfter = response.headers.get("retry-after")?.trim() ?? "";
|
|
19247
|
-
if (response.status === 429 && /^\d{1,9}$/.test(retryAfter))
|
|
19478
|
+
if ((response.status === 429 || response.status === 503) && /^\d{1,9}$/.test(retryAfter))
|
|
19248
19479
|
details.retryAfter = Number(retryAfter);
|
|
19249
19480
|
if (body?.error?.fields !== void 0) details.fields = body.error.fields;
|
|
19250
19481
|
if (typeof body?.error?.installUrl === "string")
|
|
@@ -19712,6 +19943,16 @@ function isReservedEnvTarget(value) {
|
|
|
19712
19943
|
function isEnvTargetName(value) {
|
|
19713
19944
|
return environmentSlugProblem(value) !== "invalid";
|
|
19714
19945
|
}
|
|
19946
|
+
var NARO_SYSTEM_ENV_NAMES = [
|
|
19947
|
+
"NARO_URL",
|
|
19948
|
+
"NEXT_PUBLIC_NARO_URL",
|
|
19949
|
+
"VITE_NARO_URL",
|
|
19950
|
+
"PUBLIC_NARO_URL",
|
|
19951
|
+
"NARO_ANON_KEY",
|
|
19952
|
+
"NEXT_PUBLIC_NARO_ANON_KEY",
|
|
19953
|
+
"VITE_NARO_ANON_KEY",
|
|
19954
|
+
"PUBLIC_NARO_ANON_KEY"
|
|
19955
|
+
];
|
|
19715
19956
|
|
|
19716
19957
|
// ../../packages/sdk/src/insights.ts
|
|
19717
19958
|
var SPEED_INSIGHTS_METRICS = [
|
|
@@ -19745,6 +19986,7 @@ var BACKEND_STEPS = [
|
|
|
19745
19986
|
"worker",
|
|
19746
19987
|
"routes",
|
|
19747
19988
|
"migrate",
|
|
19989
|
+
"schema",
|
|
19748
19990
|
"health"
|
|
19749
19991
|
];
|
|
19750
19992
|
var STALLED_GRACE_MS = 2 * 6e4;
|
|
@@ -19808,9 +20050,17 @@ function buildLogEntry(row) {
|
|
|
19808
20050
|
}
|
|
19809
20051
|
|
|
19810
20052
|
// ../../packages/sdk/src/client.ts
|
|
20053
|
+
var DATA_PATH = /^\/api\/projects\/[^/?]+\/(?:database|backend(?!\/functions))(?:[/?]|$)/;
|
|
20054
|
+
function withDataEnvironment(path, environment) {
|
|
20055
|
+
if (environment === null || !DATA_PATH.test(path)) return path;
|
|
20056
|
+
return `${path}${path.includes("?") ? "&" : "?"}environment=${encodeURIComponent(environment)}`;
|
|
20057
|
+
}
|
|
19811
20058
|
var DEFAULT_REQUEST_TIMEOUT_MS = 45e3;
|
|
19812
|
-
var NaroClient = class {
|
|
20059
|
+
var NaroClient = class _NaroClient {
|
|
19813
20060
|
baseUrl;
|
|
20061
|
+
/** The environment whose data the data calls act on; null: production. */
|
|
20062
|
+
environment;
|
|
20063
|
+
options;
|
|
19814
20064
|
getAccessToken;
|
|
19815
20065
|
doFetch;
|
|
19816
20066
|
requestTimeoutMs;
|
|
@@ -19825,11 +20075,14 @@ var NaroClient = class {
|
|
|
19825
20075
|
backend;
|
|
19826
20076
|
storage;
|
|
19827
20077
|
realtime;
|
|
20078
|
+
functions;
|
|
19828
20079
|
insights;
|
|
19829
20080
|
analytics;
|
|
19830
20081
|
protection;
|
|
19831
20082
|
environments;
|
|
19832
20083
|
constructor(options) {
|
|
20084
|
+
this.options = options;
|
|
20085
|
+
this.environment = options.environment ?? null;
|
|
19833
20086
|
this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
|
|
19834
20087
|
this.getAccessToken = options.getAccessToken;
|
|
19835
20088
|
this.doFetch = options.fetch ?? fetch;
|
|
@@ -19845,25 +20098,41 @@ var NaroClient = class {
|
|
|
19845
20098
|
this.backend = new BackendApi(this);
|
|
19846
20099
|
this.storage = new StorageApi(this);
|
|
19847
20100
|
this.realtime = new RealtimeApi(this);
|
|
20101
|
+
this.functions = new FunctionsApi(this);
|
|
19848
20102
|
this.insights = new InsightsApi(this);
|
|
19849
20103
|
this.analytics = new AnalyticsApi(this);
|
|
19850
20104
|
this.protection = new ProtectionApi(this);
|
|
19851
20105
|
this.environments = new EnvironmentsApi(this);
|
|
19852
20106
|
}
|
|
20107
|
+
/**
|
|
20108
|
+
* The same client acting on `environment`'s data — or production's, for
|
|
20109
|
+
* null. Every other call is the same: the scope is only the
|
|
20110
|
+
* `?environment=` on database and backend paths.
|
|
20111
|
+
*/
|
|
20112
|
+
inEnvironment(environment) {
|
|
20113
|
+
if (environment === this.environment) return this;
|
|
20114
|
+
const options = { ...this.options };
|
|
20115
|
+
delete options.environment;
|
|
20116
|
+
if (environment !== null) options.environment = environment;
|
|
20117
|
+
return new _NaroClient(options);
|
|
20118
|
+
}
|
|
19853
20119
|
async request(path, options) {
|
|
19854
20120
|
const token = await this.getAccessToken();
|
|
19855
20121
|
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
19856
20122
|
let response;
|
|
19857
20123
|
try {
|
|
19858
|
-
response = await this.doFetch(
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
20124
|
+
response = await this.doFetch(
|
|
20125
|
+
`${this.baseUrl}${withDataEnvironment(path, this.environment)}`,
|
|
20126
|
+
{
|
|
20127
|
+
method: options.method ?? "GET",
|
|
20128
|
+
headers: {
|
|
20129
|
+
Authorization: `Bearer ${token}`,
|
|
20130
|
+
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
20131
|
+
},
|
|
20132
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
20133
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
20134
|
+
}
|
|
20135
|
+
);
|
|
19867
20136
|
} catch (error51) {
|
|
19868
20137
|
if (error51 instanceof Error && (error51.name === "TimeoutError" || error51.name === "AbortError")) {
|
|
19869
20138
|
throw new NaroError(
|
|
@@ -20061,17 +20330,23 @@ var DeploymentsApi = class {
|
|
|
20061
20330
|
* `PROMOTE_TARGET_UNSUPPORTED` before anything is promoted. The target is
|
|
20062
20331
|
* trimmed once, as the control plane trims it, so `" production"` is
|
|
20063
20332
|
* production and asks no preview.
|
|
20333
|
+
*
|
|
20334
|
+
* `check: true` asks the preview for production too, for a caller that
|
|
20335
|
+
* cannot show it first and returns it instead (MCP's `promote_deployment`):
|
|
20336
|
+
* a 405 there means a control plane from before previews, and production
|
|
20337
|
+
* is promoted unchecked as before. The preview asked is the result's
|
|
20338
|
+
* `check`, so its migration warnings reach the caller (spec §9.4).
|
|
20064
20339
|
*/
|
|
20065
20340
|
async promote(projectId, deploymentId, options = {}) {
|
|
20066
20341
|
const target2 = options.target?.trim();
|
|
20067
|
-
|
|
20342
|
+
const toEnvironment = target2 !== void 0 && target2 !== "production";
|
|
20343
|
+
let check2;
|
|
20344
|
+
if (toEnvironment || options.check === true) {
|
|
20068
20345
|
try {
|
|
20069
|
-
await this.promoteCheck(projectId, deploymentId, target2);
|
|
20346
|
+
check2 = await this.promoteCheck(projectId, deploymentId, target2);
|
|
20070
20347
|
} catch (error51) {
|
|
20071
|
-
if (isNaroError(error51)
|
|
20072
|
-
|
|
20073
|
-
}
|
|
20074
|
-
throw error51;
|
|
20348
|
+
if (!isNaroError(error51) || error51.status !== 405) throw error51;
|
|
20349
|
+
if (toEnvironment) throw promoteTargetUnsupported(error51);
|
|
20075
20350
|
}
|
|
20076
20351
|
}
|
|
20077
20352
|
const body = await this.client.request(this.promotePath(projectId, deploymentId), {
|
|
@@ -20081,7 +20356,8 @@ var DeploymentsApi = class {
|
|
|
20081
20356
|
});
|
|
20082
20357
|
return {
|
|
20083
20358
|
...deploymentInfo(body.deployment),
|
|
20084
|
-
mode: body.mode ?? "reupload"
|
|
20359
|
+
mode: body.mode ?? "reupload",
|
|
20360
|
+
...check2 === void 0 ? {} : { check: check2 }
|
|
20085
20361
|
};
|
|
20086
20362
|
}
|
|
20087
20363
|
/**
|
|
@@ -20582,6 +20858,19 @@ var EnvironmentsApi = class {
|
|
|
20582
20858
|
{ method: "DELETE", context: "Delete environment" }
|
|
20583
20859
|
);
|
|
20584
20860
|
}
|
|
20861
|
+
/**
|
|
20862
|
+
* Makes or continues the environment's data — its D1, Naro backend and
|
|
20863
|
+
* Storage bucket (spec §11.2): the run goes on after the answer, which
|
|
20864
|
+
* says `data.status` `provisioning`. A run that stopped (`error`) starts
|
|
20865
|
+
* again from where it stopped; `none` when the project uses neither.
|
|
20866
|
+
*/
|
|
20867
|
+
async provision(projectId, environmentId) {
|
|
20868
|
+
const body = await this.client.request(
|
|
20869
|
+
`${this.base(projectId)}/${encodeURIComponent(environmentId)}/provision`,
|
|
20870
|
+
{ method: "POST", context: "Provision environment" }
|
|
20871
|
+
);
|
|
20872
|
+
return body.environment;
|
|
20873
|
+
}
|
|
20585
20874
|
};
|
|
20586
20875
|
var EXPORT_FIRST_POLL_MS = 1e3;
|
|
20587
20876
|
var EXPORT_MAX_POLL_MS = 5e3;
|
|
@@ -21615,6 +21904,202 @@ var RealtimeApi = class {
|
|
|
21615
21904
|
);
|
|
21616
21905
|
}
|
|
21617
21906
|
};
|
|
21907
|
+
var FunctionsApi = class {
|
|
21908
|
+
constructor(client) {
|
|
21909
|
+
this.client = client;
|
|
21910
|
+
}
|
|
21911
|
+
path(projectId, suffix = "") {
|
|
21912
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/functions${suffix}`;
|
|
21913
|
+
}
|
|
21914
|
+
/**
|
|
21915
|
+
* A segment no URL can carry is refused before a request, like
|
|
21916
|
+
* `StorageApi.bucketPath`: URL parsing folds `/`, `.` and `..` into the
|
|
21917
|
+
* path, an empty one is the collection, and `encodeURIComponent` throws on
|
|
21918
|
+
* a lone surrogate. The server checks the name's own rule.
|
|
21919
|
+
*/
|
|
21920
|
+
segment(value, what) {
|
|
21921
|
+
if (value === "" || value === "." || value === ".." || value.includes("/") || LONE_SURROGATE.test(value))
|
|
21922
|
+
throw new NaroError("INVALID_PATH", `The ${what} is not valid.`);
|
|
21923
|
+
return encodeURIComponent(value);
|
|
21924
|
+
}
|
|
21925
|
+
functionPath(projectId, name, suffix = "") {
|
|
21926
|
+
return this.path(
|
|
21927
|
+
projectId,
|
|
21928
|
+
`/${this.segment(name, "function name")}${suffix}`
|
|
21929
|
+
);
|
|
21930
|
+
}
|
|
21931
|
+
/** The project's functions, the plan's limits, and (3-3e) this month's usage. */
|
|
21932
|
+
async list(projectId) {
|
|
21933
|
+
return this.client.request(this.path(projectId), {
|
|
21934
|
+
context: "List functions"
|
|
21935
|
+
});
|
|
21936
|
+
}
|
|
21937
|
+
/** One function and its newest 20 versions. `FUNCTION_NOT_FOUND` (404). */
|
|
21938
|
+
async get(projectId, name) {
|
|
21939
|
+
return this.client.request(
|
|
21940
|
+
this.functionPath(projectId, name),
|
|
21941
|
+
{ context: "Read a function" }
|
|
21942
|
+
);
|
|
21943
|
+
}
|
|
21944
|
+
/**
|
|
21945
|
+
* A new version — the function too, the first time — and where to PUT its
|
|
21946
|
+
* bundle: `upload.url` with `upload.headers` (the checksum header is
|
|
21947
|
+
* signed). Refusals in the order they are checked: `BACKEND_DISABLED`,
|
|
21948
|
+
* `UNAUTHORIZED`, `NOT_FOUND`, `FORBIDDEN` (`deployment.create`),
|
|
21949
|
+
* `FUNCTIONS_DISABLED`, `FORBIDDEN` (a suspended project),
|
|
21950
|
+
* `VALIDATION_FAILED` (the name or the request), `BUNDLE_TOO_LARGE`,
|
|
21951
|
+
* `DATABASE_NOT_ENABLED`, `BACKEND_NOT_READY`, `BACKEND_OFF`,
|
|
21952
|
+
* `BACKEND_BUSY` (the project's Naro worker is not the current version and
|
|
21953
|
+
* could not be brought up to date just now), `FUNCTION_LIMIT` (a new name
|
|
21954
|
+
* over the plan's cap) and `FUNCTION_BUSY` (the function is being deleted).
|
|
21955
|
+
* It never turns the backend on. Two minutes.
|
|
21956
|
+
*/
|
|
21957
|
+
async createVersion(projectId, name, request2) {
|
|
21958
|
+
return this.client.request(
|
|
21959
|
+
this.functionPath(projectId, name, "/versions"),
|
|
21960
|
+
{
|
|
21961
|
+
method: "POST",
|
|
21962
|
+
body: request2,
|
|
21963
|
+
context: "Create a function version",
|
|
21964
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21965
|
+
}
|
|
21966
|
+
);
|
|
21967
|
+
}
|
|
21968
|
+
/**
|
|
21969
|
+
* Uploads a version whose bundle is in R2 and makes it live. `versionId` is
|
|
21970
|
+
* `CreatedFunctionVersion.version.id`. The refusals before anything is
|
|
21971
|
+
* uploaded leave the function as it was: `FUNCTION_NOT_FOUND`,
|
|
21972
|
+
* `VERSION_NOT_FOUND`, `VERSION_NOT_DEPLOYABLE` (pruned),
|
|
21973
|
+
* `BACKEND_NOT_READY`, `BACKEND_OFF`, `FUNCTION_BUSY`, `BUNDLE_MISSING`,
|
|
21974
|
+
* `BUNDLE_MISMATCH`. Then `FUNCTION_DEPLOY_FAILED` (Cloudflare refused or
|
|
21975
|
+
* could not be reached; the version that was live still runs),
|
|
21976
|
+
* `FUNCTIONS_RATE_LIMITED` (503, `retry-after`),
|
|
21977
|
+
* `FUNCTION_DEPLOY_INCOMPLETE` (the code runs but the function list did
|
|
21978
|
+
* not take it, so callers may still get the previous `verifyJwt` setting
|
|
21979
|
+
* or 404 — deploy again) and `BACKEND_OUTDATED` (the project's
|
|
21980
|
+
* database predates the function list — `backend.enable` updates it).
|
|
21981
|
+
* Two minutes.
|
|
21982
|
+
*/
|
|
21983
|
+
async deploy(projectId, name, versionId) {
|
|
21984
|
+
const body = await this.client.request(
|
|
21985
|
+
this.functionPath(
|
|
21986
|
+
projectId,
|
|
21987
|
+
name,
|
|
21988
|
+
`/versions/${this.segment(versionId, "version id")}/deploy`
|
|
21989
|
+
),
|
|
21990
|
+
{
|
|
21991
|
+
method: "POST",
|
|
21992
|
+
context: "Deploy a function",
|
|
21993
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21994
|
+
}
|
|
21995
|
+
);
|
|
21996
|
+
return body.function;
|
|
21997
|
+
}
|
|
21998
|
+
/**
|
|
21999
|
+
* Puts a stored version live again: the one numbered `version`, which must
|
|
22000
|
+
* have been live before (`VERSION_NOT_FOUND` otherwise — put a version that
|
|
22001
|
+
* never went live up with `deploy`), or without it the deployed version
|
|
22002
|
+
* before the live one. The code comes back, not the old keys or secrets:
|
|
22003
|
+
* the bindings are the project's now. The refusals are `deploy`'s, and
|
|
22004
|
+
* `VERSION_NOT_FOUND` also says there is no earlier deployed version.
|
|
22005
|
+
* Two minutes.
|
|
22006
|
+
*/
|
|
22007
|
+
async rollback(projectId, name, version2) {
|
|
22008
|
+
const body = await this.client.request(
|
|
22009
|
+
this.functionPath(projectId, name, "/rollback"),
|
|
22010
|
+
{
|
|
22011
|
+
method: "POST",
|
|
22012
|
+
body: version2 === void 0 ? {} : { version: version2 },
|
|
22013
|
+
context: "Roll back a function",
|
|
22014
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22015
|
+
}
|
|
22016
|
+
);
|
|
22017
|
+
return body.function;
|
|
22018
|
+
}
|
|
22019
|
+
/**
|
|
22020
|
+
* `verifyJwt` without an upload, live within seconds; the next deploy sets
|
|
22021
|
+
* it again from the version it puts live. `FUNCTION_NOT_FOUND`,
|
|
22022
|
+
* `FUNCTION_BUSY`, `FUNCTION_UPDATE_FAILED` (502: the function list in the
|
|
22023
|
+
* project's database could not be written — nothing changed, send it again)
|
|
22024
|
+
* and `BACKEND_OUTDATED`.
|
|
22025
|
+
*/
|
|
22026
|
+
async update(projectId, name, patch) {
|
|
22027
|
+
const body = await this.client.request(
|
|
22028
|
+
this.functionPath(projectId, name),
|
|
22029
|
+
{ method: "PATCH", body: patch, context: "Update a function" }
|
|
22030
|
+
);
|
|
22031
|
+
return body.function;
|
|
22032
|
+
}
|
|
22033
|
+
/**
|
|
22034
|
+
* Its URL stops working within seconds; its stored versions are removed by
|
|
22035
|
+
* the control plane's sweep. `FUNCTION_NOT_FOUND`, `FUNCTION_BUSY`.
|
|
22036
|
+
*/
|
|
22037
|
+
async remove(projectId, name) {
|
|
22038
|
+
await this.client.request(
|
|
22039
|
+
this.functionPath(projectId, name),
|
|
22040
|
+
{
|
|
22041
|
+
method: "DELETE",
|
|
22042
|
+
context: "Delete a function"
|
|
22043
|
+
}
|
|
22044
|
+
);
|
|
22045
|
+
}
|
|
22046
|
+
/**
|
|
22047
|
+
* The function's newest 200 log lines, optionally of one level (`log`,
|
|
22048
|
+
* `info`, `warn`, `error` or `debug`; anything else is `400
|
|
22049
|
+
* VALIDATION_FAILED`). `LOGS_DISABLED` (503) while the control plane has no
|
|
22050
|
+
* log tail.
|
|
22051
|
+
*/
|
|
22052
|
+
async logs(projectId, name, options = {}) {
|
|
22053
|
+
const query = options.level ? `?${new URLSearchParams({ level: options.level }).toString()}` : "";
|
|
22054
|
+
const body = await this.client.request(
|
|
22055
|
+
this.functionPath(projectId, name, `/logs${query}`),
|
|
22056
|
+
{ context: "Read function logs" }
|
|
22057
|
+
);
|
|
22058
|
+
return body.entries;
|
|
22059
|
+
}
|
|
22060
|
+
/** The project's function secrets: names, digests and when — never a value. `env.read`. */
|
|
22061
|
+
async secrets(projectId) {
|
|
22062
|
+
const body = await this.client.request(
|
|
22063
|
+
this.path(projectId, "-secrets"),
|
|
22064
|
+
{ context: "List function secrets" }
|
|
22065
|
+
);
|
|
22066
|
+
return body.secrets;
|
|
22067
|
+
}
|
|
22068
|
+
/**
|
|
22069
|
+
* Sets and unsets function secrets in one change (`env.write.production`;
|
|
22070
|
+
* a suspended project is refused). `VALIDATION_FAILED` names the first
|
|
22071
|
+
* problem — a name, a reserved name (`NARO_*`, `ASSETS`, `DB`) or
|
|
22072
|
+
* `__proto__`, a value over 5,120 bytes, more than 100 secrets. Live
|
|
22073
|
+
* functions carry the change once synced: `pending` says how many. The
|
|
22074
|
+
* control plane starts two at once and its five-minute sweep goes on with
|
|
22075
|
+
* the rest, 20 a run; `sync` brings them up now — call it until `pending`
|
|
22076
|
+
* is 0 to be sure, as a change racing another can leave a function only
|
|
22077
|
+
* `sync` finds (known-issues #322).
|
|
22078
|
+
*/
|
|
22079
|
+
async setSecrets(projectId, change) {
|
|
22080
|
+
return this.client.request(
|
|
22081
|
+
this.path(projectId, "-secrets"),
|
|
22082
|
+
{ method: "PUT", body: change, context: "Change function secrets" }
|
|
22083
|
+
);
|
|
22084
|
+
}
|
|
22085
|
+
/**
|
|
22086
|
+
* Uploads stale functions again — `limit` of them — and with `registry`
|
|
22087
|
+
* writes the function list in the project's database again. Call it
|
|
22088
|
+
* again while `pending` is above 0 and the call made progress.
|
|
22089
|
+
* `FUNCTIONS_RATE_LIMITED` (503) carries `details.retryAfter`. Two minutes.
|
|
22090
|
+
*/
|
|
22091
|
+
async sync(projectId, options = {}) {
|
|
22092
|
+
return this.client.request(
|
|
22093
|
+
this.path(projectId, "-sync"),
|
|
22094
|
+
{
|
|
22095
|
+
method: "POST",
|
|
22096
|
+
body: options,
|
|
22097
|
+
context: "Sync functions",
|
|
22098
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22099
|
+
}
|
|
22100
|
+
);
|
|
22101
|
+
}
|
|
22102
|
+
};
|
|
21618
22103
|
|
|
21619
22104
|
// ../../packages/sdk/src/database.ts
|
|
21620
22105
|
var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
|
|
@@ -21637,8 +22122,8 @@ var MAX_CELL_CHARS = 60;
|
|
|
21637
22122
|
function formatCell(value) {
|
|
21638
22123
|
if (value === null || value === void 0) return "NULL";
|
|
21639
22124
|
const text2 = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
21640
|
-
const
|
|
21641
|
-
return
|
|
22125
|
+
const oneLine2 = text2.replace(/\s+/g, " ");
|
|
22126
|
+
return oneLine2.length > MAX_CELL_CHARS ? `${oneLine2.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine2;
|
|
21642
22127
|
}
|
|
21643
22128
|
function renderRows(statement2) {
|
|
21644
22129
|
if (statement2.columns.length === 0) return [];
|
|
@@ -22056,7 +22541,7 @@ function isWordChar(char) {
|
|
|
22056
22541
|
function asciiUpper2(text2) {
|
|
22057
22542
|
return text2.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22058
22543
|
}
|
|
22059
|
-
function
|
|
22544
|
+
function quoted2(sql, start, close, doubles) {
|
|
22060
22545
|
let value = "";
|
|
22061
22546
|
for (let index = start + 1; index < sql.length; index++) {
|
|
22062
22547
|
const char = sql.charAt(index);
|
|
@@ -22089,7 +22574,7 @@ function tokenize(sql) {
|
|
|
22089
22574
|
index = end + 2;
|
|
22090
22575
|
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22091
22576
|
const close = char === "[" ? "]" : char;
|
|
22092
|
-
const read =
|
|
22577
|
+
const read = quoted2(sql, index, close, char !== "[");
|
|
22093
22578
|
if (!read) return void 0;
|
|
22094
22579
|
tokens.push({
|
|
22095
22580
|
kind: char === "'" ? "string" : "name",
|
|
@@ -24179,9 +24664,9 @@ function readCondition(stream) {
|
|
|
24179
24664
|
const token = stream.peek();
|
|
24180
24665
|
const state = newState(stream);
|
|
24181
24666
|
const expr = readOr(state);
|
|
24182
|
-
const
|
|
24183
|
-
if (
|
|
24184
|
-
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM,
|
|
24667
|
+
const bare2 = state.foldernames.find(({ node }) => node.index === null);
|
|
24668
|
+
if (bare2)
|
|
24669
|
+
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM, bare2.token.position);
|
|
24185
24670
|
return requireCondition(expr, token);
|
|
24186
24671
|
}
|
|
24187
24672
|
function checkExpressionSize(expr, position) {
|
|
@@ -24589,8 +25074,8 @@ function policiesOn(policies, table) {
|
|
|
24589
25074
|
const folded = asciiLower(table);
|
|
24590
25075
|
return policies.filter((policy) => asciiLower(policy.table) === folded);
|
|
24591
25076
|
}
|
|
24592
|
-
function foldPolicyName(name,
|
|
24593
|
-
return
|
|
25077
|
+
function foldPolicyName(name, quoted3) {
|
|
25078
|
+
return quoted3 ? name : asciiLower(name);
|
|
24594
25079
|
}
|
|
24595
25080
|
function sqlStringLiteral(text2) {
|
|
24596
25081
|
return text2 === null ? "NULL" : quoteString(text2);
|
|
@@ -28258,10 +28743,10 @@ function headTokens(text2) {
|
|
|
28258
28743
|
const char = text2.charAt(index);
|
|
28259
28744
|
if (index >= text2.length) break;
|
|
28260
28745
|
if (char === '"' || char === "`" || char === "'") {
|
|
28261
|
-
const
|
|
28262
|
-
if (!
|
|
28263
|
-
tokens.push({ kind: "name", text:
|
|
28264
|
-
index =
|
|
28746
|
+
const quoted3 = readQuoted(text2, index, char);
|
|
28747
|
+
if (!quoted3) break;
|
|
28748
|
+
tokens.push({ kind: "name", text: quoted3.text });
|
|
28749
|
+
index = quoted3.end;
|
|
28265
28750
|
} else if (char === "[") {
|
|
28266
28751
|
const close = text2.indexOf("]", index + 1);
|
|
28267
28752
|
if (close < 0) break;
|
|
@@ -28428,6 +28913,13 @@ function readSqlHead(text2) {
|
|
|
28428
28913
|
return OTHER;
|
|
28429
28914
|
}
|
|
28430
28915
|
|
|
28916
|
+
// ../../packages/data-core/src/functions-registry.ts
|
|
28917
|
+
var FUNCTIONS_TABLE = "_naro_functions";
|
|
28918
|
+
var FUNCTIONS_REGISTRY_VERSION = 9;
|
|
28919
|
+
var FUNCTIONS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ${FUNCTIONS_TABLE} (name TEXT NOT NULL PRIMARY KEY CHECK (name GLOB '[a-z]*' AND length(name) BETWEEN 1 AND 64), verify_jwt INTEGER NOT NULL DEFAULT 1 CHECK (verify_jwt IN (0, 1)), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`;
|
|
28920
|
+
var MAX_REGISTERED_FUNCTIONS = 2e3;
|
|
28921
|
+
var FUNCTIONS_REGISTRY_SQL = `SELECT name, verify_jwt FROM ${FUNCTIONS_TABLE} ORDER BY name LIMIT ${String(MAX_REGISTERED_FUNCTIONS)}`;
|
|
28922
|
+
|
|
28431
28923
|
// ../../packages/data-core/src/policy-index.ts
|
|
28432
28924
|
var INDEX_SQL = `SELECT t.name AS table_name, il.name AS index_name, il."partial" AS partial, ix.name AS column_name, ix.coll AS collation FROM pragma_table_list AS t JOIN pragma_index_list(t.name, t.schema) AS il JOIN pragma_index_xinfo(il.name, t.schema) AS ix WHERE t.schema = 'main' AND t.type = 'table' AND ${systemTablePredicate("t.name")} AND ix.seqno = 0`;
|
|
28433
28925
|
function indexesFromRows(rows) {
|
|
@@ -30069,13 +30561,13 @@ function ownerComment(text2, position) {
|
|
|
30069
30561
|
if (rest !== "" && !/^[ \t]/.test(rest)) return void 0;
|
|
30070
30562
|
let column2 = rest.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
30071
30563
|
if (column2.startsWith('"')) {
|
|
30072
|
-
const
|
|
30073
|
-
if (!
|
|
30564
|
+
const quoted3 = /^"((?:[^"]|"")*)"$/.exec(column2);
|
|
30565
|
+
if (!quoted3)
|
|
30074
30566
|
throw new PolicySyntaxError(
|
|
30075
30567
|
"naro:owner needs one column name, bare or in double quotes",
|
|
30076
30568
|
position
|
|
30077
30569
|
);
|
|
30078
|
-
column2 = (
|
|
30570
|
+
column2 = (quoted3[1] ?? "").replaceAll('""', '"');
|
|
30079
30571
|
}
|
|
30080
30572
|
if (column2 === "")
|
|
30081
30573
|
throw new PolicySyntaxError("naro:owner needs a column name", position);
|
|
@@ -30282,6 +30774,15 @@ var SYSTEM_MIGRATIONS = [
|
|
|
30282
30774
|
version: REALTIME_TABLES_VERSION,
|
|
30283
30775
|
name: "realtime",
|
|
30284
30776
|
statements: [REALTIME_TABLES_SQL]
|
|
30777
|
+
},
|
|
30778
|
+
{
|
|
30779
|
+
// Functions 3-3a (design 2026-10-29 §6.3): the function registry the
|
|
30780
|
+
// gate reads. Every backend gets it, Functions on or not; the control
|
|
30781
|
+
// plane (3-3b) writes it. A worker from before it never reads it, and a
|
|
30782
|
+
// worker that reads it on a D1 without it answers 409 BACKEND_OUTDATED.
|
|
30783
|
+
version: FUNCTIONS_REGISTRY_VERSION,
|
|
30784
|
+
name: "functions",
|
|
30785
|
+
statements: [FUNCTIONS_TABLE_SQL]
|
|
30285
30786
|
}
|
|
30286
30787
|
];
|
|
30287
30788
|
|
|
@@ -31053,13 +31554,13 @@ function compareMigrations(local, applied) {
|
|
|
31053
31554
|
rows.push({ name, status: "applied", appliedAt: exact.appliedAt });
|
|
31054
31555
|
continue;
|
|
31055
31556
|
}
|
|
31056
|
-
const
|
|
31057
|
-
if (
|
|
31058
|
-
ledger.delete(
|
|
31557
|
+
const bare2 = ledger.get(name.replace(/\.sql$/, ""));
|
|
31558
|
+
if (bare2) {
|
|
31559
|
+
ledger.delete(bare2.name);
|
|
31059
31560
|
rows.push({
|
|
31060
31561
|
name,
|
|
31061
31562
|
status: "applied",
|
|
31062
|
-
appliedAt:
|
|
31563
|
+
appliedAt: bare2.appliedAt,
|
|
31063
31564
|
warning: "ledger-name-missing-extension"
|
|
31064
31565
|
});
|
|
31065
31566
|
continue;
|
|
@@ -32002,6 +32503,10 @@ function failureCode(error51, fallback) {
|
|
|
32002
32503
|
const code = error51 && typeof error51 === "object" ? error51.code : void 0;
|
|
32003
32504
|
return typeof code === "string" && DEPLOYMENT_CODES.has(code) ? code : fallback;
|
|
32004
32505
|
}
|
|
32506
|
+
var SYSTEM_NAMES = new Set(NARO_SYSTEM_ENV_NAMES);
|
|
32507
|
+
function isNaroSystemName(key) {
|
|
32508
|
+
return SYSTEM_NAMES.has(key);
|
|
32509
|
+
}
|
|
32005
32510
|
function slugify2(value) {
|
|
32006
32511
|
const slug = value.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 54);
|
|
32007
32512
|
return slug || "app";
|
|
@@ -32184,6 +32689,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32184
32689
|
const provided = options.env ?? {};
|
|
32185
32690
|
missing = scanned.filter((key) => !existing.has(key) && !(key in provided));
|
|
32186
32691
|
}
|
|
32692
|
+
if (environment !== void 0 && options.projectId && missing.some((key) => isNaroSystemName(key))) {
|
|
32693
|
+
const backend = await client.backend.info(options.projectId).catch(() => null);
|
|
32694
|
+
if (backend !== null && backend.status !== "none") {
|
|
32695
|
+
const set2 = missing.filter((key) => isNaroSystemName(key));
|
|
32696
|
+
missing = missing.filter((key) => !isNaroSystemName(key));
|
|
32697
|
+
log2(
|
|
32698
|
+
`\u25B8 naro sets ${set2.join(", ")} for ${environment} to its own backend's: not asked for`
|
|
32699
|
+
);
|
|
32700
|
+
}
|
|
32701
|
+
}
|
|
32187
32702
|
let collected = {};
|
|
32188
32703
|
if (missing.length > 0) {
|
|
32189
32704
|
log2(
|
|
@@ -32229,7 +32744,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32229
32744
|
repositoryUrl: sourceDir,
|
|
32230
32745
|
branch: ctx.branch,
|
|
32231
32746
|
rootDirectory: options.rootDirectory ?? null,
|
|
32232
|
-
|
|
32747
|
+
// An environment's deploy: the control plane saved what this sent for
|
|
32748
|
+
// that environment and then chose its values, so a key sent and a key
|
|
32749
|
+
// chosen usually hold the same value — except the names naro sets for
|
|
32750
|
+
// an environment with a backend (named environments spec §8.3), which
|
|
32751
|
+
// must win here too, or this build would call another backend than
|
|
32752
|
+
// the worker's bindings, and a key the server failed to save
|
|
32753
|
+
// (`envSkipped`), whose stored value the worker gets and so this build
|
|
32754
|
+
// takes too. Production and preview keep a value sent over the stored
|
|
32755
|
+
// one, as before.
|
|
32756
|
+
env: ctx.environment ? { ...envToSend, ...ctx.env } : { ...ctx.env, ...envToSend },
|
|
32233
32757
|
strategy: ctx.strategy ?? null,
|
|
32234
32758
|
...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
|
|
32235
32759
|
},
|
|
@@ -32320,10 +32844,12 @@ function deploymentUrl(detail, ctx, production) {
|
|
|
32320
32844
|
}
|
|
32321
32845
|
|
|
32322
32846
|
export {
|
|
32847
|
+
environmentGuidance,
|
|
32323
32848
|
parseGitHubRemote,
|
|
32324
32849
|
schemaMigrationTitle,
|
|
32325
32850
|
projectPath,
|
|
32326
32851
|
splitStatements,
|
|
32852
|
+
environmentSlugProblem,
|
|
32327
32853
|
ANALYTICS_FILTER_KEYS,
|
|
32328
32854
|
NaroError,
|
|
32329
32855
|
promoteTargetUnsupported,
|