naro-cli 0.22.0 → 0.24.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 +58 -0
- package/dist/args.d.ts +238 -0
- package/dist/{chunk-MBSO3K6L.js → chunk-XJIWWZ2T.js} +2264 -372
- 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/env-services.d.ts +44 -0
- package/dist/functions/deploy.d.ts +70 -0
- package/dist/functions/retry.d.ts +16 -0
- package/dist/index.js +1743 -721
- 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);
|
|
@@ -320,6 +320,33 @@ var PHASES = [
|
|
|
320
320
|
];
|
|
321
321
|
var ACTIVE_STATUSES = new Set(PHASES);
|
|
322
322
|
|
|
323
|
+
// ../../packages/core/src/dotenv.ts
|
|
324
|
+
var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/dgm;
|
|
325
|
+
var QUOTES = /* @__PURE__ */ new Set(["'", '"', "`"]);
|
|
326
|
+
function parseDotenvDetailed(text3) {
|
|
327
|
+
const vars = {};
|
|
328
|
+
const unclosedQuotes = [];
|
|
329
|
+
const lines = text3.replace(/\r\n?/g, "\n");
|
|
330
|
+
let line = 1;
|
|
331
|
+
let counted = 0;
|
|
332
|
+
for (const match of lines.matchAll(LINE)) {
|
|
333
|
+
const key = match[1];
|
|
334
|
+
const raw = match[2] ?? "";
|
|
335
|
+
let value = raw.trim();
|
|
336
|
+
const maybeQuote = value[0];
|
|
337
|
+
if (maybeQuote !== void 0 && QUOTES.has(maybeQuote) && !value.includes(maybeQuote, 1)) {
|
|
338
|
+
const at = (match.indices?.[2]?.[0] ?? match.index) + (raw.length - raw.trimStart().length);
|
|
339
|
+
for (; counted < at; counted += 1) if (lines[counted] === "\n") line += 1;
|
|
340
|
+
unclosedQuotes.push(line);
|
|
341
|
+
}
|
|
342
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
343
|
+
if (maybeQuote === '"')
|
|
344
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
|
|
345
|
+
vars[key] = value;
|
|
346
|
+
}
|
|
347
|
+
return { vars, unclosedQuotes };
|
|
348
|
+
}
|
|
349
|
+
|
|
323
350
|
// ../../packages/core/src/deployment-stall.ts
|
|
324
351
|
var STALL_WINDOW_MS = 10 * 6e4;
|
|
325
352
|
|
|
@@ -450,14 +477,14 @@ function bare(match, command, rest, after, slug, switchContext) {
|
|
|
450
477
|
return match;
|
|
451
478
|
}
|
|
452
479
|
}
|
|
453
|
-
function environmentGuidance(
|
|
454
|
-
if (slug === null || !
|
|
455
|
-
const switchContext = SWITCH_CONTEXT.test(
|
|
480
|
+
function environmentGuidance(text3, slug) {
|
|
481
|
+
if (slug === null || !text3.includes("naro ")) return text3;
|
|
482
|
+
const switchContext = SWITCH_CONTEXT.test(text3);
|
|
456
483
|
const spans = [];
|
|
457
|
-
const marked =
|
|
484
|
+
const marked = text3.replaceAll(
|
|
458
485
|
QUOTED,
|
|
459
486
|
(match, span, offset) => {
|
|
460
|
-
const noted =
|
|
487
|
+
const noted = text3.startsWith(
|
|
461
488
|
` (${PRODUCTION_SWITCH_NOTE})`,
|
|
462
489
|
offset + match.length
|
|
463
490
|
);
|
|
@@ -621,7 +648,11 @@ var DeploymentError = class extends Error {
|
|
|
621
648
|
};
|
|
622
649
|
|
|
623
650
|
// ../../packages/core/src/functions.ts
|
|
651
|
+
var FUNCTION_NAME_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
624
652
|
var FUNCTION_WORKER_NAME_PREFIX = "naro-fn-";
|
|
653
|
+
function isFunctionName(name) {
|
|
654
|
+
return FUNCTION_NAME_RE.test(name);
|
|
655
|
+
}
|
|
625
656
|
|
|
626
657
|
// ../../packages/core/src/git-remote.ts
|
|
627
658
|
var SCP_LIKE = /^[^@\s/]+@([^:\s/]+):(.+)$/;
|
|
@@ -1934,16 +1965,16 @@ function cleanEnum(obj) {
|
|
|
1934
1965
|
}
|
|
1935
1966
|
function base64ToUint8Array(base643) {
|
|
1936
1967
|
const binaryString = atob(base643);
|
|
1937
|
-
const
|
|
1968
|
+
const bytes2 = new Uint8Array(binaryString.length);
|
|
1938
1969
|
for (let i = 0; i < binaryString.length; i++) {
|
|
1939
|
-
|
|
1970
|
+
bytes2[i] = binaryString.charCodeAt(i);
|
|
1940
1971
|
}
|
|
1941
|
-
return
|
|
1972
|
+
return bytes2;
|
|
1942
1973
|
}
|
|
1943
|
-
function uint8ArrayToBase64(
|
|
1974
|
+
function uint8ArrayToBase64(bytes2) {
|
|
1944
1975
|
let binaryString = "";
|
|
1945
|
-
for (let i = 0; i <
|
|
1946
|
-
binaryString += String.fromCharCode(
|
|
1976
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
1977
|
+
binaryString += String.fromCharCode(bytes2[i]);
|
|
1947
1978
|
}
|
|
1948
1979
|
return btoa(binaryString);
|
|
1949
1980
|
}
|
|
@@ -1952,22 +1983,22 @@ function base64urlToUint8Array(base64url3) {
|
|
|
1952
1983
|
const padding = "=".repeat((4 - base643.length % 4) % 4);
|
|
1953
1984
|
return base64ToUint8Array(base643 + padding);
|
|
1954
1985
|
}
|
|
1955
|
-
function uint8ArrayToBase64url(
|
|
1956
|
-
return uint8ArrayToBase64(
|
|
1986
|
+
function uint8ArrayToBase64url(bytes2) {
|
|
1987
|
+
return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1957
1988
|
}
|
|
1958
1989
|
function hexToUint8Array(hex3) {
|
|
1959
1990
|
const cleanHex = hex3.replace(/^0x/, "");
|
|
1960
1991
|
if (cleanHex.length % 2 !== 0) {
|
|
1961
1992
|
throw new Error("Invalid hex string length");
|
|
1962
1993
|
}
|
|
1963
|
-
const
|
|
1994
|
+
const bytes2 = new Uint8Array(cleanHex.length / 2);
|
|
1964
1995
|
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1965
|
-
|
|
1996
|
+
bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1966
1997
|
}
|
|
1967
|
-
return
|
|
1998
|
+
return bytes2;
|
|
1968
1999
|
}
|
|
1969
|
-
function uint8ArrayToHex(
|
|
1970
|
-
return Array.from(
|
|
2000
|
+
function uint8ArrayToHex(bytes2) {
|
|
2001
|
+
return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1971
2002
|
}
|
|
1972
2003
|
var Class = class {
|
|
1973
2004
|
constructor(..._args) {
|
|
@@ -2273,10 +2304,10 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
|
2273
2304
|
var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
|
|
2274
2305
|
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)?)??$/;
|
|
2275
2306
|
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})$/;
|
|
2276
|
-
var uuid = (
|
|
2277
|
-
if (!
|
|
2307
|
+
var uuid = (version3) => {
|
|
2308
|
+
if (!version3)
|
|
2278
2309
|
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)$/;
|
|
2279
|
-
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${
|
|
2310
|
+
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})$`);
|
|
2280
2311
|
};
|
|
2281
2312
|
var uuid4 = /* @__PURE__ */ uuid(4);
|
|
2282
2313
|
var uuid6 = /* @__PURE__ */ uuid(6);
|
|
@@ -3758,7 +3789,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3758
3789
|
const _normalized = cached(() => normalizeDef(def));
|
|
3759
3790
|
const generateFastpass = (shape) => {
|
|
3760
3791
|
const doc = new Doc(["shape", "payload", "ctx"]);
|
|
3761
|
-
const
|
|
3792
|
+
const normalized2 = _normalized.value;
|
|
3762
3793
|
const parseStr = (key) => {
|
|
3763
3794
|
const k = esc(key);
|
|
3764
3795
|
return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
@@ -3766,11 +3797,11 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3766
3797
|
doc.write(`const input = payload.value;`);
|
|
3767
3798
|
const ids = /* @__PURE__ */ Object.create(null);
|
|
3768
3799
|
let counter = 0;
|
|
3769
|
-
for (const key of
|
|
3800
|
+
for (const key of normalized2.keys) {
|
|
3770
3801
|
ids[key] = `key_${counter++}`;
|
|
3771
3802
|
}
|
|
3772
3803
|
doc.write(`const newResult = {};`);
|
|
3773
|
-
for (const key of
|
|
3804
|
+
for (const key of normalized2.keys) {
|
|
3774
3805
|
const id = ids[key];
|
|
3775
3806
|
const k = esc(key);
|
|
3776
3807
|
const schema = shape[key];
|
|
@@ -8317,8 +8348,8 @@ function ko_default() {
|
|
|
8317
8348
|
}
|
|
8318
8349
|
|
|
8319
8350
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js
|
|
8320
|
-
var capitalizeFirstCharacter = (
|
|
8321
|
-
return
|
|
8351
|
+
var capitalizeFirstCharacter = (text3) => {
|
|
8352
|
+
return text3.charAt(0).toUpperCase() + text3.slice(1);
|
|
8322
8353
|
};
|
|
8323
8354
|
function getUnitTypeFromNumber(number4) {
|
|
8324
8355
|
const abs = Math.abs(number4);
|
|
@@ -15146,23 +15177,23 @@ function fromJSONSchema(schema, params) {
|
|
|
15146
15177
|
if (typeof schema === "boolean") {
|
|
15147
15178
|
return schema ? z.any() : z.never();
|
|
15148
15179
|
}
|
|
15149
|
-
let
|
|
15180
|
+
let normalized2;
|
|
15150
15181
|
try {
|
|
15151
|
-
|
|
15182
|
+
normalized2 = JSON.parse(JSON.stringify(schema));
|
|
15152
15183
|
} catch {
|
|
15153
15184
|
throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
|
|
15154
15185
|
}
|
|
15155
|
-
const
|
|
15156
|
-
const defs =
|
|
15186
|
+
const version3 = detectVersion(normalized2, params?.defaultTarget);
|
|
15187
|
+
const defs = normalized2.$defs || normalized2.definitions || {};
|
|
15157
15188
|
const ctx = {
|
|
15158
|
-
version:
|
|
15189
|
+
version: version3,
|
|
15159
15190
|
defs,
|
|
15160
15191
|
refs: /* @__PURE__ */ new Map(),
|
|
15161
15192
|
processing: /* @__PURE__ */ new Set(),
|
|
15162
|
-
rootSchema:
|
|
15193
|
+
rootSchema: normalized2,
|
|
15163
15194
|
registry: params?.registry ?? globalRegistry
|
|
15164
15195
|
};
|
|
15165
|
-
return convertSchema(
|
|
15196
|
+
return convertSchema(normalized2, ctx);
|
|
15166
15197
|
}
|
|
15167
15198
|
|
|
15168
15199
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/coerce.js
|
|
@@ -15219,13 +15250,22 @@ var RESERVED_SUBDOMAINS = [
|
|
|
15219
15250
|
"www"
|
|
15220
15251
|
];
|
|
15221
15252
|
var BACKEND_WORKER_NAME_PREFIX = "naro-backend-";
|
|
15253
|
+
var AUTH_WORKER_NAME_PREFIX = "naro-auth-";
|
|
15254
|
+
var DATA_WORKER_NAME_PREFIX = "naro-data-";
|
|
15255
|
+
var NARO_WORKER_NAME_PREFIXES = [
|
|
15256
|
+
BACKEND_WORKER_NAME_PREFIX,
|
|
15257
|
+
AUTH_WORKER_NAME_PREFIX,
|
|
15258
|
+
DATA_WORKER_NAME_PREFIX
|
|
15259
|
+
];
|
|
15222
15260
|
var SUBDOMAIN_PATTERN = /^[a-z0-9](-?[a-z0-9])*$/;
|
|
15223
15261
|
var SUBDOMAIN_MAX_LENGTH = 54;
|
|
15224
15262
|
function subdomainProblem(value) {
|
|
15225
15263
|
if (value.length < 1 || value.length > SUBDOMAIN_MAX_LENGTH) return "invalid";
|
|
15226
15264
|
if (!SUBDOMAIN_PATTERN.test(value)) return "invalid";
|
|
15227
15265
|
if (value.endsWith("-preview")) return "reserved";
|
|
15228
|
-
if (
|
|
15266
|
+
if (NARO_WORKER_NAME_PREFIXES.some(
|
|
15267
|
+
(prefix) => value === prefix.slice(0, -1) || value.startsWith(prefix)
|
|
15268
|
+
))
|
|
15229
15269
|
return "reserved";
|
|
15230
15270
|
if (value === FUNCTION_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(FUNCTION_WORKER_NAME_PREFIX))
|
|
15231
15271
|
return "reserved";
|
|
@@ -15307,8 +15347,8 @@ var ISSPACE = /[ \t\n\r\f\v]/;
|
|
|
15307
15347
|
var NUL = String.fromCharCode(0);
|
|
15308
15348
|
var BOM = 65279;
|
|
15309
15349
|
var PRAGMA_PREFIX = "PRAGMA_";
|
|
15310
|
-
function asciiUpper(
|
|
15311
|
-
return
|
|
15350
|
+
function asciiUpper(text3) {
|
|
15351
|
+
return text3.replace(/[a-z]+/g, (run) => run.toUpperCase());
|
|
15312
15352
|
}
|
|
15313
15353
|
function pragmaCandidate(sql, start, end, doubled) {
|
|
15314
15354
|
const prefix = sql.slice(start + 1, start + 1 + PRAGMA_PREFIX.length);
|
|
@@ -15666,46 +15706,46 @@ function supportEndsAt(major) {
|
|
|
15666
15706
|
function compare(a, b) {
|
|
15667
15707
|
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
15668
15708
|
}
|
|
15669
|
-
function withinVerifiedRange(
|
|
15670
|
-
if (
|
|
15671
|
-
if (
|
|
15672
|
-
return
|
|
15709
|
+
function withinVerifiedRange(version3) {
|
|
15710
|
+
if (version3.major === 15) return compare(version3, VERIFIED_15_FLOOR) >= 0;
|
|
15711
|
+
if (version3.major === 16) return compare(version3, VERIFIED_16_FLOOR) >= 0;
|
|
15712
|
+
return version3.major > 16;
|
|
15673
15713
|
}
|
|
15674
15714
|
function formatDate(date5) {
|
|
15675
15715
|
return date5.toISOString().slice(0, 10);
|
|
15676
15716
|
}
|
|
15677
15717
|
function classifyNextVersion(raw, now) {
|
|
15678
|
-
const
|
|
15679
|
-
if (!
|
|
15718
|
+
const version3 = parseNextVersion(raw);
|
|
15719
|
+
if (!version3) {
|
|
15680
15720
|
return {
|
|
15681
15721
|
status: "unknown",
|
|
15682
15722
|
version: null,
|
|
15683
15723
|
reason: `Could not read a Next.js version from "${raw}".`
|
|
15684
15724
|
};
|
|
15685
15725
|
}
|
|
15686
|
-
const windowEnd = supportEndsAt(
|
|
15687
|
-
if (!windowEnd ||
|
|
15726
|
+
const windowEnd = supportEndsAt(version3.major);
|
|
15727
|
+
if (!windowEnd || version3.major < VERIFIED_15_FLOOR.major) {
|
|
15688
15728
|
return {
|
|
15689
15729
|
status: "too-old",
|
|
15690
|
-
version:
|
|
15730
|
+
version: version3,
|
|
15691
15731
|
reason: `Next.js ${raw} is too old to deploy. Naro deploys Next.js 15 or newer. Upgrade to Next.js 16.`
|
|
15692
15732
|
};
|
|
15693
15733
|
}
|
|
15694
15734
|
if (now > windowEnd) {
|
|
15695
15735
|
return {
|
|
15696
15736
|
status: "needs-unsupported-flag",
|
|
15697
|
-
version:
|
|
15698
|
-
reason: `Next.js ${
|
|
15737
|
+
version: version3,
|
|
15738
|
+
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`
|
|
15699
15739
|
};
|
|
15700
15740
|
}
|
|
15701
|
-
if (!withinVerifiedRange(
|
|
15741
|
+
if (!withinVerifiedRange(version3)) {
|
|
15702
15742
|
return {
|
|
15703
15743
|
status: "outside-peer-range",
|
|
15704
|
-
version:
|
|
15744
|
+
version: version3,
|
|
15705
15745
|
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.`
|
|
15706
15746
|
};
|
|
15707
15747
|
}
|
|
15708
|
-
return { status: "supported", version:
|
|
15748
|
+
return { status: "supported", version: version3, reason: "" };
|
|
15709
15749
|
}
|
|
15710
15750
|
|
|
15711
15751
|
// ../../packages/build/src/package-manager.ts
|
|
@@ -15743,8 +15783,8 @@ async function usesModernYarn(root) {
|
|
|
15743
15783
|
const raw = await readFile2(join2(root, "package.json"), "utf8");
|
|
15744
15784
|
const packageJson = JSON.parse(raw);
|
|
15745
15785
|
if (typeof packageJson.packageManager !== "string") return false;
|
|
15746
|
-
const
|
|
15747
|
-
return
|
|
15786
|
+
const version3 = /^yarn@(\d+)/.exec(packageJson.packageManager)?.[1];
|
|
15787
|
+
return version3 !== void 0 && Number(version3) >= 2;
|
|
15748
15788
|
} catch {
|
|
15749
15789
|
return false;
|
|
15750
15790
|
}
|
|
@@ -16632,8 +16672,8 @@ var FRAMEWORK_LABELS = {
|
|
|
16632
16672
|
static: "static files"
|
|
16633
16673
|
};
|
|
16634
16674
|
function dependencyVersion(packageJson, name) {
|
|
16635
|
-
const
|
|
16636
|
-
return typeof
|
|
16675
|
+
const version3 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
|
|
16676
|
+
return typeof version3 === "string" && version3.trim() !== "" ? version3 : null;
|
|
16637
16677
|
}
|
|
16638
16678
|
var STATIC_EXPORT = /(?:^|[\s,{;])output\s*:\s*(["'`])export\1/;
|
|
16639
16679
|
function declaresStaticExport(source) {
|
|
@@ -16822,13 +16862,13 @@ async function detectFramework(root, overrides = {}) {
|
|
|
16822
16862
|
`The project must declare a "${buildScript}" script.`
|
|
16823
16863
|
);
|
|
16824
16864
|
}
|
|
16825
|
-
const
|
|
16865
|
+
const version3 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
|
|
16826
16866
|
return {
|
|
16827
16867
|
preset,
|
|
16828
16868
|
buildScript,
|
|
16829
16869
|
outputDirectory: overrides.outputDirectory ?? (preset.id === "angular" ? await angularOutput(root) : preset.defaultOutputDirectory),
|
|
16830
|
-
description:
|
|
16831
|
-
dependencyRange:
|
|
16870
|
+
description: version3 ? `${FRAMEWORK_LABELS[preset.id]} ${version3}` : FRAMEWORK_LABELS[preset.id],
|
|
16871
|
+
dependencyRange: version3
|
|
16832
16872
|
};
|
|
16833
16873
|
}
|
|
16834
16874
|
|
|
@@ -17362,11 +17402,11 @@ function adapterKeyFromConfigSource(source) {
|
|
|
17362
17402
|
return EXPERIMENTAL_ADAPTER_PATH.test(code) ? "experimental.adapterPath" : "adapterPath";
|
|
17363
17403
|
}
|
|
17364
17404
|
function keysNextReads(nextVersion) {
|
|
17365
|
-
const
|
|
17366
|
-
if (!
|
|
17367
|
-
if (
|
|
17405
|
+
const version3 = parseNextVersion(nextVersion);
|
|
17406
|
+
if (!version3) return ["adapterPath", "experimental.adapterPath"];
|
|
17407
|
+
if (version3.major < 15 || version3.major === 15 && version3.minor < 4)
|
|
17368
17408
|
return [];
|
|
17369
|
-
if (
|
|
17409
|
+
if (version3.major > 16 || version3.major === 16 && version3.minor >= 2)
|
|
17370
17410
|
return ["adapterPath", "experimental.adapterPath"];
|
|
17371
17411
|
return ["experimental.adapterPath"];
|
|
17372
17412
|
}
|
|
@@ -17434,10 +17474,10 @@ async function exists4(path) {
|
|
|
17434
17474
|
return false;
|
|
17435
17475
|
}
|
|
17436
17476
|
}
|
|
17437
|
-
async function findSourceFile(appRoot,
|
|
17477
|
+
async function findSourceFile(appRoot, basename6) {
|
|
17438
17478
|
for (const dir of SOURCE_DIRECTORIES) {
|
|
17439
17479
|
for (const ext of SOURCE_EXTENSIONS2) {
|
|
17440
|
-
const rel = dir ? `${dir}/${
|
|
17480
|
+
const rel = dir ? `${dir}/${basename6}.${ext}` : `${basename6}.${ext}`;
|
|
17441
17481
|
if (await exists4(join12(appRoot, rel))) return rel;
|
|
17442
17482
|
}
|
|
17443
17483
|
}
|
|
@@ -17514,11 +17554,11 @@ async function collectProjectFacts(appRoot) {
|
|
|
17514
17554
|
function label(strategy) {
|
|
17515
17555
|
return `P${String(strategy.priority)} ${strategy.id}`;
|
|
17516
17556
|
}
|
|
17517
|
-
function clause(
|
|
17518
|
-
return
|
|
17557
|
+
function clause(text3) {
|
|
17558
|
+
return text3.endsWith(".") && !text3.endsWith("..") ? text3.slice(0, -1) : text3;
|
|
17519
17559
|
}
|
|
17520
|
-
function sentence(
|
|
17521
|
-
return
|
|
17560
|
+
function sentence(text3) {
|
|
17561
|
+
return text3.endsWith(".") ? text3 : `${text3}.`;
|
|
17522
17562
|
}
|
|
17523
17563
|
function describeOutcome(outcome) {
|
|
17524
17564
|
switch (outcome.status) {
|
|
@@ -18799,8 +18839,8 @@ async function readInstalledNextVersion(appRoot, checkout) {
|
|
|
18799
18839
|
join18(dir, "node_modules", "next", "package.json"),
|
|
18800
18840
|
"utf8"
|
|
18801
18841
|
);
|
|
18802
|
-
const
|
|
18803
|
-
return typeof
|
|
18842
|
+
const version3 = JSON.parse(source).version;
|
|
18843
|
+
return typeof version3 === "string" ? version3 : null;
|
|
18804
18844
|
} catch {
|
|
18805
18845
|
}
|
|
18806
18846
|
if (dir === stop) return null;
|
|
@@ -19689,8 +19729,8 @@ function sendPage(res, status, html) {
|
|
|
19689
19729
|
async function startCallbackServer() {
|
|
19690
19730
|
let resolveCode;
|
|
19691
19731
|
let rejectCode;
|
|
19692
|
-
const codePromise = new Promise((
|
|
19693
|
-
resolveCode =
|
|
19732
|
+
const codePromise = new Promise((resolve15, reject) => {
|
|
19733
|
+
resolveCode = resolve15;
|
|
19694
19734
|
rejectCode = reject;
|
|
19695
19735
|
});
|
|
19696
19736
|
const server = createServer2((req, res) => {
|
|
@@ -19719,7 +19759,7 @@ async function startCallbackServer() {
|
|
|
19719
19759
|
sendPage(res, 200, successPage());
|
|
19720
19760
|
resolveCode(code);
|
|
19721
19761
|
});
|
|
19722
|
-
await new Promise((
|
|
19762
|
+
await new Promise((resolve15) => server.listen(0, "127.0.0.1", resolve15));
|
|
19723
19763
|
const port = server.address().port;
|
|
19724
19764
|
return {
|
|
19725
19765
|
port,
|
|
@@ -20127,6 +20167,7 @@ var NaroClient = class _NaroClient {
|
|
|
20127
20167
|
method: options.method ?? "GET",
|
|
20128
20168
|
headers: {
|
|
20129
20169
|
Authorization: `Bearer ${token}`,
|
|
20170
|
+
"X-Naro-Client": this.options.clientName ?? "next-deploy-sdk",
|
|
20130
20171
|
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
20131
20172
|
},
|
|
20132
20173
|
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
@@ -20170,9 +20211,9 @@ var TeamsApi = class {
|
|
|
20170
20211
|
async usage(teamSlug, query = {}) {
|
|
20171
20212
|
const params = new URLSearchParams();
|
|
20172
20213
|
if (query.month !== void 0) params.set("month", query.month);
|
|
20173
|
-
const
|
|
20214
|
+
const text3 = params.toString();
|
|
20174
20215
|
return this.client.request(
|
|
20175
|
-
`/api/teams/${encodeURIComponent(teamSlug)}/usage${
|
|
20216
|
+
`/api/teams/${encodeURIComponent(teamSlug)}/usage${text3 ? `?${text3}` : ""}`,
|
|
20176
20217
|
{ context: "Read team usage" }
|
|
20177
20218
|
);
|
|
20178
20219
|
}
|
|
@@ -20397,7 +20438,7 @@ var DeploymentsApi = class {
|
|
|
20397
20438
|
async wait(deploymentId, options = {}) {
|
|
20398
20439
|
const intervalMs = options.intervalMs ?? 2500;
|
|
20399
20440
|
const timeoutMs = options.timeoutMs ?? 15 * 6e4;
|
|
20400
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((
|
|
20441
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
|
|
20401
20442
|
const startedAt = Date.now();
|
|
20402
20443
|
let lastStatus = null;
|
|
20403
20444
|
let lastSequence = -1;
|
|
@@ -20690,8 +20731,8 @@ function insightsQuery(options) {
|
|
|
20690
20731
|
if (options.environment !== void 0) params.set("env", options.environment);
|
|
20691
20732
|
if (options.route !== void 0) params.set("route", options.route);
|
|
20692
20733
|
appendFilters(params, SPEED_INSIGHTS_FILTER_KEYS, options.filters);
|
|
20693
|
-
const
|
|
20694
|
-
return
|
|
20734
|
+
const text3 = params.toString();
|
|
20735
|
+
return text3 ? `?${text3}` : "";
|
|
20695
20736
|
}
|
|
20696
20737
|
var InsightsApi = class {
|
|
20697
20738
|
constructor(client) {
|
|
@@ -20769,9 +20810,9 @@ var AnalyticsApi = class {
|
|
|
20769
20810
|
const params = new URLSearchParams();
|
|
20770
20811
|
if (options.range !== void 0) params.set("range", options.range);
|
|
20771
20812
|
appendFilters(params, ANALYTICS_FILTER_KEYS, options.filters);
|
|
20772
|
-
const
|
|
20813
|
+
const text3 = params.toString();
|
|
20773
20814
|
return this.client.request(
|
|
20774
|
-
`/api/projects/${encodeURIComponent(projectId)}/analytics${
|
|
20815
|
+
`/api/projects/${encodeURIComponent(projectId)}/analytics${text3 ? `?${text3}` : ""}`,
|
|
20775
20816
|
{ context: "Read Analytics" }
|
|
20776
20817
|
);
|
|
20777
20818
|
}
|
|
@@ -21009,7 +21050,7 @@ var DbApi = class {
|
|
|
21009
21050
|
*/
|
|
21010
21051
|
async export(projectId, options = {}, wait = {}) {
|
|
21011
21052
|
const timeoutMs = wait.timeoutMs ?? EXPORT_TIMEOUT_MS;
|
|
21012
|
-
const sleep2 = wait.sleep ?? ((ms) => new Promise((
|
|
21053
|
+
const sleep2 = wait.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
|
|
21013
21054
|
const given = wait.pollIntervalMs;
|
|
21014
21055
|
const firstPollMs = given !== void 0 && Number.isFinite(given) && given > 0 ? given : EXPORT_FIRST_POLL_MS;
|
|
21015
21056
|
const maxPollMs = Math.max(EXPORT_MAX_POLL_MS, firstPollMs);
|
|
@@ -21311,6 +21352,23 @@ var BackendApi = class {
|
|
|
21311
21352
|
);
|
|
21312
21353
|
return { backend, key };
|
|
21313
21354
|
}
|
|
21355
|
+
/**
|
|
21356
|
+
* Replaces the access JWT's signing key (design 2026-11-01). Signing moves
|
|
21357
|
+
* to the new key at once; tokens the old keys signed keep working until
|
|
21358
|
+
* `previousKeysExpireAt` (their own 15 minutes), so nobody is signed out.
|
|
21359
|
+
* The API keys and the sessions are untouched. A worker uploaded before
|
|
21360
|
+
* this answers `BACKEND_OUTDATED` — `enable` updates it.
|
|
21361
|
+
*/
|
|
21362
|
+
async rotateSigningKey(projectId) {
|
|
21363
|
+
return this.client.request(
|
|
21364
|
+
this.path(projectId, "/signing-key/rotate"),
|
|
21365
|
+
{
|
|
21366
|
+
method: "POST",
|
|
21367
|
+
body: {},
|
|
21368
|
+
context: "Rotate signing key"
|
|
21369
|
+
}
|
|
21370
|
+
);
|
|
21371
|
+
}
|
|
21314
21372
|
/**
|
|
21315
21373
|
* The sign-in settings (spec P2-6 §12): email, Google and GitHub, the
|
|
21316
21374
|
* allowed redirect origins, whether sign-in waits for a confirmed email,
|
|
@@ -21952,16 +22010,17 @@ var FunctionsApi = class {
|
|
|
21952
22010
|
* `BACKEND_BUSY` (the project's Naro worker is not the current version and
|
|
21953
22011
|
* could not be brought up to date just now), `FUNCTION_LIMIT` (a new name
|
|
21954
22012
|
* over the plan's cap) and `FUNCTION_BUSY` (the function is being deleted).
|
|
21955
|
-
* It never turns the backend on. Two minutes.
|
|
22013
|
+
* It never turns the backend on. Two minutes, or `options.timeoutMs`
|
|
22014
|
+
* (naro-mcp's deploy_function shares two minutes across its steps).
|
|
21956
22015
|
*/
|
|
21957
|
-
async createVersion(projectId, name, request2) {
|
|
22016
|
+
async createVersion(projectId, name, request2, options = {}) {
|
|
21958
22017
|
return this.client.request(
|
|
21959
22018
|
this.functionPath(projectId, name, "/versions"),
|
|
21960
22019
|
{
|
|
21961
22020
|
method: "POST",
|
|
21962
22021
|
body: request2,
|
|
21963
22022
|
context: "Create a function version",
|
|
21964
|
-
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22023
|
+
timeoutMs: options.timeoutMs ?? PROVISION_TIMEOUT_MS
|
|
21965
22024
|
}
|
|
21966
22025
|
);
|
|
21967
22026
|
}
|
|
@@ -21978,9 +22037,10 @@ var FunctionsApi = class {
|
|
|
21978
22037
|
* not take it, so callers may still get the previous `verifyJwt` setting
|
|
21979
22038
|
* or 404 — deploy again) and `BACKEND_OUTDATED` (the project's
|
|
21980
22039
|
* database predates the function list — `backend.enable` updates it).
|
|
21981
|
-
* Two minutes.
|
|
22040
|
+
* Two minutes, or `options.timeoutMs`; a timeout here does not say
|
|
22041
|
+
* whether the version went live — `get` does.
|
|
21982
22042
|
*/
|
|
21983
|
-
async deploy(projectId, name, versionId) {
|
|
22043
|
+
async deploy(projectId, name, versionId, options = {}) {
|
|
21984
22044
|
const body = await this.client.request(
|
|
21985
22045
|
this.functionPath(
|
|
21986
22046
|
projectId,
|
|
@@ -21990,7 +22050,7 @@ var FunctionsApi = class {
|
|
|
21990
22050
|
{
|
|
21991
22051
|
method: "POST",
|
|
21992
22052
|
context: "Deploy a function",
|
|
21993
|
-
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22053
|
+
timeoutMs: options.timeoutMs ?? PROVISION_TIMEOUT_MS
|
|
21994
22054
|
}
|
|
21995
22055
|
);
|
|
21996
22056
|
return body.function;
|
|
@@ -22004,12 +22064,12 @@ var FunctionsApi = class {
|
|
|
22004
22064
|
* `VERSION_NOT_FOUND` also says there is no earlier deployed version.
|
|
22005
22065
|
* Two minutes.
|
|
22006
22066
|
*/
|
|
22007
|
-
async rollback(projectId, name,
|
|
22067
|
+
async rollback(projectId, name, version3) {
|
|
22008
22068
|
const body = await this.client.request(
|
|
22009
22069
|
this.functionPath(projectId, name, "/rollback"),
|
|
22010
22070
|
{
|
|
22011
22071
|
method: "POST",
|
|
22012
|
-
body:
|
|
22072
|
+
body: version3 === void 0 ? {} : { version: version3 },
|
|
22013
22073
|
context: "Roll back a function",
|
|
22014
22074
|
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22015
22075
|
}
|
|
@@ -22058,10 +22118,13 @@ var FunctionsApi = class {
|
|
|
22058
22118
|
return body.entries;
|
|
22059
22119
|
}
|
|
22060
22120
|
/** The project's function secrets: names, digests and when — never a value. `env.read`. */
|
|
22061
|
-
async secrets(projectId) {
|
|
22121
|
+
async secrets(projectId, options = {}) {
|
|
22062
22122
|
const body = await this.client.request(
|
|
22063
22123
|
this.path(projectId, "-secrets"),
|
|
22064
|
-
{
|
|
22124
|
+
{
|
|
22125
|
+
context: "List function secrets",
|
|
22126
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
22127
|
+
}
|
|
22065
22128
|
);
|
|
22066
22129
|
return body.secrets;
|
|
22067
22130
|
}
|
|
@@ -22121,8 +22184,8 @@ function cellText(statement2, row, column2) {
|
|
|
22121
22184
|
var MAX_CELL_CHARS = 60;
|
|
22122
22185
|
function formatCell(value) {
|
|
22123
22186
|
if (value === null || value === void 0) return "NULL";
|
|
22124
|
-
const
|
|
22125
|
-
const oneLine2 =
|
|
22187
|
+
const text3 = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
22188
|
+
const oneLine2 = text3.replace(/\s+/g, " ");
|
|
22126
22189
|
return oneLine2.length > MAX_CELL_CHARS ? `${oneLine2.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine2;
|
|
22127
22190
|
}
|
|
22128
22191
|
function renderRows(statement2) {
|
|
@@ -22152,33 +22215,9 @@ function summarizeStatement(statement2) {
|
|
|
22152
22215
|
// ../../packages/sdk/src/dotenv.ts
|
|
22153
22216
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
22154
22217
|
import { join as join20 } from "path";
|
|
22155
|
-
var
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
const vars = {};
|
|
22159
|
-
const unclosedQuotes = [];
|
|
22160
|
-
const lines = text2.replace(/\r\n?/g, "\n");
|
|
22161
|
-
let line = 1;
|
|
22162
|
-
let counted = 0;
|
|
22163
|
-
for (const match of lines.matchAll(LINE)) {
|
|
22164
|
-
const key = match[1];
|
|
22165
|
-
const raw = match[2] ?? "";
|
|
22166
|
-
let value = raw.trim();
|
|
22167
|
-
const maybeQuote = value[0];
|
|
22168
|
-
if (maybeQuote !== void 0 && QUOTES.has(maybeQuote) && !value.includes(maybeQuote, 1)) {
|
|
22169
|
-
const at = (match.indices?.[2]?.[0] ?? match.index) + (raw.length - raw.trimStart().length);
|
|
22170
|
-
for (; counted < at; counted += 1) if (lines[counted] === "\n") line += 1;
|
|
22171
|
-
unclosedQuotes.push(line);
|
|
22172
|
-
}
|
|
22173
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
22174
|
-
if (maybeQuote === '"')
|
|
22175
|
-
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
|
|
22176
|
-
vars[key] = value;
|
|
22177
|
-
}
|
|
22178
|
-
return { vars, unclosedQuotes };
|
|
22179
|
-
}
|
|
22180
|
-
function parseDotenv(text2) {
|
|
22181
|
-
return parseDotenvDetailed(text2).vars;
|
|
22218
|
+
var QUOTES2 = /* @__PURE__ */ new Set(["'", '"', "`"]);
|
|
22219
|
+
function parseDotenv(text3) {
|
|
22220
|
+
return parseDotenvDetailed(text3).vars;
|
|
22182
22221
|
}
|
|
22183
22222
|
function readProjectEnv(dir) {
|
|
22184
22223
|
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
@@ -22189,7 +22228,7 @@ function readProjectEnv(dir) {
|
|
|
22189
22228
|
}
|
|
22190
22229
|
var PLAIN = /^[^\s"'#$`\\]*$/;
|
|
22191
22230
|
function bareReadsBack(value) {
|
|
22192
|
-
return !/[#\r\n\u2028\u2029]/.test(value) && value.trim() === value && !
|
|
22231
|
+
return !/[#\r\n\u2028\u2029]/.test(value) && value.trim() === value && !QUOTES2.has(value[0] ?? "");
|
|
22193
22232
|
}
|
|
22194
22233
|
function dotenvValue(value) {
|
|
22195
22234
|
if (PLAIN.test(value)) return value;
|
|
@@ -22309,14 +22348,14 @@ function scanEnvReferences(dir) {
|
|
|
22309
22348
|
const files = [];
|
|
22310
22349
|
collect(dir, files);
|
|
22311
22350
|
for (const file2 of files) {
|
|
22312
|
-
let
|
|
22351
|
+
let text3;
|
|
22313
22352
|
try {
|
|
22314
22353
|
if (statSync(file2).size > MAX_FILE_BYTES2) continue;
|
|
22315
|
-
|
|
22354
|
+
text3 = readFileSync3(file2, "utf8");
|
|
22316
22355
|
} catch {
|
|
22317
22356
|
continue;
|
|
22318
22357
|
}
|
|
22319
|
-
for (const match of
|
|
22358
|
+
for (const match of text3.matchAll(REFERENCE)) {
|
|
22320
22359
|
const key = match[1] ?? match[2];
|
|
22321
22360
|
if (!key || ignored(key)) continue;
|
|
22322
22361
|
found.set(key, "code");
|
|
@@ -22504,15 +22543,15 @@ var SYSTEM_TABLE_PREFIXES = [
|
|
|
22504
22543
|
"sqlite_"
|
|
22505
22544
|
];
|
|
22506
22545
|
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
22507
|
-
function asciiLower(
|
|
22508
|
-
return
|
|
22546
|
+
function asciiLower(text3) {
|
|
22547
|
+
return text3.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
22509
22548
|
}
|
|
22510
22549
|
function isSystemTable(name) {
|
|
22511
22550
|
const lower = asciiLower(name);
|
|
22512
22551
|
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
22513
22552
|
}
|
|
22514
|
-
function likeLiteral(
|
|
22515
|
-
return
|
|
22553
|
+
function likeLiteral(text3) {
|
|
22554
|
+
return text3.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
22516
22555
|
}
|
|
22517
22556
|
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
22518
22557
|
function systemTablePredicate(column2 = "name") {
|
|
@@ -22538,8 +22577,8 @@ function isSpace(char) {
|
|
|
22538
22577
|
function isWordChar(char) {
|
|
22539
22578
|
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
22540
22579
|
}
|
|
22541
|
-
function asciiUpper2(
|
|
22542
|
-
return
|
|
22580
|
+
function asciiUpper2(text3) {
|
|
22581
|
+
return text3.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22543
22582
|
}
|
|
22544
22583
|
function quoted2(sql, start, close, doubles) {
|
|
22545
22584
|
let value = "";
|
|
@@ -22677,16 +22716,16 @@ function declaredCollations(sql, columns) {
|
|
|
22677
22716
|
}
|
|
22678
22717
|
|
|
22679
22718
|
// ../../packages/data-core/src/unicode-controls.ts
|
|
22680
|
-
function hasControlCharacter(
|
|
22681
|
-
for (let i = 0; i <
|
|
22682
|
-
const code =
|
|
22719
|
+
function hasControlCharacter(text3) {
|
|
22720
|
+
for (let i = 0; i < text3.length; i += 1) {
|
|
22721
|
+
const code = text3.charCodeAt(i);
|
|
22683
22722
|
if (code <= 31 || code >= 127 && code <= 159) return true;
|
|
22684
22723
|
}
|
|
22685
22724
|
return false;
|
|
22686
22725
|
}
|
|
22687
|
-
function hasBidiControl(
|
|
22688
|
-
for (let i = 0; i <
|
|
22689
|
-
const code =
|
|
22726
|
+
function hasBidiControl(text3) {
|
|
22727
|
+
for (let i = 0; i < text3.length; i += 1) {
|
|
22728
|
+
const code = text3.charCodeAt(i);
|
|
22690
22729
|
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
22691
22730
|
return true;
|
|
22692
22731
|
}
|
|
@@ -22711,8 +22750,8 @@ var RESERVED = new Set(STORAGE_RESERVED_WORDS);
|
|
|
22711
22750
|
var ENCODER = new TextEncoder();
|
|
22712
22751
|
var LONE_SURROGATE2 = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
22713
22752
|
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
22714
|
-
function hasLoneSurrogate(
|
|
22715
|
-
return LONE_SURROGATE2.test(
|
|
22753
|
+
function hasLoneSurrogate(text3) {
|
|
22754
|
+
return LONE_SURROGATE2.test(text3);
|
|
22716
22755
|
}
|
|
22717
22756
|
function refuse(reason) {
|
|
22718
22757
|
return { ok: false, reason };
|
|
@@ -22754,9 +22793,9 @@ function parseCacheControl(header) {
|
|
|
22754
22793
|
return maxAge <= MAX_MAX_AGE ? { ok: true, maxAge } : { ok: false };
|
|
22755
22794
|
}
|
|
22756
22795
|
var MAX_NAME_IN_MESSAGE = 256;
|
|
22757
|
-
function storageName(
|
|
22758
|
-
if (
|
|
22759
|
-
const cut =
|
|
22796
|
+
function storageName(text3) {
|
|
22797
|
+
if (text3.length <= MAX_NAME_IN_MESSAGE) return text3;
|
|
22798
|
+
const cut = text3.slice(0, MAX_NAME_IN_MESSAGE);
|
|
22760
22799
|
const last = cut.charCodeAt(MAX_NAME_IN_MESSAGE - 1);
|
|
22761
22800
|
return last >= 55296 && last <= 56319 ? cut.slice(0, MAX_NAME_IN_MESSAGE - 1) : cut;
|
|
22762
22801
|
}
|
|
@@ -22901,15 +22940,15 @@ var CATALOG = {
|
|
|
22901
22940
|
policy,
|
|
22902
22941
|
recursion,
|
|
22903
22942
|
values,
|
|
22904
|
-
bytes,
|
|
22943
|
+
bytes: bytes2,
|
|
22905
22944
|
depth
|
|
22906
22945
|
}) => {
|
|
22907
22946
|
if (recursion)
|
|
22908
22947
|
return `Infinite recursion detected in policy for relation ${table}.`;
|
|
22909
22948
|
if (values)
|
|
22910
22949
|
return `The policies of ${table} bind more than ${values === true ? "50" : String(values)} values in one statement.`;
|
|
22911
|
-
if (
|
|
22912
|
-
return `The policies of ${table} write more than ${
|
|
22950
|
+
if (bytes2)
|
|
22951
|
+
return `The policies of ${table} write more than ${bytes2 === true ? "50000" : String(bytes2)} bytes of SQL into one statement.`;
|
|
22913
22952
|
if (depth)
|
|
22914
22953
|
return `The policies of ${table} nest an expression more than 100 deep into one statement.`;
|
|
22915
22954
|
if (policy !== void 0)
|
|
@@ -23110,11 +23149,11 @@ function isDigit(char) {
|
|
|
23110
23149
|
return char >= "0" && char <= "9";
|
|
23111
23150
|
}
|
|
23112
23151
|
var NUMBER = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
|
23113
|
-
function tokenize2(
|
|
23152
|
+
function tokenize2(text3) {
|
|
23114
23153
|
const tokens = [];
|
|
23115
23154
|
let index = 0;
|
|
23116
|
-
const at = (offset = 0) =>
|
|
23117
|
-
while (index <
|
|
23155
|
+
const at = (offset = 0) => text3.charAt(index + offset);
|
|
23156
|
+
while (index < text3.length) {
|
|
23118
23157
|
const char = at();
|
|
23119
23158
|
const position = index + 1;
|
|
23120
23159
|
if (char === "\0")
|
|
@@ -23124,12 +23163,12 @@ function tokenize2(text2) {
|
|
|
23124
23163
|
continue;
|
|
23125
23164
|
}
|
|
23126
23165
|
if (char === "-" && at(1) === "-") {
|
|
23127
|
-
while (index <
|
|
23166
|
+
while (index < text3.length && at() !== "\n" && at() !== "\r") index++;
|
|
23128
23167
|
continue;
|
|
23129
23168
|
}
|
|
23130
23169
|
if (char === "/" && at(1) === "*") {
|
|
23131
23170
|
let depth = 0;
|
|
23132
|
-
while (index <
|
|
23171
|
+
while (index < text3.length) {
|
|
23133
23172
|
if (at() === "/" && at(1) === "*") {
|
|
23134
23173
|
depth++;
|
|
23135
23174
|
index += 2;
|
|
@@ -23147,7 +23186,7 @@ function tokenize2(text2) {
|
|
|
23147
23186
|
let value = "";
|
|
23148
23187
|
index++;
|
|
23149
23188
|
for (; ; ) {
|
|
23150
|
-
if (index >=
|
|
23189
|
+
if (index >= text3.length)
|
|
23151
23190
|
throw new PolicySyntaxError("unterminated quoted string", position);
|
|
23152
23191
|
if (at() === "'") {
|
|
23153
23192
|
if (at(1) === "'") {
|
|
@@ -23168,7 +23207,7 @@ function tokenize2(text2) {
|
|
|
23168
23207
|
let value = "";
|
|
23169
23208
|
index++;
|
|
23170
23209
|
for (; ; ) {
|
|
23171
|
-
if (index >=
|
|
23210
|
+
if (index >= text3.length)
|
|
23172
23211
|
throw new PolicySyntaxError(
|
|
23173
23212
|
"unterminated quoted identifier",
|
|
23174
23213
|
position
|
|
@@ -23196,10 +23235,10 @@ function tokenize2(text2) {
|
|
|
23196
23235
|
position
|
|
23197
23236
|
);
|
|
23198
23237
|
if (isDigit(char) || char === "." && isDigit(at(1))) {
|
|
23199
|
-
const match = NUMBER.exec(
|
|
23238
|
+
const match = NUMBER.exec(text3.slice(index));
|
|
23200
23239
|
const value = match?.[0] ?? char;
|
|
23201
23240
|
index += value.length;
|
|
23202
|
-
if (index <
|
|
23241
|
+
if (index < text3.length && isIdentChar(at()))
|
|
23203
23242
|
throw new PolicySyntaxError(
|
|
23204
23243
|
`trailing junk after numeric literal ${value}`,
|
|
23205
23244
|
position
|
|
@@ -23209,9 +23248,9 @@ function tokenize2(text2) {
|
|
|
23209
23248
|
}
|
|
23210
23249
|
if (isIdentStart(char)) {
|
|
23211
23250
|
let end = index + 1;
|
|
23212
|
-
while (end <
|
|
23213
|
-
const value =
|
|
23214
|
-
if (
|
|
23251
|
+
while (end < text3.length && isIdentChar(text3.charAt(end))) end++;
|
|
23252
|
+
const value = text3.slice(index, end);
|
|
23253
|
+
if (text3.charAt(end) === "'")
|
|
23215
23254
|
throw new PolicySyntaxError(
|
|
23216
23255
|
`${value}'\u2026' strings are not supported: use a plain '\u2026' string`,
|
|
23217
23256
|
position
|
|
@@ -23221,7 +23260,7 @@ function tokenize2(text2) {
|
|
|
23221
23260
|
continue;
|
|
23222
23261
|
}
|
|
23223
23262
|
const operator = OPERATORS.find(
|
|
23224
|
-
(candidate) =>
|
|
23263
|
+
(candidate) => text3.startsWith(candidate, index)
|
|
23225
23264
|
);
|
|
23226
23265
|
if (!operator)
|
|
23227
23266
|
throw new PolicySyntaxError(
|
|
@@ -23235,7 +23274,7 @@ function tokenize2(text2) {
|
|
|
23235
23274
|
kind: "eof",
|
|
23236
23275
|
value: "",
|
|
23237
23276
|
quoted: false,
|
|
23238
|
-
position:
|
|
23277
|
+
position: text3.length + 1
|
|
23239
23278
|
});
|
|
23240
23279
|
return tokens;
|
|
23241
23280
|
}
|
|
@@ -23564,21 +23603,21 @@ function toBoundValue(value) {
|
|
|
23564
23603
|
if (typeof value === "string" || typeof value === "number") return value;
|
|
23565
23604
|
return JSON.stringify(value);
|
|
23566
23605
|
}
|
|
23567
|
-
function utf8ByteLength(
|
|
23568
|
-
let
|
|
23569
|
-
for (let index = 0; index <
|
|
23570
|
-
const unit =
|
|
23571
|
-
if (unit < 128)
|
|
23572
|
-
else if (unit < 2048)
|
|
23573
|
-
else if (unit >= 55296 && unit <= 56319 && index + 1 <
|
|
23574
|
-
const next =
|
|
23606
|
+
function utf8ByteLength(text3) {
|
|
23607
|
+
let bytes2 = 0;
|
|
23608
|
+
for (let index = 0; index < text3.length; index++) {
|
|
23609
|
+
const unit = text3.charCodeAt(index);
|
|
23610
|
+
if (unit < 128) bytes2 += 1;
|
|
23611
|
+
else if (unit < 2048) bytes2 += 2;
|
|
23612
|
+
else if (unit >= 55296 && unit <= 56319 && index + 1 < text3.length) {
|
|
23613
|
+
const next = text3.charCodeAt(index + 1);
|
|
23575
23614
|
if (next >= 56320 && next <= 57343) {
|
|
23576
|
-
|
|
23615
|
+
bytes2 += 4;
|
|
23577
23616
|
index++;
|
|
23578
|
-
} else
|
|
23579
|
-
} else
|
|
23617
|
+
} else bytes2 += 3;
|
|
23618
|
+
} else bytes2 += 3;
|
|
23580
23619
|
}
|
|
23581
|
-
return
|
|
23620
|
+
return bytes2;
|
|
23582
23621
|
}
|
|
23583
23622
|
|
|
23584
23623
|
// ../../packages/data-core/src/sql-fragments.ts
|
|
@@ -23672,9 +23711,9 @@ function isNumeric(item) {
|
|
|
23672
23711
|
const affinity2 = columnAffinity(item);
|
|
23673
23712
|
return affinity2 !== "TEXT" && affinity2 !== "BLOB";
|
|
23674
23713
|
}
|
|
23675
|
-
function refusesText(item,
|
|
23714
|
+
function refusesText(item, text3) {
|
|
23676
23715
|
const kind = numberClass(item.type);
|
|
23677
|
-
return kind === "number" || kind === "fallback" && storesAsNumber(
|
|
23716
|
+
return kind === "number" || kind === "fallback" && storesAsNumber(text3);
|
|
23678
23717
|
}
|
|
23679
23718
|
function readsAsIs(item, value) {
|
|
23680
23719
|
return typeof value === "string" && numberClass(item.type) === "fallback" && !storesAsNumber(value);
|
|
@@ -23700,15 +23739,15 @@ function asStored(item, sql) {
|
|
|
23700
23739
|
var LITERAL_DEFAULT = /^(?:null|true|false|[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|'(?:[^']|'')*')$/i;
|
|
23701
23740
|
function omittedSql(item) {
|
|
23702
23741
|
if (item.primaryKey > 0) return void 0;
|
|
23703
|
-
const
|
|
23704
|
-
if (
|
|
23705
|
-
if (!LITERAL_DEFAULT.test(
|
|
23706
|
-
const word = asciiLower(
|
|
23742
|
+
const text3 = item.defaultValue;
|
|
23743
|
+
if (text3 === null) return "NULL";
|
|
23744
|
+
if (!LITERAL_DEFAULT.test(text3)) return void 0;
|
|
23745
|
+
const word = asciiLower(text3);
|
|
23707
23746
|
if (word === "null") return "NULL";
|
|
23708
23747
|
if (word === "true" || word === "false")
|
|
23709
23748
|
return asStored(item, word === "true" ? "1" : "0");
|
|
23710
|
-
if (
|
|
23711
|
-
return asStored(item,
|
|
23749
|
+
if (text3.startsWith("'") && isNumeric(item)) return void 0;
|
|
23750
|
+
return asStored(item, text3);
|
|
23712
23751
|
}
|
|
23713
23752
|
var DEFAULT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
23714
23753
|
"true",
|
|
@@ -23719,27 +23758,27 @@ var DEFAULT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
23719
23758
|
"current_timestamp"
|
|
23720
23759
|
]);
|
|
23721
23760
|
var BARE_WORD = /^[A-Za-z_\u0080-][A-Za-z0-9_$\u0080-]*$/;
|
|
23722
|
-
function quotedName(
|
|
23723
|
-
const double = /^"((?:[^"]|"")*)"$/.exec(
|
|
23761
|
+
function quotedName(text3) {
|
|
23762
|
+
const double = /^"((?:[^"]|"")*)"$/.exec(text3);
|
|
23724
23763
|
if (double) return (double[1] ?? "").replaceAll('""', '"');
|
|
23725
|
-
const tick = /^`((?:[^`]|``)*)`$/.exec(
|
|
23764
|
+
const tick = /^`((?:[^`]|``)*)`$/.exec(text3);
|
|
23726
23765
|
if (tick) return (tick[1] ?? "").replaceAll("``", "`");
|
|
23727
|
-
const bracket = /^\[([^\]]*)\]$/.exec(
|
|
23766
|
+
const bracket = /^\[([^\]]*)\]$/.exec(text3);
|
|
23728
23767
|
return bracket ? bracket[1] ?? "" : void 0;
|
|
23729
23768
|
}
|
|
23730
23769
|
function stringDefault(item) {
|
|
23731
|
-
let
|
|
23732
|
-
if (
|
|
23733
|
-
while (
|
|
23734
|
-
|
|
23735
|
-
if (/^\+\s*(?:'|x')/i.test(
|
|
23736
|
-
if (/^'(?:[^']|'')*'$/.test(
|
|
23737
|
-
return { kind: "text", text:
|
|
23738
|
-
if (/^x'[0-9a-f]*'$/i.test(
|
|
23739
|
-
const name = quotedName(
|
|
23770
|
+
let text3 = item.defaultValue?.trim();
|
|
23771
|
+
if (text3 === void 0) return void 0;
|
|
23772
|
+
while (text3.startsWith("(") && text3.endsWith(")"))
|
|
23773
|
+
text3 = text3.slice(1, -1).trim();
|
|
23774
|
+
if (/^\+\s*(?:'|x')/i.test(text3)) text3 = text3.slice(1).trim();
|
|
23775
|
+
if (/^'(?:[^']|'')*'$/.test(text3))
|
|
23776
|
+
return { kind: "text", text: text3.slice(1, -1).replaceAll("''", "'") };
|
|
23777
|
+
if (/^x'[0-9a-f]*'$/i.test(text3)) return { kind: "blob" };
|
|
23778
|
+
const name = quotedName(text3);
|
|
23740
23779
|
if (name !== void 0) return { kind: "text", text: name };
|
|
23741
|
-
if (BARE_WORD.test(
|
|
23742
|
-
return { kind: "text", text:
|
|
23780
|
+
if (BARE_WORD.test(text3) && !DEFAULT_KEYWORDS.has(asciiLower(text3)))
|
|
23781
|
+
return { kind: "text", text: text3 };
|
|
23743
23782
|
return void 0;
|
|
23744
23783
|
}
|
|
23745
23784
|
var DEFAULT_CHECK_NONCE = "0";
|
|
@@ -23871,25 +23910,25 @@ function nested(state, token, read) {
|
|
|
23871
23910
|
}
|
|
23872
23911
|
}
|
|
23873
23912
|
function truthProblem(operand) {
|
|
23874
|
-
const
|
|
23913
|
+
const text3 = "is text, where 'true' reads as false";
|
|
23875
23914
|
switch (operand.kind) {
|
|
23876
23915
|
case "column":
|
|
23877
23916
|
return void 0;
|
|
23878
23917
|
case "literal":
|
|
23879
|
-
return typeof operand.value === "string" ? `a '\u2026' string ${
|
|
23918
|
+
return typeof operand.value === "string" ? `a '\u2026' string ${text3}: write TRUE or FALSE, or compare the string with =` : void 0;
|
|
23880
23919
|
case "jwt": {
|
|
23881
23920
|
const claim = printValue(operand);
|
|
23882
|
-
return `${claim} ${
|
|
23921
|
+
return `${claim} ${text3}: compare it with text, ${claim} = 'true'`;
|
|
23883
23922
|
}
|
|
23884
23923
|
case "auth":
|
|
23885
23924
|
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'";
|
|
23886
23925
|
case "cast":
|
|
23887
|
-
return `a ::text cast ${
|
|
23926
|
+
return `a ::text cast ${text3}: compare it with text, (\u2026)::text = 'true'`;
|
|
23888
23927
|
case "func":
|
|
23889
23928
|
switch (operand.name) {
|
|
23890
23929
|
case "lower":
|
|
23891
23930
|
case "upper":
|
|
23892
|
-
return `${operand.name}(\u2026) ${
|
|
23931
|
+
return `${operand.name}(\u2026) ${text3}: compare it with text, ${operand.name}(\u2026) = 'true'`;
|
|
23893
23932
|
case "coalesce":
|
|
23894
23933
|
return "coalesce(\u2026) is not a boolean: compare it, coalesce(\u2026) = 'true'";
|
|
23895
23934
|
case "length":
|
|
@@ -23899,10 +23938,10 @@ function truthProblem(operand) {
|
|
|
23899
23938
|
}
|
|
23900
23939
|
case "storage": {
|
|
23901
23940
|
const call = operand.fn === "foldername" ? "(storage.foldername(\u2026))[n]" : `storage.${operand.fn}(\u2026)`;
|
|
23902
|
-
return `${call} ${
|
|
23941
|
+
return `${call} ${text3}: compare it with text, ${call} = 'true'`;
|
|
23903
23942
|
}
|
|
23904
23943
|
case "realtime":
|
|
23905
|
-
return `realtime.topic() ${
|
|
23944
|
+
return `realtime.topic() ${text3}: compare it with text, realtime.topic() = 'room:1'`;
|
|
23906
23945
|
}
|
|
23907
23946
|
}
|
|
23908
23947
|
function truthTestProblem(operand) {
|
|
@@ -23956,9 +23995,9 @@ function booleanComparisonProblem(expr) {
|
|
|
23956
23995
|
const met = [...operandValues(left), ...operandValues(right)];
|
|
23957
23996
|
if (!met.some(isBooleanConstant)) continue;
|
|
23958
23997
|
const texts = met.filter(isTextValue);
|
|
23959
|
-
const
|
|
23960
|
-
if (
|
|
23961
|
-
const problem =
|
|
23998
|
+
const text3 = texts.find((value) => value.kind !== "literal") ?? texts[0];
|
|
23999
|
+
if (text3 === void 0) continue;
|
|
24000
|
+
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);
|
|
23962
24001
|
return `a comparison with TRUE or FALSE compares a boolean, and ${String(problem)}`;
|
|
23963
24002
|
}
|
|
23964
24003
|
return void 0;
|
|
@@ -24036,17 +24075,17 @@ function readPredicate(state) {
|
|
|
24036
24075
|
}
|
|
24037
24076
|
const left = readValue(state);
|
|
24038
24077
|
const next = stream.peek();
|
|
24039
|
-
const
|
|
24078
|
+
const compare3 = ["=", "<>", "!=", "<", "<=", ">", ">="].find(
|
|
24040
24079
|
(op) => isOperator(next, op)
|
|
24041
24080
|
);
|
|
24042
|
-
if (
|
|
24081
|
+
if (compare3) {
|
|
24043
24082
|
stream.next();
|
|
24044
24083
|
count(state, next);
|
|
24045
24084
|
const right = readValue(state);
|
|
24046
24085
|
return comparable(
|
|
24047
24086
|
{
|
|
24048
24087
|
kind: "compare",
|
|
24049
|
-
op:
|
|
24088
|
+
op: compare3 === "!=" ? "<>" : compare3,
|
|
24050
24089
|
left: requireValue(left, token),
|
|
24051
24090
|
right: requireValue(right, next)
|
|
24052
24091
|
},
|
|
@@ -24148,8 +24187,8 @@ function policyPatternProblem(pattern, ci) {
|
|
|
24148
24187
|
if (!patternTooLong(pattern, ci)) return void 0;
|
|
24149
24188
|
const chars = [...pattern];
|
|
24150
24189
|
const shown = `${commentText(chars.slice(0, PATTERN_SHOWN).join(""))}${chars.length > PATTERN_SHOWN ? "\u2026" : ""}`;
|
|
24151
|
-
const
|
|
24152
|
-
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(
|
|
24190
|
+
const bytes2 = utf8ByteLength(boundPattern(pattern, ci));
|
|
24191
|
+
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)"}`;
|
|
24153
24192
|
}
|
|
24154
24193
|
function checkPattern(token, ci) {
|
|
24155
24194
|
const pattern = token.value;
|
|
@@ -24670,17 +24709,17 @@ function readCondition(stream) {
|
|
|
24670
24709
|
return requireCondition(expr, token);
|
|
24671
24710
|
}
|
|
24672
24711
|
function checkExpressionSize(expr, position) {
|
|
24673
|
-
const
|
|
24674
|
-
if (utf8ByteLength(
|
|
24712
|
+
const text3 = printExpr(expr);
|
|
24713
|
+
if (utf8ByteLength(text3) > MAX_EXPRESSION_BYTES)
|
|
24675
24714
|
throw new PolicySyntaxError(
|
|
24676
24715
|
`the expression is longer than ${String(MAX_EXPRESSION_BYTES)} bytes`,
|
|
24677
24716
|
position
|
|
24678
24717
|
);
|
|
24679
|
-
return
|
|
24718
|
+
return text3;
|
|
24680
24719
|
}
|
|
24681
|
-
function parseExpression(
|
|
24720
|
+
function parseExpression(text3) {
|
|
24682
24721
|
try {
|
|
24683
|
-
const stream = new TokenStream(tokenize2(
|
|
24722
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
24684
24723
|
if (stream.peek().kind === "eof")
|
|
24685
24724
|
throw new PolicySyntaxError("the expression is empty", 1);
|
|
24686
24725
|
const expr = readCondition(stream);
|
|
@@ -24695,22 +24734,22 @@ function parseExpression(text2) {
|
|
|
24695
24734
|
function quoteIdent(name) {
|
|
24696
24735
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !SPECIAL_WORDS.has(asciiLower(name)) ? name : `"${name.replaceAll('"', '""')}"`;
|
|
24697
24736
|
}
|
|
24698
|
-
function commentText(
|
|
24737
|
+
function commentText(text3) {
|
|
24699
24738
|
let out = "";
|
|
24700
|
-
for (const char of
|
|
24739
|
+
for (const char of text3) {
|
|
24701
24740
|
const code = char.charCodeAt(0);
|
|
24702
24741
|
const control = code < 32 || code >= 127 && code <= 159 || code === 8232 || code === 8233;
|
|
24703
24742
|
out += control ? `\\u${code.toString(16).padStart(4, "0")}` : char;
|
|
24704
24743
|
}
|
|
24705
24744
|
return out;
|
|
24706
24745
|
}
|
|
24707
|
-
function quoteString(
|
|
24708
|
-
return `'${
|
|
24746
|
+
function quoteString(text3) {
|
|
24747
|
+
return `'${text3.replaceAll("'", "''")}'`;
|
|
24709
24748
|
}
|
|
24710
24749
|
function printNumber(value) {
|
|
24711
24750
|
if (Object.is(value, -0)) return "-0.0";
|
|
24712
|
-
const
|
|
24713
|
-
return Number.isSafeInteger(value) || /[.e]/.test(
|
|
24751
|
+
const text3 = String(value);
|
|
24752
|
+
return Number.isSafeInteger(value) || /[.e]/.test(text3) ? text3 : `${text3}.0`;
|
|
24714
24753
|
}
|
|
24715
24754
|
function printLiteral(value) {
|
|
24716
24755
|
if (value === null) return "NULL";
|
|
@@ -24964,8 +25003,8 @@ function ownerExpression(column2) {
|
|
|
24964
25003
|
});
|
|
24965
25004
|
}
|
|
24966
25005
|
function ownerClauses(column2, command) {
|
|
24967
|
-
const
|
|
24968
|
-
return command === "insert" ? { using: null, check:
|
|
25006
|
+
const text3 = ownerExpression(column2);
|
|
25007
|
+
return command === "insert" ? { using: null, check: text3 } : { using: text3, check: null };
|
|
24969
25008
|
}
|
|
24970
25009
|
var NAMED_POLICY_TABLE = "_naro_policies_v2";
|
|
24971
25010
|
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))`;
|
|
@@ -25077,11 +25116,11 @@ function policiesOn(policies, table) {
|
|
|
25077
25116
|
function foldPolicyName(name, quoted3) {
|
|
25078
25117
|
return quoted3 ? name : asciiLower(name);
|
|
25079
25118
|
}
|
|
25080
|
-
function sqlStringLiteral(
|
|
25081
|
-
return
|
|
25119
|
+
function sqlStringLiteral(text3) {
|
|
25120
|
+
return text3 === null ? "NULL" : quoteString(text3);
|
|
25082
25121
|
}
|
|
25083
|
-
function policySqlTooLong(
|
|
25084
|
-
return utf8ByteLength(
|
|
25122
|
+
function policySqlTooLong(text3) {
|
|
25123
|
+
return utf8ByteLength(text3) > MAX_POLICY_SQL_BYTES;
|
|
25085
25124
|
}
|
|
25086
25125
|
|
|
25087
25126
|
// ../../packages/data-core/src/query.ts
|
|
@@ -25171,11 +25210,11 @@ function readValue2(value, path, auth = false) {
|
|
|
25171
25210
|
);
|
|
25172
25211
|
return value;
|
|
25173
25212
|
}
|
|
25174
|
-
function longerThan(
|
|
25175
|
-
if (
|
|
25176
|
-
if (
|
|
25213
|
+
function longerThan(text3, max) {
|
|
25214
|
+
if (text3.length <= max) return false;
|
|
25215
|
+
if (text3.length > max * 2) return true;
|
|
25177
25216
|
let count2 = 0;
|
|
25178
|
-
for (const _ of
|
|
25217
|
+
for (const _ of text3) if (++count2 > max) return true;
|
|
25179
25218
|
return false;
|
|
25180
25219
|
}
|
|
25181
25220
|
function endsWithLoneEscape(pattern) {
|
|
@@ -25332,8 +25371,8 @@ function parseCondition(action, value) {
|
|
|
25332
25371
|
}
|
|
25333
25372
|
function conditionsTooLarge(texts) {
|
|
25334
25373
|
let total = 0;
|
|
25335
|
-
for (const
|
|
25336
|
-
if (typeof
|
|
25374
|
+
for (const text3 of texts)
|
|
25375
|
+
if (typeof text3 === "string") total += utf8ByteLength(text3);
|
|
25337
25376
|
return total > MAX_CONDITION_BYTES;
|
|
25338
25377
|
}
|
|
25339
25378
|
function isInvalidCondition(value) {
|
|
@@ -25419,14 +25458,14 @@ var POLICY_SQL = {
|
|
|
25419
25458
|
*/
|
|
25420
25459
|
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})`
|
|
25421
25460
|
};
|
|
25422
|
-
function readStoredCondition(action,
|
|
25423
|
-
if (
|
|
25424
|
-
if (typeof
|
|
25461
|
+
function readStoredCondition(action, text3, tooLarge) {
|
|
25462
|
+
if (text3 === void 0 || text3 === null) return "is missing";
|
|
25463
|
+
if (typeof text3 !== "string") return "is not JSON";
|
|
25425
25464
|
if (tooLarge)
|
|
25426
25465
|
return `the conditions of the policy are larger than ${String(MAX_CONDITION_BYTES)} bytes`;
|
|
25427
25466
|
let value;
|
|
25428
25467
|
try {
|
|
25429
|
-
value = JSON.parse(
|
|
25468
|
+
value = JSON.parse(text3);
|
|
25430
25469
|
} catch {
|
|
25431
25470
|
return "is not JSON";
|
|
25432
25471
|
}
|
|
@@ -25441,7 +25480,7 @@ function policyFromRow(row, log2) {
|
|
|
25441
25480
|
(action) => row[`${action}_rule`] === CONDITION_RULE ? row[`${action}_condition`] : void 0
|
|
25442
25481
|
);
|
|
25443
25482
|
const tooLarge = conditionsTooLarge(
|
|
25444
|
-
texts.filter((
|
|
25483
|
+
texts.filter((text3) => typeof text3 === "string")
|
|
25445
25484
|
);
|
|
25446
25485
|
const read = POLICY_ACTIONS2.map(
|
|
25447
25486
|
(action, index) => {
|
|
@@ -26619,13 +26658,13 @@ function boundBytes(value) {
|
|
|
26619
26658
|
return typeof value === "string" ? utf8ByteLength(value) : 8;
|
|
26620
26659
|
}
|
|
26621
26660
|
function captureGuard(table, where, params, nonce, set2) {
|
|
26622
|
-
const
|
|
26661
|
+
const bytes2 = balancedSum(
|
|
26623
26662
|
table.columns.map(
|
|
26624
26663
|
(item) => set2.has(item.name) ? String(boundBytes(set2.get(item.name) ?? null)) : `coalesce(length(CAST(${qualifiedColumn(table.name, item.name)} AS BLOB)), 8)`
|
|
26625
26664
|
)
|
|
26626
26665
|
);
|
|
26627
26666
|
return statement(
|
|
26628
|
-
`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 ${
|
|
26667
|
+
`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)}))`,
|
|
26629
26668
|
params
|
|
26630
26669
|
);
|
|
26631
26670
|
}
|
|
@@ -27280,18 +27319,18 @@ var Rls = class {
|
|
|
27280
27319
|
}),
|
|
27281
27320
|
`they bind ${String(count2)}`
|
|
27282
27321
|
);
|
|
27283
|
-
const
|
|
27322
|
+
const bytes2 = parts.reduce(
|
|
27284
27323
|
(sum, part) => sum + utf8ByteLength(part.sql),
|
|
27285
27324
|
0
|
|
27286
27325
|
);
|
|
27287
|
-
if (
|
|
27326
|
+
if (bytes2 > MAX_POLICY_STATEMENT_BYTES)
|
|
27288
27327
|
fail(
|
|
27289
27328
|
dataError("POLICY_INVALID", {
|
|
27290
27329
|
action: "select",
|
|
27291
27330
|
table: this.policyTable,
|
|
27292
27331
|
bytes: true
|
|
27293
27332
|
}),
|
|
27294
|
-
`they write ${String(
|
|
27333
|
+
`they write ${String(bytes2)}`
|
|
27295
27334
|
);
|
|
27296
27335
|
}
|
|
27297
27336
|
};
|
|
@@ -27980,11 +28019,11 @@ function tokenize3(sql) {
|
|
|
27980
28019
|
at = closing("'") + 1;
|
|
27981
28020
|
tokens.push({ kind: "literal" });
|
|
27982
28021
|
} else if (isDigit2(code) || code === 46 && isDigit2(next)) {
|
|
27983
|
-
const
|
|
28022
|
+
const text3 = NUMBER2.exec(sql.slice(at, at + 64))?.[0] || sql[at] || "";
|
|
27984
28023
|
tokens.push(
|
|
27985
|
-
/^[0-9]+$/.test(
|
|
28024
|
+
/^[0-9]+$/.test(text3) ? { kind: "literal", integer: Number(text3) } : { kind: "literal" }
|
|
27986
28025
|
);
|
|
27987
|
-
at +=
|
|
28026
|
+
at += text3.length;
|
|
27988
28027
|
} else if (code === 63 || code === 58 || code === 64 || code === 36) {
|
|
27989
28028
|
at++;
|
|
27990
28029
|
while (at < length && isWordPart(sql.charCodeAt(at))) at++;
|
|
@@ -27992,8 +28031,8 @@ function tokenize3(sql) {
|
|
|
27992
28031
|
} else if (isWordStart(code)) {
|
|
27993
28032
|
const start = at;
|
|
27994
28033
|
while (at < length && isWordPart(sql.charCodeAt(at))) at++;
|
|
27995
|
-
const
|
|
27996
|
-
tokens.push({ kind: "word", text:
|
|
28034
|
+
const text3 = sql.slice(start, at);
|
|
28035
|
+
tokens.push({ kind: "word", text: text3, upper: text3.toUpperCase() });
|
|
27997
28036
|
} else if (sql.startsWith("->>", at)) {
|
|
27998
28037
|
tokens.push({ kind: "op", text: "->>" });
|
|
27999
28038
|
at += 3;
|
|
@@ -28703,31 +28742,31 @@ function isWordStart2(char) {
|
|
|
28703
28742
|
function isWordPart2(char) {
|
|
28704
28743
|
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28705
28744
|
}
|
|
28706
|
-
function skipBlanks(
|
|
28745
|
+
function skipBlanks(text3, from) {
|
|
28707
28746
|
let index = from;
|
|
28708
28747
|
for (; ; ) {
|
|
28709
|
-
const char =
|
|
28748
|
+
const char = text3.charAt(index);
|
|
28710
28749
|
if (isBlankStart(char)) {
|
|
28711
28750
|
index++;
|
|
28712
|
-
while (index <
|
|
28713
|
-
} else if (
|
|
28714
|
-
else if (char === "-" &&
|
|
28715
|
-
const end =
|
|
28716
|
-
index = end < 0 ?
|
|
28717
|
-
} else if (char === "/" &&
|
|
28718
|
-
const end =
|
|
28719
|
-
index = end < 0 ?
|
|
28751
|
+
while (index < text3.length && isBlank(text3.charAt(index))) index++;
|
|
28752
|
+
} else if (text3.charCodeAt(index) === BOM2) index++;
|
|
28753
|
+
else if (char === "-" && text3.charAt(index + 1) === "-") {
|
|
28754
|
+
const end = text3.indexOf("\n", index + 2);
|
|
28755
|
+
index = end < 0 ? text3.length : end;
|
|
28756
|
+
} else if (char === "/" && text3.charAt(index + 1) === "*") {
|
|
28757
|
+
const end = text3.indexOf("*/", index + 2);
|
|
28758
|
+
index = end < 0 ? text3.length : end + 2;
|
|
28720
28759
|
} else return index;
|
|
28721
28760
|
}
|
|
28722
28761
|
}
|
|
28723
|
-
function readQuoted(
|
|
28762
|
+
function readQuoted(text3, start, delimiter) {
|
|
28724
28763
|
let value = "";
|
|
28725
28764
|
let index = start + 1;
|
|
28726
28765
|
for (; ; ) {
|
|
28727
|
-
const close =
|
|
28766
|
+
const close = text3.indexOf(delimiter, index);
|
|
28728
28767
|
if (close < 0) return void 0;
|
|
28729
|
-
value +=
|
|
28730
|
-
if (
|
|
28768
|
+
value += text3.slice(index, close);
|
|
28769
|
+
if (text3.charAt(close + 1) === delimiter) {
|
|
28731
28770
|
value += delimiter;
|
|
28732
28771
|
index = close + 2;
|
|
28733
28772
|
continue;
|
|
@@ -28735,31 +28774,31 @@ function readQuoted(text2, start, delimiter) {
|
|
|
28735
28774
|
return { text: value, end: close + 1 };
|
|
28736
28775
|
}
|
|
28737
28776
|
}
|
|
28738
|
-
function headTokens(
|
|
28777
|
+
function headTokens(text3) {
|
|
28739
28778
|
const tokens = [];
|
|
28740
28779
|
let index = 0;
|
|
28741
28780
|
while (tokens.length < HEAD_TOKENS) {
|
|
28742
|
-
index = skipBlanks(
|
|
28743
|
-
const char =
|
|
28744
|
-
if (index >=
|
|
28781
|
+
index = skipBlanks(text3, index);
|
|
28782
|
+
const char = text3.charAt(index);
|
|
28783
|
+
if (index >= text3.length) break;
|
|
28745
28784
|
if (char === '"' || char === "`" || char === "'") {
|
|
28746
|
-
const quoted3 = readQuoted(
|
|
28785
|
+
const quoted3 = readQuoted(text3, index, char);
|
|
28747
28786
|
if (!quoted3) break;
|
|
28748
28787
|
tokens.push({ kind: "name", text: quoted3.text });
|
|
28749
28788
|
index = quoted3.end;
|
|
28750
28789
|
} else if (char === "[") {
|
|
28751
|
-
const close =
|
|
28790
|
+
const close = text3.indexOf("]", index + 1);
|
|
28752
28791
|
if (close < 0) break;
|
|
28753
|
-
tokens.push({ kind: "name", text:
|
|
28792
|
+
tokens.push({ kind: "name", text: text3.slice(index + 1, close) });
|
|
28754
28793
|
index = close + 1;
|
|
28755
|
-
} else if ((char === "x" || char === "X") &&
|
|
28794
|
+
} else if ((char === "x" || char === "X") && text3.charAt(index + 1) === "'") {
|
|
28756
28795
|
break;
|
|
28757
28796
|
} else if (isWordStart2(char)) {
|
|
28758
28797
|
let end = index + 1;
|
|
28759
|
-
while (end <
|
|
28760
|
-
tokens.push({ kind: "word", text:
|
|
28798
|
+
while (end < text3.length && isWordPart2(text3.charAt(end))) end++;
|
|
28799
|
+
tokens.push({ kind: "word", text: text3.slice(index, end) });
|
|
28761
28800
|
index = end;
|
|
28762
|
-
} else if (char === "." && !/[0-9]/.test(
|
|
28801
|
+
} else if (char === "." && !/[0-9]/.test(text3.charAt(index + 1))) {
|
|
28763
28802
|
tokens.push({ kind: "dot" });
|
|
28764
28803
|
index++;
|
|
28765
28804
|
} else break;
|
|
@@ -28899,10 +28938,10 @@ function readAlter(head) {
|
|
|
28899
28938
|
return { kind: "rls" };
|
|
28900
28939
|
return sqlite.ok ? OTHER : sqlite.head;
|
|
28901
28940
|
}
|
|
28902
|
-
function readSqlHead(
|
|
28903
|
-
if (
|
|
28941
|
+
function readSqlHead(text3) {
|
|
28942
|
+
if (text3.includes("\0"))
|
|
28904
28943
|
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
28905
|
-
const head = new Head(headTokens(
|
|
28944
|
+
const head = new Head(headTokens(text3));
|
|
28906
28945
|
if (head.is(0, "create")) return readCreate(head);
|
|
28907
28946
|
if (head.is(0, "drop")) return readDrop(head);
|
|
28908
28947
|
if (head.is(0, "alter")) return readAlter(head);
|
|
@@ -29423,15 +29462,15 @@ function storageStatementProblem(statement2, action) {
|
|
|
29423
29462
|
}),
|
|
29424
29463
|
reason: `the ${STORAGE_OBJECTS} policies need more than ${String(STORAGE_STATEMENT_PARAM_LIMIT)} values in one statement`
|
|
29425
29464
|
};
|
|
29426
|
-
const
|
|
29427
|
-
if (
|
|
29465
|
+
const bytes2 = utf8ByteLength(statement2.sql);
|
|
29466
|
+
if (bytes2 > STORAGE_STATEMENT_BYTE_LIMIT)
|
|
29428
29467
|
return {
|
|
29429
29468
|
error: dataError("POLICY_INVALID", {
|
|
29430
29469
|
action,
|
|
29431
29470
|
table: STORAGE_OBJECTS,
|
|
29432
29471
|
bytes: STORAGE_STATEMENT_BYTE_LIMIT
|
|
29433
29472
|
}),
|
|
29434
|
-
reason: `the ${STORAGE_OBJECTS} policies make one statement ${String(
|
|
29473
|
+
reason: `the ${STORAGE_OBJECTS} policies make one statement ${String(bytes2)} bytes long, more than ${String(STORAGE_STATEMENT_BYTE_LIMIT)}`
|
|
29435
29474
|
};
|
|
29436
29475
|
return void 0;
|
|
29437
29476
|
}
|
|
@@ -29704,8 +29743,8 @@ function budgetProblem(fragment, action) {
|
|
|
29704
29743
|
}),
|
|
29705
29744
|
reason: `the ${STORAGE_OBJECTS} policies need more than ${String(STORAGE_STATEMENT_PARAM_LIMIT)} values in one statement`
|
|
29706
29745
|
};
|
|
29707
|
-
const
|
|
29708
|
-
if (
|
|
29746
|
+
const bytes2 = utf8ByteLength(fragment.sql);
|
|
29747
|
+
if (bytes2 > MAX_POLICY_STATEMENT_BYTES)
|
|
29709
29748
|
return {
|
|
29710
29749
|
ok: false,
|
|
29711
29750
|
error: dataError("POLICY_INVALID", {
|
|
@@ -29713,7 +29752,7 @@ function budgetProblem(fragment, action) {
|
|
|
29713
29752
|
table: STORAGE_OBJECTS,
|
|
29714
29753
|
bytes: true
|
|
29715
29754
|
}),
|
|
29716
|
-
reason: `they write ${String(
|
|
29755
|
+
reason: `they write ${String(bytes2)}`
|
|
29717
29756
|
};
|
|
29718
29757
|
return void 0;
|
|
29719
29758
|
}
|
|
@@ -29867,7 +29906,7 @@ function depthContexts(contexts, claims) {
|
|
|
29867
29906
|
function readsClaims(policies) {
|
|
29868
29907
|
return policies.some(
|
|
29869
29908
|
(policy) => [policy.using, policy.check].some(
|
|
29870
|
-
(
|
|
29909
|
+
(text3) => text3 !== null && /auth\s*\.\s*jwt\s*\(/i.test(text3)
|
|
29871
29910
|
)
|
|
29872
29911
|
);
|
|
29873
29912
|
}
|
|
@@ -30497,15 +30536,15 @@ function readEnd(stream) {
|
|
|
30497
30536
|
rest.position
|
|
30498
30537
|
);
|
|
30499
30538
|
}
|
|
30500
|
-
function parsePolicyStatement(
|
|
30501
|
-
if (policySqlTooLong(
|
|
30539
|
+
function parsePolicyStatement(text3) {
|
|
30540
|
+
if (policySqlTooLong(text3))
|
|
30502
30541
|
return {
|
|
30503
30542
|
ok: false,
|
|
30504
30543
|
reason: `the statement is longer than ${String(MAX_POLICY_SQL_BYTES)} bytes`,
|
|
30505
30544
|
position: 1
|
|
30506
30545
|
};
|
|
30507
30546
|
try {
|
|
30508
|
-
const stream = new TokenStream(tokenize2(
|
|
30547
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
30509
30548
|
const start = stream.next();
|
|
30510
30549
|
let statement2;
|
|
30511
30550
|
if (isKeyword(start, "create")) statement2 = readCreate2(stream, start);
|
|
@@ -30529,10 +30568,10 @@ function parsePolicyStatement(text2) {
|
|
|
30529
30568
|
throw error51;
|
|
30530
30569
|
}
|
|
30531
30570
|
}
|
|
30532
|
-
function splitPolicyStatements(
|
|
30571
|
+
function splitPolicyStatements(text3) {
|
|
30533
30572
|
let tokens;
|
|
30534
30573
|
try {
|
|
30535
|
-
tokens = tokenize2(
|
|
30574
|
+
tokens = tokenize2(text3);
|
|
30536
30575
|
} catch (error51) {
|
|
30537
30576
|
if (error51 instanceof PolicySyntaxError)
|
|
30538
30577
|
return { ok: false, reason: error51.reason, position: error51.position };
|
|
@@ -30544,20 +30583,20 @@ function splitPolicyStatements(text2) {
|
|
|
30544
30583
|
if (token.kind === "eof") break;
|
|
30545
30584
|
start ??= token.position - 1;
|
|
30546
30585
|
if (token.kind === "op" && token.value === ";") {
|
|
30547
|
-
statements.push(
|
|
30586
|
+
statements.push(text3.slice(start, token.position).trim());
|
|
30548
30587
|
start = void 0;
|
|
30549
30588
|
}
|
|
30550
30589
|
}
|
|
30551
30590
|
if (start !== void 0) {
|
|
30552
|
-
const last =
|
|
30591
|
+
const last = text3.slice(start).trim();
|
|
30553
30592
|
if (last !== "") statements.push(last);
|
|
30554
30593
|
}
|
|
30555
30594
|
return { ok: true, statements };
|
|
30556
30595
|
}
|
|
30557
30596
|
var OWNER_PREFIX = "naro:owner";
|
|
30558
|
-
function ownerComment(
|
|
30559
|
-
if (!
|
|
30560
|
-
const rest =
|
|
30597
|
+
function ownerComment(text3, position) {
|
|
30598
|
+
if (!text3.startsWith(OWNER_PREFIX)) return void 0;
|
|
30599
|
+
const rest = text3.slice(OWNER_PREFIX.length);
|
|
30561
30600
|
if (rest !== "" && !/^[ \t]/.test(rest)) return void 0;
|
|
30562
30601
|
let column2 = rest.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
30563
30602
|
if (column2.startsWith('"')) {
|
|
@@ -30613,22 +30652,22 @@ function readRowLevelSecurity(stream) {
|
|
|
30613
30652
|
stream.expectKeyword("security");
|
|
30614
30653
|
return { kind: "rls", table, action };
|
|
30615
30654
|
}
|
|
30616
|
-
function parseMigrationPolicyStatement(
|
|
30617
|
-
if (policySqlTooLong(
|
|
30655
|
+
function parseMigrationPolicyStatement(text3) {
|
|
30656
|
+
if (policySqlTooLong(text3))
|
|
30618
30657
|
return {
|
|
30619
30658
|
ok: false,
|
|
30620
30659
|
reason: `the statement is longer than ${String(MAX_POLICY_SQL_BYTES)} bytes`,
|
|
30621
30660
|
position: 1
|
|
30622
30661
|
};
|
|
30623
30662
|
try {
|
|
30624
|
-
const stream = new TokenStream(tokenize2(
|
|
30663
|
+
const stream = new TokenStream(tokenize2(text3));
|
|
30625
30664
|
let statement2;
|
|
30626
30665
|
if (stream.acceptKeyword("comment")) statement2 = readComment(stream);
|
|
30627
30666
|
else if (isKeyword(stream.peek(), "alter") && isKeyword(stream.peek(1), "table")) {
|
|
30628
30667
|
stream.next();
|
|
30629
30668
|
stream.next();
|
|
30630
30669
|
statement2 = readRowLevelSecurity(stream);
|
|
30631
|
-
} else return parsePolicyStatement(
|
|
30670
|
+
} else return parsePolicyStatement(text3);
|
|
30632
30671
|
readEnd(stream);
|
|
30633
30672
|
return { ok: true, statement: statement2 };
|
|
30634
30673
|
} catch (error51) {
|
|
@@ -30814,17 +30853,17 @@ function tablesChanged(head) {
|
|
|
30814
30853
|
}
|
|
30815
30854
|
}
|
|
30816
30855
|
function readsBareSubqueryColumn(policy) {
|
|
30817
|
-
return [policy.using, policy.check].some((
|
|
30818
|
-
if (
|
|
30819
|
-
const parsed = parseExpression(
|
|
30856
|
+
return [policy.using, policy.check].some((text3) => {
|
|
30857
|
+
if (text3 === null) return false;
|
|
30858
|
+
const parsed = parseExpression(text3);
|
|
30820
30859
|
return parsed.ok && unqualifiedInSubquery(parsed.expr) !== void 0;
|
|
30821
30860
|
});
|
|
30822
30861
|
}
|
|
30823
30862
|
function tablesInScope(policy) {
|
|
30824
30863
|
const tables = /* @__PURE__ */ new Set([asciiLower(policy.table)]);
|
|
30825
|
-
for (const
|
|
30826
|
-
if (
|
|
30827
|
-
const parsed = parseExpression(
|
|
30864
|
+
for (const text3 of [policy.using, policy.check]) {
|
|
30865
|
+
if (text3 === null) continue;
|
|
30866
|
+
const parsed = parseExpression(text3);
|
|
30828
30867
|
if (!parsed.ok) continue;
|
|
30829
30868
|
for (const subquery of subqueriesOf(parsed.expr))
|
|
30830
30869
|
tables.add(asciiLower(subquery.table));
|
|
@@ -30832,9 +30871,9 @@ function tablesInScope(policy) {
|
|
|
30832
30871
|
return tables;
|
|
30833
30872
|
}
|
|
30834
30873
|
function readsStorageObjects(policy) {
|
|
30835
|
-
return [policy.using, policy.check].some((
|
|
30836
|
-
if (
|
|
30837
|
-
const parsed = parseExpression(
|
|
30874
|
+
return [policy.using, policy.check].some((text3) => {
|
|
30875
|
+
if (text3 === null) return false;
|
|
30876
|
+
const parsed = parseExpression(text3);
|
|
30838
30877
|
return parsed.ok && subqueriesOf(parsed.expr).some(
|
|
30839
30878
|
(subquery) => isStorageObjects(subquery.table)
|
|
30840
30879
|
);
|
|
@@ -30846,54 +30885,54 @@ function planPolicyMigration(statements) {
|
|
|
30846
30885
|
let hasPolicyStatements = false;
|
|
30847
30886
|
let touchesTables = false;
|
|
30848
30887
|
let renamesColumns = false;
|
|
30849
|
-
const heads = statements.map((
|
|
30888
|
+
const heads = statements.map((text3) => readSqlHead(text3));
|
|
30850
30889
|
const changes = heads.map(tablesChanged);
|
|
30851
30890
|
const changedAfter = (index, tables) => changes.slice(index + 1).some(
|
|
30852
30891
|
(changed) => changed === "any" || changed.some((table) => tables.has(table))
|
|
30853
30892
|
);
|
|
30854
|
-
for (const [index,
|
|
30855
|
-
const
|
|
30893
|
+
for (const [index, text3] of statements.entries()) {
|
|
30894
|
+
const refuse3 = (message) => ({
|
|
30856
30895
|
ok: false,
|
|
30857
30896
|
index,
|
|
30858
30897
|
message: `Statement ${String(index + 1)} of ${String(count2)}: ${message}`
|
|
30859
30898
|
});
|
|
30860
|
-
const head = heads[index] ?? readSqlHead(
|
|
30899
|
+
const head = heads[index] ?? readSqlHead(text3);
|
|
30861
30900
|
switch (head.kind) {
|
|
30862
30901
|
case "grant":
|
|
30863
30902
|
case "revoke":
|
|
30864
|
-
return
|
|
30903
|
+
return refuse3(GRANT_REFUSAL);
|
|
30865
30904
|
case "unreadable":
|
|
30866
|
-
return
|
|
30905
|
+
return refuse3(`${head.reason}.`);
|
|
30867
30906
|
case "create-table":
|
|
30868
30907
|
case "drop-table":
|
|
30869
30908
|
case "rename-table":
|
|
30870
30909
|
touchesTables = true;
|
|
30871
|
-
planned.push({ kind: "verbatim", text:
|
|
30910
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30872
30911
|
continue;
|
|
30873
30912
|
case "rename-column":
|
|
30874
30913
|
renamesColumns = true;
|
|
30875
|
-
planned.push({ kind: "verbatim", text:
|
|
30914
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30876
30915
|
continue;
|
|
30877
30916
|
case "alter-table":
|
|
30878
30917
|
case "other":
|
|
30879
|
-
planned.push({ kind: "verbatim", text:
|
|
30918
|
+
planned.push({ kind: "verbatim", text: text3, head });
|
|
30880
30919
|
continue;
|
|
30881
30920
|
default:
|
|
30882
30921
|
break;
|
|
30883
30922
|
}
|
|
30884
|
-
const parsed = parseMigrationPolicyStatement(
|
|
30923
|
+
const parsed = parseMigrationPolicyStatement(text3);
|
|
30885
30924
|
if (!parsed.ok)
|
|
30886
|
-
return
|
|
30925
|
+
return refuse3(
|
|
30887
30926
|
`${parsed.reason} at character ${String(parsed.position)}.`
|
|
30888
30927
|
);
|
|
30889
30928
|
const statement2 = parsed.statement;
|
|
30890
30929
|
const table = statement2.kind === "create" ? statement2.policy.table : statement2.table;
|
|
30891
30930
|
if (!isStorageObjects(table) && isSystemTable(table))
|
|
30892
|
-
return
|
|
30931
|
+
return refuse3(`${relationMissing(table)}.`);
|
|
30893
30932
|
switch (statement2.kind) {
|
|
30894
30933
|
case "create":
|
|
30895
30934
|
if (readsBareSubqueryColumn(statement2.policy) && changedAfter(index, tablesInScope(statement2.policy)))
|
|
30896
|
-
return
|
|
30935
|
+
return refuse3(LATER_TABLE_CHANGE);
|
|
30897
30936
|
hasPolicyStatements = true;
|
|
30898
30937
|
planned.push({ kind: "create-policy", policy: statement2.policy });
|
|
30899
30938
|
break;
|
|
@@ -30921,7 +30960,7 @@ function planPolicyMigration(statements) {
|
|
|
30921
30960
|
break;
|
|
30922
30961
|
case "rls":
|
|
30923
30962
|
if (statement2.action === "disable")
|
|
30924
|
-
return
|
|
30963
|
+
return refuse3(disableRefusal(table));
|
|
30925
30964
|
planned.push({ kind: "noop" });
|
|
30926
30965
|
break;
|
|
30927
30966
|
}
|
|
@@ -31001,19 +31040,19 @@ var TYPED_PATH = /^[a-z][a-z0-9+.-]*:\/\/[^/]*(\/.*)?$/i;
|
|
|
31001
31040
|
var SEGMENT = /^[A-Za-z0-9._~-]+$/;
|
|
31002
31041
|
function parseRedirectEntry(value) {
|
|
31003
31042
|
if (typeof value !== "string") return null;
|
|
31004
|
-
const
|
|
31005
|
-
if (
|
|
31006
|
-
if (/[\s?#*%\\]/.test(
|
|
31043
|
+
const text3 = value.trim();
|
|
31044
|
+
if (text3 === "" || text3.length > MAX_ENTRY_LENGTH) return null;
|
|
31045
|
+
if (/[\s?#*%\\]/.test(text3)) return null;
|
|
31007
31046
|
let url2;
|
|
31008
31047
|
try {
|
|
31009
|
-
url2 = new URL(
|
|
31048
|
+
url2 = new URL(text3);
|
|
31010
31049
|
} catch {
|
|
31011
31050
|
return null;
|
|
31012
31051
|
}
|
|
31013
31052
|
if (url2.username !== "" || url2.password !== "") return null;
|
|
31014
31053
|
if (url2.search !== "" || url2.hash !== "") return null;
|
|
31015
31054
|
if (!isSecureOrLoopback(url2)) return null;
|
|
31016
|
-
const typed = TYPED_PATH.exec(
|
|
31055
|
+
const typed = TYPED_PATH.exec(text3)?.[1] ?? "/";
|
|
31017
31056
|
if (typed !== url2.pathname) return null;
|
|
31018
31057
|
const path = url2.pathname;
|
|
31019
31058
|
if (path !== "/") {
|
|
@@ -31535,13 +31574,13 @@ function listMigrationFiles(dir) {
|
|
|
31535
31574
|
);
|
|
31536
31575
|
}
|
|
31537
31576
|
function nextMigrationFileName(existing, name) {
|
|
31538
|
-
const
|
|
31539
|
-
if (
|
|
31577
|
+
const normalized2 = normalizeMigrationName(name);
|
|
31578
|
+
if (normalized2 === "")
|
|
31540
31579
|
throw new NaroError(
|
|
31541
31580
|
"VALIDATION_FAILED",
|
|
31542
31581
|
"Migration name must contain letters or digits."
|
|
31543
31582
|
);
|
|
31544
|
-
return numberedMigrationName(existing,
|
|
31583
|
+
return numberedMigrationName(existing, normalized2);
|
|
31545
31584
|
}
|
|
31546
31585
|
function compareMigrations(local, applied) {
|
|
31547
31586
|
const ledger = new Map(applied.map((row) => [row.name, row]));
|
|
@@ -32021,6 +32060,43 @@ async function promptForMissingEnv(ctx, keys) {
|
|
|
32021
32060
|
return collected;
|
|
32022
32061
|
}
|
|
32023
32062
|
|
|
32063
|
+
// src/env-services.ts
|
|
32064
|
+
var PUBLIC_PREFIXES = [
|
|
32065
|
+
"NEXT_PUBLIC_",
|
|
32066
|
+
"NUXT_PUBLIC_",
|
|
32067
|
+
"EXPO_PUBLIC_",
|
|
32068
|
+
"REACT_APP_",
|
|
32069
|
+
"PUBLIC_",
|
|
32070
|
+
"GATSBY_",
|
|
32071
|
+
"VITE_"
|
|
32072
|
+
];
|
|
32073
|
+
function bareEnvKey(key) {
|
|
32074
|
+
let longest = "";
|
|
32075
|
+
for (const prefix of PUBLIC_PREFIXES)
|
|
32076
|
+
if (key.startsWith(prefix) && prefix.length > longest.length)
|
|
32077
|
+
longest = prefix;
|
|
32078
|
+
return key.slice(longest.length);
|
|
32079
|
+
}
|
|
32080
|
+
function supabaseKeys(keys) {
|
|
32081
|
+
return keys.filter((key) => bareEnvKey(key).startsWith("SUPABASE_"));
|
|
32082
|
+
}
|
|
32083
|
+
function envServiceHints(keys) {
|
|
32084
|
+
const own2 = supabaseKeys(keys);
|
|
32085
|
+
const isUrl = (key) => bareEnvKey(key).endsWith("_URL");
|
|
32086
|
+
const isKey = (key) => bareEnvKey(key).endsWith("_KEY");
|
|
32087
|
+
const placed = own2.filter((key) => isUrl(key) || isKey(key));
|
|
32088
|
+
if (placed.length === 0) return [];
|
|
32089
|
+
const lines = [
|
|
32090
|
+
placed.length === keys.length ? " Supabase \u2014 in that project's dashboard:" : ` Supabase (${placed.join(", ")}) \u2014 in that project's dashboard:`
|
|
32091
|
+
];
|
|
32092
|
+
if (placed.some(isUrl))
|
|
32093
|
+
lines.push(
|
|
32094
|
+
" the project URL \u2014 the Connect dialog at the top of the page"
|
|
32095
|
+
);
|
|
32096
|
+
if (placed.some(isKey)) lines.push(" the keys \u2014 Settings > API Keys");
|
|
32097
|
+
return lines;
|
|
32098
|
+
}
|
|
32099
|
+
|
|
32024
32100
|
// src/env-surface.ts
|
|
32025
32101
|
function pullEnv(rows, surface) {
|
|
32026
32102
|
const chosen = /* @__PURE__ */ new Map();
|
|
@@ -32042,10 +32118,10 @@ var SUPPORTED_REMOTE = [
|
|
|
32042
32118
|
/^ssh:\/\/[^\s]+$/,
|
|
32043
32119
|
/^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
|
|
32044
32120
|
];
|
|
32045
|
-
var defaultExec = (args, cwd) => new Promise((
|
|
32121
|
+
var defaultExec = (args, cwd) => new Promise((resolve15, reject) => {
|
|
32046
32122
|
execFile2("git", args, { cwd }, (error51, stdout) => {
|
|
32047
32123
|
if (error51) reject(error51);
|
|
32048
|
-
else
|
|
32124
|
+
else resolve15(stdout);
|
|
32049
32125
|
});
|
|
32050
32126
|
});
|
|
32051
32127
|
async function tryExec(exec, args, cwd) {
|
|
@@ -32194,6 +32270,24 @@ async function uploadArtifact(artifact, opts) {
|
|
|
32194
32270
|
};
|
|
32195
32271
|
}
|
|
32196
32272
|
|
|
32273
|
+
// src/version.ts
|
|
32274
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
32275
|
+
import { dirname as dirname5, join as join25 } from "path";
|
|
32276
|
+
import { fileURLToPath } from "url";
|
|
32277
|
+
function version2() {
|
|
32278
|
+
try {
|
|
32279
|
+
const pkg = JSON.parse(
|
|
32280
|
+
readFileSync6(
|
|
32281
|
+
join25(dirname5(fileURLToPath(import.meta.url)), "..", "package.json"),
|
|
32282
|
+
"utf8"
|
|
32283
|
+
)
|
|
32284
|
+
);
|
|
32285
|
+
return pkg.version ?? "unknown";
|
|
32286
|
+
} catch {
|
|
32287
|
+
return "unknown";
|
|
32288
|
+
}
|
|
32289
|
+
}
|
|
32290
|
+
|
|
32197
32291
|
// src/working-tree.ts
|
|
32198
32292
|
import { execFile as execFile3 } from "child_process";
|
|
32199
32293
|
import {
|
|
@@ -32211,9 +32305,9 @@ import {
|
|
|
32211
32305
|
} from "fs/promises";
|
|
32212
32306
|
import {
|
|
32213
32307
|
basename as basename3,
|
|
32214
|
-
dirname as
|
|
32308
|
+
dirname as dirname6,
|
|
32215
32309
|
isAbsolute as isAbsolute4,
|
|
32216
|
-
join as
|
|
32310
|
+
join as join26,
|
|
32217
32311
|
relative as relative6,
|
|
32218
32312
|
resolve as resolve10,
|
|
32219
32313
|
sep as sep5
|
|
@@ -32252,15 +32346,15 @@ async function committedEnvFiles(sourceDir) {
|
|
|
32252
32346
|
return [];
|
|
32253
32347
|
});
|
|
32254
32348
|
}
|
|
32255
|
-
async function staysInside(link,
|
|
32349
|
+
async function staysInside(link, text3, root) {
|
|
32256
32350
|
try {
|
|
32257
32351
|
return inside(await realpath3(link), root);
|
|
32258
32352
|
} catch (error51) {
|
|
32259
|
-
return error51.code === "ENOENT" && !
|
|
32353
|
+
return error51.code === "ENOENT" && !text3.split(/[\\/]/).includes("..");
|
|
32260
32354
|
}
|
|
32261
32355
|
}
|
|
32262
|
-
function climbsOnlyFirst(
|
|
32263
|
-
const segments =
|
|
32356
|
+
function climbsOnlyFirst(text3) {
|
|
32357
|
+
const segments = text3.split(/[\\/]/).filter((s) => s !== "" && s !== ".");
|
|
32264
32358
|
const down = segments.findIndex((segment) => segment !== "..");
|
|
32265
32359
|
return down < 0 || !segments.slice(down).includes("..");
|
|
32266
32360
|
}
|
|
@@ -32280,11 +32374,11 @@ async function readLocal(path) {
|
|
|
32280
32374
|
return readFile14(path).catch(() => null);
|
|
32281
32375
|
}
|
|
32282
32376
|
async function realAncestor(dir) {
|
|
32283
|
-
for (let current = dir; ; current =
|
|
32377
|
+
for (let current = dir; ; current = dirname6(current)) {
|
|
32284
32378
|
try {
|
|
32285
32379
|
return await realpath3(current);
|
|
32286
32380
|
} catch {
|
|
32287
|
-
if (
|
|
32381
|
+
if (dirname6(current) === current)
|
|
32288
32382
|
throw new Error(`No ancestor of ${dir}`);
|
|
32289
32383
|
}
|
|
32290
32384
|
}
|
|
@@ -32322,7 +32416,7 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32322
32416
|
absoluteLinks.push(path);
|
|
32323
32417
|
return false;
|
|
32324
32418
|
}
|
|
32325
|
-
if (!inside(resolve10(
|
|
32419
|
+
if (!inside(resolve10(dirname6(src), target2), sourceDir)) {
|
|
32326
32420
|
linksOutside.push(path);
|
|
32327
32421
|
return false;
|
|
32328
32422
|
}
|
|
@@ -32338,14 +32432,14 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32338
32432
|
const root = await realpath3(checkout);
|
|
32339
32433
|
const strays = await keptOnly(
|
|
32340
32434
|
keptLinks,
|
|
32341
|
-
async ({ path, text:
|
|
32435
|
+
async ({ path, text: text3 }) => {
|
|
32342
32436
|
try {
|
|
32343
|
-
return inside(await realpath3(
|
|
32437
|
+
return inside(await realpath3(join26(checkout, path)), root);
|
|
32344
32438
|
} catch (error51) {
|
|
32345
|
-
return error51.code === "ENOENT" && climbsOnlyFirst(
|
|
32439
|
+
return error51.code === "ENOENT" && climbsOnlyFirst(text3);
|
|
32346
32440
|
}
|
|
32347
32441
|
},
|
|
32348
|
-
({ path }) => unlink(
|
|
32442
|
+
({ path }) => unlink(join26(checkout, path))
|
|
32349
32443
|
);
|
|
32350
32444
|
linksElsewhere.push(...strays.map(({ path }) => path));
|
|
32351
32445
|
const committed = await committedEnvFiles(sourceDir);
|
|
@@ -32359,13 +32453,13 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32359
32453
|
outsideProject.push(path);
|
|
32360
32454
|
continue;
|
|
32361
32455
|
}
|
|
32362
|
-
const target2 =
|
|
32363
|
-
if (!inside(await realAncestor(
|
|
32456
|
+
const target2 = join26(checkout, path);
|
|
32457
|
+
if (!inside(await realAncestor(dirname6(target2)), root)) {
|
|
32364
32458
|
outsideProject.push(path);
|
|
32365
32459
|
continue;
|
|
32366
32460
|
}
|
|
32367
32461
|
try {
|
|
32368
|
-
await mkdir3(
|
|
32462
|
+
await mkdir3(dirname6(target2), { recursive: true });
|
|
32369
32463
|
} catch {
|
|
32370
32464
|
outsideProject.push(path);
|
|
32371
32465
|
continue;
|
|
@@ -32374,7 +32468,7 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32374
32468
|
const content = await git(sourceDir, ["show", `HEAD:./${path}`]);
|
|
32375
32469
|
if (isLink) {
|
|
32376
32470
|
const linkTarget = content.toString("utf8");
|
|
32377
|
-
const pointsAt = resolve10(
|
|
32471
|
+
const pointsAt = resolve10(dirname6(target2), linkTarget);
|
|
32378
32472
|
if (isAbsolute4(linkTarget) || !inside(pointsAt, checkout)) {
|
|
32379
32473
|
outsideProject.push(path);
|
|
32380
32474
|
continue;
|
|
@@ -32384,16 +32478,16 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32384
32478
|
continue;
|
|
32385
32479
|
}
|
|
32386
32480
|
await writeFile7(target2, content, { flag: "wx" });
|
|
32387
|
-
const local = await readLocal(
|
|
32481
|
+
const local = await readLocal(join26(sourceDir, path));
|
|
32388
32482
|
if (!local || !sameText(local, content)) committedVersion.push(path);
|
|
32389
32483
|
}
|
|
32390
32484
|
const envStrays = await keptOnly(
|
|
32391
32485
|
envLinks,
|
|
32392
32486
|
async (path) => {
|
|
32393
|
-
const real = await realpath3(
|
|
32487
|
+
const real = await realpath3(join26(checkout, path)).catch(() => null);
|
|
32394
32488
|
return real !== null && inside(real, root);
|
|
32395
32489
|
},
|
|
32396
|
-
(path) => unlink(
|
|
32490
|
+
(path) => unlink(join26(checkout, path))
|
|
32397
32491
|
);
|
|
32398
32492
|
outsideProject.push(...envStrays);
|
|
32399
32493
|
const inCommit = new Set(
|
|
@@ -32413,9 +32507,1776 @@ async function copyWorkingTree(source, checkout) {
|
|
|
32413
32507
|
};
|
|
32414
32508
|
}
|
|
32415
32509
|
|
|
32510
|
+
// src/functions/deploy.ts
|
|
32511
|
+
import { relative as relative11, resolve as resolve13, sep as sep10 } from "path";
|
|
32512
|
+
|
|
32513
|
+
// ../../packages/functions-bundler/src/errors.ts
|
|
32514
|
+
var FunctionBundleError = class extends Error {
|
|
32515
|
+
code;
|
|
32516
|
+
constructor(code, message, options) {
|
|
32517
|
+
super(message, options);
|
|
32518
|
+
this.name = "FunctionBundleError";
|
|
32519
|
+
this.code = code;
|
|
32520
|
+
}
|
|
32521
|
+
};
|
|
32522
|
+
var NAME_RULE = "names are 1\u201364 characters of lowercase letters, digits, - and _, starting with a letter";
|
|
32523
|
+
var JSR_REGISTRY = '"@jsr:registry=https://npm.jsr.io"';
|
|
32524
|
+
var bytes = (n) => n.toLocaleString("en-US");
|
|
32525
|
+
var SHELL_SAFE = /^[A-Za-z0-9@/._^=+:-]+$/;
|
|
32526
|
+
var shellArg = (spec) => SHELL_SAFE.test(spec) ? spec : `'${spec.replaceAll("'", "'\\''")}'`;
|
|
32527
|
+
var BUNDLE_MESSAGES = {
|
|
32528
|
+
noDir: (dir) => `There is no ${dir} folder here. Create a function with naro functions new <name>.`,
|
|
32529
|
+
supabaseFound: "Found supabase/functions. Deploy it with --dir supabase/functions.",
|
|
32530
|
+
noFunctions: (dir) => `No functions in ${dir}. Create one with naro functions new <name>.`,
|
|
32531
|
+
badName: (name) => `Function folder ${name} is not a valid function name: ${NAME_RULE}.`,
|
|
32532
|
+
noFolder: (name, dir) => `Function ${name} has no folder: ${dir}/${name} does not exist.`,
|
|
32533
|
+
jsonInvalid: (name) => `function.json of ${name} is not valid JSON.`,
|
|
32534
|
+
jsonKeys: (name, keys) => `function.json of ${name} has keys Naro does not read: ${keys.join(", ")}. It takes verifyJwt and entrypoint.`,
|
|
32535
|
+
jsonVerifyJwt: (name) => `verifyJwt in function.json of ${name} must be true or false.`,
|
|
32536
|
+
jsonEntrypoint: (name) => `entrypoint in function.json of ${name} must be a relative path inside the function folder.`,
|
|
32537
|
+
noEntry: (name, dir) => `Function ${name} has no entry file. Add ${dir}/${name}/index.ts (or set "entrypoint" in function.json).`,
|
|
32538
|
+
entryMissing: (name, entrypoint) => `Function ${name} has no ${entrypoint}, the entrypoint its function.json names.`,
|
|
32539
|
+
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.`,
|
|
32540
|
+
importMapMissing: (file2) => `--import-map ${file2} does not exist.`,
|
|
32541
|
+
importMapInvalid: (file2) => `${file2} is not valid JSON.`,
|
|
32542
|
+
importMapShape: (file2) => `The imports of ${file2} must map names to text.`,
|
|
32543
|
+
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.`,
|
|
32544
|
+
npmMissing: (spec, install, dir) => `npm:${spec} is not installed. Run npm install ${shellArg(install)} in ${dir}.`,
|
|
32545
|
+
jsrMissing: (spec, install) => `jsr:${spec} is not installed. Add ${JSR_REGISTRY} to .npmrc, then run npm install ${shellArg(install)}.`,
|
|
32546
|
+
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(" ")}`,
|
|
32547
|
+
noPackageName: (specifier, example) => `${specifier} names no package. Write it like ${example}.`,
|
|
32548
|
+
jsrNoScope: (spec) => `jsr:${spec} needs a scope, like jsr:@scope/${spec}.`,
|
|
32549
|
+
urlNpm: (url2, target2) => `URL imports are not supported: ${url2}. Use ${target2} instead.`,
|
|
32550
|
+
urlStd: (url2, module) => `URL imports are not supported: ${url2}. Use jsr:@std/${module} instead.`,
|
|
32551
|
+
urlOther: (url2) => `URL imports are not supported: ${url2}. Install the package from npm and import it by name.`,
|
|
32552
|
+
outOfRange: (pkg, version3, range, file2) => `\u26A0 ${pkg} is installed at ${version3}, outside the range ${range} that ${file2} asks for.`,
|
|
32553
|
+
wasm: (file2) => `WebAssembly modules are not supported in functions yet: ${file2}.`,
|
|
32554
|
+
naroJsMissing: 'Cannot find "naro-js/functions". Install naro-js 0.7.0 or later: npm install naro-js@latest.',
|
|
32555
|
+
tooLarge: (name, size, inputs) => `Function ${name} bundles to ${bytes(size)} bytes; the limit is 67,108,864 (64 MiB). The biggest inputs: ${inputs}.`,
|
|
32556
|
+
// Rounded up: a size over the limit never reads as the limit (10.0 MB).
|
|
32557
|
+
tooLargeCompressed: (name, size) => `Function ${name} is ${(Math.ceil(size / 1048576 * 10) / 10).toFixed(1)} MB after compression; Cloudflare accepts 10 MB.`,
|
|
32558
|
+
otherBundle: (name, text3, location2) => `Function ${name} did not bundle: ${text3}${location2 ? ` (${location2})` : ""}`,
|
|
32559
|
+
noHandler: (name) => `Function ${name} did not register a handler. Call Deno.serve(handler) or export default { fetch }.`,
|
|
32560
|
+
failedToStart: (name, line) => `Function ${name} failed to start: ${line}`,
|
|
32561
|
+
esbuildMissing: (name, cause) => `Function ${name} did not bundle: esbuild could not be loaded (${cause}). Reinstall naro-cli.`,
|
|
32562
|
+
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).`,
|
|
32563
|
+
bootTimeout: (name, ms) => `Function ${name} did not start within ${String(ms / 1e3)} seconds.`
|
|
32564
|
+
};
|
|
32565
|
+
|
|
32566
|
+
// ../../packages/functions-bundler/src/discover.ts
|
|
32567
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
32568
|
+
import { join as join27, relative as relative7, resolve as resolve11, sep as sep6 } from "path";
|
|
32569
|
+
|
|
32570
|
+
// ../../packages/functions-bundler/src/limits.ts
|
|
32571
|
+
var FUNCTION_BUNDLE_MAX_BYTES = 67108864;
|
|
32572
|
+
var FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES = 10485760;
|
|
32573
|
+
var ENTRY_FILES = [
|
|
32574
|
+
"index.ts",
|
|
32575
|
+
"index.js",
|
|
32576
|
+
"index.mjs",
|
|
32577
|
+
"index.tsx",
|
|
32578
|
+
"index.jsx"
|
|
32579
|
+
];
|
|
32580
|
+
|
|
32581
|
+
// ../../packages/functions-bundler/src/discover.ts
|
|
32582
|
+
var SEGMENT2 = /^[A-Za-z0-9._@+-]+$/;
|
|
32583
|
+
function isEntrypoint(value) {
|
|
32584
|
+
if (value.length < 1 || value.length > 200) return false;
|
|
32585
|
+
return value.split("/").every(
|
|
32586
|
+
(segment) => segment !== "." && segment !== ".." && SEGMENT2.test(segment)
|
|
32587
|
+
);
|
|
32588
|
+
}
|
|
32589
|
+
function functionsFolder(options) {
|
|
32590
|
+
const root = resolve11(options.cwd, options.root ?? ".");
|
|
32591
|
+
const explicit = options.dir !== void 0;
|
|
32592
|
+
const dir = explicit ? resolve11(options.cwd, options.dir) : join27(root, "naro", "functions");
|
|
32593
|
+
const label2 = relative7(options.cwd, dir).split(sep6).join("/") || ".";
|
|
32594
|
+
return { dir, label: label2, root, explicit };
|
|
32595
|
+
}
|
|
32596
|
+
function listFunctionNames(folder) {
|
|
32597
|
+
let entries;
|
|
32598
|
+
try {
|
|
32599
|
+
entries = readdirSync3(folder.dir, { withFileTypes: true });
|
|
32600
|
+
} catch {
|
|
32601
|
+
if (!folder.explicit && existsSync7(join27(folder.root, "supabase", "functions")))
|
|
32602
|
+
throw new FunctionBundleError(
|
|
32603
|
+
"VALIDATION_FAILED",
|
|
32604
|
+
BUNDLE_MESSAGES.supabaseFound
|
|
32605
|
+
);
|
|
32606
|
+
throw new FunctionBundleError(
|
|
32607
|
+
"VALIDATION_FAILED",
|
|
32608
|
+
BUNDLE_MESSAGES.noDir(folder.label)
|
|
32609
|
+
);
|
|
32610
|
+
}
|
|
32611
|
+
const names = entries.filter(
|
|
32612
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".") && entry.name !== "node_modules"
|
|
32613
|
+
).map((entry) => entry.name).sort();
|
|
32614
|
+
if (names.length === 0)
|
|
32615
|
+
throw new FunctionBundleError(
|
|
32616
|
+
"VALIDATION_FAILED",
|
|
32617
|
+
BUNDLE_MESSAGES.noFunctions(folder.label)
|
|
32618
|
+
);
|
|
32619
|
+
return names;
|
|
32620
|
+
}
|
|
32621
|
+
function readFunctionJson(dir, name) {
|
|
32622
|
+
const file2 = join27(dir, "function.json");
|
|
32623
|
+
if (!existsSync7(file2)) return { verifyJwt: void 0, entrypoint: void 0 };
|
|
32624
|
+
let parsed;
|
|
32625
|
+
try {
|
|
32626
|
+
parsed = JSON.parse(readFileSync7(file2, "utf8"));
|
|
32627
|
+
} catch {
|
|
32628
|
+
throw new FunctionBundleError(
|
|
32629
|
+
"VALIDATION_FAILED",
|
|
32630
|
+
BUNDLE_MESSAGES.jsonInvalid(name)
|
|
32631
|
+
);
|
|
32632
|
+
}
|
|
32633
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
32634
|
+
throw new FunctionBundleError(
|
|
32635
|
+
"VALIDATION_FAILED",
|
|
32636
|
+
BUNDLE_MESSAGES.jsonInvalid(name)
|
|
32637
|
+
);
|
|
32638
|
+
const record3 = parsed;
|
|
32639
|
+
const unknown2 = Object.keys(record3).filter(
|
|
32640
|
+
(key) => key !== "verifyJwt" && key !== "entrypoint"
|
|
32641
|
+
);
|
|
32642
|
+
if (unknown2.length > 0)
|
|
32643
|
+
throw new FunctionBundleError(
|
|
32644
|
+
"VALIDATION_FAILED",
|
|
32645
|
+
BUNDLE_MESSAGES.jsonKeys(name, unknown2)
|
|
32646
|
+
);
|
|
32647
|
+
const { verifyJwt, entrypoint } = record3;
|
|
32648
|
+
if (verifyJwt !== void 0 && typeof verifyJwt !== "boolean")
|
|
32649
|
+
throw new FunctionBundleError(
|
|
32650
|
+
"VALIDATION_FAILED",
|
|
32651
|
+
BUNDLE_MESSAGES.jsonVerifyJwt(name)
|
|
32652
|
+
);
|
|
32653
|
+
if (entrypoint !== void 0 && (typeof entrypoint !== "string" || !isEntrypoint(entrypoint)))
|
|
32654
|
+
throw new FunctionBundleError(
|
|
32655
|
+
"VALIDATION_FAILED",
|
|
32656
|
+
BUNDLE_MESSAGES.jsonEntrypoint(name)
|
|
32657
|
+
);
|
|
32658
|
+
return { verifyJwt, entrypoint };
|
|
32659
|
+
}
|
|
32660
|
+
function readFunctionSource(folder, name) {
|
|
32661
|
+
if (!isFunctionName(name))
|
|
32662
|
+
throw new FunctionBundleError(
|
|
32663
|
+
"VALIDATION_FAILED",
|
|
32664
|
+
BUNDLE_MESSAGES.badName(name)
|
|
32665
|
+
);
|
|
32666
|
+
const dir = join27(folder.dir, name);
|
|
32667
|
+
if (!statSync2(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
32668
|
+
throw new FunctionBundleError(
|
|
32669
|
+
"VALIDATION_FAILED",
|
|
32670
|
+
BUNDLE_MESSAGES.noFolder(name, folder.label)
|
|
32671
|
+
);
|
|
32672
|
+
const config2 = readFunctionJson(dir, name);
|
|
32673
|
+
let entrypoint;
|
|
32674
|
+
if (config2.entrypoint !== void 0) {
|
|
32675
|
+
if (!existsSync7(join27(dir, ...config2.entrypoint.split("/"))))
|
|
32676
|
+
throw new FunctionBundleError(
|
|
32677
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
32678
|
+
BUNDLE_MESSAGES.entryMissing(name, config2.entrypoint)
|
|
32679
|
+
);
|
|
32680
|
+
entrypoint = config2.entrypoint;
|
|
32681
|
+
} else {
|
|
32682
|
+
const found = ENTRY_FILES.find((file2) => existsSync7(join27(dir, file2)));
|
|
32683
|
+
if (found === void 0)
|
|
32684
|
+
throw new FunctionBundleError(
|
|
32685
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
32686
|
+
BUNDLE_MESSAGES.noEntry(name, folder.label)
|
|
32687
|
+
);
|
|
32688
|
+
entrypoint = found;
|
|
32689
|
+
}
|
|
32690
|
+
return {
|
|
32691
|
+
name,
|
|
32692
|
+
dir,
|
|
32693
|
+
entry: join27(dir, ...entrypoint.split("/")),
|
|
32694
|
+
entrypoint,
|
|
32695
|
+
verifyJwt: config2.verifyJwt
|
|
32696
|
+
};
|
|
32697
|
+
}
|
|
32698
|
+
function configTomlWarning(folder) {
|
|
32699
|
+
if (!folder.dir.endsWith(join27("supabase", "functions"))) return null;
|
|
32700
|
+
const file2 = join27(folder.dir, "..", "config.toml");
|
|
32701
|
+
if (!existsSync7(file2)) return null;
|
|
32702
|
+
return /^\s*verify_jwt\s*=/m.test(readFileSync7(file2, "utf8")) ? BUNDLE_MESSAGES.supabaseVerifyJwt(folder.label) : null;
|
|
32703
|
+
}
|
|
32704
|
+
|
|
32705
|
+
// ../../packages/functions-bundler/src/jsonc.ts
|
|
32706
|
+
function closesNext(text3, from) {
|
|
32707
|
+
let i = from;
|
|
32708
|
+
while (i < text3.length && /\s/.test(text3[i] ?? "")) i += 1;
|
|
32709
|
+
return text3[i] === "}" || text3[i] === "]";
|
|
32710
|
+
}
|
|
32711
|
+
function parseJsonc2(text3) {
|
|
32712
|
+
const uncommented = stripComments2(text3);
|
|
32713
|
+
let out = "";
|
|
32714
|
+
let inString = false;
|
|
32715
|
+
for (let i = 0; i < uncommented.length; i += 1) {
|
|
32716
|
+
const char = uncommented[i] ?? "";
|
|
32717
|
+
if (inString) {
|
|
32718
|
+
out += char;
|
|
32719
|
+
if (char === "\\") {
|
|
32720
|
+
out += uncommented[i + 1] ?? "";
|
|
32721
|
+
i += 1;
|
|
32722
|
+
} else if (char === '"') inString = false;
|
|
32723
|
+
continue;
|
|
32724
|
+
}
|
|
32725
|
+
if (char === '"') inString = true;
|
|
32726
|
+
if (char === "," && closesNext(uncommented, i + 1)) continue;
|
|
32727
|
+
out += char;
|
|
32728
|
+
}
|
|
32729
|
+
return JSON.parse(out);
|
|
32730
|
+
}
|
|
32731
|
+
function stripComments2(text3) {
|
|
32732
|
+
let out = "";
|
|
32733
|
+
let i = 0;
|
|
32734
|
+
let inString = false;
|
|
32735
|
+
while (i < text3.length) {
|
|
32736
|
+
const char = text3[i] ?? "";
|
|
32737
|
+
const next = text3[i + 1] ?? "";
|
|
32738
|
+
if (inString) {
|
|
32739
|
+
out += char;
|
|
32740
|
+
if (char === "\\") {
|
|
32741
|
+
out += next;
|
|
32742
|
+
i += 2;
|
|
32743
|
+
continue;
|
|
32744
|
+
}
|
|
32745
|
+
if (char === '"') inString = false;
|
|
32746
|
+
i += 1;
|
|
32747
|
+
continue;
|
|
32748
|
+
}
|
|
32749
|
+
if (char === '"') {
|
|
32750
|
+
inString = true;
|
|
32751
|
+
out += char;
|
|
32752
|
+
i += 1;
|
|
32753
|
+
continue;
|
|
32754
|
+
}
|
|
32755
|
+
if (char === "/" && next === "/") {
|
|
32756
|
+
while (i < text3.length && text3[i] !== "\n") i += 1;
|
|
32757
|
+
continue;
|
|
32758
|
+
}
|
|
32759
|
+
if (char === "/" && next === "*") {
|
|
32760
|
+
const end = text3.indexOf("*/", i + 2);
|
|
32761
|
+
i = end === -1 ? text3.length : end + 2;
|
|
32762
|
+
out += " ";
|
|
32763
|
+
continue;
|
|
32764
|
+
}
|
|
32765
|
+
out += char;
|
|
32766
|
+
i += 1;
|
|
32767
|
+
}
|
|
32768
|
+
return out;
|
|
32769
|
+
}
|
|
32770
|
+
|
|
32771
|
+
// ../../packages/functions-bundler/src/node-compat.ts
|
|
32772
|
+
import { createRequire as createRequire2 } from "module";
|
|
32773
|
+
import { dirname as dirname7, resolve as resolvePath } from "path";
|
|
32774
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
32775
|
+
var require2 = createRequire2(import.meta.url);
|
|
32776
|
+
var MODULE_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
32777
|
+
var REQUIRED_NODE_BUILT_IN_NAMESPACE = "node-built-in-modules";
|
|
32778
|
+
var REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
|
|
32779
|
+
var SKIPPED_ALIASES = /* @__PURE__ */ new Set(["debug"]);
|
|
32780
|
+
async function nodeCompatEnvironment(options) {
|
|
32781
|
+
const { defineEnv } = await import("unenv");
|
|
32782
|
+
const { getCloudflarePreset } = await import("@cloudflare/unenv-preset");
|
|
32783
|
+
return defineEnv({
|
|
32784
|
+
presets: [
|
|
32785
|
+
getCloudflarePreset({
|
|
32786
|
+
compatibilityDate: options.compatibilityDate,
|
|
32787
|
+
compatibilityFlags: [...options.compatibilityFlags]
|
|
32788
|
+
})
|
|
32789
|
+
],
|
|
32790
|
+
npmShims: true
|
|
32791
|
+
}).env;
|
|
32792
|
+
}
|
|
32793
|
+
function nodeCompatPlugin(options) {
|
|
32794
|
+
return {
|
|
32795
|
+
name: "hybrid-nodejs_compat",
|
|
32796
|
+
async setup(build) {
|
|
32797
|
+
const { nonPrefixedNodeModules } = await import("@cloudflare/unenv-preset");
|
|
32798
|
+
const { alias, inject, external, polyfill } = await nodeCompatEnvironment(options);
|
|
32799
|
+
const nodeJsModuleRegexp = new RegExp(
|
|
32800
|
+
`^(${nonPrefixedNodeModules.join("|")}|node:.+)$`
|
|
32801
|
+
);
|
|
32802
|
+
handleRequireCallsToNodeJSBuiltins(build, nodeJsModuleRegexp);
|
|
32803
|
+
handleUnenvAliasedPackages(build, alias, external);
|
|
32804
|
+
handleNodeJSGlobals(build, inject, polyfill, options.virtualBase);
|
|
32805
|
+
}
|
|
32806
|
+
};
|
|
32807
|
+
}
|
|
32808
|
+
function handleRequireCallsToNodeJSBuiltins(build, nodeJsModuleRegexp) {
|
|
32809
|
+
build.onResolve({ filter: nodeJsModuleRegexp }, (args) => {
|
|
32810
|
+
if (args.kind === "require-call")
|
|
32811
|
+
return { path: args.path, namespace: REQUIRED_NODE_BUILT_IN_NAMESPACE };
|
|
32812
|
+
return void 0;
|
|
32813
|
+
});
|
|
32814
|
+
build.onLoad(
|
|
32815
|
+
{ filter: /.*/, namespace: REQUIRED_NODE_BUILT_IN_NAMESPACE },
|
|
32816
|
+
({ path }) => ({
|
|
32817
|
+
contents: `import libDefault from '${path}';
|
|
32818
|
+
module.exports = libDefault;`,
|
|
32819
|
+
loader: "js"
|
|
32820
|
+
})
|
|
32821
|
+
);
|
|
32822
|
+
}
|
|
32823
|
+
function handleUnenvAliasedPackages(build, alias, external) {
|
|
32824
|
+
const aliasAbsolute = {};
|
|
32825
|
+
for (const [module, unresolvedAlias] of Object.entries(alias)) {
|
|
32826
|
+
if (SKIPPED_ALIASES.has(module)) continue;
|
|
32827
|
+
try {
|
|
32828
|
+
aliasAbsolute[module] = require2.resolve(unresolvedAlias);
|
|
32829
|
+
} catch {
|
|
32830
|
+
}
|
|
32831
|
+
}
|
|
32832
|
+
const UNENV_ALIAS_RE = new RegExp(
|
|
32833
|
+
`^(${Object.keys(aliasAbsolute).join("|")})$`
|
|
32834
|
+
);
|
|
32835
|
+
build.onResolve({ filter: UNENV_ALIAS_RE }, (args) => {
|
|
32836
|
+
const unresolvedAlias = alias[args.path] ?? "";
|
|
32837
|
+
if (args.kind === "require-call" && (unresolvedAlias.startsWith("unenv/npm/") || unresolvedAlias.startsWith("unenv/mock/")))
|
|
32838
|
+
return { path: args.path, namespace: REQUIRED_UNENV_ALIAS_NAMESPACE };
|
|
32839
|
+
return {
|
|
32840
|
+
path: aliasAbsolute[args.path] ?? args.path,
|
|
32841
|
+
external: external.includes(unresolvedAlias)
|
|
32842
|
+
};
|
|
32843
|
+
});
|
|
32844
|
+
build.onLoad(
|
|
32845
|
+
{ filter: /.*/, namespace: REQUIRED_UNENV_ALIAS_NAMESPACE },
|
|
32846
|
+
({ path }) => ({
|
|
32847
|
+
contents: `import * as esm from '${path}';
|
|
32848
|
+
module.exports = Object.entries(esm)
|
|
32849
|
+
.filter(([k,]) => k !== 'default')
|
|
32850
|
+
.reduce((cjs, [k, value]) =>
|
|
32851
|
+
Object.defineProperty(cjs, k, { value, enumerable: true }),
|
|
32852
|
+
"default" in esm ? esm.default : {}
|
|
32853
|
+
);`,
|
|
32854
|
+
loader: "js"
|
|
32855
|
+
})
|
|
32856
|
+
);
|
|
32857
|
+
}
|
|
32858
|
+
function handleNodeJSGlobals(build, inject, polyfill, virtualBase) {
|
|
32859
|
+
const UNENV_VIRTUAL_MODULE_RE = /_virtual_unenv_global_polyfill-(.+)$/;
|
|
32860
|
+
const prefix = resolvePath(virtualBase, "_virtual_unenv_global_polyfill-");
|
|
32861
|
+
const injectsByModule = /* @__PURE__ */ new Map();
|
|
32862
|
+
const virtualModulePathToSpecifier = /* @__PURE__ */ new Map();
|
|
32863
|
+
for (const [injectedName, moduleSpecifier] of Object.entries(inject)) {
|
|
32864
|
+
const [module, exportName, importName] = Array.isArray(moduleSpecifier) ? [
|
|
32865
|
+
moduleSpecifier[0] ?? "",
|
|
32866
|
+
moduleSpecifier[1] ?? "",
|
|
32867
|
+
moduleSpecifier[1] ?? ""
|
|
32868
|
+
] : [moduleSpecifier, "default", "defaultExport"];
|
|
32869
|
+
if (!injectsByModule.has(module)) {
|
|
32870
|
+
injectsByModule.set(module, []);
|
|
32871
|
+
virtualModulePathToSpecifier.set(
|
|
32872
|
+
prefix + module.replaceAll("/", "-"),
|
|
32873
|
+
module
|
|
32874
|
+
);
|
|
32875
|
+
}
|
|
32876
|
+
injectsByModule.get(module)?.push({ injectedName, exportName, importName });
|
|
32877
|
+
}
|
|
32878
|
+
build.initialOptions.inject = [
|
|
32879
|
+
...build.initialOptions.inject ?? [],
|
|
32880
|
+
...virtualModulePathToSpecifier.keys(),
|
|
32881
|
+
...polyfill.map((module) => require2.resolve(module))
|
|
32882
|
+
];
|
|
32883
|
+
build.onResolve({ filter: UNENV_VIRTUAL_MODULE_RE }, ({ path }) => ({
|
|
32884
|
+
path
|
|
32885
|
+
}));
|
|
32886
|
+
build.onLoad({ filter: UNENV_VIRTUAL_MODULE_RE }, ({ path }) => {
|
|
32887
|
+
const module = virtualModulePathToSpecifier.get(path);
|
|
32888
|
+
const injects = module === void 0 ? void 0 : injectsByModule.get(module);
|
|
32889
|
+
if (module === void 0 || injects === void 0)
|
|
32890
|
+
throw new Error(`Expected ${path} to be mapped to a module specifier`);
|
|
32891
|
+
const imports = injects.map(
|
|
32892
|
+
({ exportName, importName }) => importName === exportName ? exportName : `${exportName} as ${importName}`
|
|
32893
|
+
);
|
|
32894
|
+
return {
|
|
32895
|
+
resolveDir: MODULE_DIR,
|
|
32896
|
+
contents: `import { ${imports.join(", ")} } from "${module}";
|
|
32897
|
+
${injects.map(
|
|
32898
|
+
({ injectedName, importName }) => `globalThis.${injectedName} = ${importName};`
|
|
32899
|
+
).join("\n")}`
|
|
32900
|
+
};
|
|
32901
|
+
});
|
|
32902
|
+
}
|
|
32903
|
+
|
|
32904
|
+
// ../../packages/functions-bundler/src/deno-imports.ts
|
|
32905
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, realpathSync as realpathSync2 } from "fs";
|
|
32906
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join29, relative as relative8, sep as sep8 } from "path";
|
|
32907
|
+
|
|
32908
|
+
// ../../packages/functions-bundler/src/import-map.ts
|
|
32909
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, realpathSync } from "fs";
|
|
32910
|
+
import { basename as basename4, dirname as dirname8, join as join28, resolve as resolve12, sep as sep7 } from "path";
|
|
32911
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
32912
|
+
|
|
32913
|
+
// ../../packages/functions-bundler/src/printable.ts
|
|
32914
|
+
var UNPRINTABLE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
32915
|
+
function printable(text3) {
|
|
32916
|
+
return text3.replace(UNPRINTABLE, "?");
|
|
32917
|
+
}
|
|
32918
|
+
function causeLine(error51) {
|
|
32919
|
+
const text3 = error51 instanceof Error ? error51.message : String(error51);
|
|
32920
|
+
return printable(
|
|
32921
|
+
text3.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "unknown error"
|
|
32922
|
+
);
|
|
32923
|
+
}
|
|
32924
|
+
|
|
32925
|
+
// ../../packages/functions-bundler/src/import-map.ts
|
|
32926
|
+
var FILES = ["deno.json", "deno.jsonc", "import_map.json"];
|
|
32927
|
+
var DENO_CONFIGS = /* @__PURE__ */ new Set(["deno.json", "deno.jsonc"]);
|
|
32928
|
+
function expandBareKeys(imports) {
|
|
32929
|
+
const out = {};
|
|
32930
|
+
for (const [key, value] of Object.entries(imports)) {
|
|
32931
|
+
out[key] = value;
|
|
32932
|
+
if (key.endsWith("/") || Object.hasOwn(imports, `${key}/`)) continue;
|
|
32933
|
+
if (value.endsWith("/")) continue;
|
|
32934
|
+
const scheme = value.startsWith("jsr:") ? "jsr:" : value.startsWith("npm:") ? "npm:" : null;
|
|
32935
|
+
if (scheme === null) continue;
|
|
32936
|
+
const rest = value.slice(scheme.length).replace(/^\//, "");
|
|
32937
|
+
out[`${key}/`] = `${scheme}/${rest}/`;
|
|
32938
|
+
}
|
|
32939
|
+
return out;
|
|
32940
|
+
}
|
|
32941
|
+
function load(file2, label2) {
|
|
32942
|
+
let parsed;
|
|
32943
|
+
try {
|
|
32944
|
+
parsed = parseJsonc2(readFileSync8(file2, "utf8"));
|
|
32945
|
+
} catch {
|
|
32946
|
+
throw new FunctionBundleError(
|
|
32947
|
+
"VALIDATION_FAILED",
|
|
32948
|
+
BUNDLE_MESSAGES.importMapInvalid(label2)
|
|
32949
|
+
);
|
|
32950
|
+
}
|
|
32951
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
32952
|
+
const imports = parsed.imports;
|
|
32953
|
+
if (imports === void 0) return null;
|
|
32954
|
+
if (typeof imports !== "object" || imports === null || Array.isArray(imports) || Object.values(imports).some((target2) => typeof target2 !== "string"))
|
|
32955
|
+
throw new FunctionBundleError(
|
|
32956
|
+
"VALIDATION_FAILED",
|
|
32957
|
+
BUNDLE_MESSAGES.importMapShape(label2)
|
|
32958
|
+
);
|
|
32959
|
+
const written = imports;
|
|
32960
|
+
for (const [key, value] of Object.entries(written))
|
|
32961
|
+
if (key.endsWith("/") && !value.endsWith("/"))
|
|
32962
|
+
throw new FunctionBundleError(
|
|
32963
|
+
"VALIDATION_FAILED",
|
|
32964
|
+
printable(BUNDLE_MESSAGES.importMapPrefix(label2, key, value))
|
|
32965
|
+
);
|
|
32966
|
+
return {
|
|
32967
|
+
imports: DENO_CONFIGS.has(basename4(file2)) ? expandBareKeys(written) : written,
|
|
32968
|
+
base: dirname8(file2),
|
|
32969
|
+
label: label2
|
|
32970
|
+
};
|
|
32971
|
+
}
|
|
32972
|
+
function readImportMapFile(file2, label2) {
|
|
32973
|
+
if (!existsSync8(file2))
|
|
32974
|
+
throw new FunctionBundleError(
|
|
32975
|
+
"VALIDATION_FAILED",
|
|
32976
|
+
BUNDLE_MESSAGES.importMapMissing(label2)
|
|
32977
|
+
);
|
|
32978
|
+
return load(file2, label2);
|
|
32979
|
+
}
|
|
32980
|
+
function readImportMap(functionDir, fallback, labelOf) {
|
|
32981
|
+
for (const name of FILES) {
|
|
32982
|
+
const file2 = join28(functionDir, name);
|
|
32983
|
+
if (!existsSync8(file2)) continue;
|
|
32984
|
+
const map2 = load(file2, labelOf(file2));
|
|
32985
|
+
if (map2 !== null) return map2;
|
|
32986
|
+
}
|
|
32987
|
+
if (fallback === void 0 || fallback === null) return null;
|
|
32988
|
+
return typeof fallback === "string" ? readImportMapFile(fallback, labelOf(fallback)) : fallback;
|
|
32989
|
+
}
|
|
32990
|
+
function realPath(path) {
|
|
32991
|
+
try {
|
|
32992
|
+
return realpathSync.native(path);
|
|
32993
|
+
} catch {
|
|
32994
|
+
return path;
|
|
32995
|
+
}
|
|
32996
|
+
}
|
|
32997
|
+
var isPathLike = (text3) => text3.startsWith("./") || text3.startsWith("../") || text3.startsWith("/");
|
|
32998
|
+
function filePath(text3) {
|
|
32999
|
+
if (!text3.startsWith("file:")) return null;
|
|
33000
|
+
try {
|
|
33001
|
+
return fileURLToPath3(text3);
|
|
33002
|
+
} catch {
|
|
33003
|
+
return null;
|
|
33004
|
+
}
|
|
33005
|
+
}
|
|
33006
|
+
function realKey(path, trailing) {
|
|
33007
|
+
const real = realPath(path);
|
|
33008
|
+
return trailing && !real.endsWith(sep7) ? `${real}${sep7}` : real;
|
|
33009
|
+
}
|
|
33010
|
+
var normalized = /* @__PURE__ */ new WeakMap();
|
|
33011
|
+
function keysOf(map2) {
|
|
33012
|
+
const cached2 = normalized.get(map2);
|
|
33013
|
+
if (cached2 !== void 0) return cached2;
|
|
33014
|
+
const base2 = realPath(map2.base);
|
|
33015
|
+
const keys = /* @__PURE__ */ new Map();
|
|
33016
|
+
for (const [key, target2] of Object.entries(map2.imports)) {
|
|
33017
|
+
const trailing = key.endsWith("/");
|
|
33018
|
+
const file2 = filePath(key);
|
|
33019
|
+
const name = file2 !== null ? realKey(file2, trailing) : isPathLike(key) ? realKey(resolve12(base2, key), trailing) : key;
|
|
33020
|
+
if (!keys.has(name)) keys.set(name, target2);
|
|
33021
|
+
}
|
|
33022
|
+
normalized.set(map2, keys);
|
|
33023
|
+
return keys;
|
|
33024
|
+
}
|
|
33025
|
+
function mapSpecifier(map2, specifier, importerDir) {
|
|
33026
|
+
let lookup = specifier;
|
|
33027
|
+
const trailing = specifier.endsWith("/");
|
|
33028
|
+
const file2 = filePath(specifier);
|
|
33029
|
+
if (file2 !== null) lookup = realKey(file2, trailing);
|
|
33030
|
+
else if (specifier.startsWith("/"))
|
|
33031
|
+
lookup = realKey(resolve12(specifier), trailing);
|
|
33032
|
+
else if (isPathLike(specifier)) {
|
|
33033
|
+
if (importerDir === void 0) return null;
|
|
33034
|
+
lookup = realKey(resolve12(importerDir, specifier), trailing);
|
|
33035
|
+
}
|
|
33036
|
+
const keys = keysOf(map2);
|
|
33037
|
+
let target2 = keys.get(lookup);
|
|
33038
|
+
if (target2 === void 0) {
|
|
33039
|
+
let prefix;
|
|
33040
|
+
for (const key of keys.keys())
|
|
33041
|
+
if ((key.endsWith("/") || key.endsWith(sep7)) && lookup.startsWith(key) && (prefix === void 0 || key.length > prefix.length))
|
|
33042
|
+
prefix = key;
|
|
33043
|
+
if (prefix === void 0) return null;
|
|
33044
|
+
target2 = `${keys.get(prefix) ?? ""}${lookup.slice(prefix.length)}`;
|
|
33045
|
+
}
|
|
33046
|
+
if (target2.startsWith("./") || target2.startsWith("../"))
|
|
33047
|
+
return resolve12(map2.base, target2);
|
|
33048
|
+
return filePath(target2) ?? target2;
|
|
33049
|
+
}
|
|
33050
|
+
|
|
33051
|
+
// ../../packages/functions-bundler/src/range.ts
|
|
33052
|
+
function parse3(text3) {
|
|
33053
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(text3.trim());
|
|
33054
|
+
if (!match) return null;
|
|
33055
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
33056
|
+
}
|
|
33057
|
+
function partial2(text3) {
|
|
33058
|
+
if (!/^\d+(\.\d+){0,2}$/.test(text3)) return null;
|
|
33059
|
+
return text3.split(".").map(Number);
|
|
33060
|
+
}
|
|
33061
|
+
function compare2(a, b) {
|
|
33062
|
+
for (let i = 0; i < 3; i += 1) {
|
|
33063
|
+
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
|
33064
|
+
if (diff !== 0) return diff;
|
|
33065
|
+
}
|
|
33066
|
+
return 0;
|
|
33067
|
+
}
|
|
33068
|
+
var filled = (parts) => [
|
|
33069
|
+
parts[0] ?? 0,
|
|
33070
|
+
parts[1] ?? 0,
|
|
33071
|
+
parts[2] ?? 0
|
|
33072
|
+
];
|
|
33073
|
+
function inRange(versionText, rangeText) {
|
|
33074
|
+
const version3 = parse3(versionText);
|
|
33075
|
+
if (version3 === null) return null;
|
|
33076
|
+
const range = rangeText.trim();
|
|
33077
|
+
if (range === "" || range === "*" || range === "latest") return true;
|
|
33078
|
+
const operator = /^(\^|~|>=)?(.*)$/.exec(range);
|
|
33079
|
+
const op = operator?.[1] ?? "";
|
|
33080
|
+
const parts = partial2(operator?.[2] ?? "");
|
|
33081
|
+
if (parts === null) return null;
|
|
33082
|
+
const floor = filled(parts);
|
|
33083
|
+
if (op === ">=") return compare2(version3, floor) >= 0;
|
|
33084
|
+
if (op === "") return parts.every((part, i) => version3[i] === part);
|
|
33085
|
+
if (compare2(version3, floor) < 0) return false;
|
|
33086
|
+
if (op === "~")
|
|
33087
|
+
return version3[0] === floor[0] && (parts.length < 2 || version3[1] === floor[1]);
|
|
33088
|
+
if (floor[0] !== 0 || parts.length === 1) return version3[0] === floor[0];
|
|
33089
|
+
if (floor[1] !== 0 || parts.length === 2)
|
|
33090
|
+
return version3[0] === 0 && version3[1] === floor[1];
|
|
33091
|
+
return version3[0] === 0 && version3[1] === 0 && version3[2] === floor[2];
|
|
33092
|
+
}
|
|
33093
|
+
|
|
33094
|
+
// ../../packages/functions-bundler/src/deno-imports.ts
|
|
33095
|
+
var MISSING_NAMESPACE = "naro-missing-package";
|
|
33096
|
+
var MAPPED = "naroMapped";
|
|
33097
|
+
function parseNpm(text3) {
|
|
33098
|
+
const scoped = text3.startsWith("@");
|
|
33099
|
+
const slash = text3.indexOf("/", scoped ? text3.indexOf("/") + 1 : 0);
|
|
33100
|
+
const head = slash === -1 ? text3 : text3.slice(0, slash);
|
|
33101
|
+
const sub = slash === -1 ? "" : text3.slice(slash);
|
|
33102
|
+
const at = head.indexOf("@", scoped ? 1 : 0);
|
|
33103
|
+
return at === -1 ? { pkg: head, range: void 0, sub } : { pkg: head.slice(0, at), range: head.slice(at + 1) || void 0, sub };
|
|
33104
|
+
}
|
|
33105
|
+
var afterScheme = (specifier) => specifier.slice(4).replace(/^\//, "");
|
|
33106
|
+
var isPackageName = (pkg) => pkg.startsWith("@") ? /^@[^/]+\/[^/]+$/.test(pkg) : /^[^/]+$/.test(pkg);
|
|
33107
|
+
var refuse2 = (text3) => ({ errors: [{ text: printable(text3) }] });
|
|
33108
|
+
var installOf = (pkg, range) => range ? `${pkg}@${range}` : pkg;
|
|
33109
|
+
function realPath2(path) {
|
|
33110
|
+
try {
|
|
33111
|
+
return realpathSync2.native(path);
|
|
33112
|
+
} catch {
|
|
33113
|
+
return path;
|
|
33114
|
+
}
|
|
33115
|
+
}
|
|
33116
|
+
var fromRoot = (projectRoot, path) => relative8(projectRoot, path).split(sep8).join("/");
|
|
33117
|
+
function isInside(path, projectRoot) {
|
|
33118
|
+
const rel = relative8(projectRoot, path);
|
|
33119
|
+
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep8}`) && !isAbsolute5(rel);
|
|
33120
|
+
}
|
|
33121
|
+
function packageDir(from, projectRoot) {
|
|
33122
|
+
let current = from;
|
|
33123
|
+
while (isInside(current, projectRoot)) {
|
|
33124
|
+
const inNodeModules = fromRoot(projectRoot, current).split("/").includes("node_modules");
|
|
33125
|
+
if (!inNodeModules && existsSync9(join29(current, "package.json")))
|
|
33126
|
+
return fromRoot(projectRoot, current) || ".";
|
|
33127
|
+
const up = dirname9(current);
|
|
33128
|
+
if (up === current) break;
|
|
33129
|
+
current = up;
|
|
33130
|
+
}
|
|
33131
|
+
return ".";
|
|
33132
|
+
}
|
|
33133
|
+
function isInstalled(from, pkg) {
|
|
33134
|
+
if (pkg === "") return false;
|
|
33135
|
+
let current = from;
|
|
33136
|
+
for (; ; ) {
|
|
33137
|
+
if (existsSync9(join29(current, "node_modules", pkg))) return true;
|
|
33138
|
+
const up = dirname9(current);
|
|
33139
|
+
if (up === current) return false;
|
|
33140
|
+
current = up;
|
|
33141
|
+
}
|
|
33142
|
+
}
|
|
33143
|
+
var NODE_MODULES = /[\\/]node_modules[\\/]/;
|
|
33144
|
+
function installedVersion(resolved, pkg) {
|
|
33145
|
+
let current = dirname9(resolved);
|
|
33146
|
+
for (; ; ) {
|
|
33147
|
+
const file2 = join29(current, "package.json");
|
|
33148
|
+
if (existsSync9(file2)) {
|
|
33149
|
+
try {
|
|
33150
|
+
const manifest2 = JSON.parse(readFileSync9(file2, "utf8"));
|
|
33151
|
+
if (manifest2.name === pkg && typeof manifest2.version === "string")
|
|
33152
|
+
return manifest2.version;
|
|
33153
|
+
} catch {
|
|
33154
|
+
}
|
|
33155
|
+
}
|
|
33156
|
+
const up = dirname9(current);
|
|
33157
|
+
if (up === current) return null;
|
|
33158
|
+
current = up;
|
|
33159
|
+
}
|
|
33160
|
+
}
|
|
33161
|
+
var byCodePoint = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
33162
|
+
function missingPackagesMessage(unsorted) {
|
|
33163
|
+
return printable(installSentence(unsorted));
|
|
33164
|
+
}
|
|
33165
|
+
function installSentence(unsorted) {
|
|
33166
|
+
const missing = [...unsorted].sort(
|
|
33167
|
+
(a, b) => byCodePoint(`${a.kind}:${a.spec}`, `${b.kind}:${b.spec}`)
|
|
33168
|
+
);
|
|
33169
|
+
const [only] = missing;
|
|
33170
|
+
if (missing.length === 1 && only !== void 0)
|
|
33171
|
+
return only.kind === "npm" ? BUNDLE_MESSAGES.npmMissing(only.spec, only.install, only.dir) : BUNDLE_MESSAGES.jsrMissing(only.spec, only.install);
|
|
33172
|
+
return BUNDLE_MESSAGES.missingMany(
|
|
33173
|
+
missing.map((item) => `${item.kind}:${item.spec}`),
|
|
33174
|
+
// npm:zod@3 and npm:zod@3/x need one zod@3; zod@3 and zod@4 stay two.
|
|
33175
|
+
[...new Set(missing.map((item) => item.install))].sort(byCodePoint),
|
|
33176
|
+
missing.some((item) => item.kind === "jsr")
|
|
33177
|
+
);
|
|
33178
|
+
}
|
|
33179
|
+
var ESM_SH_NOT_NPM = /* @__PURE__ */ new Set(["stable", "gh", "jsr", "pr"]);
|
|
33180
|
+
var URL_IMPORT = /^https?:\/\//;
|
|
33181
|
+
var WASM = /\.wasm(\?.*)?$/;
|
|
33182
|
+
function urlRefusal(url2) {
|
|
33183
|
+
let parsed;
|
|
33184
|
+
try {
|
|
33185
|
+
parsed = new URL(url2);
|
|
33186
|
+
} catch {
|
|
33187
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33188
|
+
}
|
|
33189
|
+
if (parsed.hostname === "esm.sh") {
|
|
33190
|
+
const path = parsed.pathname.replace(/^\/v\d+(?=\/)/, "");
|
|
33191
|
+
const first = path.split("/")[1] ?? "";
|
|
33192
|
+
if (ESM_SH_NOT_NPM.has(first) || first.startsWith("*"))
|
|
33193
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33194
|
+
const match = /^\/((?:@[^/@]+\/)?[^/@]+)(?:@([^/?]+))?/.exec(path);
|
|
33195
|
+
if (match?.[1])
|
|
33196
|
+
return BUNDLE_MESSAGES.urlNpm(
|
|
33197
|
+
url2,
|
|
33198
|
+
`npm:${installOf(match[1], match[2])}`
|
|
33199
|
+
);
|
|
33200
|
+
}
|
|
33201
|
+
if (parsed.hostname === "deno.land") {
|
|
33202
|
+
const match = /^\/std(?:@[^/]+)?\/([^/]+)/.exec(parsed.pathname);
|
|
33203
|
+
if (match?.[1]) return BUNDLE_MESSAGES.urlStd(url2, match[1]);
|
|
33204
|
+
}
|
|
33205
|
+
return BUNDLE_MESSAGES.urlOther(url2);
|
|
33206
|
+
}
|
|
33207
|
+
function denoImportsPlugin(options) {
|
|
33208
|
+
const { importMap, state } = options;
|
|
33209
|
+
const projectRoot = realPath2(options.projectRoot);
|
|
33210
|
+
const seenMissing = /* @__PURE__ */ new Set();
|
|
33211
|
+
return {
|
|
33212
|
+
name: "naro-deno-imports",
|
|
33213
|
+
setup(build) {
|
|
33214
|
+
build.onStart(() => {
|
|
33215
|
+
state.missing.length = 0;
|
|
33216
|
+
state.warnings.length = 0;
|
|
33217
|
+
seenMissing.clear();
|
|
33218
|
+
});
|
|
33219
|
+
const rewritten = async (kind, written, spec, target2, args) => {
|
|
33220
|
+
const result = await build.resolve(target2, {
|
|
33221
|
+
kind: args.kind,
|
|
33222
|
+
resolveDir: args.resolveDir,
|
|
33223
|
+
importer: args.importer,
|
|
33224
|
+
pluginData: { [MAPPED]: true }
|
|
33225
|
+
});
|
|
33226
|
+
const pkgName = kind === "npm" ? spec.pkg : target2.split("/").slice(0, 2).join("/");
|
|
33227
|
+
if (result.errors.length > 0) {
|
|
33228
|
+
if (isInstalled(args.resolveDir, pkgName))
|
|
33229
|
+
return { errors: result.errors };
|
|
33230
|
+
if (!seenMissing.has(`${kind}:${written}`)) {
|
|
33231
|
+
seenMissing.add(`${kind}:${written}`);
|
|
33232
|
+
state.missing.push({
|
|
33233
|
+
kind,
|
|
33234
|
+
spec: written,
|
|
33235
|
+
install: installOf(pkgName, spec.range),
|
|
33236
|
+
dir: packageDir(args.resolveDir, projectRoot)
|
|
33237
|
+
});
|
|
33238
|
+
}
|
|
33239
|
+
return { path: `${kind}:${written}`, namespace: MISSING_NAMESPACE };
|
|
33240
|
+
}
|
|
33241
|
+
if (spec.range) {
|
|
33242
|
+
const version3 = installedVersion(result.path, pkgName);
|
|
33243
|
+
if (version3 !== null && inRange(version3, spec.range) === false) {
|
|
33244
|
+
const warning = printable(
|
|
33245
|
+
BUNDLE_MESSAGES.outOfRange(
|
|
33246
|
+
pkgName,
|
|
33247
|
+
version3,
|
|
33248
|
+
spec.range,
|
|
33249
|
+
fromRoot(projectRoot, args.importer)
|
|
33250
|
+
)
|
|
33251
|
+
);
|
|
33252
|
+
if (!state.warnings.includes(warning)) state.warnings.push(warning);
|
|
33253
|
+
}
|
|
33254
|
+
}
|
|
33255
|
+
return {
|
|
33256
|
+
path: result.path,
|
|
33257
|
+
external: result.external,
|
|
33258
|
+
namespace: result.namespace,
|
|
33259
|
+
sideEffects: result.sideEffects
|
|
33260
|
+
};
|
|
33261
|
+
};
|
|
33262
|
+
build.onResolve({ filter: /.*/, namespace: "file" }, async (args) => {
|
|
33263
|
+
if (importMap === null || args.kind === "entry-point") return void 0;
|
|
33264
|
+
if (NODE_MODULES.test(args.importer)) return void 0;
|
|
33265
|
+
if (args.pluginData?.[MAPPED])
|
|
33266
|
+
return void 0;
|
|
33267
|
+
const mapped = mapSpecifier(importMap, args.path, args.resolveDir);
|
|
33268
|
+
if (mapped === null) return void 0;
|
|
33269
|
+
if (WASM.test(mapped)) return refuse2(BUNDLE_MESSAGES.wasm(args.path));
|
|
33270
|
+
if (URL_IMPORT.test(mapped)) return refuse2(urlRefusal(mapped));
|
|
33271
|
+
const result = await build.resolve(mapped, {
|
|
33272
|
+
kind: args.kind,
|
|
33273
|
+
resolveDir: args.resolveDir,
|
|
33274
|
+
importer: args.importer,
|
|
33275
|
+
pluginData: { [MAPPED]: true }
|
|
33276
|
+
});
|
|
33277
|
+
if (result.errors.length > 0) return { errors: result.errors };
|
|
33278
|
+
return {
|
|
33279
|
+
path: result.path,
|
|
33280
|
+
external: result.external,
|
|
33281
|
+
namespace: result.namespace,
|
|
33282
|
+
sideEffects: result.sideEffects
|
|
33283
|
+
};
|
|
33284
|
+
});
|
|
33285
|
+
build.onResolve({ filter: /^npm:/ }, async (args) => {
|
|
33286
|
+
const written = afterScheme(args.path);
|
|
33287
|
+
const spec = parseNpm(written);
|
|
33288
|
+
if (!isPackageName(spec.pkg))
|
|
33289
|
+
return refuse2(BUNDLE_MESSAGES.noPackageName(args.path, "npm:zod@3"));
|
|
33290
|
+
return rewritten("npm", written, spec, `${spec.pkg}${spec.sub}`, args);
|
|
33291
|
+
});
|
|
33292
|
+
build.onResolve({ filter: /^jsr:/ }, async (args) => {
|
|
33293
|
+
const written = afterScheme(args.path);
|
|
33294
|
+
const spec = parseNpm(written);
|
|
33295
|
+
if (spec.pkg !== "" && !spec.pkg.startsWith("@"))
|
|
33296
|
+
return refuse2(BUNDLE_MESSAGES.jsrNoScope(written));
|
|
33297
|
+
if (!isPackageName(spec.pkg))
|
|
33298
|
+
return refuse2(
|
|
33299
|
+
BUNDLE_MESSAGES.noPackageName(args.path, "jsr:@std/http@1")
|
|
33300
|
+
);
|
|
33301
|
+
const [scope, name] = spec.pkg.slice(1).split("/");
|
|
33302
|
+
return rewritten(
|
|
33303
|
+
"jsr",
|
|
33304
|
+
written,
|
|
33305
|
+
spec,
|
|
33306
|
+
`@jsr/${scope ?? ""}__${name ?? ""}${spec.sub}`,
|
|
33307
|
+
args
|
|
33308
|
+
);
|
|
33309
|
+
});
|
|
33310
|
+
build.onResolve(
|
|
33311
|
+
{ filter: URL_IMPORT },
|
|
33312
|
+
(args) => refuse2(urlRefusal(args.path))
|
|
33313
|
+
);
|
|
33314
|
+
build.onResolve(
|
|
33315
|
+
{ filter: WASM },
|
|
33316
|
+
(args) => refuse2(BUNDLE_MESSAGES.wasm(args.path))
|
|
33317
|
+
);
|
|
33318
|
+
build.onLoad({ filter: /.*/, namespace: MISSING_NAMESPACE }, () => ({
|
|
33319
|
+
contents: "module.exports = {};",
|
|
33320
|
+
loader: "js"
|
|
33321
|
+
}));
|
|
33322
|
+
build.onEnd(
|
|
33323
|
+
() => state.missing.length === 0 ? void 0 : { errors: [{ text: missingPackagesMessage(state.missing) }] }
|
|
33324
|
+
);
|
|
33325
|
+
}
|
|
33326
|
+
};
|
|
33327
|
+
}
|
|
33328
|
+
|
|
33329
|
+
// ../../packages/functions-bundler/src/bundle.ts
|
|
33330
|
+
import { createHash as createHash4 } from "crypto";
|
|
33331
|
+
import { join as join30, relative as relative9, sep as sep9 } from "path";
|
|
33332
|
+
import { gzipSync } from "zlib";
|
|
33333
|
+
|
|
33334
|
+
// ../../packages/functions-runtime/dist/index.js
|
|
33335
|
+
var NARO_SHIM_MODULE = "naro-shim.js";
|
|
33336
|
+
var NARO_USER_MODULE = "user.js";
|
|
33337
|
+
var NARO_BOOT_MODULE = "naro-boot.js";
|
|
33338
|
+
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';
|
|
33339
|
+
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';
|
|
33340
|
+
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';
|
|
33341
|
+
var FUNCTIONS_COMPATIBILITY_DATE = "2026-08-19";
|
|
33342
|
+
var FUNCTIONS_COMPATIBILITY_FLAGS = Object.freeze(["nodejs_compat", "global_fetch_strictly_public"]);
|
|
33343
|
+
|
|
33344
|
+
// ../../packages/functions-bundler/src/bundle.ts
|
|
33345
|
+
var NARO_JS_FUNCTIONS = /Could not resolve "naro-js\/functions"/;
|
|
33346
|
+
var runtimeGzip;
|
|
33347
|
+
function runtimeCompressedBytes() {
|
|
33348
|
+
runtimeGzip ??= gzipSync(NARO_ENTRY_SOURCE).byteLength + gzipSync(NARO_SHIM_SOURCE).byteLength;
|
|
33349
|
+
return runtimeGzip;
|
|
33350
|
+
}
|
|
33351
|
+
function relativeLabel(root, file2) {
|
|
33352
|
+
return relative9(root, file2).split(sep9).join("/");
|
|
33353
|
+
}
|
|
33354
|
+
function location(message) {
|
|
33355
|
+
return message.location ? `${message.location.file}:${String(message.location.line)}` : null;
|
|
33356
|
+
}
|
|
33357
|
+
function failure(name, error51, state) {
|
|
33358
|
+
if (error51 instanceof FunctionBundleError) return error51;
|
|
33359
|
+
if (state.missing.length > 0)
|
|
33360
|
+
return new FunctionBundleError(
|
|
33361
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33362
|
+
missingPackagesMessage(state.missing)
|
|
33363
|
+
);
|
|
33364
|
+
const errors = error51.errors ?? [];
|
|
33365
|
+
if (errors.length === 0)
|
|
33366
|
+
return new FunctionBundleError(
|
|
33367
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33368
|
+
BUNDLE_MESSAGES.otherBundle(
|
|
33369
|
+
name,
|
|
33370
|
+
error51 instanceof Error ? error51.message : String(error51),
|
|
33371
|
+
null
|
|
33372
|
+
)
|
|
33373
|
+
);
|
|
33374
|
+
const lines = errors.map((message) => {
|
|
33375
|
+
if (NARO_JS_FUNCTIONS.test(message.text))
|
|
33376
|
+
return BUNDLE_MESSAGES.naroJsMissing;
|
|
33377
|
+
if (message.pluginName === "naro-deno-imports") return message.text;
|
|
33378
|
+
return BUNDLE_MESSAGES.otherBundle(name, message.text, location(message));
|
|
33379
|
+
});
|
|
33380
|
+
return new FunctionBundleError(
|
|
33381
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33382
|
+
[...new Set(lines)].sort().join("\n")
|
|
33383
|
+
);
|
|
33384
|
+
}
|
|
33385
|
+
async function bundleFunction(source, folder, options = {}) {
|
|
33386
|
+
let build;
|
|
33387
|
+
try {
|
|
33388
|
+
({ build } = await import("esbuild"));
|
|
33389
|
+
} catch (error51) {
|
|
33390
|
+
throw new FunctionBundleError(
|
|
33391
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33392
|
+
BUNDLE_MESSAGES.esbuildMissing(source.name, causeLine(error51)),
|
|
33393
|
+
{ cause: error51 }
|
|
33394
|
+
);
|
|
33395
|
+
}
|
|
33396
|
+
const importMap = readImportMap(
|
|
33397
|
+
source.dir,
|
|
33398
|
+
options.importMap !== void 0 ? options.importMap : options.importMapFile,
|
|
33399
|
+
(file2) => relativeLabel(folder.root, file2)
|
|
33400
|
+
);
|
|
33401
|
+
const state = { missing: [], warnings: [] };
|
|
33402
|
+
let result;
|
|
33403
|
+
try {
|
|
33404
|
+
result = await build({
|
|
33405
|
+
absWorkingDir: folder.root,
|
|
33406
|
+
entryPoints: [source.entry],
|
|
33407
|
+
outfile: join30(source.dir, NARO_USER_MODULE),
|
|
33408
|
+
bundle: true,
|
|
33409
|
+
write: false,
|
|
33410
|
+
format: "esm",
|
|
33411
|
+
platform: "neutral",
|
|
33412
|
+
target: "es2024",
|
|
33413
|
+
conditions: ["workerd", "worker", "browser"],
|
|
33414
|
+
mainFields: ["browser", "module", "main"],
|
|
33415
|
+
external: ["cloudflare:*"],
|
|
33416
|
+
metafile: true,
|
|
33417
|
+
legalComments: "none",
|
|
33418
|
+
logLevel: "silent",
|
|
33419
|
+
plugins: [
|
|
33420
|
+
denoImportsPlugin({ importMap, state, projectRoot: folder.root }),
|
|
33421
|
+
nodeCompatPlugin({
|
|
33422
|
+
compatibilityDate: FUNCTIONS_COMPATIBILITY_DATE,
|
|
33423
|
+
compatibilityFlags: FUNCTIONS_COMPATIBILITY_FLAGS,
|
|
33424
|
+
virtualBase: folder.root
|
|
33425
|
+
})
|
|
33426
|
+
]
|
|
33427
|
+
});
|
|
33428
|
+
} catch (error51) {
|
|
33429
|
+
throw failure(source.name, error51, state);
|
|
33430
|
+
}
|
|
33431
|
+
if (state.missing.length > 0)
|
|
33432
|
+
throw new FunctionBundleError(
|
|
33433
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33434
|
+
missingPackagesMessage(state.missing)
|
|
33435
|
+
);
|
|
33436
|
+
const [output] = result.outputFiles;
|
|
33437
|
+
if (output === void 0 || result.outputFiles.length !== 1)
|
|
33438
|
+
throw new FunctionBundleError(
|
|
33439
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33440
|
+
BUNDLE_MESSAGES.otherBundle(
|
|
33441
|
+
source.name,
|
|
33442
|
+
"esbuild did not produce exactly one file",
|
|
33443
|
+
null
|
|
33444
|
+
)
|
|
33445
|
+
);
|
|
33446
|
+
const bytes2 = output.contents.byteLength;
|
|
33447
|
+
if (bytes2 > (options.maxBytes ?? FUNCTION_BUNDLE_MAX_BYTES)) {
|
|
33448
|
+
const inputs = Object.values(result.metafile.outputs)[0]?.inputs ?? {};
|
|
33449
|
+
const biggest = Object.entries(inputs).sort(([, a], [, b]) => b.bytesInOutput - a.bytesInOutput).slice(0, 5).map(
|
|
33450
|
+
([path, input]) => `${path} (${input.bytesInOutput.toLocaleString("en-US")} bytes)`
|
|
33451
|
+
).join(", ");
|
|
33452
|
+
throw new FunctionBundleError(
|
|
33453
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33454
|
+
BUNDLE_MESSAGES.tooLarge(source.name, bytes2, biggest)
|
|
33455
|
+
);
|
|
33456
|
+
}
|
|
33457
|
+
const compressedBytes = gzipSync(output.contents).byteLength + runtimeCompressedBytes();
|
|
33458
|
+
if (compressedBytes > (options.maxCompressedBytes ?? FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES))
|
|
33459
|
+
throw new FunctionBundleError(
|
|
33460
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
33461
|
+
BUNDLE_MESSAGES.tooLargeCompressed(source.name, compressedBytes)
|
|
33462
|
+
);
|
|
33463
|
+
return {
|
|
33464
|
+
name: source.name,
|
|
33465
|
+
code: output.text,
|
|
33466
|
+
contents: output.contents,
|
|
33467
|
+
bytes: bytes2,
|
|
33468
|
+
compressedBytes,
|
|
33469
|
+
sha256: createHash4("sha256").update(output.contents).digest("hex"),
|
|
33470
|
+
warnings: [...state.warnings]
|
|
33471
|
+
};
|
|
33472
|
+
}
|
|
33473
|
+
|
|
33474
|
+
// ../../packages/functions-bundler/src/boot.ts
|
|
33475
|
+
var BOOT_PATH = "/__naro/boot";
|
|
33476
|
+
var BOOT_TIMEOUT_MS = 3e4;
|
|
33477
|
+
var BOOT_SECRET_PLACEHOLDER = "naro-boot-check-placeholder";
|
|
33478
|
+
var text2 = (value) => ({ type: "text", value });
|
|
33479
|
+
function bindings(name, secretNames) {
|
|
33480
|
+
return {
|
|
33481
|
+
// First, so a name that is also a fixed binding never replaces it.
|
|
33482
|
+
...Object.fromEntries(
|
|
33483
|
+
secretNames.map((secret) => [secret, text2(BOOT_SECRET_PLACEHOLDER)])
|
|
33484
|
+
),
|
|
33485
|
+
NARO_PROJECT_ID: text2("00000000-0000-4000-8000-000000000000"),
|
|
33486
|
+
NARO_FUNCTION_NAME: text2(name),
|
|
33487
|
+
NARO_ROOT_DOMAIN: text2("naro.sh"),
|
|
33488
|
+
NARO_URL: text2("https://boot-check.naro.sh"),
|
|
33489
|
+
NARO_ANON_KEY: text2("naro_pk_boot_check"),
|
|
33490
|
+
NARO_SERVICE_KEY: text2("naro_sk_boot_check"),
|
|
33491
|
+
DB: { type: "d1", id: "boot-check" }
|
|
33492
|
+
};
|
|
33493
|
+
}
|
|
33494
|
+
var WORKERD_SERVICE_HEAD = /^service core:user:naro-boot: /;
|
|
33495
|
+
function bootErrorLine(logs, error51) {
|
|
33496
|
+
const logged = logs.find((line2) => line2.includes("Uncaught")) ?? logs.find((line2) => line2.includes("Error"));
|
|
33497
|
+
const raw = logged ?? (error51 instanceof Error ? error51.message : String(error51));
|
|
33498
|
+
const line = raw.split("\n").map((part) => part.trim()).find((part) => part.length > 0) ?? "unknown error";
|
|
33499
|
+
return printable(line.replace(WORKERD_SERVICE_HEAD, ""));
|
|
33500
|
+
}
|
|
33501
|
+
async function bootCheck(name, code, options = {}) {
|
|
33502
|
+
const timeoutMs = options.timeoutMs ?? BOOT_TIMEOUT_MS;
|
|
33503
|
+
let Miniflare;
|
|
33504
|
+
try {
|
|
33505
|
+
({ Miniflare } = await import("miniflare"));
|
|
33506
|
+
} catch (error51) {
|
|
33507
|
+
throw new FunctionBundleError(
|
|
33508
|
+
"FUNCTION_BOOT_FAILED",
|
|
33509
|
+
BUNDLE_MESSAGES.miniflareMissing(name, causeLine(error51)),
|
|
33510
|
+
{ cause: error51 }
|
|
33511
|
+
);
|
|
33512
|
+
}
|
|
33513
|
+
const logs = [];
|
|
33514
|
+
let mf;
|
|
33515
|
+
let timer;
|
|
33516
|
+
try {
|
|
33517
|
+
mf = new Miniflare({
|
|
33518
|
+
handleStructuredLogs: (log2) => logs.push(log2.message),
|
|
33519
|
+
workers: [
|
|
33520
|
+
{
|
|
33521
|
+
config: {
|
|
33522
|
+
// No `type`: the Miniflare wrangler pins (5.20261006) refuses
|
|
33523
|
+
// the key the functions-runtime tests' older one still takes.
|
|
33524
|
+
name: "naro-boot",
|
|
33525
|
+
compatibilityDate: FUNCTIONS_COMPATIBILITY_DATE,
|
|
33526
|
+
compatibilityFlags: [...FUNCTIONS_COMPATIBILITY_FLAGS],
|
|
33527
|
+
manifest: {
|
|
33528
|
+
mainModule: NARO_BOOT_MODULE,
|
|
33529
|
+
modules: {
|
|
33530
|
+
[NARO_BOOT_MODULE]: { type: "esm", contents: NARO_BOOT_SOURCE },
|
|
33531
|
+
[NARO_SHIM_MODULE]: { type: "esm", contents: NARO_SHIM_SOURCE },
|
|
33532
|
+
[NARO_USER_MODULE]: { type: "esm", contents: code }
|
|
33533
|
+
}
|
|
33534
|
+
},
|
|
33535
|
+
env: bindings(name, options.secretNames ?? [])
|
|
33536
|
+
}
|
|
33537
|
+
}
|
|
33538
|
+
]
|
|
33539
|
+
});
|
|
33540
|
+
const started = mf;
|
|
33541
|
+
const answer = async () => {
|
|
33542
|
+
await started.ready;
|
|
33543
|
+
const worker = await started.getWorker("naro-boot");
|
|
33544
|
+
const response2 = await worker.fetch(`http://boot.local${BOOT_PATH}`);
|
|
33545
|
+
const body2 = await response2.json().catch(() => null);
|
|
33546
|
+
return { response: response2, body: body2 };
|
|
33547
|
+
};
|
|
33548
|
+
const timedOut = new Promise((_, reject) => {
|
|
33549
|
+
timer = setTimeout(() => {
|
|
33550
|
+
reject(
|
|
33551
|
+
new FunctionBundleError(
|
|
33552
|
+
"FUNCTION_BOOT_FAILED",
|
|
33553
|
+
BUNDLE_MESSAGES.bootTimeout(name, timeoutMs)
|
|
33554
|
+
)
|
|
33555
|
+
);
|
|
33556
|
+
}, timeoutMs);
|
|
33557
|
+
});
|
|
33558
|
+
const { response, body } = await Promise.race([answer(), timedOut]);
|
|
33559
|
+
if (response.status !== 200 || body === null)
|
|
33560
|
+
throw new Error(
|
|
33561
|
+
logs[0] ?? `the boot request answered ${String(response.status)}`
|
|
33562
|
+
);
|
|
33563
|
+
if (body.handler !== true)
|
|
33564
|
+
throw new FunctionBundleError(
|
|
33565
|
+
"FUNCTION_BOOT_FAILED",
|
|
33566
|
+
BUNDLE_MESSAGES.noHandler(name)
|
|
33567
|
+
);
|
|
33568
|
+
} catch (error51) {
|
|
33569
|
+
if (error51 instanceof FunctionBundleError) throw error51;
|
|
33570
|
+
throw new FunctionBundleError(
|
|
33571
|
+
"FUNCTION_BOOT_FAILED",
|
|
33572
|
+
BUNDLE_MESSAGES.failedToStart(name, bootErrorLine(logs, error51))
|
|
33573
|
+
);
|
|
33574
|
+
} finally {
|
|
33575
|
+
clearTimeout(timer);
|
|
33576
|
+
await mf?.dispose().catch(() => void 0);
|
|
33577
|
+
}
|
|
33578
|
+
}
|
|
33579
|
+
|
|
33580
|
+
// src/commands/migration.ts
|
|
33581
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
33582
|
+
import { join as join31, relative as relative10 } from "path";
|
|
33583
|
+
|
|
33584
|
+
// src/data-environment.ts
|
|
33585
|
+
var DATA_ENV_MESSAGES = {
|
|
33586
|
+
name: "--env must be production or an environment name.",
|
|
33587
|
+
preview: "--env preview has no data of its own: preview deployments use production's. Drop --env, or name an environment.",
|
|
33588
|
+
projectWide: (command) => `--env is not an option of ${command}: turning the backend on or off applies to every environment.`
|
|
33589
|
+
};
|
|
33590
|
+
function dataEnvironment(flags) {
|
|
33591
|
+
const value = flags.env;
|
|
33592
|
+
if (value === void 0 || value === "production") return null;
|
|
33593
|
+
if (value === "preview")
|
|
33594
|
+
throw new NaroError("VALIDATION_FAILED", DATA_ENV_MESSAGES.preview);
|
|
33595
|
+
if (environmentSlugProblem(value) !== null)
|
|
33596
|
+
throw new NaroError("VALIDATION_FAILED", DATA_ENV_MESSAGES.name);
|
|
33597
|
+
return value;
|
|
33598
|
+
}
|
|
33599
|
+
async function dataClient(context) {
|
|
33600
|
+
return (await context.client()).inEnvironment(dataEnvironment(context.flags));
|
|
33601
|
+
}
|
|
33602
|
+
function databaseName(environment) {
|
|
33603
|
+
return environment === null ? "the project database" : `the ${environment} environment's database`;
|
|
33604
|
+
}
|
|
33605
|
+
function enableBackendCommand(environment) {
|
|
33606
|
+
return environment === null ? "naro backend enable" : `naro environments provision ${environment}`;
|
|
33607
|
+
}
|
|
33608
|
+
function envFlag(environment) {
|
|
33609
|
+
return environment === null ? "" : ` --env ${environment}`;
|
|
33610
|
+
}
|
|
33611
|
+
function forEnvironment(text3, environment) {
|
|
33612
|
+
return environmentGuidance(text3, environment);
|
|
33613
|
+
}
|
|
33614
|
+
|
|
33615
|
+
// src/prompt.ts
|
|
33616
|
+
import { createInterface as createInterface2 } from "readline";
|
|
33617
|
+
import { Writable } from "stream";
|
|
33618
|
+
function confirmHint(defaultYes) {
|
|
33619
|
+
return defaultYes ? "(Y/n)" : "(y/N)";
|
|
33620
|
+
}
|
|
33621
|
+
function readConfirmAnswer(answer, defaultYes) {
|
|
33622
|
+
const normalized2 = answer.trim().toLowerCase();
|
|
33623
|
+
if (normalized2 === "") return defaultYes;
|
|
33624
|
+
if (normalized2 === "y" || normalized2 === "yes") return true;
|
|
33625
|
+
if (!defaultYes) return false;
|
|
33626
|
+
if (normalized2 === "n" || normalized2 === "no") return false;
|
|
33627
|
+
return null;
|
|
33628
|
+
}
|
|
33629
|
+
function isInteractive() {
|
|
33630
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
33631
|
+
}
|
|
33632
|
+
async function readStdin() {
|
|
33633
|
+
let data = "";
|
|
33634
|
+
for await (const chunk of process.stdin) data += String(chunk);
|
|
33635
|
+
return data.replace(/\r?\n$/, "");
|
|
33636
|
+
}
|
|
33637
|
+
var TtyPrompter = class {
|
|
33638
|
+
async ask(question) {
|
|
33639
|
+
const rl = createInterface2({
|
|
33640
|
+
input: process.stdin,
|
|
33641
|
+
output: process.stdout
|
|
33642
|
+
});
|
|
33643
|
+
try {
|
|
33644
|
+
return await new Promise(
|
|
33645
|
+
(resolve15) => rl.question(question, resolve15)
|
|
33646
|
+
);
|
|
33647
|
+
} finally {
|
|
33648
|
+
rl.close();
|
|
33649
|
+
}
|
|
33650
|
+
}
|
|
33651
|
+
async askHidden(question) {
|
|
33652
|
+
let muted = false;
|
|
33653
|
+
const output = new Writable({
|
|
33654
|
+
write(chunk, _enc, done) {
|
|
33655
|
+
if (!muted) process.stdout.write(chunk);
|
|
33656
|
+
done();
|
|
33657
|
+
}
|
|
33658
|
+
});
|
|
33659
|
+
const rl = createInterface2({
|
|
33660
|
+
input: process.stdin,
|
|
33661
|
+
output,
|
|
33662
|
+
terminal: true
|
|
33663
|
+
});
|
|
33664
|
+
try {
|
|
33665
|
+
const answer = new Promise(
|
|
33666
|
+
(resolve15) => rl.question(question, resolve15)
|
|
33667
|
+
);
|
|
33668
|
+
muted = true;
|
|
33669
|
+
const value = await answer;
|
|
33670
|
+
process.stdout.write("\n");
|
|
33671
|
+
return value;
|
|
33672
|
+
} finally {
|
|
33673
|
+
rl.close();
|
|
33674
|
+
}
|
|
33675
|
+
}
|
|
33676
|
+
async confirm(question, options = {}) {
|
|
33677
|
+
const defaultYes = options.defaultYes ?? false;
|
|
33678
|
+
for (; ; ) {
|
|
33679
|
+
const read = readConfirmAnswer(
|
|
33680
|
+
await this.ask(`${question} ${confirmHint(defaultYes)} `),
|
|
33681
|
+
defaultYes
|
|
33682
|
+
);
|
|
33683
|
+
if (read !== null) return read;
|
|
33684
|
+
}
|
|
33685
|
+
}
|
|
33686
|
+
async select(question, options) {
|
|
33687
|
+
process.stdout.write(`${question}
|
|
33688
|
+
`);
|
|
33689
|
+
options.forEach((option, index) => {
|
|
33690
|
+
process.stdout.write(` ${String(index + 1)}. ${option}
|
|
33691
|
+
`);
|
|
33692
|
+
});
|
|
33693
|
+
for (; ; ) {
|
|
33694
|
+
const answer = Number.parseInt(
|
|
33695
|
+
(await this.ask(`Choose 1-${String(options.length)}: `)).trim(),
|
|
33696
|
+
10
|
|
33697
|
+
);
|
|
33698
|
+
if (Number.isInteger(answer) && answer >= 1 && answer <= options.length)
|
|
33699
|
+
return answer - 1;
|
|
33700
|
+
}
|
|
33701
|
+
}
|
|
33702
|
+
};
|
|
33703
|
+
var NonInteractivePrompter = class {
|
|
33704
|
+
constructor(assumeYes) {
|
|
33705
|
+
this.assumeYes = assumeYes;
|
|
33706
|
+
}
|
|
33707
|
+
refuse(question) {
|
|
33708
|
+
throw new NaroError(
|
|
33709
|
+
"VALIDATION_FAILED",
|
|
33710
|
+
`Interactive input required (${question.trim()}). Pass the value via flags/stdin or run without --json in a terminal.`
|
|
33711
|
+
);
|
|
33712
|
+
}
|
|
33713
|
+
async ask(question) {
|
|
33714
|
+
this.refuse(question);
|
|
33715
|
+
}
|
|
33716
|
+
async askHidden(question) {
|
|
33717
|
+
this.refuse(question);
|
|
33718
|
+
}
|
|
33719
|
+
async confirm() {
|
|
33720
|
+
return this.assumeYes;
|
|
33721
|
+
}
|
|
33722
|
+
async select(question) {
|
|
33723
|
+
this.refuse(question);
|
|
33724
|
+
}
|
|
33725
|
+
};
|
|
33726
|
+
function createPrompter(options) {
|
|
33727
|
+
if (options.yes || options.json || !isInteractive())
|
|
33728
|
+
return new NonInteractivePrompter(options.yes);
|
|
33729
|
+
return new TtyPrompter();
|
|
33730
|
+
}
|
|
33731
|
+
|
|
33732
|
+
// src/commands/confirm.ts
|
|
33733
|
+
function cannotAsk(context) {
|
|
33734
|
+
return context.flags.json || !isInteractive();
|
|
33735
|
+
}
|
|
33736
|
+
async function confirmOrAbort(context, question) {
|
|
33737
|
+
if (context.flags.yes) return;
|
|
33738
|
+
if (cannotAsk(context)) {
|
|
33739
|
+
throw new NaroError(
|
|
33740
|
+
"CONFIRMATION_REQUIRED",
|
|
33741
|
+
`${question} Re-run with --yes to confirm.`
|
|
33742
|
+
);
|
|
33743
|
+
}
|
|
33744
|
+
const confirmed = await context.prompter.confirm(question);
|
|
33745
|
+
if (!confirmed) throw new NaroError("CANCELED", "Canceled.");
|
|
33746
|
+
}
|
|
33747
|
+
|
|
33748
|
+
// src/commands/migration.ts
|
|
33749
|
+
var processStdin = {
|
|
33750
|
+
stdinIsTty: () => process.stdin.isTTY === true,
|
|
33751
|
+
readStdin
|
|
33752
|
+
};
|
|
33753
|
+
function migrationsDir(context) {
|
|
33754
|
+
return resolveMigrationsDir({
|
|
33755
|
+
baseDir: context.cwd,
|
|
33756
|
+
rootDirectory: context.flags.root,
|
|
33757
|
+
dir: context.flags.dir
|
|
33758
|
+
});
|
|
33759
|
+
}
|
|
33760
|
+
function relativeToCwd(context, path) {
|
|
33761
|
+
const rel = relative10(context.cwd, path);
|
|
33762
|
+
return rel === "" ? "." : rel;
|
|
33763
|
+
}
|
|
33764
|
+
async function appliedMigrations(client, projectId) {
|
|
33765
|
+
try {
|
|
33766
|
+
return await client.db.migrations(projectId);
|
|
33767
|
+
} catch (error51) {
|
|
33768
|
+
if (isNaroError(error51) && error51.code === "DATABASE_NOT_READY") return [];
|
|
33769
|
+
throw error51;
|
|
33770
|
+
}
|
|
33771
|
+
}
|
|
33772
|
+
var UNPRINTABLE2 = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
33773
|
+
function printable2(text3) {
|
|
33774
|
+
return text3.replace(UNPRINTABLE2, "?");
|
|
33775
|
+
}
|
|
33776
|
+
function isPrintable(text3) {
|
|
33777
|
+
return printable2(text3) === text3;
|
|
33778
|
+
}
|
|
33779
|
+
var UNSHOWN_WARNING_LINE = "(a line of this warning has characters the terminal cannot show; run with --json to see it exactly)";
|
|
33780
|
+
function warningLines(message) {
|
|
33781
|
+
return message.split("\n").map(
|
|
33782
|
+
(line, index) => index === 0 ? `\u26A0 ${printable2(line)}` : ` ${isPrintable(line) ? line : UNSHOWN_WARNING_LINE}`
|
|
33783
|
+
);
|
|
33784
|
+
}
|
|
33785
|
+
function policyProblems(dir, names) {
|
|
33786
|
+
return names.flatMap((name) => {
|
|
33787
|
+
const split2 = splitStatements(readFileSync10(join31(dir, name), "utf8"));
|
|
33788
|
+
if (!split2.ok) return [];
|
|
33789
|
+
const planned = planPolicyMigration(
|
|
33790
|
+
split2.statements.map((statement2) => statement2.text)
|
|
33791
|
+
);
|
|
33792
|
+
return planned.ok ? [] : [{ migration: name, message: planned.message }];
|
|
33793
|
+
});
|
|
33794
|
+
}
|
|
33795
|
+
function warningLine(warning) {
|
|
33796
|
+
if (warning === "out-of-order")
|
|
33797
|
+
return " \u26A0 older than an applied migration \u2014 db push will still apply it";
|
|
33798
|
+
if (warning === "ledger-name-missing-extension")
|
|
33799
|
+
return " \u26A0 recorded without .sql in the ledger";
|
|
33800
|
+
return null;
|
|
33801
|
+
}
|
|
33802
|
+
async function runMigrationNew(context, deps = processStdin) {
|
|
33803
|
+
const [name] = context.positionalOrThrow(
|
|
33804
|
+
1,
|
|
33805
|
+
"Usage: naro migration new <name>"
|
|
33806
|
+
);
|
|
33807
|
+
const dir = migrationsDir(context);
|
|
33808
|
+
const fileName = nextMigrationFileName(
|
|
33809
|
+
listMigrationFiles(dir),
|
|
33810
|
+
name
|
|
33811
|
+
);
|
|
33812
|
+
mkdirSync3(dir, { recursive: true });
|
|
33813
|
+
const piped = !deps.stdinIsTty() ? await deps.readStdin() : "";
|
|
33814
|
+
const fromStdin = piped.length > 0;
|
|
33815
|
+
const body = fromStdin ? piped.endsWith("\n") ? piped : `${piped}
|
|
33816
|
+
` : `-- Migration: ${fileName}
|
|
33817
|
+
`;
|
|
33818
|
+
const path = join31(dir, fileName);
|
|
33819
|
+
writeFileSync3(path, body, { flag: "wx" });
|
|
33820
|
+
const relPath = relativeToCwd(context, path);
|
|
33821
|
+
context.output.result({ file: fileName, path: relPath, fromStdin }, [
|
|
33822
|
+
`\u2705 Created ${relPath}`
|
|
33823
|
+
]);
|
|
33824
|
+
}
|
|
33825
|
+
async function runMigrationList(context) {
|
|
33826
|
+
const client = await dataClient(context);
|
|
33827
|
+
const projectId = await context.resolveProjectId();
|
|
33828
|
+
const dir = migrationsDir(context);
|
|
33829
|
+
const local = listMigrationFiles(dir);
|
|
33830
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33831
|
+
const migrations = compareMigrations(local, ledger);
|
|
33832
|
+
const relDir = relativeToCwd(context, dir);
|
|
33833
|
+
const lines = migrations.length === 0 ? ["No migrations yet. Create one with `naro migration new <name>`."] : [
|
|
33834
|
+
`Migrations in ${relDir} (${String(migrations.length)}):`,
|
|
33835
|
+
...migrations.flatMap((row) => {
|
|
33836
|
+
const line = ` ${row.status.padEnd(12)}${printable2(row.name)}${row.appliedAt ? ` ${printable2(row.appliedAt)}` : ""}`;
|
|
33837
|
+
const warning = warningLine(row.warning);
|
|
33838
|
+
return warning ? [line, warning] : [line];
|
|
33839
|
+
})
|
|
33840
|
+
];
|
|
33841
|
+
context.output.result({ projectId, dir: relDir, migrations }, lines);
|
|
33842
|
+
}
|
|
33843
|
+
async function applyMigrationFiles(context, client, projectId, dir, names) {
|
|
33844
|
+
const applied = [];
|
|
33845
|
+
const warnings = [];
|
|
33846
|
+
for (const name of names) {
|
|
33847
|
+
const sql = readFileSync10(join31(dir, name), "utf8");
|
|
33848
|
+
let answer;
|
|
33849
|
+
try {
|
|
33850
|
+
answer = await client.db.applyMigration(projectId, { name, sql });
|
|
33851
|
+
} catch (error51) {
|
|
33852
|
+
if (!isNaroError(error51)) throw error51;
|
|
33853
|
+
for (const message of serverWarnings(error51.details))
|
|
33854
|
+
for (const line of warningLines(message)) context.output.warn(line);
|
|
33855
|
+
const shown = context.output.json ? name : printable2(name);
|
|
33856
|
+
throw new NaroError(error51.code, `${shown}: ${error51.message}`, {
|
|
33857
|
+
...error51.status === void 0 ? {} : { status: error51.status },
|
|
33858
|
+
details: {
|
|
33859
|
+
...error51.details,
|
|
33860
|
+
migration: name,
|
|
33861
|
+
applied,
|
|
33862
|
+
appliedWarnings: warnings
|
|
33863
|
+
},
|
|
33864
|
+
cause: error51
|
|
33865
|
+
});
|
|
33866
|
+
}
|
|
33867
|
+
context.output.log(`\u2713 ${printable2(name)}`);
|
|
33868
|
+
applied.push(name);
|
|
33869
|
+
for (const message of answer.warnings) {
|
|
33870
|
+
warnings.push({ migration: name, message });
|
|
33871
|
+
for (const line of warningLines(message)) context.output.warn(line);
|
|
33872
|
+
}
|
|
33873
|
+
}
|
|
33874
|
+
return { applied, warnings };
|
|
33875
|
+
}
|
|
33876
|
+
function serverWarnings(details) {
|
|
33877
|
+
const warnings = details?.warnings;
|
|
33878
|
+
return Array.isArray(warnings) ? warnings.filter(
|
|
33879
|
+
(warning) => typeof warning === "string"
|
|
33880
|
+
) : [];
|
|
33881
|
+
}
|
|
33882
|
+
var MIGRATION_NAME_RE = /^[A-Za-z0-9_.-]{1,200}\.sql$/;
|
|
33883
|
+
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)";
|
|
33884
|
+
var LEDGER_INSERT = "INSERT INTO d1_migrations (name) SELECT ? WHERE NOT EXISTS (SELECT 1 FROM d1_migrations WHERE name = ?)";
|
|
33885
|
+
var LEDGER_DELETE = "DELETE FROM d1_migrations WHERE name IN (?, ?)";
|
|
33886
|
+
function withoutSuffix(key) {
|
|
33887
|
+
return key.slice(0, -".sql".length);
|
|
33888
|
+
}
|
|
33889
|
+
async function runMigrationRepair(context) {
|
|
33890
|
+
const [name] = context.positionalOrThrow(
|
|
33891
|
+
1,
|
|
33892
|
+
"Usage: naro migration repair <name> --status applied|reverted"
|
|
33893
|
+
);
|
|
33894
|
+
const key = name;
|
|
33895
|
+
if (!MIGRATION_NAME_RE.test(key))
|
|
33896
|
+
throw new NaroError(
|
|
33897
|
+
"VALIDATION_FAILED",
|
|
33898
|
+
"name must end with .sql and contain only letters, digits, _ . -"
|
|
33899
|
+
);
|
|
33900
|
+
const status = context.flags.status;
|
|
33901
|
+
if (status !== "applied" && status !== "reverted")
|
|
33902
|
+
throw new NaroError(
|
|
33903
|
+
"VALIDATION_FAILED",
|
|
33904
|
+
"Pass --status applied or --status reverted."
|
|
33905
|
+
);
|
|
33906
|
+
const client = await dataClient(context);
|
|
33907
|
+
const projectId = await context.resolveProjectId();
|
|
33908
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33909
|
+
const recorded = ledger.some(
|
|
33910
|
+
(row) => row.name === key || row.name === withoutSuffix(key)
|
|
33911
|
+
);
|
|
33912
|
+
const nothingToDo = status === "applied" ? `${key} was already applied; nothing to do.` : `${key} was not in the ledger; nothing to do.`;
|
|
33913
|
+
if (status === "applied" ? recorded : !recorded) {
|
|
33914
|
+
context.output.warn(nothingToDo);
|
|
33915
|
+
context.output.result({ name: key, status, changed: false });
|
|
33916
|
+
return;
|
|
33917
|
+
}
|
|
33918
|
+
await confirmOrAbort(
|
|
33919
|
+
context,
|
|
33920
|
+
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.`
|
|
33921
|
+
);
|
|
33922
|
+
if (status === "applied" && ledger.length === 0)
|
|
33923
|
+
await client.db.query(projectId, { sql: LEDGER_DDL });
|
|
33924
|
+
const answer = await client.db.query(projectId, {
|
|
33925
|
+
sql: status === "applied" ? LEDGER_INSERT : LEDGER_DELETE,
|
|
33926
|
+
params: status === "applied" ? [key, key] : [key, withoutSuffix(key)]
|
|
33927
|
+
});
|
|
33928
|
+
const changed = (answer.statements[0]?.meta.changes ?? 0) > 0;
|
|
33929
|
+
if (!changed) {
|
|
33930
|
+
context.output.warn(nothingToDo);
|
|
33931
|
+
context.output.result({ name: key, status, changed: false });
|
|
33932
|
+
return;
|
|
33933
|
+
}
|
|
33934
|
+
context.output.result({ name: key, status, changed: true }, [
|
|
33935
|
+
status === "applied" ? `\u2713 Marked ${key} as applied.` : `\u2713 Removed ${key} from the ledger.`
|
|
33936
|
+
]);
|
|
33937
|
+
}
|
|
33938
|
+
async function runMigrationUp(context) {
|
|
33939
|
+
const client = await dataClient(context);
|
|
33940
|
+
const projectId = await context.resolveProjectId();
|
|
33941
|
+
const dir = migrationsDir(context);
|
|
33942
|
+
const local = listMigrationFiles(dir);
|
|
33943
|
+
const ledger = await appliedMigrations(client, projectId);
|
|
33944
|
+
const pending = compareMigrations(local, ledger).filter((row) => row.status === "pending").map((row) => row.name);
|
|
33945
|
+
if (pending.length === 0) {
|
|
33946
|
+
context.output.result({ projectId, applied: [], pending: [] }, [
|
|
33947
|
+
"No pending migrations."
|
|
33948
|
+
]);
|
|
33949
|
+
return;
|
|
33950
|
+
}
|
|
33951
|
+
if (context.flags.dryRun) {
|
|
33952
|
+
const problems2 = policyProblems(dir, pending);
|
|
33953
|
+
context.output.result({ projectId, dryRun: true, pending, problems: problems2 }, [
|
|
33954
|
+
`Pending migrations (${String(pending.length)}):`,
|
|
33955
|
+
...pending.map((name) => ` ${printable2(name)}`),
|
|
33956
|
+
...problems2.map(
|
|
33957
|
+
(problem) => `${printable2(problem.migration)}: ${printable2(problem.message)}`
|
|
33958
|
+
)
|
|
33959
|
+
]);
|
|
33960
|
+
if (problems2.length > 0) process.exitCode = 1;
|
|
33961
|
+
return;
|
|
33962
|
+
}
|
|
33963
|
+
context.output.log(`Pending migrations (${String(pending.length)}):`);
|
|
33964
|
+
for (const name of pending) context.output.log(` ${printable2(name)}`);
|
|
33965
|
+
await confirmOrAbort(
|
|
33966
|
+
context,
|
|
33967
|
+
`Apply ${String(pending.length)} migration(s) to ${databaseName(dataEnvironment(context.flags))}? Each file is applied all-or-nothing; a failure stops the run.`
|
|
33968
|
+
);
|
|
33969
|
+
const { applied, warnings } = await applyMigrationFiles(
|
|
33970
|
+
context,
|
|
33971
|
+
client,
|
|
33972
|
+
projectId,
|
|
33973
|
+
dir,
|
|
33974
|
+
pending
|
|
33975
|
+
);
|
|
33976
|
+
context.output.result({ projectId, applied, pending: [], warnings }, [
|
|
33977
|
+
`\u2705 Applied ${String(applied.length)} migration(s).`
|
|
33978
|
+
]);
|
|
33979
|
+
}
|
|
33980
|
+
|
|
33981
|
+
// src/functions/retry.ts
|
|
33982
|
+
var FUNCTION_RETRIES = 3;
|
|
33983
|
+
var FUNCTION_BUSY_WAIT_MS = 5e3;
|
|
33984
|
+
var FUNCTION_RATE_LIMIT_DEFAULT_SECONDS = 60;
|
|
33985
|
+
var FUNCTION_RATE_LIMIT_MAX_SECONDS = 300;
|
|
33986
|
+
function defaultSleep(ms) {
|
|
33987
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
33988
|
+
}
|
|
33989
|
+
function retryAfterSeconds(error51) {
|
|
33990
|
+
const value = error51.details?.retryAfter;
|
|
33991
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(Math.ceil(value), FUNCTION_RATE_LIMIT_MAX_SECONDS) : FUNCTION_RATE_LIMIT_DEFAULT_SECONDS;
|
|
33992
|
+
}
|
|
33993
|
+
async function withFunctionRetries(op, name, io) {
|
|
33994
|
+
const rateRetries = io.rateLimitRetries ?? FUNCTION_RETRIES;
|
|
33995
|
+
const busyRetries = io.busyRetries ?? FUNCTION_RETRIES;
|
|
33996
|
+
let rate = 0;
|
|
33997
|
+
let busy = 0;
|
|
33998
|
+
for (; ; ) {
|
|
33999
|
+
try {
|
|
34000
|
+
return await op();
|
|
34001
|
+
} catch (error51) {
|
|
34002
|
+
if (!isNaroError(error51)) throw error51;
|
|
34003
|
+
if (error51.code === "FUNCTIONS_RATE_LIMITED" && rate < rateRetries) {
|
|
34004
|
+
rate += 1;
|
|
34005
|
+
const seconds = retryAfterSeconds(error51);
|
|
34006
|
+
io.log(
|
|
34007
|
+
`\u23F3 Cloudflare is rate limiting function uploads; trying ${name} again in ${String(seconds)} seconds (${String(rate)}/${String(rateRetries)}).`
|
|
34008
|
+
);
|
|
34009
|
+
await io.sleep(seconds * 1e3);
|
|
34010
|
+
continue;
|
|
34011
|
+
}
|
|
34012
|
+
if (error51.code === "FUNCTION_BUSY" && busy < busyRetries) {
|
|
34013
|
+
busy += 1;
|
|
34014
|
+
io.log(
|
|
34015
|
+
`\u23F3 ${name} is being changed right now; trying again in ${String(FUNCTION_BUSY_WAIT_MS / 1e3)} seconds (${String(busy)}/${String(busyRetries)}).`
|
|
34016
|
+
);
|
|
34017
|
+
await io.sleep(FUNCTION_BUSY_WAIT_MS);
|
|
34018
|
+
continue;
|
|
34019
|
+
}
|
|
34020
|
+
throw error51;
|
|
34021
|
+
}
|
|
34022
|
+
}
|
|
34023
|
+
}
|
|
34024
|
+
|
|
34025
|
+
// src/functions/deploy.ts
|
|
34026
|
+
var PER_FUNCTION_CODES = /* @__PURE__ */ new Set([
|
|
34027
|
+
"VALIDATION_FAILED",
|
|
34028
|
+
"FUNCTION_BUNDLE_FAILED",
|
|
34029
|
+
"FUNCTION_BOOT_FAILED",
|
|
34030
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
34031
|
+
"BUNDLE_TOO_LARGE",
|
|
34032
|
+
"BUNDLE_MISSING",
|
|
34033
|
+
"BUNDLE_MISMATCH",
|
|
34034
|
+
"FUNCTION_LIMIT",
|
|
34035
|
+
"FUNCTION_BUSY",
|
|
34036
|
+
"FUNCTION_NOT_FOUND",
|
|
34037
|
+
"VERSION_NOT_FOUND",
|
|
34038
|
+
"VERSION_NOT_DEPLOYABLE",
|
|
34039
|
+
"FUNCTION_DEPLOY_FAILED",
|
|
34040
|
+
"FUNCTION_DEPLOY_INCOMPLETE",
|
|
34041
|
+
"FUNCTIONS_RATE_LIMITED"
|
|
34042
|
+
]);
|
|
34043
|
+
function assertFunctionsNode(version3 = process.versions.node) {
|
|
34044
|
+
const major = Number(version3.split(".")[0]);
|
|
34045
|
+
if (!(major >= 22))
|
|
34046
|
+
throw new NaroError(
|
|
34047
|
+
"NODE_VERSION_UNSUPPORTED",
|
|
34048
|
+
`naro functions needs Node.js 22 or later (found ${version3}).`
|
|
34049
|
+
);
|
|
34050
|
+
}
|
|
34051
|
+
function asNaroError(error51) {
|
|
34052
|
+
return error51 instanceof FunctionBundleError ? new NaroError(error51.code, error51.message) : error51;
|
|
34053
|
+
}
|
|
34054
|
+
var UPLOAD_TIMEOUT_DEFAULT_MS = 15 * 6e4;
|
|
34055
|
+
var UPLOAD_TIMEOUT_MIN_MS = 6e4;
|
|
34056
|
+
var UPLOAD_TIMEOUT_SLACK_MS = 3e4;
|
|
34057
|
+
function uploadTimeoutMs(upload, now = Date.now()) {
|
|
34058
|
+
const expires = typeof upload.expiresAt === "string" ? Date.parse(upload.expiresAt) : NaN;
|
|
34059
|
+
if (Number.isNaN(expires)) return UPLOAD_TIMEOUT_DEFAULT_MS;
|
|
34060
|
+
return Math.max(expires - now, UPLOAD_TIMEOUT_MIN_MS) + UPLOAD_TIMEOUT_SLACK_MS;
|
|
34061
|
+
}
|
|
34062
|
+
var SECRETS_LIST_TIMEOUT_MS = 45e3;
|
|
34063
|
+
var FUNCTION_CALL_TIMEOUT_MS = 12e4;
|
|
34064
|
+
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.`;
|
|
34065
|
+
function budgetOf(options) {
|
|
34066
|
+
const { deadlineMs } = options;
|
|
34067
|
+
if (deadlineMs === void 0) return null;
|
|
34068
|
+
const now = options.now ?? Date.now;
|
|
34069
|
+
const end = now() + deadlineMs;
|
|
34070
|
+
return {
|
|
34071
|
+
left(name, cap) {
|
|
34072
|
+
const left = end - now();
|
|
34073
|
+
if (left <= 0)
|
|
34074
|
+
throw new NaroError("TIMEOUT", deadlineReached(name, deadlineMs));
|
|
34075
|
+
return Math.min(cap, left);
|
|
34076
|
+
}
|
|
34077
|
+
};
|
|
34078
|
+
}
|
|
34079
|
+
var bootSeconds = (ms) => Math.max(1e3, Math.floor(ms / 1e3) * 1e3);
|
|
34080
|
+
async function putBundle(doFetch, name, upload, contents, timeoutMs) {
|
|
34081
|
+
const headers = Object.fromEntries(
|
|
34082
|
+
Object.entries(upload.headers).filter(
|
|
34083
|
+
([header]) => header.toLowerCase() !== "content-length"
|
|
34084
|
+
)
|
|
34085
|
+
);
|
|
34086
|
+
const again = `Run naro functions deploy ${name} again.`;
|
|
34087
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
34088
|
+
let response;
|
|
34089
|
+
try {
|
|
34090
|
+
response = await doFetch(upload.url, {
|
|
34091
|
+
method: upload.method,
|
|
34092
|
+
headers,
|
|
34093
|
+
body: contents,
|
|
34094
|
+
signal
|
|
34095
|
+
});
|
|
34096
|
+
} catch (cause) {
|
|
34097
|
+
const timedOut = signal.aborted || cause instanceof Error && cause.name === "TimeoutError";
|
|
34098
|
+
throw new NaroError(
|
|
34099
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
34100
|
+
`Uploading the bundle of ${name} failed (${timedOut ? "timed out" : "network error"}). ${again}`,
|
|
34101
|
+
{ cause }
|
|
34102
|
+
);
|
|
34103
|
+
}
|
|
34104
|
+
if (!response.ok)
|
|
34105
|
+
throw new NaroError(
|
|
34106
|
+
"FUNCTION_UPLOAD_FAILED",
|
|
34107
|
+
`Uploading the bundle of ${name} failed (${String(response.status)}). ${again}`,
|
|
34108
|
+
{ status: response.status }
|
|
34109
|
+
);
|
|
34110
|
+
}
|
|
34111
|
+
async function projectSecretNames(client, projectId, timeoutMs) {
|
|
34112
|
+
try {
|
|
34113
|
+
const secrets = timeoutMs === void 0 ? await client.functions.secrets(projectId) : await client.functions.secrets(projectId, { timeoutMs });
|
|
34114
|
+
return secrets.map((secret) => secret.name);
|
|
34115
|
+
} catch {
|
|
34116
|
+
return [];
|
|
34117
|
+
}
|
|
34118
|
+
}
|
|
34119
|
+
async function deployOne(options, folder, name, importMap, secretNames, io, budget) {
|
|
34120
|
+
const steps = options.steps ?? { bundle: bundleFunction, boot: bootCheck };
|
|
34121
|
+
const { client, projectId } = options;
|
|
34122
|
+
const call = () => budget ? { timeoutMs: budget.left(name, FUNCTION_CALL_TIMEOUT_MS) } : null;
|
|
34123
|
+
io.log(`\u23F3 Deploying ${name}\u2026`);
|
|
34124
|
+
try {
|
|
34125
|
+
const source = readFunctionSource(folder, name);
|
|
34126
|
+
const bundle = await steps.bundle(source, folder, { importMap });
|
|
34127
|
+
for (const warning of bundle.warnings) io.log(warning);
|
|
34128
|
+
if (!options.skipCheck)
|
|
34129
|
+
await steps.boot(
|
|
34130
|
+
name,
|
|
34131
|
+
bundle.code,
|
|
34132
|
+
budget ? {
|
|
34133
|
+
secretNames,
|
|
34134
|
+
timeoutMs: bootSeconds(budget.left(name, BOOT_TIMEOUT_MS))
|
|
34135
|
+
} : { secretNames }
|
|
34136
|
+
);
|
|
34137
|
+
const verifyJwt = options.verifyJwt ?? source.verifyJwt ?? true;
|
|
34138
|
+
const request2 = {
|
|
34139
|
+
size: bundle.bytes,
|
|
34140
|
+
sha256: bundle.sha256,
|
|
34141
|
+
verifyJwt,
|
|
34142
|
+
entrypoint: source.entrypoint,
|
|
34143
|
+
...options.cliVersion ? { cliVersion: options.cliVersion } : {}
|
|
34144
|
+
};
|
|
34145
|
+
const created = await withFunctionRetries(
|
|
34146
|
+
() => {
|
|
34147
|
+
const limit = call();
|
|
34148
|
+
return limit ? client.functions.createVersion(projectId, name, request2, limit) : client.functions.createVersion(projectId, name, request2);
|
|
34149
|
+
},
|
|
34150
|
+
name,
|
|
34151
|
+
io
|
|
34152
|
+
);
|
|
34153
|
+
const urlLife = uploadTimeoutMs(created.upload);
|
|
34154
|
+
await putBundle(
|
|
34155
|
+
options.fetch ?? fetch,
|
|
34156
|
+
name,
|
|
34157
|
+
created.upload,
|
|
34158
|
+
bundle.contents,
|
|
34159
|
+
budget ? budget.left(name, urlLife) : urlLife
|
|
34160
|
+
);
|
|
34161
|
+
const live = await withFunctionRetries(
|
|
34162
|
+
() => {
|
|
34163
|
+
const limit = call();
|
|
34164
|
+
return limit ? client.functions.deploy(projectId, name, created.version.id, limit) : client.functions.deploy(projectId, name, created.version.id);
|
|
34165
|
+
},
|
|
34166
|
+
name,
|
|
34167
|
+
io
|
|
34168
|
+
);
|
|
34169
|
+
io.log(
|
|
34170
|
+
`\u2705 Deployed ${name} (v${String(live.version ?? created.version.number)}) \u2014 ${live.url}`
|
|
34171
|
+
);
|
|
34172
|
+
return {
|
|
34173
|
+
name,
|
|
34174
|
+
version: live.version,
|
|
34175
|
+
url: live.url,
|
|
34176
|
+
verifyJwt: live.verifyJwt
|
|
34177
|
+
};
|
|
34178
|
+
} catch (error51) {
|
|
34179
|
+
throw asNaroError(error51);
|
|
34180
|
+
}
|
|
34181
|
+
}
|
|
34182
|
+
function readFlagImportMap(options, folder) {
|
|
34183
|
+
if (options.importMap === void 0) return void 0;
|
|
34184
|
+
const file2 = resolve13(options.cwd, options.importMap);
|
|
34185
|
+
try {
|
|
34186
|
+
return readImportMapFile(
|
|
34187
|
+
file2,
|
|
34188
|
+
relative11(folder.root, file2).split(sep10).join("/")
|
|
34189
|
+
);
|
|
34190
|
+
} catch (error51) {
|
|
34191
|
+
throw asNaroError(error51);
|
|
34192
|
+
}
|
|
34193
|
+
}
|
|
34194
|
+
function withProgress(error51, deployed, failures) {
|
|
34195
|
+
return new NaroError(error51.code, error51.message, {
|
|
34196
|
+
...error51.status === void 0 ? {} : { status: error51.status },
|
|
34197
|
+
details: { ...error51.details, deployed, failures },
|
|
34198
|
+
cause: error51
|
|
34199
|
+
});
|
|
34200
|
+
}
|
|
34201
|
+
async function deployFunctions(options) {
|
|
34202
|
+
assertFunctionsNode(options.nodeVersion);
|
|
34203
|
+
const budget = budgetOf(options);
|
|
34204
|
+
const io = {
|
|
34205
|
+
sleep: options.sleep ?? defaultSleep,
|
|
34206
|
+
log: options.log ?? (() => {
|
|
34207
|
+
}),
|
|
34208
|
+
rateLimitRetries: options.rateLimitRetries,
|
|
34209
|
+
busyRetries: options.busyRetries
|
|
34210
|
+
};
|
|
34211
|
+
const folder = functionsFolder({
|
|
34212
|
+
cwd: options.cwd,
|
|
34213
|
+
root: options.root,
|
|
34214
|
+
dir: options.dir
|
|
34215
|
+
});
|
|
34216
|
+
let names;
|
|
34217
|
+
try {
|
|
34218
|
+
names = options.names && options.names.length > 0 ? [...new Set(options.names)] : listFunctionNames(folder);
|
|
34219
|
+
} catch (error51) {
|
|
34220
|
+
throw asNaroError(error51);
|
|
34221
|
+
}
|
|
34222
|
+
const importMap = readFlagImportMap(options, folder);
|
|
34223
|
+
const warning = configTomlWarning(folder);
|
|
34224
|
+
if (warning !== null) io.log(warning);
|
|
34225
|
+
const [only] = names;
|
|
34226
|
+
const secretNames = options.skipCheck ? [] : await projectSecretNames(
|
|
34227
|
+
options.client,
|
|
34228
|
+
options.projectId,
|
|
34229
|
+
budget?.left(only ?? "", SECRETS_LIST_TIMEOUT_MS)
|
|
34230
|
+
);
|
|
34231
|
+
if (names.length === 1 && only !== void 0)
|
|
34232
|
+
return {
|
|
34233
|
+
deployed: [
|
|
34234
|
+
await deployOne(
|
|
34235
|
+
options,
|
|
34236
|
+
folder,
|
|
34237
|
+
only,
|
|
34238
|
+
importMap,
|
|
34239
|
+
secretNames,
|
|
34240
|
+
io,
|
|
34241
|
+
budget
|
|
34242
|
+
)
|
|
34243
|
+
]
|
|
34244
|
+
};
|
|
34245
|
+
const deployed = [];
|
|
34246
|
+
const failures = [];
|
|
34247
|
+
for (const name of names) {
|
|
34248
|
+
try {
|
|
34249
|
+
deployed.push(
|
|
34250
|
+
await deployOne(
|
|
34251
|
+
options,
|
|
34252
|
+
folder,
|
|
34253
|
+
name,
|
|
34254
|
+
importMap,
|
|
34255
|
+
secretNames,
|
|
34256
|
+
io,
|
|
34257
|
+
budget
|
|
34258
|
+
)
|
|
34259
|
+
);
|
|
34260
|
+
} catch (error51) {
|
|
34261
|
+
if (!isNaroError(error51)) throw error51;
|
|
34262
|
+
if (!PER_FUNCTION_CODES.has(error51.code))
|
|
34263
|
+
throw withProgress(error51, deployed, failures);
|
|
34264
|
+
failures.push({ name, code: error51.code, message: error51.message });
|
|
34265
|
+
io.log(`\u2717 ${name}: ${printable2(error51.message)}`);
|
|
34266
|
+
}
|
|
34267
|
+
}
|
|
34268
|
+
if (failures.length > 0)
|
|
34269
|
+
throw new NaroError(
|
|
34270
|
+
"FUNCTIONS_DEPLOY_PARTIAL",
|
|
34271
|
+
`Deployed ${String(deployed.length)} of ${String(names.length)} functions. Failed: ${failures.map((failure2) => `${failure2.name} (${failure2.code})`).join(", ")}.`,
|
|
34272
|
+
{ details: { deployed, failures } }
|
|
34273
|
+
);
|
|
34274
|
+
return { deployed };
|
|
34275
|
+
}
|
|
34276
|
+
|
|
32416
34277
|
// src/deploy.ts
|
|
32417
|
-
function parseDotenv2(
|
|
32418
|
-
return parseDotenv(
|
|
34278
|
+
function parseDotenv2(text3) {
|
|
34279
|
+
return parseDotenv(text3);
|
|
32419
34280
|
}
|
|
32420
34281
|
function readProjectEnv2(dir) {
|
|
32421
34282
|
return readProjectEnv(dir);
|
|
@@ -32438,7 +34299,7 @@ function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUr
|
|
|
32438
34299
|
} catch (error51) {
|
|
32439
34300
|
lastError = error51;
|
|
32440
34301
|
await new Promise(
|
|
32441
|
-
(
|
|
34302
|
+
(resolve15) => setTimeout(resolve15, 500 * (attempt + 1))
|
|
32442
34303
|
);
|
|
32443
34304
|
}
|
|
32444
34305
|
}
|
|
@@ -32514,19 +34375,19 @@ function slugify2(value) {
|
|
|
32514
34375
|
async function projectName(sourceDir) {
|
|
32515
34376
|
try {
|
|
32516
34377
|
const pkg = JSON.parse(
|
|
32517
|
-
await readFile15(
|
|
34378
|
+
await readFile15(join32(sourceDir, "package.json"), "utf8")
|
|
32518
34379
|
);
|
|
32519
34380
|
if (pkg.name) return pkg.name;
|
|
32520
34381
|
} catch {
|
|
32521
34382
|
}
|
|
32522
|
-
return
|
|
34383
|
+
return basename5(sourceDir);
|
|
32523
34384
|
}
|
|
32524
34385
|
function resolveBin(name) {
|
|
32525
|
-
let dir =
|
|
34386
|
+
let dir = dirname10(fileURLToPath4(import.meta.url));
|
|
32526
34387
|
for (; ; ) {
|
|
32527
|
-
const candidate =
|
|
32528
|
-
if (
|
|
32529
|
-
const parent =
|
|
34388
|
+
const candidate = join32(dir, "node_modules", ".bin", name);
|
|
34389
|
+
if (existsSync10(candidate)) return candidate;
|
|
34390
|
+
const parent = dirname10(dir);
|
|
32530
34391
|
if (parent === dir) return name;
|
|
32531
34392
|
dir = parent;
|
|
32532
34393
|
}
|
|
@@ -32591,7 +34452,7 @@ function workingTreeNotices(copy) {
|
|
|
32591
34452
|
}
|
|
32592
34453
|
function localDependencies(sourceDir, say = () => void 0) {
|
|
32593
34454
|
return {
|
|
32594
|
-
createWorkspace: () => mkdtemp2(
|
|
34455
|
+
createWorkspace: () => mkdtemp2(join32(tmpdir2(), "naro-build-")),
|
|
32595
34456
|
removeWorkspace: (path) => rm5(path, { recursive: true, force: true }),
|
|
32596
34457
|
async runCommand(spec, observer) {
|
|
32597
34458
|
if (spec.command === "git" && spec.args[0] === "clone") {
|
|
@@ -32641,7 +34502,7 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32641
34502
|
"Pass production: true for production; preview is the default."
|
|
32642
34503
|
);
|
|
32643
34504
|
const surface = production ? "production" : environment ?? "preview";
|
|
32644
|
-
const appRoot = options.rootDirectory ?
|
|
34505
|
+
const appRoot = options.rootDirectory ? resolve14(sourceDir, options.rootDirectory) : sourceDir;
|
|
32645
34506
|
const framework = detectFramework2(appRoot);
|
|
32646
34507
|
if (!framework.deployable && !options.force) {
|
|
32647
34508
|
throw new NaroError(
|
|
@@ -32661,6 +34522,7 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32661
34522
|
const client = new NaroClient({
|
|
32662
34523
|
baseUrl: cpUrl,
|
|
32663
34524
|
getAccessToken: () => auth.getAccessToken(),
|
|
34525
|
+
clientName: `naro-cli/${version2()}`,
|
|
32664
34526
|
...options.fetch ? { fetch: options.fetch } : {}
|
|
32665
34527
|
});
|
|
32666
34528
|
if (environment !== void 0 && options.projectId)
|
|
@@ -32704,6 +34566,7 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
32704
34566
|
log2(
|
|
32705
34567
|
`\u25B8 ${String(missing.length)} env var(s) needed, not set on this project: ${missing.join(", ")}`
|
|
32706
34568
|
);
|
|
34569
|
+
for (const line of envServiceHints(missing)) log2(line);
|
|
32707
34570
|
const canCollect = environment === void 0 || options.projectId;
|
|
32708
34571
|
if (options.onMissingEnv && canCollect)
|
|
32709
34572
|
collected = await options.onMissingEnv(missing);
|
|
@@ -32844,12 +34707,11 @@ function deploymentUrl(detail, ctx, production) {
|
|
|
32844
34707
|
}
|
|
32845
34708
|
|
|
32846
34709
|
export {
|
|
32847
|
-
|
|
34710
|
+
parseDotenvDetailed,
|
|
34711
|
+
isFunctionName,
|
|
32848
34712
|
parseGitHubRemote,
|
|
32849
34713
|
schemaMigrationTitle,
|
|
32850
34714
|
projectPath,
|
|
32851
|
-
splitStatements,
|
|
32852
|
-
environmentSlugProblem,
|
|
32853
34715
|
ANALYTICS_FILTER_KEYS,
|
|
32854
34716
|
NaroError,
|
|
32855
34717
|
promoteTargetUnsupported,
|
|
@@ -32884,7 +34746,6 @@ export {
|
|
|
32884
34746
|
cellText,
|
|
32885
34747
|
renderRows,
|
|
32886
34748
|
summarizeStatement,
|
|
32887
|
-
parseDotenvDetailed,
|
|
32888
34749
|
serializeDotenv,
|
|
32889
34750
|
systemTablePredicate,
|
|
32890
34751
|
MAX_SINGLE_UPLOAD_BYTES,
|
|
@@ -32907,7 +34768,6 @@ export {
|
|
|
32907
34768
|
parsePolicyStatement,
|
|
32908
34769
|
splitPolicyStatements,
|
|
32909
34770
|
STORAGE_SUBQUERIES_SQL,
|
|
32910
|
-
planPolicyMigration,
|
|
32911
34771
|
unreadablePolicyComment,
|
|
32912
34772
|
parseRedirectEntry,
|
|
32913
34773
|
formatRedirectEntry,
|
|
@@ -32919,10 +34779,8 @@ export {
|
|
|
32919
34779
|
ensureGitignoreHasNaro,
|
|
32920
34780
|
countLintedTables,
|
|
32921
34781
|
lintSchema,
|
|
32922
|
-
resolveMigrationsDir,
|
|
32923
34782
|
listMigrationFiles,
|
|
32924
34783
|
nextMigrationFileName,
|
|
32925
|
-
compareMigrations,
|
|
32926
34784
|
pendingMigrationNames,
|
|
32927
34785
|
migrationsPendingError,
|
|
32928
34786
|
migrationNameMismatch,
|
|
@@ -32941,6 +34799,40 @@ export {
|
|
|
32941
34799
|
promptForMissingEnv,
|
|
32942
34800
|
pullEnv,
|
|
32943
34801
|
detectGitInfo,
|
|
34802
|
+
version2 as version,
|
|
34803
|
+
BUNDLE_MESSAGES,
|
|
34804
|
+
functionsFolder,
|
|
34805
|
+
parseJsonc2 as parseJsonc,
|
|
34806
|
+
DATA_ENV_MESSAGES,
|
|
34807
|
+
dataEnvironment,
|
|
34808
|
+
dataClient,
|
|
34809
|
+
databaseName,
|
|
34810
|
+
enableBackendCommand,
|
|
34811
|
+
envFlag,
|
|
34812
|
+
forEnvironment,
|
|
34813
|
+
isInteractive,
|
|
34814
|
+
readStdin,
|
|
34815
|
+
createPrompter,
|
|
34816
|
+
cannotAsk,
|
|
34817
|
+
confirmOrAbort,
|
|
34818
|
+
migrationsDir,
|
|
34819
|
+
relativeToCwd,
|
|
34820
|
+
appliedMigrations,
|
|
34821
|
+
printable2 as printable,
|
|
34822
|
+
isPrintable,
|
|
34823
|
+
UNSHOWN_WARNING_LINE,
|
|
34824
|
+
warningLines,
|
|
34825
|
+
policyProblems,
|
|
34826
|
+
runMigrationNew,
|
|
34827
|
+
runMigrationList,
|
|
34828
|
+
applyMigrationFiles,
|
|
34829
|
+
runMigrationRepair,
|
|
34830
|
+
runMigrationUp,
|
|
34831
|
+
defaultSleep,
|
|
34832
|
+
retryAfterSeconds,
|
|
34833
|
+
withFunctionRetries,
|
|
34834
|
+
assertFunctionsNode,
|
|
34835
|
+
deployFunctions,
|
|
32944
34836
|
parseDotenv2 as parseDotenv,
|
|
32945
34837
|
readProjectEnv2 as readProjectEnv,
|
|
32946
34838
|
eventsObserver,
|