naro-cli 0.21.0 → 0.23.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 +86 -2
- package/dist/args.d.ts +238 -0
- package/dist/{chunk-FFPOVA7I.js → chunk-ALK4VTL2.js} +2743 -392
- package/dist/commands/confirm.d.ts +12 -0
- package/dist/commands/db.d.ts +32 -0
- package/dist/commands/migration.d.ts +78 -0
- package/dist/context.d.ts +21 -0
- package/dist/data-environment.d.ts +36 -0
- package/dist/deploy.d.ts +1 -0
- package/dist/deploy.js +5 -1
- package/dist/functions/deploy.d.ts +70 -0
- package/dist/functions/retry.d.ts +16 -0
- package/dist/index.js +2256 -853
- package/dist/version.d.ts +6 -0
- package/package.json +11 -5
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-K3NQKI34.js";
|
|
5
5
|
|
|
6
6
|
// src/deploy.ts
|
|
7
|
-
import { existsSync as
|
|
7
|
+
import { existsSync as existsSync10 } from "fs";
|
|
8
8
|
import { mkdtemp as mkdtemp2, readFile as readFile15, rm as rm5 } from "fs/promises";
|
|
9
9
|
import { tmpdir as tmpdir2 } from "os";
|
|
10
|
-
import { basename as
|
|
11
|
-
import { fileURLToPath } from "url";
|
|
10
|
+
import { basename as basename5, dirname as dirname10, join as join32, resolve as resolve14 } from "path";
|
|
11
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12
12
|
|
|
13
13
|
// ../../packages/build/src/artifact-upload.ts
|
|
14
14
|
function artifactUploadEntries(artifact) {
|
|
@@ -41,7 +41,7 @@ function parseArtifactPutUrls(payload) {
|
|
|
41
41
|
}
|
|
42
42
|
async function eachWithinBudget(items, limit, budget, weigh, work) {
|
|
43
43
|
if (items.length === 0) return;
|
|
44
|
-
await new Promise((
|
|
44
|
+
await new Promise((resolve15, reject) => {
|
|
45
45
|
let next = 0;
|
|
46
46
|
let running = 0;
|
|
47
47
|
let weight = 0;
|
|
@@ -58,7 +58,7 @@ async function eachWithinBudget(items, limit, budget, weigh, work) {
|
|
|
58
58
|
running -= 1;
|
|
59
59
|
weight -= itemWeight;
|
|
60
60
|
finished += 1;
|
|
61
|
-
if (finished === items.length)
|
|
61
|
+
if (finished === items.length) resolve15();
|
|
62
62
|
else pump();
|
|
63
63
|
}, reject);
|
|
64
64
|
}
|
|
@@ -98,11 +98,11 @@ async function uploadArtifactFiles(artifact, ports, limits = {}) {
|
|
|
98
98
|
await ports.putProxied(entry);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
const
|
|
101
|
+
const bytes2 = entries.reduce(
|
|
102
102
|
(total, entry) => total + entry.file.data.byteLength,
|
|
103
103
|
0
|
|
104
104
|
);
|
|
105
|
-
return { proxied, total: entries.length, bytes };
|
|
105
|
+
return { proxied, total: entries.length, bytes: bytes2 };
|
|
106
106
|
}
|
|
107
107
|
function artifactUploadSummary(report, elapsedMs) {
|
|
108
108
|
const kilobytes = Math.ceil(report.bytes / 1e3);
|
|
@@ -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(text3, slug) {
|
|
454
|
+
if (slug === null || !text3.includes("naro ")) return text3;
|
|
455
|
+
const switchContext = SWITCH_CONTEXT.test(text3);
|
|
456
|
+
const spans = [];
|
|
457
|
+
const marked = text3.replaceAll(
|
|
458
|
+
QUOTED,
|
|
459
|
+
(match, span, offset) => {
|
|
460
|
+
const noted = text3.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,13 @@ var DeploymentError = class extends Error {
|
|
|
460
620
|
}
|
|
461
621
|
};
|
|
462
622
|
|
|
623
|
+
// ../../packages/core/src/functions.ts
|
|
624
|
+
var FUNCTION_NAME_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
625
|
+
var FUNCTION_WORKER_NAME_PREFIX = "naro-fn-";
|
|
626
|
+
function isFunctionName(name) {
|
|
627
|
+
return FUNCTION_NAME_RE.test(name);
|
|
628
|
+
}
|
|
629
|
+
|
|
463
630
|
// ../../packages/core/src/git-remote.ts
|
|
464
631
|
var SCP_LIKE = /^[^@\s/]+@([^:\s/]+):(.+)$/;
|
|
465
632
|
function parseGitHubRemote(url2) {
|
|
@@ -1771,16 +1938,16 @@ function cleanEnum(obj) {
|
|
|
1771
1938
|
}
|
|
1772
1939
|
function base64ToUint8Array(base643) {
|
|
1773
1940
|
const binaryString = atob(base643);
|
|
1774
|
-
const
|
|
1941
|
+
const bytes2 = new Uint8Array(binaryString.length);
|
|
1775
1942
|
for (let i = 0; i < binaryString.length; i++) {
|
|
1776
|
-
|
|
1943
|
+
bytes2[i] = binaryString.charCodeAt(i);
|
|
1777
1944
|
}
|
|
1778
|
-
return
|
|
1945
|
+
return bytes2;
|
|
1779
1946
|
}
|
|
1780
|
-
function uint8ArrayToBase64(
|
|
1947
|
+
function uint8ArrayToBase64(bytes2) {
|
|
1781
1948
|
let binaryString = "";
|
|
1782
|
-
for (let i = 0; i <
|
|
1783
|
-
binaryString += String.fromCharCode(
|
|
1949
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
1950
|
+
binaryString += String.fromCharCode(bytes2[i]);
|
|
1784
1951
|
}
|
|
1785
1952
|
return btoa(binaryString);
|
|
1786
1953
|
}
|
|
@@ -1789,22 +1956,22 @@ function base64urlToUint8Array(base64url3) {
|
|
|
1789
1956
|
const padding = "=".repeat((4 - base643.length % 4) % 4);
|
|
1790
1957
|
return base64ToUint8Array(base643 + padding);
|
|
1791
1958
|
}
|
|
1792
|
-
function uint8ArrayToBase64url(
|
|
1793
|
-
return uint8ArrayToBase64(
|
|
1959
|
+
function uint8ArrayToBase64url(bytes2) {
|
|
1960
|
+
return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1794
1961
|
}
|
|
1795
1962
|
function hexToUint8Array(hex3) {
|
|
1796
1963
|
const cleanHex = hex3.replace(/^0x/, "");
|
|
1797
1964
|
if (cleanHex.length % 2 !== 0) {
|
|
1798
1965
|
throw new Error("Invalid hex string length");
|
|
1799
1966
|
}
|
|
1800
|
-
const
|
|
1967
|
+
const bytes2 = new Uint8Array(cleanHex.length / 2);
|
|
1801
1968
|
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1802
|
-
|
|
1969
|
+
bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1803
1970
|
}
|
|
1804
|
-
return
|
|
1971
|
+
return bytes2;
|
|
1805
1972
|
}
|
|
1806
|
-
function uint8ArrayToHex(
|
|
1807
|
-
return Array.from(
|
|
1973
|
+
function uint8ArrayToHex(bytes2) {
|
|
1974
|
+
return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1808
1975
|
}
|
|
1809
1976
|
var Class = class {
|
|
1810
1977
|
constructor(..._args) {
|
|
@@ -2110,10 +2277,10 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
|
2110
2277
|
var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
|
|
2111
2278
|
var extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
|
|
2112
2279
|
var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
|
|
2113
|
-
var uuid = (
|
|
2114
|
-
if (!
|
|
2280
|
+
var uuid = (version3) => {
|
|
2281
|
+
if (!version3)
|
|
2115
2282
|
return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
|
|
2116
|
-
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${
|
|
2283
|
+
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
|
|
2117
2284
|
};
|
|
2118
2285
|
var uuid4 = /* @__PURE__ */ uuid(4);
|
|
2119
2286
|
var uuid6 = /* @__PURE__ */ uuid(6);
|
|
@@ -3595,7 +3762,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3595
3762
|
const _normalized = cached(() => normalizeDef(def));
|
|
3596
3763
|
const generateFastpass = (shape) => {
|
|
3597
3764
|
const doc = new Doc(["shape", "payload", "ctx"]);
|
|
3598
|
-
const
|
|
3765
|
+
const normalized2 = _normalized.value;
|
|
3599
3766
|
const parseStr = (key) => {
|
|
3600
3767
|
const k = esc(key);
|
|
3601
3768
|
return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
@@ -3603,11 +3770,11 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3603
3770
|
doc.write(`const input = payload.value;`);
|
|
3604
3771
|
const ids = /* @__PURE__ */ Object.create(null);
|
|
3605
3772
|
let counter = 0;
|
|
3606
|
-
for (const key of
|
|
3773
|
+
for (const key of normalized2.keys) {
|
|
3607
3774
|
ids[key] = `key_${counter++}`;
|
|
3608
3775
|
}
|
|
3609
3776
|
doc.write(`const newResult = {};`);
|
|
3610
|
-
for (const key of
|
|
3777
|
+
for (const key of normalized2.keys) {
|
|
3611
3778
|
const id = ids[key];
|
|
3612
3779
|
const k = esc(key);
|
|
3613
3780
|
const schema = shape[key];
|
|
@@ -8154,8 +8321,8 @@ function ko_default() {
|
|
|
8154
8321
|
}
|
|
8155
8322
|
|
|
8156
8323
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js
|
|
8157
|
-
var capitalizeFirstCharacter = (
|
|
8158
|
-
return
|
|
8324
|
+
var capitalizeFirstCharacter = (text3) => {
|
|
8325
|
+
return text3.charAt(0).toUpperCase() + text3.slice(1);
|
|
8159
8326
|
};
|
|
8160
8327
|
function getUnitTypeFromNumber(number4) {
|
|
8161
8328
|
const abs = Math.abs(number4);
|
|
@@ -14983,23 +15150,23 @@ function fromJSONSchema(schema, params) {
|
|
|
14983
15150
|
if (typeof schema === "boolean") {
|
|
14984
15151
|
return schema ? z.any() : z.never();
|
|
14985
15152
|
}
|
|
14986
|
-
let
|
|
15153
|
+
let normalized2;
|
|
14987
15154
|
try {
|
|
14988
|
-
|
|
15155
|
+
normalized2 = JSON.parse(JSON.stringify(schema));
|
|
14989
15156
|
} catch {
|
|
14990
15157
|
throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
|
|
14991
15158
|
}
|
|
14992
|
-
const
|
|
14993
|
-
const defs =
|
|
15159
|
+
const version3 = detectVersion(normalized2, params?.defaultTarget);
|
|
15160
|
+
const defs = normalized2.$defs || normalized2.definitions || {};
|
|
14994
15161
|
const ctx = {
|
|
14995
|
-
version:
|
|
15162
|
+
version: version3,
|
|
14996
15163
|
defs,
|
|
14997
15164
|
refs: /* @__PURE__ */ new Map(),
|
|
14998
15165
|
processing: /* @__PURE__ */ new Set(),
|
|
14999
|
-
rootSchema:
|
|
15166
|
+
rootSchema: normalized2,
|
|
15000
15167
|
registry: params?.registry ?? globalRegistry
|
|
15001
15168
|
};
|
|
15002
|
-
return convertSchema(
|
|
15169
|
+
return convertSchema(normalized2, ctx);
|
|
15003
15170
|
}
|
|
15004
15171
|
|
|
15005
15172
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/coerce.js
|
|
@@ -15064,6 +15231,8 @@ function subdomainProblem(value) {
|
|
|
15064
15231
|
if (value.endsWith("-preview")) return "reserved";
|
|
15065
15232
|
if (value === BACKEND_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(BACKEND_WORKER_NAME_PREFIX))
|
|
15066
15233
|
return "reserved";
|
|
15234
|
+
if (value === FUNCTION_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(FUNCTION_WORKER_NAME_PREFIX))
|
|
15235
|
+
return "reserved";
|
|
15067
15236
|
if (RESERVED_SUBDOMAINS.includes(value))
|
|
15068
15237
|
return "reserved";
|
|
15069
15238
|
return null;
|
|
@@ -15142,8 +15311,8 @@ var ISSPACE = /[ \t\n\r\f\v]/;
|
|
|
15142
15311
|
var NUL = String.fromCharCode(0);
|
|
15143
15312
|
var BOM = 65279;
|
|
15144
15313
|
var PRAGMA_PREFIX = "PRAGMA_";
|
|
15145
|
-
function asciiUpper(
|
|
15146
|
-
return
|
|
15314
|
+
function asciiUpper(text3) {
|
|
15315
|
+
return text3.replace(/[a-z]+/g, (run) => run.toUpperCase());
|
|
15147
15316
|
}
|
|
15148
15317
|
function pragmaCandidate(sql, start, end, doubled) {
|
|
15149
15318
|
const prefix = sql.slice(start + 1, start + 1 + PRAGMA_PREFIX.length);
|
|
@@ -15399,6 +15568,72 @@ function workerNameForSurface(subdomain, surface) {
|
|
|
15399
15568
|
}
|
|
15400
15569
|
}
|
|
15401
15570
|
|
|
15571
|
+
// ../../packages/core/src/usage-alert-mail.ts
|
|
15572
|
+
var REQUESTS = {
|
|
15573
|
+
en: {
|
|
15574
|
+
subject80: (team) => `${team} has used 80% of its monthly requests`,
|
|
15575
|
+
subject100: (team) => `${team} has reached its monthly request limit`,
|
|
15576
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} requests the Hobby plan includes for ${month}.`,
|
|
15577
|
+
warning: "When it reaches the limit, requests past it may be blocked until the month ends (UTC).",
|
|
15578
|
+
blocked: "Sites on this team answer 503 until the month ends (UTC).",
|
|
15579
|
+
running: "Sites keep running for now.",
|
|
15580
|
+
button: "View usage",
|
|
15581
|
+
footer: (team) => `You're receiving this because you're an owner or billing member of ${team} on naro.`
|
|
15582
|
+
},
|
|
15583
|
+
ko: {
|
|
15584
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15585
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15586
|
+
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.`,
|
|
15587
|
+
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.",
|
|
15588
|
+
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.",
|
|
15589
|
+
running: "\uC9C0\uAE08\uC740 \uC0AC\uC774\uD2B8\uAC00 \uACC4\uC18D \uB3D9\uC791\uD569\uB2C8\uB2E4.",
|
|
15590
|
+
button: "\uC0AC\uC6A9\uB7C9 \uBCF4\uAE30",
|
|
15591
|
+
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.`
|
|
15592
|
+
},
|
|
15593
|
+
ja: {
|
|
15594
|
+
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`,
|
|
15595
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15596
|
+
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`,
|
|
15597
|
+
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",
|
|
15598
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u30B5\u30A4\u30C8\u306F503\u3092\u8FD4\u3057\u307E\u3059\u3002",
|
|
15599
|
+
running: "\u73FE\u5728\u3001\u30B5\u30A4\u30C8\u306F\u5F15\u304D\u7D9A\u304D\u52D5\u4F5C\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
15600
|
+
button: "\u4F7F\u7528\u91CF\u3092\u898B\u308B",
|
|
15601
|
+
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`
|
|
15602
|
+
}
|
|
15603
|
+
};
|
|
15604
|
+
var BUILDS = {
|
|
15605
|
+
en: {
|
|
15606
|
+
subject80: (team) => `${team} has used 80% of its monthly builds`,
|
|
15607
|
+
subject100: (team) => `${team} has reached its monthly build limit`,
|
|
15608
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} builds the Hobby plan includes for ${month}.`,
|
|
15609
|
+
warning: "When it reaches the limit, new deploys may be refused until the month ends (UTC).",
|
|
15610
|
+
blocked: "New deploys on this team are refused until the month ends (UTC).",
|
|
15611
|
+
running: "Deploys keep working for now.",
|
|
15612
|
+
button: REQUESTS.en.button,
|
|
15613
|
+
footer: REQUESTS.en.footer
|
|
15614
|
+
},
|
|
15615
|
+
ko: {
|
|
15616
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15617
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15618
|
+
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.`,
|
|
15619
|
+
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.",
|
|
15620
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0C8 \uBC30\uD3EC\uB294 \uAC70\uC808\uB429\uB2C8\uB2E4.",
|
|
15621
|
+
running: "\uC9C0\uAE08\uC740 \uBC30\uD3EC\uAC00 \uACC4\uC18D \uB429\uB2C8\uB2E4.",
|
|
15622
|
+
button: REQUESTS.ko.button,
|
|
15623
|
+
footer: REQUESTS.ko.footer
|
|
15624
|
+
},
|
|
15625
|
+
ja: {
|
|
15626
|
+
subject80: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306E80%\u3092\u4F7F\u7528\u3057\u307E\u3057\u305F`,
|
|
15627
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15628
|
+
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`,
|
|
15629
|
+
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",
|
|
15630
|
+
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",
|
|
15631
|
+
running: "\u73FE\u5728\u3001\u30C7\u30D7\u30ED\u30A4\u306F\u5F15\u304D\u7D9A\u304D\u884C\u3048\u307E\u3059\u3002",
|
|
15632
|
+
button: REQUESTS.ja.button,
|
|
15633
|
+
footer: REQUESTS.ja.footer
|
|
15634
|
+
}
|
|
15635
|
+
};
|
|
15636
|
+
|
|
15402
15637
|
// ../../packages/build/src/next-version.ts
|
|
15403
15638
|
var NEXT_RELEASE_DATES = {
|
|
15404
15639
|
16: "2025-10-21",
|
|
@@ -15435,46 +15670,46 @@ function supportEndsAt(major) {
|
|
|
15435
15670
|
function compare(a, b) {
|
|
15436
15671
|
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
15437
15672
|
}
|
|
15438
|
-
function withinVerifiedRange(
|
|
15439
|
-
if (
|
|
15440
|
-
if (
|
|
15441
|
-
return
|
|
15673
|
+
function withinVerifiedRange(version3) {
|
|
15674
|
+
if (version3.major === 15) return compare(version3, VERIFIED_15_FLOOR) >= 0;
|
|
15675
|
+
if (version3.major === 16) return compare(version3, VERIFIED_16_FLOOR) >= 0;
|
|
15676
|
+
return version3.major > 16;
|
|
15442
15677
|
}
|
|
15443
15678
|
function formatDate(date5) {
|
|
15444
15679
|
return date5.toISOString().slice(0, 10);
|
|
15445
15680
|
}
|
|
15446
15681
|
function classifyNextVersion(raw, now) {
|
|
15447
|
-
const
|
|
15448
|
-
if (!
|
|
15682
|
+
const version3 = parseNextVersion(raw);
|
|
15683
|
+
if (!version3) {
|
|
15449
15684
|
return {
|
|
15450
15685
|
status: "unknown",
|
|
15451
15686
|
version: null,
|
|
15452
15687
|
reason: `Could not read a Next.js version from "${raw}".`
|
|
15453
15688
|
};
|
|
15454
15689
|
}
|
|
15455
|
-
const windowEnd = supportEndsAt(
|
|
15456
|
-
if (!windowEnd ||
|
|
15690
|
+
const windowEnd = supportEndsAt(version3.major);
|
|
15691
|
+
if (!windowEnd || version3.major < VERIFIED_15_FLOOR.major) {
|
|
15457
15692
|
return {
|
|
15458
15693
|
status: "too-old",
|
|
15459
|
-
version:
|
|
15694
|
+
version: version3,
|
|
15460
15695
|
reason: `Next.js ${raw} is too old to deploy. Naro deploys Next.js 15 or newer. Upgrade to Next.js 16.`
|
|
15461
15696
|
};
|
|
15462
15697
|
}
|
|
15463
15698
|
if (now > windowEnd) {
|
|
15464
15699
|
return {
|
|
15465
15700
|
status: "needs-unsupported-flag",
|
|
15466
|
-
version:
|
|
15467
|
-
reason: `Next.js ${
|
|
15701
|
+
version: version3,
|
|
15702
|
+
reason: `Next.js ${version3.major} left its support window on ${formatDate(windowEnd)}. The build continues, but unsupported releases stop receiving security patches \u2014 upgrade to Next.js 16. See https://nextjs.org/support-policy`
|
|
15468
15703
|
};
|
|
15469
15704
|
}
|
|
15470
|
-
if (!withinVerifiedRange(
|
|
15705
|
+
if (!withinVerifiedRange(version3)) {
|
|
15471
15706
|
return {
|
|
15472
15707
|
status: "outside-peer-range",
|
|
15473
|
-
version:
|
|
15708
|
+
version: version3,
|
|
15474
15709
|
reason: `Next.js ${raw} is outside the range Naro verifies deploys on (>=15.5.21 <16 || >=16.2.11). The build may fail in ways Naro cannot explain.`
|
|
15475
15710
|
};
|
|
15476
15711
|
}
|
|
15477
|
-
return { status: "supported", version:
|
|
15712
|
+
return { status: "supported", version: version3, reason: "" };
|
|
15478
15713
|
}
|
|
15479
15714
|
|
|
15480
15715
|
// ../../packages/build/src/package-manager.ts
|
|
@@ -15512,8 +15747,8 @@ async function usesModernYarn(root) {
|
|
|
15512
15747
|
const raw = await readFile2(join2(root, "package.json"), "utf8");
|
|
15513
15748
|
const packageJson = JSON.parse(raw);
|
|
15514
15749
|
if (typeof packageJson.packageManager !== "string") return false;
|
|
15515
|
-
const
|
|
15516
|
-
return
|
|
15750
|
+
const version3 = /^yarn@(\d+)/.exec(packageJson.packageManager)?.[1];
|
|
15751
|
+
return version3 !== void 0 && Number(version3) >= 2;
|
|
15517
15752
|
} catch {
|
|
15518
15753
|
return false;
|
|
15519
15754
|
}
|
|
@@ -16401,8 +16636,8 @@ var FRAMEWORK_LABELS = {
|
|
|
16401
16636
|
static: "static files"
|
|
16402
16637
|
};
|
|
16403
16638
|
function dependencyVersion(packageJson, name) {
|
|
16404
|
-
const
|
|
16405
|
-
return typeof
|
|
16639
|
+
const version3 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
|
|
16640
|
+
return typeof version3 === "string" && version3.trim() !== "" ? version3 : null;
|
|
16406
16641
|
}
|
|
16407
16642
|
var STATIC_EXPORT = /(?:^|[\s,{;])output\s*:\s*(["'`])export\1/;
|
|
16408
16643
|
function declaresStaticExport(source) {
|
|
@@ -16591,13 +16826,13 @@ async function detectFramework(root, overrides = {}) {
|
|
|
16591
16826
|
`The project must declare a "${buildScript}" script.`
|
|
16592
16827
|
);
|
|
16593
16828
|
}
|
|
16594
|
-
const
|
|
16829
|
+
const version3 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
|
|
16595
16830
|
return {
|
|
16596
16831
|
preset,
|
|
16597
16832
|
buildScript,
|
|
16598
16833
|
outputDirectory: overrides.outputDirectory ?? (preset.id === "angular" ? await angularOutput(root) : preset.defaultOutputDirectory),
|
|
16599
|
-
description:
|
|
16600
|
-
dependencyRange:
|
|
16834
|
+
description: version3 ? `${FRAMEWORK_LABELS[preset.id]} ${version3}` : FRAMEWORK_LABELS[preset.id],
|
|
16835
|
+
dependencyRange: version3
|
|
16601
16836
|
};
|
|
16602
16837
|
}
|
|
16603
16838
|
|
|
@@ -17131,11 +17366,11 @@ function adapterKeyFromConfigSource(source) {
|
|
|
17131
17366
|
return EXPERIMENTAL_ADAPTER_PATH.test(code) ? "experimental.adapterPath" : "adapterPath";
|
|
17132
17367
|
}
|
|
17133
17368
|
function keysNextReads(nextVersion) {
|
|
17134
|
-
const
|
|
17135
|
-
if (!
|
|
17136
|
-
if (
|
|
17369
|
+
const version3 = parseNextVersion(nextVersion);
|
|
17370
|
+
if (!version3) return ["adapterPath", "experimental.adapterPath"];
|
|
17371
|
+
if (version3.major < 15 || version3.major === 15 && version3.minor < 4)
|
|
17137
17372
|
return [];
|
|
17138
|
-
if (
|
|
17373
|
+
if (version3.major > 16 || version3.major === 16 && version3.minor >= 2)
|
|
17139
17374
|
return ["adapterPath", "experimental.adapterPath"];
|
|
17140
17375
|
return ["experimental.adapterPath"];
|
|
17141
17376
|
}
|
|
@@ -17203,10 +17438,10 @@ async function exists4(path) {
|
|
|
17203
17438
|
return false;
|
|
17204
17439
|
}
|
|
17205
17440
|
}
|
|
17206
|
-
async function findSourceFile(appRoot,
|
|
17441
|
+
async function findSourceFile(appRoot, basename6) {
|
|
17207
17442
|
for (const dir of SOURCE_DIRECTORIES) {
|
|
17208
17443
|
for (const ext of SOURCE_EXTENSIONS2) {
|
|
17209
|
-
const rel = dir ? `${dir}/${
|
|
17444
|
+
const rel = dir ? `${dir}/${basename6}.${ext}` : `${basename6}.${ext}`;
|
|
17210
17445
|
if (await exists4(join12(appRoot, rel))) return rel;
|
|
17211
17446
|
}
|
|
17212
17447
|
}
|
|
@@ -17283,11 +17518,11 @@ async function collectProjectFacts(appRoot) {
|
|
|
17283
17518
|
function label(strategy) {
|
|
17284
17519
|
return `P${String(strategy.priority)} ${strategy.id}`;
|
|
17285
17520
|
}
|
|
17286
|
-
function clause(
|
|
17287
|
-
return
|
|
17521
|
+
function clause(text3) {
|
|
17522
|
+
return text3.endsWith(".") && !text3.endsWith("..") ? text3.slice(0, -1) : text3;
|
|
17288
17523
|
}
|
|
17289
|
-
function sentence(
|
|
17290
|
-
return
|
|
17524
|
+
function sentence(text3) {
|
|
17525
|
+
return text3.endsWith(".") ? text3 : `${text3}.`;
|
|
17291
17526
|
}
|
|
17292
17527
|
function describeOutcome(outcome) {
|
|
17293
17528
|
switch (outcome.status) {
|
|
@@ -18568,8 +18803,8 @@ async function readInstalledNextVersion(appRoot, checkout) {
|
|
|
18568
18803
|
join18(dir, "node_modules", "next", "package.json"),
|
|
18569
18804
|
"utf8"
|
|
18570
18805
|
);
|
|
18571
|
-
const
|
|
18572
|
-
return typeof
|
|
18806
|
+
const version3 = JSON.parse(source).version;
|
|
18807
|
+
return typeof version3 === "string" ? version3 : null;
|
|
18573
18808
|
} catch {
|
|
18574
18809
|
}
|
|
18575
18810
|
if (dir === stop) return null;
|
|
@@ -19244,7 +19479,7 @@ async function errorFromResponse(response, context) {
|
|
|
19244
19479
|
if (typeof serverDetails === "object" && serverDetails !== null && !Array.isArray(serverDetails))
|
|
19245
19480
|
Object.assign(details, serverDetails);
|
|
19246
19481
|
const retryAfter = response.headers.get("retry-after")?.trim() ?? "";
|
|
19247
|
-
if (response.status === 429 && /^\d{1,9}$/.test(retryAfter))
|
|
19482
|
+
if ((response.status === 429 || response.status === 503) && /^\d{1,9}$/.test(retryAfter))
|
|
19248
19483
|
details.retryAfter = Number(retryAfter);
|
|
19249
19484
|
if (body?.error?.fields !== void 0) details.fields = body.error.fields;
|
|
19250
19485
|
if (typeof body?.error?.installUrl === "string")
|
|
@@ -19458,8 +19693,8 @@ function sendPage(res, status, html) {
|
|
|
19458
19693
|
async function startCallbackServer() {
|
|
19459
19694
|
let resolveCode;
|
|
19460
19695
|
let rejectCode;
|
|
19461
|
-
const codePromise = new Promise((
|
|
19462
|
-
resolveCode =
|
|
19696
|
+
const codePromise = new Promise((resolve15, reject) => {
|
|
19697
|
+
resolveCode = resolve15;
|
|
19463
19698
|
rejectCode = reject;
|
|
19464
19699
|
});
|
|
19465
19700
|
const server = createServer2((req, res) => {
|
|
@@ -19488,7 +19723,7 @@ async function startCallbackServer() {
|
|
|
19488
19723
|
sendPage(res, 200, successPage());
|
|
19489
19724
|
resolveCode(code);
|
|
19490
19725
|
});
|
|
19491
|
-
await new Promise((
|
|
19726
|
+
await new Promise((resolve15) => server.listen(0, "127.0.0.1", resolve15));
|
|
19492
19727
|
const port = server.address().port;
|
|
19493
19728
|
return {
|
|
19494
19729
|
port,
|
|
@@ -19712,6 +19947,16 @@ function isReservedEnvTarget(value) {
|
|
|
19712
19947
|
function isEnvTargetName(value) {
|
|
19713
19948
|
return environmentSlugProblem(value) !== "invalid";
|
|
19714
19949
|
}
|
|
19950
|
+
var NARO_SYSTEM_ENV_NAMES = [
|
|
19951
|
+
"NARO_URL",
|
|
19952
|
+
"NEXT_PUBLIC_NARO_URL",
|
|
19953
|
+
"VITE_NARO_URL",
|
|
19954
|
+
"PUBLIC_NARO_URL",
|
|
19955
|
+
"NARO_ANON_KEY",
|
|
19956
|
+
"NEXT_PUBLIC_NARO_ANON_KEY",
|
|
19957
|
+
"VITE_NARO_ANON_KEY",
|
|
19958
|
+
"PUBLIC_NARO_ANON_KEY"
|
|
19959
|
+
];
|
|
19715
19960
|
|
|
19716
19961
|
// ../../packages/sdk/src/insights.ts
|
|
19717
19962
|
var SPEED_INSIGHTS_METRICS = [
|
|
@@ -19745,6 +19990,7 @@ var BACKEND_STEPS = [
|
|
|
19745
19990
|
"worker",
|
|
19746
19991
|
"routes",
|
|
19747
19992
|
"migrate",
|
|
19993
|
+
"schema",
|
|
19748
19994
|
"health"
|
|
19749
19995
|
];
|
|
19750
19996
|
var STALLED_GRACE_MS = 2 * 6e4;
|
|
@@ -19808,9 +20054,17 @@ function buildLogEntry(row) {
|
|
|
19808
20054
|
}
|
|
19809
20055
|
|
|
19810
20056
|
// ../../packages/sdk/src/client.ts
|
|
20057
|
+
var DATA_PATH = /^\/api\/projects\/[^/?]+\/(?:database|backend(?!\/functions))(?:[/?]|$)/;
|
|
20058
|
+
function withDataEnvironment(path, environment) {
|
|
20059
|
+
if (environment === null || !DATA_PATH.test(path)) return path;
|
|
20060
|
+
return `${path}${path.includes("?") ? "&" : "?"}environment=${encodeURIComponent(environment)}`;
|
|
20061
|
+
}
|
|
19811
20062
|
var DEFAULT_REQUEST_TIMEOUT_MS = 45e3;
|
|
19812
|
-
var NaroClient = class {
|
|
20063
|
+
var NaroClient = class _NaroClient {
|
|
19813
20064
|
baseUrl;
|
|
20065
|
+
/** The environment whose data the data calls act on; null: production. */
|
|
20066
|
+
environment;
|
|
20067
|
+
options;
|
|
19814
20068
|
getAccessToken;
|
|
19815
20069
|
doFetch;
|
|
19816
20070
|
requestTimeoutMs;
|
|
@@ -19825,11 +20079,14 @@ var NaroClient = class {
|
|
|
19825
20079
|
backend;
|
|
19826
20080
|
storage;
|
|
19827
20081
|
realtime;
|
|
20082
|
+
functions;
|
|
19828
20083
|
insights;
|
|
19829
20084
|
analytics;
|
|
19830
20085
|
protection;
|
|
19831
20086
|
environments;
|
|
19832
20087
|
constructor(options) {
|
|
20088
|
+
this.options = options;
|
|
20089
|
+
this.environment = options.environment ?? null;
|
|
19833
20090
|
this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
|
|
19834
20091
|
this.getAccessToken = options.getAccessToken;
|
|
19835
20092
|
this.doFetch = options.fetch ?? fetch;
|
|
@@ -19845,25 +20102,42 @@ var NaroClient = class {
|
|
|
19845
20102
|
this.backend = new BackendApi(this);
|
|
19846
20103
|
this.storage = new StorageApi(this);
|
|
19847
20104
|
this.realtime = new RealtimeApi(this);
|
|
20105
|
+
this.functions = new FunctionsApi(this);
|
|
19848
20106
|
this.insights = new InsightsApi(this);
|
|
19849
20107
|
this.analytics = new AnalyticsApi(this);
|
|
19850
20108
|
this.protection = new ProtectionApi(this);
|
|
19851
20109
|
this.environments = new EnvironmentsApi(this);
|
|
19852
20110
|
}
|
|
20111
|
+
/**
|
|
20112
|
+
* The same client acting on `environment`'s data — or production's, for
|
|
20113
|
+
* null. Every other call is the same: the scope is only the
|
|
20114
|
+
* `?environment=` on database and backend paths.
|
|
20115
|
+
*/
|
|
20116
|
+
inEnvironment(environment) {
|
|
20117
|
+
if (environment === this.environment) return this;
|
|
20118
|
+
const options = { ...this.options };
|
|
20119
|
+
delete options.environment;
|
|
20120
|
+
if (environment !== null) options.environment = environment;
|
|
20121
|
+
return new _NaroClient(options);
|
|
20122
|
+
}
|
|
19853
20123
|
async request(path, options) {
|
|
19854
20124
|
const token = await this.getAccessToken();
|
|
19855
20125
|
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
19856
20126
|
let response;
|
|
19857
20127
|
try {
|
|
19858
|
-
response = await this.doFetch(
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
20128
|
+
response = await this.doFetch(
|
|
20129
|
+
`${this.baseUrl}${withDataEnvironment(path, this.environment)}`,
|
|
20130
|
+
{
|
|
20131
|
+
method: options.method ?? "GET",
|
|
20132
|
+
headers: {
|
|
20133
|
+
Authorization: `Bearer ${token}`,
|
|
20134
|
+
"X-Naro-Client": this.options.clientName ?? "next-deploy-sdk",
|
|
20135
|
+
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
20136
|
+
},
|
|
20137
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
20138
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
20139
|
+
}
|
|
20140
|
+
);
|
|
19867
20141
|
} catch (error51) {
|
|
19868
20142
|
if (error51 instanceof Error && (error51.name === "TimeoutError" || error51.name === "AbortError")) {
|
|
19869
20143
|
throw new NaroError(
|
|
@@ -19901,9 +20175,9 @@ var TeamsApi = class {
|
|
|
19901
20175
|
async usage(teamSlug, query = {}) {
|
|
19902
20176
|
const params = new URLSearchParams();
|
|
19903
20177
|
if (query.month !== void 0) params.set("month", query.month);
|
|
19904
|
-
const
|
|
20178
|
+
const text3 = params.toString();
|
|
19905
20179
|
return this.client.request(
|
|
19906
|
-
`/api/teams/${encodeURIComponent(teamSlug)}/usage${
|
|
20180
|
+
`/api/teams/${encodeURIComponent(teamSlug)}/usage${text3 ? `?${text3}` : ""}`,
|
|
19907
20181
|
{ context: "Read team usage" }
|
|
19908
20182
|
);
|
|
19909
20183
|
}
|
|
@@ -20061,17 +20335,23 @@ var DeploymentsApi = class {
|
|
|
20061
20335
|
* `PROMOTE_TARGET_UNSUPPORTED` before anything is promoted. The target is
|
|
20062
20336
|
* trimmed once, as the control plane trims it, so `" production"` is
|
|
20063
20337
|
* production and asks no preview.
|
|
20338
|
+
*
|
|
20339
|
+
* `check: true` asks the preview for production too, for a caller that
|
|
20340
|
+
* cannot show it first and returns it instead (MCP's `promote_deployment`):
|
|
20341
|
+
* a 405 there means a control plane from before previews, and production
|
|
20342
|
+
* is promoted unchecked as before. The preview asked is the result's
|
|
20343
|
+
* `check`, so its migration warnings reach the caller (spec §9.4).
|
|
20064
20344
|
*/
|
|
20065
20345
|
async promote(projectId, deploymentId, options = {}) {
|
|
20066
20346
|
const target2 = options.target?.trim();
|
|
20067
|
-
|
|
20347
|
+
const toEnvironment = target2 !== void 0 && target2 !== "production";
|
|
20348
|
+
let check2;
|
|
20349
|
+
if (toEnvironment || options.check === true) {
|
|
20068
20350
|
try {
|
|
20069
|
-
await this.promoteCheck(projectId, deploymentId, target2);
|
|
20351
|
+
check2 = await this.promoteCheck(projectId, deploymentId, target2);
|
|
20070
20352
|
} catch (error51) {
|
|
20071
|
-
if (isNaroError(error51)
|
|
20072
|
-
|
|
20073
|
-
}
|
|
20074
|
-
throw error51;
|
|
20353
|
+
if (!isNaroError(error51) || error51.status !== 405) throw error51;
|
|
20354
|
+
if (toEnvironment) throw promoteTargetUnsupported(error51);
|
|
20075
20355
|
}
|
|
20076
20356
|
}
|
|
20077
20357
|
const body = await this.client.request(this.promotePath(projectId, deploymentId), {
|
|
@@ -20081,7 +20361,8 @@ var DeploymentsApi = class {
|
|
|
20081
20361
|
});
|
|
20082
20362
|
return {
|
|
20083
20363
|
...deploymentInfo(body.deployment),
|
|
20084
|
-
mode: body.mode ?? "reupload"
|
|
20364
|
+
mode: body.mode ?? "reupload",
|
|
20365
|
+
...check2 === void 0 ? {} : { check: check2 }
|
|
20085
20366
|
};
|
|
20086
20367
|
}
|
|
20087
20368
|
/**
|
|
@@ -20121,7 +20402,7 @@ var DeploymentsApi = class {
|
|
|
20121
20402
|
async wait(deploymentId, options = {}) {
|
|
20122
20403
|
const intervalMs = options.intervalMs ?? 2500;
|
|
20123
20404
|
const timeoutMs = options.timeoutMs ?? 15 * 6e4;
|
|
20124
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((
|
|
20405
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
|
|
20125
20406
|
const startedAt = Date.now();
|
|
20126
20407
|
let lastStatus = null;
|
|
20127
20408
|
let lastSequence = -1;
|
|
@@ -20414,8 +20695,8 @@ function insightsQuery(options) {
|
|
|
20414
20695
|
if (options.environment !== void 0) params.set("env", options.environment);
|
|
20415
20696
|
if (options.route !== void 0) params.set("route", options.route);
|
|
20416
20697
|
appendFilters(params, SPEED_INSIGHTS_FILTER_KEYS, options.filters);
|
|
20417
|
-
const
|
|
20418
|
-
return
|
|
20698
|
+
const text3 = params.toString();
|
|
20699
|
+
return text3 ? `?${text3}` : "";
|
|
20419
20700
|
}
|
|
20420
20701
|
var InsightsApi = class {
|
|
20421
20702
|
constructor(client) {
|
|
@@ -20493,9 +20774,9 @@ var AnalyticsApi = class {
|
|
|
20493
20774
|
const params = new URLSearchParams();
|
|
20494
20775
|
if (options.range !== void 0) params.set("range", options.range);
|
|
20495
20776
|
appendFilters(params, ANALYTICS_FILTER_KEYS, options.filters);
|
|
20496
|
-
const
|
|
20777
|
+
const text3 = params.toString();
|
|
20497
20778
|
return this.client.request(
|
|
20498
|
-
`/api/projects/${encodeURIComponent(projectId)}/analytics${
|
|
20779
|
+
`/api/projects/${encodeURIComponent(projectId)}/analytics${text3 ? `?${text3}` : ""}`,
|
|
20499
20780
|
{ context: "Read Analytics" }
|
|
20500
20781
|
);
|
|
20501
20782
|
}
|
|
@@ -20582,6 +20863,19 @@ var EnvironmentsApi = class {
|
|
|
20582
20863
|
{ method: "DELETE", context: "Delete environment" }
|
|
20583
20864
|
);
|
|
20584
20865
|
}
|
|
20866
|
+
/**
|
|
20867
|
+
* Makes or continues the environment's data — its D1, Naro backend and
|
|
20868
|
+
* Storage bucket (spec §11.2): the run goes on after the answer, which
|
|
20869
|
+
* says `data.status` `provisioning`. A run that stopped (`error`) starts
|
|
20870
|
+
* again from where it stopped; `none` when the project uses neither.
|
|
20871
|
+
*/
|
|
20872
|
+
async provision(projectId, environmentId) {
|
|
20873
|
+
const body = await this.client.request(
|
|
20874
|
+
`${this.base(projectId)}/${encodeURIComponent(environmentId)}/provision`,
|
|
20875
|
+
{ method: "POST", context: "Provision environment" }
|
|
20876
|
+
);
|
|
20877
|
+
return body.environment;
|
|
20878
|
+
}
|
|
20585
20879
|
};
|
|
20586
20880
|
var EXPORT_FIRST_POLL_MS = 1e3;
|
|
20587
20881
|
var EXPORT_MAX_POLL_MS = 5e3;
|
|
@@ -20720,7 +21014,7 @@ var DbApi = class {
|
|
|
20720
21014
|
*/
|
|
20721
21015
|
async export(projectId, options = {}, wait = {}) {
|
|
20722
21016
|
const timeoutMs = wait.timeoutMs ?? EXPORT_TIMEOUT_MS;
|
|
20723
|
-
const sleep2 = wait.sleep ?? ((ms) => new Promise((
|
|
21017
|
+
const sleep2 = wait.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
|
|
20724
21018
|
const given = wait.pollIntervalMs;
|
|
20725
21019
|
const firstPollMs = given !== void 0 && Number.isFinite(given) && given > 0 ? given : EXPORT_FIRST_POLL_MS;
|
|
20726
21020
|
const maxPollMs = Math.max(EXPORT_MAX_POLL_MS, firstPollMs);
|
|
@@ -21615,6 +21909,207 @@ var RealtimeApi = class {
|
|
|
21615
21909
|
);
|
|
21616
21910
|
}
|
|
21617
21911
|
};
|
|
21912
|
+
var FunctionsApi = class {
|
|
21913
|
+
constructor(client) {
|
|
21914
|
+
this.client = client;
|
|
21915
|
+
}
|
|
21916
|
+
path(projectId, suffix = "") {
|
|
21917
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/functions${suffix}`;
|
|
21918
|
+
}
|
|
21919
|
+
/**
|
|
21920
|
+
* A segment no URL can carry is refused before a request, like
|
|
21921
|
+
* `StorageApi.bucketPath`: URL parsing folds `/`, `.` and `..` into the
|
|
21922
|
+
* path, an empty one is the collection, and `encodeURIComponent` throws on
|
|
21923
|
+
* a lone surrogate. The server checks the name's own rule.
|
|
21924
|
+
*/
|
|
21925
|
+
segment(value, what) {
|
|
21926
|
+
if (value === "" || value === "." || value === ".." || value.includes("/") || LONE_SURROGATE.test(value))
|
|
21927
|
+
throw new NaroError("INVALID_PATH", `The ${what} is not valid.`);
|
|
21928
|
+
return encodeURIComponent(value);
|
|
21929
|
+
}
|
|
21930
|
+
functionPath(projectId, name, suffix = "") {
|
|
21931
|
+
return this.path(
|
|
21932
|
+
projectId,
|
|
21933
|
+
`/${this.segment(name, "function name")}${suffix}`
|
|
21934
|
+
);
|
|
21935
|
+
}
|
|
21936
|
+
/** The project's functions, the plan's limits, and (3-3e) this month's usage. */
|
|
21937
|
+
async list(projectId) {
|
|
21938
|
+
return this.client.request(this.path(projectId), {
|
|
21939
|
+
context: "List functions"
|
|
21940
|
+
});
|
|
21941
|
+
}
|
|
21942
|
+
/** One function and its newest 20 versions. `FUNCTION_NOT_FOUND` (404). */
|
|
21943
|
+
async get(projectId, name) {
|
|
21944
|
+
return this.client.request(
|
|
21945
|
+
this.functionPath(projectId, name),
|
|
21946
|
+
{ context: "Read a function" }
|
|
21947
|
+
);
|
|
21948
|
+
}
|
|
21949
|
+
/**
|
|
21950
|
+
* A new version — the function too, the first time — and where to PUT its
|
|
21951
|
+
* bundle: `upload.url` with `upload.headers` (the checksum header is
|
|
21952
|
+
* signed). Refusals in the order they are checked: `BACKEND_DISABLED`,
|
|
21953
|
+
* `UNAUTHORIZED`, `NOT_FOUND`, `FORBIDDEN` (`deployment.create`),
|
|
21954
|
+
* `FUNCTIONS_DISABLED`, `FORBIDDEN` (a suspended project),
|
|
21955
|
+
* `VALIDATION_FAILED` (the name or the request), `BUNDLE_TOO_LARGE`,
|
|
21956
|
+
* `DATABASE_NOT_ENABLED`, `BACKEND_NOT_READY`, `BACKEND_OFF`,
|
|
21957
|
+
* `BACKEND_BUSY` (the project's Naro worker is not the current version and
|
|
21958
|
+
* could not be brought up to date just now), `FUNCTION_LIMIT` (a new name
|
|
21959
|
+
* over the plan's cap) and `FUNCTION_BUSY` (the function is being deleted).
|
|
21960
|
+
* It never turns the backend on. Two minutes, or `options.timeoutMs`
|
|
21961
|
+
* (naro-mcp's deploy_function shares two minutes across its steps).
|
|
21962
|
+
*/
|
|
21963
|
+
async createVersion(projectId, name, request2, options = {}) {
|
|
21964
|
+
return this.client.request(
|
|
21965
|
+
this.functionPath(projectId, name, "/versions"),
|
|
21966
|
+
{
|
|
21967
|
+
method: "POST",
|
|
21968
|
+
body: request2,
|
|
21969
|
+
context: "Create a function version",
|
|
21970
|
+
timeoutMs: options.timeoutMs ?? PROVISION_TIMEOUT_MS
|
|
21971
|
+
}
|
|
21972
|
+
);
|
|
21973
|
+
}
|
|
21974
|
+
/**
|
|
21975
|
+
* Uploads a version whose bundle is in R2 and makes it live. `versionId` is
|
|
21976
|
+
* `CreatedFunctionVersion.version.id`. The refusals before anything is
|
|
21977
|
+
* uploaded leave the function as it was: `FUNCTION_NOT_FOUND`,
|
|
21978
|
+
* `VERSION_NOT_FOUND`, `VERSION_NOT_DEPLOYABLE` (pruned),
|
|
21979
|
+
* `BACKEND_NOT_READY`, `BACKEND_OFF`, `FUNCTION_BUSY`, `BUNDLE_MISSING`,
|
|
21980
|
+
* `BUNDLE_MISMATCH`. Then `FUNCTION_DEPLOY_FAILED` (Cloudflare refused or
|
|
21981
|
+
* could not be reached; the version that was live still runs),
|
|
21982
|
+
* `FUNCTIONS_RATE_LIMITED` (503, `retry-after`),
|
|
21983
|
+
* `FUNCTION_DEPLOY_INCOMPLETE` (the code runs but the function list did
|
|
21984
|
+
* not take it, so callers may still get the previous `verifyJwt` setting
|
|
21985
|
+
* or 404 — deploy again) and `BACKEND_OUTDATED` (the project's
|
|
21986
|
+
* database predates the function list — `backend.enable` updates it).
|
|
21987
|
+
* Two minutes, or `options.timeoutMs`; a timeout here does not say
|
|
21988
|
+
* whether the version went live — `get` does.
|
|
21989
|
+
*/
|
|
21990
|
+
async deploy(projectId, name, versionId, options = {}) {
|
|
21991
|
+
const body = await this.client.request(
|
|
21992
|
+
this.functionPath(
|
|
21993
|
+
projectId,
|
|
21994
|
+
name,
|
|
21995
|
+
`/versions/${this.segment(versionId, "version id")}/deploy`
|
|
21996
|
+
),
|
|
21997
|
+
{
|
|
21998
|
+
method: "POST",
|
|
21999
|
+
context: "Deploy a function",
|
|
22000
|
+
timeoutMs: options.timeoutMs ?? PROVISION_TIMEOUT_MS
|
|
22001
|
+
}
|
|
22002
|
+
);
|
|
22003
|
+
return body.function;
|
|
22004
|
+
}
|
|
22005
|
+
/**
|
|
22006
|
+
* Puts a stored version live again: the one numbered `version`, which must
|
|
22007
|
+
* have been live before (`VERSION_NOT_FOUND` otherwise — put a version that
|
|
22008
|
+
* never went live up with `deploy`), or without it the deployed version
|
|
22009
|
+
* before the live one. The code comes back, not the old keys or secrets:
|
|
22010
|
+
* the bindings are the project's now. The refusals are `deploy`'s, and
|
|
22011
|
+
* `VERSION_NOT_FOUND` also says there is no earlier deployed version.
|
|
22012
|
+
* Two minutes.
|
|
22013
|
+
*/
|
|
22014
|
+
async rollback(projectId, name, version3) {
|
|
22015
|
+
const body = await this.client.request(
|
|
22016
|
+
this.functionPath(projectId, name, "/rollback"),
|
|
22017
|
+
{
|
|
22018
|
+
method: "POST",
|
|
22019
|
+
body: version3 === void 0 ? {} : { version: version3 },
|
|
22020
|
+
context: "Roll back a function",
|
|
22021
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22022
|
+
}
|
|
22023
|
+
);
|
|
22024
|
+
return body.function;
|
|
22025
|
+
}
|
|
22026
|
+
/**
|
|
22027
|
+
* `verifyJwt` without an upload, live within seconds; the next deploy sets
|
|
22028
|
+
* it again from the version it puts live. `FUNCTION_NOT_FOUND`,
|
|
22029
|
+
* `FUNCTION_BUSY`, `FUNCTION_UPDATE_FAILED` (502: the function list in the
|
|
22030
|
+
* project's database could not be written — nothing changed, send it again)
|
|
22031
|
+
* and `BACKEND_OUTDATED`.
|
|
22032
|
+
*/
|
|
22033
|
+
async update(projectId, name, patch) {
|
|
22034
|
+
const body = await this.client.request(
|
|
22035
|
+
this.functionPath(projectId, name),
|
|
22036
|
+
{ method: "PATCH", body: patch, context: "Update a function" }
|
|
22037
|
+
);
|
|
22038
|
+
return body.function;
|
|
22039
|
+
}
|
|
22040
|
+
/**
|
|
22041
|
+
* Its URL stops working within seconds; its stored versions are removed by
|
|
22042
|
+
* the control plane's sweep. `FUNCTION_NOT_FOUND`, `FUNCTION_BUSY`.
|
|
22043
|
+
*/
|
|
22044
|
+
async remove(projectId, name) {
|
|
22045
|
+
await this.client.request(
|
|
22046
|
+
this.functionPath(projectId, name),
|
|
22047
|
+
{
|
|
22048
|
+
method: "DELETE",
|
|
22049
|
+
context: "Delete a function"
|
|
22050
|
+
}
|
|
22051
|
+
);
|
|
22052
|
+
}
|
|
22053
|
+
/**
|
|
22054
|
+
* The function's newest 200 log lines, optionally of one level (`log`,
|
|
22055
|
+
* `info`, `warn`, `error` or `debug`; anything else is `400
|
|
22056
|
+
* VALIDATION_FAILED`). `LOGS_DISABLED` (503) while the control plane has no
|
|
22057
|
+
* log tail.
|
|
22058
|
+
*/
|
|
22059
|
+
async logs(projectId, name, options = {}) {
|
|
22060
|
+
const query = options.level ? `?${new URLSearchParams({ level: options.level }).toString()}` : "";
|
|
22061
|
+
const body = await this.client.request(
|
|
22062
|
+
this.functionPath(projectId, name, `/logs${query}`),
|
|
22063
|
+
{ context: "Read function logs" }
|
|
22064
|
+
);
|
|
22065
|
+
return body.entries;
|
|
22066
|
+
}
|
|
22067
|
+
/** The project's function secrets: names, digests and when — never a value. `env.read`. */
|
|
22068
|
+
async secrets(projectId, options = {}) {
|
|
22069
|
+
const body = await this.client.request(
|
|
22070
|
+
this.path(projectId, "-secrets"),
|
|
22071
|
+
{
|
|
22072
|
+
context: "List function secrets",
|
|
22073
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
22074
|
+
}
|
|
22075
|
+
);
|
|
22076
|
+
return body.secrets;
|
|
22077
|
+
}
|
|
22078
|
+
/**
|
|
22079
|
+
* Sets and unsets function secrets in one change (`env.write.production`;
|
|
22080
|
+
* a suspended project is refused). `VALIDATION_FAILED` names the first
|
|
22081
|
+
* problem — a name, a reserved name (`NARO_*`, `ASSETS`, `DB`) or
|
|
22082
|
+
* `__proto__`, a value over 5,120 bytes, more than 100 secrets. Live
|
|
22083
|
+
* functions carry the change once synced: `pending` says how many. The
|
|
22084
|
+
* control plane starts two at once and its five-minute sweep goes on with
|
|
22085
|
+
* the rest, 20 a run; `sync` brings them up now — call it until `pending`
|
|
22086
|
+
* is 0 to be sure, as a change racing another can leave a function only
|
|
22087
|
+
* `sync` finds (known-issues #322).
|
|
22088
|
+
*/
|
|
22089
|
+
async setSecrets(projectId, change) {
|
|
22090
|
+
return this.client.request(
|
|
22091
|
+
this.path(projectId, "-secrets"),
|
|
22092
|
+
{ method: "PUT", body: change, context: "Change function secrets" }
|
|
22093
|
+
);
|
|
22094
|
+
}
|
|
22095
|
+
/**
|
|
22096
|
+
* Uploads stale functions again — `limit` of them — and with `registry`
|
|
22097
|
+
* writes the function list in the project's database again. Call it
|
|
22098
|
+
* again while `pending` is above 0 and the call made progress.
|
|
22099
|
+
* `FUNCTIONS_RATE_LIMITED` (503) carries `details.retryAfter`. Two minutes.
|
|
22100
|
+
*/
|
|
22101
|
+
async sync(projectId, options = {}) {
|
|
22102
|
+
return this.client.request(
|
|
22103
|
+
this.path(projectId, "-sync"),
|
|
22104
|
+
{
|
|
22105
|
+
method: "POST",
|
|
22106
|
+
body: options,
|
|
22107
|
+
context: "Sync functions",
|
|
22108
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22109
|
+
}
|
|
22110
|
+
);
|
|
22111
|
+
}
|
|
22112
|
+
};
|
|
21618
22113
|
|
|
21619
22114
|
// ../../packages/sdk/src/database.ts
|
|
21620
22115
|
var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
|
|
@@ -21636,9 +22131,9 @@ function cellText(statement2, row, column2) {
|
|
|
21636
22131
|
var MAX_CELL_CHARS = 60;
|
|
21637
22132
|
function formatCell(value) {
|
|
21638
22133
|
if (value === null || value === void 0) return "NULL";
|
|
21639
|
-
const
|
|
21640
|
-
const
|
|
21641
|
-
return
|
|
22134
|
+
const text3 = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
22135
|
+
const oneLine2 = text3.replace(/\s+/g, " ");
|
|
22136
|
+
return oneLine2.length > MAX_CELL_CHARS ? `${oneLine2.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine2;
|
|
21642
22137
|
}
|
|
21643
22138
|
function renderRows(statement2) {
|
|
21644
22139
|
if (statement2.columns.length === 0) return [];
|
|
@@ -21669,10 +22164,10 @@ import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
|
21669
22164
|
import { join as join20 } from "path";
|
|
21670
22165
|
var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/dgm;
|
|
21671
22166
|
var QUOTES = /* @__PURE__ */ new Set(["'", '"', "`"]);
|
|
21672
|
-
function parseDotenvDetailed(
|
|
22167
|
+
function parseDotenvDetailed(text3) {
|
|
21673
22168
|
const vars = {};
|
|
21674
22169
|
const unclosedQuotes = [];
|
|
21675
|
-
const lines =
|
|
22170
|
+
const lines = text3.replace(/\r\n?/g, "\n");
|
|
21676
22171
|
let line = 1;
|
|
21677
22172
|
let counted = 0;
|
|
21678
22173
|
for (const match of lines.matchAll(LINE)) {
|
|
@@ -21692,8 +22187,8 @@ function parseDotenvDetailed(text2) {
|
|
|
21692
22187
|
}
|
|
21693
22188
|
return { vars, unclosedQuotes };
|
|
21694
22189
|
}
|
|
21695
|
-
function parseDotenv(
|
|
21696
|
-
return parseDotenvDetailed(
|
|
22190
|
+
function parseDotenv(text3) {
|
|
22191
|
+
return parseDotenvDetailed(text3).vars;
|
|
21697
22192
|
}
|
|
21698
22193
|
function readProjectEnv(dir) {
|
|
21699
22194
|
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
@@ -21824,14 +22319,14 @@ function scanEnvReferences(dir) {
|
|
|
21824
22319
|
const files = [];
|
|
21825
22320
|
collect(dir, files);
|
|
21826
22321
|
for (const file2 of files) {
|
|
21827
|
-
let
|
|
22322
|
+
let text3;
|
|
21828
22323
|
try {
|
|
21829
22324
|
if (statSync(file2).size > MAX_FILE_BYTES2) continue;
|
|
21830
|
-
|
|
22325
|
+
text3 = readFileSync3(file2, "utf8");
|
|
21831
22326
|
} catch {
|
|
21832
22327
|
continue;
|
|
21833
22328
|
}
|
|
21834
|
-
for (const match of
|
|
22329
|
+
for (const match of text3.matchAll(REFERENCE)) {
|
|
21835
22330
|
const key = match[1] ?? match[2];
|
|
21836
22331
|
if (!key || ignored(key)) continue;
|
|
21837
22332
|
found.set(key, "code");
|
|
@@ -22019,15 +22514,15 @@ var SYSTEM_TABLE_PREFIXES = [
|
|
|
22019
22514
|
"sqlite_"
|
|
22020
22515
|
];
|
|
22021
22516
|
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
22022
|
-
function asciiLower(
|
|
22023
|
-
return
|
|
22517
|
+
function asciiLower(text3) {
|
|
22518
|
+
return text3.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
22024
22519
|
}
|
|
22025
22520
|
function isSystemTable(name) {
|
|
22026
22521
|
const lower = asciiLower(name);
|
|
22027
22522
|
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
22028
22523
|
}
|
|
22029
|
-
function likeLiteral(
|
|
22030
|
-
return
|
|
22524
|
+
function likeLiteral(text3) {
|
|
22525
|
+
return text3.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
22031
22526
|
}
|
|
22032
22527
|
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
22033
22528
|
function systemTablePredicate(column2 = "name") {
|
|
@@ -22053,10 +22548,10 @@ function isSpace(char) {
|
|
|
22053
22548
|
function isWordChar(char) {
|
|
22054
22549
|
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
22055
22550
|
}
|
|
22056
|
-
function asciiUpper2(
|
|
22057
|
-
return
|
|
22551
|
+
function asciiUpper2(text3) {
|
|
22552
|
+
return text3.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22058
22553
|
}
|
|
22059
|
-
function
|
|
22554
|
+
function quoted2(sql, start, close, doubles) {
|
|
22060
22555
|
let value = "";
|
|
22061
22556
|
for (let index = start + 1; index < sql.length; index++) {
|
|
22062
22557
|
const char = sql.charAt(index);
|
|
@@ -22089,7 +22584,7 @@ function tokenize(sql) {
|
|
|
22089
22584
|
index = end + 2;
|
|
22090
22585
|
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22091
22586
|
const close = char === "[" ? "]" : char;
|
|
22092
|
-
const read =
|
|
22587
|
+
const read = quoted2(sql, index, close, char !== "[");
|
|
22093
22588
|
if (!read) return void 0;
|
|
22094
22589
|
tokens.push({
|
|
22095
22590
|
kind: char === "'" ? "string" : "name",
|
|
@@ -22192,16 +22687,16 @@ function declaredCollations(sql, columns) {
|
|
|
22192
22687
|
}
|
|
22193
22688
|
|
|
22194
22689
|
// ../../packages/data-core/src/unicode-controls.ts
|
|
22195
|
-
function hasControlCharacter(
|
|
22196
|
-
for (let i = 0; i <
|
|
22197
|
-
const code =
|
|
22690
|
+
function hasControlCharacter(text3) {
|
|
22691
|
+
for (let i = 0; i < text3.length; i += 1) {
|
|
22692
|
+
const code = text3.charCodeAt(i);
|
|
22198
22693
|
if (code <= 31 || code >= 127 && code <= 159) return true;
|
|
22199
22694
|
}
|
|
22200
22695
|
return false;
|
|
22201
22696
|
}
|
|
22202
|
-
function hasBidiControl(
|
|
22203
|
-
for (let i = 0; i <
|
|
22204
|
-
const code =
|
|
22697
|
+
function hasBidiControl(text3) {
|
|
22698
|
+
for (let i = 0; i < text3.length; i += 1) {
|
|
22699
|
+
const code = text3.charCodeAt(i);
|
|
22205
22700
|
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
22206
22701
|
return true;
|
|
22207
22702
|
}
|
|
@@ -22226,8 +22721,8 @@ var RESERVED = new Set(STORAGE_RESERVED_WORDS);
|
|
|
22226
22721
|
var ENCODER = new TextEncoder();
|
|
22227
22722
|
var LONE_SURROGATE2 = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
22228
22723
|
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
22229
|
-
function hasLoneSurrogate(
|
|
22230
|
-
return LONE_SURROGATE2.test(
|
|
22724
|
+
function hasLoneSurrogate(text3) {
|
|
22725
|
+
return LONE_SURROGATE2.test(text3);
|
|
22231
22726
|
}
|
|
22232
22727
|
function refuse(reason) {
|
|
22233
22728
|
return { ok: false, reason };
|
|
@@ -22269,9 +22764,9 @@ function parseCacheControl(header) {
|
|
|
22269
22764
|
return maxAge <= MAX_MAX_AGE ? { ok: true, maxAge } : { ok: false };
|
|
22270
22765
|
}
|
|
22271
22766
|
var MAX_NAME_IN_MESSAGE = 256;
|
|
22272
|
-
function storageName(
|
|
22273
|
-
if (
|
|
22274
|
-
const cut =
|
|
22767
|
+
function storageName(text3) {
|
|
22768
|
+
if (text3.length <= MAX_NAME_IN_MESSAGE) return text3;
|
|
22769
|
+
const cut = text3.slice(0, MAX_NAME_IN_MESSAGE);
|
|
22275
22770
|
const last = cut.charCodeAt(MAX_NAME_IN_MESSAGE - 1);
|
|
22276
22771
|
return last >= 55296 && last <= 56319 ? cut.slice(0, MAX_NAME_IN_MESSAGE - 1) : cut;
|
|
22277
22772
|
}
|
|
@@ -22416,15 +22911,15 @@ var CATALOG = {
|
|
|
22416
22911
|
policy,
|
|
22417
22912
|
recursion,
|
|
22418
22913
|
values,
|
|
22419
|
-
bytes,
|
|
22914
|
+
bytes: bytes2,
|
|
22420
22915
|
depth
|
|
22421
22916
|
}) => {
|
|
22422
22917
|
if (recursion)
|
|
22423
22918
|
return `Infinite recursion detected in policy for relation ${table}.`;
|
|
22424
22919
|
if (values)
|
|
22425
22920
|
return `The policies of ${table} bind more than ${values === true ? "50" : String(values)} values in one statement.`;
|
|
22426
|
-
if (
|
|
22427
|
-
return `The policies of ${table} write more than ${
|
|
22921
|
+
if (bytes2)
|
|
22922
|
+
return `The policies of ${table} write more than ${bytes2 === true ? "50000" : String(bytes2)} bytes of SQL into one statement.`;
|
|
22428
22923
|
if (depth)
|
|
22429
22924
|
return `The policies of ${table} nest an expression more than 100 deep into one statement.`;
|
|
22430
22925
|
if (policy !== void 0)
|
|
@@ -22625,11 +23120,11 @@ function isDigit(char) {
|
|
|
22625
23120
|
return char >= "0" && char <= "9";
|
|
22626
23121
|
}
|
|
22627
23122
|
var NUMBER = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
|
22628
|
-
function tokenize2(
|
|
23123
|
+
function tokenize2(text3) {
|
|
22629
23124
|
const tokens = [];
|
|
22630
23125
|
let index = 0;
|
|
22631
|
-
const at = (offset = 0) =>
|
|
22632
|
-
while (index <
|
|
23126
|
+
const at = (offset = 0) => text3.charAt(index + offset);
|
|
23127
|
+
while (index < text3.length) {
|
|
22633
23128
|
const char = at();
|
|
22634
23129
|
const position = index + 1;
|
|
22635
23130
|
if (char === "\0")
|
|
@@ -22639,12 +23134,12 @@ function tokenize2(text2) {
|
|
|
22639
23134
|
continue;
|
|
22640
23135
|
}
|
|
22641
23136
|
if (char === "-" && at(1) === "-") {
|
|
22642
|
-
while (index <
|
|
23137
|
+
while (index < text3.length && at() !== "\n" && at() !== "\r") index++;
|
|
22643
23138
|
continue;
|
|
22644
23139
|
}
|
|
22645
23140
|
if (char === "/" && at(1) === "*") {
|
|
22646
23141
|
let depth = 0;
|
|
22647
|
-
while (index <
|
|
23142
|
+
while (index < text3.length) {
|
|
22648
23143
|
if (at() === "/" && at(1) === "*") {
|
|
22649
23144
|
depth++;
|
|
22650
23145
|
index += 2;
|
|
@@ -22662,7 +23157,7 @@ function tokenize2(text2) {
|
|
|
22662
23157
|
let value = "";
|
|
22663
23158
|
index++;
|
|
22664
23159
|
for (; ; ) {
|
|
22665
|
-
if (index >=
|
|
23160
|
+
if (index >= text3.length)
|
|
22666
23161
|
throw new PolicySyntaxError("unterminated quoted string", position);
|
|
22667
23162
|
if (at() === "'") {
|
|
22668
23163
|
if (at(1) === "'") {
|
|
@@ -22683,7 +23178,7 @@ function tokenize2(text2) {
|
|
|
22683
23178
|
let value = "";
|
|
22684
23179
|
index++;
|
|
22685
23180
|
for (; ; ) {
|
|
22686
|
-
if (index >=
|
|
23181
|
+
if (index >= text3.length)
|
|
22687
23182
|
throw new PolicySyntaxError(
|
|
22688
23183
|
"unterminated quoted identifier",
|
|
22689
23184
|
position
|
|
@@ -22711,10 +23206,10 @@ function tokenize2(text2) {
|
|
|
22711
23206
|
position
|
|
22712
23207
|
);
|
|
22713
23208
|
if (isDigit(char) || char === "." && isDigit(at(1))) {
|
|
22714
|
-
const match = NUMBER.exec(
|
|
23209
|
+
const match = NUMBER.exec(text3.slice(index));
|
|
22715
23210
|
const value = match?.[0] ?? char;
|
|
22716
23211
|
index += value.length;
|
|
22717
|
-
if (index <
|
|
23212
|
+
if (index < text3.length && isIdentChar(at()))
|
|
22718
23213
|
throw new PolicySyntaxError(
|
|
22719
23214
|
`trailing junk after numeric literal ${value}`,
|
|
22720
23215
|
position
|
|
@@ -22724,9 +23219,9 @@ function tokenize2(text2) {
|
|
|
22724
23219
|
}
|
|
22725
23220
|
if (isIdentStart(char)) {
|
|
22726
23221
|
let end = index + 1;
|
|
22727
|
-
while (end <
|
|
22728
|
-
const value =
|
|
22729
|
-
if (
|
|
23222
|
+
while (end < text3.length && isIdentChar(text3.charAt(end))) end++;
|
|
23223
|
+
const value = text3.slice(index, end);
|
|
23224
|
+
if (text3.charAt(end) === "'")
|
|
22730
23225
|
throw new PolicySyntaxError(
|
|
22731
23226
|
`${value}'\u2026' strings are not supported: use a plain '\u2026' string`,
|
|
22732
23227
|
position
|
|
@@ -22736,7 +23231,7 @@ function tokenize2(text2) {
|
|
|
22736
23231
|
continue;
|
|
22737
23232
|
}
|
|
22738
23233
|
const operator = OPERATORS.find(
|
|
22739
|
-
(candidate) =>
|
|
23234
|
+
(candidate) => text3.startsWith(candidate, index)
|
|
22740
23235
|
);
|
|
22741
23236
|
if (!operator)
|
|
22742
23237
|
throw new PolicySyntaxError(
|
|
@@ -22750,7 +23245,7 @@ function tokenize2(text2) {
|
|
|
22750
23245
|
kind: "eof",
|
|
22751
23246
|
value: "",
|
|
22752
23247
|
quoted: false,
|
|
22753
|
-
position:
|
|
23248
|
+
position: text3.length + 1
|
|
22754
23249
|
});
|
|
22755
23250
|
return tokens;
|
|
22756
23251
|
}
|
|
@@ -23079,21 +23574,21 @@ function toBoundValue(value) {
|
|
|
23079
23574
|
if (typeof value === "string" || typeof value === "number") return value;
|
|
23080
23575
|
return JSON.stringify(value);
|
|
23081
23576
|
}
|
|
23082
|
-
function utf8ByteLength(
|
|
23083
|
-
let
|
|
23084
|
-
for (let index = 0; index <
|
|
23085
|
-
const unit =
|
|
23086
|
-
if (unit < 128)
|
|
23087
|
-
else if (unit < 2048)
|
|
23088
|
-
else if (unit >= 55296 && unit <= 56319 && index + 1 <
|
|
23089
|
-
const next =
|
|
23577
|
+
function utf8ByteLength(text3) {
|
|
23578
|
+
let bytes2 = 0;
|
|
23579
|
+
for (let index = 0; index < text3.length; index++) {
|
|
23580
|
+
const unit = text3.charCodeAt(index);
|
|
23581
|
+
if (unit < 128) bytes2 += 1;
|
|
23582
|
+
else if (unit < 2048) bytes2 += 2;
|
|
23583
|
+
else if (unit >= 55296 && unit <= 56319 && index + 1 < text3.length) {
|
|
23584
|
+
const next = text3.charCodeAt(index + 1);
|
|
23090
23585
|
if (next >= 56320 && next <= 57343) {
|
|
23091
|
-
|
|
23586
|
+
bytes2 += 4;
|
|
23092
23587
|
index++;
|
|
23093
|
-
} else
|
|
23094
|
-
} else
|
|
23588
|
+
} else bytes2 += 3;
|
|
23589
|
+
} else bytes2 += 3;
|
|
23095
23590
|
}
|
|
23096
|
-
return
|
|
23591
|
+
return bytes2;
|
|
23097
23592
|
}
|
|
23098
23593
|
|
|
23099
23594
|
// ../../packages/data-core/src/sql-fragments.ts
|
|
@@ -23187,9 +23682,9 @@ function isNumeric(item) {
|
|
|
23187
23682
|
const affinity2 = columnAffinity(item);
|
|
23188
23683
|
return affinity2 !== "TEXT" && affinity2 !== "BLOB";
|
|
23189
23684
|
}
|
|
23190
|
-
function refusesText(item,
|
|
23685
|
+
function refusesText(item, text3) {
|
|
23191
23686
|
const kind = numberClass(item.type);
|
|
23192
|
-
return kind === "number" || kind === "fallback" && storesAsNumber(
|
|
23687
|
+
return kind === "number" || kind === "fallback" && storesAsNumber(text3);
|
|
23193
23688
|
}
|
|
23194
23689
|
function readsAsIs(item, value) {
|
|
23195
23690
|
return typeof value === "string" && numberClass(item.type) === "fallback" && !storesAsNumber(value);
|
|
@@ -23215,15 +23710,15 @@ function asStored(item, sql) {
|
|
|
23215
23710
|
var LITERAL_DEFAULT = /^(?:null|true|false|[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|'(?:[^']|'')*')$/i;
|
|
23216
23711
|
function omittedSql(item) {
|
|
23217
23712
|
if (item.primaryKey > 0) return void 0;
|
|
23218
|
-
const
|
|
23219
|
-
if (
|
|
23220
|
-
if (!LITERAL_DEFAULT.test(
|
|
23221
|
-
const word = asciiLower(
|
|
23713
|
+
const text3 = item.defaultValue;
|
|
23714
|
+
if (text3 === null) return "NULL";
|
|
23715
|
+
if (!LITERAL_DEFAULT.test(text3)) return void 0;
|
|
23716
|
+
const word = asciiLower(text3);
|
|
23222
23717
|
if (word === "null") return "NULL";
|
|
23223
23718
|
if (word === "true" || word === "false")
|
|
23224
23719
|
return asStored(item, word === "true" ? "1" : "0");
|
|
23225
|
-
if (
|
|
23226
|
-
return asStored(item,
|
|
23720
|
+
if (text3.startsWith("'") && isNumeric(item)) return void 0;
|
|
23721
|
+
return asStored(item, text3);
|
|
23227
23722
|
}
|
|
23228
23723
|
var DEFAULT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
23229
23724
|
"true",
|
|
@@ -23234,27 +23729,27 @@ var DEFAULT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
23234
23729
|
"current_timestamp"
|
|
23235
23730
|
]);
|
|
23236
23731
|
var BARE_WORD = /^[A-Za-z_\u0080-][A-Za-z0-9_$\u0080-]*$/;
|
|
23237
|
-
function quotedName(
|
|
23238
|
-
const double = /^"((?:[^"]|"")*)"$/.exec(
|
|
23732
|
+
function quotedName(text3) {
|
|
23733
|
+
const double = /^"((?:[^"]|"")*)"$/.exec(text3);
|
|
23239
23734
|
if (double) return (double[1] ?? "").replaceAll('""', '"');
|
|
23240
|
-
const tick = /^`((?:[^`]|``)*)`$/.exec(
|
|
23735
|
+
const tick = /^`((?:[^`]|``)*)`$/.exec(text3);
|
|
23241
23736
|
if (tick) return (tick[1] ?? "").replaceAll("``", "`");
|
|
23242
|
-
const bracket = /^\[([^\]]*)\]$/.exec(
|
|
23737
|
+
const bracket = /^\[([^\]]*)\]$/.exec(text3);
|
|
23243
23738
|
return bracket ? bracket[1] ?? "" : void 0;
|
|
23244
23739
|
}
|
|
23245
23740
|
function stringDefault(item) {
|
|
23246
|
-
let
|
|
23247
|
-
if (
|
|
23248
|
-
while (
|
|
23249
|
-
|
|
23250
|
-
if (/^\+\s*(?:'|x')/i.test(
|
|
23251
|
-
if (/^'(?:[^']|'')*'$/.test(
|
|
23252
|
-
return { kind: "text", text:
|
|
23253
|
-
if (/^x'[0-9a-f]*'$/i.test(
|
|
23254
|
-
const name = quotedName(
|
|
23741
|
+
let text3 = item.defaultValue?.trim();
|
|
23742
|
+
if (text3 === void 0) return void 0;
|
|
23743
|
+
while (text3.startsWith("(") && text3.endsWith(")"))
|
|
23744
|
+
text3 = text3.slice(1, -1).trim();
|
|
23745
|
+
if (/^\+\s*(?:'|x')/i.test(text3)) text3 = text3.slice(1).trim();
|
|
23746
|
+
if (/^'(?:[^']|'')*'$/.test(text3))
|
|
23747
|
+
return { kind: "text", text: text3.slice(1, -1).replaceAll("''", "'") };
|
|
23748
|
+
if (/^x'[0-9a-f]*'$/i.test(text3)) return { kind: "blob" };
|
|
23749
|
+
const name = quotedName(text3);
|
|
23255
23750
|
if (name !== void 0) return { kind: "text", text: name };
|
|
23256
|
-
if (BARE_WORD.test(
|
|
23257
|
-
return { kind: "text", text:
|
|
23751
|
+
if (BARE_WORD.test(text3) && !DEFAULT_KEYWORDS.has(asciiLower(text3)))
|
|
23752
|
+
return { kind: "text", text: text3 };
|
|
23258
23753
|
return void 0;
|
|
23259
23754
|
}
|
|
23260
23755
|
var DEFAULT_CHECK_NONCE = "0";
|
|
@@ -23386,25 +23881,25 @@ function nested(state, token, read) {
|
|
|
23386
23881
|
}
|
|
23387
23882
|
}
|
|
23388
23883
|
function truthProblem(operand) {
|
|
23389
|
-
const
|
|
23884
|
+
const text3 = "is text, where 'true' reads as false";
|
|
23390
23885
|
switch (operand.kind) {
|
|
23391
23886
|
case "column":
|
|
23392
23887
|
return void 0;
|
|
23393
23888
|
case "literal":
|
|
23394
|
-
return typeof operand.value === "string" ? `a '\u2026' string ${
|
|
23889
|
+
return typeof operand.value === "string" ? `a '\u2026' string ${text3}: write TRUE or FALSE, or compare the string with =` : void 0;
|
|
23395
23890
|
case "jwt": {
|
|
23396
23891
|
const claim = printValue(operand);
|
|
23397
|
-
return `${claim} ${
|
|
23892
|
+
return `${claim} ${text3}: compare it with text, ${claim} = 'true'`;
|
|
23398
23893
|
}
|
|
23399
23894
|
case "auth":
|
|
23400
23895
|
return operand.fn === "uid" ? "auth.uid() is text: compare it, auth.uid() = user_id, or test auth.uid() IS NOT NULL" : "auth.role() is text: compare it, auth.role() = 'authenticated'";
|
|
23401
23896
|
case "cast":
|
|
23402
|
-
return `a ::text cast ${
|
|
23897
|
+
return `a ::text cast ${text3}: compare it with text, (\u2026)::text = 'true'`;
|
|
23403
23898
|
case "func":
|
|
23404
23899
|
switch (operand.name) {
|
|
23405
23900
|
case "lower":
|
|
23406
23901
|
case "upper":
|
|
23407
|
-
return `${operand.name}(\u2026) ${
|
|
23902
|
+
return `${operand.name}(\u2026) ${text3}: compare it with text, ${operand.name}(\u2026) = 'true'`;
|
|
23408
23903
|
case "coalesce":
|
|
23409
23904
|
return "coalesce(\u2026) is not a boolean: compare it, coalesce(\u2026) = 'true'";
|
|
23410
23905
|
case "length":
|
|
@@ -23414,10 +23909,10 @@ function truthProblem(operand) {
|
|
|
23414
23909
|
}
|
|
23415
23910
|
case "storage": {
|
|
23416
23911
|
const call = operand.fn === "foldername" ? "(storage.foldername(\u2026))[n]" : `storage.${operand.fn}(\u2026)`;
|
|
23417
|
-
return `${call} ${
|
|
23912
|
+
return `${call} ${text3}: compare it with text, ${call} = 'true'`;
|
|
23418
23913
|
}
|
|
23419
23914
|
case "realtime":
|
|
23420
|
-
return `realtime.topic() ${
|
|
23915
|
+
return `realtime.topic() ${text3}: compare it with text, realtime.topic() = 'room:1'`;
|
|
23421
23916
|
}
|
|
23422
23917
|
}
|
|
23423
23918
|
function truthTestProblem(operand) {
|
|
@@ -23471,9 +23966,9 @@ function booleanComparisonProblem(expr) {
|
|
|
23471
23966
|
const met = [...operandValues(left), ...operandValues(right)];
|
|
23472
23967
|
if (!met.some(isBooleanConstant)) continue;
|
|
23473
23968
|
const texts = met.filter(isTextValue);
|
|
23474
|
-
const
|
|
23475
|
-
if (
|
|
23476
|
-
const problem =
|
|
23969
|
+
const text3 = texts.find((value) => value.kind !== "literal") ?? texts[0];
|
|
23970
|
+
if (text3 === void 0) continue;
|
|
23971
|
+
const problem = text3.kind === "literal" ? "a '\u2026' string is text, where 'true' reads as false: write TRUE or FALSE on both sides, or a '\u2026' string on both" : truthProblem(text3);
|
|
23477
23972
|
return `a comparison with TRUE or FALSE compares a boolean, and ${String(problem)}`;
|
|
23478
23973
|
}
|
|
23479
23974
|
return void 0;
|
|
@@ -23551,17 +24046,17 @@ function readPredicate(state) {
|
|
|
23551
24046
|
}
|
|
23552
24047
|
const left = readValue(state);
|
|
23553
24048
|
const next = stream.peek();
|
|
23554
|
-
const
|
|
24049
|
+
const compare3 = ["=", "<>", "!=", "<", "<=", ">", ">="].find(
|
|
23555
24050
|
(op) => isOperator(next, op)
|
|
23556
24051
|
);
|
|
23557
|
-
if (
|
|
24052
|
+
if (compare3) {
|
|
23558
24053
|
stream.next();
|
|
23559
24054
|
count(state, next);
|
|
23560
24055
|
const right = readValue(state);
|
|
23561
24056
|
return comparable(
|
|
23562
24057
|
{
|
|
23563
24058
|
kind: "compare",
|
|
23564
|
-
op:
|
|
24059
|
+
op: compare3 === "!=" ? "<>" : compare3,
|
|
23565
24060
|
left: requireValue(left, token),
|
|
23566
24061
|
right: requireValue(right, next)
|
|
23567
24062
|
},
|
|
@@ -23663,8 +24158,8 @@ function policyPatternProblem(pattern, ci) {
|
|
|
23663
24158
|
if (!patternTooLong(pattern, ci)) return void 0;
|
|
23664
24159
|
const chars = [...pattern];
|
|
23665
24160
|
const shown = `${commentText(chars.slice(0, PATTERN_SHOWN).join(""))}${chars.length > PATTERN_SHOWN ? "\u2026" : ""}`;
|
|
23666
|
-
const
|
|
23667
|
-
return `${ci ? "an ILIKE" : "a LIKE"} pattern may be at most ${String(MAX_POLICY_PATTERN_LENGTH)} bytes of UTF-8, D1's limit, and '${shown}' is ${String(
|
|
24161
|
+
const bytes2 = utf8ByteLength(boundPattern(pattern, ci));
|
|
24162
|
+
return `${ci ? "an ILIKE" : "a LIKE"} pattern may be at most ${String(MAX_POLICY_PATTERN_LENGTH)} bytes of UTF-8, D1's limit, and '${shown}' is ${String(bytes2)}${ci ? "" : " (LIKE counts each *, ? and [ as 3 bytes)"}`;
|
|
23668
24163
|
}
|
|
23669
24164
|
function checkPattern(token, ci) {
|
|
23670
24165
|
const pattern = token.value;
|
|
@@ -24179,23 +24674,23 @@ function readCondition(stream) {
|
|
|
24179
24674
|
const token = stream.peek();
|
|
24180
24675
|
const state = newState(stream);
|
|
24181
24676
|
const expr = readOr(state);
|
|
24182
|
-
const
|
|
24183
|
-
if (
|
|
24184
|
-
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM,
|
|
24677
|
+
const bare2 = state.foldernames.find(({ node }) => node.index === null);
|
|
24678
|
+
if (bare2)
|
|
24679
|
+
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM, bare2.token.position);
|
|
24185
24680
|
return requireCondition(expr, token);
|
|
24186
24681
|
}
|
|
24187
24682
|
function checkExpressionSize(expr, position) {
|
|
24188
|
-
const
|
|
24189
|
-
if (utf8ByteLength(
|
|
24683
|
+
const text3 = printExpr(expr);
|
|
24684
|
+
if (utf8ByteLength(text3) > MAX_EXPRESSION_BYTES)
|
|
24190
24685
|
throw new PolicySyntaxError(
|
|
24191
24686
|
`the expression is longer than ${String(MAX_EXPRESSION_BYTES)} bytes`,
|
|
24192
24687
|
position
|
|
24193
24688
|
);
|
|
24194
|
-
return
|
|
24689
|
+
return text3;
|
|
24195
24690
|
}
|
|
24196
|
-
function parseExpression(
|
|
24691
|
+
function parseExpression(text3) {
|
|
24197
24692
|
try {
|
|
24198
|
-
const stream = new TokenStream(tokenize2(
|
|
24693
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
24199
24694
|
if (stream.peek().kind === "eof")
|
|
24200
24695
|
throw new PolicySyntaxError("the expression is empty", 1);
|
|
24201
24696
|
const expr = readCondition(stream);
|
|
@@ -24210,22 +24705,22 @@ function parseExpression(text2) {
|
|
|
24210
24705
|
function quoteIdent(name) {
|
|
24211
24706
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !SPECIAL_WORDS.has(asciiLower(name)) ? name : `"${name.replaceAll('"', '""')}"`;
|
|
24212
24707
|
}
|
|
24213
|
-
function commentText(
|
|
24708
|
+
function commentText(text3) {
|
|
24214
24709
|
let out = "";
|
|
24215
|
-
for (const char of
|
|
24710
|
+
for (const char of text3) {
|
|
24216
24711
|
const code = char.charCodeAt(0);
|
|
24217
24712
|
const control = code < 32 || code >= 127 && code <= 159 || code === 8232 || code === 8233;
|
|
24218
24713
|
out += control ? `\\u${code.toString(16).padStart(4, "0")}` : char;
|
|
24219
24714
|
}
|
|
24220
24715
|
return out;
|
|
24221
24716
|
}
|
|
24222
|
-
function quoteString(
|
|
24223
|
-
return `'${
|
|
24717
|
+
function quoteString(text3) {
|
|
24718
|
+
return `'${text3.replaceAll("'", "''")}'`;
|
|
24224
24719
|
}
|
|
24225
24720
|
function printNumber(value) {
|
|
24226
24721
|
if (Object.is(value, -0)) return "-0.0";
|
|
24227
|
-
const
|
|
24228
|
-
return Number.isSafeInteger(value) || /[.e]/.test(
|
|
24722
|
+
const text3 = String(value);
|
|
24723
|
+
return Number.isSafeInteger(value) || /[.e]/.test(text3) ? text3 : `${text3}.0`;
|
|
24229
24724
|
}
|
|
24230
24725
|
function printLiteral(value) {
|
|
24231
24726
|
if (value === null) return "NULL";
|
|
@@ -24479,8 +24974,8 @@ function ownerExpression(column2) {
|
|
|
24479
24974
|
});
|
|
24480
24975
|
}
|
|
24481
24976
|
function ownerClauses(column2, command) {
|
|
24482
|
-
const
|
|
24483
|
-
return command === "insert" ? { using: null, check:
|
|
24977
|
+
const text3 = ownerExpression(column2);
|
|
24978
|
+
return command === "insert" ? { using: null, check: text3 } : { using: text3, check: null };
|
|
24484
24979
|
}
|
|
24485
24980
|
var NAMED_POLICY_TABLE = "_naro_policies_v2";
|
|
24486
24981
|
var NAMED_POLICY_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ${NAMED_POLICY_TABLE} (table_name TEXT NOT NULL, name TEXT NOT NULL, kind TEXT NOT NULL DEFAULT 'permissive', command TEXT NOT NULL DEFAULT 'all', roles TEXT NOT NULL DEFAULT 'public', using_expr TEXT, check_expr TEXT, owner_column TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), PRIMARY KEY (table_name, name))`;
|
|
@@ -24589,14 +25084,14 @@ function policiesOn(policies, table) {
|
|
|
24589
25084
|
const folded = asciiLower(table);
|
|
24590
25085
|
return policies.filter((policy) => asciiLower(policy.table) === folded);
|
|
24591
25086
|
}
|
|
24592
|
-
function foldPolicyName(name,
|
|
24593
|
-
return
|
|
25087
|
+
function foldPolicyName(name, quoted3) {
|
|
25088
|
+
return quoted3 ? name : asciiLower(name);
|
|
24594
25089
|
}
|
|
24595
|
-
function sqlStringLiteral(
|
|
24596
|
-
return
|
|
25090
|
+
function sqlStringLiteral(text3) {
|
|
25091
|
+
return text3 === null ? "NULL" : quoteString(text3);
|
|
24597
25092
|
}
|
|
24598
|
-
function policySqlTooLong(
|
|
24599
|
-
return utf8ByteLength(
|
|
25093
|
+
function policySqlTooLong(text3) {
|
|
25094
|
+
return utf8ByteLength(text3) > MAX_POLICY_SQL_BYTES;
|
|
24600
25095
|
}
|
|
24601
25096
|
|
|
24602
25097
|
// ../../packages/data-core/src/query.ts
|
|
@@ -24686,11 +25181,11 @@ function readValue2(value, path, auth = false) {
|
|
|
24686
25181
|
);
|
|
24687
25182
|
return value;
|
|
24688
25183
|
}
|
|
24689
|
-
function longerThan(
|
|
24690
|
-
if (
|
|
24691
|
-
if (
|
|
25184
|
+
function longerThan(text3, max) {
|
|
25185
|
+
if (text3.length <= max) return false;
|
|
25186
|
+
if (text3.length > max * 2) return true;
|
|
24692
25187
|
let count2 = 0;
|
|
24693
|
-
for (const _ of
|
|
25188
|
+
for (const _ of text3) if (++count2 > max) return true;
|
|
24694
25189
|
return false;
|
|
24695
25190
|
}
|
|
24696
25191
|
function endsWithLoneEscape(pattern) {
|
|
@@ -24847,8 +25342,8 @@ function parseCondition(action, value) {
|
|
|
24847
25342
|
}
|
|
24848
25343
|
function conditionsTooLarge(texts) {
|
|
24849
25344
|
let total = 0;
|
|
24850
|
-
for (const
|
|
24851
|
-
if (typeof
|
|
25345
|
+
for (const text3 of texts)
|
|
25346
|
+
if (typeof text3 === "string") total += utf8ByteLength(text3);
|
|
24852
25347
|
return total > MAX_CONDITION_BYTES;
|
|
24853
25348
|
}
|
|
24854
25349
|
function isInvalidCondition(value) {
|
|
@@ -24934,14 +25429,14 @@ var POLICY_SQL = {
|
|
|
24934
25429
|
*/
|
|
24935
25430
|
insertConverted: `INSERT OR IGNORE INTO _naro_policies_v2 (table_name, name, kind, command, roles, using_expr, check_expr, owner_column) SELECT ?, ?, ?, ?, ?, ?, ?, ? WHERE EXISTS (SELECT 1 FROM _naro_policies WHERE ${ROW_AS_READ})`
|
|
24936
25431
|
};
|
|
24937
|
-
function readStoredCondition(action,
|
|
24938
|
-
if (
|
|
24939
|
-
if (typeof
|
|
25432
|
+
function readStoredCondition(action, text3, tooLarge) {
|
|
25433
|
+
if (text3 === void 0 || text3 === null) return "is missing";
|
|
25434
|
+
if (typeof text3 !== "string") return "is not JSON";
|
|
24940
25435
|
if (tooLarge)
|
|
24941
25436
|
return `the conditions of the policy are larger than ${String(MAX_CONDITION_BYTES)} bytes`;
|
|
24942
25437
|
let value;
|
|
24943
25438
|
try {
|
|
24944
|
-
value = JSON.parse(
|
|
25439
|
+
value = JSON.parse(text3);
|
|
24945
25440
|
} catch {
|
|
24946
25441
|
return "is not JSON";
|
|
24947
25442
|
}
|
|
@@ -24956,7 +25451,7 @@ function policyFromRow(row, log2) {
|
|
|
24956
25451
|
(action) => row[`${action}_rule`] === CONDITION_RULE ? row[`${action}_condition`] : void 0
|
|
24957
25452
|
);
|
|
24958
25453
|
const tooLarge = conditionsTooLarge(
|
|
24959
|
-
texts.filter((
|
|
25454
|
+
texts.filter((text3) => typeof text3 === "string")
|
|
24960
25455
|
);
|
|
24961
25456
|
const read = POLICY_ACTIONS2.map(
|
|
24962
25457
|
(action, index) => {
|
|
@@ -26134,13 +26629,13 @@ function boundBytes(value) {
|
|
|
26134
26629
|
return typeof value === "string" ? utf8ByteLength(value) : 8;
|
|
26135
26630
|
}
|
|
26136
26631
|
function captureGuard(table, where, params, nonce, set2) {
|
|
26137
|
-
const
|
|
26632
|
+
const bytes2 = balancedSum(
|
|
26138
26633
|
table.columns.map(
|
|
26139
26634
|
(item) => set2.has(item.name) ? String(boundBytes(set2.get(item.name) ?? null)) : `coalesce(length(CAST(${qualifiedColumn(table.name, item.name)} AS BLOB)), 8)`
|
|
26140
26635
|
)
|
|
26141
26636
|
);
|
|
26142
26637
|
return statement(
|
|
26143
|
-
`SELECT CASE WHEN n > ${String(MAX_CAPTURE_ROWS)} OR b > ${String(MAX_CAPTURE_BYTES)} THEN ${captureMarker(nonce)} ELSE 1 END AS ok FROM (SELECT count(*) AS n, coalesce(sum(r), 0) AS b FROM (SELECT ${
|
|
26638
|
+
`SELECT CASE WHEN n > ${String(MAX_CAPTURE_ROWS)} OR b > ${String(MAX_CAPTURE_BYTES)} THEN ${captureMarker(nonce)} ELSE 1 END AS ok FROM (SELECT count(*) AS n, coalesce(sum(r), 0) AS b FROM (SELECT ${bytes2} AS r FROM ${quoteIdentifier(table.name)}${where} LIMIT ${String(MAX_CAPTURE_ROWS + 1)}))`,
|
|
26144
26639
|
params
|
|
26145
26640
|
);
|
|
26146
26641
|
}
|
|
@@ -26795,18 +27290,18 @@ var Rls = class {
|
|
|
26795
27290
|
}),
|
|
26796
27291
|
`they bind ${String(count2)}`
|
|
26797
27292
|
);
|
|
26798
|
-
const
|
|
27293
|
+
const bytes2 = parts.reduce(
|
|
26799
27294
|
(sum, part) => sum + utf8ByteLength(part.sql),
|
|
26800
27295
|
0
|
|
26801
27296
|
);
|
|
26802
|
-
if (
|
|
27297
|
+
if (bytes2 > MAX_POLICY_STATEMENT_BYTES)
|
|
26803
27298
|
fail(
|
|
26804
27299
|
dataError("POLICY_INVALID", {
|
|
26805
27300
|
action: "select",
|
|
26806
27301
|
table: this.policyTable,
|
|
26807
27302
|
bytes: true
|
|
26808
27303
|
}),
|
|
26809
|
-
`they write ${String(
|
|
27304
|
+
`they write ${String(bytes2)}`
|
|
26810
27305
|
);
|
|
26811
27306
|
}
|
|
26812
27307
|
};
|
|
@@ -27495,11 +27990,11 @@ function tokenize3(sql) {
|
|
|
27495
27990
|
at = closing("'") + 1;
|
|
27496
27991
|
tokens.push({ kind: "literal" });
|
|
27497
27992
|
} else if (isDigit2(code) || code === 46 && isDigit2(next)) {
|
|
27498
|
-
const
|
|
27993
|
+
const text3 = NUMBER2.exec(sql.slice(at, at + 64))?.[0] || sql[at] || "";
|
|
27499
27994
|
tokens.push(
|
|
27500
|
-
/^[0-9]+$/.test(
|
|
27995
|
+
/^[0-9]+$/.test(text3) ? { kind: "literal", integer: Number(text3) } : { kind: "literal" }
|
|
27501
27996
|
);
|
|
27502
|
-
at +=
|
|
27997
|
+
at += text3.length;
|
|
27503
27998
|
} else if (code === 63 || code === 58 || code === 64 || code === 36) {
|
|
27504
27999
|
at++;
|
|
27505
28000
|
while (at < length && isWordPart(sql.charCodeAt(at))) at++;
|
|
@@ -27507,8 +28002,8 @@ function tokenize3(sql) {
|
|
|
27507
28002
|
} else if (isWordStart(code)) {
|
|
27508
28003
|
const start = at;
|
|
27509
28004
|
while (at < length && isWordPart(sql.charCodeAt(at))) at++;
|
|
27510
|
-
const
|
|
27511
|
-
tokens.push({ kind: "word", text:
|
|
28005
|
+
const text3 = sql.slice(start, at);
|
|
28006
|
+
tokens.push({ kind: "word", text: text3, upper: text3.toUpperCase() });
|
|
27512
28007
|
} else if (sql.startsWith("->>", at)) {
|
|
27513
28008
|
tokens.push({ kind: "op", text: "->>" });
|
|
27514
28009
|
at += 3;
|
|
@@ -28218,31 +28713,31 @@ function isWordStart2(char) {
|
|
|
28218
28713
|
function isWordPart2(char) {
|
|
28219
28714
|
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28220
28715
|
}
|
|
28221
|
-
function skipBlanks(
|
|
28716
|
+
function skipBlanks(text3, from) {
|
|
28222
28717
|
let index = from;
|
|
28223
28718
|
for (; ; ) {
|
|
28224
|
-
const char =
|
|
28719
|
+
const char = text3.charAt(index);
|
|
28225
28720
|
if (isBlankStart(char)) {
|
|
28226
28721
|
index++;
|
|
28227
|
-
while (index <
|
|
28228
|
-
} else if (
|
|
28229
|
-
else if (char === "-" &&
|
|
28230
|
-
const end =
|
|
28231
|
-
index = end < 0 ?
|
|
28232
|
-
} else if (char === "/" &&
|
|
28233
|
-
const end =
|
|
28234
|
-
index = end < 0 ?
|
|
28722
|
+
while (index < text3.length && isBlank(text3.charAt(index))) index++;
|
|
28723
|
+
} else if (text3.charCodeAt(index) === BOM2) index++;
|
|
28724
|
+
else if (char === "-" && text3.charAt(index + 1) === "-") {
|
|
28725
|
+
const end = text3.indexOf("\n", index + 2);
|
|
28726
|
+
index = end < 0 ? text3.length : end;
|
|
28727
|
+
} else if (char === "/" && text3.charAt(index + 1) === "*") {
|
|
28728
|
+
const end = text3.indexOf("*/", index + 2);
|
|
28729
|
+
index = end < 0 ? text3.length : end + 2;
|
|
28235
28730
|
} else return index;
|
|
28236
28731
|
}
|
|
28237
28732
|
}
|
|
28238
|
-
function readQuoted(
|
|
28733
|
+
function readQuoted(text3, start, delimiter) {
|
|
28239
28734
|
let value = "";
|
|
28240
28735
|
let index = start + 1;
|
|
28241
28736
|
for (; ; ) {
|
|
28242
|
-
const close =
|
|
28737
|
+
const close = text3.indexOf(delimiter, index);
|
|
28243
28738
|
if (close < 0) return void 0;
|
|
28244
|
-
value +=
|
|
28245
|
-
if (
|
|
28739
|
+
value += text3.slice(index, close);
|
|
28740
|
+
if (text3.charAt(close + 1) === delimiter) {
|
|
28246
28741
|
value += delimiter;
|
|
28247
28742
|
index = close + 2;
|
|
28248
28743
|
continue;
|
|
@@ -28250,31 +28745,31 @@ function readQuoted(text2, start, delimiter) {
|
|
|
28250
28745
|
return { text: value, end: close + 1 };
|
|
28251
28746
|
}
|
|
28252
28747
|
}
|
|
28253
|
-
function headTokens(
|
|
28748
|
+
function headTokens(text3) {
|
|
28254
28749
|
const tokens = [];
|
|
28255
28750
|
let index = 0;
|
|
28256
28751
|
while (tokens.length < HEAD_TOKENS) {
|
|
28257
|
-
index = skipBlanks(
|
|
28258
|
-
const char =
|
|
28259
|
-
if (index >=
|
|
28752
|
+
index = skipBlanks(text3, index);
|
|
28753
|
+
const char = text3.charAt(index);
|
|
28754
|
+
if (index >= text3.length) break;
|
|
28260
28755
|
if (char === '"' || char === "`" || char === "'") {
|
|
28261
|
-
const
|
|
28262
|
-
if (!
|
|
28263
|
-
tokens.push({ kind: "name", text:
|
|
28264
|
-
index =
|
|
28756
|
+
const quoted3 = readQuoted(text3, index, char);
|
|
28757
|
+
if (!quoted3) break;
|
|
28758
|
+
tokens.push({ kind: "name", text: quoted3.text });
|
|
28759
|
+
index = quoted3.end;
|
|
28265
28760
|
} else if (char === "[") {
|
|
28266
|
-
const close =
|
|
28761
|
+
const close = text3.indexOf("]", index + 1);
|
|
28267
28762
|
if (close < 0) break;
|
|
28268
|
-
tokens.push({ kind: "name", text:
|
|
28763
|
+
tokens.push({ kind: "name", text: text3.slice(index + 1, close) });
|
|
28269
28764
|
index = close + 1;
|
|
28270
|
-
} else if ((char === "x" || char === "X") &&
|
|
28765
|
+
} else if ((char === "x" || char === "X") && text3.charAt(index + 1) === "'") {
|
|
28271
28766
|
break;
|
|
28272
28767
|
} else if (isWordStart2(char)) {
|
|
28273
28768
|
let end = index + 1;
|
|
28274
|
-
while (end <
|
|
28275
|
-
tokens.push({ kind: "word", text:
|
|
28769
|
+
while (end < text3.length && isWordPart2(text3.charAt(end))) end++;
|
|
28770
|
+
tokens.push({ kind: "word", text: text3.slice(index, end) });
|
|
28276
28771
|
index = end;
|
|
28277
|
-
} else if (char === "." && !/[0-9]/.test(
|
|
28772
|
+
} else if (char === "." && !/[0-9]/.test(text3.charAt(index + 1))) {
|
|
28278
28773
|
tokens.push({ kind: "dot" });
|
|
28279
28774
|
index++;
|
|
28280
28775
|
} else break;
|
|
@@ -28414,10 +28909,10 @@ function readAlter(head) {
|
|
|
28414
28909
|
return { kind: "rls" };
|
|
28415
28910
|
return sqlite.ok ? OTHER : sqlite.head;
|
|
28416
28911
|
}
|
|
28417
|
-
function readSqlHead(
|
|
28418
|
-
if (
|
|
28912
|
+
function readSqlHead(text3) {
|
|
28913
|
+
if (text3.includes("\0"))
|
|
28419
28914
|
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
28420
|
-
const head = new Head(headTokens(
|
|
28915
|
+
const head = new Head(headTokens(text3));
|
|
28421
28916
|
if (head.is(0, "create")) return readCreate(head);
|
|
28422
28917
|
if (head.is(0, "drop")) return readDrop(head);
|
|
28423
28918
|
if (head.is(0, "alter")) return readAlter(head);
|
|
@@ -28428,6 +28923,13 @@ function readSqlHead(text2) {
|
|
|
28428
28923
|
return OTHER;
|
|
28429
28924
|
}
|
|
28430
28925
|
|
|
28926
|
+
// ../../packages/data-core/src/functions-registry.ts
|
|
28927
|
+
var FUNCTIONS_TABLE = "_naro_functions";
|
|
28928
|
+
var FUNCTIONS_REGISTRY_VERSION = 9;
|
|
28929
|
+
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')))`;
|
|
28930
|
+
var MAX_REGISTERED_FUNCTIONS = 2e3;
|
|
28931
|
+
var FUNCTIONS_REGISTRY_SQL = `SELECT name, verify_jwt FROM ${FUNCTIONS_TABLE} ORDER BY name LIMIT ${String(MAX_REGISTERED_FUNCTIONS)}`;
|
|
28932
|
+
|
|
28431
28933
|
// ../../packages/data-core/src/policy-index.ts
|
|
28432
28934
|
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
28935
|
function indexesFromRows(rows) {
|
|
@@ -28931,15 +29433,15 @@ function storageStatementProblem(statement2, action) {
|
|
|
28931
29433
|
}),
|
|
28932
29434
|
reason: `the ${STORAGE_OBJECTS} policies need more than ${String(STORAGE_STATEMENT_PARAM_LIMIT)} values in one statement`
|
|
28933
29435
|
};
|
|
28934
|
-
const
|
|
28935
|
-
if (
|
|
29436
|
+
const bytes2 = utf8ByteLength(statement2.sql);
|
|
29437
|
+
if (bytes2 > STORAGE_STATEMENT_BYTE_LIMIT)
|
|
28936
29438
|
return {
|
|
28937
29439
|
error: dataError("POLICY_INVALID", {
|
|
28938
29440
|
action,
|
|
28939
29441
|
table: STORAGE_OBJECTS,
|
|
28940
29442
|
bytes: STORAGE_STATEMENT_BYTE_LIMIT
|
|
28941
29443
|
}),
|
|
28942
|
-
reason: `the ${STORAGE_OBJECTS} policies make one statement ${String(
|
|
29444
|
+
reason: `the ${STORAGE_OBJECTS} policies make one statement ${String(bytes2)} bytes long, more than ${String(STORAGE_STATEMENT_BYTE_LIMIT)}`
|
|
28943
29445
|
};
|
|
28944
29446
|
return void 0;
|
|
28945
29447
|
}
|
|
@@ -29212,8 +29714,8 @@ function budgetProblem(fragment, action) {
|
|
|
29212
29714
|
}),
|
|
29213
29715
|
reason: `the ${STORAGE_OBJECTS} policies need more than ${String(STORAGE_STATEMENT_PARAM_LIMIT)} values in one statement`
|
|
29214
29716
|
};
|
|
29215
|
-
const
|
|
29216
|
-
if (
|
|
29717
|
+
const bytes2 = utf8ByteLength(fragment.sql);
|
|
29718
|
+
if (bytes2 > MAX_POLICY_STATEMENT_BYTES)
|
|
29217
29719
|
return {
|
|
29218
29720
|
ok: false,
|
|
29219
29721
|
error: dataError("POLICY_INVALID", {
|
|
@@ -29221,7 +29723,7 @@ function budgetProblem(fragment, action) {
|
|
|
29221
29723
|
table: STORAGE_OBJECTS,
|
|
29222
29724
|
bytes: true
|
|
29223
29725
|
}),
|
|
29224
|
-
reason: `they write ${String(
|
|
29726
|
+
reason: `they write ${String(bytes2)}`
|
|
29225
29727
|
};
|
|
29226
29728
|
return void 0;
|
|
29227
29729
|
}
|
|
@@ -29375,7 +29877,7 @@ function depthContexts(contexts, claims) {
|
|
|
29375
29877
|
function readsClaims(policies) {
|
|
29376
29878
|
return policies.some(
|
|
29377
29879
|
(policy) => [policy.using, policy.check].some(
|
|
29378
|
-
(
|
|
29880
|
+
(text3) => text3 !== null && /auth\s*\.\s*jwt\s*\(/i.test(text3)
|
|
29379
29881
|
)
|
|
29380
29882
|
);
|
|
29381
29883
|
}
|
|
@@ -30005,15 +30507,15 @@ function readEnd(stream) {
|
|
|
30005
30507
|
rest.position
|
|
30006
30508
|
);
|
|
30007
30509
|
}
|
|
30008
|
-
function parsePolicyStatement(
|
|
30009
|
-
if (policySqlTooLong(
|
|
30510
|
+
function parsePolicyStatement(text3) {
|
|
30511
|
+
if (policySqlTooLong(text3))
|
|
30010
30512
|
return {
|
|
30011
30513
|
ok: false,
|
|
30012
30514
|
reason: `the statement is longer than ${String(MAX_POLICY_SQL_BYTES)} bytes`,
|
|
30013
30515
|
position: 1
|
|
30014
30516
|
};
|
|
30015
30517
|
try {
|
|
30016
|
-
const stream = new TokenStream(tokenize2(
|
|
30518
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
30017
30519
|
const start = stream.next();
|
|
30018
30520
|
let statement2;
|
|
30019
30521
|
if (isKeyword(start, "create")) statement2 = readCreate2(stream, start);
|
|
@@ -30037,10 +30539,10 @@ function parsePolicyStatement(text2) {
|
|
|
30037
30539
|
throw error51;
|
|
30038
30540
|
}
|
|
30039
30541
|
}
|
|
30040
|
-
function splitPolicyStatements(
|
|
30542
|
+
function splitPolicyStatements(text3) {
|
|
30041
30543
|
let tokens;
|
|
30042
30544
|
try {
|
|
30043
|
-
tokens = tokenize2(
|
|
30545
|
+
tokens = tokenize2(text3);
|
|
30044
30546
|
} catch (error51) {
|
|
30045
30547
|
if (error51 instanceof PolicySyntaxError)
|
|
30046
30548
|
return { ok: false, reason: error51.reason, position: error51.position };
|
|
@@ -30052,30 +30554,30 @@ function splitPolicyStatements(text2) {
|
|
|
30052
30554
|
if (token.kind === "eof") break;
|
|
30053
30555
|
start ??= token.position - 1;
|
|
30054
30556
|
if (token.kind === "op" && token.value === ";") {
|
|
30055
|
-
statements.push(
|
|
30557
|
+
statements.push(text3.slice(start, token.position).trim());
|
|
30056
30558
|
start = void 0;
|
|
30057
30559
|
}
|
|
30058
30560
|
}
|
|
30059
30561
|
if (start !== void 0) {
|
|
30060
|
-
const last =
|
|
30562
|
+
const last = text3.slice(start).trim();
|
|
30061
30563
|
if (last !== "") statements.push(last);
|
|
30062
30564
|
}
|
|
30063
30565
|
return { ok: true, statements };
|
|
30064
30566
|
}
|
|
30065
30567
|
var OWNER_PREFIX = "naro:owner";
|
|
30066
|
-
function ownerComment(
|
|
30067
|
-
if (!
|
|
30068
|
-
const rest =
|
|
30568
|
+
function ownerComment(text3, position) {
|
|
30569
|
+
if (!text3.startsWith(OWNER_PREFIX)) return void 0;
|
|
30570
|
+
const rest = text3.slice(OWNER_PREFIX.length);
|
|
30069
30571
|
if (rest !== "" && !/^[ \t]/.test(rest)) return void 0;
|
|
30070
30572
|
let column2 = rest.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
30071
30573
|
if (column2.startsWith('"')) {
|
|
30072
|
-
const
|
|
30073
|
-
if (!
|
|
30574
|
+
const quoted3 = /^"((?:[^"]|"")*)"$/.exec(column2);
|
|
30575
|
+
if (!quoted3)
|
|
30074
30576
|
throw new PolicySyntaxError(
|
|
30075
30577
|
"naro:owner needs one column name, bare or in double quotes",
|
|
30076
30578
|
position
|
|
30077
30579
|
);
|
|
30078
|
-
column2 = (
|
|
30580
|
+
column2 = (quoted3[1] ?? "").replaceAll('""', '"');
|
|
30079
30581
|
}
|
|
30080
30582
|
if (column2 === "")
|
|
30081
30583
|
throw new PolicySyntaxError("naro:owner needs a column name", position);
|
|
@@ -30121,22 +30623,22 @@ function readRowLevelSecurity(stream) {
|
|
|
30121
30623
|
stream.expectKeyword("security");
|
|
30122
30624
|
return { kind: "rls", table, action };
|
|
30123
30625
|
}
|
|
30124
|
-
function parseMigrationPolicyStatement(
|
|
30125
|
-
if (policySqlTooLong(
|
|
30626
|
+
function parseMigrationPolicyStatement(text3) {
|
|
30627
|
+
if (policySqlTooLong(text3))
|
|
30126
30628
|
return {
|
|
30127
30629
|
ok: false,
|
|
30128
30630
|
reason: `the statement is longer than ${String(MAX_POLICY_SQL_BYTES)} bytes`,
|
|
30129
30631
|
position: 1
|
|
30130
30632
|
};
|
|
30131
30633
|
try {
|
|
30132
|
-
const stream = new TokenStream(tokenize2(
|
|
30634
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
30133
30635
|
let statement2;
|
|
30134
30636
|
if (stream.acceptKeyword("comment")) statement2 = readComment(stream);
|
|
30135
30637
|
else if (isKeyword(stream.peek(), "alter") && isKeyword(stream.peek(1), "table")) {
|
|
30136
30638
|
stream.next();
|
|
30137
30639
|
stream.next();
|
|
30138
30640
|
statement2 = readRowLevelSecurity(stream);
|
|
30139
|
-
} else return parsePolicyStatement(
|
|
30641
|
+
} else return parsePolicyStatement(text3);
|
|
30140
30642
|
readEnd(stream);
|
|
30141
30643
|
return { ok: true, statement: statement2 };
|
|
30142
30644
|
} catch (error51) {
|
|
@@ -30282,6 +30784,15 @@ var SYSTEM_MIGRATIONS = [
|
|
|
30282
30784
|
version: REALTIME_TABLES_VERSION,
|
|
30283
30785
|
name: "realtime",
|
|
30284
30786
|
statements: [REALTIME_TABLES_SQL]
|
|
30787
|
+
},
|
|
30788
|
+
{
|
|
30789
|
+
// Functions 3-3a (design 2026-10-29 §6.3): the function registry the
|
|
30790
|
+
// gate reads. Every backend gets it, Functions on or not; the control
|
|
30791
|
+
// plane (3-3b) writes it. A worker from before it never reads it, and a
|
|
30792
|
+
// worker that reads it on a D1 without it answers 409 BACKEND_OUTDATED.
|
|
30793
|
+
version: FUNCTIONS_REGISTRY_VERSION,
|
|
30794
|
+
name: "functions",
|
|
30795
|
+
statements: [FUNCTIONS_TABLE_SQL]
|
|
30285
30796
|
}
|
|
30286
30797
|
];
|
|
30287
30798
|
|
|
@@ -30313,17 +30824,17 @@ function tablesChanged(head) {
|
|
|
30313
30824
|
}
|
|
30314
30825
|
}
|
|
30315
30826
|
function readsBareSubqueryColumn(policy) {
|
|
30316
|
-
return [policy.using, policy.check].some((
|
|
30317
|
-
if (
|
|
30318
|
-
const parsed = parseExpression(
|
|
30827
|
+
return [policy.using, policy.check].some((text3) => {
|
|
30828
|
+
if (text3 === null) return false;
|
|
30829
|
+
const parsed = parseExpression(text3);
|
|
30319
30830
|
return parsed.ok && unqualifiedInSubquery(parsed.expr) !== void 0;
|
|
30320
30831
|
});
|
|
30321
30832
|
}
|
|
30322
30833
|
function tablesInScope(policy) {
|
|
30323
30834
|
const tables = /* @__PURE__ */ new Set([asciiLower(policy.table)]);
|
|
30324
|
-
for (const
|
|
30325
|
-
if (
|
|
30326
|
-
const parsed = parseExpression(
|
|
30835
|
+
for (const text3 of [policy.using, policy.check]) {
|
|
30836
|
+
if (text3 === null) continue;
|
|
30837
|
+
const parsed = parseExpression(text3);
|
|
30327
30838
|
if (!parsed.ok) continue;
|
|
30328
30839
|
for (const subquery of subqueriesOf(parsed.expr))
|
|
30329
30840
|
tables.add(asciiLower(subquery.table));
|
|
@@ -30331,9 +30842,9 @@ function tablesInScope(policy) {
|
|
|
30331
30842
|
return tables;
|
|
30332
30843
|
}
|
|
30333
30844
|
function readsStorageObjects(policy) {
|
|
30334
|
-
return [policy.using, policy.check].some((
|
|
30335
|
-
if (
|
|
30336
|
-
const parsed = parseExpression(
|
|
30845
|
+
return [policy.using, policy.check].some((text3) => {
|
|
30846
|
+
if (text3 === null) return false;
|
|
30847
|
+
const parsed = parseExpression(text3);
|
|
30337
30848
|
return parsed.ok && subqueriesOf(parsed.expr).some(
|
|
30338
30849
|
(subquery) => isStorageObjects(subquery.table)
|
|
30339
30850
|
);
|
|
@@ -30345,54 +30856,54 @@ function planPolicyMigration(statements) {
|
|
|
30345
30856
|
let hasPolicyStatements = false;
|
|
30346
30857
|
let touchesTables = false;
|
|
30347
30858
|
let renamesColumns = false;
|
|
30348
|
-
const heads = statements.map((
|
|
30859
|
+
const heads = statements.map((text3) => readSqlHead(text3));
|
|
30349
30860
|
const changes = heads.map(tablesChanged);
|
|
30350
30861
|
const changedAfter = (index, tables) => changes.slice(index + 1).some(
|
|
30351
30862
|
(changed) => changed === "any" || changed.some((table) => tables.has(table))
|
|
30352
30863
|
);
|
|
30353
|
-
for (const [index,
|
|
30354
|
-
const
|
|
30864
|
+
for (const [index, text3] of statements.entries()) {
|
|
30865
|
+
const refuse3 = (message) => ({
|
|
30355
30866
|
ok: false,
|
|
30356
30867
|
index,
|
|
30357
30868
|
message: `Statement ${String(index + 1)} of ${String(count2)}: ${message}`
|
|
30358
30869
|
});
|
|
30359
|
-
const head = heads[index] ?? readSqlHead(
|
|
30870
|
+
const head = heads[index] ?? readSqlHead(text3);
|
|
30360
30871
|
switch (head.kind) {
|
|
30361
30872
|
case "grant":
|
|
30362
30873
|
case "revoke":
|
|
30363
|
-
return
|
|
30874
|
+
return refuse3(GRANT_REFUSAL);
|
|
30364
30875
|
case "unreadable":
|
|
30365
|
-
return
|
|
30876
|
+
return refuse3(`${head.reason}.`);
|
|
30366
30877
|
case "create-table":
|
|
30367
30878
|
case "drop-table":
|
|
30368
30879
|
case "rename-table":
|
|
30369
30880
|
touchesTables = true;
|
|
30370
|
-
planned.push({ kind: "verbatim", text:
|
|
30881
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30371
30882
|
continue;
|
|
30372
30883
|
case "rename-column":
|
|
30373
30884
|
renamesColumns = true;
|
|
30374
|
-
planned.push({ kind: "verbatim", text:
|
|
30885
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30375
30886
|
continue;
|
|
30376
30887
|
case "alter-table":
|
|
30377
30888
|
case "other":
|
|
30378
|
-
planned.push({ kind: "verbatim", text:
|
|
30889
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30379
30890
|
continue;
|
|
30380
30891
|
default:
|
|
30381
30892
|
break;
|
|
30382
30893
|
}
|
|
30383
|
-
const parsed = parseMigrationPolicyStatement(
|
|
30894
|
+
const parsed = parseMigrationPolicyStatement(text3);
|
|
30384
30895
|
if (!parsed.ok)
|
|
30385
|
-
return
|
|
30896
|
+
return refuse3(
|
|
30386
30897
|
`${parsed.reason} at character ${String(parsed.position)}.`
|
|
30387
30898
|
);
|
|
30388
30899
|
const statement2 = parsed.statement;
|
|
30389
30900
|
const table = statement2.kind === "create" ? statement2.policy.table : statement2.table;
|
|
30390
30901
|
if (!isStorageObjects(table) && isSystemTable(table))
|
|
30391
|
-
return
|
|
30902
|
+
return refuse3(`${relationMissing(table)}.`);
|
|
30392
30903
|
switch (statement2.kind) {
|
|
30393
30904
|
case "create":
|
|
30394
30905
|
if (readsBareSubqueryColumn(statement2.policy) && changedAfter(index, tablesInScope(statement2.policy)))
|
|
30395
|
-
return
|
|
30906
|
+
return refuse3(LATER_TABLE_CHANGE);
|
|
30396
30907
|
hasPolicyStatements = true;
|
|
30397
30908
|
planned.push({ kind: "create-policy", policy: statement2.policy });
|
|
30398
30909
|
break;
|
|
@@ -30420,7 +30931,7 @@ function planPolicyMigration(statements) {
|
|
|
30420
30931
|
break;
|
|
30421
30932
|
case "rls":
|
|
30422
30933
|
if (statement2.action === "disable")
|
|
30423
|
-
return
|
|
30934
|
+
return refuse3(disableRefusal(table));
|
|
30424
30935
|
planned.push({ kind: "noop" });
|
|
30425
30936
|
break;
|
|
30426
30937
|
}
|
|
@@ -30500,19 +31011,19 @@ var TYPED_PATH = /^[a-z][a-z0-9+.-]*:\/\/[^/]*(\/.*)?$/i;
|
|
|
30500
31011
|
var SEGMENT = /^[A-Za-z0-9._~-]+$/;
|
|
30501
31012
|
function parseRedirectEntry(value) {
|
|
30502
31013
|
if (typeof value !== "string") return null;
|
|
30503
|
-
const
|
|
30504
|
-
if (
|
|
30505
|
-
if (/[\s?#*%\\]/.test(
|
|
31014
|
+
const text3 = value.trim();
|
|
31015
|
+
if (text3 === "" || text3.length > MAX_ENTRY_LENGTH) return null;
|
|
31016
|
+
if (/[\s?#*%\\]/.test(text3)) return null;
|
|
30506
31017
|
let url2;
|
|
30507
31018
|
try {
|
|
30508
|
-
url2 = new URL(
|
|
31019
|
+
url2 = new URL(text3);
|
|
30509
31020
|
} catch {
|
|
30510
31021
|
return null;
|
|
30511
31022
|
}
|
|
30512
31023
|
if (url2.username !== "" || url2.password !== "") return null;
|
|
30513
31024
|
if (url2.search !== "" || url2.hash !== "") return null;
|
|
30514
31025
|
if (!isSecureOrLoopback(url2)) return null;
|
|
30515
|
-
const typed = TYPED_PATH.exec(
|
|
31026
|
+
const typed = TYPED_PATH.exec(text3)?.[1] ?? "/";
|
|
30516
31027
|
if (typed !== url2.pathname) return null;
|
|
30517
31028
|
const path = url2.pathname;
|
|
30518
31029
|
if (path !== "/") {
|
|
@@ -31034,13 +31545,13 @@ function listMigrationFiles(dir) {
|
|
|
31034
31545
|
);
|
|
31035
31546
|
}
|
|
31036
31547
|
function nextMigrationFileName(existing, name) {
|
|
31037
|
-
const
|
|
31038
|
-
if (
|
|
31548
|
+
const normalized2 = normalizeMigrationName(name);
|
|
31549
|
+
if (normalized2 === "")
|
|
31039
31550
|
throw new NaroError(
|
|
31040
31551
|
"VALIDATION_FAILED",
|
|
31041
31552
|
"Migration name must contain letters or digits."
|
|
31042
31553
|
);
|
|
31043
|
-
return numberedMigrationName(existing,
|
|
31554
|
+
return numberedMigrationName(existing, normalized2);
|
|
31044
31555
|
}
|
|
31045
31556
|
function compareMigrations(local, applied) {
|
|
31046
31557
|
const ledger = new Map(applied.map((row) => [row.name, row]));
|
|
@@ -31053,13 +31564,13 @@ function compareMigrations(local, applied) {
|
|
|
31053
31564
|
rows.push({ name, status: "applied", appliedAt: exact.appliedAt });
|
|
31054
31565
|
continue;
|
|
31055
31566
|
}
|
|
31056
|
-
const
|
|
31057
|
-
if (
|
|
31058
|
-
ledger.delete(
|
|
31567
|
+
const bare2 = ledger.get(name.replace(/\.sql$/, ""));
|
|
31568
|
+
if (bare2) {
|
|
31569
|
+
ledger.delete(bare2.name);
|
|
31059
31570
|
rows.push({
|
|
31060
31571
|
name,
|
|
31061
31572
|
status: "applied",
|
|
31062
|
-
appliedAt:
|
|
31573
|
+
appliedAt: bare2.appliedAt,
|
|
31063
31574
|
warning: "ledger-name-missing-extension"
|
|
31064
31575
|
});
|
|
31065
31576
|
continue;
|
|
@@ -31541,10 +32052,10 @@ var SUPPORTED_REMOTE = [
|
|
|
31541
32052
|
/^ssh:\/\/[^\s]+$/,
|
|
31542
32053
|
/^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
|
|
31543
32054
|
];
|
|
31544
|
-
var defaultExec = (args, cwd) => new Promise((
|
|
32055
|
+
var defaultExec = (args, cwd) => new Promise((resolve15, reject) => {
|
|
31545
32056
|
execFile2("git", args, { cwd }, (error51, stdout) => {
|
|
31546
32057
|
if (error51) reject(error51);
|
|
31547
|
-
else
|
|
32058
|
+
else resolve15(stdout);
|
|
31548
32059
|
});
|
|
31549
32060
|
});
|
|
31550
32061
|
async function tryExec(exec, args, cwd) {
|
|
@@ -31693,6 +32204,24 @@ async function uploadArtifact(artifact, opts) {
|
|
|
31693
32204
|
};
|
|
31694
32205
|
}
|
|
31695
32206
|
|
|
32207
|
+
// src/version.ts
|
|
32208
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
32209
|
+
import { dirname as dirname5, join as join25 } from "path";
|
|
32210
|
+
import { fileURLToPath } from "url";
|
|
32211
|
+
function version2() {
|
|
32212
|
+
try {
|
|
32213
|
+
const pkg = JSON.parse(
|
|
32214
|
+
readFileSync6(
|
|
32215
|
+
join25(dirname5(fileURLToPath(import.meta.url)), "..", "package.json"),
|
|
32216
|
+
"utf8"
|
|
32217
|
+
)
|
|
32218
|
+
);
|
|
32219
|
+
return pkg.version ?? "unknown";
|
|
32220
|
+
} catch {
|
|
32221
|
+
return "unknown";
|
|
32222
|
+
}
|
|
32223
|
+
}
|
|
32224
|
+
|
|
31696
32225
|
// src/working-tree.ts
|
|
31697
32226
|
import { execFile as execFile3 } from "child_process";
|
|
31698
32227
|
import {
|
|
@@ -31710,9 +32239,9 @@ import {
|
|
|
31710
32239
|
} from "fs/promises";
|
|
31711
32240
|
import {
|
|
31712
32241
|
basename as basename3,
|
|
31713
|
-
dirname as
|
|
32242
|
+
dirname as dirname6,
|
|
31714
32243
|
isAbsolute as isAbsolute4,
|
|
31715
|
-
join as
|
|
32244
|
+
join as join26,
|
|
31716
32245
|
relative as relative6,
|
|
31717
32246
|
resolve as resolve10,
|
|
31718
32247
|
sep as sep5
|
|
@@ -31751,15 +32280,15 @@ async function committedEnvFiles(sourceDir) {
|
|
|
31751
32280
|
return [];
|
|
31752
32281
|
});
|
|
31753
32282
|
}
|
|
31754
|
-
async function staysInside(link,
|
|
32283
|
+
async function staysInside(link, text3, root) {
|
|
31755
32284
|
try {
|
|
31756
32285
|
return inside(await realpath3(link), root);
|
|
31757
32286
|
} catch (error51) {
|
|
31758
|
-
return error51.code === "ENOENT" && !
|
|
32287
|
+
return error51.code === "ENOENT" && !text3.split(/[\\/]/).includes("..");
|
|
31759
32288
|
}
|
|
31760
32289
|
}
|
|
31761
|
-
function climbsOnlyFirst(
|
|
31762
|
-
const segments =
|
|
32290
|
+
function climbsOnlyFirst(text3) {
|
|
32291
|
+
const segments = text3.split(/[\\/]/).filter((s) => s !== "" && s !== ".");
|
|
31763
32292
|
const down = segments.findIndex((segment) => segment !== "..");
|
|
31764
32293
|
return down < 0 || !segments.slice(down).includes("..");
|
|
31765
32294
|
}
|
|
@@ -31779,11 +32308,11 @@ async function readLocal(path) {
|
|
|
31779
32308
|
return readFile14(path).catch(() => null);
|
|
31780
32309
|
}
|
|
31781
32310
|
async function realAncestor(dir) {
|
|
31782
|
-
for (let current = dir; ; current =
|
|
32311
|
+
for (let current = dir; ; current = dirname6(current)) {
|
|
31783
32312
|
try {
|
|
31784
32313
|
return await realpath3(current);
|
|
31785
32314
|
} catch {
|
|
31786
|
-
if (
|
|
32315
|
+
if (dirname6(current) === current)
|
|
31787
32316
|
throw new Error(`No ancestor of ${dir}`);
|
|
31788
32317
|
}
|
|
31789
32318
|
}
|
|
@@ -31821,7 +32350,7 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31821
32350
|
absoluteLinks.push(path);
|
|
31822
32351
|
return false;
|
|
31823
32352
|
}
|
|
31824
|
-
if (!inside(resolve10(
|
|
32353
|
+
if (!inside(resolve10(dirname6(src), target2), sourceDir)) {
|
|
31825
32354
|
linksOutside.push(path);
|
|
31826
32355
|
return false;
|
|
31827
32356
|
}
|
|
@@ -31837,14 +32366,14 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31837
32366
|
const root = await realpath3(checkout);
|
|
31838
32367
|
const strays = await keptOnly(
|
|
31839
32368
|
keptLinks,
|
|
31840
|
-
async ({ path, text:
|
|
32369
|
+
async ({ path, text: text3 }) => {
|
|
31841
32370
|
try {
|
|
31842
|
-
return inside(await realpath3(
|
|
32371
|
+
return inside(await realpath3(join26(checkout, path)), root);
|
|
31843
32372
|
} catch (error51) {
|
|
31844
|
-
return error51.code === "ENOENT" && climbsOnlyFirst(
|
|
32373
|
+
return error51.code === "ENOENT" && climbsOnlyFirst(text3);
|
|
31845
32374
|
}
|
|
31846
32375
|
},
|
|
31847
|
-
({ path }) => unlink(
|
|
32376
|
+
({ path }) => unlink(join26(checkout, path))
|
|
31848
32377
|
);
|
|
31849
32378
|
linksElsewhere.push(...strays.map(({ path }) => path));
|
|
31850
32379
|
const committed = await committedEnvFiles(sourceDir);
|
|
@@ -31858,13 +32387,13 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31858
32387
|
outsideProject.push(path);
|
|
31859
32388
|
continue;
|
|
31860
32389
|
}
|
|
31861
|
-
const target2 =
|
|
31862
|
-
if (!inside(await realAncestor(
|
|
32390
|
+
const target2 = join26(checkout, path);
|
|
32391
|
+
if (!inside(await realAncestor(dirname6(target2)), root)) {
|
|
31863
32392
|
outsideProject.push(path);
|
|
31864
32393
|
continue;
|
|
31865
32394
|
}
|
|
31866
32395
|
try {
|
|
31867
|
-
await mkdir3(
|
|
32396
|
+
await mkdir3(dirname6(target2), { recursive: true });
|
|
31868
32397
|
} catch {
|
|
31869
32398
|
outsideProject.push(path);
|
|
31870
32399
|
continue;
|
|
@@ -31873,7 +32402,7 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31873
32402
|
const content = await git(sourceDir, ["show", `HEAD:./${path}`]);
|
|
31874
32403
|
if (isLink) {
|
|
31875
32404
|
const linkTarget = content.toString("utf8");
|
|
31876
|
-
const pointsAt = resolve10(
|
|
32405
|
+
const pointsAt = resolve10(dirname6(target2), linkTarget);
|
|
31877
32406
|
if (isAbsolute4(linkTarget) || !inside(pointsAt, checkout)) {
|
|
31878
32407
|
outsideProject.push(path);
|
|
31879
32408
|
continue;
|
|
@@ -31883,16 +32412,16 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31883
32412
|
continue;
|
|
31884
32413
|
}
|
|
31885
32414
|
await writeFile7(target2, content, { flag: "wx" });
|
|
31886
|
-
const local = await readLocal(
|
|
32415
|
+
const local = await readLocal(join26(sourceDir, path));
|
|
31887
32416
|
if (!local || !sameText(local, content)) committedVersion.push(path);
|
|
31888
32417
|
}
|
|
31889
32418
|
const envStrays = await keptOnly(
|
|
31890
32419
|
envLinks,
|
|
31891
32420
|
async (path) => {
|
|
31892
|
-
const real = await realpath3(
|
|
32421
|
+
const real = await realpath3(join26(checkout, path)).catch(() => null);
|
|
31893
32422
|
return real !== null && inside(real, root);
|
|
31894
32423
|
},
|
|
31895
|
-
(path) => unlink(
|
|
32424
|
+
(path) => unlink(join26(checkout, path))
|
|
31896
32425
|
);
|
|
31897
32426
|
outsideProject.push(...envStrays);
|
|
31898
32427
|
const inCommit = new Set(
|
|
@@ -31912,38 +32441,1805 @@ async function copyWorkingTree(source, checkout) {
|
|
|
31912
32441
|
};
|
|
31913
32442
|
}
|
|
31914
32443
|
|
|
31915
|
-
// src/deploy.ts
|
|
31916
|
-
|
|
31917
|
-
|
|
32444
|
+
// src/functions/deploy.ts
|
|
32445
|
+
import { relative as relative11, resolve as resolve13, sep as sep10 } from "path";
|
|
32446
|
+
|
|
32447
|
+
// ../../packages/functions-bundler/src/errors.ts
|
|
32448
|
+
var FunctionBundleError = class extends Error {
|
|
32449
|
+
code;
|
|
32450
|
+
constructor(code, message, options) {
|
|
32451
|
+
super(message, options);
|
|
32452
|
+
this.name = "FunctionBundleError";
|
|
32453
|
+
this.code = code;
|
|
32454
|
+
}
|
|
32455
|
+
};
|
|
32456
|
+
var NAME_RULE = "names are 1\u201364 characters of lowercase letters, digits, - and _, starting with a letter";
|
|
32457
|
+
var JSR_REGISTRY = '"@jsr:registry=https://npm.jsr.io"';
|
|
32458
|
+
var bytes = (n) => n.toLocaleString("en-US");
|
|
32459
|
+
var SHELL_SAFE = /^[A-Za-z0-9@/._^=+:-]+$/;
|
|
32460
|
+
var shellArg = (spec) => SHELL_SAFE.test(spec) ? spec : `'${spec.replaceAll("'", "'\\''")}'`;
|
|
32461
|
+
var BUNDLE_MESSAGES = {
|
|
32462
|
+
noDir: (dir) => `There is no ${dir} folder here. Create a function with naro functions new <name>.`,
|
|
32463
|
+
supabaseFound: "Found supabase/functions. Deploy it with --dir supabase/functions.",
|
|
32464
|
+
noFunctions: (dir) => `No functions in ${dir}. Create one with naro functions new <name>.`,
|
|
32465
|
+
badName: (name) => `Function folder ${name} is not a valid function name: ${NAME_RULE}.`,
|
|
32466
|
+
noFolder: (name, dir) => `Function ${name} has no folder: ${dir}/${name} does not exist.`,
|
|
32467
|
+
jsonInvalid: (name) => `function.json of ${name} is not valid JSON.`,
|
|
32468
|
+
jsonKeys: (name, keys) => `function.json of ${name} has keys Naro does not read: ${keys.join(", ")}. It takes verifyJwt and entrypoint.`,
|
|
32469
|
+
jsonVerifyJwt: (name) => `verifyJwt in function.json of ${name} must be true or false.`,
|
|
32470
|
+
jsonEntrypoint: (name) => `entrypoint in function.json of ${name} must be a relative path inside the function folder.`,
|
|
32471
|
+
noEntry: (name, dir) => `Function ${name} has no entry file. Add ${dir}/${name}/index.ts (or set "entrypoint" in function.json).`,
|
|
32472
|
+
entryMissing: (name, entrypoint) => `Function ${name} has no ${entrypoint}, the entrypoint its function.json names.`,
|
|
32473
|
+
supabaseVerifyJwt: (dir) => `\u26A0 supabase/config.toml's verify_jwt is not read. Set "verifyJwt": false in ${dir}/<name>/function.json, or deploy with --no-verify-jwt.`,
|
|
32474
|
+
importMapMissing: (file2) => `--import-map ${file2} does not exist.`,
|
|
32475
|
+
importMapInvalid: (file2) => `${file2} is not valid JSON.`,
|
|
32476
|
+
importMapShape: (file2) => `The imports of ${file2} must map names to text.`,
|
|
32477
|
+
importMapPrefix: (file2, key, value) => `The imports of ${file2} map ${JSON.stringify(key)} to ${JSON.stringify(value)}: a key ending in / needs a value ending in / too.`,
|
|
32478
|
+
npmMissing: (spec, install, dir) => `npm:${spec} is not installed. Run npm install ${shellArg(install)} in ${dir}.`,
|
|
32479
|
+
jsrMissing: (spec, install) => `jsr:${spec} is not installed. Add ${JSR_REGISTRY} to .npmrc, then run npm install ${shellArg(install)}.`,
|
|
32480
|
+
missingMany: (specs, installs, jsr) => jsr ? `Missing packages: ${specs.join(", ")}. Add ${JSR_REGISTRY} to .npmrc, then install them with: npm install ${installs.map(shellArg).join(" ")}` : `Missing packages: ${specs.join(", ")}. Install them with: npm install ${installs.map(shellArg).join(" ")}`,
|
|
32481
|
+
noPackageName: (specifier, example) => `${specifier} names no package. Write it like ${example}.`,
|
|
32482
|
+
jsrNoScope: (spec) => `jsr:${spec} needs a scope, like jsr:@scope/${spec}.`,
|
|
32483
|
+
urlNpm: (url2, target2) => `URL imports are not supported: ${url2}. Use ${target2} instead.`,
|
|
32484
|
+
urlStd: (url2, module) => `URL imports are not supported: ${url2}. Use jsr:@std/${module} instead.`,
|
|
32485
|
+
urlOther: (url2) => `URL imports are not supported: ${url2}. Install the package from npm and import it by name.`,
|
|
32486
|
+
outOfRange: (pkg, version3, range, file2) => `\u26A0 ${pkg} is installed at ${version3}, outside the range ${range} that ${file2} asks for.`,
|
|
32487
|
+
wasm: (file2) => `WebAssembly modules are not supported in functions yet: ${file2}.`,
|
|
32488
|
+
naroJsMissing: 'Cannot find "naro-js/functions". Install naro-js 0.7.0 or later: npm install naro-js@latest.',
|
|
32489
|
+
tooLarge: (name, size, inputs) => `Function ${name} bundles to ${bytes(size)} bytes; the limit is 67,108,864 (64 MiB). The biggest inputs: ${inputs}.`,
|
|
32490
|
+
// Rounded up: a size over the limit never reads as the limit (10.0 MB).
|
|
32491
|
+
tooLargeCompressed: (name, size) => `Function ${name} is ${(Math.ceil(size / 1048576 * 10) / 10).toFixed(1)} MB after compression; Cloudflare accepts 10 MB.`,
|
|
32492
|
+
otherBundle: (name, text3, location2) => `Function ${name} did not bundle: ${text3}${location2 ? ` (${location2})` : ""}`,
|
|
32493
|
+
noHandler: (name) => `Function ${name} did not register a handler. Call Deno.serve(handler) or export default { fetch }.`,
|
|
32494
|
+
failedToStart: (name, line) => `Function ${name} failed to start: ${line}`,
|
|
32495
|
+
esbuildMissing: (name, cause) => `Function ${name} did not bundle: esbuild could not be loaded (${cause}). Reinstall naro-cli.`,
|
|
32496
|
+
miniflareMissing: (name, cause) => `Function ${name} could not be checked: miniflare could not be loaded (${cause}). Reinstall naro-cli, or skip the check with --skip-check (skipCheck: true in naro-mcp).`,
|
|
32497
|
+
bootTimeout: (name, ms) => `Function ${name} did not start within ${String(ms / 1e3)} seconds.`
|
|
32498
|
+
};
|
|
32499
|
+
|
|
32500
|
+
// ../../packages/functions-bundler/src/discover.ts
|
|
32501
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
32502
|
+
import { join as join27, relative as relative7, resolve as resolve11, sep as sep6 } from "path";
|
|
32503
|
+
|
|
32504
|
+
// ../../packages/functions-bundler/src/limits.ts
|
|
32505
|
+
var FUNCTION_BUNDLE_MAX_BYTES = 67108864;
|
|
32506
|
+
var FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES = 10485760;
|
|
32507
|
+
var ENTRY_FILES = [
|
|
32508
|
+
"index.ts",
|
|
32509
|
+
"index.js",
|
|
32510
|
+
"index.mjs",
|
|
32511
|
+
"index.tsx",
|
|
32512
|
+
"index.jsx"
|
|
32513
|
+
];
|
|
32514
|
+
|
|
32515
|
+
// ../../packages/functions-bundler/src/discover.ts
|
|
32516
|
+
var SEGMENT2 = /^[A-Za-z0-9._@+-]+$/;
|
|
32517
|
+
function isEntrypoint(value) {
|
|
32518
|
+
if (value.length < 1 || value.length > 200) return false;
|
|
32519
|
+
return value.split("/").every(
|
|
32520
|
+
(segment) => segment !== "." && segment !== ".." && SEGMENT2.test(segment)
|
|
32521
|
+
);
|
|
31918
32522
|
}
|
|
31919
|
-
function
|
|
31920
|
-
|
|
32523
|
+
function functionsFolder(options) {
|
|
32524
|
+
const root = resolve11(options.cwd, options.root ?? ".");
|
|
32525
|
+
const explicit = options.dir !== void 0;
|
|
32526
|
+
const dir = explicit ? resolve11(options.cwd, options.dir) : join27(root, "naro", "functions");
|
|
32527
|
+
const label2 = relative7(options.cwd, dir).split(sep6).join("/") || ".";
|
|
32528
|
+
return { dir, label: label2, root, explicit };
|
|
31921
32529
|
}
|
|
31922
|
-
function
|
|
31923
|
-
|
|
31924
|
-
|
|
31925
|
-
|
|
31926
|
-
|
|
31927
|
-
|
|
31928
|
-
|
|
31929
|
-
|
|
31930
|
-
|
|
31931
|
-
|
|
31932
|
-
|
|
31933
|
-
|
|
31934
|
-
|
|
31935
|
-
|
|
31936
|
-
|
|
31937
|
-
|
|
31938
|
-
|
|
31939
|
-
|
|
31940
|
-
|
|
31941
|
-
|
|
32530
|
+
function listFunctionNames(folder) {
|
|
32531
|
+
let entries;
|
|
32532
|
+
try {
|
|
32533
|
+
entries = readdirSync3(folder.dir, { withFileTypes: true });
|
|
32534
|
+
} catch {
|
|
32535
|
+
if (!folder.explicit && existsSync7(join27(folder.root, "supabase", "functions")))
|
|
32536
|
+
throw new FunctionBundleError(
|
|
32537
|
+
"VALIDATION_FAILED",
|
|
32538
|
+
BUNDLE_MESSAGES.supabaseFound
|
|
32539
|
+
);
|
|
32540
|
+
throw new FunctionBundleError(
|
|
32541
|
+
"VALIDATION_FAILED",
|
|
32542
|
+
BUNDLE_MESSAGES.noDir(folder.label)
|
|
32543
|
+
);
|
|
32544
|
+
}
|
|
32545
|
+
const names = entries.filter(
|
|
32546
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".") && entry.name !== "node_modules"
|
|
32547
|
+
).map((entry) => entry.name).sort();
|
|
32548
|
+
if (names.length === 0)
|
|
32549
|
+
throw new FunctionBundleError(
|
|
32550
|
+
"VALIDATION_FAILED",
|
|
32551
|
+
BUNDLE_MESSAGES.noFunctions(folder.label)
|
|
32552
|
+
);
|
|
32553
|
+
return names;
|
|
32554
|
+
}
|
|
32555
|
+
function readFunctionJson(dir, name) {
|
|
32556
|
+
const file2 = join27(dir, "function.json");
|
|
32557
|
+
if (!existsSync7(file2)) return { verifyJwt: void 0, entrypoint: void 0 };
|
|
32558
|
+
let parsed;
|
|
32559
|
+
try {
|
|
32560
|
+
parsed = JSON.parse(readFileSync7(file2, "utf8"));
|
|
32561
|
+
} catch {
|
|
32562
|
+
throw new FunctionBundleError(
|
|
32563
|
+
"VALIDATION_FAILED",
|
|
32564
|
+
BUNDLE_MESSAGES.jsonInvalid(name)
|
|
32565
|
+
);
|
|
32566
|
+
}
|
|
32567
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
32568
|
+
throw new FunctionBundleError(
|
|
32569
|
+
"VALIDATION_FAILED",
|
|
32570
|
+
BUNDLE_MESSAGES.jsonInvalid(name)
|
|
32571
|
+
);
|
|
32572
|
+
const record3 = parsed;
|
|
32573
|
+
const unknown2 = Object.keys(record3).filter(
|
|
32574
|
+
(key) => key !== "verifyJwt" && key !== "entrypoint"
|
|
32575
|
+
);
|
|
32576
|
+
if (unknown2.length > 0)
|
|
32577
|
+
throw new FunctionBundleError(
|
|
32578
|
+
"VALIDATION_FAILED",
|
|
32579
|
+
BUNDLE_MESSAGES.jsonKeys(name, unknown2)
|
|
32580
|
+
);
|
|
32581
|
+
const { verifyJwt, entrypoint } = record3;
|
|
32582
|
+
if (verifyJwt !== void 0 && typeof verifyJwt !== "boolean")
|
|
32583
|
+
throw new FunctionBundleError(
|
|
32584
|
+
"VALIDATION_FAILED",
|
|
32585
|
+
BUNDLE_MESSAGES.jsonVerifyJwt(name)
|
|
32586
|
+
);
|
|
32587
|
+
if (entrypoint !== void 0 && (typeof entrypoint !== "string" || !isEntrypoint(entrypoint)))
|
|
32588
|
+
throw new FunctionBundleError(
|
|
32589
|
+
"VALIDATION_FAILED",
|
|
32590
|
+
BUNDLE_MESSAGES.jsonEntrypoint(name)
|
|
32591
|
+
);
|
|
32592
|
+
return { verifyJwt, entrypoint };
|
|
32593
|
+
}
|
|
32594
|
+
function readFunctionSource(folder, name) {
|
|
32595
|
+
if (!isFunctionName(name))
|
|
32596
|
+
throw new FunctionBundleError(
|
|
32597
|
+
"VALIDATION_FAILED",
|
|
32598
|
+
BUNDLE_MESSAGES.badName(name)
|
|
32599
|
+
);
|
|
32600
|
+
const dir = join27(folder.dir, name);
|
|
32601
|
+
if (!statSync2(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
32602
|
+
throw new FunctionBundleError(
|
|
32603
|
+
"VALIDATION_FAILED",
|
|
32604
|
+
BUNDLE_MESSAGES.noFolder(name, folder.label)
|
|
32605
|
+
);
|
|
32606
|
+
const config2 = readFunctionJson(dir, name);
|
|
32607
|
+
let entrypoint;
|
|
32608
|
+
if (config2.entrypoint !== void 0) {
|
|
32609
|
+
if (!existsSync7(join27(dir, ...config2.entrypoint.split("/"))))
|
|
32610
|
+
throw new FunctionBundleError(
|
|
32611
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
32612
|
+
BUNDLE_MESSAGES.entryMissing(name, config2.entrypoint)
|
|
32613
|
+
);
|
|
32614
|
+
entrypoint = config2.entrypoint;
|
|
32615
|
+
} else {
|
|
32616
|
+
const found = ENTRY_FILES.find((file2) => existsSync7(join27(dir, file2)));
|
|
32617
|
+
if (found === void 0)
|
|
32618
|
+
throw new FunctionBundleError(
|
|
32619
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
32620
|
+
BUNDLE_MESSAGES.noEntry(name, folder.label)
|
|
32621
|
+
);
|
|
32622
|
+
entrypoint = found;
|
|
32623
|
+
}
|
|
32624
|
+
return {
|
|
32625
|
+
name,
|
|
32626
|
+
dir,
|
|
32627
|
+
entry: join27(dir, ...entrypoint.split("/")),
|
|
32628
|
+
entrypoint,
|
|
32629
|
+
verifyJwt: config2.verifyJwt
|
|
32630
|
+
};
|
|
32631
|
+
}
|
|
32632
|
+
function configTomlWarning(folder) {
|
|
32633
|
+
if (!folder.dir.endsWith(join27("supabase", "functions"))) return null;
|
|
32634
|
+
const file2 = join27(folder.dir, "..", "config.toml");
|
|
32635
|
+
if (!existsSync7(file2)) return null;
|
|
32636
|
+
return /^\s*verify_jwt\s*=/m.test(readFileSync7(file2, "utf8")) ? BUNDLE_MESSAGES.supabaseVerifyJwt(folder.label) : null;
|
|
32637
|
+
}
|
|
32638
|
+
|
|
32639
|
+
// ../../packages/functions-bundler/src/jsonc.ts
|
|
32640
|
+
function closesNext(text3, from) {
|
|
32641
|
+
let i = from;
|
|
32642
|
+
while (i < text3.length && /\s/.test(text3[i] ?? "")) i += 1;
|
|
32643
|
+
return text3[i] === "}" || text3[i] === "]";
|
|
32644
|
+
}
|
|
32645
|
+
function parseJsonc2(text3) {
|
|
32646
|
+
const uncommented = stripComments2(text3);
|
|
32647
|
+
let out = "";
|
|
32648
|
+
let inString = false;
|
|
32649
|
+
for (let i = 0; i < uncommented.length; i += 1) {
|
|
32650
|
+
const char = uncommented[i] ?? "";
|
|
32651
|
+
if (inString) {
|
|
32652
|
+
out += char;
|
|
32653
|
+
if (char === "\\") {
|
|
32654
|
+
out += uncommented[i + 1] ?? "";
|
|
32655
|
+
i += 1;
|
|
32656
|
+
} else if (char === '"') inString = false;
|
|
32657
|
+
continue;
|
|
32658
|
+
}
|
|
32659
|
+
if (char === '"') inString = true;
|
|
32660
|
+
if (char === "," && closesNext(uncommented, i + 1)) continue;
|
|
32661
|
+
out += char;
|
|
32662
|
+
}
|
|
32663
|
+
return JSON.parse(out);
|
|
32664
|
+
}
|
|
32665
|
+
function stripComments2(text3) {
|
|
32666
|
+
let out = "";
|
|
32667
|
+
let i = 0;
|
|
32668
|
+
let inString = false;
|
|
32669
|
+
while (i < text3.length) {
|
|
32670
|
+
const char = text3[i] ?? "";
|
|
32671
|
+
const next = text3[i + 1] ?? "";
|
|
32672
|
+
if (inString) {
|
|
32673
|
+
out += char;
|
|
32674
|
+
if (char === "\\") {
|
|
32675
|
+
out += next;
|
|
32676
|
+
i += 2;
|
|
32677
|
+
continue;
|
|
31942
32678
|
}
|
|
32679
|
+
if (char === '"') inString = false;
|
|
32680
|
+
i += 1;
|
|
32681
|
+
continue;
|
|
31943
32682
|
}
|
|
31944
|
-
|
|
31945
|
-
|
|
31946
|
-
|
|
32683
|
+
if (char === '"') {
|
|
32684
|
+
inString = true;
|
|
32685
|
+
out += char;
|
|
32686
|
+
i += 1;
|
|
32687
|
+
continue;
|
|
32688
|
+
}
|
|
32689
|
+
if (char === "/" && next === "/") {
|
|
32690
|
+
while (i < text3.length && text3[i] !== "\n") i += 1;
|
|
32691
|
+
continue;
|
|
32692
|
+
}
|
|
32693
|
+
if (char === "/" && next === "*") {
|
|
32694
|
+
const end = text3.indexOf("*/", i + 2);
|
|
32695
|
+
i = end === -1 ? text3.length : end + 2;
|
|
32696
|
+
out += " ";
|
|
32697
|
+
continue;
|
|
32698
|
+
}
|
|
32699
|
+
out += char;
|
|
32700
|
+
i += 1;
|
|
32701
|
+
}
|
|
32702
|
+
return out;
|
|
32703
|
+
}
|
|
32704
|
+
|
|
32705
|
+
// ../../packages/functions-bundler/src/node-compat.ts
|
|
32706
|
+
import { createRequire as createRequire2 } from "module";
|
|
32707
|
+
import { dirname as dirname7, resolve as resolvePath } from "path";
|
|
32708
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
32709
|
+
var require2 = createRequire2(import.meta.url);
|
|
32710
|
+
var MODULE_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
32711
|
+
var REQUIRED_NODE_BUILT_IN_NAMESPACE = "node-built-in-modules";
|
|
32712
|
+
var REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
|
|
32713
|
+
var SKIPPED_ALIASES = /* @__PURE__ */ new Set(["debug"]);
|
|
32714
|
+
async function nodeCompatEnvironment(options) {
|
|
32715
|
+
const { defineEnv } = await import("unenv");
|
|
32716
|
+
const { getCloudflarePreset } = await import("@cloudflare/unenv-preset");
|
|
32717
|
+
return defineEnv({
|
|
32718
|
+
presets: [
|
|
32719
|
+
getCloudflarePreset({
|
|
32720
|
+
compatibilityDate: options.compatibilityDate,
|
|
32721
|
+
compatibilityFlags: [...options.compatibilityFlags]
|
|
32722
|
+
})
|
|
32723
|
+
],
|
|
32724
|
+
npmShims: true
|
|
32725
|
+
}).env;
|
|
32726
|
+
}
|
|
32727
|
+
function nodeCompatPlugin(options) {
|
|
32728
|
+
return {
|
|
32729
|
+
name: "hybrid-nodejs_compat",
|
|
32730
|
+
async setup(build) {
|
|
32731
|
+
const { nonPrefixedNodeModules } = await import("@cloudflare/unenv-preset");
|
|
32732
|
+
const { alias, inject, external, polyfill } = await nodeCompatEnvironment(options);
|
|
32733
|
+
const nodeJsModuleRegexp = new RegExp(
|
|
32734
|
+
`^(${nonPrefixedNodeModules.join("|")}|node:.+)$`
|
|
32735
|
+
);
|
|
32736
|
+
handleRequireCallsToNodeJSBuiltins(build, nodeJsModuleRegexp);
|
|
32737
|
+
handleUnenvAliasedPackages(build, alias, external);
|
|
32738
|
+
handleNodeJSGlobals(build, inject, polyfill, options.virtualBase);
|
|
32739
|
+
}
|
|
32740
|
+
};
|
|
32741
|
+
}
|
|
32742
|
+
function handleRequireCallsToNodeJSBuiltins(build, nodeJsModuleRegexp) {
|
|
32743
|
+
build.onResolve({ filter: nodeJsModuleRegexp }, (args) => {
|
|
32744
|
+
if (args.kind === "require-call")
|
|
32745
|
+
return { path: args.path, namespace: REQUIRED_NODE_BUILT_IN_NAMESPACE };
|
|
32746
|
+
return void 0;
|
|
32747
|
+
});
|
|
32748
|
+
build.onLoad(
|
|
32749
|
+
{ filter: /.*/, namespace: REQUIRED_NODE_BUILT_IN_NAMESPACE },
|
|
32750
|
+
({ path }) => ({
|
|
32751
|
+
contents: `import libDefault from '${path}';
|
|
32752
|
+
module.exports = libDefault;`,
|
|
32753
|
+
loader: "js"
|
|
32754
|
+
})
|
|
32755
|
+
);
|
|
32756
|
+
}
|
|
32757
|
+
function handleUnenvAliasedPackages(build, alias, external) {
|
|
32758
|
+
const aliasAbsolute = {};
|
|
32759
|
+
for (const [module, unresolvedAlias] of Object.entries(alias)) {
|
|
32760
|
+
if (SKIPPED_ALIASES.has(module)) continue;
|
|
32761
|
+
try {
|
|
32762
|
+
aliasAbsolute[module] = require2.resolve(unresolvedAlias);
|
|
32763
|
+
} catch {
|
|
32764
|
+
}
|
|
32765
|
+
}
|
|
32766
|
+
const UNENV_ALIAS_RE = new RegExp(
|
|
32767
|
+
`^(${Object.keys(aliasAbsolute).join("|")})$`
|
|
32768
|
+
);
|
|
32769
|
+
build.onResolve({ filter: UNENV_ALIAS_RE }, (args) => {
|
|
32770
|
+
const unresolvedAlias = alias[args.path] ?? "";
|
|
32771
|
+
if (args.kind === "require-call" && (unresolvedAlias.startsWith("unenv/npm/") || unresolvedAlias.startsWith("unenv/mock/")))
|
|
32772
|
+
return { path: args.path, namespace: REQUIRED_UNENV_ALIAS_NAMESPACE };
|
|
32773
|
+
return {
|
|
32774
|
+
path: aliasAbsolute[args.path] ?? args.path,
|
|
32775
|
+
external: external.includes(unresolvedAlias)
|
|
32776
|
+
};
|
|
32777
|
+
});
|
|
32778
|
+
build.onLoad(
|
|
32779
|
+
{ filter: /.*/, namespace: REQUIRED_UNENV_ALIAS_NAMESPACE },
|
|
32780
|
+
({ path }) => ({
|
|
32781
|
+
contents: `import * as esm from '${path}';
|
|
32782
|
+
module.exports = Object.entries(esm)
|
|
32783
|
+
.filter(([k,]) => k !== 'default')
|
|
32784
|
+
.reduce((cjs, [k, value]) =>
|
|
32785
|
+
Object.defineProperty(cjs, k, { value, enumerable: true }),
|
|
32786
|
+
"default" in esm ? esm.default : {}
|
|
32787
|
+
);`,
|
|
32788
|
+
loader: "js"
|
|
32789
|
+
})
|
|
32790
|
+
);
|
|
32791
|
+
}
|
|
32792
|
+
function handleNodeJSGlobals(build, inject, polyfill, virtualBase) {
|
|
32793
|
+
const UNENV_VIRTUAL_MODULE_RE = /_virtual_unenv_global_polyfill-(.+)$/;
|
|
32794
|
+
const prefix = resolvePath(virtualBase, "_virtual_unenv_global_polyfill-");
|
|
32795
|
+
const injectsByModule = /* @__PURE__ */ new Map();
|
|
32796
|
+
const virtualModulePathToSpecifier = /* @__PURE__ */ new Map();
|
|
32797
|
+
for (const [injectedName, moduleSpecifier] of Object.entries(inject)) {
|
|
32798
|
+
const [module, exportName, importName] = Array.isArray(moduleSpecifier) ? [
|
|
32799
|
+
moduleSpecifier[0] ?? "",
|
|
32800
|
+
moduleSpecifier[1] ?? "",
|
|
32801
|
+
moduleSpecifier[1] ?? ""
|
|
32802
|
+
] : [moduleSpecifier, "default", "defaultExport"];
|
|
32803
|
+
if (!injectsByModule.has(module)) {
|
|
32804
|
+
injectsByModule.set(module, []);
|
|
32805
|
+
virtualModulePathToSpecifier.set(
|
|
32806
|
+
prefix + module.replaceAll("/", "-"),
|
|
32807
|
+
module
|
|
32808
|
+
);
|
|
32809
|
+
}
|
|
32810
|
+
injectsByModule.get(module)?.push({ injectedName, exportName, importName });
|
|
32811
|
+
}
|
|
32812
|
+
build.initialOptions.inject = [
|
|
32813
|
+
...build.initialOptions.inject ?? [],
|
|
32814
|
+
...virtualModulePathToSpecifier.keys(),
|
|
32815
|
+
...polyfill.map((module) => require2.resolve(module))
|
|
32816
|
+
];
|
|
32817
|
+
build.onResolve({ filter: UNENV_VIRTUAL_MODULE_RE }, ({ path }) => ({
|
|
32818
|
+
path
|
|
32819
|
+
}));
|
|
32820
|
+
build.onLoad({ filter: UNENV_VIRTUAL_MODULE_RE }, ({ path }) => {
|
|
32821
|
+
const module = virtualModulePathToSpecifier.get(path);
|
|
32822
|
+
const injects = module === void 0 ? void 0 : injectsByModule.get(module);
|
|
32823
|
+
if (module === void 0 || injects === void 0)
|
|
32824
|
+
throw new Error(`Expected ${path} to be mapped to a module specifier`);
|
|
32825
|
+
const imports = injects.map(
|
|
32826
|
+
({ exportName, importName }) => importName === exportName ? exportName : `${exportName} as ${importName}`
|
|
32827
|
+
);
|
|
32828
|
+
return {
|
|
32829
|
+
resolveDir: MODULE_DIR,
|
|
32830
|
+
contents: `import { ${imports.join(", ")} } from "${module}";
|
|
32831
|
+
${injects.map(
|
|
32832
|
+
({ injectedName, importName }) => `globalThis.${injectedName} = ${importName};`
|
|
32833
|
+
).join("\n")}`
|
|
32834
|
+
};
|
|
32835
|
+
});
|
|
32836
|
+
}
|
|
32837
|
+
|
|
32838
|
+
// ../../packages/functions-bundler/src/deno-imports.ts
|
|
32839
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, realpathSync as realpathSync2 } from "fs";
|
|
32840
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join29, relative as relative8, sep as sep8 } from "path";
|
|
32841
|
+
|
|
32842
|
+
// ../../packages/functions-bundler/src/import-map.ts
|
|
32843
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, realpathSync } from "fs";
|
|
32844
|
+
import { basename as basename4, dirname as dirname8, join as join28, resolve as resolve12, sep as sep7 } from "path";
|
|
32845
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
32846
|
+
|
|
32847
|
+
// ../../packages/functions-bundler/src/printable.ts
|
|
32848
|
+
var UNPRINTABLE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
32849
|
+
function printable(text3) {
|
|
32850
|
+
return text3.replace(UNPRINTABLE, "?");
|
|
32851
|
+
}
|
|
32852
|
+
function causeLine(error51) {
|
|
32853
|
+
const text3 = error51 instanceof Error ? error51.message : String(error51);
|
|
32854
|
+
return printable(
|
|
32855
|
+
text3.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "unknown error"
|
|
32856
|
+
);
|
|
32857
|
+
}
|
|
32858
|
+
|
|
32859
|
+
// ../../packages/functions-bundler/src/import-map.ts
|
|
32860
|
+
var FILES = ["deno.json", "deno.jsonc", "import_map.json"];
|
|
32861
|
+
var DENO_CONFIGS = /* @__PURE__ */ new Set(["deno.json", "deno.jsonc"]);
|
|
32862
|
+
function expandBareKeys(imports) {
|
|
32863
|
+
const out = {};
|
|
32864
|
+
for (const [key, value] of Object.entries(imports)) {
|
|
32865
|
+
out[key] = value;
|
|
32866
|
+
if (key.endsWith("/") || Object.hasOwn(imports, `${key}/`)) continue;
|
|
32867
|
+
if (value.endsWith("/")) continue;
|
|
32868
|
+
const scheme = value.startsWith("jsr:") ? "jsr:" : value.startsWith("npm:") ? "npm:" : null;
|
|
32869
|
+
if (scheme === null) continue;
|
|
32870
|
+
const rest = value.slice(scheme.length).replace(/^\//, "");
|
|
32871
|
+
out[`${key}/`] = `${scheme}/${rest}/`;
|
|
32872
|
+
}
|
|
32873
|
+
return out;
|
|
32874
|
+
}
|
|
32875
|
+
function load(file2, label2) {
|
|
32876
|
+
let parsed;
|
|
32877
|
+
try {
|
|
32878
|
+
parsed = parseJsonc2(readFileSync8(file2, "utf8"));
|
|
32879
|
+
} catch {
|
|
32880
|
+
throw new FunctionBundleError(
|
|
32881
|
+
"VALIDATION_FAILED",
|
|
32882
|
+
BUNDLE_MESSAGES.importMapInvalid(label2)
|
|
32883
|
+
);
|
|
32884
|
+
}
|
|
32885
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
32886
|
+
const imports = parsed.imports;
|
|
32887
|
+
if (imports === void 0) return null;
|
|
32888
|
+
if (typeof imports !== "object" || imports === null || Array.isArray(imports) || Object.values(imports).some((target2) => typeof target2 !== "string"))
|
|
32889
|
+
throw new FunctionBundleError(
|
|
32890
|
+
"VALIDATION_FAILED",
|
|
32891
|
+
BUNDLE_MESSAGES.importMapShape(label2)
|
|
32892
|
+
);
|
|
32893
|
+
const written = imports;
|
|
32894
|
+
for (const [key, value] of Object.entries(written))
|
|
32895
|
+
if (key.endsWith("/") && !value.endsWith("/"))
|
|
32896
|
+
throw new FunctionBundleError(
|
|
32897
|
+
"VALIDATION_FAILED",
|
|
32898
|
+
printable(BUNDLE_MESSAGES.importMapPrefix(label2, key, value))
|
|
32899
|
+
);
|
|
32900
|
+
return {
|
|
32901
|
+
imports: DENO_CONFIGS.has(basename4(file2)) ? expandBareKeys(written) : written,
|
|
32902
|
+
base: dirname8(file2),
|
|
32903
|
+
label: label2
|
|
32904
|
+
};
|
|
32905
|
+
}
|
|
32906
|
+
function readImportMapFile(file2, label2) {
|
|
32907
|
+
if (!existsSync8(file2))
|
|
32908
|
+
throw new FunctionBundleError(
|
|
32909
|
+
"VALIDATION_FAILED",
|
|
32910
|
+
BUNDLE_MESSAGES.importMapMissing(label2)
|
|
32911
|
+
);
|
|
32912
|
+
return load(file2, label2);
|
|
32913
|
+
}
|
|
32914
|
+
function readImportMap(functionDir, fallback, labelOf) {
|
|
32915
|
+
for (const name of FILES) {
|
|
32916
|
+
const file2 = join28(functionDir, name);
|
|
32917
|
+
if (!existsSync8(file2)) continue;
|
|
32918
|
+
const map2 = load(file2, labelOf(file2));
|
|
32919
|
+
if (map2 !== null) return map2;
|
|
32920
|
+
}
|
|
32921
|
+
if (fallback === void 0 || fallback === null) return null;
|
|
32922
|
+
return typeof fallback === "string" ? readImportMapFile(fallback, labelOf(fallback)) : fallback;
|
|
32923
|
+
}
|
|
32924
|
+
function realPath(path) {
|
|
32925
|
+
try {
|
|
32926
|
+
return realpathSync.native(path);
|
|
32927
|
+
} catch {
|
|
32928
|
+
return path;
|
|
32929
|
+
}
|
|
32930
|
+
}
|
|
32931
|
+
var isPathLike = (text3) => text3.startsWith("./") || text3.startsWith("../") || text3.startsWith("/");
|
|
32932
|
+
function filePath(text3) {
|
|
32933
|
+
if (!text3.startsWith("file:")) return null;
|
|
32934
|
+
try {
|
|
32935
|
+
return fileURLToPath3(text3);
|
|
32936
|
+
} catch {
|
|
32937
|
+
return null;
|
|
32938
|
+
}
|
|
32939
|
+
}
|
|
32940
|
+
function realKey(path, trailing) {
|
|
32941
|
+
const real = realPath(path);
|
|
32942
|
+
return trailing && !real.endsWith(sep7) ? `${real}${sep7}` : real;
|
|
32943
|
+
}
|
|
32944
|
+
var normalized = /* @__PURE__ */ new WeakMap();
|
|
32945
|
+
function keysOf(map2) {
|
|
32946
|
+
const cached2 = normalized.get(map2);
|
|
32947
|
+
if (cached2 !== void 0) return cached2;
|
|
32948
|
+
const base2 = realPath(map2.base);
|
|
32949
|
+
const keys = /* @__PURE__ */ new Map();
|
|
32950
|
+
for (const [key, target2] of Object.entries(map2.imports)) {
|
|
32951
|
+
const trailing = key.endsWith("/");
|
|
32952
|
+
const file2 = filePath(key);
|
|
32953
|
+
const name = file2 !== null ? realKey(file2, trailing) : isPathLike(key) ? realKey(resolve12(base2, key), trailing) : key;
|
|
32954
|
+
if (!keys.has(name)) keys.set(name, target2);
|
|
32955
|
+
}
|
|
32956
|
+
normalized.set(map2, keys);
|
|
32957
|
+
return keys;
|
|
32958
|
+
}
|
|
32959
|
+
function mapSpecifier(map2, specifier, importerDir) {
|
|
32960
|
+
let lookup = specifier;
|
|
32961
|
+
const trailing = specifier.endsWith("/");
|
|
32962
|
+
const file2 = filePath(specifier);
|
|
32963
|
+
if (file2 !== null) lookup = realKey(file2, trailing);
|
|
32964
|
+
else if (specifier.startsWith("/"))
|
|
32965
|
+
lookup = realKey(resolve12(specifier), trailing);
|
|
32966
|
+
else if (isPathLike(specifier)) {
|
|
32967
|
+
if (importerDir === void 0) return null;
|
|
32968
|
+
lookup = realKey(resolve12(importerDir, specifier), trailing);
|
|
32969
|
+
}
|
|
32970
|
+
const keys = keysOf(map2);
|
|
32971
|
+
let target2 = keys.get(lookup);
|
|
32972
|
+
if (target2 === void 0) {
|
|
32973
|
+
let prefix;
|
|
32974
|
+
for (const key of keys.keys())
|
|
32975
|
+
if ((key.endsWith("/") || key.endsWith(sep7)) && lookup.startsWith(key) && (prefix === void 0 || key.length > prefix.length))
|
|
32976
|
+
prefix = key;
|
|
32977
|
+
if (prefix === void 0) return null;
|
|
32978
|
+
target2 = `${keys.get(prefix) ?? ""}${lookup.slice(prefix.length)}`;
|
|
32979
|
+
}
|
|
32980
|
+
if (target2.startsWith("./") || target2.startsWith("../"))
|
|
32981
|
+
return resolve12(map2.base, target2);
|
|
32982
|
+
return filePath(target2) ?? target2;
|
|
32983
|
+
}
|
|
32984
|
+
|
|
32985
|
+
// ../../packages/functions-bundler/src/range.ts
|
|
32986
|
+
function parse3(text3) {
|
|
32987
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(text3.trim());
|
|
32988
|
+
if (!match) return null;
|
|
32989
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
32990
|
+
}
|
|
32991
|
+
function partial2(text3) {
|
|
32992
|
+
if (!/^\d+(\.\d+){0,2}$/.test(text3)) return null;
|
|
32993
|
+
return text3.split(".").map(Number);
|
|
32994
|
+
}
|
|
32995
|
+
function compare2(a, b) {
|
|
32996
|
+
for (let i = 0; i < 3; i += 1) {
|
|
32997
|
+
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
|
32998
|
+
if (diff !== 0) return diff;
|
|
32999
|
+
}
|
|
33000
|
+
return 0;
|
|
33001
|
+
}
|
|
33002
|
+
var filled = (parts) => [
|
|
33003
|
+
parts[0] ?? 0,
|
|
33004
|
+
parts[1] ?? 0,
|
|
33005
|
+
parts[2] ?? 0
|
|
33006
|
+
];
|
|
33007
|
+
function inRange(versionText, rangeText) {
|
|
33008
|
+
const version3 = parse3(versionText);
|
|
33009
|
+
if (version3 === null) return null;
|
|
33010
|
+
const range = rangeText.trim();
|
|
33011
|
+
if (range === "" || range === "*" || range === "latest") return true;
|
|
33012
|
+
const operator = /^(\^|~|>=)?(.*)$/.exec(range);
|
|
33013
|
+
const op = operator?.[1] ?? "";
|
|
33014
|
+
const parts = partial2(operator?.[2] ?? "");
|
|
33015
|
+
if (parts === null) return null;
|
|
33016
|
+
const floor = filled(parts);
|
|
33017
|
+
if (op === ">=") return compare2(version3, floor) >= 0;
|
|
33018
|
+
if (op === "") return parts.every((part, i) => version3[i] === part);
|
|
33019
|
+
if (compare2(version3, floor) < 0) return false;
|
|
33020
|
+
if (op === "~")
|
|
33021
|
+
return version3[0] === floor[0] && (parts.length < 2 || version3[1] === floor[1]);
|
|
33022
|
+
if (floor[0] !== 0 || parts.length === 1) return version3[0] === floor[0];
|
|
33023
|
+
if (floor[1] !== 0 || parts.length === 2)
|
|
33024
|
+
return version3[0] === 0 && version3[1] === floor[1];
|
|
33025
|
+
return version3[0] === 0 && version3[1] === 0 && version3[2] === floor[2];
|
|
33026
|
+
}
|
|
33027
|
+
|
|
33028
|
+
// ../../packages/functions-bundler/src/deno-imports.ts
|
|
33029
|
+
var MISSING_NAMESPACE = "naro-missing-package";
|
|
33030
|
+
var MAPPED = "naroMapped";
|
|
33031
|
+
function parseNpm(text3) {
|
|
33032
|
+
const scoped = text3.startsWith("@");
|
|
33033
|
+
const slash = text3.indexOf("/", scoped ? text3.indexOf("/") + 1 : 0);
|
|
33034
|
+
const head = slash === -1 ? text3 : text3.slice(0, slash);
|
|
33035
|
+
const sub = slash === -1 ? "" : text3.slice(slash);
|
|
33036
|
+
const at = head.indexOf("@", scoped ? 1 : 0);
|
|
33037
|
+
return at === -1 ? { pkg: head, range: void 0, sub } : { pkg: head.slice(0, at), range: head.slice(at + 1) || void 0, sub };
|
|
33038
|
+
}
|
|
33039
|
+
var afterScheme = (specifier) => specifier.slice(4).replace(/^\//, "");
|
|
33040
|
+
var isPackageName = (pkg) => pkg.startsWith("@") ? /^@[^/]+\/[^/]+$/.test(pkg) : /^[^/]+$/.test(pkg);
|
|
33041
|
+
var refuse2 = (text3) => ({ errors: [{ text: printable(text3) }] });
|
|
33042
|
+
var installOf = (pkg, range) => range ? `${pkg}@${range}` : pkg;
|
|
33043
|
+
function realPath2(path) {
|
|
33044
|
+
try {
|
|
33045
|
+
return realpathSync2.native(path);
|
|
33046
|
+
} catch {
|
|
33047
|
+
return path;
|
|
33048
|
+
}
|
|
33049
|
+
}
|
|
33050
|
+
var fromRoot = (projectRoot, path) => relative8(projectRoot, path).split(sep8).join("/");
|
|
33051
|
+
function isInside(path, projectRoot) {
|
|
33052
|
+
const rel = relative8(projectRoot, path);
|
|
33053
|
+
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep8}`) && !isAbsolute5(rel);
|
|
33054
|
+
}
|
|
33055
|
+
function packageDir(from, projectRoot) {
|
|
33056
|
+
let current = from;
|
|
33057
|
+
while (isInside(current, projectRoot)) {
|
|
33058
|
+
const inNodeModules = fromRoot(projectRoot, current).split("/").includes("node_modules");
|
|
33059
|
+
if (!inNodeModules && existsSync9(join29(current, "package.json")))
|
|
33060
|
+
return fromRoot(projectRoot, current) || ".";
|
|
33061
|
+
const up = dirname9(current);
|
|
33062
|
+
if (up === current) break;
|
|
33063
|
+
current = up;
|
|
33064
|
+
}
|
|
33065
|
+
return ".";
|
|
33066
|
+
}
|
|
33067
|
+
function isInstalled(from, pkg) {
|
|
33068
|
+
if (pkg === "") return false;
|
|
33069
|
+
let current = from;
|
|
33070
|
+
for (; ; ) {
|
|
33071
|
+
if (existsSync9(join29(current, "node_modules", pkg))) return true;
|
|
33072
|
+
const up = dirname9(current);
|
|
33073
|
+
if (up === current) return false;
|
|
33074
|
+
current = up;
|
|
33075
|
+
}
|
|
33076
|
+
}
|
|
33077
|
+
var NODE_MODULES = /[\\/]node_modules[\\/]/;
|
|
33078
|
+
function installedVersion(resolved, pkg) {
|
|
33079
|
+
let current = dirname9(resolved);
|
|
33080
|
+
for (; ; ) {
|
|
33081
|
+
const file2 = join29(current, "package.json");
|
|
33082
|
+
if (existsSync9(file2)) {
|
|
33083
|
+
try {
|
|
33084
|
+
const manifest2 = JSON.parse(readFileSync9(file2, "utf8"));
|
|
33085
|
+
if (manifest2.name === pkg && typeof manifest2.version === "string")
|
|
33086
|
+
return manifest2.version;
|
|
33087
|
+
} catch {
|
|
33088
|
+
}
|
|
33089
|
+
}
|
|
33090
|
+
const up = dirname9(current);
|
|
33091
|
+
if (up === current) return null;
|
|
33092
|
+
current = up;
|
|
33093
|
+
}
|
|
33094
|
+
}
|
|
33095
|
+
var byCodePoint = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
33096
|
+
function missingPackagesMessage(unsorted) {
|
|
33097
|
+
return printable(installSentence(unsorted));
|
|
33098
|
+
}
|
|
33099
|
+
function installSentence(unsorted) {
|
|
33100
|
+
const missing = [...unsorted].sort(
|
|
33101
|
+
(a, b) => byCodePoint(`${a.kind}:${a.spec}`, `${b.kind}:${b.spec}`)
|
|
33102
|
+
);
|
|
33103
|
+
const [only] = missing;
|
|
33104
|
+
if (missing.length === 1 && only !== void 0)
|
|
33105
|
+
return only.kind === "npm" ? BUNDLE_MESSAGES.npmMissing(only.spec, only.install, only.dir) : BUNDLE_MESSAGES.jsrMissing(only.spec, only.install);
|
|
33106
|
+
return BUNDLE_MESSAGES.missingMany(
|
|
33107
|
+
missing.map((item) => `${item.kind}:${item.spec}`),
|
|
33108
|
+
// npm:zod@3 and npm:zod@3/x need one zod@3; zod@3 and zod@4 stay two.
|
|
33109
|
+
[...new Set(missing.map((item) => item.install))].sort(byCodePoint),
|
|
33110
|
+
missing.some((item) => item.kind === "jsr")
|
|
33111
|
+
);
|
|
33112
|
+
}
|
|
33113
|
+
var ESM_SH_NOT_NPM = /* @__PURE__ */ new Set(["stable", "gh", "jsr", "pr"]);
|
|
33114
|
+
var URL_IMPORT = /^https?:\/\//;
|
|
33115
|
+
var WASM = /\.wasm(\?.*)?$/;
|
|
33116
|
+
function urlRefusal(url2) {
|
|
33117
|
+
let parsed;
|
|
33118
|
+
try {
|
|
33119
|
+
parsed = new URL(url2);
|
|
33120
|
+
} catch {
|
|
33121
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33122
|
+
}
|
|
33123
|
+
if (parsed.hostname === "esm.sh") {
|
|
33124
|
+
const path = parsed.pathname.replace(/^\/v\d+(?=\/)/, "");
|
|
33125
|
+
const first = path.split("/")[1] ?? "";
|
|
33126
|
+
if (ESM_SH_NOT_NPM.has(first) || first.startsWith("*"))
|
|
33127
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33128
|
+
const match = /^\/((?:@[^/@]+\/)?[^/@]+)(?:@([^/?]+))?/.exec(path);
|
|
33129
|
+
if (match?.[1])
|
|
33130
|
+
return BUNDLE_MESSAGES.urlNpm(
|
|
33131
|
+
url2,
|
|
33132
|
+
`npm:${installOf(match[1], match[2])}`
|
|
33133
|
+
);
|
|
33134
|
+
}
|
|
33135
|
+
if (parsed.hostname === "deno.land") {
|
|
33136
|
+
const match = /^\/std(?:@[^/]+)?\/([^/]+)/.exec(parsed.pathname);
|
|
33137
|
+
if (match?.[1]) return BUNDLE_MESSAGES.urlStd(url2, match[1]);
|
|
33138
|
+
}
|
|
33139
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33140
|
+
}
|
|
33141
|
+
function denoImportsPlugin(options) {
|
|
33142
|
+
const { importMap, state } = options;
|
|
33143
|
+
const projectRoot = realPath2(options.projectRoot);
|
|
33144
|
+
const seenMissing = /* @__PURE__ */ new Set();
|
|
33145
|
+
return {
|
|
33146
|
+
name: "naro-deno-imports",
|
|
33147
|
+
setup(build) {
|
|
33148
|
+
build.onStart(() => {
|
|
33149
|
+
state.missing.length = 0;
|
|
33150
|
+
state.warnings.length = 0;
|
|
33151
|
+
seenMissing.clear();
|
|
33152
|
+
});
|
|
33153
|
+
const rewritten = async (kind, written, spec, target2, args) => {
|
|
33154
|
+
const result = await build.resolve(target2, {
|
|
33155
|
+
kind: args.kind,
|
|
33156
|
+
resolveDir: args.resolveDir,
|
|
33157
|
+
importer: args.importer,
|
|
33158
|
+
pluginData: { [MAPPED]: true }
|
|
33159
|
+
});
|
|
33160
|
+
const pkgName = kind === "npm" ? spec.pkg : target2.split("/").slice(0, 2).join("/");
|
|
33161
|
+
if (result.errors.length > 0) {
|
|
33162
|
+
if (isInstalled(args.resolveDir, pkgName))
|
|
33163
|
+
return { errors: result.errors };
|
|
33164
|
+
if (!seenMissing.has(`${kind}:${written}`)) {
|
|
33165
|
+
seenMissing.add(`${kind}:${written}`);
|
|
33166
|
+
state.missing.push({
|
|
33167
|
+
kind,
|
|
33168
|
+
spec: written,
|
|
33169
|
+
install: installOf(pkgName, spec.range),
|
|
33170
|
+
dir: packageDir(args.resolveDir, projectRoot)
|
|
33171
|
+
});
|
|
33172
|
+
}
|
|
33173
|
+
return { path: `${kind}:${written}`, namespace: MISSING_NAMESPACE };
|
|
33174
|
+
}
|
|
33175
|
+
if (spec.range) {
|
|
33176
|
+
const version3 = installedVersion(result.path, pkgName);
|
|
33177
|
+
if (version3 !== null && inRange(version3, spec.range) === false) {
|
|
33178
|
+
const warning = printable(
|
|
33179
|
+
BUNDLE_MESSAGES.outOfRange(
|
|
33180
|
+
pkgName,
|
|
33181
|
+
version3,
|
|
33182
|
+
spec.range,
|
|
33183
|
+
fromRoot(projectRoot, args.importer)
|
|
33184
|
+
)
|
|
33185
|
+
);
|
|
33186
|
+
if (!state.warnings.includes(warning)) state.warnings.push(warning);
|
|
33187
|
+
}
|
|
33188
|
+
}
|
|
33189
|
+
return {
|
|
33190
|
+
path: result.path,
|
|
33191
|
+
external: result.external,
|
|
33192
|
+
namespace: result.namespace,
|
|
33193
|
+
sideEffects: result.sideEffects
|
|
33194
|
+
};
|
|
33195
|
+
};
|
|
33196
|
+
build.onResolve({ filter: /.*/, namespace: "file" }, async (args) => {
|
|
33197
|
+
if (importMap === null || args.kind === "entry-point") return void 0;
|
|
33198
|
+
if (NODE_MODULES.test(args.importer)) return void 0;
|
|
33199
|
+
if (args.pluginData?.[MAPPED])
|
|
33200
|
+
return void 0;
|
|
33201
|
+
const mapped = mapSpecifier(importMap, args.path, args.resolveDir);
|
|
33202
|
+
if (mapped === null) return void 0;
|
|
33203
|
+
if (WASM.test(mapped)) return refuse2(BUNDLE_MESSAGES.wasm(args.path));
|
|
33204
|
+
if (URL_IMPORT.test(mapped)) return refuse2(urlRefusal(mapped));
|
|
33205
|
+
const result = await build.resolve(mapped, {
|
|
33206
|
+
kind: args.kind,
|
|
33207
|
+
resolveDir: args.resolveDir,
|
|
33208
|
+
importer: args.importer,
|
|
33209
|
+
pluginData: { [MAPPED]: true }
|
|
33210
|
+
});
|
|
33211
|
+
if (result.errors.length > 0) return { errors: result.errors };
|
|
33212
|
+
return {
|
|
33213
|
+
path: result.path,
|
|
33214
|
+
external: result.external,
|
|
33215
|
+
namespace: result.namespace,
|
|
33216
|
+
sideEffects: result.sideEffects
|
|
33217
|
+
};
|
|
33218
|
+
});
|
|
33219
|
+
build.onResolve({ filter: /^npm:/ }, async (args) => {
|
|
33220
|
+
const written = afterScheme(args.path);
|
|
33221
|
+
const spec = parseNpm(written);
|
|
33222
|
+
if (!isPackageName(spec.pkg))
|
|
33223
|
+
return refuse2(BUNDLE_MESSAGES.noPackageName(args.path, "npm:zod@3"));
|
|
33224
|
+
return rewritten("npm", written, spec, `${spec.pkg}${spec.sub}`, args);
|
|
33225
|
+
});
|
|
33226
|
+
build.onResolve({ filter: /^jsr:/ }, async (args) => {
|
|
33227
|
+
const written = afterScheme(args.path);
|
|
33228
|
+
const spec = parseNpm(written);
|
|
33229
|
+
if (spec.pkg !== "" && !spec.pkg.startsWith("@"))
|
|
33230
|
+
return refuse2(BUNDLE_MESSAGES.jsrNoScope(written));
|
|
33231
|
+
if (!isPackageName(spec.pkg))
|
|
33232
|
+
return refuse2(
|
|
33233
|
+
BUNDLE_MESSAGES.noPackageName(args.path, "jsr:@std/http@1")
|
|
33234
|
+
);
|
|
33235
|
+
const [scope, name] = spec.pkg.slice(1).split("/");
|
|
33236
|
+
return rewritten(
|
|
33237
|
+
"jsr",
|
|
33238
|
+
written,
|
|
33239
|
+
spec,
|
|
33240
|
+
`@jsr/${scope ?? ""}__${name ?? ""}${spec.sub}`,
|
|
33241
|
+
args
|
|
33242
|
+
);
|
|
33243
|
+
});
|
|
33244
|
+
build.onResolve(
|
|
33245
|
+
{ filter: URL_IMPORT },
|
|
33246
|
+
(args) => refuse2(urlRefusal(args.path))
|
|
33247
|
+
);
|
|
33248
|
+
build.onResolve(
|
|
33249
|
+
{ filter: WASM },
|
|
33250
|
+
(args) => refuse2(BUNDLE_MESSAGES.wasm(args.path))
|
|
33251
|
+
);
|
|
33252
|
+
build.onLoad({ filter: /.*/, namespace: MISSING_NAMESPACE }, () => ({
|
|
33253
|
+
contents: "module.exports = {};",
|
|
33254
|
+
loader: "js"
|
|
33255
|
+
}));
|
|
33256
|
+
build.onEnd(
|
|
33257
|
+
() => state.missing.length === 0 ? void 0 : { errors: [{ text: missingPackagesMessage(state.missing) }] }
|
|
33258
|
+
);
|
|
33259
|
+
}
|
|
33260
|
+
};
|
|
33261
|
+
}
|
|
33262
|
+
|
|
33263
|
+
// ../../packages/functions-bundler/src/bundle.ts
|
|
33264
|
+
import { createHash as createHash4 } from "crypto";
|
|
33265
|
+
import { join as join30, relative as relative9, sep as sep9 } from "path";
|
|
33266
|
+
import { gzipSync } from "zlib";
|
|
33267
|
+
|
|
33268
|
+
// ../../packages/functions-runtime/dist/index.js
|
|
33269
|
+
var NARO_SHIM_MODULE = "naro-shim.js";
|
|
33270
|
+
var NARO_USER_MODULE = "user.js";
|
|
33271
|
+
var NARO_BOOT_MODULE = "naro-boot.js";
|
|
33272
|
+
var NARO_ENTRY_SOURCE = '// src/naro-entry.ts\nimport "./naro-shim.js";\nimport * as user from "./user.js";\n\n// src/context.ts\nvar CONTEXT_HEADER = "x-naro-context";\nvar MAX_CONTEXT_LENGTH = 4096;\nvar BASE64URL = /^[A-Za-z0-9_-]+$/;\nvar TRACE_ID_RE = /^[A-Za-z0-9-]{1,64}$/;\nvar MAX_HOP = 1e3;\nvar ROLES = /* @__PURE__ */ new Set([\n "anon",\n "authenticated",\n "service"\n]);\nfunction fieldsOf(value) {\n if (typeof value !== "object" || value === null || Array.isArray(value))\n return null;\n return value;\n}\nfunction own(fields, key) {\n return Object.hasOwn(fields, key) ? fields[key] : void 0;\n}\nfunction nonEmptyText(value) {\n return typeof value === "string" && value !== "";\n}\nvar MAX_USER_ID_LENGTH = 256;\nfunction headerSafeId(value) {\n if (!nonEmptyText(value) || value.length > MAX_USER_ID_LENGTH) return false;\n if (value.trim() !== value) return false;\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code < 32 || code >= 127 && code <= 159) return false;\n if (code >= 55296 && code <= 57343) {\n const next = value.charCodeAt(index + 1);\n if (code >= 56320 || !(next >= 56320 && next <= 57343)) return false;\n index += 1;\n }\n }\n return true;\n}\nfunction userOf(value) {\n const fields = fieldsOf(value);\n if (fields === null) return null;\n const id = own(fields, "id");\n const email = own(fields, "email");\n const emailVerified = own(fields, "emailVerified");\n if (!headerSafeId(id)) return null;\n if (email !== null && typeof email !== "string") return null;\n if (typeof emailVerified !== "boolean") return null;\n return { id, email, emailVerified };\n}\nfunction contextOf(value, projectId, name) {\n const fields = fieldsOf(value);\n if (fields === null) return null;\n if (own(fields, "v") !== 1) return null;\n if (own(fields, "projectId") !== projectId) return null;\n if (own(fields, "function") !== name) return null;\n const traceId = own(fields, "traceId");\n if (typeof traceId !== "string" || !TRACE_ID_RE.test(traceId)) return null;\n const hop = own(fields, "hop");\n if (typeof hop !== "number" || !Number.isInteger(hop) || hop < 0 || hop > MAX_HOP)\n return null;\n const role = own(fields, "role");\n if (role !== null && !ROLES.has(role)) return null;\n const sentUser = own(fields, "user");\n let user2 = null;\n if (role === "authenticated") {\n user2 = userOf(sentUser);\n if (user2 === null) return null;\n } else if (sentUser !== null) return null;\n return {\n v: 1,\n projectId,\n function: name,\n role,\n user: user2,\n traceId,\n hop\n };\n}\nfunction bytesOf(value) {\n if (!BASE64URL.test(value) || value.length % 4 === 1) return null;\n const base64 = value.replace(/-/g, "+").replace(/_/g, "/");\n let binary;\n try {\n binary = atob(base64 + "=".repeat((4 - base64.length % 4) % 4));\n } catch {\n return null;\n }\n const again = btoa(binary).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");\n if (again !== value) return null;\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n}\nfunction decodeContext(value, bindings) {\n const projectId = bindings.NARO_PROJECT_ID;\n const name = bindings.NARO_FUNCTION_NAME;\n if (!nonEmptyText(projectId) || !nonEmptyText(name)) return null;\n if (typeof value !== "string" || value.length > MAX_CONTEXT_LENGTH)\n return null;\n const bytes = bytesOf(value);\n if (bytes === null) return null;\n let parsed;\n try {\n parsed = JSON.parse(\n new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes)\n );\n } catch {\n return null;\n }\n return contextOf(parsed, projectId, name);\n}\n\n// src/rejections.ts\nfunction loggedRejection(promise, name) {\n return Promise.resolve(promise).then(\n () => void 0,\n (error) => {\n console.error(`naro functions: ${name} waitUntil rejected`, error);\n }\n );\n}\n\n// src/respond.ts\nvar FUNCTION_EXPOSE_HEADERS = [\n "x-relay-error",\n "x-naro-trace-id",\n "retry-after",\n "x-retry-after"\n];\nvar WRAPPER_ERRORS = {\n FUNCTION_ERROR: "The function threw an error. See `naro functions logs {name}`.",\n FUNCTION_NO_HANDLER: "The function has no handler. Call Deno.serve(handler) or export default { fetch }.",\n FUNCTION_MISROUTED: "This function was reached without the Naro gateway."\n};\nfunction appendExposeHeaders(value) {\n const listed = (value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "");\n const seen = new Set(listed.map((part) => part.toLowerCase()));\n for (const name of FUNCTION_EXPOSE_HEADERS)\n if (!seen.has(name)) listed.push(name);\n return listed.join(", ");\n}\nfunction wrapperError(code, name, traceId) {\n const headers = new Headers({\n "access-control-allow-origin": "*",\n "access-control-expose-headers": appendExposeHeaders(null),\n "cache-control": "no-store"\n });\n if (traceId !== null) headers.set("x-naro-trace-id", traceId);\n return Response.json(\n {\n data: null,\n error: {\n code,\n // A function, not a string: `$&` in a name stays as it is.\n message: WRAPPER_ERRORS[code].replace("{name}", () => name)\n }\n },\n { status: 500, headers }\n );\n}\nfunction withFunctionHeaders(response, traceId) {\n if (response.status === 101) return response;\n const out = new Response(response.body, response);\n const headers = out.headers;\n headers.delete("x-relay-error");\n if (!headers.has("access-control-allow-origin"))\n headers.set("access-control-allow-origin", "*");\n headers.set(\n "access-control-expose-headers",\n appendExposeHeaders(headers.get("access-control-expose-headers"))\n );\n headers.set("x-naro-trace-id", traceId);\n return out;\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar CONTEXT_KEY = /* @__PURE__ */ Symbol.for("naro.functions.context");\nvar SHIM_FIRST = "The Naro functions shim must be imported before the wrapper.";\nfunction globals() {\n return globalThis;\n}\nfunction runtimeState() {\n const state2 = globals()[RUNTIME_KEY];\n if (state2 === void 0) throw new Error(SHIM_FIRST);\n return state2;\n}\nfunction callContexts() {\n const contexts2 = globals()[CONTEXT_KEY];\n if (contexts2 === void 0) throw new Error(SHIM_FIRST);\n return contexts2;\n}\n\n// src/naro-entry.ts\nvar state = runtimeState();\nvar contexts = callContexts();\nasync function serveWithInfo(registered, request) {\n let complete;\n const info = {\n remoteAddr: {\n transport: "tcp",\n hostname: request.headers.get("cf-connecting-ip") ?? "0.0.0.0",\n port: 0\n },\n completed: new Promise((resolve) => {\n complete = resolve;\n })\n };\n try {\n return await registered(request, info);\n } finally {\n complete();\n }\n}\nfunction chooseHandler() {\n const registered = state.handler;\n if (registered !== null)\n return {\n handler: async (request) => serveWithInfo(registered, request),\n onError: state.onError\n };\n const exported = user.default;\n if (typeof exported === "object" && exported !== null) {\n const fetchHandler = exported.fetch;\n if (typeof fetchHandler === "function")\n return {\n handler: async (request, env, ctx) => fetchHandler.call(exported, request, env, ctx),\n onError: null\n };\n }\n if (typeof exported === "function") {\n const prototype = exported.prototype;\n if (typeof prototype === "object" && prototype !== null && typeof prototype.fetch === "function")\n return {\n handler: async (request, env, ctx) => new exported(ctx, env).fetch(request),\n onError: null\n };\n return {\n handler: async (request, env, ctx) => exported(request, env, ctx),\n onError: null\n };\n }\n return null;\n}\nfunction callerRequest(request, context) {\n const headers = new Headers();\n for (const [name, value] of request.headers)\n if (!name.startsWith("x-naro-")) headers.append(name, value);\n if (context.role !== null) headers.set("x-naro-role", context.role);\n if (context.user !== null) headers.set("x-naro-user-id", context.user.id);\n headers.set("x-naro-trace-id", context.traceId);\n return new Request(request, { headers });\n}\nfunction logWaitUntilRejections(ctx, name) {\n const waitUntil = ctx.waitUntil.bind(ctx);\n try {\n Object.defineProperty(ctx, "waitUntil", {\n configurable: true,\n writable: true,\n value: (promise) => waitUntil(loggedRejection(promise, name))\n });\n } catch {\n }\n}\nfunction textOf(value) {\n return typeof value === "string" ? value : "";\n}\nfunction responseOf(value, what) {\n if (value instanceof Response) return value;\n throw new TypeError(`${what} did not return a Response.`);\n}\nasync function answerOf(chosen, name, request, env, ctx) {\n try {\n return responseOf(await chosen.handler(request, env, ctx), "The handler");\n } catch (error) {\n if (chosen.onError === null) {\n console.error(`naro functions: ${name} threw`, error);\n return null;\n }\n try {\n return responseOf(await chosen.onError(error), "onError");\n } catch (failed) {\n console.error(`naro functions: ${name} threw`, error, failed);\n return null;\n }\n }\n}\nasync function answerCall(request, env, ctx, seen) {\n const name = textOf(env.NARO_FUNCTION_NAME);\n const context = decodeContext(request.headers.get(CONTEXT_HEADER), env);\n if (context === null) return wrapperError("FUNCTION_MISROUTED", name, null);\n let forwarded;\n try {\n forwarded = callerRequest(request, context);\n } catch (error) {\n console.error(`naro functions: ${name} misrouted`, error);\n return wrapperError("FUNCTION_MISROUTED", name, null);\n }\n seen.traceId = context.traceId;\n const call = {\n role: context.role,\n user: context.user,\n traceId: context.traceId\n };\n contexts.set(forwarded, call);\n const chosen = chooseHandler();\n if (chosen === null)\n return wrapperError("FUNCTION_NO_HANDLER", name, context.traceId);\n logWaitUntilRejections(ctx, name);\n const answer = await state.requests.run(\n { ctx, hop: context.hop, traceId: context.traceId, call },\n () => answerOf(chosen, name, forwarded, env, ctx)\n );\n if (answer === null)\n return wrapperError("FUNCTION_ERROR", name, context.traceId);\n try {\n return withFunctionHeaders(answer, context.traceId);\n } catch (error) {\n console.error(\n `naro functions: ${name} answered with a response that cannot be sent`,\n error\n );\n return wrapperError("FUNCTION_ERROR", name, context.traceId);\n }\n}\nvar naro_entry_default = {\n async fetch(request, env, ctx) {\n const seen = { traceId: null };\n let answer;\n try {\n answer = await answerCall(request, env, ctx, seen);\n } catch (error) {\n return state.lastResort(error, seen.traceId);\n }\n if (state.isResponse(answer)) return answer;\n return state.lastResort(\n new TypeError("The wrapper\'s answer is not a Response."),\n seen.traceId\n );\n }\n};\nexport {\n naro_entry_default as default\n};\n';
|
|
33273
|
+
var NARO_SHIM_SOURCE = '// src/naro-shim.ts\nimport { AsyncLocalStorage } from "node:async_hooks";\nimport { env } from "cloudflare:workers";\n\n// src/hosts.ts\nfunction bareHost(host) {\n return host.toLowerCase().replace(/\\.+$/, "");\n}\nfunction isNaroHost(hostname, rootDomain2, naroUrl2) {\n const host = bareHost(hostname);\n if (host === "") return false;\n const root = rootDomain2.toLowerCase().replace(/^\\.+|\\.+$/g, "");\n if (root !== "" && host.endsWith(`.${root}`)) return true;\n if (naroUrl2 === "") return false;\n try {\n return bareHost(new URL(naroUrl2).hostname) === host;\n } catch {\n return false;\n }\n}\n\n// src/redact.ts\nvar MIN_REDACTED_LENGTH = 8;\nvar REDACTED = "[redacted]";\nfunction isSecretBinding(name) {\n return name === "NARO_SERVICE_KEY" || !name.startsWith("NARO_") && name !== "DB";\n}\nfunction secretValues(bindings2) {\n const values = /* @__PURE__ */ new Set();\n for (const [name, value] of Object.entries(bindings2)) {\n if (typeof value !== "string" || value.length < MIN_REDACTED_LENGTH)\n continue;\n if (!isSecretBinding(name)) continue;\n values.add(value);\n values.add(JSON.stringify(value).slice(1, -1));\n }\n return [...values].toSorted(\n (left, right) => right.length - left.length || (left < right ? -1 : left > right ? 1 : 0)\n );\n}\nfunction redactText(text, secrets2) {\n const spans = [];\n for (const secret of secrets2) {\n if (secret === "") continue;\n let last;\n for (let at = text.indexOf(secret); at !== -1; at = text.indexOf(secret, at + 1)) {\n if (last !== void 0 && at <= last[1]) last[1] = at + secret.length;\n else spans.push(last = [at, at + secret.length]);\n }\n }\n if (spans.length === 0) return text;\n const ordered = spans.toSorted((left, right) => left[0] - right[0]);\n let out = "";\n let kept = 0;\n let [start, end] = ordered[0];\n for (const [nextStart, nextEnd] of ordered) {\n if (nextStart <= end) {\n end = Math.max(end, nextEnd);\n continue;\n }\n out += text.slice(kept, start) + REDACTED;\n kept = end;\n start = nextStart;\n end = nextEnd;\n }\n return out + text.slice(kept, start) + REDACTED + text.slice(end);\n}\nfunction stringOf(value) {\n try {\n return String(value);\n } catch {\n return void 0;\n }\n}\nfunction withHead(stack, head) {\n if (stack === head || stack.startsWith(`${head}\n`)) return stack;\n const frames = stack.indexOf("\\n at ");\n return frames === -1 ? `${head}\n${stack}` : `${head}${stack.slice(frames)}`;\n}\nfunction redactError(error, secrets2) {\n const stack = typeof error.stack === "string" ? error.stack : `${error.name}: ${error.message}`;\n const head = stringOf(error);\n let json;\n try {\n json = JSON.stringify(error);\n } catch {\n json = void 0;\n }\n const cleanStack = redactText(stack, secrets2);\n const cleanHead = head === void 0 ? void 0 : redactText(head, secrets2);\n const cleanJson = json === void 0 ? void 0 : redactText(json, secrets2);\n if (cleanStack === stack && head !== void 0 && cleanHead === head && json !== void 0 && cleanJson === json)\n return error;\n const text = cleanHead === void 0 ? cleanStack : withHead(cleanStack, cleanHead);\n return cleanJson === void 0 || cleanJson === "{}" ? text : `${text}\n${cleanJson}`;\n}\nfunction isPlain(value) {\n if (Array.isArray(value)) return true;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\nfunction redactArg(arg, secrets2) {\n if (typeof arg === "string") return redactText(arg, secrets2);\n if (arg === null || typeof arg !== "object" && typeof arg !== "function")\n return arg;\n try {\n if (arg instanceof Error) return redactError(arg, secrets2);\n let json;\n try {\n json = JSON.stringify(arg);\n } catch {\n return redactText(String(arg), secrets2);\n }\n if (json !== void 0) {\n const clean2 = redactText(json, secrets2);\n if (clean2 !== json) return clean2;\n }\n if (isPlain(arg)) return arg;\n const text = stringOf(arg);\n if (text === void 0) return arg;\n const clean = redactText(text, secrets2);\n return clean === text ? arg : clean;\n } catch {\n return REDACTED;\n }\n}\nfunction redactArgs(args, secrets2) {\n if (secrets2.length === 0) return [...args];\n return args.map((arg) => redactArg(arg, secrets2));\n}\n\n// src/rejections.ts\nfunction loggedRejection(promise, name) {\n return Promise.resolve(promise).then(\n () => void 0,\n (error) => {\n console.error(`naro functions: ${name} waitUntil rejected`, error);\n }\n );\n}\n\n// src/respond.ts\nvar FUNCTION_EXPOSE_HEADERS = [\n "x-relay-error",\n "x-naro-trace-id",\n "retry-after",\n "x-retry-after"\n];\nvar WRAPPER_ERRORS = {\n FUNCTION_ERROR: "The function threw an error. See `naro functions logs {name}`.",\n FUNCTION_NO_HANDLER: "The function has no handler. Call Deno.serve(handler) or export default { fetch }.",\n FUNCTION_MISROUTED: "This function was reached without the Naro gateway."\n};\nfunction appendExposeHeaders(value) {\n const listed = (value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "");\n const seen = new Set(listed.map((part) => part.toLowerCase()));\n for (const name of FUNCTION_EXPOSE_HEADERS)\n if (!seen.has(name)) listed.push(name);\n return listed.join(", ");\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar CONTEXT_KEY = /* @__PURE__ */ Symbol.for("naro.functions.context");\n\n// src/naro-shim.ts\nvar bindings = env;\nvar globals = globalThis;\nvar functionName = typeof bindings.NARO_FUNCTION_NAME === "string" ? bindings.NARO_FUNCTION_NAME : "";\nvar secrets = secretValues(bindings);\nfor (const level of ["log", "info", "warn", "error", "debug"]) {\n const original = console[level].bind(console);\n console[level] = (...args) => {\n original(...redactArgs(args, secrets));\n };\n}\nvar logError = console.error;\nvar NativeResponse = Response;\nvar bareRecord = Object.create;\nvar failedBody = JSON.stringify({\n data: null,\n error: {\n code: "FUNCTION_ERROR",\n message: WRAPPER_ERRORS.FUNCTION_ERROR.replace(\n "{name}",\n () => functionName\n )\n }\n});\nvar exposedHeaders = appendExposeHeaders(null);\nfunction lastResort(error, traceId) {\n try {\n logError(`naro functions: ${functionName} threw`, error);\n } catch {\n }\n const headers = bareRecord(null);\n headers["content-type"] = "application/json";\n headers["access-control-allow-origin"] = "*";\n headers["access-control-expose-headers"] = exposedHeaders;\n headers["cache-control"] = "no-store";\n if (traceId !== null) headers["x-naro-trace-id"] = traceId;\n const init = bareRecord(null);\n init.status = 500;\n init.headers = headers;\n return new NativeResponse(failedBody, init);\n}\nvar reflectApply = Reflect.apply;\nvar responseStatus = Object.getOwnPropertyDescriptor(\n NativeResponse.prototype,\n "status"\n)?.get;\nfunction isResponse(value) {\n if (typeof value !== "object" || value === null) return false;\n try {\n if (responseStatus === void 0) return value instanceof NativeResponse;\n reflectApply(responseStatus, value, []);\n return true;\n } catch {\n return false;\n }\n}\nvar state = {\n handler: null,\n onError: null,\n requests: new AsyncLocalStorage(),\n lastResort,\n isResponse\n};\nglobals[RUNTIME_KEY] = state;\nglobals[CONTEXT_KEY] = /* @__PURE__ */ new WeakMap();\nvar rootDomain = typeof bindings.NARO_ROOT_DOMAIN === "string" ? bindings.NARO_ROOT_DOMAIN : "";\nvar naroUrl = typeof bindings.NARO_URL === "string" ? bindings.NARO_URL : "";\nvar originalFetch = globalThis.fetch.bind(globalThis);\nasync function naroFetch(input, init) {\n const store = state.requests.getStore();\n if (store === void 0) return originalFetch(input, init);\n let host;\n try {\n host = new URL(input instanceof Request ? input.url : String(input)).hostname;\n } catch {\n return originalFetch(input, init);\n }\n if (!isNaroHost(host, rootDomain, naroUrl)) return originalFetch(input, init);\n const request = new Request(input, init);\n const headers = new Headers(request.headers);\n headers.set("x-naro-fn-hop", String(store.hop + 1));\n headers.set("x-naro-trace-id", store.traceId);\n return originalFetch(new Request(request, { headers }));\n}\nglobalThis.fetch = naroFetch;\nvar addListener = EventTarget.prototype.addEventListener;\nvar removeListener = EventTarget.prototype.removeEventListener;\nvar standIns = /* @__PURE__ */ new WeakMap();\nfunction standInFor(socket, listener, store) {\n let listeners = standIns.get(socket);\n if (listeners === void 0)\n standIns.set(socket, listeners = /* @__PURE__ */ new WeakMap());\n let standIn = listeners.get(listener);\n if (standIn === void 0) {\n standIn = function(event) {\n state.requests.run(\n store,\n () => typeof listener === "function" ? Reflect.apply(listener, this, [event]) : listener.handleEvent(event)\n );\n };\n listeners.set(listener, standIn);\n }\n return standIn;\n}\nObject.defineProperties(WebSocket.prototype, {\n addEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const store = state.requests.getStore();\n const runs = store !== void 0 && listener !== null && (typeof listener === "function" || typeof listener === "object");\n return Reflect.apply(addListener, this, [\n type,\n runs ? standInFor(this, listener, store) : listener,\n ...rest\n ]);\n }\n },\n removeEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const standIn = typeof listener === "function" || typeof listener === "object" && listener !== null ? standIns.get(this)?.get(listener) : void 0;\n return Reflect.apply(removeListener, this, [\n type,\n standIn ?? listener,\n ...rest\n ]);\n }\n }\n});\nvar ON_EVENTS = ["onopen", "onmessage", "onclose", "onerror"];\nvar onHandlers = /* @__PURE__ */ new WeakMap();\nfunction nativeAccessor(target, name) {\n for (let at = target; at !== null; at = Object.getPrototypeOf(at)) {\n const descriptor = Object.getOwnPropertyDescriptor(at, name);\n if (descriptor === void 0) continue;\n return descriptor.get !== void 0 && descriptor.set !== void 0 ? { get: descriptor.get, set: descriptor.set } : void 0;\n }\n return void 0;\n}\nfor (const name of ON_EVENTS) {\n const native = nativeAccessor(WebSocket.prototype, name);\n Object.defineProperty(WebSocket.prototype, name, {\n configurable: true,\n enumerable: true,\n get() {\n const set = onHandlers.get(this)?.get(name);\n if (set === void 0)\n return native === void 0 ? void 0 : Reflect.apply(native.get, this, []);\n return state.requests.getStore() === void 0 ? set.standIn : set.handler;\n },\n set(handler) {\n const store = state.requests.getStore();\n const standIn = store !== void 0 && typeof handler === "function" ? standInFor(this, handler, store) : handler;\n let handlers = onHandlers.get(this);\n if (handlers === void 0) onHandlers.set(this, handlers = /* @__PURE__ */ new Map());\n handlers.set(name, { handler, standIn });\n if (native !== void 0) Reflect.apply(native.set, this, [standIn]);\n }\n });\n}\nfunction serve(first, second) {\n const options = typeof first === "function" ? {} : first ?? {};\n const handler = typeof first === "function" ? first : second ?? options.handler;\n if (typeof handler !== "function")\n throw new TypeError("Deno.serve needs a handler.");\n state.handler = handler;\n state.onError = typeof options.onError === "function" ? options.onError : null;\n return {\n finished: new Promise(() => {\n }),\n shutdown: () => Promise.resolve()\n };\n}\nfunction textBindings() {\n const out = {};\n for (const [name, value] of Object.entries(bindings))\n if (typeof value === "string") out[name] = value;\n return out;\n}\nvar build = Object.freeze({\n os: "linux",\n arch: "x86_64",\n target: "x86_64-unknown-linux-gnu",\n vendor: "unknown",\n env: "gnu"\n});\nvar version = Object.freeze({\n deno: "2.1.4",\n v8: "13.0.245.12",\n typescript: "5.6.2"\n});\nvar DENO_ERROR_NAMES = [\n "NotFound",\n "PermissionDenied",\n "ConnectionRefused",\n "ConnectionReset",\n "ConnectionAborted",\n "NotConnected",\n "AddrInUse",\n "AddrNotAvailable",\n "BrokenPipe",\n "AlreadyExists",\n "InvalidData",\n "TimedOut",\n "Interrupted",\n "WriteZero",\n "WouldBlock",\n "UnexpectedEof",\n "BadResource",\n "Http",\n "Busy",\n "NotSupported",\n "FilesystemLoop",\n "IsADirectory",\n "NetworkUnreachable",\n "NotADirectory",\n "NotCapable"\n];\nfunction denoError(name) {\n const made = { [name]: class extends Error {\n } }[name];\n Object.defineProperty(made.prototype, "name", {\n configurable: true,\n writable: true,\n value: name\n });\n return made;\n}\nvar errors = Object.freeze(\n Object.fromEntries(DENO_ERROR_NAMES.map((name) => [name, denoError(name)]))\n);\nvar deno = {\n serve,\n env: {\n get: (name) => {\n const value = bindings[name];\n return typeof value === "string" ? value : void 0;\n },\n has: (name) => typeof bindings[name] === "string",\n toObject: () => textBindings(),\n set: () => {\n throw new Error("Deno.env.set is not available on Naro Functions.");\n },\n delete: () => {\n throw new Error("Deno.env.delete is not available on Naro Functions.");\n }\n },\n build,\n version,\n errors\n};\nvar PROBES = /* @__PURE__ */ new Set(["then", "toJSON"]);\nglobals.Deno = new Proxy(deno, {\n get(target, property) {\n if (typeof property === "symbol" || PROBES.has(property)) return void 0;\n if (Object.hasOwn(target, property)) return target[property];\n return () => {\n throw new Error(`Deno.${property} is not available on Naro Functions.`);\n };\n }\n});\nglobals.EdgeRuntime = {\n waitUntil(promise) {\n const logged = loggedRejection(promise, functionName);\n state.requests.getStore()?.ctx.waitUntil(logged);\n return promise;\n }\n};\naddEventListener("unhandledrejection", (event) => {\n event.preventDefault();\n console.error(\n `naro functions: ${functionName} unhandled rejection`,\n event.reason\n );\n});\n';
|
|
33274
|
+
var NARO_BOOT_SOURCE = '// src/naro-boot.ts\nimport "./naro-shim.js";\nimport * as user from "./user.js";\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar SHIM_FIRST = "The Naro functions shim must be imported before the wrapper.";\nfunction globals() {\n return globalThis;\n}\nfunction runtimeState() {\n const state2 = globals()[RUNTIME_KEY];\n if (state2 === void 0) throw new Error(SHIM_FIRST);\n return state2;\n}\n\n// src/naro-boot.ts\nvar BOOT_PATH = "/__naro/boot";\nvar state = runtimeState();\nfunction hasHandler(exported) {\n if (state.handler !== null) return true;\n if (typeof exported === "function") return true;\n return typeof exported === "object" && exported !== null && typeof exported.fetch === "function";\n}\nvar naro_boot_default = {\n fetch(request) {\n if (new URL(request.url).pathname !== BOOT_PATH)\n return new Response("Not found", { status: 404 });\n return Response.json({ handler: hasHandler(user.default) });\n }\n};\nexport {\n naro_boot_default as default\n};\n';
|
|
33275
|
+
var FUNCTIONS_COMPATIBILITY_DATE = "2026-08-19";
|
|
33276
|
+
var FUNCTIONS_COMPATIBILITY_FLAGS = Object.freeze(["nodejs_compat", "global_fetch_strictly_public"]);
|
|
33277
|
+
|
|
33278
|
+
// ../../packages/functions-bundler/src/bundle.ts
|
|
33279
|
+
var NARO_JS_FUNCTIONS = /Could not resolve "naro-js\/functions"/;
|
|
33280
|
+
var runtimeGzip;
|
|
33281
|
+
function runtimeCompressedBytes() {
|
|
33282
|
+
runtimeGzip ??= gzipSync(NARO_ENTRY_SOURCE).byteLength + gzipSync(NARO_SHIM_SOURCE).byteLength;
|
|
33283
|
+
return runtimeGzip;
|
|
33284
|
+
}
|
|
33285
|
+
function relativeLabel(root, file2) {
|
|
33286
|
+
return relative9(root, file2).split(sep9).join("/");
|
|
33287
|
+
}
|
|
33288
|
+
function location(message) {
|
|
33289
|
+
return message.location ? `${message.location.file}:${String(message.location.line)}` : null;
|
|
33290
|
+
}
|
|
33291
|
+
function failure(name, error51, state) {
|
|
33292
|
+
if (error51 instanceof FunctionBundleError) return error51;
|
|
33293
|
+
if (state.missing.length > 0)
|
|
33294
|
+
return new FunctionBundleError(
|
|
33295
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33296
|
+
missingPackagesMessage(state.missing)
|
|
33297
|
+
);
|
|
33298
|
+
const errors = error51.errors ?? [];
|
|
33299
|
+
if (errors.length === 0)
|
|
33300
|
+
return new FunctionBundleError(
|
|
33301
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33302
|
+
BUNDLE_MESSAGES.otherBundle(
|
|
33303
|
+
name,
|
|
33304
|
+
error51 instanceof Error ? error51.message : String(error51),
|
|
33305
|
+
null
|
|
33306
|
+
)
|
|
33307
|
+
);
|
|
33308
|
+
const lines = errors.map((message) => {
|
|
33309
|
+
if (NARO_JS_FUNCTIONS.test(message.text))
|
|
33310
|
+
return BUNDLE_MESSAGES.naroJsMissing;
|
|
33311
|
+
if (message.pluginName === "naro-deno-imports") return message.text;
|
|
33312
|
+
return BUNDLE_MESSAGES.otherBundle(name, message.text, location(message));
|
|
33313
|
+
});
|
|
33314
|
+
return new FunctionBundleError(
|
|
33315
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33316
|
+
[...new Set(lines)].sort().join("\n")
|
|
33317
|
+
);
|
|
33318
|
+
}
|
|
33319
|
+
async function bundleFunction(source, folder, options = {}) {
|
|
33320
|
+
let build;
|
|
33321
|
+
try {
|
|
33322
|
+
({ build } = await import("esbuild"));
|
|
33323
|
+
} catch (error51) {
|
|
33324
|
+
throw new FunctionBundleError(
|
|
33325
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33326
|
+
BUNDLE_MESSAGES.esbuildMissing(source.name, causeLine(error51)),
|
|
33327
|
+
{ cause: error51 }
|
|
33328
|
+
);
|
|
33329
|
+
}
|
|
33330
|
+
const importMap = readImportMap(
|
|
33331
|
+
source.dir,
|
|
33332
|
+
options.importMap !== void 0 ? options.importMap : options.importMapFile,
|
|
33333
|
+
(file2) => relativeLabel(folder.root, file2)
|
|
33334
|
+
);
|
|
33335
|
+
const state = { missing: [], warnings: [] };
|
|
33336
|
+
let result;
|
|
33337
|
+
try {
|
|
33338
|
+
result = await build({
|
|
33339
|
+
absWorkingDir: folder.root,
|
|
33340
|
+
entryPoints: [source.entry],
|
|
33341
|
+
outfile: join30(source.dir, NARO_USER_MODULE),
|
|
33342
|
+
bundle: true,
|
|
33343
|
+
write: false,
|
|
33344
|
+
format: "esm",
|
|
33345
|
+
platform: "neutral",
|
|
33346
|
+
target: "es2024",
|
|
33347
|
+
conditions: ["workerd", "worker", "browser"],
|
|
33348
|
+
mainFields: ["browser", "module", "main"],
|
|
33349
|
+
external: ["cloudflare:*"],
|
|
33350
|
+
metafile: true,
|
|
33351
|
+
legalComments: "none",
|
|
33352
|
+
logLevel: "silent",
|
|
33353
|
+
plugins: [
|
|
33354
|
+
denoImportsPlugin({ importMap, state, projectRoot: folder.root }),
|
|
33355
|
+
nodeCompatPlugin({
|
|
33356
|
+
compatibilityDate: FUNCTIONS_COMPATIBILITY_DATE,
|
|
33357
|
+
compatibilityFlags: FUNCTIONS_COMPATIBILITY_FLAGS,
|
|
33358
|
+
virtualBase: folder.root
|
|
33359
|
+
})
|
|
33360
|
+
]
|
|
33361
|
+
});
|
|
33362
|
+
} catch (error51) {
|
|
33363
|
+
throw failure(source.name, error51, state);
|
|
33364
|
+
}
|
|
33365
|
+
if (state.missing.length > 0)
|
|
33366
|
+
throw new FunctionBundleError(
|
|
33367
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33368
|
+
missingPackagesMessage(state.missing)
|
|
33369
|
+
);
|
|
33370
|
+
const [output] = result.outputFiles;
|
|
33371
|
+
if (output === void 0 || result.outputFiles.length !== 1)
|
|
33372
|
+
throw new FunctionBundleError(
|
|
33373
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33374
|
+
BUNDLE_MESSAGES.otherBundle(
|
|
33375
|
+
source.name,
|
|
33376
|
+
"esbuild did not produce exactly one file",
|
|
33377
|
+
null
|
|
33378
|
+
)
|
|
33379
|
+
);
|
|
33380
|
+
const bytes2 = output.contents.byteLength;
|
|
33381
|
+
if (bytes2 > (options.maxBytes ?? FUNCTION_BUNDLE_MAX_BYTES)) {
|
|
33382
|
+
const inputs = Object.values(result.metafile.outputs)[0]?.inputs ?? {};
|
|
33383
|
+
const biggest = Object.entries(inputs).sort(([, a], [, b]) => b.bytesInOutput - a.bytesInOutput).slice(0, 5).map(
|
|
33384
|
+
([path, input]) => `${path} (${input.bytesInOutput.toLocaleString("en-US")} bytes)`
|
|
33385
|
+
).join(", ");
|
|
33386
|
+
throw new FunctionBundleError(
|
|
33387
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33388
|
+
BUNDLE_MESSAGES.tooLarge(source.name, bytes2, biggest)
|
|
33389
|
+
);
|
|
33390
|
+
}
|
|
33391
|
+
const compressedBytes = gzipSync(output.contents).byteLength + runtimeCompressedBytes();
|
|
33392
|
+
if (compressedBytes > (options.maxCompressedBytes ?? FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES))
|
|
33393
|
+
throw new FunctionBundleError(
|
|
33394
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33395
|
+
BUNDLE_MESSAGES.tooLargeCompressed(source.name, compressedBytes)
|
|
33396
|
+
);
|
|
33397
|
+
return {
|
|
33398
|
+
name: source.name,
|
|
33399
|
+
code: output.text,
|
|
33400
|
+
contents: output.contents,
|
|
33401
|
+
bytes: bytes2,
|
|
33402
|
+
compressedBytes,
|
|
33403
|
+
sha256: createHash4("sha256").update(output.contents).digest("hex"),
|
|
33404
|
+
warnings: [...state.warnings]
|
|
33405
|
+
};
|
|
33406
|
+
}
|
|
33407
|
+
|
|
33408
|
+
// ../../packages/functions-bundler/src/boot.ts
|
|
33409
|
+
var BOOT_PATH = "/__naro/boot";
|
|
33410
|
+
var BOOT_TIMEOUT_MS = 3e4;
|
|
33411
|
+
var BOOT_SECRET_PLACEHOLDER = "naro-boot-check-placeholder";
|
|
33412
|
+
var text2 = (value) => ({ type: "text", value });
|
|
33413
|
+
function bindings(name, secretNames) {
|
|
33414
|
+
return {
|
|
33415
|
+
// First, so a name that is also a fixed binding never replaces it.
|
|
33416
|
+
...Object.fromEntries(
|
|
33417
|
+
secretNames.map((secret) => [secret, text2(BOOT_SECRET_PLACEHOLDER)])
|
|
33418
|
+
),
|
|
33419
|
+
NARO_PROJECT_ID: text2("00000000-0000-4000-8000-000000000000"),
|
|
33420
|
+
NARO_FUNCTION_NAME: text2(name),
|
|
33421
|
+
NARO_ROOT_DOMAIN: text2("naro.sh"),
|
|
33422
|
+
NARO_URL: text2("https://boot-check.naro.sh"),
|
|
33423
|
+
NARO_ANON_KEY: text2("naro_pk_boot_check"),
|
|
33424
|
+
NARO_SERVICE_KEY: text2("naro_sk_boot_check"),
|
|
33425
|
+
DB: { type: "d1", id: "boot-check" }
|
|
33426
|
+
};
|
|
33427
|
+
}
|
|
33428
|
+
var WORKERD_SERVICE_HEAD = /^service core:user:naro-boot: /;
|
|
33429
|
+
function bootErrorLine(logs, error51) {
|
|
33430
|
+
const logged = logs.find((line2) => line2.includes("Uncaught")) ?? logs.find((line2) => line2.includes("Error"));
|
|
33431
|
+
const raw = logged ?? (error51 instanceof Error ? error51.message : String(error51));
|
|
33432
|
+
const line = raw.split("\n").map((part) => part.trim()).find((part) => part.length > 0) ?? "unknown error";
|
|
33433
|
+
return printable(line.replace(WORKERD_SERVICE_HEAD, ""));
|
|
33434
|
+
}
|
|
33435
|
+
async function bootCheck(name, code, options = {}) {
|
|
33436
|
+
const timeoutMs = options.timeoutMs ?? BOOT_TIMEOUT_MS;
|
|
33437
|
+
let Miniflare;
|
|
33438
|
+
try {
|
|
33439
|
+
({ Miniflare } = await import("miniflare"));
|
|
33440
|
+
} catch (error51) {
|
|
33441
|
+
throw new FunctionBundleError(
|
|
33442
|
+
"FUNCTION_BOOT_FAILED",
|
|
33443
|
+
BUNDLE_MESSAGES.miniflareMissing(name, causeLine(error51)),
|
|
33444
|
+
{ cause: error51 }
|
|
33445
|
+
);
|
|
33446
|
+
}
|
|
33447
|
+
const logs = [];
|
|
33448
|
+
let mf;
|
|
33449
|
+
let timer;
|
|
33450
|
+
try {
|
|
33451
|
+
mf = new Miniflare({
|
|
33452
|
+
handleStructuredLogs: (log2) => logs.push(log2.message),
|
|
33453
|
+
workers: [
|
|
33454
|
+
{
|
|
33455
|
+
config: {
|
|
33456
|
+
// No `type`: the Miniflare wrangler pins (5.20261006) refuses
|
|
33457
|
+
// the key the functions-runtime tests' older one still takes.
|
|
33458
|
+
name: "naro-boot",
|
|
33459
|
+
compatibilityDate: FUNCTIONS_COMPATIBILITY_DATE,
|
|
33460
|
+
compatibilityFlags: [...FUNCTIONS_COMPATIBILITY_FLAGS],
|
|
33461
|
+
manifest: {
|
|
33462
|
+
mainModule: NARO_BOOT_MODULE,
|
|
33463
|
+
modules: {
|
|
33464
|
+
[NARO_BOOT_MODULE]: { type: "esm", contents: NARO_BOOT_SOURCE },
|
|
33465
|
+
[NARO_SHIM_MODULE]: { type: "esm", contents: NARO_SHIM_SOURCE },
|
|
33466
|
+
[NARO_USER_MODULE]: { type: "esm", contents: code }
|
|
33467
|
+
}
|
|
33468
|
+
},
|
|
33469
|
+
env: bindings(name, options.secretNames ?? [])
|
|
33470
|
+
}
|
|
33471
|
+
}
|
|
33472
|
+
]
|
|
33473
|
+
});
|
|
33474
|
+
const started = mf;
|
|
33475
|
+
const answer = async () => {
|
|
33476
|
+
await started.ready;
|
|
33477
|
+
const worker = await started.getWorker("naro-boot");
|
|
33478
|
+
const response2 = await worker.fetch(`http://boot.local${BOOT_PATH}`);
|
|
33479
|
+
const body2 = await response2.json().catch(() => null);
|
|
33480
|
+
return { response: response2, body: body2 };
|
|
33481
|
+
};
|
|
33482
|
+
const timedOut = new Promise((_, reject) => {
|
|
33483
|
+
timer = setTimeout(() => {
|
|
33484
|
+
reject(
|
|
33485
|
+
new FunctionBundleError(
|
|
33486
|
+
"FUNCTION_BOOT_FAILED",
|
|
33487
|
+
BUNDLE_MESSAGES.bootTimeout(name, timeoutMs)
|
|
33488
|
+
)
|
|
33489
|
+
);
|
|
33490
|
+
}, timeoutMs);
|
|
33491
|
+
});
|
|
33492
|
+
const { response, body } = await Promise.race([answer(), timedOut]);
|
|
33493
|
+
if (response.status !== 200 || body === null)
|
|
33494
|
+
throw new Error(
|
|
33495
|
+
logs[0] ?? `the boot request answered ${String(response.status)}`
|
|
33496
|
+
);
|
|
33497
|
+
if (body.handler !== true)
|
|
33498
|
+
throw new FunctionBundleError(
|
|
33499
|
+
"FUNCTION_BOOT_FAILED",
|
|
33500
|
+
BUNDLE_MESSAGES.noHandler(name)
|
|
33501
|
+
);
|
|
33502
|
+
} catch (error51) {
|
|
33503
|
+
if (error51 instanceof FunctionBundleError) throw error51;
|
|
33504
|
+
throw new FunctionBundleError(
|
|
33505
|
+
"FUNCTION_BOOT_FAILED",
|
|
33506
|
+
BUNDLE_MESSAGES.failedToStart(name, bootErrorLine(logs, error51))
|
|
33507
|
+
);
|
|
33508
|
+
} finally {
|
|
33509
|
+
clearTimeout(timer);
|
|
33510
|
+
await mf?.dispose().catch(() => void 0);
|
|
33511
|
+
}
|
|
33512
|
+
}
|
|
33513
|
+
|
|
33514
|
+
// src/commands/migration.ts
|
|
33515
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
33516
|
+
import { join as join31, relative as relative10 } from "path";
|
|
33517
|
+
|
|
33518
|
+
// src/data-environment.ts
|
|
33519
|
+
var DATA_ENV_MESSAGES = {
|
|
33520
|
+
name: "--env must be production or an environment name.",
|
|
33521
|
+
preview: "--env preview has no data of its own: preview deployments use production's. Drop --env, or name an environment.",
|
|
33522
|
+
projectWide: (command) => `--env is not an option of ${command}: turning the backend on or off applies to every environment.`
|
|
33523
|
+
};
|
|
33524
|
+
function dataEnvironment(flags) {
|
|
33525
|
+
const value = flags.env;
|
|
33526
|
+
if (value === void 0 || value === "production") return null;
|
|
33527
|
+
if (value === "preview")
|
|
33528
|
+
throw new NaroError("VALIDATION_FAILED", DATA_ENV_MESSAGES.preview);
|
|
33529
|
+
if (environmentSlugProblem(value) !== null)
|
|
33530
|
+
throw new NaroError("VALIDATION_FAILED", DATA_ENV_MESSAGES.name);
|
|
33531
|
+
return value;
|
|
33532
|
+
}
|
|
33533
|
+
async function dataClient(context) {
|
|
33534
|
+
return (await context.client()).inEnvironment(dataEnvironment(context.flags));
|
|
33535
|
+
}
|
|
33536
|
+
function databaseName(environment) {
|
|
33537
|
+
return environment === null ? "the project database" : `the ${environment} environment's database`;
|
|
33538
|
+
}
|
|
33539
|
+
function enableBackendCommand(environment) {
|
|
33540
|
+
return environment === null ? "naro backend enable" : `naro environments provision ${environment}`;
|
|
33541
|
+
}
|
|
33542
|
+
function envFlag(environment) {
|
|
33543
|
+
return environment === null ? "" : ` --env ${environment}`;
|
|
33544
|
+
}
|
|
33545
|
+
function forEnvironment(text3, environment) {
|
|
33546
|
+
return environmentGuidance(text3, environment);
|
|
33547
|
+
}
|
|
33548
|
+
|
|
33549
|
+
// src/prompt.ts
|
|
33550
|
+
import { createInterface as createInterface2 } from "readline";
|
|
33551
|
+
import { Writable } from "stream";
|
|
33552
|
+
function confirmHint(defaultYes) {
|
|
33553
|
+
return defaultYes ? "(Y/n)" : "(y/N)";
|
|
33554
|
+
}
|
|
33555
|
+
function readConfirmAnswer(answer, defaultYes) {
|
|
33556
|
+
const normalized2 = answer.trim().toLowerCase();
|
|
33557
|
+
if (normalized2 === "") return defaultYes;
|
|
33558
|
+
if (normalized2 === "y" || normalized2 === "yes") return true;
|
|
33559
|
+
if (!defaultYes) return false;
|
|
33560
|
+
if (normalized2 === "n" || normalized2 === "no") return false;
|
|
33561
|
+
return null;
|
|
33562
|
+
}
|
|
33563
|
+
function isInteractive() {
|
|
33564
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
33565
|
+
}
|
|
33566
|
+
async function readStdin() {
|
|
33567
|
+
let data = "";
|
|
33568
|
+
for await (const chunk of process.stdin) data += String(chunk);
|
|
33569
|
+
return data.replace(/\r?\n$/, "");
|
|
33570
|
+
}
|
|
33571
|
+
var TtyPrompter = class {
|
|
33572
|
+
async ask(question) {
|
|
33573
|
+
const rl = createInterface2({
|
|
33574
|
+
input: process.stdin,
|
|
33575
|
+
output: process.stdout
|
|
33576
|
+
});
|
|
33577
|
+
try {
|
|
33578
|
+
return await new Promise(
|
|
33579
|
+
(resolve15) => rl.question(question, resolve15)
|
|
33580
|
+
);
|
|
33581
|
+
} finally {
|
|
33582
|
+
rl.close();
|
|
33583
|
+
}
|
|
33584
|
+
}
|
|
33585
|
+
async askHidden(question) {
|
|
33586
|
+
let muted = false;
|
|
33587
|
+
const output = new Writable({
|
|
33588
|
+
write(chunk, _enc, done) {
|
|
33589
|
+
if (!muted) process.stdout.write(chunk);
|
|
33590
|
+
done();
|
|
33591
|
+
}
|
|
33592
|
+
});
|
|
33593
|
+
const rl = createInterface2({
|
|
33594
|
+
input: process.stdin,
|
|
33595
|
+
output,
|
|
33596
|
+
terminal: true
|
|
33597
|
+
});
|
|
33598
|
+
try {
|
|
33599
|
+
const answer = new Promise(
|
|
33600
|
+
(resolve15) => rl.question(question, resolve15)
|
|
33601
|
+
);
|
|
33602
|
+
muted = true;
|
|
33603
|
+
const value = await answer;
|
|
33604
|
+
process.stdout.write("\n");
|
|
33605
|
+
return value;
|
|
33606
|
+
} finally {
|
|
33607
|
+
rl.close();
|
|
33608
|
+
}
|
|
33609
|
+
}
|
|
33610
|
+
async confirm(question, options = {}) {
|
|
33611
|
+
const defaultYes = options.defaultYes ?? false;
|
|
33612
|
+
for (; ; ) {
|
|
33613
|
+
const read = readConfirmAnswer(
|
|
33614
|
+
await this.ask(`${question} ${confirmHint(defaultYes)} `),
|
|
33615
|
+
defaultYes
|
|
33616
|
+
);
|
|
33617
|
+
if (read !== null) return read;
|
|
33618
|
+
}
|
|
33619
|
+
}
|
|
33620
|
+
async select(question, options) {
|
|
33621
|
+
process.stdout.write(`${question}
|
|
33622
|
+
`);
|
|
33623
|
+
options.forEach((option, index) => {
|
|
33624
|
+
process.stdout.write(` ${String(index + 1)}. ${option}
|
|
33625
|
+
`);
|
|
33626
|
+
});
|
|
33627
|
+
for (; ; ) {
|
|
33628
|
+
const answer = Number.parseInt(
|
|
33629
|
+
(await this.ask(`Choose 1-${String(options.length)}: `)).trim(),
|
|
33630
|
+
10
|
|
33631
|
+
);
|
|
33632
|
+
if (Number.isInteger(answer) && answer >= 1 && answer <= options.length)
|
|
33633
|
+
return answer - 1;
|
|
33634
|
+
}
|
|
33635
|
+
}
|
|
33636
|
+
};
|
|
33637
|
+
var NonInteractivePrompter = class {
|
|
33638
|
+
constructor(assumeYes) {
|
|
33639
|
+
this.assumeYes = assumeYes;
|
|
33640
|
+
}
|
|
33641
|
+
refuse(question) {
|
|
33642
|
+
throw new NaroError(
|
|
33643
|
+
"VALIDATION_FAILED",
|
|
33644
|
+
`Interactive input required (${question.trim()}). Pass the value via flags/stdin or run without --json in a terminal.`
|
|
33645
|
+
);
|
|
33646
|
+
}
|
|
33647
|
+
async ask(question) {
|
|
33648
|
+
this.refuse(question);
|
|
33649
|
+
}
|
|
33650
|
+
async askHidden(question) {
|
|
33651
|
+
this.refuse(question);
|
|
33652
|
+
}
|
|
33653
|
+
async confirm() {
|
|
33654
|
+
return this.assumeYes;
|
|
33655
|
+
}
|
|
33656
|
+
async select(question) {
|
|
33657
|
+
this.refuse(question);
|
|
33658
|
+
}
|
|
33659
|
+
};
|
|
33660
|
+
function createPrompter(options) {
|
|
33661
|
+
if (options.yes || options.json || !isInteractive())
|
|
33662
|
+
return new NonInteractivePrompter(options.yes);
|
|
33663
|
+
return new TtyPrompter();
|
|
33664
|
+
}
|
|
33665
|
+
|
|
33666
|
+
// src/commands/confirm.ts
|
|
33667
|
+
function cannotAsk(context) {
|
|
33668
|
+
return context.flags.json || !isInteractive();
|
|
33669
|
+
}
|
|
33670
|
+
async function confirmOrAbort(context, question) {
|
|
33671
|
+
if (context.flags.yes) return;
|
|
33672
|
+
if (cannotAsk(context)) {
|
|
33673
|
+
throw new NaroError(
|
|
33674
|
+
"CONFIRMATION_REQUIRED",
|
|
33675
|
+
`${question} Re-run with --yes to confirm.`
|
|
33676
|
+
);
|
|
33677
|
+
}
|
|
33678
|
+
const confirmed = await context.prompter.confirm(question);
|
|
33679
|
+
if (!confirmed) throw new NaroError("CANCELED", "Canceled.");
|
|
33680
|
+
}
|
|
33681
|
+
|
|
33682
|
+
// src/commands/migration.ts
|
|
33683
|
+
var processStdin = {
|
|
33684
|
+
stdinIsTty: () => process.stdin.isTTY === true,
|
|
33685
|
+
readStdin
|
|
33686
|
+
};
|
|
33687
|
+
function migrationsDir(context) {
|
|
33688
|
+
return resolveMigrationsDir({
|
|
33689
|
+
baseDir: context.cwd,
|
|
33690
|
+
rootDirectory: context.flags.root,
|
|
33691
|
+
dir: context.flags.dir
|
|
33692
|
+
});
|
|
33693
|
+
}
|
|
33694
|
+
function relativeToCwd(context, path) {
|
|
33695
|
+
const rel = relative10(context.cwd, path);
|
|
33696
|
+
return rel === "" ? "." : rel;
|
|
33697
|
+
}
|
|
33698
|
+
async function appliedMigrations(client, projectId) {
|
|
33699
|
+
try {
|
|
33700
|
+
return await client.db.migrations(projectId);
|
|
33701
|
+
} catch (error51) {
|
|
33702
|
+
if (isNaroError(error51) && error51.code === "DATABASE_NOT_READY") return [];
|
|
33703
|
+
throw error51;
|
|
33704
|
+
}
|
|
33705
|
+
}
|
|
33706
|
+
var UNPRINTABLE2 = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
33707
|
+
function printable2(text3) {
|
|
33708
|
+
return text3.replace(UNPRINTABLE2, "?");
|
|
33709
|
+
}
|
|
33710
|
+
function isPrintable(text3) {
|
|
33711
|
+
return printable2(text3) === text3;
|
|
33712
|
+
}
|
|
33713
|
+
var UNSHOWN_WARNING_LINE = "(a line of this warning has characters the terminal cannot show; run with --json to see it exactly)";
|
|
33714
|
+
function warningLines(message) {
|
|
33715
|
+
return message.split("\n").map(
|
|
33716
|
+
(line, index) => index === 0 ? `\u26A0 ${printable2(line)}` : ` ${isPrintable(line) ? line : UNSHOWN_WARNING_LINE}`
|
|
33717
|
+
);
|
|
33718
|
+
}
|
|
33719
|
+
function policyProblems(dir, names) {
|
|
33720
|
+
return names.flatMap((name) => {
|
|
33721
|
+
const split2 = splitStatements(readFileSync10(join31(dir, name), "utf8"));
|
|
33722
|
+
if (!split2.ok) return [];
|
|
33723
|
+
const planned = planPolicyMigration(
|
|
33724
|
+
split2.statements.map((statement2) => statement2.text)
|
|
33725
|
+
);
|
|
33726
|
+
return planned.ok ? [] : [{ migration: name, message: planned.message }];
|
|
33727
|
+
});
|
|
33728
|
+
}
|
|
33729
|
+
function warningLine(warning) {
|
|
33730
|
+
if (warning === "out-of-order")
|
|
33731
|
+
return " \u26A0 older than an applied migration \u2014 db push will still apply it";
|
|
33732
|
+
if (warning === "ledger-name-missing-extension")
|
|
33733
|
+
return " \u26A0 recorded without .sql in the ledger";
|
|
33734
|
+
return null;
|
|
33735
|
+
}
|
|
33736
|
+
async function runMigrationNew(context, deps = processStdin) {
|
|
33737
|
+
const [name] = context.positionalOrThrow(
|
|
33738
|
+
1,
|
|
33739
|
+
"Usage: naro migration new <name>"
|
|
33740
|
+
);
|
|
33741
|
+
const dir = migrationsDir(context);
|
|
33742
|
+
const fileName = nextMigrationFileName(
|
|
33743
|
+
listMigrationFiles(dir),
|
|
33744
|
+
name
|
|
33745
|
+
);
|
|
33746
|
+
mkdirSync3(dir, { recursive: true });
|
|
33747
|
+
const piped = !deps.stdinIsTty() ? await deps.readStdin() : "";
|
|
33748
|
+
const fromStdin = piped.length > 0;
|
|
33749
|
+
const body = fromStdin ? piped.endsWith("\n") ? piped : `${piped}
|
|
33750
|
+
` : `-- Migration: ${fileName}
|
|
33751
|
+
`;
|
|
33752
|
+
const path = join31(dir, fileName);
|
|
33753
|
+
writeFileSync3(path, body, { flag: "wx" });
|
|
33754
|
+
const relPath = relativeToCwd(context, path);
|
|
33755
|
+
context.output.result({ file: fileName, path: relPath, fromStdin }, [
|
|
33756
|
+
`\u2705 Created ${relPath}`
|
|
33757
|
+
]);
|
|
33758
|
+
}
|
|
33759
|
+
async function runMigrationList(context) {
|
|
33760
|
+
const client = await dataClient(context);
|
|
33761
|
+
const projectId = await context.resolveProjectId();
|
|
33762
|
+
const dir = migrationsDir(context);
|
|
33763
|
+
const local = listMigrationFiles(dir);
|
|
33764
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33765
|
+
const migrations = compareMigrations(local, ledger);
|
|
33766
|
+
const relDir = relativeToCwd(context, dir);
|
|
33767
|
+
const lines = migrations.length === 0 ? ["No migrations yet. Create one with `naro migration new <name>`."] : [
|
|
33768
|
+
`Migrations in ${relDir} (${String(migrations.length)}):`,
|
|
33769
|
+
...migrations.flatMap((row) => {
|
|
33770
|
+
const line = ` ${row.status.padEnd(12)}${printable2(row.name)}${row.appliedAt ? ` ${printable2(row.appliedAt)}` : ""}`;
|
|
33771
|
+
const warning = warningLine(row.warning);
|
|
33772
|
+
return warning ? [line, warning] : [line];
|
|
33773
|
+
})
|
|
33774
|
+
];
|
|
33775
|
+
context.output.result({ projectId, dir: relDir, migrations }, lines);
|
|
33776
|
+
}
|
|
33777
|
+
async function applyMigrationFiles(context, client, projectId, dir, names) {
|
|
33778
|
+
const applied = [];
|
|
33779
|
+
const warnings = [];
|
|
33780
|
+
for (const name of names) {
|
|
33781
|
+
const sql = readFileSync10(join31(dir, name), "utf8");
|
|
33782
|
+
let answer;
|
|
33783
|
+
try {
|
|
33784
|
+
answer = await client.db.applyMigration(projectId, { name, sql });
|
|
33785
|
+
} catch (error51) {
|
|
33786
|
+
if (!isNaroError(error51)) throw error51;
|
|
33787
|
+
for (const message of serverWarnings(error51.details))
|
|
33788
|
+
for (const line of warningLines(message)) context.output.warn(line);
|
|
33789
|
+
const shown = context.output.json ? name : printable2(name);
|
|
33790
|
+
throw new NaroError(error51.code, `${shown}: ${error51.message}`, {
|
|
33791
|
+
...error51.status === void 0 ? {} : { status: error51.status },
|
|
33792
|
+
details: {
|
|
33793
|
+
...error51.details,
|
|
33794
|
+
migration: name,
|
|
33795
|
+
applied,
|
|
33796
|
+
appliedWarnings: warnings
|
|
33797
|
+
},
|
|
33798
|
+
cause: error51
|
|
33799
|
+
});
|
|
33800
|
+
}
|
|
33801
|
+
context.output.log(`\u2713 ${printable2(name)}`);
|
|
33802
|
+
applied.push(name);
|
|
33803
|
+
for (const message of answer.warnings) {
|
|
33804
|
+
warnings.push({ migration: name, message });
|
|
33805
|
+
for (const line of warningLines(message)) context.output.warn(line);
|
|
33806
|
+
}
|
|
33807
|
+
}
|
|
33808
|
+
return { applied, warnings };
|
|
33809
|
+
}
|
|
33810
|
+
function serverWarnings(details) {
|
|
33811
|
+
const warnings = details?.warnings;
|
|
33812
|
+
return Array.isArray(warnings) ? warnings.filter(
|
|
33813
|
+
(warning) => typeof warning === "string"
|
|
33814
|
+
) : [];
|
|
33815
|
+
}
|
|
33816
|
+
var MIGRATION_NAME_RE = /^[A-Za-z0-9_.-]{1,200}\.sql$/;
|
|
33817
|
+
var LEDGER_DDL = "CREATE TABLE IF NOT EXISTS d1_migrations (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)";
|
|
33818
|
+
var LEDGER_INSERT = "INSERT INTO d1_migrations (name) SELECT ? WHERE NOT EXISTS (SELECT 1 FROM d1_migrations WHERE name = ?)";
|
|
33819
|
+
var LEDGER_DELETE = "DELETE FROM d1_migrations WHERE name IN (?, ?)";
|
|
33820
|
+
function withoutSuffix(key) {
|
|
33821
|
+
return key.slice(0, -".sql".length);
|
|
33822
|
+
}
|
|
33823
|
+
async function runMigrationRepair(context) {
|
|
33824
|
+
const [name] = context.positionalOrThrow(
|
|
33825
|
+
1,
|
|
33826
|
+
"Usage: naro migration repair <name> --status applied|reverted"
|
|
33827
|
+
);
|
|
33828
|
+
const key = name;
|
|
33829
|
+
if (!MIGRATION_NAME_RE.test(key))
|
|
33830
|
+
throw new NaroError(
|
|
33831
|
+
"VALIDATION_FAILED",
|
|
33832
|
+
"name must end with .sql and contain only letters, digits, _ . -"
|
|
33833
|
+
);
|
|
33834
|
+
const status = context.flags.status;
|
|
33835
|
+
if (status !== "applied" && status !== "reverted")
|
|
33836
|
+
throw new NaroError(
|
|
33837
|
+
"VALIDATION_FAILED",
|
|
33838
|
+
"Pass --status applied or --status reverted."
|
|
33839
|
+
);
|
|
33840
|
+
const client = await dataClient(context);
|
|
33841
|
+
const projectId = await context.resolveProjectId();
|
|
33842
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33843
|
+
const recorded = ledger.some(
|
|
33844
|
+
(row) => row.name === key || row.name === withoutSuffix(key)
|
|
33845
|
+
);
|
|
33846
|
+
const nothingToDo = status === "applied" ? `${key} was already applied; nothing to do.` : `${key} was not in the ledger; nothing to do.`;
|
|
33847
|
+
if (status === "applied" ? recorded : !recorded) {
|
|
33848
|
+
context.output.warn(nothingToDo);
|
|
33849
|
+
context.output.result({ name: key, status, changed: false });
|
|
33850
|
+
return;
|
|
33851
|
+
}
|
|
33852
|
+
await confirmOrAbort(
|
|
33853
|
+
context,
|
|
33854
|
+
status === "applied" ? `Mark ${key} as applied in ${databaseName(dataEnvironment(context.flags))}'s ledger? Its SQL is not run.` : `Remove ${key} from ${databaseName(dataEnvironment(context.flags))}'s ledger? Its SQL is not rolled back.`
|
|
33855
|
+
);
|
|
33856
|
+
if (status === "applied" && ledger.length === 0)
|
|
33857
|
+
await client.db.query(projectId, { sql: LEDGER_DDL });
|
|
33858
|
+
const answer = await client.db.query(projectId, {
|
|
33859
|
+
sql: status === "applied" ? LEDGER_INSERT : LEDGER_DELETE,
|
|
33860
|
+
params: status === "applied" ? [key, key] : [key, withoutSuffix(key)]
|
|
33861
|
+
});
|
|
33862
|
+
const changed = (answer.statements[0]?.meta.changes ?? 0) > 0;
|
|
33863
|
+
if (!changed) {
|
|
33864
|
+
context.output.warn(nothingToDo);
|
|
33865
|
+
context.output.result({ name: key, status, changed: false });
|
|
33866
|
+
return;
|
|
33867
|
+
}
|
|
33868
|
+
context.output.result({ name: key, status, changed: true }, [
|
|
33869
|
+
status === "applied" ? `\u2713 Marked ${key} as applied.` : `\u2713 Removed ${key} from the ledger.`
|
|
33870
|
+
]);
|
|
33871
|
+
}
|
|
33872
|
+
async function runMigrationUp(context) {
|
|
33873
|
+
const client = await dataClient(context);
|
|
33874
|
+
const projectId = await context.resolveProjectId();
|
|
33875
|
+
const dir = migrationsDir(context);
|
|
33876
|
+
const local = listMigrationFiles(dir);
|
|
33877
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33878
|
+
const pending = compareMigrations(local, ledger).filter((row) => row.status === "pending").map((row) => row.name);
|
|
33879
|
+
if (pending.length === 0) {
|
|
33880
|
+
context.output.result({ projectId, applied: [], pending: [] }, [
|
|
33881
|
+
"No pending migrations."
|
|
33882
|
+
]);
|
|
33883
|
+
return;
|
|
33884
|
+
}
|
|
33885
|
+
if (context.flags.dryRun) {
|
|
33886
|
+
const problems2 = policyProblems(dir, pending);
|
|
33887
|
+
context.output.result({ projectId, dryRun: true, pending, problems: problems2 }, [
|
|
33888
|
+
`Pending migrations (${String(pending.length)}):`,
|
|
33889
|
+
...pending.map((name) => ` ${printable2(name)}`),
|
|
33890
|
+
...problems2.map(
|
|
33891
|
+
(problem) => `${printable2(problem.migration)}: ${printable2(problem.message)}`
|
|
33892
|
+
)
|
|
33893
|
+
]);
|
|
33894
|
+
if (problems2.length > 0) process.exitCode = 1;
|
|
33895
|
+
return;
|
|
33896
|
+
}
|
|
33897
|
+
context.output.log(`Pending migrations (${String(pending.length)}):`);
|
|
33898
|
+
for (const name of pending) context.output.log(` ${printable2(name)}`);
|
|
33899
|
+
await confirmOrAbort(
|
|
33900
|
+
context,
|
|
33901
|
+
`Apply ${String(pending.length)} migration(s) to ${databaseName(dataEnvironment(context.flags))}? Each file is applied all-or-nothing; a failure stops the run.`
|
|
33902
|
+
);
|
|
33903
|
+
const { applied, warnings } = await applyMigrationFiles(
|
|
33904
|
+
context,
|
|
33905
|
+
client,
|
|
33906
|
+
projectId,
|
|
33907
|
+
dir,
|
|
33908
|
+
pending
|
|
33909
|
+
);
|
|
33910
|
+
context.output.result({ projectId, applied, pending: [], warnings }, [
|
|
33911
|
+
`\u2705 Applied ${String(applied.length)} migration(s).`
|
|
33912
|
+
]);
|
|
33913
|
+
}
|
|
33914
|
+
|
|
33915
|
+
// src/functions/retry.ts
|
|
33916
|
+
var FUNCTION_RETRIES = 3;
|
|
33917
|
+
var FUNCTION_BUSY_WAIT_MS = 5e3;
|
|
33918
|
+
var FUNCTION_RATE_LIMIT_DEFAULT_SECONDS = 60;
|
|
33919
|
+
var FUNCTION_RATE_LIMIT_MAX_SECONDS = 300;
|
|
33920
|
+
function defaultSleep(ms) {
|
|
33921
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
33922
|
+
}
|
|
33923
|
+
function retryAfterSeconds(error51) {
|
|
33924
|
+
const value = error51.details?.retryAfter;
|
|
33925
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(Math.ceil(value), FUNCTION_RATE_LIMIT_MAX_SECONDS) : FUNCTION_RATE_LIMIT_DEFAULT_SECONDS;
|
|
33926
|
+
}
|
|
33927
|
+
async function withFunctionRetries(op, name, io) {
|
|
33928
|
+
const rateRetries = io.rateLimitRetries ?? FUNCTION_RETRIES;
|
|
33929
|
+
const busyRetries = io.busyRetries ?? FUNCTION_RETRIES;
|
|
33930
|
+
let rate = 0;
|
|
33931
|
+
let busy = 0;
|
|
33932
|
+
for (; ; ) {
|
|
33933
|
+
try {
|
|
33934
|
+
return await op();
|
|
33935
|
+
} catch (error51) {
|
|
33936
|
+
if (!isNaroError(error51)) throw error51;
|
|
33937
|
+
if (error51.code === "FUNCTIONS_RATE_LIMITED" && rate < rateRetries) {
|
|
33938
|
+
rate += 1;
|
|
33939
|
+
const seconds = retryAfterSeconds(error51);
|
|
33940
|
+
io.log(
|
|
33941
|
+
`\u23F3 Cloudflare is rate limiting function uploads; trying ${name} again in ${String(seconds)} seconds (${String(rate)}/${String(rateRetries)}).`
|
|
33942
|
+
);
|
|
33943
|
+
await io.sleep(seconds * 1e3);
|
|
33944
|
+
continue;
|
|
33945
|
+
}
|
|
33946
|
+
if (error51.code === "FUNCTION_BUSY" && busy < busyRetries) {
|
|
33947
|
+
busy += 1;
|
|
33948
|
+
io.log(
|
|
33949
|
+
`\u23F3 ${name} is being changed right now; trying again in ${String(FUNCTION_BUSY_WAIT_MS / 1e3)} seconds (${String(busy)}/${String(busyRetries)}).`
|
|
33950
|
+
);
|
|
33951
|
+
await io.sleep(FUNCTION_BUSY_WAIT_MS);
|
|
33952
|
+
continue;
|
|
33953
|
+
}
|
|
33954
|
+
throw error51;
|
|
33955
|
+
}
|
|
33956
|
+
}
|
|
33957
|
+
}
|
|
33958
|
+
|
|
33959
|
+
// src/functions/deploy.ts
|
|
33960
|
+
var PER_FUNCTION_CODES = /* @__PURE__ */ new Set([
|
|
33961
|
+
"VALIDATION_FAILED",
|
|
33962
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33963
|
+
"FUNCTION_BOOT_FAILED",
|
|
33964
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
33965
|
+
"BUNDLE_TOO_LARGE",
|
|
33966
|
+
"BUNDLE_MISSING",
|
|
33967
|
+
"BUNDLE_MISMATCH",
|
|
33968
|
+
"FUNCTION_LIMIT",
|
|
33969
|
+
"FUNCTION_BUSY",
|
|
33970
|
+
"FUNCTION_NOT_FOUND",
|
|
33971
|
+
"VERSION_NOT_FOUND",
|
|
33972
|
+
"VERSION_NOT_DEPLOYABLE",
|
|
33973
|
+
"FUNCTION_DEPLOY_FAILED",
|
|
33974
|
+
"FUNCTION_DEPLOY_INCOMPLETE",
|
|
33975
|
+
"FUNCTIONS_RATE_LIMITED"
|
|
33976
|
+
]);
|
|
33977
|
+
function assertFunctionsNode(version3 = process.versions.node) {
|
|
33978
|
+
const major = Number(version3.split(".")[0]);
|
|
33979
|
+
if (!(major >= 22))
|
|
33980
|
+
throw new NaroError(
|
|
33981
|
+
"NODE_VERSION_UNSUPPORTED",
|
|
33982
|
+
`naro functions needs Node.js 22 or later (found ${version3}).`
|
|
33983
|
+
);
|
|
33984
|
+
}
|
|
33985
|
+
function asNaroError(error51) {
|
|
33986
|
+
return error51 instanceof FunctionBundleError ? new NaroError(error51.code, error51.message) : error51;
|
|
33987
|
+
}
|
|
33988
|
+
var UPLOAD_TIMEOUT_DEFAULT_MS = 15 * 6e4;
|
|
33989
|
+
var UPLOAD_TIMEOUT_MIN_MS = 6e4;
|
|
33990
|
+
var UPLOAD_TIMEOUT_SLACK_MS = 3e4;
|
|
33991
|
+
function uploadTimeoutMs(upload, now = Date.now()) {
|
|
33992
|
+
const expires = typeof upload.expiresAt === "string" ? Date.parse(upload.expiresAt) : NaN;
|
|
33993
|
+
if (Number.isNaN(expires)) return UPLOAD_TIMEOUT_DEFAULT_MS;
|
|
33994
|
+
return Math.max(expires - now, UPLOAD_TIMEOUT_MIN_MS) + UPLOAD_TIMEOUT_SLACK_MS;
|
|
33995
|
+
}
|
|
33996
|
+
var SECRETS_LIST_TIMEOUT_MS = 45e3;
|
|
33997
|
+
var FUNCTION_CALL_TIMEOUT_MS = 12e4;
|
|
33998
|
+
var deadlineReached = (name, deadlineMs) => `Deploying ${name} reached the ${String(deadlineMs / 1e3)}-second limit before it went live; the live version did not change. Try again.`;
|
|
33999
|
+
function budgetOf(options) {
|
|
34000
|
+
const { deadlineMs } = options;
|
|
34001
|
+
if (deadlineMs === void 0) return null;
|
|
34002
|
+
const now = options.now ?? Date.now;
|
|
34003
|
+
const end = now() + deadlineMs;
|
|
34004
|
+
return {
|
|
34005
|
+
left(name, cap) {
|
|
34006
|
+
const left = end - now();
|
|
34007
|
+
if (left <= 0)
|
|
34008
|
+
throw new NaroError("TIMEOUT", deadlineReached(name, deadlineMs));
|
|
34009
|
+
return Math.min(cap, left);
|
|
34010
|
+
}
|
|
34011
|
+
};
|
|
34012
|
+
}
|
|
34013
|
+
var bootSeconds = (ms) => Math.max(1e3, Math.floor(ms / 1e3) * 1e3);
|
|
34014
|
+
async function putBundle(doFetch, name, upload, contents, timeoutMs) {
|
|
34015
|
+
const headers = Object.fromEntries(
|
|
34016
|
+
Object.entries(upload.headers).filter(
|
|
34017
|
+
([header]) => header.toLowerCase() !== "content-length"
|
|
34018
|
+
)
|
|
34019
|
+
);
|
|
34020
|
+
const again = `Run naro functions deploy ${name} again.`;
|
|
34021
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
34022
|
+
let response;
|
|
34023
|
+
try {
|
|
34024
|
+
response = await doFetch(upload.url, {
|
|
34025
|
+
method: upload.method,
|
|
34026
|
+
headers,
|
|
34027
|
+
body: contents,
|
|
34028
|
+
signal
|
|
34029
|
+
});
|
|
34030
|
+
} catch (cause) {
|
|
34031
|
+
const timedOut = signal.aborted || cause instanceof Error && cause.name === "TimeoutError";
|
|
34032
|
+
throw new NaroError(
|
|
34033
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
34034
|
+
`Uploading the bundle of ${name} failed (${timedOut ? "timed out" : "network error"}). ${again}`,
|
|
34035
|
+
{ cause }
|
|
34036
|
+
);
|
|
34037
|
+
}
|
|
34038
|
+
if (!response.ok)
|
|
34039
|
+
throw new NaroError(
|
|
34040
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
34041
|
+
`Uploading the bundle of ${name} failed (${String(response.status)}). ${again}`,
|
|
34042
|
+
{ status: response.status }
|
|
34043
|
+
);
|
|
34044
|
+
}
|
|
34045
|
+
async function projectSecretNames(client, projectId, timeoutMs) {
|
|
34046
|
+
try {
|
|
34047
|
+
const secrets = timeoutMs === void 0 ? await client.functions.secrets(projectId) : await client.functions.secrets(projectId, { timeoutMs });
|
|
34048
|
+
return secrets.map((secret) => secret.name);
|
|
34049
|
+
} catch {
|
|
34050
|
+
return [];
|
|
34051
|
+
}
|
|
34052
|
+
}
|
|
34053
|
+
async function deployOne(options, folder, name, importMap, secretNames, io, budget) {
|
|
34054
|
+
const steps = options.steps ?? { bundle: bundleFunction, boot: bootCheck };
|
|
34055
|
+
const { client, projectId } = options;
|
|
34056
|
+
const call = () => budget ? { timeoutMs: budget.left(name, FUNCTION_CALL_TIMEOUT_MS) } : null;
|
|
34057
|
+
io.log(`\u23F3 Deploying ${name}\u2026`);
|
|
34058
|
+
try {
|
|
34059
|
+
const source = readFunctionSource(folder, name);
|
|
34060
|
+
const bundle = await steps.bundle(source, folder, { importMap });
|
|
34061
|
+
for (const warning of bundle.warnings) io.log(warning);
|
|
34062
|
+
if (!options.skipCheck)
|
|
34063
|
+
await steps.boot(
|
|
34064
|
+
name,
|
|
34065
|
+
bundle.code,
|
|
34066
|
+
budget ? {
|
|
34067
|
+
secretNames,
|
|
34068
|
+
timeoutMs: bootSeconds(budget.left(name, BOOT_TIMEOUT_MS))
|
|
34069
|
+
} : { secretNames }
|
|
34070
|
+
);
|
|
34071
|
+
const verifyJwt = options.verifyJwt ?? source.verifyJwt ?? true;
|
|
34072
|
+
const request2 = {
|
|
34073
|
+
size: bundle.bytes,
|
|
34074
|
+
sha256: bundle.sha256,
|
|
34075
|
+
verifyJwt,
|
|
34076
|
+
entrypoint: source.entrypoint,
|
|
34077
|
+
...options.cliVersion ? { cliVersion: options.cliVersion } : {}
|
|
34078
|
+
};
|
|
34079
|
+
const created = await withFunctionRetries(
|
|
34080
|
+
() => {
|
|
34081
|
+
const limit = call();
|
|
34082
|
+
return limit ? client.functions.createVersion(projectId, name, request2, limit) : client.functions.createVersion(projectId, name, request2);
|
|
34083
|
+
},
|
|
34084
|
+
name,
|
|
34085
|
+
io
|
|
34086
|
+
);
|
|
34087
|
+
const urlLife = uploadTimeoutMs(created.upload);
|
|
34088
|
+
await putBundle(
|
|
34089
|
+
options.fetch ?? fetch,
|
|
34090
|
+
name,
|
|
34091
|
+
created.upload,
|
|
34092
|
+
bundle.contents,
|
|
34093
|
+
budget ? budget.left(name, urlLife) : urlLife
|
|
34094
|
+
);
|
|
34095
|
+
const live = await withFunctionRetries(
|
|
34096
|
+
() => {
|
|
34097
|
+
const limit = call();
|
|
34098
|
+
return limit ? client.functions.deploy(projectId, name, created.version.id, limit) : client.functions.deploy(projectId, name, created.version.id);
|
|
34099
|
+
},
|
|
34100
|
+
name,
|
|
34101
|
+
io
|
|
34102
|
+
);
|
|
34103
|
+
io.log(
|
|
34104
|
+
`\u2705 Deployed ${name} (v${String(live.version ?? created.version.number)}) \u2014 ${live.url}`
|
|
34105
|
+
);
|
|
34106
|
+
return {
|
|
34107
|
+
name,
|
|
34108
|
+
version: live.version,
|
|
34109
|
+
url: live.url,
|
|
34110
|
+
verifyJwt: live.verifyJwt
|
|
34111
|
+
};
|
|
34112
|
+
} catch (error51) {
|
|
34113
|
+
throw asNaroError(error51);
|
|
34114
|
+
}
|
|
34115
|
+
}
|
|
34116
|
+
function readFlagImportMap(options, folder) {
|
|
34117
|
+
if (options.importMap === void 0) return void 0;
|
|
34118
|
+
const file2 = resolve13(options.cwd, options.importMap);
|
|
34119
|
+
try {
|
|
34120
|
+
return readImportMapFile(
|
|
34121
|
+
file2,
|
|
34122
|
+
relative11(folder.root, file2).split(sep10).join("/")
|
|
34123
|
+
);
|
|
34124
|
+
} catch (error51) {
|
|
34125
|
+
throw asNaroError(error51);
|
|
34126
|
+
}
|
|
34127
|
+
}
|
|
34128
|
+
function withProgress(error51, deployed, failures) {
|
|
34129
|
+
return new NaroError(error51.code, error51.message, {
|
|
34130
|
+
...error51.status === void 0 ? {} : { status: error51.status },
|
|
34131
|
+
details: { ...error51.details, deployed, failures },
|
|
34132
|
+
cause: error51
|
|
34133
|
+
});
|
|
34134
|
+
}
|
|
34135
|
+
async function deployFunctions(options) {
|
|
34136
|
+
assertFunctionsNode(options.nodeVersion);
|
|
34137
|
+
const budget = budgetOf(options);
|
|
34138
|
+
const io = {
|
|
34139
|
+
sleep: options.sleep ?? defaultSleep,
|
|
34140
|
+
log: options.log ?? (() => {
|
|
34141
|
+
}),
|
|
34142
|
+
rateLimitRetries: options.rateLimitRetries,
|
|
34143
|
+
busyRetries: options.busyRetries
|
|
34144
|
+
};
|
|
34145
|
+
const folder = functionsFolder({
|
|
34146
|
+
cwd: options.cwd,
|
|
34147
|
+
root: options.root,
|
|
34148
|
+
dir: options.dir
|
|
34149
|
+
});
|
|
34150
|
+
let names;
|
|
34151
|
+
try {
|
|
34152
|
+
names = options.names && options.names.length > 0 ? [...new Set(options.names)] : listFunctionNames(folder);
|
|
34153
|
+
} catch (error51) {
|
|
34154
|
+
throw asNaroError(error51);
|
|
34155
|
+
}
|
|
34156
|
+
const importMap = readFlagImportMap(options, folder);
|
|
34157
|
+
const warning = configTomlWarning(folder);
|
|
34158
|
+
if (warning !== null) io.log(warning);
|
|
34159
|
+
const [only] = names;
|
|
34160
|
+
const secretNames = options.skipCheck ? [] : await projectSecretNames(
|
|
34161
|
+
options.client,
|
|
34162
|
+
options.projectId,
|
|
34163
|
+
budget?.left(only ?? "", SECRETS_LIST_TIMEOUT_MS)
|
|
34164
|
+
);
|
|
34165
|
+
if (names.length === 1 && only !== void 0)
|
|
34166
|
+
return {
|
|
34167
|
+
deployed: [
|
|
34168
|
+
await deployOne(
|
|
34169
|
+
options,
|
|
34170
|
+
folder,
|
|
34171
|
+
only,
|
|
34172
|
+
importMap,
|
|
34173
|
+
secretNames,
|
|
34174
|
+
io,
|
|
34175
|
+
budget
|
|
34176
|
+
)
|
|
34177
|
+
]
|
|
34178
|
+
};
|
|
34179
|
+
const deployed = [];
|
|
34180
|
+
const failures = [];
|
|
34181
|
+
for (const name of names) {
|
|
34182
|
+
try {
|
|
34183
|
+
deployed.push(
|
|
34184
|
+
await deployOne(
|
|
34185
|
+
options,
|
|
34186
|
+
folder,
|
|
34187
|
+
name,
|
|
34188
|
+
importMap,
|
|
34189
|
+
secretNames,
|
|
34190
|
+
io,
|
|
34191
|
+
budget
|
|
34192
|
+
)
|
|
34193
|
+
);
|
|
34194
|
+
} catch (error51) {
|
|
34195
|
+
if (!isNaroError(error51)) throw error51;
|
|
34196
|
+
if (!PER_FUNCTION_CODES.has(error51.code))
|
|
34197
|
+
throw withProgress(error51, deployed, failures);
|
|
34198
|
+
failures.push({ name, code: error51.code, message: error51.message });
|
|
34199
|
+
io.log(`\u2717 ${name}: ${printable2(error51.message)}`);
|
|
34200
|
+
}
|
|
34201
|
+
}
|
|
34202
|
+
if (failures.length > 0)
|
|
34203
|
+
throw new NaroError(
|
|
34204
|
+
"FUNCTIONS_DEPLOY_PARTIAL",
|
|
34205
|
+
`Deployed ${String(deployed.length)} of ${String(names.length)} functions. Failed: ${failures.map((failure2) => `${failure2.name} (${failure2.code})`).join(", ")}.`,
|
|
34206
|
+
{ details: { deployed, failures } }
|
|
34207
|
+
);
|
|
34208
|
+
return { deployed };
|
|
34209
|
+
}
|
|
34210
|
+
|
|
34211
|
+
// src/deploy.ts
|
|
34212
|
+
function parseDotenv2(text3) {
|
|
34213
|
+
return parseDotenv(text3);
|
|
34214
|
+
}
|
|
34215
|
+
function readProjectEnv2(dir) {
|
|
34216
|
+
return readProjectEnv(dir);
|
|
34217
|
+
}
|
|
34218
|
+
function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUrl) {
|
|
34219
|
+
const post = (body) => fetchImpl(`${cp3}/api/internal/deployments/${deploymentId}/events`, {
|
|
34220
|
+
method: "POST",
|
|
34221
|
+
headers: {
|
|
34222
|
+
"Content-Type": "application/json",
|
|
34223
|
+
Authorization: `Bearer ${token}`
|
|
34224
|
+
},
|
|
34225
|
+
body: JSON.stringify(body)
|
|
34226
|
+
}).then(() => {
|
|
34227
|
+
});
|
|
34228
|
+
const postStatus = async (body) => {
|
|
34229
|
+
let lastError;
|
|
34230
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
34231
|
+
try {
|
|
34232
|
+
return await post(body);
|
|
34233
|
+
} catch (error51) {
|
|
34234
|
+
lastError = error51;
|
|
34235
|
+
await new Promise(
|
|
34236
|
+
(resolve15) => setTimeout(resolve15, 500 * (attempt + 1))
|
|
34237
|
+
);
|
|
34238
|
+
}
|
|
34239
|
+
}
|
|
34240
|
+
throw lastError;
|
|
34241
|
+
};
|
|
34242
|
+
const send = createLogSender({
|
|
31947
34243
|
...logIngestUrl ? { ingestUrl: logIngestUrl } : {},
|
|
31948
34244
|
toIngest: logIngestUrl ? (logs) => fetchImpl(`${logIngestUrl}/deployments/${deploymentId}/logs`, {
|
|
31949
34245
|
method: "POST",
|
|
@@ -32002,6 +34298,10 @@ function failureCode(error51, fallback) {
|
|
|
32002
34298
|
const code = error51 && typeof error51 === "object" ? error51.code : void 0;
|
|
32003
34299
|
return typeof code === "string" && DEPLOYMENT_CODES.has(code) ? code : fallback;
|
|
32004
34300
|
}
|
|
34301
|
+
var SYSTEM_NAMES = new Set(NARO_SYSTEM_ENV_NAMES);
|
|
34302
|
+
function isNaroSystemName(key) {
|
|
34303
|
+
return SYSTEM_NAMES.has(key);
|
|
34304
|
+
}
|
|
32005
34305
|
function slugify2(value) {
|
|
32006
34306
|
const slug = value.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 54);
|
|
32007
34307
|
return slug || "app";
|
|
@@ -32009,19 +34309,19 @@ function slugify2(value) {
|
|
|
32009
34309
|
async function projectName(sourceDir) {
|
|
32010
34310
|
try {
|
|
32011
34311
|
const pkg = JSON.parse(
|
|
32012
|
-
await readFile15(
|
|
34312
|
+
await readFile15(join32(sourceDir, "package.json"), "utf8")
|
|
32013
34313
|
);
|
|
32014
34314
|
if (pkg.name) return pkg.name;
|
|
32015
34315
|
} catch {
|
|
32016
34316
|
}
|
|
32017
|
-
return
|
|
34317
|
+
return basename5(sourceDir);
|
|
32018
34318
|
}
|
|
32019
34319
|
function resolveBin(name) {
|
|
32020
|
-
let dir =
|
|
34320
|
+
let dir = dirname10(fileURLToPath4(import.meta.url));
|
|
32021
34321
|
for (; ; ) {
|
|
32022
|
-
const candidate =
|
|
32023
|
-
if (
|
|
32024
|
-
const parent =
|
|
34322
|
+
const candidate = join32(dir, "node_modules", ".bin", name);
|
|
34323
|
+
if (existsSync10(candidate)) return candidate;
|
|
34324
|
+
const parent = dirname10(dir);
|
|
32025
34325
|
if (parent === dir) return name;
|
|
32026
34326
|
dir = parent;
|
|
32027
34327
|
}
|
|
@@ -32086,7 +34386,7 @@ function workingTreeNotices(copy) {
|
|
|
32086
34386
|
}
|
|
32087
34387
|
function localDependencies(sourceDir, say = () => void 0) {
|
|
32088
34388
|
return {
|
|
32089
|
-
createWorkspace: () => mkdtemp2(
|
|
34389
|
+
createWorkspace: () => mkdtemp2(join32(tmpdir2(), "naro-build-")),
|
|
32090
34390
|
removeWorkspace: (path) => rm5(path, { recursive: true, force: true }),
|
|
32091
34391
|
async runCommand(spec, observer) {
|
|
32092
34392
|
if (spec.command === "git" && spec.args[0] === "clone") {
|
|
@@ -32136,7 +34436,7 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32136
34436
|
"Pass production: true for production; preview is the default."
|
|
32137
34437
|
);
|
|
32138
34438
|
const surface = production ? "production" : environment ?? "preview";
|
|
32139
|
-
const appRoot = options.rootDirectory ?
|
|
34439
|
+
const appRoot = options.rootDirectory ? resolve14(sourceDir, options.rootDirectory) : sourceDir;
|
|
32140
34440
|
const framework = detectFramework2(appRoot);
|
|
32141
34441
|
if (!framework.deployable && !options.force) {
|
|
32142
34442
|
throw new NaroError(
|
|
@@ -32156,6 +34456,7 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32156
34456
|
const client = new NaroClient({
|
|
32157
34457
|
baseUrl: cpUrl,
|
|
32158
34458
|
getAccessToken: () => auth.getAccessToken(),
|
|
34459
|
+
clientName: `naro-cli/${version2()}`,
|
|
32159
34460
|
...options.fetch ? { fetch: options.fetch } : {}
|
|
32160
34461
|
});
|
|
32161
34462
|
if (environment !== void 0 && options.projectId)
|
|
@@ -32184,6 +34485,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32184
34485
|
const provided = options.env ?? {};
|
|
32185
34486
|
missing = scanned.filter((key) => !existing.has(key) && !(key in provided));
|
|
32186
34487
|
}
|
|
34488
|
+
if (environment !== void 0 && options.projectId && missing.some((key) => isNaroSystemName(key))) {
|
|
34489
|
+
const backend = await client.backend.info(options.projectId).catch(() => null);
|
|
34490
|
+
if (backend !== null && backend.status !== "none") {
|
|
34491
|
+
const set2 = missing.filter((key) => isNaroSystemName(key));
|
|
34492
|
+
missing = missing.filter((key) => !isNaroSystemName(key));
|
|
34493
|
+
log2(
|
|
34494
|
+
`\u25B8 naro sets ${set2.join(", ")} for ${environment} to its own backend's: not asked for`
|
|
34495
|
+
);
|
|
34496
|
+
}
|
|
34497
|
+
}
|
|
32187
34498
|
let collected = {};
|
|
32188
34499
|
if (missing.length > 0) {
|
|
32189
34500
|
log2(
|
|
@@ -32229,7 +34540,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32229
34540
|
repositoryUrl: sourceDir,
|
|
32230
34541
|
branch: ctx.branch,
|
|
32231
34542
|
rootDirectory: options.rootDirectory ?? null,
|
|
32232
|
-
|
|
34543
|
+
// An environment's deploy: the control plane saved what this sent for
|
|
34544
|
+
// that environment and then chose its values, so a key sent and a key
|
|
34545
|
+
// chosen usually hold the same value — except the names naro sets for
|
|
34546
|
+
// an environment with a backend (named environments spec §8.3), which
|
|
34547
|
+
// must win here too, or this build would call another backend than
|
|
34548
|
+
// the worker's bindings, and a key the server failed to save
|
|
34549
|
+
// (`envSkipped`), whose stored value the worker gets and so this build
|
|
34550
|
+
// takes too. Production and preview keep a value sent over the stored
|
|
34551
|
+
// one, as before.
|
|
34552
|
+
env: ctx.environment ? { ...envToSend, ...ctx.env } : { ...ctx.env, ...envToSend },
|
|
32233
34553
|
strategy: ctx.strategy ?? null,
|
|
32234
34554
|
...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
|
|
32235
34555
|
},
|
|
@@ -32320,10 +34640,10 @@ function deploymentUrl(detail, ctx, production) {
|
|
|
32320
34640
|
}
|
|
32321
34641
|
|
|
32322
34642
|
export {
|
|
34643
|
+
isFunctionName,
|
|
32323
34644
|
parseGitHubRemote,
|
|
32324
34645
|
schemaMigrationTitle,
|
|
32325
34646
|
projectPath,
|
|
32326
|
-
splitStatements,
|
|
32327
34647
|
ANALYTICS_FILTER_KEYS,
|
|
32328
34648
|
NaroError,
|
|
32329
34649
|
promoteTargetUnsupported,
|
|
@@ -32381,7 +34701,6 @@ export {
|
|
|
32381
34701
|
parsePolicyStatement,
|
|
32382
34702
|
splitPolicyStatements,
|
|
32383
34703
|
STORAGE_SUBQUERIES_SQL,
|
|
32384
|
-
planPolicyMigration,
|
|
32385
34704
|
unreadablePolicyComment,
|
|
32386
34705
|
parseRedirectEntry,
|
|
32387
34706
|
formatRedirectEntry,
|
|
@@ -32393,10 +34712,8 @@ export {
|
|
|
32393
34712
|
ensureGitignoreHasNaro,
|
|
32394
34713
|
countLintedTables,
|
|
32395
34714
|
lintSchema,
|
|
32396
|
-
resolveMigrationsDir,
|
|
32397
34715
|
listMigrationFiles,
|
|
32398
34716
|
nextMigrationFileName,
|
|
32399
|
-
compareMigrations,
|
|
32400
34717
|
pendingMigrationNames,
|
|
32401
34718
|
migrationsPendingError,
|
|
32402
34719
|
migrationNameMismatch,
|
|
@@ -32415,6 +34732,40 @@ export {
|
|
|
32415
34732
|
promptForMissingEnv,
|
|
32416
34733
|
pullEnv,
|
|
32417
34734
|
detectGitInfo,
|
|
34735
|
+
version2 as version,
|
|
34736
|
+
BUNDLE_MESSAGES,
|
|
34737
|
+
functionsFolder,
|
|
34738
|
+
parseJsonc2 as parseJsonc,
|
|
34739
|
+
DATA_ENV_MESSAGES,
|
|
34740
|
+
dataEnvironment,
|
|
34741
|
+
dataClient,
|
|
34742
|
+
databaseName,
|
|
34743
|
+
enableBackendCommand,
|
|
34744
|
+
envFlag,
|
|
34745
|
+
forEnvironment,
|
|
34746
|
+
isInteractive,
|
|
34747
|
+
readStdin,
|
|
34748
|
+
createPrompter,
|
|
34749
|
+
cannotAsk,
|
|
34750
|
+
confirmOrAbort,
|
|
34751
|
+
migrationsDir,
|
|
34752
|
+
relativeToCwd,
|
|
34753
|
+
appliedMigrations,
|
|
34754
|
+
printable2 as printable,
|
|
34755
|
+
isPrintable,
|
|
34756
|
+
UNSHOWN_WARNING_LINE,
|
|
34757
|
+
warningLines,
|
|
34758
|
+
policyProblems,
|
|
34759
|
+
runMigrationNew,
|
|
34760
|
+
runMigrationList,
|
|
34761
|
+
applyMigrationFiles,
|
|
34762
|
+
runMigrationRepair,
|
|
34763
|
+
runMigrationUp,
|
|
34764
|
+
defaultSleep,
|
|
34765
|
+
retryAfterSeconds,
|
|
34766
|
+
withFunctionRetries,
|
|
34767
|
+
assertFunctionsNode,
|
|
34768
|
+
deployFunctions,
|
|
32418
34769
|
parseDotenv2 as parseDotenv,
|
|
32419
34770
|
readProjectEnv2 as readProjectEnv,
|
|
32420
34771
|
eventsObserver,
|