naro-cli 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -2
- package/dist/{chunk-SAA66XLD.js → chunk-MBSO3K6L.js} +1603 -564
- package/dist/deploy.js +1 -1
- package/dist/index.js +3062 -228
- package/package.json +3 -3
|
@@ -326,6 +326,162 @@ var STALL_WINDOW_MS = 10 * 6e4;
|
|
|
326
326
|
// ../../packages/core/src/deployment-strategy.ts
|
|
327
327
|
var MIN_SUPPORTED_NEXT_MAJOR = 15;
|
|
328
328
|
|
|
329
|
+
// ../../packages/core/src/environment-guidance.ts
|
|
330
|
+
var DATA_COMMANDS = /* @__PURE__ */ new Set([
|
|
331
|
+
"db",
|
|
332
|
+
"migration",
|
|
333
|
+
"gen",
|
|
334
|
+
"backend",
|
|
335
|
+
"policy",
|
|
336
|
+
"storage",
|
|
337
|
+
"realtime"
|
|
338
|
+
]);
|
|
339
|
+
var BACKEND_SWITCHES = /* @__PURE__ */ new Set([
|
|
340
|
+
"enable",
|
|
341
|
+
"provision",
|
|
342
|
+
"disable"
|
|
343
|
+
]);
|
|
344
|
+
var PRODUCTION_SWITCH_NOTE = "production's switch, which every environment follows";
|
|
345
|
+
var SWITCH_CONTEXT = /\bturned off\b|\bturn(?:s|ing)? (?:it|them) (?:back )?on\b|\bbackend is on\b/i;
|
|
346
|
+
var SUBCOMMANDS = {
|
|
347
|
+
db: /* @__PURE__ */ new Set([
|
|
348
|
+
"info",
|
|
349
|
+
"enable",
|
|
350
|
+
"query",
|
|
351
|
+
"push",
|
|
352
|
+
"dump",
|
|
353
|
+
"restore",
|
|
354
|
+
"pull",
|
|
355
|
+
"reset",
|
|
356
|
+
"lint",
|
|
357
|
+
"table",
|
|
358
|
+
"index",
|
|
359
|
+
"create",
|
|
360
|
+
"add-column",
|
|
361
|
+
"rename-column",
|
|
362
|
+
"rename",
|
|
363
|
+
"drop-column",
|
|
364
|
+
"drop"
|
|
365
|
+
]),
|
|
366
|
+
migration: /* @__PURE__ */ new Set(["list", "ls", "new", "up", "repair"]),
|
|
367
|
+
gen: /* @__PURE__ */ new Set(["types"]),
|
|
368
|
+
backend: /* @__PURE__ */ new Set([
|
|
369
|
+
"info",
|
|
370
|
+
"status",
|
|
371
|
+
"enable",
|
|
372
|
+
"provision",
|
|
373
|
+
"disable",
|
|
374
|
+
"keys",
|
|
375
|
+
"rotate",
|
|
376
|
+
"auth",
|
|
377
|
+
"email",
|
|
378
|
+
"oauth",
|
|
379
|
+
"redirects",
|
|
380
|
+
"require-verification",
|
|
381
|
+
"signups",
|
|
382
|
+
"password",
|
|
383
|
+
"set",
|
|
384
|
+
"add",
|
|
385
|
+
"remove",
|
|
386
|
+
"users",
|
|
387
|
+
"create",
|
|
388
|
+
"delete"
|
|
389
|
+
]),
|
|
390
|
+
policy: /* @__PURE__ */ new Set(["list", "ls", "set", "create", "drop", "remove", "rm"]),
|
|
391
|
+
storage: /* @__PURE__ */ new Set([
|
|
392
|
+
"enable",
|
|
393
|
+
"disable",
|
|
394
|
+
"bucket",
|
|
395
|
+
"list",
|
|
396
|
+
"create",
|
|
397
|
+
"update",
|
|
398
|
+
"empty",
|
|
399
|
+
"remove",
|
|
400
|
+
"cp",
|
|
401
|
+
"mv",
|
|
402
|
+
"ls",
|
|
403
|
+
"rm"
|
|
404
|
+
]),
|
|
405
|
+
realtime: /* @__PURE__ */ new Set(["enable", "disable", "tables", "add", "remove"])
|
|
406
|
+
};
|
|
407
|
+
function kindOf(words) {
|
|
408
|
+
const command = words[1] ?? "";
|
|
409
|
+
if (!DATA_COMMANDS.has(command)) return "leave";
|
|
410
|
+
if (words.some((word) => word === "--env" || word.startsWith("--env=")))
|
|
411
|
+
return "leave";
|
|
412
|
+
if (command === "migration" && words[2] === "new") return "leave";
|
|
413
|
+
if (command === "backend" && BACKEND_SWITCHES.has(words[2] ?? ""))
|
|
414
|
+
return words.length === 3 ? "switch" : "leave";
|
|
415
|
+
return "env";
|
|
416
|
+
}
|
|
417
|
+
function quoted(span, slug, switchContext) {
|
|
418
|
+
const words = span.split(" ");
|
|
419
|
+
switch (kindOf(words)) {
|
|
420
|
+
case "env":
|
|
421
|
+
return `\`${span} --env ${slug}\``;
|
|
422
|
+
case "switch":
|
|
423
|
+
return words[2] !== "disable" && !switchContext ? `\`naro environments provision ${slug}\`` : `\`${span}\` (${PRODUCTION_SWITCH_NOTE})`;
|
|
424
|
+
default:
|
|
425
|
+
return `\`${span}\``;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
var QUOTED = /`(naro [^`\n]+)`/g;
|
|
429
|
+
var BARE = /(?<![`\w/-])naro ([a-z]+)((?: [a-z][a-z-]*)*)/g;
|
|
430
|
+
function bare(match, command, rest, after, slug, switchContext) {
|
|
431
|
+
const vocabulary = SUBCOMMANDS[command];
|
|
432
|
+
if (vocabulary === void 0) return match;
|
|
433
|
+
const words = rest.split(" ").filter((word) => word !== "");
|
|
434
|
+
let count2 = 0;
|
|
435
|
+
while (count2 < words.length && vocabulary.has(words[count2] ?? "")) count2++;
|
|
436
|
+
if (count2 === 0) return match;
|
|
437
|
+
const head = ["naro", command, ...words.slice(0, count2)];
|
|
438
|
+
const tail = words.slice(count2);
|
|
439
|
+
const following = after.split(/\n|[.;,)](?:\s|$)/, 1)[0] ?? "";
|
|
440
|
+
if (/(?:^|\s)--env[\s=]/.test(following) || after.startsWith(` \u2014 ${PRODUCTION_SWITCH_NOTE}`))
|
|
441
|
+
return match;
|
|
442
|
+
const shown = head.join(" ");
|
|
443
|
+
const remainder = tail.length > 0 ? ` ${tail.join(" ")}` : "";
|
|
444
|
+
switch (kindOf(head)) {
|
|
445
|
+
case "env":
|
|
446
|
+
return `${shown} --env ${slug}${remainder}`;
|
|
447
|
+
case "switch":
|
|
448
|
+
return head[2] !== "disable" && !switchContext ? `naro environments provision ${slug}${remainder}` : `${shown} \u2014 ${PRODUCTION_SWITCH_NOTE}${remainder}`;
|
|
449
|
+
default:
|
|
450
|
+
return match;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function environmentGuidance(text2, slug) {
|
|
454
|
+
if (slug === null || !text2.includes("naro ")) return text2;
|
|
455
|
+
const switchContext = SWITCH_CONTEXT.test(text2);
|
|
456
|
+
const spans = [];
|
|
457
|
+
const marked = text2.replaceAll(
|
|
458
|
+
QUOTED,
|
|
459
|
+
(match, span, offset) => {
|
|
460
|
+
const noted = text2.startsWith(
|
|
461
|
+
` (${PRODUCTION_SWITCH_NOTE})`,
|
|
462
|
+
offset + match.length
|
|
463
|
+
);
|
|
464
|
+
spans.push(noted ? match : quoted(span, slug, switchContext));
|
|
465
|
+
return `\uE000${String(spans.length - 1)}\uE000`;
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
const rewritten = marked.replaceAll(
|
|
469
|
+
BARE,
|
|
470
|
+
(match, command, rest, offset) => bare(
|
|
471
|
+
match,
|
|
472
|
+
command,
|
|
473
|
+
rest,
|
|
474
|
+
marked.slice(offset + match.length),
|
|
475
|
+
slug,
|
|
476
|
+
switchContext
|
|
477
|
+
)
|
|
478
|
+
);
|
|
479
|
+
return rewritten.replaceAll(
|
|
480
|
+
/\uE000(\d+)\uE000/g,
|
|
481
|
+
(_match, index) => spans[Number(index)] ?? ""
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
329
485
|
// ../../packages/core/src/errors.ts
|
|
330
486
|
var DEPLOYMENT_ERROR_CODES = [
|
|
331
487
|
"CLONE_FAILED",
|
|
@@ -436,7 +592,11 @@ var DEPLOYMENT_ERROR_CODES = [
|
|
|
436
592
|
"ENVIRONMENT_BRANCH_IS_PRODUCTION",
|
|
437
593
|
/** The plan's environment count is full. */
|
|
438
594
|
"ENVIRONMENT_LIMIT_REACHED",
|
|
439
|
-
/**
|
|
595
|
+
/**
|
|
596
|
+
* The project uses a Naro database, which environments did not yet support
|
|
597
|
+
* (stages 1 and 2 only). No route answers it since named environments 3c;
|
|
598
|
+
* kept for clients of a control plane from before.
|
|
599
|
+
*/
|
|
440
600
|
"ENVIRONMENT_DATABASE_UNSUPPORTED",
|
|
441
601
|
"ENVIRONMENT_NOT_FOUND",
|
|
442
602
|
/** The project's new default branch is the one an environment deploys. */
|
|
@@ -460,6 +620,9 @@ var DeploymentError = class extends Error {
|
|
|
460
620
|
}
|
|
461
621
|
};
|
|
462
622
|
|
|
623
|
+
// ../../packages/core/src/functions.ts
|
|
624
|
+
var FUNCTION_WORKER_NAME_PREFIX = "naro-fn-";
|
|
625
|
+
|
|
463
626
|
// ../../packages/core/src/git-remote.ts
|
|
464
627
|
var SCP_LIKE = /^[^@\s/]+@([^:\s/]+):(.+)$/;
|
|
465
628
|
function parseGitHubRemote(url2) {
|
|
@@ -15064,6 +15227,8 @@ function subdomainProblem(value) {
|
|
|
15064
15227
|
if (value.endsWith("-preview")) return "reserved";
|
|
15065
15228
|
if (value === BACKEND_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(BACKEND_WORKER_NAME_PREFIX))
|
|
15066
15229
|
return "reserved";
|
|
15230
|
+
if (value === FUNCTION_WORKER_NAME_PREFIX.slice(0, -1) || value.startsWith(FUNCTION_WORKER_NAME_PREFIX))
|
|
15231
|
+
return "reserved";
|
|
15067
15232
|
if (RESERVED_SUBDOMAINS.includes(value))
|
|
15068
15233
|
return "reserved";
|
|
15069
15234
|
return null;
|
|
@@ -15399,6 +15564,72 @@ function workerNameForSurface(subdomain, surface) {
|
|
|
15399
15564
|
}
|
|
15400
15565
|
}
|
|
15401
15566
|
|
|
15567
|
+
// ../../packages/core/src/usage-alert-mail.ts
|
|
15568
|
+
var REQUESTS = {
|
|
15569
|
+
en: {
|
|
15570
|
+
subject80: (team) => `${team} has used 80% of its monthly requests`,
|
|
15571
|
+
subject100: (team) => `${team} has reached its monthly request limit`,
|
|
15572
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} requests the Hobby plan includes for ${month}.`,
|
|
15573
|
+
warning: "When it reaches the limit, requests past it may be blocked until the month ends (UTC).",
|
|
15574
|
+
blocked: "Sites on this team answer 503 until the month ends (UTC).",
|
|
15575
|
+
running: "Sites keep running for now.",
|
|
15576
|
+
button: "View usage",
|
|
15577
|
+
footer: (team) => `You're receiving this because you're an owner or billing member of ${team} on naro.`
|
|
15578
|
+
},
|
|
15579
|
+
ko: {
|
|
15580
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15581
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15582
|
+
usage: (team, used, limit, month) => `${team} \uD300\uC774 ${month} Hobby \uD50C\uB79C\uC5D0 \uD3EC\uD568\uB41C \uC694\uCCAD ${limit}\uAC74 \uC911 ${used}\uAC74\uC744 \uC37C\uC2B5\uB2C8\uB2E4.`,
|
|
15583
|
+
warning: "\uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uADF8 \uB4A4 \uC694\uCCAD\uC774 \uB9C9\uD790 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
15584
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0AC\uC774\uD2B8\uB294 503\uC73C\uB85C \uC751\uB2F5\uD569\uB2C8\uB2E4.",
|
|
15585
|
+
running: "\uC9C0\uAE08\uC740 \uC0AC\uC774\uD2B8\uAC00 \uACC4\uC18D \uB3D9\uC791\uD569\uB2C8\uB2E4.",
|
|
15586
|
+
button: "\uC0AC\uC6A9\uB7C9 \uBCF4\uAE30",
|
|
15587
|
+
footer: (team) => `naro\uC5D0\uC11C ${team} \uD300\uC758 owner \uB610\uB294 billing \uC5ED\uD560\uC774\uB77C \uC774 \uBA54\uC77C\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4.`
|
|
15588
|
+
},
|
|
15589
|
+
ja: {
|
|
15590
|
+
subject80: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306E80%\u3092\u4F7F\u7528\u3057\u307E\u3057\u305F`,
|
|
15591
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15592
|
+
usage: (team, used, limit, month) => `${team} \u30C1\u30FC\u30E0\u306F ${month} \u306E Hobby \u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B ${limit} \u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u3046\u3061 ${used} \u3092\u4F7F\u7528\u3057\u307E\u3057\u305F\u3002`,
|
|
15593
|
+
warning: "\u4E0A\u9650\u306B\u9054\u3059\u308B\u3068\u3001\u6708\u672B(UTC)\u307E\u3067\u4EE5\u964D\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
15594
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u30B5\u30A4\u30C8\u306F503\u3092\u8FD4\u3057\u307E\u3059\u3002",
|
|
15595
|
+
running: "\u73FE\u5728\u3001\u30B5\u30A4\u30C8\u306F\u5F15\u304D\u7D9A\u304D\u52D5\u4F5C\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
15596
|
+
button: "\u4F7F\u7528\u91CF\u3092\u898B\u308B",
|
|
15597
|
+
footer: (team) => `naro \u3067 ${team} \u30C1\u30FC\u30E0\u306E owner \u307E\u305F\u306F billing \u30ED\u30FC\u30EB\u306E\u305F\u3081\u3001\u3053\u306E\u30E1\u30FC\u30EB\u3092\u304A\u9001\u308A\u3057\u3066\u3044\u307E\u3059\u3002`
|
|
15598
|
+
}
|
|
15599
|
+
};
|
|
15600
|
+
var BUILDS = {
|
|
15601
|
+
en: {
|
|
15602
|
+
subject80: (team) => `${team} has used 80% of its monthly builds`,
|
|
15603
|
+
subject100: (team) => `${team} has reached its monthly build limit`,
|
|
15604
|
+
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} builds the Hobby plan includes for ${month}.`,
|
|
15605
|
+
warning: "When it reaches the limit, new deploys may be refused until the month ends (UTC).",
|
|
15606
|
+
blocked: "New deploys on this team are refused until the month ends (UTC).",
|
|
15607
|
+
running: "Deploys keep working for now.",
|
|
15608
|
+
button: REQUESTS.en.button,
|
|
15609
|
+
footer: REQUESTS.en.footer
|
|
15610
|
+
},
|
|
15611
|
+
ko: {
|
|
15612
|
+
subject80: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC758 80%\uB97C \uC37C\uC2B5\uB2C8\uB2E4`,
|
|
15613
|
+
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uBE4C\uB4DC \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
15614
|
+
usage: (team, used, limit, month) => `${team} \uD300\uC774 ${month} Hobby \uD50C\uB79C\uC5D0 \uD3EC\uD568\uB41C \uBE4C\uB4DC ${limit}\uAC74 \uC911 ${used}\uAC74\uC744 \uC37C\uC2B5\uB2C8\uB2E4.`,
|
|
15615
|
+
warning: "\uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC0C8 \uBC30\uD3EC\uAC00 \uAC70\uC808\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
15616
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0C8 \uBC30\uD3EC\uB294 \uAC70\uC808\uB429\uB2C8\uB2E4.",
|
|
15617
|
+
running: "\uC9C0\uAE08\uC740 \uBC30\uD3EC\uAC00 \uACC4\uC18D \uB429\uB2C8\uB2E4.",
|
|
15618
|
+
button: REQUESTS.ko.button,
|
|
15619
|
+
footer: REQUESTS.ko.footer
|
|
15620
|
+
},
|
|
15621
|
+
ja: {
|
|
15622
|
+
subject80: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306E80%\u3092\u4F7F\u7528\u3057\u307E\u3057\u305F`,
|
|
15623
|
+
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30D3\u30EB\u30C9\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
15624
|
+
usage: (team, used, limit, month) => `${team} \u30C1\u30FC\u30E0\u306F ${month} \u306E Hobby \u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B ${limit} \u30D3\u30EB\u30C9\u306E\u3046\u3061 ${used} \u3092\u4F7F\u7528\u3057\u307E\u3057\u305F\u3002`,
|
|
15625
|
+
warning: "\u4E0A\u9650\u306B\u9054\u3059\u308B\u3068\u3001\u6708\u672B(UTC)\u307E\u3067\u65B0\u3057\u3044\u30C7\u30D7\u30ED\u30A4\u304C\u62D2\u5426\u3055\u308C\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
15626
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u65B0\u3057\u3044\u30C7\u30D7\u30ED\u30A4\u306F\u62D2\u5426\u3055\u308C\u307E\u3059\u3002",
|
|
15627
|
+
running: "\u73FE\u5728\u3001\u30C7\u30D7\u30ED\u30A4\u306F\u5F15\u304D\u7D9A\u304D\u884C\u3048\u307E\u3059\u3002",
|
|
15628
|
+
button: REQUESTS.ja.button,
|
|
15629
|
+
footer: REQUESTS.ja.footer
|
|
15630
|
+
}
|
|
15631
|
+
};
|
|
15632
|
+
|
|
15402
15633
|
// ../../packages/build/src/next-version.ts
|
|
15403
15634
|
var NEXT_RELEASE_DATES = {
|
|
15404
15635
|
16: "2025-10-21",
|
|
@@ -19244,7 +19475,7 @@ async function errorFromResponse(response, context) {
|
|
|
19244
19475
|
if (typeof serverDetails === "object" && serverDetails !== null && !Array.isArray(serverDetails))
|
|
19245
19476
|
Object.assign(details, serverDetails);
|
|
19246
19477
|
const retryAfter = response.headers.get("retry-after")?.trim() ?? "";
|
|
19247
|
-
if (response.status === 429 && /^\d{1,9}$/.test(retryAfter))
|
|
19478
|
+
if ((response.status === 429 || response.status === 503) && /^\d{1,9}$/.test(retryAfter))
|
|
19248
19479
|
details.retryAfter = Number(retryAfter);
|
|
19249
19480
|
if (body?.error?.fields !== void 0) details.fields = body.error.fields;
|
|
19250
19481
|
if (typeof body?.error?.installUrl === "string")
|
|
@@ -19712,6 +19943,16 @@ function isReservedEnvTarget(value) {
|
|
|
19712
19943
|
function isEnvTargetName(value) {
|
|
19713
19944
|
return environmentSlugProblem(value) !== "invalid";
|
|
19714
19945
|
}
|
|
19946
|
+
var NARO_SYSTEM_ENV_NAMES = [
|
|
19947
|
+
"NARO_URL",
|
|
19948
|
+
"NEXT_PUBLIC_NARO_URL",
|
|
19949
|
+
"VITE_NARO_URL",
|
|
19950
|
+
"PUBLIC_NARO_URL",
|
|
19951
|
+
"NARO_ANON_KEY",
|
|
19952
|
+
"NEXT_PUBLIC_NARO_ANON_KEY",
|
|
19953
|
+
"VITE_NARO_ANON_KEY",
|
|
19954
|
+
"PUBLIC_NARO_ANON_KEY"
|
|
19955
|
+
];
|
|
19715
19956
|
|
|
19716
19957
|
// ../../packages/sdk/src/insights.ts
|
|
19717
19958
|
var SPEED_INSIGHTS_METRICS = [
|
|
@@ -19745,6 +19986,7 @@ var BACKEND_STEPS = [
|
|
|
19745
19986
|
"worker",
|
|
19746
19987
|
"routes",
|
|
19747
19988
|
"migrate",
|
|
19989
|
+
"schema",
|
|
19748
19990
|
"health"
|
|
19749
19991
|
];
|
|
19750
19992
|
var STALLED_GRACE_MS = 2 * 6e4;
|
|
@@ -19808,9 +20050,17 @@ function buildLogEntry(row) {
|
|
|
19808
20050
|
}
|
|
19809
20051
|
|
|
19810
20052
|
// ../../packages/sdk/src/client.ts
|
|
20053
|
+
var DATA_PATH = /^\/api\/projects\/[^/?]+\/(?:database|backend(?!\/functions))(?:[/?]|$)/;
|
|
20054
|
+
function withDataEnvironment(path, environment) {
|
|
20055
|
+
if (environment === null || !DATA_PATH.test(path)) return path;
|
|
20056
|
+
return `${path}${path.includes("?") ? "&" : "?"}environment=${encodeURIComponent(environment)}`;
|
|
20057
|
+
}
|
|
19811
20058
|
var DEFAULT_REQUEST_TIMEOUT_MS = 45e3;
|
|
19812
|
-
var NaroClient = class {
|
|
20059
|
+
var NaroClient = class _NaroClient {
|
|
19813
20060
|
baseUrl;
|
|
20061
|
+
/** The environment whose data the data calls act on; null: production. */
|
|
20062
|
+
environment;
|
|
20063
|
+
options;
|
|
19814
20064
|
getAccessToken;
|
|
19815
20065
|
doFetch;
|
|
19816
20066
|
requestTimeoutMs;
|
|
@@ -19824,11 +20074,15 @@ var NaroClient = class {
|
|
|
19824
20074
|
db;
|
|
19825
20075
|
backend;
|
|
19826
20076
|
storage;
|
|
20077
|
+
realtime;
|
|
20078
|
+
functions;
|
|
19827
20079
|
insights;
|
|
19828
20080
|
analytics;
|
|
19829
20081
|
protection;
|
|
19830
20082
|
environments;
|
|
19831
20083
|
constructor(options) {
|
|
20084
|
+
this.options = options;
|
|
20085
|
+
this.environment = options.environment ?? null;
|
|
19832
20086
|
this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
|
|
19833
20087
|
this.getAccessToken = options.getAccessToken;
|
|
19834
20088
|
this.doFetch = options.fetch ?? fetch;
|
|
@@ -19843,25 +20097,42 @@ var NaroClient = class {
|
|
|
19843
20097
|
this.db = new DbApi(this);
|
|
19844
20098
|
this.backend = new BackendApi(this);
|
|
19845
20099
|
this.storage = new StorageApi(this);
|
|
20100
|
+
this.realtime = new RealtimeApi(this);
|
|
20101
|
+
this.functions = new FunctionsApi(this);
|
|
19846
20102
|
this.insights = new InsightsApi(this);
|
|
19847
20103
|
this.analytics = new AnalyticsApi(this);
|
|
19848
20104
|
this.protection = new ProtectionApi(this);
|
|
19849
20105
|
this.environments = new EnvironmentsApi(this);
|
|
19850
20106
|
}
|
|
20107
|
+
/**
|
|
20108
|
+
* The same client acting on `environment`'s data — or production's, for
|
|
20109
|
+
* null. Every other call is the same: the scope is only the
|
|
20110
|
+
* `?environment=` on database and backend paths.
|
|
20111
|
+
*/
|
|
20112
|
+
inEnvironment(environment) {
|
|
20113
|
+
if (environment === this.environment) return this;
|
|
20114
|
+
const options = { ...this.options };
|
|
20115
|
+
delete options.environment;
|
|
20116
|
+
if (environment !== null) options.environment = environment;
|
|
20117
|
+
return new _NaroClient(options);
|
|
20118
|
+
}
|
|
19851
20119
|
async request(path, options) {
|
|
19852
20120
|
const token = await this.getAccessToken();
|
|
19853
20121
|
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
19854
20122
|
let response;
|
|
19855
20123
|
try {
|
|
19856
|
-
response = await this.doFetch(
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
20124
|
+
response = await this.doFetch(
|
|
20125
|
+
`${this.baseUrl}${withDataEnvironment(path, this.environment)}`,
|
|
20126
|
+
{
|
|
20127
|
+
method: options.method ?? "GET",
|
|
20128
|
+
headers: {
|
|
20129
|
+
Authorization: `Bearer ${token}`,
|
|
20130
|
+
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
20131
|
+
},
|
|
20132
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
20133
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
20134
|
+
}
|
|
20135
|
+
);
|
|
19865
20136
|
} catch (error51) {
|
|
19866
20137
|
if (error51 instanceof Error && (error51.name === "TimeoutError" || error51.name === "AbortError")) {
|
|
19867
20138
|
throw new NaroError(
|
|
@@ -20059,17 +20330,23 @@ var DeploymentsApi = class {
|
|
|
20059
20330
|
* `PROMOTE_TARGET_UNSUPPORTED` before anything is promoted. The target is
|
|
20060
20331
|
* trimmed once, as the control plane trims it, so `" production"` is
|
|
20061
20332
|
* production and asks no preview.
|
|
20333
|
+
*
|
|
20334
|
+
* `check: true` asks the preview for production too, for a caller that
|
|
20335
|
+
* cannot show it first and returns it instead (MCP's `promote_deployment`):
|
|
20336
|
+
* a 405 there means a control plane from before previews, and production
|
|
20337
|
+
* is promoted unchecked as before. The preview asked is the result's
|
|
20338
|
+
* `check`, so its migration warnings reach the caller (spec §9.4).
|
|
20062
20339
|
*/
|
|
20063
20340
|
async promote(projectId, deploymentId, options = {}) {
|
|
20064
20341
|
const target2 = options.target?.trim();
|
|
20065
|
-
|
|
20342
|
+
const toEnvironment = target2 !== void 0 && target2 !== "production";
|
|
20343
|
+
let check2;
|
|
20344
|
+
if (toEnvironment || options.check === true) {
|
|
20066
20345
|
try {
|
|
20067
|
-
await this.promoteCheck(projectId, deploymentId, target2);
|
|
20346
|
+
check2 = await this.promoteCheck(projectId, deploymentId, target2);
|
|
20068
20347
|
} catch (error51) {
|
|
20069
|
-
if (isNaroError(error51)
|
|
20070
|
-
|
|
20071
|
-
}
|
|
20072
|
-
throw error51;
|
|
20348
|
+
if (!isNaroError(error51) || error51.status !== 405) throw error51;
|
|
20349
|
+
if (toEnvironment) throw promoteTargetUnsupported(error51);
|
|
20073
20350
|
}
|
|
20074
20351
|
}
|
|
20075
20352
|
const body = await this.client.request(this.promotePath(projectId, deploymentId), {
|
|
@@ -20079,7 +20356,8 @@ var DeploymentsApi = class {
|
|
|
20079
20356
|
});
|
|
20080
20357
|
return {
|
|
20081
20358
|
...deploymentInfo(body.deployment),
|
|
20082
|
-
mode: body.mode ?? "reupload"
|
|
20359
|
+
mode: body.mode ?? "reupload",
|
|
20360
|
+
...check2 === void 0 ? {} : { check: check2 }
|
|
20083
20361
|
};
|
|
20084
20362
|
}
|
|
20085
20363
|
/**
|
|
@@ -20580,6 +20858,19 @@ var EnvironmentsApi = class {
|
|
|
20580
20858
|
{ method: "DELETE", context: "Delete environment" }
|
|
20581
20859
|
);
|
|
20582
20860
|
}
|
|
20861
|
+
/**
|
|
20862
|
+
* Makes or continues the environment's data — its D1, Naro backend and
|
|
20863
|
+
* Storage bucket (spec §11.2): the run goes on after the answer, which
|
|
20864
|
+
* says `data.status` `provisioning`. A run that stopped (`error`) starts
|
|
20865
|
+
* again from where it stopped; `none` when the project uses neither.
|
|
20866
|
+
*/
|
|
20867
|
+
async provision(projectId, environmentId) {
|
|
20868
|
+
const body = await this.client.request(
|
|
20869
|
+
`${this.base(projectId)}/${encodeURIComponent(environmentId)}/provision`,
|
|
20870
|
+
{ method: "POST", context: "Provision environment" }
|
|
20871
|
+
);
|
|
20872
|
+
return body.environment;
|
|
20873
|
+
}
|
|
20583
20874
|
};
|
|
20584
20875
|
var EXPORT_FIRST_POLL_MS = 1e3;
|
|
20585
20876
|
var EXPORT_MAX_POLL_MS = 5e3;
|
|
@@ -20868,9 +21159,9 @@ function storageInfo(body) {
|
|
|
20868
21159
|
buckets: (body.buckets ?? []).map(storageBucket)
|
|
20869
21160
|
};
|
|
20870
21161
|
}
|
|
20871
|
-
function
|
|
21162
|
+
function provisionFailure(error51, feature) {
|
|
20872
21163
|
const details = isNaroError(error51) ? error51.details : void 0;
|
|
20873
|
-
if (isNaroError(error51) && error51.code === "BACKEND_PROVISION_FAILED" && isRecord(details?.
|
|
21164
|
+
if (isNaroError(error51) && error51.code === "BACKEND_PROVISION_FAILED" && isRecord(details?.[feature]) && isRecord(details.backend))
|
|
20874
21165
|
return new NaroError(error51.code, error51.message, {
|
|
20875
21166
|
...error51.status !== void 0 ? { status: error51.status } : {},
|
|
20876
21167
|
details: {
|
|
@@ -21308,7 +21599,7 @@ var StorageApi = class {
|
|
|
21308
21599
|
}
|
|
21309
21600
|
);
|
|
21310
21601
|
} catch (error51) {
|
|
21311
|
-
throw
|
|
21602
|
+
throw provisionFailure(error51, "storage");
|
|
21312
21603
|
}
|
|
21313
21604
|
return storageInfo(body.storage);
|
|
21314
21605
|
}
|
|
@@ -21346,7 +21637,7 @@ var StorageApi = class {
|
|
|
21346
21637
|
}
|
|
21347
21638
|
);
|
|
21348
21639
|
} catch (error51) {
|
|
21349
|
-
throw
|
|
21640
|
+
throw provisionFailure(error51, "storage");
|
|
21350
21641
|
}
|
|
21351
21642
|
return storageInfo(body.storage);
|
|
21352
21643
|
}
|
|
@@ -21505,6 +21796,310 @@ var StorageApi = class {
|
|
|
21505
21796
|
return body.objects;
|
|
21506
21797
|
}
|
|
21507
21798
|
};
|
|
21799
|
+
var RealtimeApi = class {
|
|
21800
|
+
constructor(client) {
|
|
21801
|
+
this.client = client;
|
|
21802
|
+
}
|
|
21803
|
+
path(projectId, suffix = "") {
|
|
21804
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/realtime${suffix}`;
|
|
21805
|
+
}
|
|
21806
|
+
/**
|
|
21807
|
+
* Like `StorageApi.bucketPath`: URL parsing folds `.` and `..`, an empty
|
|
21808
|
+
* name is the collection, and `encodeURIComponent` throws on a lone
|
|
21809
|
+
* surrogate — so a name no URL can carry is refused here, before a
|
|
21810
|
+
* request. SQLite does make tables named `.` and `..`, and one with the
|
|
21811
|
+
* empty name (`CREATE TABLE "" (a)`); such a table cannot be published
|
|
21812
|
+
* or unpublished through this API.
|
|
21813
|
+
*/
|
|
21814
|
+
tablePath(projectId, table) {
|
|
21815
|
+
if (table === "" || table === "." || table === ".." || LONE_SURROGATE.test(table))
|
|
21816
|
+
throw new NaroError("INVALID_PATH", "The table name is not valid.");
|
|
21817
|
+
return this.path(projectId, `/tables/${encodeURIComponent(table)}`);
|
|
21818
|
+
}
|
|
21819
|
+
/**
|
|
21820
|
+
* Whether Realtime is on, the limits the hub enforces, the published
|
|
21821
|
+
* tables and the hub's own numbers. A project that never turned it on
|
|
21822
|
+
* answers `enabled: false` with no tables.
|
|
21823
|
+
*/
|
|
21824
|
+
async info(projectId) {
|
|
21825
|
+
const body = await this.client.request(
|
|
21826
|
+
this.path(projectId),
|
|
21827
|
+
{ context: "Read realtime" }
|
|
21828
|
+
);
|
|
21829
|
+
return body.realtime;
|
|
21830
|
+
}
|
|
21831
|
+
/**
|
|
21832
|
+
* Turns Realtime on: the request and the plan's limits are recorded, and
|
|
21833
|
+
* the Naro worker is uploaded again with its hub (a Durable Object). Safe
|
|
21834
|
+
* to repeat, and a repair — also after a plan change. The project must have
|
|
21835
|
+
* a backend, turned on, with its database. A backend in `error` or with a
|
|
21836
|
+
* run that stalled is not refused: its provisioning is resumed on the way.
|
|
21837
|
+
* Two minutes, like `backend.enable`.
|
|
21838
|
+
*
|
|
21839
|
+
* Refusals, in the order they are checked: `BACKEND_DISABLED` (the control
|
|
21840
|
+
* plane's backend switch), `FORBIDDEN` (`database.write`),
|
|
21841
|
+
* `REALTIME_DISABLED` (the control plane's Realtime switch),
|
|
21842
|
+
* `DATABASE_NOT_ENABLED` (the project's database is external),
|
|
21843
|
+
* `BACKEND_NOT_READY` (409, only when the project has no backend at all),
|
|
21844
|
+
* `BACKEND_OFF`, `BACKEND_BUSY`, `REALTIME_ENABLE_FAILED` (502: the request
|
|
21845
|
+
* could not be recorded; nothing changed), and `BACKEND_PROVISION_FAILED`
|
|
21846
|
+
* (502) when Realtime is recorded as on but the worker's upload stopped.
|
|
21847
|
+
* The last one's `details` hold a `RealtimeProvisionFailure` — `{ realtime:
|
|
21848
|
+
* { applied: false, enabled: true }, backend }`, the backend read like
|
|
21849
|
+
* `backend.info` — and `enable` again finishes.
|
|
21850
|
+
*/
|
|
21851
|
+
async enable(projectId) {
|
|
21852
|
+
let body;
|
|
21853
|
+
try {
|
|
21854
|
+
body = await this.client.request(
|
|
21855
|
+
this.path(projectId),
|
|
21856
|
+
{
|
|
21857
|
+
method: "POST",
|
|
21858
|
+
context: "Turn on Realtime",
|
|
21859
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21860
|
+
}
|
|
21861
|
+
);
|
|
21862
|
+
} catch (error51) {
|
|
21863
|
+
throw provisionFailure(error51, "realtime");
|
|
21864
|
+
}
|
|
21865
|
+
return body.realtime;
|
|
21866
|
+
}
|
|
21867
|
+
/**
|
|
21868
|
+
* The tables whose Data API writes are published, in the order they were
|
|
21869
|
+
* published. `exists: false` is a published name whose table was dropped.
|
|
21870
|
+
* `REALTIME_NOT_ENABLED` (409) before `enable`; it answers while the
|
|
21871
|
+
* backend is off, since the list is in the project's database.
|
|
21872
|
+
*/
|
|
21873
|
+
async listTables(projectId) {
|
|
21874
|
+
const body = await this.client.request(
|
|
21875
|
+
this.path(projectId, "/tables"),
|
|
21876
|
+
{ context: "List realtime tables" }
|
|
21877
|
+
);
|
|
21878
|
+
return body.tables;
|
|
21879
|
+
}
|
|
21880
|
+
/**
|
|
21881
|
+
* Publishes a table's changes to the subscribers of its channel. Safe to
|
|
21882
|
+
* repeat. Only changes made through the Data API are published — writes
|
|
21883
|
+
* the app makes with `env.DB` are not (`warnings` says so).
|
|
21884
|
+
* `TABLE_NOT_FOUND` (404) for a table that does not exist;
|
|
21885
|
+
* `VALIDATION_FAILED` (400) for a Naro system table, a virtual table, a
|
|
21886
|
+
* name over 256 characters, or the 100-table cap; `REALTIME_NOT_ENABLED`
|
|
21887
|
+
* (409) before `enable`.
|
|
21888
|
+
*/
|
|
21889
|
+
async publishTable(projectId, table) {
|
|
21890
|
+
return this.client.request(
|
|
21891
|
+
this.tablePath(projectId, table),
|
|
21892
|
+
{ method: "PUT", context: "Publish a realtime table" }
|
|
21893
|
+
);
|
|
21894
|
+
}
|
|
21895
|
+
/**
|
|
21896
|
+
* Stops publishing a table's changes; a table dropped since can still be
|
|
21897
|
+
* unpublished. `TABLE_NOT_PUBLISHED` (404) for a table that does not
|
|
21898
|
+
* publish, `REALTIME_NOT_ENABLED` (409) before `enable`.
|
|
21899
|
+
*/
|
|
21900
|
+
async unpublishTable(projectId, table) {
|
|
21901
|
+
return this.client.request(
|
|
21902
|
+
this.tablePath(projectId, table),
|
|
21903
|
+
{ method: "DELETE", context: "Stop publishing a realtime table" }
|
|
21904
|
+
);
|
|
21905
|
+
}
|
|
21906
|
+
};
|
|
21907
|
+
var FunctionsApi = class {
|
|
21908
|
+
constructor(client) {
|
|
21909
|
+
this.client = client;
|
|
21910
|
+
}
|
|
21911
|
+
path(projectId, suffix = "") {
|
|
21912
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/functions${suffix}`;
|
|
21913
|
+
}
|
|
21914
|
+
/**
|
|
21915
|
+
* A segment no URL can carry is refused before a request, like
|
|
21916
|
+
* `StorageApi.bucketPath`: URL parsing folds `/`, `.` and `..` into the
|
|
21917
|
+
* path, an empty one is the collection, and `encodeURIComponent` throws on
|
|
21918
|
+
* a lone surrogate. The server checks the name's own rule.
|
|
21919
|
+
*/
|
|
21920
|
+
segment(value, what) {
|
|
21921
|
+
if (value === "" || value === "." || value === ".." || value.includes("/") || LONE_SURROGATE.test(value))
|
|
21922
|
+
throw new NaroError("INVALID_PATH", `The ${what} is not valid.`);
|
|
21923
|
+
return encodeURIComponent(value);
|
|
21924
|
+
}
|
|
21925
|
+
functionPath(projectId, name, suffix = "") {
|
|
21926
|
+
return this.path(
|
|
21927
|
+
projectId,
|
|
21928
|
+
`/${this.segment(name, "function name")}${suffix}`
|
|
21929
|
+
);
|
|
21930
|
+
}
|
|
21931
|
+
/** The project's functions, the plan's limits, and (3-3e) this month's usage. */
|
|
21932
|
+
async list(projectId) {
|
|
21933
|
+
return this.client.request(this.path(projectId), {
|
|
21934
|
+
context: "List functions"
|
|
21935
|
+
});
|
|
21936
|
+
}
|
|
21937
|
+
/** One function and its newest 20 versions. `FUNCTION_NOT_FOUND` (404). */
|
|
21938
|
+
async get(projectId, name) {
|
|
21939
|
+
return this.client.request(
|
|
21940
|
+
this.functionPath(projectId, name),
|
|
21941
|
+
{ context: "Read a function" }
|
|
21942
|
+
);
|
|
21943
|
+
}
|
|
21944
|
+
/**
|
|
21945
|
+
* A new version — the function too, the first time — and where to PUT its
|
|
21946
|
+
* bundle: `upload.url` with `upload.headers` (the checksum header is
|
|
21947
|
+
* signed). Refusals in the order they are checked: `BACKEND_DISABLED`,
|
|
21948
|
+
* `UNAUTHORIZED`, `NOT_FOUND`, `FORBIDDEN` (`deployment.create`),
|
|
21949
|
+
* `FUNCTIONS_DISABLED`, `FORBIDDEN` (a suspended project),
|
|
21950
|
+
* `VALIDATION_FAILED` (the name or the request), `BUNDLE_TOO_LARGE`,
|
|
21951
|
+
* `DATABASE_NOT_ENABLED`, `BACKEND_NOT_READY`, `BACKEND_OFF`,
|
|
21952
|
+
* `BACKEND_BUSY` (the project's Naro worker is not the current version and
|
|
21953
|
+
* could not be brought up to date just now), `FUNCTION_LIMIT` (a new name
|
|
21954
|
+
* over the plan's cap) and `FUNCTION_BUSY` (the function is being deleted).
|
|
21955
|
+
* It never turns the backend on. Two minutes.
|
|
21956
|
+
*/
|
|
21957
|
+
async createVersion(projectId, name, request2) {
|
|
21958
|
+
return this.client.request(
|
|
21959
|
+
this.functionPath(projectId, name, "/versions"),
|
|
21960
|
+
{
|
|
21961
|
+
method: "POST",
|
|
21962
|
+
body: request2,
|
|
21963
|
+
context: "Create a function version",
|
|
21964
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21965
|
+
}
|
|
21966
|
+
);
|
|
21967
|
+
}
|
|
21968
|
+
/**
|
|
21969
|
+
* Uploads a version whose bundle is in R2 and makes it live. `versionId` is
|
|
21970
|
+
* `CreatedFunctionVersion.version.id`. The refusals before anything is
|
|
21971
|
+
* uploaded leave the function as it was: `FUNCTION_NOT_FOUND`,
|
|
21972
|
+
* `VERSION_NOT_FOUND`, `VERSION_NOT_DEPLOYABLE` (pruned),
|
|
21973
|
+
* `BACKEND_NOT_READY`, `BACKEND_OFF`, `FUNCTION_BUSY`, `BUNDLE_MISSING`,
|
|
21974
|
+
* `BUNDLE_MISMATCH`. Then `FUNCTION_DEPLOY_FAILED` (Cloudflare refused or
|
|
21975
|
+
* could not be reached; the version that was live still runs),
|
|
21976
|
+
* `FUNCTIONS_RATE_LIMITED` (503, `retry-after`),
|
|
21977
|
+
* `FUNCTION_DEPLOY_INCOMPLETE` (the code runs but the function list did
|
|
21978
|
+
* not take it, so callers may still get the previous `verifyJwt` setting
|
|
21979
|
+
* or 404 — deploy again) and `BACKEND_OUTDATED` (the project's
|
|
21980
|
+
* database predates the function list — `backend.enable` updates it).
|
|
21981
|
+
* Two minutes.
|
|
21982
|
+
*/
|
|
21983
|
+
async deploy(projectId, name, versionId) {
|
|
21984
|
+
const body = await this.client.request(
|
|
21985
|
+
this.functionPath(
|
|
21986
|
+
projectId,
|
|
21987
|
+
name,
|
|
21988
|
+
`/versions/${this.segment(versionId, "version id")}/deploy`
|
|
21989
|
+
),
|
|
21990
|
+
{
|
|
21991
|
+
method: "POST",
|
|
21992
|
+
context: "Deploy a function",
|
|
21993
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21994
|
+
}
|
|
21995
|
+
);
|
|
21996
|
+
return body.function;
|
|
21997
|
+
}
|
|
21998
|
+
/**
|
|
21999
|
+
* Puts a stored version live again: the one numbered `version`, which must
|
|
22000
|
+
* have been live before (`VERSION_NOT_FOUND` otherwise — put a version that
|
|
22001
|
+
* never went live up with `deploy`), or without it the deployed version
|
|
22002
|
+
* before the live one. The code comes back, not the old keys or secrets:
|
|
22003
|
+
* the bindings are the project's now. The refusals are `deploy`'s, and
|
|
22004
|
+
* `VERSION_NOT_FOUND` also says there is no earlier deployed version.
|
|
22005
|
+
* Two minutes.
|
|
22006
|
+
*/
|
|
22007
|
+
async rollback(projectId, name, version2) {
|
|
22008
|
+
const body = await this.client.request(
|
|
22009
|
+
this.functionPath(projectId, name, "/rollback"),
|
|
22010
|
+
{
|
|
22011
|
+
method: "POST",
|
|
22012
|
+
body: version2 === void 0 ? {} : { version: version2 },
|
|
22013
|
+
context: "Roll back a function",
|
|
22014
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22015
|
+
}
|
|
22016
|
+
);
|
|
22017
|
+
return body.function;
|
|
22018
|
+
}
|
|
22019
|
+
/**
|
|
22020
|
+
* `verifyJwt` without an upload, live within seconds; the next deploy sets
|
|
22021
|
+
* it again from the version it puts live. `FUNCTION_NOT_FOUND`,
|
|
22022
|
+
* `FUNCTION_BUSY`, `FUNCTION_UPDATE_FAILED` (502: the function list in the
|
|
22023
|
+
* project's database could not be written — nothing changed, send it again)
|
|
22024
|
+
* and `BACKEND_OUTDATED`.
|
|
22025
|
+
*/
|
|
22026
|
+
async update(projectId, name, patch) {
|
|
22027
|
+
const body = await this.client.request(
|
|
22028
|
+
this.functionPath(projectId, name),
|
|
22029
|
+
{ method: "PATCH", body: patch, context: "Update a function" }
|
|
22030
|
+
);
|
|
22031
|
+
return body.function;
|
|
22032
|
+
}
|
|
22033
|
+
/**
|
|
22034
|
+
* Its URL stops working within seconds; its stored versions are removed by
|
|
22035
|
+
* the control plane's sweep. `FUNCTION_NOT_FOUND`, `FUNCTION_BUSY`.
|
|
22036
|
+
*/
|
|
22037
|
+
async remove(projectId, name) {
|
|
22038
|
+
await this.client.request(
|
|
22039
|
+
this.functionPath(projectId, name),
|
|
22040
|
+
{
|
|
22041
|
+
method: "DELETE",
|
|
22042
|
+
context: "Delete a function"
|
|
22043
|
+
}
|
|
22044
|
+
);
|
|
22045
|
+
}
|
|
22046
|
+
/**
|
|
22047
|
+
* The function's newest 200 log lines, optionally of one level (`log`,
|
|
22048
|
+
* `info`, `warn`, `error` or `debug`; anything else is `400
|
|
22049
|
+
* VALIDATION_FAILED`). `LOGS_DISABLED` (503) while the control plane has no
|
|
22050
|
+
* log tail.
|
|
22051
|
+
*/
|
|
22052
|
+
async logs(projectId, name, options = {}) {
|
|
22053
|
+
const query = options.level ? `?${new URLSearchParams({ level: options.level }).toString()}` : "";
|
|
22054
|
+
const body = await this.client.request(
|
|
22055
|
+
this.functionPath(projectId, name, `/logs${query}`),
|
|
22056
|
+
{ context: "Read function logs" }
|
|
22057
|
+
);
|
|
22058
|
+
return body.entries;
|
|
22059
|
+
}
|
|
22060
|
+
/** The project's function secrets: names, digests and when — never a value. `env.read`. */
|
|
22061
|
+
async secrets(projectId) {
|
|
22062
|
+
const body = await this.client.request(
|
|
22063
|
+
this.path(projectId, "-secrets"),
|
|
22064
|
+
{ context: "List function secrets" }
|
|
22065
|
+
);
|
|
22066
|
+
return body.secrets;
|
|
22067
|
+
}
|
|
22068
|
+
/**
|
|
22069
|
+
* Sets and unsets function secrets in one change (`env.write.production`;
|
|
22070
|
+
* a suspended project is refused). `VALIDATION_FAILED` names the first
|
|
22071
|
+
* problem — a name, a reserved name (`NARO_*`, `ASSETS`, `DB`) or
|
|
22072
|
+
* `__proto__`, a value over 5,120 bytes, more than 100 secrets. Live
|
|
22073
|
+
* functions carry the change once synced: `pending` says how many. The
|
|
22074
|
+
* control plane starts two at once and its five-minute sweep goes on with
|
|
22075
|
+
* the rest, 20 a run; `sync` brings them up now — call it until `pending`
|
|
22076
|
+
* is 0 to be sure, as a change racing another can leave a function only
|
|
22077
|
+
* `sync` finds (known-issues #322).
|
|
22078
|
+
*/
|
|
22079
|
+
async setSecrets(projectId, change) {
|
|
22080
|
+
return this.client.request(
|
|
22081
|
+
this.path(projectId, "-secrets"),
|
|
22082
|
+
{ method: "PUT", body: change, context: "Change function secrets" }
|
|
22083
|
+
);
|
|
22084
|
+
}
|
|
22085
|
+
/**
|
|
22086
|
+
* Uploads stale functions again — `limit` of them — and with `registry`
|
|
22087
|
+
* writes the function list in the project's database again. Call it
|
|
22088
|
+
* again while `pending` is above 0 and the call made progress.
|
|
22089
|
+
* `FUNCTIONS_RATE_LIMITED` (503) carries `details.retryAfter`. Two minutes.
|
|
22090
|
+
*/
|
|
22091
|
+
async sync(projectId, options = {}) {
|
|
22092
|
+
return this.client.request(
|
|
22093
|
+
this.path(projectId, "-sync"),
|
|
22094
|
+
{
|
|
22095
|
+
method: "POST",
|
|
22096
|
+
body: options,
|
|
22097
|
+
context: "Sync functions",
|
|
22098
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
22099
|
+
}
|
|
22100
|
+
);
|
|
22101
|
+
}
|
|
22102
|
+
};
|
|
21508
22103
|
|
|
21509
22104
|
// ../../packages/sdk/src/database.ts
|
|
21510
22105
|
var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
|
|
@@ -21527,8 +22122,8 @@ var MAX_CELL_CHARS = 60;
|
|
|
21527
22122
|
function formatCell(value) {
|
|
21528
22123
|
if (value === null || value === void 0) return "NULL";
|
|
21529
22124
|
const text2 = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
21530
|
-
const
|
|
21531
|
-
return
|
|
22125
|
+
const oneLine2 = text2.replace(/\s+/g, " ");
|
|
22126
|
+
return oneLine2.length > MAX_CELL_CHARS ? `${oneLine2.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine2;
|
|
21532
22127
|
}
|
|
21533
22128
|
function renderRows(statement2) {
|
|
21534
22129
|
if (statement2.columns.length === 0) return [];
|
|
@@ -21902,56 +22497,235 @@ function detectFramework2(dir) {
|
|
|
21902
22497
|
};
|
|
21903
22498
|
}
|
|
21904
22499
|
|
|
21905
|
-
// ../../packages/data-core/src/
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
}
|
|
21911
|
-
return false;
|
|
21912
|
-
}
|
|
21913
|
-
function hasBidiControl(text2) {
|
|
21914
|
-
for (let i = 0; i < text2.length; i += 1) {
|
|
21915
|
-
const code = text2.charCodeAt(i);
|
|
21916
|
-
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
21917
|
-
return true;
|
|
21918
|
-
}
|
|
21919
|
-
return false;
|
|
21920
|
-
}
|
|
21921
|
-
|
|
21922
|
-
// ../../packages/data-core/src/storage-path.ts
|
|
21923
|
-
var MAX_SINGLE_UPLOAD_BYTES = 52428800;
|
|
21924
|
-
var MAX_OBJECT_NAME_BYTES = 900;
|
|
21925
|
-
var STORAGE_RESERVED_WORDS = [
|
|
21926
|
-
"authenticated",
|
|
21927
|
-
"copy",
|
|
21928
|
-
"info",
|
|
21929
|
-
"list",
|
|
21930
|
-
"move",
|
|
21931
|
-
"public",
|
|
21932
|
-
"render",
|
|
21933
|
-
"sign",
|
|
21934
|
-
"upload"
|
|
22500
|
+
// ../../packages/data-core/src/system-tables.ts
|
|
22501
|
+
var SYSTEM_TABLE_PREFIXES = [
|
|
22502
|
+
"_naro_",
|
|
22503
|
+
"_cf_",
|
|
22504
|
+
"sqlite_"
|
|
21935
22505
|
];
|
|
21936
|
-
var
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
21940
|
-
function hasLoneSurrogate(text2) {
|
|
21941
|
-
return LONE_SURROGATE2.test(text2);
|
|
22506
|
+
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
22507
|
+
function asciiLower(text2) {
|
|
22508
|
+
return text2.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
21942
22509
|
}
|
|
21943
|
-
function
|
|
21944
|
-
|
|
22510
|
+
function isSystemTable(name) {
|
|
22511
|
+
const lower = asciiLower(name);
|
|
22512
|
+
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
21945
22513
|
}
|
|
21946
|
-
function
|
|
21947
|
-
|
|
21948
|
-
|
|
21949
|
-
|
|
21950
|
-
|
|
21951
|
-
|
|
21952
|
-
|
|
21953
|
-
|
|
21954
|
-
|
|
22514
|
+
function likeLiteral(text2) {
|
|
22515
|
+
return text2.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
22516
|
+
}
|
|
22517
|
+
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
22518
|
+
function systemTablePredicate(column2 = "name") {
|
|
22519
|
+
if (!COLUMN_REFERENCE.test(column2))
|
|
22520
|
+
throw new Error(`Not a column reference: ${column2}`);
|
|
22521
|
+
return [
|
|
22522
|
+
...SYSTEM_TABLE_PREFIXES.map(
|
|
22523
|
+
(prefix) => `${column2} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
22524
|
+
),
|
|
22525
|
+
...SYSTEM_TABLE_NAMES.map(
|
|
22526
|
+
(name) => `${column2} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
22527
|
+
),
|
|
22528
|
+
`${column2} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
22529
|
+
].join(" AND ");
|
|
22530
|
+
}
|
|
22531
|
+
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
22532
|
+
|
|
22533
|
+
// ../../packages/data-core/src/collation.ts
|
|
22534
|
+
var COLLATIONS = ["BINARY", "NOCASE", "RTRIM"];
|
|
22535
|
+
function isSpace(char) {
|
|
22536
|
+
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
22537
|
+
}
|
|
22538
|
+
function isWordChar(char) {
|
|
22539
|
+
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
22540
|
+
}
|
|
22541
|
+
function asciiUpper2(text2) {
|
|
22542
|
+
return text2.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22543
|
+
}
|
|
22544
|
+
function quoted2(sql, start, close, doubles) {
|
|
22545
|
+
let value = "";
|
|
22546
|
+
for (let index = start + 1; index < sql.length; index++) {
|
|
22547
|
+
const char = sql.charAt(index);
|
|
22548
|
+
if (char !== close) {
|
|
22549
|
+
value += char;
|
|
22550
|
+
continue;
|
|
22551
|
+
}
|
|
22552
|
+
if (doubles && sql.charAt(index + 1) === close) {
|
|
22553
|
+
value += close;
|
|
22554
|
+
index++;
|
|
22555
|
+
continue;
|
|
22556
|
+
}
|
|
22557
|
+
return { value, end: index + 1 };
|
|
22558
|
+
}
|
|
22559
|
+
return void 0;
|
|
22560
|
+
}
|
|
22561
|
+
function tokenize(sql) {
|
|
22562
|
+
const tokens = [];
|
|
22563
|
+
let index = 0;
|
|
22564
|
+
while (index < sql.length) {
|
|
22565
|
+
const char = sql.charAt(index);
|
|
22566
|
+
if (isSpace(char)) {
|
|
22567
|
+
index++;
|
|
22568
|
+
} else if (char === "-" && sql.charAt(index + 1) === "-") {
|
|
22569
|
+
const end = sql.indexOf("\n", index);
|
|
22570
|
+
index = end < 0 ? sql.length : end + 1;
|
|
22571
|
+
} else if (char === "/" && sql.charAt(index + 1) === "*") {
|
|
22572
|
+
const end = sql.indexOf("*/", index + 2);
|
|
22573
|
+
if (end < 0) return void 0;
|
|
22574
|
+
index = end + 2;
|
|
22575
|
+
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22576
|
+
const close = char === "[" ? "]" : char;
|
|
22577
|
+
const read = quoted2(sql, index, close, char !== "[");
|
|
22578
|
+
if (!read) return void 0;
|
|
22579
|
+
tokens.push({
|
|
22580
|
+
kind: char === "'" ? "string" : "name",
|
|
22581
|
+
value: read.value
|
|
22582
|
+
});
|
|
22583
|
+
index = read.end;
|
|
22584
|
+
} else if (char === "(" || char === ")" || char === ",") {
|
|
22585
|
+
tokens.push({ kind: "punct", value: char });
|
|
22586
|
+
index++;
|
|
22587
|
+
} else if (isWordChar(char)) {
|
|
22588
|
+
let end = index;
|
|
22589
|
+
while (end < sql.length && isWordChar(sql.charAt(end))) end++;
|
|
22590
|
+
tokens.push({ kind: "word", value: sql.slice(index, end) });
|
|
22591
|
+
index = end;
|
|
22592
|
+
} else {
|
|
22593
|
+
tokens.push({ kind: "other", value: char });
|
|
22594
|
+
index++;
|
|
22595
|
+
}
|
|
22596
|
+
}
|
|
22597
|
+
return tokens;
|
|
22598
|
+
}
|
|
22599
|
+
function isWord2(token, word) {
|
|
22600
|
+
return token?.kind === "word" && asciiLower(token.value) === word;
|
|
22601
|
+
}
|
|
22602
|
+
function definitions(tokens) {
|
|
22603
|
+
const open = tokens.findIndex(
|
|
22604
|
+
(token) => token.kind === "punct" && token.value === "("
|
|
22605
|
+
);
|
|
22606
|
+
if (open < 0) return void 0;
|
|
22607
|
+
const found = [[]];
|
|
22608
|
+
let depth = 0;
|
|
22609
|
+
for (const token of tokens.slice(open + 1)) {
|
|
22610
|
+
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22611
|
+
if (token.kind === "punct" && token.value === ")") {
|
|
22612
|
+
if (depth === 0) return found;
|
|
22613
|
+
depth--;
|
|
22614
|
+
}
|
|
22615
|
+
if (depth === 0 && token.kind === "punct" && token.value === ",") {
|
|
22616
|
+
found.push([]);
|
|
22617
|
+
continue;
|
|
22618
|
+
}
|
|
22619
|
+
found.at(-1)?.push(token);
|
|
22620
|
+
}
|
|
22621
|
+
return void 0;
|
|
22622
|
+
}
|
|
22623
|
+
function collationOf(definition) {
|
|
22624
|
+
const named = [];
|
|
22625
|
+
let depth = 0;
|
|
22626
|
+
for (const [index, token] of definition.entries()) {
|
|
22627
|
+
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22628
|
+
else if (token.kind === "punct" && token.value === ")") depth--;
|
|
22629
|
+
else if (depth === 0 && index > 0 && isWord2(token, "collate")) {
|
|
22630
|
+
const name = definition[index + 1];
|
|
22631
|
+
if (!name || name.kind === "punct" || name.kind === "other") return null;
|
|
22632
|
+
named.push(asciiUpper2(name.value));
|
|
22633
|
+
}
|
|
22634
|
+
}
|
|
22635
|
+
if (named.length === 0) return "BINARY";
|
|
22636
|
+
const [first] = named;
|
|
22637
|
+
if (named.some((name) => name !== first)) return null;
|
|
22638
|
+
return COLLATIONS.includes(first ?? "") ? first : null;
|
|
22639
|
+
}
|
|
22640
|
+
function declaresStrict(sql) {
|
|
22641
|
+
if (typeof sql !== "string") return void 0;
|
|
22642
|
+
const tokens = tokenize(sql);
|
|
22643
|
+
if (!tokens || !isWord2(tokens[0], "create")) return void 0;
|
|
22644
|
+
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22645
|
+
if (!isWord2(kind, "table")) return void 0;
|
|
22646
|
+
const open = tokens.findIndex(
|
|
22647
|
+
(token) => token.kind === "punct" && token.value === "("
|
|
22648
|
+
);
|
|
22649
|
+
if (open < 0) return void 0;
|
|
22650
|
+
let depth = 0;
|
|
22651
|
+
for (const [index, token] of tokens.entries()) {
|
|
22652
|
+
if (index < open || token.kind !== "punct") continue;
|
|
22653
|
+
if (token.value === "(") depth++;
|
|
22654
|
+
else if (token.value === ")" && --depth === 0)
|
|
22655
|
+
return tokens.slice(index + 1).some((option) => isWord2(option, "strict"));
|
|
22656
|
+
}
|
|
22657
|
+
return void 0;
|
|
22658
|
+
}
|
|
22659
|
+
function declaredCollations(sql, columns) {
|
|
22660
|
+
const unknown2 = columns.map(() => null);
|
|
22661
|
+
if (typeof sql !== "string") return unknown2;
|
|
22662
|
+
const tokens = tokenize(sql);
|
|
22663
|
+
if (!tokens || !isWord2(tokens[0], "create")) return unknown2;
|
|
22664
|
+
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22665
|
+
if (!isWord2(kind, "table")) return unknown2;
|
|
22666
|
+
const found = definitions(tokens);
|
|
22667
|
+
if (!found || found.length < columns.length) return unknown2;
|
|
22668
|
+
const collations = [];
|
|
22669
|
+
for (const [index, column2] of columns.entries()) {
|
|
22670
|
+
const definition = found[index] ?? [];
|
|
22671
|
+
const name = definition[0];
|
|
22672
|
+
if (!name || name.kind === "punct" || name.kind === "other" || asciiLower(name.value) !== asciiLower(column2))
|
|
22673
|
+
return unknown2;
|
|
22674
|
+
collations.push(collationOf(definition));
|
|
22675
|
+
}
|
|
22676
|
+
return collations;
|
|
22677
|
+
}
|
|
22678
|
+
|
|
22679
|
+
// ../../packages/data-core/src/unicode-controls.ts
|
|
22680
|
+
function hasControlCharacter(text2) {
|
|
22681
|
+
for (let i = 0; i < text2.length; i += 1) {
|
|
22682
|
+
const code = text2.charCodeAt(i);
|
|
22683
|
+
if (code <= 31 || code >= 127 && code <= 159) return true;
|
|
22684
|
+
}
|
|
22685
|
+
return false;
|
|
22686
|
+
}
|
|
22687
|
+
function hasBidiControl(text2) {
|
|
22688
|
+
for (let i = 0; i < text2.length; i += 1) {
|
|
22689
|
+
const code = text2.charCodeAt(i);
|
|
22690
|
+
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
22691
|
+
return true;
|
|
22692
|
+
}
|
|
22693
|
+
return false;
|
|
22694
|
+
}
|
|
22695
|
+
|
|
22696
|
+
// ../../packages/data-core/src/storage-path.ts
|
|
22697
|
+
var MAX_SINGLE_UPLOAD_BYTES = 52428800;
|
|
22698
|
+
var MAX_OBJECT_NAME_BYTES = 900;
|
|
22699
|
+
var STORAGE_RESERVED_WORDS = [
|
|
22700
|
+
"authenticated",
|
|
22701
|
+
"copy",
|
|
22702
|
+
"info",
|
|
22703
|
+
"list",
|
|
22704
|
+
"move",
|
|
22705
|
+
"public",
|
|
22706
|
+
"render",
|
|
22707
|
+
"sign",
|
|
22708
|
+
"upload"
|
|
22709
|
+
];
|
|
22710
|
+
var RESERVED = new Set(STORAGE_RESERVED_WORDS);
|
|
22711
|
+
var ENCODER = new TextEncoder();
|
|
22712
|
+
var LONE_SURROGATE2 = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
22713
|
+
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
22714
|
+
function hasLoneSurrogate(text2) {
|
|
22715
|
+
return LONE_SURROGATE2.test(text2);
|
|
22716
|
+
}
|
|
22717
|
+
function refuse(reason) {
|
|
22718
|
+
return { ok: false, reason };
|
|
22719
|
+
}
|
|
22720
|
+
function checkWellFormed(name) {
|
|
22721
|
+
if (name === "") return refuse("is empty");
|
|
22722
|
+
if (name.length > MAX_OBJECT_NAME_BYTES || ENCODER.encode(name).length > MAX_OBJECT_NAME_BYTES)
|
|
22723
|
+
return refuse(`is longer than ${String(MAX_OBJECT_NAME_BYTES)} bytes`);
|
|
22724
|
+
if (name.startsWith("/") || name.endsWith("/"))
|
|
22725
|
+
return refuse("starts or ends with /");
|
|
22726
|
+
if (name.includes("//")) return refuse("has an empty segment (//)");
|
|
22727
|
+
for (const segment of name.split("/"))
|
|
22728
|
+
if (segment === "." || segment === "..")
|
|
21955
22729
|
return refuse("has a . or .. segment");
|
|
21956
22730
|
if (hasControlCharacter(name)) return refuse("has a control character");
|
|
21957
22731
|
if (name.includes("\\")) return refuse("has a backslash");
|
|
@@ -22046,39 +22820,6 @@ var STORAGE_BUCKET_DELETING_COLUMN = {
|
|
|
22046
22820
|
definition: "TEXT"
|
|
22047
22821
|
};
|
|
22048
22822
|
|
|
22049
|
-
// ../../packages/data-core/src/system-tables.ts
|
|
22050
|
-
var SYSTEM_TABLE_PREFIXES = [
|
|
22051
|
-
"_naro_",
|
|
22052
|
-
"_cf_",
|
|
22053
|
-
"sqlite_"
|
|
22054
|
-
];
|
|
22055
|
-
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
22056
|
-
function asciiLower(text2) {
|
|
22057
|
-
return text2.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
22058
|
-
}
|
|
22059
|
-
function isSystemTable(name) {
|
|
22060
|
-
const lower = asciiLower(name);
|
|
22061
|
-
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
22062
|
-
}
|
|
22063
|
-
function likeLiteral(text2) {
|
|
22064
|
-
return text2.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
22065
|
-
}
|
|
22066
|
-
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
22067
|
-
function systemTablePredicate(column2 = "name") {
|
|
22068
|
-
if (!COLUMN_REFERENCE.test(column2))
|
|
22069
|
-
throw new Error(`Not a column reference: ${column2}`);
|
|
22070
|
-
return [
|
|
22071
|
-
...SYSTEM_TABLE_PREFIXES.map(
|
|
22072
|
-
(prefix) => `${column2} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
22073
|
-
),
|
|
22074
|
-
...SYSTEM_TABLE_NAMES.map(
|
|
22075
|
-
(name) => `${column2} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
22076
|
-
),
|
|
22077
|
-
`${column2} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
22078
|
-
].join(" AND ");
|
|
22079
|
-
}
|
|
22080
|
-
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
22081
|
-
|
|
22082
22823
|
// ../../packages/data-core/src/errors.ts
|
|
22083
22824
|
var DATABASE_FULL_MESSAGE = "The database has reached D1's 10 GB limit, which cannot be raised. Delete data to free space; writes fail until it is under the limit.";
|
|
22084
22825
|
var STORAGE_MIGRATION_PENDING_MESSAGE = "The project backend is partway through an update. Run `naro backend enable` to finish it, then try again.";
|
|
@@ -22356,7 +23097,7 @@ var OPERATORS = [
|
|
|
22356
23097
|
"*",
|
|
22357
23098
|
"-"
|
|
22358
23099
|
];
|
|
22359
|
-
function
|
|
23100
|
+
function isSpace2(char) {
|
|
22360
23101
|
return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f";
|
|
22361
23102
|
}
|
|
22362
23103
|
function isIdentStart(char) {
|
|
@@ -22369,7 +23110,7 @@ function isDigit(char) {
|
|
|
22369
23110
|
return char >= "0" && char <= "9";
|
|
22370
23111
|
}
|
|
22371
23112
|
var NUMBER = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
|
22372
|
-
function
|
|
23113
|
+
function tokenize2(text2) {
|
|
22373
23114
|
const tokens = [];
|
|
22374
23115
|
let index = 0;
|
|
22375
23116
|
const at = (offset = 0) => text2.charAt(index + offset);
|
|
@@ -22378,7 +23119,7 @@ function tokenize(text2) {
|
|
|
22378
23119
|
const position = index + 1;
|
|
22379
23120
|
if (char === "\0")
|
|
22380
23121
|
throw new PolicySyntaxError("NUL characters are not allowed", position);
|
|
22381
|
-
if (
|
|
23122
|
+
if (isSpace2(char)) {
|
|
22382
23123
|
index++;
|
|
22383
23124
|
continue;
|
|
22384
23125
|
}
|
|
@@ -22563,133 +23304,6 @@ var TokenStream = class {
|
|
|
22563
23304
|
}
|
|
22564
23305
|
};
|
|
22565
23306
|
|
|
22566
|
-
// ../../packages/data-core/src/collation.ts
|
|
22567
|
-
var COLLATIONS = ["BINARY", "NOCASE", "RTRIM"];
|
|
22568
|
-
function isSpace2(char) {
|
|
22569
|
-
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
22570
|
-
}
|
|
22571
|
-
function isWordChar(char) {
|
|
22572
|
-
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
22573
|
-
}
|
|
22574
|
-
function asciiUpper2(text2) {
|
|
22575
|
-
return text2.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22576
|
-
}
|
|
22577
|
-
function quoted(sql, start, close, doubles) {
|
|
22578
|
-
let value = "";
|
|
22579
|
-
for (let index = start + 1; index < sql.length; index++) {
|
|
22580
|
-
const char = sql.charAt(index);
|
|
22581
|
-
if (char !== close) {
|
|
22582
|
-
value += char;
|
|
22583
|
-
continue;
|
|
22584
|
-
}
|
|
22585
|
-
if (doubles && sql.charAt(index + 1) === close) {
|
|
22586
|
-
value += close;
|
|
22587
|
-
index++;
|
|
22588
|
-
continue;
|
|
22589
|
-
}
|
|
22590
|
-
return { value, end: index + 1 };
|
|
22591
|
-
}
|
|
22592
|
-
return void 0;
|
|
22593
|
-
}
|
|
22594
|
-
function tokenize2(sql) {
|
|
22595
|
-
const tokens = [];
|
|
22596
|
-
let index = 0;
|
|
22597
|
-
while (index < sql.length) {
|
|
22598
|
-
const char = sql.charAt(index);
|
|
22599
|
-
if (isSpace2(char)) {
|
|
22600
|
-
index++;
|
|
22601
|
-
} else if (char === "-" && sql.charAt(index + 1) === "-") {
|
|
22602
|
-
const end = sql.indexOf("\n", index);
|
|
22603
|
-
index = end < 0 ? sql.length : end + 1;
|
|
22604
|
-
} else if (char === "/" && sql.charAt(index + 1) === "*") {
|
|
22605
|
-
const end = sql.indexOf("*/", index + 2);
|
|
22606
|
-
if (end < 0) return void 0;
|
|
22607
|
-
index = end + 2;
|
|
22608
|
-
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22609
|
-
const close = char === "[" ? "]" : char;
|
|
22610
|
-
const read = quoted(sql, index, close, char !== "[");
|
|
22611
|
-
if (!read) return void 0;
|
|
22612
|
-
tokens.push({
|
|
22613
|
-
kind: char === "'" ? "string" : "name",
|
|
22614
|
-
value: read.value
|
|
22615
|
-
});
|
|
22616
|
-
index = read.end;
|
|
22617
|
-
} else if (char === "(" || char === ")" || char === ",") {
|
|
22618
|
-
tokens.push({ kind: "punct", value: char });
|
|
22619
|
-
index++;
|
|
22620
|
-
} else if (isWordChar(char)) {
|
|
22621
|
-
let end = index;
|
|
22622
|
-
while (end < sql.length && isWordChar(sql.charAt(end))) end++;
|
|
22623
|
-
tokens.push({ kind: "word", value: sql.slice(index, end) });
|
|
22624
|
-
index = end;
|
|
22625
|
-
} else {
|
|
22626
|
-
tokens.push({ kind: "other", value: char });
|
|
22627
|
-
index++;
|
|
22628
|
-
}
|
|
22629
|
-
}
|
|
22630
|
-
return tokens;
|
|
22631
|
-
}
|
|
22632
|
-
function isWord2(token, word) {
|
|
22633
|
-
return token?.kind === "word" && asciiLower(token.value) === word;
|
|
22634
|
-
}
|
|
22635
|
-
function definitions(tokens) {
|
|
22636
|
-
const open = tokens.findIndex(
|
|
22637
|
-
(token) => token.kind === "punct" && token.value === "("
|
|
22638
|
-
);
|
|
22639
|
-
if (open < 0) return void 0;
|
|
22640
|
-
const found = [[]];
|
|
22641
|
-
let depth = 0;
|
|
22642
|
-
for (const token of tokens.slice(open + 1)) {
|
|
22643
|
-
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22644
|
-
if (token.kind === "punct" && token.value === ")") {
|
|
22645
|
-
if (depth === 0) return found;
|
|
22646
|
-
depth--;
|
|
22647
|
-
}
|
|
22648
|
-
if (depth === 0 && token.kind === "punct" && token.value === ",") {
|
|
22649
|
-
found.push([]);
|
|
22650
|
-
continue;
|
|
22651
|
-
}
|
|
22652
|
-
found.at(-1)?.push(token);
|
|
22653
|
-
}
|
|
22654
|
-
return void 0;
|
|
22655
|
-
}
|
|
22656
|
-
function collationOf(definition) {
|
|
22657
|
-
const named = [];
|
|
22658
|
-
let depth = 0;
|
|
22659
|
-
for (const [index, token] of definition.entries()) {
|
|
22660
|
-
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22661
|
-
else if (token.kind === "punct" && token.value === ")") depth--;
|
|
22662
|
-
else if (depth === 0 && index > 0 && isWord2(token, "collate")) {
|
|
22663
|
-
const name = definition[index + 1];
|
|
22664
|
-
if (!name || name.kind === "punct" || name.kind === "other") return null;
|
|
22665
|
-
named.push(asciiUpper2(name.value));
|
|
22666
|
-
}
|
|
22667
|
-
}
|
|
22668
|
-
if (named.length === 0) return "BINARY";
|
|
22669
|
-
const [first] = named;
|
|
22670
|
-
if (named.some((name) => name !== first)) return null;
|
|
22671
|
-
return COLLATIONS.includes(first ?? "") ? first : null;
|
|
22672
|
-
}
|
|
22673
|
-
function declaredCollations(sql, columns) {
|
|
22674
|
-
const unknown2 = columns.map(() => null);
|
|
22675
|
-
if (typeof sql !== "string") return unknown2;
|
|
22676
|
-
const tokens = tokenize2(sql);
|
|
22677
|
-
if (!tokens || !isWord2(tokens[0], "create")) return unknown2;
|
|
22678
|
-
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22679
|
-
if (!isWord2(kind, "table")) return unknown2;
|
|
22680
|
-
const found = definitions(tokens);
|
|
22681
|
-
if (!found || found.length < columns.length) return unknown2;
|
|
22682
|
-
const collations = [];
|
|
22683
|
-
for (const [index, column2] of columns.entries()) {
|
|
22684
|
-
const definition = found[index] ?? [];
|
|
22685
|
-
const name = definition[0];
|
|
22686
|
-
if (!name || name.kind === "punct" || name.kind === "other" || asciiLower(name.value) !== asciiLower(column2))
|
|
22687
|
-
return unknown2;
|
|
22688
|
-
collations.push(collationOf(definition));
|
|
22689
|
-
}
|
|
22690
|
-
return collations;
|
|
22691
|
-
}
|
|
22692
|
-
|
|
22693
23307
|
// ../../packages/data-core/src/realtime-table.ts
|
|
22694
23308
|
var REALTIME_MESSAGES = "realtime.messages";
|
|
22695
23309
|
var REALTIME_MESSAGES_NAMES = ["messages", REALTIME_MESSAGES];
|
|
@@ -22767,6 +23381,9 @@ function columnAffinity(column2) {
|
|
|
22767
23381
|
return "REAL";
|
|
22768
23382
|
return "NUMERIC";
|
|
22769
23383
|
}
|
|
23384
|
+
function isAnyType(type) {
|
|
23385
|
+
return asciiLower(type.trim()) === "any";
|
|
23386
|
+
}
|
|
22770
23387
|
function numberClass(type) {
|
|
22771
23388
|
const affinity2 = columnAffinity({ type });
|
|
22772
23389
|
if (affinity2 === "TEXT" || affinity2 === "BLOB") return void 0;
|
|
@@ -22968,6 +23585,9 @@ function utf8ByteLength(text2) {
|
|
|
22968
23585
|
function quoteIdentifier(name) {
|
|
22969
23586
|
return `"${name.replaceAll('"', '""')}"`;
|
|
22970
23587
|
}
|
|
23588
|
+
function qualifiedColumn(table, column2) {
|
|
23589
|
+
return `${quoteIdentifier(table)}.${quoteIdentifier(column2)}`;
|
|
23590
|
+
}
|
|
22971
23591
|
var SIDE_BY_SIDE = 8;
|
|
22972
23592
|
function joinConditions(terms, op) {
|
|
22973
23593
|
if (terms.length <= SIDE_BY_SIDE) return `(${terms.join(` ${op} `)})`;
|
|
@@ -23059,6 +23679,11 @@ function refusesText(item, text2) {
|
|
|
23059
23679
|
function readsAsIs(item, value) {
|
|
23060
23680
|
return typeof value === "string" && numberClass(item.type) === "fallback" && !storesAsNumber(value);
|
|
23061
23681
|
}
|
|
23682
|
+
function heldAsInteger(item, value) {
|
|
23683
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) return false;
|
|
23684
|
+
const affinity2 = columnAffinity(item);
|
|
23685
|
+
return affinity2 === "INTEGER" || affinity2 === "NUMERIC";
|
|
23686
|
+
}
|
|
23062
23687
|
function asStored(item, sql) {
|
|
23063
23688
|
switch (columnAffinity(item)) {
|
|
23064
23689
|
case "INTEGER":
|
|
@@ -23125,6 +23750,9 @@ function checkMarker(action, nonce, restrictive) {
|
|
|
23125
23750
|
function schemaMarker(nonce) {
|
|
23126
23751
|
return `json_extract('{}', 'naro:schema:${nonce}')`;
|
|
23127
23752
|
}
|
|
23753
|
+
function captureMarker(nonce) {
|
|
23754
|
+
return `json_extract('{}', 'naro:capture:${nonce}')`;
|
|
23755
|
+
}
|
|
23128
23756
|
|
|
23129
23757
|
// ../../packages/data-core/src/policy-expr.ts
|
|
23130
23758
|
var MAX_EXPRESSION_BYTES = 3 * 16384;
|
|
@@ -24036,9 +24664,9 @@ function readCondition(stream) {
|
|
|
24036
24664
|
const token = stream.peek();
|
|
24037
24665
|
const state = newState(stream);
|
|
24038
24666
|
const expr = readOr(state);
|
|
24039
|
-
const
|
|
24040
|
-
if (
|
|
24041
|
-
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM,
|
|
24667
|
+
const bare2 = state.foldernames.find(({ node }) => node.index === null);
|
|
24668
|
+
if (bare2)
|
|
24669
|
+
throw new PolicySyntaxError(FOLDERNAME_INDEX_PROBLEM, bare2.token.position);
|
|
24042
24670
|
return requireCondition(expr, token);
|
|
24043
24671
|
}
|
|
24044
24672
|
function checkExpressionSize(expr, position) {
|
|
@@ -24052,7 +24680,7 @@ function checkExpressionSize(expr, position) {
|
|
|
24052
24680
|
}
|
|
24053
24681
|
function parseExpression(text2) {
|
|
24054
24682
|
try {
|
|
24055
|
-
const stream = new TokenStream(
|
|
24683
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
24056
24684
|
if (stream.peek().kind === "eof")
|
|
24057
24685
|
throw new PolicySyntaxError("the expression is empty", 1);
|
|
24058
24686
|
const expr = readCondition(stream);
|
|
@@ -24446,8 +25074,8 @@ function policiesOn(policies, table) {
|
|
|
24446
25074
|
const folded = asciiLower(table);
|
|
24447
25075
|
return policies.filter((policy) => asciiLower(policy.table) === folded);
|
|
24448
25076
|
}
|
|
24449
|
-
function foldPolicyName(name,
|
|
24450
|
-
return
|
|
25077
|
+
function foldPolicyName(name, quoted3) {
|
|
25078
|
+
return quoted3 ? name : asciiLower(name);
|
|
24451
25079
|
}
|
|
24452
25080
|
function sqlStringLiteral(text2) {
|
|
24453
25081
|
return text2 === null ? "NULL" : quoteString(text2);
|
|
@@ -25064,8 +25692,36 @@ function policyInvalid(policy, action) {
|
|
|
25064
25692
|
policy: policy.name
|
|
25065
25693
|
});
|
|
25066
25694
|
}
|
|
25695
|
+
var ROWID_NAMES = /* @__PURE__ */ new Set(["rowid", "oid", "_rowid_"]);
|
|
25696
|
+
function isRowidName(name) {
|
|
25697
|
+
return ROWID_NAMES.has(asciiLower(name));
|
|
25698
|
+
}
|
|
25699
|
+
function noteRowidColumn(state, table, column2) {
|
|
25700
|
+
const seen = state.rowidColumns.some(
|
|
25701
|
+
(item) => asciiLower(item.table) === asciiLower(table) && asciiLower(item.column) === asciiLower(column2)
|
|
25702
|
+
);
|
|
25703
|
+
if (!seen) state.rowidColumns.push({ table, column: column2 });
|
|
25704
|
+
}
|
|
25705
|
+
function rowidColumnsGuard(columns) {
|
|
25706
|
+
if (columns.length === 0) return null;
|
|
25707
|
+
const terms = columns.map(
|
|
25708
|
+
(item) => `EXISTS (SELECT 1 FROM pragma_table_xinfo(${quoteString(item.table)}) WHERE name = ${quoteString(item.column)} COLLATE NOCASE)`
|
|
25709
|
+
);
|
|
25710
|
+
return {
|
|
25711
|
+
sql: terms.length === 1 ? terms[0] ?? "" : joinConditions(terms, "AND"),
|
|
25712
|
+
params: []
|
|
25713
|
+
};
|
|
25714
|
+
}
|
|
25715
|
+
function rowidGuarded(columns, fragment) {
|
|
25716
|
+
const guard = rowidColumnsGuard(columns);
|
|
25717
|
+
if (!guard) return fragment;
|
|
25718
|
+
return {
|
|
25719
|
+
sql: `(CASE WHEN ${guard.sql} THEN ${fragment.sql} ELSE 0 END)`,
|
|
25720
|
+
params: [...guard.params, ...fragment.params]
|
|
25721
|
+
};
|
|
25722
|
+
}
|
|
25067
25723
|
function newState2() {
|
|
25068
|
-
return { aliases: 0, subqueries: 0, depth: 0, active: [] };
|
|
25724
|
+
return { aliases: 0, subqueries: 0, depth: 0, active: [], rowidColumns: [] };
|
|
25069
25725
|
}
|
|
25070
25726
|
function bind(env, value) {
|
|
25071
25727
|
env.params.push(value);
|
|
@@ -25075,10 +25731,15 @@ function blamed(env, reason) {
|
|
|
25075
25731
|
return fail(policyInvalid(env.blame.policy, env.blame.action), reason);
|
|
25076
25732
|
}
|
|
25077
25733
|
function storedSource(table, names, alias) {
|
|
25734
|
+
const qualifier = alias ?? table.name;
|
|
25078
25735
|
return {
|
|
25079
25736
|
table,
|
|
25080
25737
|
names,
|
|
25081
|
-
read: (column2,
|
|
25738
|
+
read: (column2, env) => {
|
|
25739
|
+
if (isRowidName(column2.name))
|
|
25740
|
+
noteRowidColumn(env.state, table.name, column2.name);
|
|
25741
|
+
return qualifiedColumn(qualifier, column2.name);
|
|
25742
|
+
},
|
|
25082
25743
|
operand: (column2) => ({
|
|
25083
25744
|
stable: true,
|
|
25084
25745
|
newColumnish: true,
|
|
@@ -25147,7 +25808,7 @@ function jwtValue(env, expr) {
|
|
|
25147
25808
|
if (expr.op === "->") return bind(env, JSON.stringify(value));
|
|
25148
25809
|
return bind(env, typeof value === "boolean" ? String(value) : value);
|
|
25149
25810
|
}
|
|
25150
|
-
function valueSql(env, expr, textual = null) {
|
|
25811
|
+
function valueSql(env, expr, textual = null, exact = false, ordered = false) {
|
|
25151
25812
|
switch (expr.kind) {
|
|
25152
25813
|
case "literal":
|
|
25153
25814
|
if (expr.value === null) return "NULL";
|
|
@@ -25157,8 +25818,9 @@ function valueSql(env, expr, textual = null) {
|
|
|
25157
25818
|
case "realtime": {
|
|
25158
25819
|
const { source, column: column2 } = columnOf(env, expr);
|
|
25159
25820
|
return source.read(column2, env, {
|
|
25160
|
-
|
|
25161
|
-
textual
|
|
25821
|
+
textual,
|
|
25822
|
+
exact: textual === null && exact,
|
|
25823
|
+
...textual === null && ordered ? { ordered: true } : {}
|
|
25162
25824
|
});
|
|
25163
25825
|
}
|
|
25164
25826
|
case "auth":
|
|
@@ -25169,7 +25831,10 @@ function valueSql(env, expr, textual = null) {
|
|
|
25169
25831
|
case "func": {
|
|
25170
25832
|
if (expr.name === "now") return "CURRENT_TIMESTAMP";
|
|
25171
25833
|
if (expr.name === "coalesce") {
|
|
25172
|
-
const
|
|
25834
|
+
const one = expr.args.length === 1;
|
|
25835
|
+
const args = expr.args.map(
|
|
25836
|
+
(arg2) => valueSql(env, arg2, textual, exact, one && ordered)
|
|
25837
|
+
);
|
|
25173
25838
|
return args.length === 1 ? `(${String(args[0])})` : `coalesce(${args.join(", ")})`;
|
|
25174
25839
|
}
|
|
25175
25840
|
const [arg] = expr.args;
|
|
@@ -25193,7 +25858,7 @@ function columnOf(env, expr) {
|
|
|
25193
25858
|
return expr.kind === "column" ? resolveColumn(env, expr) : realtimeTopic(env);
|
|
25194
25859
|
}
|
|
25195
25860
|
function pathArray(operand) {
|
|
25196
|
-
return `('[
|
|
25861
|
+
return `('[' || replace(json_quote(${operand()} || ''), '/', '","') || ']')`;
|
|
25197
25862
|
}
|
|
25198
25863
|
function storageHelperSql(fn, index, operand) {
|
|
25199
25864
|
switch (fn) {
|
|
@@ -25224,10 +25889,7 @@ function storageCallSql(env, expr) {
|
|
|
25224
25889
|
return storageHelperSql(
|
|
25225
25890
|
expr.fn,
|
|
25226
25891
|
index,
|
|
25227
|
-
() => source.read(column2, env, {
|
|
25228
|
-
qualified: env.scopes.length > 1,
|
|
25229
|
-
textual: "textual"
|
|
25230
|
-
})
|
|
25892
|
+
() => source.read(column2, env, { textual: "textual" })
|
|
25231
25893
|
);
|
|
25232
25894
|
}
|
|
25233
25895
|
function operandSide(env, value) {
|
|
@@ -25265,10 +25927,73 @@ var ARRAY_OPERATORS3 = {
|
|
|
25265
25927
|
"<@": "containedBy",
|
|
25266
25928
|
"&&": "overlaps"
|
|
25267
25929
|
};
|
|
25930
|
+
function readsBack(env) {
|
|
25931
|
+
return env.scopes.some((scope) => scope.readBack === true);
|
|
25932
|
+
}
|
|
25933
|
+
function textAffinity(env, value) {
|
|
25934
|
+
const expr = comparedValue(value);
|
|
25935
|
+
if (expr.kind === "cast") return true;
|
|
25936
|
+
if (expr.kind !== "column" && expr.kind !== "realtime") return false;
|
|
25937
|
+
try {
|
|
25938
|
+
return columnAffinity(columnOf(env, expr).column) === "TEXT";
|
|
25939
|
+
} catch {
|
|
25940
|
+
return true;
|
|
25941
|
+
}
|
|
25942
|
+
}
|
|
25943
|
+
function selectedTextAffinity(env, subquery) {
|
|
25944
|
+
if (subquery.select === "*") return true;
|
|
25945
|
+
const table = resolveSubqueryTable(env.snapshot, subquery.table);
|
|
25946
|
+
if (!table) return true;
|
|
25947
|
+
const source = storedSource(
|
|
25948
|
+
table,
|
|
25949
|
+
[asciiLower(subqueryName(subquery))],
|
|
25950
|
+
null
|
|
25951
|
+
);
|
|
25952
|
+
return textAffinity(
|
|
25953
|
+
{ ...env, scopes: [...env.scopes, source] },
|
|
25954
|
+
subquery.select
|
|
25955
|
+
);
|
|
25956
|
+
}
|
|
25957
|
+
var ORDERING = /* @__PURE__ */ new Set(["<", "<=", ">", ">="]);
|
|
25958
|
+
function orderSafe(env, value) {
|
|
25959
|
+
const expr = comparedValue(value);
|
|
25960
|
+
switch (expr.kind) {
|
|
25961
|
+
case "literal":
|
|
25962
|
+
return typeof expr.value !== "string" || !storesAsNumber(expr.value);
|
|
25963
|
+
case "func":
|
|
25964
|
+
return expr.name === "now";
|
|
25965
|
+
case "column": {
|
|
25966
|
+
let column2;
|
|
25967
|
+
try {
|
|
25968
|
+
column2 = columnOf(env, expr).column;
|
|
25969
|
+
} catch {
|
|
25970
|
+
return false;
|
|
25971
|
+
}
|
|
25972
|
+
const kind = numberClass(column2.type);
|
|
25973
|
+
return kind === "number" || kind === "fallback" && !isAnyType(column2.type);
|
|
25974
|
+
}
|
|
25975
|
+
default:
|
|
25976
|
+
return false;
|
|
25977
|
+
}
|
|
25978
|
+
}
|
|
25268
25979
|
function comparison(env, left, op, right) {
|
|
25269
|
-
const
|
|
25980
|
+
const back = readsBack(env);
|
|
25981
|
+
const ordered = back && ORDERING.has(op);
|
|
25982
|
+
const l = valueSql(
|
|
25983
|
+
env,
|
|
25984
|
+
left,
|
|
25985
|
+
null,
|
|
25986
|
+
back && !textAffinity(env, right),
|
|
25987
|
+
ordered && !orderSafe(env, right)
|
|
25988
|
+
);
|
|
25270
25989
|
const collate = env.check ? checkCollation(env, operandSide(env, left), operandSide(env, right)) : "";
|
|
25271
|
-
const r = valueSql(
|
|
25990
|
+
const r = valueSql(
|
|
25991
|
+
env,
|
|
25992
|
+
right,
|
|
25993
|
+
null,
|
|
25994
|
+
back && !textAffinity(env, left),
|
|
25995
|
+
ordered && !orderSafe(env, left)
|
|
25996
|
+
);
|
|
25272
25997
|
return `(${l}${collate} ${op} ${r})`;
|
|
25273
25998
|
}
|
|
25274
25999
|
function knownNullTest(env, expr) {
|
|
@@ -25313,22 +26038,36 @@ function conditionSql(env, expr) {
|
|
|
25313
26038
|
if (untestable !== void 0) return blamed(env, untestable);
|
|
25314
26039
|
const known = knownNullTest(env, expr);
|
|
25315
26040
|
if (known !== void 0) return known;
|
|
25316
|
-
const target2 = valueSql(env, expr.operand);
|
|
26041
|
+
const target2 = valueSql(env, expr.operand, null, true);
|
|
25317
26042
|
if (expr.value === null)
|
|
25318
26043
|
return `(${target2} IS ${expr.not ? "NOT " : ""}NULL)`;
|
|
25319
26044
|
const test = isBoolean(target2, expr.value);
|
|
25320
26045
|
return expr.not ? `(NOT (${test}))` : `(${test})`;
|
|
25321
26046
|
}
|
|
25322
26047
|
case "in": {
|
|
25323
|
-
const left = valueSql(env, expr.operand);
|
|
26048
|
+
const left = valueSql(env, expr.operand, null, true);
|
|
25324
26049
|
const collate = env.check ? checkCollation(env, operandSide(env, expr.operand), null) : "";
|
|
25325
|
-
const
|
|
26050
|
+
const itemsExact = readsBack(env) && !textAffinity(env, expr.operand);
|
|
26051
|
+
const list = expr.list.map(
|
|
26052
|
+
(item) => valueSql(env, item, null, itemsExact)
|
|
26053
|
+
);
|
|
25326
26054
|
return `(${left}${collate} ${expr.not ? "NOT " : ""}IN (${list.join(", ")}))`;
|
|
25327
26055
|
}
|
|
25328
26056
|
case "inSelect": {
|
|
25329
|
-
const
|
|
26057
|
+
const back = readsBack(env);
|
|
26058
|
+
const left = valueSql(
|
|
26059
|
+
env,
|
|
26060
|
+
expr.operand,
|
|
26061
|
+
null,
|
|
26062
|
+
back && !selectedTextAffinity(env, expr.subquery)
|
|
26063
|
+
);
|
|
25330
26064
|
const leftSide = env.check ? operandSide(env, expr.operand) : null;
|
|
25331
|
-
const sub = subquerySql(
|
|
26065
|
+
const sub = subquerySql(
|
|
26066
|
+
env,
|
|
26067
|
+
expr.subquery,
|
|
26068
|
+
"value",
|
|
26069
|
+
back && !textAffinity(env, expr.operand)
|
|
26070
|
+
);
|
|
25332
26071
|
const collate = leftSide && sub.item ? checkCollation(env, leftSide, sub.item) : "";
|
|
25333
26072
|
return `(${left}${collate} ${expr.not ? "NOT " : ""}IN (${sub.sql}))`;
|
|
25334
26073
|
}
|
|
@@ -25365,7 +26104,7 @@ function conditionSql(env, expr) {
|
|
|
25365
26104
|
return arraySql(
|
|
25366
26105
|
() => (() => {
|
|
25367
26106
|
const { source, column: column2 } = resolveColumn(env, expr.operand);
|
|
25368
|
-
return source.read(column2, env, {
|
|
26107
|
+
return source.read(column2, env, { textual: null });
|
|
25369
26108
|
})(),
|
|
25370
26109
|
ARRAY_OPERATORS3[expr.op],
|
|
25371
26110
|
values.length,
|
|
@@ -25377,7 +26116,7 @@ function conditionSql(env, expr) {
|
|
|
25377
26116
|
return `(EXISTS (${subquerySql(env, expr.subquery, "one").sql}))`;
|
|
25378
26117
|
}
|
|
25379
26118
|
}
|
|
25380
|
-
function subquerySql(env, subquery, select) {
|
|
26119
|
+
function subquerySql(env, subquery, select, selectExact = false) {
|
|
25381
26120
|
const table = resolveSubqueryTable(env.snapshot, subquery.table);
|
|
25382
26121
|
if (!table) return blamed(env, unresolvedSubqueryReason(subquery.table));
|
|
25383
26122
|
const { state } = env;
|
|
@@ -25402,7 +26141,7 @@ function subquerySql(env, subquery, select) {
|
|
|
25402
26141
|
if (select === "value") {
|
|
25403
26142
|
if (subquery.select === "*")
|
|
25404
26143
|
return blamed(env, "IN (SELECT \u2026) selects one value, not *");
|
|
25405
|
-
selected = valueSql(inner, subquery.select);
|
|
26144
|
+
selected = valueSql(inner, subquery.select, null, selectExact);
|
|
25406
26145
|
if (env.check) item = operandSide(inner, subquery.select);
|
|
25407
26146
|
}
|
|
25408
26147
|
const own2 = storedSource(table, ownNames(table), alias);
|
|
@@ -25755,7 +26494,9 @@ function selectCheckRefusal(path, column2, problem) {
|
|
|
25755
26494
|
generated: `is not available: the select policy reads the generated column ${column2}, which a new row cannot be checked against`,
|
|
25756
26495
|
collation: `is not available: the select policy reads ${column2}, whose collation naro cannot read, so a new row cannot be checked against it`,
|
|
25757
26496
|
pattern: `is not available: the select policy compares the number column ${column2} with like or ilike, which a new row cannot be checked against`,
|
|
25758
|
-
textual: `is not available: the select policy turns the number column ${column2} into text, which a new row cannot be checked against
|
|
26497
|
+
textual: `is not available: the select policy turns the number column ${column2} into text, which a new row cannot be checked against`,
|
|
26498
|
+
readBack: `is not available: the select policy reads ${column2}, a number in a column that declares no number type, as text, which a new row cannot be checked against`,
|
|
26499
|
+
orderedText: `is not available: the select policy orders ${column2}, text in a column of NUMERIC affinity, against what may read as a number, which a new row cannot be checked against`
|
|
25759
26500
|
}[problem];
|
|
25760
26501
|
return invalidQuery(path, reason);
|
|
25761
26502
|
}
|
|
@@ -25817,6 +26558,15 @@ function decideQuery(context, query, policies, table) {
|
|
|
25817
26558
|
var MAX_PARAMETERS = 100;
|
|
25818
26559
|
var MAX_STATEMENT_BYTES = 1e5;
|
|
25819
26560
|
var MAX_INSERT_STATEMENTS = 100;
|
|
26561
|
+
var MAX_CAPTURE_ROWS = 1e3;
|
|
26562
|
+
var MAX_CAPTURE_BYTES = 8388608;
|
|
26563
|
+
function triggersRead(table) {
|
|
26564
|
+
const name = table.name;
|
|
26565
|
+
return statement(
|
|
26566
|
+
"SELECT sql, tbl_name = ? COLLATE NOCASE AS own FROM sqlite_master WHERE type = 'trigger' AND (tbl_name = ? COLLATE NOCASE OR instr(lower(sql), lower(?)) > 0 OR instr(lower(sql), lower(?)) > 0 OR instr(lower(sql), lower(?)) > 0)",
|
|
26567
|
+
[name, name, name, name.replaceAll('"', '""'), name.replaceAll("`", "``")]
|
|
26568
|
+
);
|
|
26569
|
+
}
|
|
25820
26570
|
var OR_ABORT = "OR ABORT";
|
|
25821
26571
|
function column(table, name) {
|
|
25822
26572
|
return table.byName.get(asciiLower(name)) ?? fail(dataError("COLUMN_NOT_FOUND", { column: name, table: table.name }));
|
|
@@ -25832,7 +26582,13 @@ function columnList(table, names) {
|
|
|
25832
26582
|
}
|
|
25833
26583
|
return columns;
|
|
25834
26584
|
}
|
|
25835
|
-
function
|
|
26585
|
+
function columnSql(table, item) {
|
|
26586
|
+
return qualifiedColumn(table.name, item.name);
|
|
26587
|
+
}
|
|
26588
|
+
function outputList(table, columns) {
|
|
26589
|
+
return columns.map((item) => columnSql(table, item)).join(", ");
|
|
26590
|
+
}
|
|
26591
|
+
function columnNames(columns) {
|
|
25836
26592
|
return columns.map((item) => quoteIdentifier(item.name)).join(", ");
|
|
25837
26593
|
}
|
|
25838
26594
|
function returningClause(table, returning, action) {
|
|
@@ -25847,7 +26603,31 @@ function returningClause(table, returning, action) {
|
|
|
25847
26603
|
)
|
|
25848
26604
|
);
|
|
25849
26605
|
const columns = returning === "*" ? table.columns : columnList(table, returning);
|
|
25850
|
-
return ` RETURNING ${
|
|
26606
|
+
return ` RETURNING ${outputList(table, columns)}`;
|
|
26607
|
+
}
|
|
26608
|
+
function writeReturning(table, returning, action, capture) {
|
|
26609
|
+
const own2 = returningClause(table, returning, action);
|
|
26610
|
+
return capture ? ` RETURNING ${outputList(table, table.columns)}` : own2;
|
|
26611
|
+
}
|
|
26612
|
+
function balancedSum(terms) {
|
|
26613
|
+
if (terms.length === 0) return "0";
|
|
26614
|
+
if (terms.length === 1) return terms[0] ?? "0";
|
|
26615
|
+
const half = Math.ceil(terms.length / 2);
|
|
26616
|
+
return `(${balancedSum(terms.slice(0, half))} + ${balancedSum(terms.slice(half))})`;
|
|
26617
|
+
}
|
|
26618
|
+
function boundBytes(value) {
|
|
26619
|
+
return typeof value === "string" ? utf8ByteLength(value) : 8;
|
|
26620
|
+
}
|
|
26621
|
+
function captureGuard(table, where, params, nonce, set2) {
|
|
26622
|
+
const bytes = balancedSum(
|
|
26623
|
+
table.columns.map(
|
|
26624
|
+
(item) => set2.has(item.name) ? String(boundBytes(set2.get(item.name) ?? null)) : `coalesce(length(CAST(${qualifiedColumn(table.name, item.name)} AS BLOB)), 8)`
|
|
26625
|
+
)
|
|
26626
|
+
);
|
|
26627
|
+
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 ${bytes} AS r FROM ${quoteIdentifier(table.name)}${where} LIMIT ${String(MAX_CAPTURE_ROWS + 1)}))`,
|
|
26629
|
+
params
|
|
26630
|
+
);
|
|
25851
26631
|
}
|
|
25852
26632
|
function scalar(value, path) {
|
|
25853
26633
|
if (Array.isArray(value) || !isValue(value))
|
|
@@ -25868,7 +26648,7 @@ function arrayValues(value, path) {
|
|
|
25868
26648
|
}
|
|
25869
26649
|
function filterSql(table, filter, params, path) {
|
|
25870
26650
|
const resolved = column(table, filter.column);
|
|
25871
|
-
const target2 =
|
|
26651
|
+
const target2 = columnSql(table, resolved);
|
|
25872
26652
|
const valuePath = `${path}.value`;
|
|
25873
26653
|
switch (filter.operator) {
|
|
25874
26654
|
case "eq":
|
|
@@ -25920,10 +26700,9 @@ function filterSql(table, filter, params, path) {
|
|
|
25920
26700
|
case "containedBy":
|
|
25921
26701
|
case "overlaps": {
|
|
25922
26702
|
const values = arrayValues(filter.value, valuePath);
|
|
25923
|
-
const qualified = `${quoteIdentifier(table.name)}.${target2}`;
|
|
25924
26703
|
let next = 0;
|
|
25925
26704
|
return arraySql(
|
|
25926
|
-
() =>
|
|
26705
|
+
() => target2,
|
|
25927
26706
|
filter.operator,
|
|
25928
26707
|
values.length,
|
|
25929
26708
|
() => {
|
|
@@ -25946,8 +26725,12 @@ function nodeSql(table, node, params, path) {
|
|
|
25946
26725
|
);
|
|
25947
26726
|
return joinConditions(terms, key === "or" ? "OR" : "AND");
|
|
25948
26727
|
}
|
|
25949
|
-
function whereClause(table, filters, policy, params) {
|
|
26728
|
+
function whereClause(table, filters, policy, params, guard = null) {
|
|
25950
26729
|
const parts = [];
|
|
26730
|
+
if (guard) {
|
|
26731
|
+
parts.push(guard.sql);
|
|
26732
|
+
params.push(...guard.params);
|
|
26733
|
+
}
|
|
25951
26734
|
if (policy) {
|
|
25952
26735
|
parts.push(policy.sql);
|
|
25953
26736
|
params.push(...policy.params);
|
|
@@ -25963,7 +26746,7 @@ function orderClause(table, order) {
|
|
|
25963
26746
|
const terms = order.map((term) => {
|
|
25964
26747
|
const direction = term.ascending ? "ASC" : "DESC";
|
|
25965
26748
|
const nulls = term.nullsFirst === void 0 ? "" : term.nullsFirst ? " NULLS FIRST" : " NULLS LAST";
|
|
25966
|
-
return `${
|
|
26749
|
+
return `${columnSql(table, column(table, term.column))} ${direction}${nulls}`;
|
|
25967
26750
|
});
|
|
25968
26751
|
return ` ORDER BY ${terms.join(", ")}`;
|
|
25969
26752
|
}
|
|
@@ -26112,6 +26895,10 @@ function checkProblemReason(column2, problem) {
|
|
|
26112
26895
|
return `${column2} is a number column, which a WITH CHECK cannot compare with LIKE or ILIKE`;
|
|
26113
26896
|
case "textual":
|
|
26114
26897
|
return `${column2} is a number column, which a WITH CHECK cannot turn into text (lower, upper, length, ::text)`;
|
|
26898
|
+
case "readBack":
|
|
26899
|
+
return `${column2} holds a number in a column that declares no number type, which a WITH CHECK cannot read as text (lower, upper, length, ::text, LIKE, or beside text)`;
|
|
26900
|
+
case "orderedText":
|
|
26901
|
+
return `${column2} holds text in a column of NUMERIC affinity (a date, uuid or json type), which a WITH CHECK cannot order against anything but a number, a number column or text that reads as no number (<, <=, >, >=, BETWEEN)`;
|
|
26115
26902
|
}
|
|
26116
26903
|
}
|
|
26117
26904
|
function usingProblemReason(name, problem) {
|
|
@@ -26124,6 +26911,10 @@ function usingProblemReason(name, problem) {
|
|
|
26124
26911
|
return `${name} is a number column, which a USING judged on a row's values cannot compare with LIKE or ILIKE`;
|
|
26125
26912
|
case "textual":
|
|
26126
26913
|
return `${name} is a number column, which a USING judged on a row's values cannot turn into text (lower, upper, length, ::text)`;
|
|
26914
|
+
case "readBack":
|
|
26915
|
+
return `${name} holds a number in a column that declares no number type, which a USING judged on a row's values cannot read as text (lower, upper, length, ::text, LIKE, or beside text)`;
|
|
26916
|
+
case "orderedText":
|
|
26917
|
+
return `${name} holds text in a column of NUMERIC affinity (a date, uuid or json type), which a USING judged on a row's values cannot order against anything but a number, a number column or text that reads as no number (<, <=, >, >=, BETWEEN)`;
|
|
26127
26918
|
}
|
|
26128
26919
|
}
|
|
26129
26920
|
function isTrue(term) {
|
|
@@ -26532,6 +27323,19 @@ function schemaGuard(name, sql, kind, nonce) {
|
|
|
26532
27323
|
policy: void 0
|
|
26533
27324
|
};
|
|
26534
27325
|
}
|
|
27326
|
+
function rowidTerm(table, state, nonce, kind) {
|
|
27327
|
+
const own2 = newState2();
|
|
27328
|
+
for (const item of table.columns)
|
|
27329
|
+
if (isRowidName(item.name)) noteRowidColumn(own2, table.name, item.name);
|
|
27330
|
+
for (const read of state.rowidColumns)
|
|
27331
|
+
noteRowidColumn(own2, read.table, read.column);
|
|
27332
|
+
const guard = rowidColumnsGuard(own2.rowidColumns);
|
|
27333
|
+
return guard ? { ...guard, marker: schemaMarker(nonce), kind, policy: void 0 } : void 0;
|
|
27334
|
+
}
|
|
27335
|
+
function rowidWhere(table, state, nonce, kind) {
|
|
27336
|
+
const term = rowidTerm(table, state, nonce, kind);
|
|
27337
|
+
return term ? nestedChecks([term], { sql: "1", params: [] }) : null;
|
|
27338
|
+
}
|
|
26535
27339
|
function nestedChecks(terms, pass) {
|
|
26536
27340
|
let sql = pass.sql;
|
|
26537
27341
|
let params = pass.params;
|
|
@@ -26559,11 +27363,15 @@ function usingFragment(target2, req, snapshot, commands, alias) {
|
|
|
26559
27363
|
names: targetNames(target2),
|
|
26560
27364
|
alias
|
|
26561
27365
|
});
|
|
26562
|
-
return
|
|
27366
|
+
return rowidGuarded(
|
|
27367
|
+
rls.state.rowidColumns,
|
|
27368
|
+
rls.using(commands) ?? { sql: "1", params: [] }
|
|
27369
|
+
);
|
|
26563
27370
|
}
|
|
26564
|
-
function rowSource(target2, row, action, stored) {
|
|
27371
|
+
function rowSource(target2, row, action, stored, readBack = false) {
|
|
26565
27372
|
const { table } = target2;
|
|
26566
27373
|
const kept = new Set(stored?.columns ?? []);
|
|
27374
|
+
const strict = readBack ? declaresStrict(table.sql) : false;
|
|
26567
27375
|
const storedRow = stored ? storedSource(table, targetNames(target2), stored.alias) : void 0;
|
|
26568
27376
|
const source = {
|
|
26569
27377
|
table,
|
|
@@ -26588,14 +27396,21 @@ function rowSource(target2, row, action, stored) {
|
|
|
26588
27396
|
`the new row of ${target2.name} has no value for ${item.name}`
|
|
26589
27397
|
);
|
|
26590
27398
|
const bound = row[item.name] ?? null;
|
|
26591
|
-
|
|
27399
|
+
const anyStrict = readBack && isAnyType(item.type) ? strict : false;
|
|
27400
|
+
if (anyStrict !== true && typeof bound === "string" && refusesText(item, bound))
|
|
26592
27401
|
fail(invalidQuery(`row.${item.name}`, mustBeNumber(action)));
|
|
27402
|
+
if (readBack && typeof bound === "number" && (anyStrict === void 0 || (!isNumeric(item) || anyStrict) && how.exact !== true))
|
|
27403
|
+
check2.problem(item.name, "readBack", env.blame.policy);
|
|
27404
|
+
if (readBack && anyStrict !== true && how.ordered === true && readsAsIs(item, bound))
|
|
27405
|
+
check2.problem(item.name, "orderedText", env.blame.policy);
|
|
26593
27406
|
env.params.push(bound);
|
|
26594
|
-
|
|
27407
|
+
if (anyStrict === true || readsAsIs(item, bound)) return "?";
|
|
27408
|
+
return readBack && heldAsInteger(item, bound) ? "CAST(? AS INTEGER)" : asStored(item, "?");
|
|
26595
27409
|
},
|
|
26596
27410
|
// A stored column is the statement's own table's: SQLite compares it
|
|
26597
27411
|
// with its own collation, the snapshot's — which the guard holds to.
|
|
26598
|
-
operand: (item) => kept.has(item.name) ? { stable: true, newColumnish: true, newCollation: item.collation } : { stable: false, newColumnish: false, newCollation: null }
|
|
27412
|
+
operand: (item) => kept.has(item.name) ? { stable: true, newColumnish: true, newCollation: item.collation } : { stable: false, newColumnish: false, newCollation: null },
|
|
27413
|
+
...readBack ? { readBack } : {}
|
|
26599
27414
|
};
|
|
26600
27415
|
return source;
|
|
26601
27416
|
}
|
|
@@ -26635,7 +27450,17 @@ function checkFragment(target2, req, snapshot, command, row, nonce, stored, with
|
|
|
26635
27450
|
() => false,
|
|
26636
27451
|
() => false
|
|
26637
27452
|
);
|
|
26638
|
-
if (
|
|
27453
|
+
if (terms.length === 0) return terms;
|
|
27454
|
+
const rowid = rowidColumnsGuard(rls.state.rowidColumns);
|
|
27455
|
+
const guards = rowid ? [
|
|
27456
|
+
{
|
|
27457
|
+
...rowid,
|
|
27458
|
+
marker: schemaMarker(checked),
|
|
27459
|
+
kind: command,
|
|
27460
|
+
policy: void 0
|
|
27461
|
+
}
|
|
27462
|
+
] : [];
|
|
27463
|
+
if (!source.storedRead) return [...guards, ...terms];
|
|
26639
27464
|
const { name, sql } = target2.table;
|
|
26640
27465
|
if (sql === null) {
|
|
26641
27466
|
const first = terms.find((term) => term.policy !== void 0)?.policy;
|
|
@@ -26644,7 +27469,7 @@ function checkFragment(target2, req, snapshot, command, row, nonce, stored, with
|
|
|
26644
27469
|
`naro cannot read the schema of ${target2.name}, so a check of the stored row cannot be guarded`
|
|
26645
27470
|
);
|
|
26646
27471
|
}
|
|
26647
|
-
return [schemaGuard(name, sql, command, checked), ...terms];
|
|
27472
|
+
return [schemaGuard(name, sql, command, checked), ...guards, ...terms];
|
|
26648
27473
|
}
|
|
26649
27474
|
var Unsupported = class extends Error {
|
|
26650
27475
|
constructor(columnName, problem, policy) {
|
|
@@ -26654,9 +27479,15 @@ var Unsupported = class extends Error {
|
|
|
26654
27479
|
this.policy = policy;
|
|
26655
27480
|
}
|
|
26656
27481
|
};
|
|
26657
|
-
function policyValue(target2, req, snapshot, command, row, nonce, state = newState2()) {
|
|
27482
|
+
function policyValue(target2, req, snapshot, command, row, nonce, state = newState2(), options = {}) {
|
|
26658
27483
|
const checked = checkNonceOf(nonce, "policyValue");
|
|
26659
|
-
const source = rowSource(
|
|
27484
|
+
const source = rowSource(
|
|
27485
|
+
target2,
|
|
27486
|
+
row,
|
|
27487
|
+
"insert",
|
|
27488
|
+
void 0,
|
|
27489
|
+
options.readBack === true
|
|
27490
|
+
);
|
|
26660
27491
|
const rls = new Rls(
|
|
26661
27492
|
req,
|
|
26662
27493
|
snapshot,
|
|
@@ -26703,34 +27534,42 @@ function policyValue(target2, req, snapshot, command, row, nonce, state = newSta
|
|
|
26703
27534
|
].filter((term) => !isTrue(term));
|
|
26704
27535
|
const [only] = kept;
|
|
26705
27536
|
if (!only) return { ok: true, fragment: { sql: "1", params: [] } };
|
|
26706
|
-
|
|
27537
|
+
const read = state.rowidColumns;
|
|
27538
|
+
if (kept.length === 1)
|
|
27539
|
+
return { ok: true, fragment: rowidGuarded(read, only) };
|
|
26707
27540
|
return {
|
|
26708
27541
|
ok: true,
|
|
26709
|
-
fragment: {
|
|
27542
|
+
fragment: rowidGuarded(read, {
|
|
26710
27543
|
sql: `(${kept.map((term) => term.sql).join(" AND ")})`,
|
|
26711
27544
|
params: kept.flatMap((term) => term.params)
|
|
26712
|
-
}
|
|
27545
|
+
})
|
|
26713
27546
|
};
|
|
26714
27547
|
}
|
|
26715
|
-
function compileSelect(query, table, rls) {
|
|
27548
|
+
function compileSelect(query, table, rls, { state, nonce }) {
|
|
26716
27549
|
if (query.columns !== "*" && query.columns.length === 0)
|
|
26717
27550
|
fail(invalidQuery("columns", "must name at least one column"));
|
|
26718
27551
|
const columns = query.columns === "*" ? table.columns : columnList(table, query.columns);
|
|
26719
27552
|
const params = [];
|
|
26720
27553
|
const policy = rls?.where("select") ?? null;
|
|
26721
27554
|
if (rls && policy) rls.budget([policy]);
|
|
26722
|
-
const where = whereClause(
|
|
27555
|
+
const where = whereClause(
|
|
27556
|
+
table,
|
|
27557
|
+
query.filters ?? [],
|
|
27558
|
+
policy,
|
|
27559
|
+
params,
|
|
27560
|
+
rowidWhere(table, state, nonce, "select")
|
|
27561
|
+
);
|
|
26723
27562
|
const order = orderClause(table, query.order ?? []);
|
|
26724
27563
|
const limit = query.limit ?? MAX_LIMIT;
|
|
26725
27564
|
params.push(query.single ? Math.min(limit, 2) : limit, query.offset ?? 0);
|
|
26726
27565
|
return [
|
|
26727
27566
|
statement(
|
|
26728
|
-
`SELECT ${
|
|
27567
|
+
`SELECT ${outputList(table, columns)} FROM ${quoteIdentifier(table.name)}${where}${order} LIMIT ? OFFSET ?`,
|
|
26729
27568
|
params
|
|
26730
27569
|
)
|
|
26731
27570
|
];
|
|
26732
27571
|
}
|
|
26733
|
-
function compileInsert(query, table, rls, nonce) {
|
|
27572
|
+
function compileInsert(query, table, rls, { state, nonce }, capture) {
|
|
26734
27573
|
const uid = rls?.req.uid ?? null;
|
|
26735
27574
|
const ownerInsert = rls?.ownerOnly("insert");
|
|
26736
27575
|
const ownerRead = rls && query.returning !== void 0 ? rls.ownerOnly("select") : void 0;
|
|
@@ -26799,6 +27638,12 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26799
27638
|
]);
|
|
26800
27639
|
}
|
|
26801
27640
|
}
|
|
27641
|
+
const rowid = rowidTerm(table, state, nonce, "insert");
|
|
27642
|
+
if (rowid)
|
|
27643
|
+
where = nestedChecks(
|
|
27644
|
+
[rowid],
|
|
27645
|
+
where ?? { sql: width === 0 ? "0" : "1", params: [] }
|
|
27646
|
+
);
|
|
26802
27647
|
const checkParams = where?.params ?? [];
|
|
26803
27648
|
const room = MAX_PARAMETERS - checkParams.length;
|
|
26804
27649
|
if (width > 0 && room < width) fail(dataError("TOO_MANY_PARAMETERS"));
|
|
@@ -26815,7 +27660,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26815
27660
|
)
|
|
26816
27661
|
);
|
|
26817
27662
|
const into = `INSERT ${OR_ABORT} INTO ${quoteIdentifier(table.name)}`;
|
|
26818
|
-
const returning =
|
|
27663
|
+
const returning = writeReturning(table, query.returning, "insert", capture);
|
|
26819
27664
|
if (rls) {
|
|
26820
27665
|
for (const { group } of plans)
|
|
26821
27666
|
for (const [row, values] of group.rows.entries())
|
|
@@ -26841,7 +27686,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26841
27686
|
continue;
|
|
26842
27687
|
}
|
|
26843
27688
|
const tuple2 = `(${group.columns.map(() => "?").join(", ")})`;
|
|
26844
|
-
const names =
|
|
27689
|
+
const names = columnNames(group.columns);
|
|
26845
27690
|
for (let start = 0; start < group.rows.length; start += perStatement) {
|
|
26846
27691
|
const chunk = group.rows.slice(start, start + perStatement);
|
|
26847
27692
|
const values = chunk.map(() => tuple2).join(", ");
|
|
@@ -26858,7 +27703,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26858
27703
|
}
|
|
26859
27704
|
return statements;
|
|
26860
27705
|
}
|
|
26861
|
-
function compileUpdate(query, table, context, rls, nonce) {
|
|
27706
|
+
function compileUpdate(query, table, context, rls, { state, nonce }, capture) {
|
|
26862
27707
|
requireFilter(query, context);
|
|
26863
27708
|
const values = assignments(table, query.values, "values");
|
|
26864
27709
|
if (values.size === 0)
|
|
@@ -26896,8 +27741,9 @@ function compileUpdate(query, table, context, rls, nonce) {
|
|
|
26896
27741
|
{ sql: checkSql, params: terms.flatMap((term) => term.params) },
|
|
26897
27742
|
...policy ? [policy] : []
|
|
26898
27743
|
]);
|
|
26899
|
-
const
|
|
26900
|
-
const
|
|
27744
|
+
const rowid = rowidWhere(table, state, nonce, "update");
|
|
27745
|
+
const where = whereClause(table, query.filters ?? [], policy, params, rowid);
|
|
27746
|
+
const returning = writeReturning(table, query.returning, "update", capture);
|
|
26901
27747
|
for (const item of values.values())
|
|
26902
27748
|
rls?.refuseText(
|
|
26903
27749
|
"update",
|
|
@@ -26905,63 +27751,170 @@ function compileUpdate(query, table, context, rls, nonce) {
|
|
|
26905
27751
|
item.column,
|
|
26906
27752
|
toBoundValue(item.value)
|
|
26907
27753
|
);
|
|
26908
|
-
|
|
26909
|
-
|
|
26910
|
-
|
|
26911
|
-
|
|
26912
|
-
|
|
26913
|
-
];
|
|
27754
|
+
const write = statement(
|
|
27755
|
+
`UPDATE ${OR_ABORT} ${quoteIdentifier(table.name)} SET ${assigned.join(", ")}${where}${returning}`,
|
|
27756
|
+
params
|
|
27757
|
+
);
|
|
27758
|
+
if (!capture) return { statements: [write], guard: null };
|
|
27759
|
+
const guardParams = [];
|
|
27760
|
+
const guardWhere = whereClause(
|
|
27761
|
+
table,
|
|
27762
|
+
query.filters ?? [],
|
|
27763
|
+
policy,
|
|
27764
|
+
guardParams,
|
|
27765
|
+
rowid
|
|
27766
|
+
);
|
|
27767
|
+
const sent = new Map(
|
|
27768
|
+
[...values.values()].map((item) => [
|
|
27769
|
+
item.column.name,
|
|
27770
|
+
toBoundValue(item.value)
|
|
27771
|
+
])
|
|
27772
|
+
);
|
|
27773
|
+
return {
|
|
27774
|
+
statements: [write],
|
|
27775
|
+
guard: captureGuard(table, guardWhere, guardParams, nonce, sent)
|
|
27776
|
+
};
|
|
26914
27777
|
}
|
|
26915
|
-
function compileDelete(query, table, context, rls) {
|
|
27778
|
+
function compileDelete(query, table, context, rls, { state, nonce }, capture) {
|
|
26916
27779
|
requireFilter(query, context);
|
|
26917
27780
|
const params = [];
|
|
26918
27781
|
const policy = rls?.where("delete") ?? null;
|
|
26919
27782
|
if (rls && policy) rls.budget([policy]);
|
|
26920
|
-
const
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
)
|
|
26926
|
-
];
|
|
26927
|
-
}
|
|
26928
|
-
function compile(query, snapshot, decision, context, options = {}) {
|
|
26929
|
-
const nonce = checkNonceOf(
|
|
26930
|
-
options.checkNonce ?? DEFAULT_CHECK_NONCE,
|
|
26931
|
-
"compile"
|
|
27783
|
+
const rowid = rowidWhere(table, state, nonce, "select");
|
|
27784
|
+
const where = whereClause(table, query.filters ?? [], policy, params, rowid);
|
|
27785
|
+
const write = statement(
|
|
27786
|
+
`DELETE FROM ${quoteIdentifier(table.name)}${where}${writeReturning(table, query.returning, "delete", capture)}`,
|
|
27787
|
+
params
|
|
26932
27788
|
);
|
|
26933
|
-
if (!
|
|
26934
|
-
|
|
26935
|
-
const
|
|
26936
|
-
|
|
26937
|
-
|
|
26938
|
-
|
|
26939
|
-
|
|
26940
|
-
|
|
27789
|
+
if (!capture) return { statements: [write], guard: null };
|
|
27790
|
+
const guardParams = [];
|
|
27791
|
+
const guardWhere = whereClause(
|
|
27792
|
+
table,
|
|
27793
|
+
query.filters ?? [],
|
|
27794
|
+
policy,
|
|
27795
|
+
guardParams,
|
|
27796
|
+
rowid
|
|
27797
|
+
);
|
|
27798
|
+
return {
|
|
27799
|
+
statements: [write],
|
|
27800
|
+
guard: captureGuard(table, guardWhere, guardParams, nonce, /* @__PURE__ */ new Map())
|
|
27801
|
+
};
|
|
27802
|
+
}
|
|
27803
|
+
function ownReturning(table, query) {
|
|
27804
|
+
if (query.action === "select" || query.returning === void 0) return null;
|
|
27805
|
+
if (query.returning === "*") return table.columns.map((item) => item.name);
|
|
27806
|
+
return columnList(table, query.returning).map((item) => item.name);
|
|
27807
|
+
}
|
|
27808
|
+
function bulkPlan(table) {
|
|
27809
|
+
return {
|
|
27810
|
+
columns: table.columns,
|
|
27811
|
+
guard: null,
|
|
27812
|
+
triggers: null,
|
|
27813
|
+
returning: null,
|
|
27814
|
+
bulk: true
|
|
27815
|
+
};
|
|
27816
|
+
}
|
|
27817
|
+
function compileWith(query, snapshot, decision, context, table, nonce, capture) {
|
|
26941
27818
|
try {
|
|
26942
|
-
const
|
|
27819
|
+
const state = newState2();
|
|
27820
|
+
const rls = decision.rls ? new Rls(
|
|
27821
|
+
decision.rls,
|
|
27822
|
+
snapshot,
|
|
27823
|
+
table,
|
|
27824
|
+
nonce,
|
|
27825
|
+
commandsOf(query),
|
|
27826
|
+
void 0,
|
|
27827
|
+
state
|
|
27828
|
+
) : void 0;
|
|
27829
|
+
const statementContext = { state, nonce };
|
|
26943
27830
|
let statements;
|
|
27831
|
+
let guard = null;
|
|
26944
27832
|
switch (query.action) {
|
|
26945
27833
|
case "select":
|
|
26946
|
-
statements = compileSelect(query, table, rls);
|
|
27834
|
+
statements = compileSelect(query, table, rls, statementContext);
|
|
26947
27835
|
break;
|
|
26948
27836
|
case "insert":
|
|
26949
|
-
statements = compileInsert(
|
|
27837
|
+
statements = compileInsert(
|
|
27838
|
+
query,
|
|
27839
|
+
table,
|
|
27840
|
+
rls,
|
|
27841
|
+
statementContext,
|
|
27842
|
+
capture
|
|
27843
|
+
);
|
|
26950
27844
|
break;
|
|
26951
27845
|
case "update":
|
|
26952
|
-
statements = compileUpdate(
|
|
27846
|
+
({ statements, guard } = compileUpdate(
|
|
27847
|
+
query,
|
|
27848
|
+
table,
|
|
27849
|
+
context,
|
|
27850
|
+
rls,
|
|
27851
|
+
statementContext,
|
|
27852
|
+
capture
|
|
27853
|
+
));
|
|
26953
27854
|
break;
|
|
26954
27855
|
case "delete":
|
|
26955
|
-
statements = compileDelete(
|
|
27856
|
+
({ statements, guard } = compileDelete(
|
|
27857
|
+
query,
|
|
27858
|
+
table,
|
|
27859
|
+
context,
|
|
27860
|
+
rls,
|
|
27861
|
+
statementContext,
|
|
27862
|
+
capture
|
|
27863
|
+
));
|
|
26956
27864
|
break;
|
|
26957
27865
|
}
|
|
26958
|
-
|
|
27866
|
+
const compiled = {
|
|
27867
|
+
ok: true,
|
|
27868
|
+
statements,
|
|
27869
|
+
restrictive: rls?.restrictive ?? []
|
|
27870
|
+
};
|
|
27871
|
+
if (!capture) return compiled;
|
|
27872
|
+
return {
|
|
27873
|
+
...compiled,
|
|
27874
|
+
capture: {
|
|
27875
|
+
columns: table.columns,
|
|
27876
|
+
guard,
|
|
27877
|
+
// A DELETE is judged on the row as it was before the write
|
|
27878
|
+
// (original spec §8.5), which its RETURNING is.
|
|
27879
|
+
triggers: query.action === "insert" || query.action === "update" ? triggersRead(table) : null,
|
|
27880
|
+
returning: ownReturning(table, query),
|
|
27881
|
+
bulk: false
|
|
27882
|
+
}
|
|
27883
|
+
};
|
|
26959
27884
|
} catch (error51) {
|
|
26960
27885
|
if (error51 instanceof Failure)
|
|
26961
27886
|
return error51.reason === void 0 ? { ok: false, error: error51.error } : { ok: false, error: error51.error, reason: error51.reason };
|
|
26962
27887
|
throw error51;
|
|
26963
27888
|
}
|
|
26964
27889
|
}
|
|
27890
|
+
function compile(query, snapshot, decision, context, options = {}) {
|
|
27891
|
+
const nonce = checkNonceOf(
|
|
27892
|
+
options.checkNonce ?? DEFAULT_CHECK_NONCE,
|
|
27893
|
+
"compile"
|
|
27894
|
+
);
|
|
27895
|
+
if (!decision.allow)
|
|
27896
|
+
return decision.reason === void 0 ? { ok: false, error: decision.error } : { ok: false, error: decision.error, reason: decision.reason };
|
|
27897
|
+
const table = resolveTable(snapshot, query.table);
|
|
27898
|
+
if (!table)
|
|
27899
|
+
return {
|
|
27900
|
+
ok: false,
|
|
27901
|
+
error: dataError("TABLE_NOT_FOUND", { table: query.table })
|
|
27902
|
+
};
|
|
27903
|
+
if (options.capture !== true || query.action === "select")
|
|
27904
|
+
return compileWith(query, snapshot, decision, context, table, nonce, false);
|
|
27905
|
+
const captured = table.kind === "virtual" && query.action !== "insert" ? void 0 : compileWith(query, snapshot, decision, context, table, nonce, true);
|
|
27906
|
+
if (captured?.ok) return captured;
|
|
27907
|
+
const plain = compileWith(
|
|
27908
|
+
query,
|
|
27909
|
+
snapshot,
|
|
27910
|
+
decision,
|
|
27911
|
+
context,
|
|
27912
|
+
table,
|
|
27913
|
+
nonce,
|
|
27914
|
+
false
|
|
27915
|
+
);
|
|
27916
|
+
return plain.ok ? { ...plain, capture: bulkPlan(table) } : plain;
|
|
27917
|
+
}
|
|
26965
27918
|
|
|
26966
27919
|
// ../../packages/data-core/src/sql-depth.ts
|
|
26967
27920
|
var D1_EXPRESSION_DEPTH = 100;
|
|
@@ -27735,6 +28688,238 @@ function expressionDepth(sql, { planner = true } = {}) {
|
|
|
27735
28688
|
}
|
|
27736
28689
|
}
|
|
27737
28690
|
|
|
28691
|
+
// ../../packages/data-core/src/sql-head.ts
|
|
28692
|
+
var HEAD_TOKENS = 12;
|
|
28693
|
+
var BOM2 = 65279;
|
|
28694
|
+
function isBlankStart(char) {
|
|
28695
|
+
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
28696
|
+
}
|
|
28697
|
+
function isBlank(char) {
|
|
28698
|
+
return isBlankStart(char) || char === "\v";
|
|
28699
|
+
}
|
|
28700
|
+
function isWordStart2(char) {
|
|
28701
|
+
return /[A-Za-z_]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28702
|
+
}
|
|
28703
|
+
function isWordPart2(char) {
|
|
28704
|
+
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28705
|
+
}
|
|
28706
|
+
function skipBlanks(text2, from) {
|
|
28707
|
+
let index = from;
|
|
28708
|
+
for (; ; ) {
|
|
28709
|
+
const char = text2.charAt(index);
|
|
28710
|
+
if (isBlankStart(char)) {
|
|
28711
|
+
index++;
|
|
28712
|
+
while (index < text2.length && isBlank(text2.charAt(index))) index++;
|
|
28713
|
+
} else if (text2.charCodeAt(index) === BOM2) index++;
|
|
28714
|
+
else if (char === "-" && text2.charAt(index + 1) === "-") {
|
|
28715
|
+
const end = text2.indexOf("\n", index + 2);
|
|
28716
|
+
index = end < 0 ? text2.length : end;
|
|
28717
|
+
} else if (char === "/" && text2.charAt(index + 1) === "*") {
|
|
28718
|
+
const end = text2.indexOf("*/", index + 2);
|
|
28719
|
+
index = end < 0 ? text2.length : end + 2;
|
|
28720
|
+
} else return index;
|
|
28721
|
+
}
|
|
28722
|
+
}
|
|
28723
|
+
function readQuoted(text2, start, delimiter) {
|
|
28724
|
+
let value = "";
|
|
28725
|
+
let index = start + 1;
|
|
28726
|
+
for (; ; ) {
|
|
28727
|
+
const close = text2.indexOf(delimiter, index);
|
|
28728
|
+
if (close < 0) return void 0;
|
|
28729
|
+
value += text2.slice(index, close);
|
|
28730
|
+
if (text2.charAt(close + 1) === delimiter) {
|
|
28731
|
+
value += delimiter;
|
|
28732
|
+
index = close + 2;
|
|
28733
|
+
continue;
|
|
28734
|
+
}
|
|
28735
|
+
return { text: value, end: close + 1 };
|
|
28736
|
+
}
|
|
28737
|
+
}
|
|
28738
|
+
function headTokens(text2) {
|
|
28739
|
+
const tokens = [];
|
|
28740
|
+
let index = 0;
|
|
28741
|
+
while (tokens.length < HEAD_TOKENS) {
|
|
28742
|
+
index = skipBlanks(text2, index);
|
|
28743
|
+
const char = text2.charAt(index);
|
|
28744
|
+
if (index >= text2.length) break;
|
|
28745
|
+
if (char === '"' || char === "`" || char === "'") {
|
|
28746
|
+
const quoted3 = readQuoted(text2, index, char);
|
|
28747
|
+
if (!quoted3) break;
|
|
28748
|
+
tokens.push({ kind: "name", text: quoted3.text });
|
|
28749
|
+
index = quoted3.end;
|
|
28750
|
+
} else if (char === "[") {
|
|
28751
|
+
const close = text2.indexOf("]", index + 1);
|
|
28752
|
+
if (close < 0) break;
|
|
28753
|
+
tokens.push({ kind: "name", text: text2.slice(index + 1, close) });
|
|
28754
|
+
index = close + 1;
|
|
28755
|
+
} else if ((char === "x" || char === "X") && text2.charAt(index + 1) === "'") {
|
|
28756
|
+
break;
|
|
28757
|
+
} else if (isWordStart2(char)) {
|
|
28758
|
+
let end = index + 1;
|
|
28759
|
+
while (end < text2.length && isWordPart2(text2.charAt(end))) end++;
|
|
28760
|
+
tokens.push({ kind: "word", text: text2.slice(index, end) });
|
|
28761
|
+
index = end;
|
|
28762
|
+
} else if (char === "." && !/[0-9]/.test(text2.charAt(index + 1))) {
|
|
28763
|
+
tokens.push({ kind: "dot" });
|
|
28764
|
+
index++;
|
|
28765
|
+
} else break;
|
|
28766
|
+
}
|
|
28767
|
+
tokens.push({ kind: "stop" });
|
|
28768
|
+
return tokens;
|
|
28769
|
+
}
|
|
28770
|
+
var OTHER = { kind: "other" };
|
|
28771
|
+
var NO_NAME = {
|
|
28772
|
+
kind: "unreadable",
|
|
28773
|
+
reason: "the table name could not be read"
|
|
28774
|
+
};
|
|
28775
|
+
var NO_COLUMN = {
|
|
28776
|
+
kind: "unreadable",
|
|
28777
|
+
reason: "the column name could not be read"
|
|
28778
|
+
};
|
|
28779
|
+
var OTHER_SCHEMA = {
|
|
28780
|
+
kind: "unreadable",
|
|
28781
|
+
reason: "a table in another schema"
|
|
28782
|
+
};
|
|
28783
|
+
var PUBLIC_SCHEMA = {
|
|
28784
|
+
kind: "unreadable",
|
|
28785
|
+
reason: 'SQLite has no schema "public": write the table name without "public."'
|
|
28786
|
+
};
|
|
28787
|
+
var Head = class {
|
|
28788
|
+
constructor(tokens) {
|
|
28789
|
+
this.tokens = tokens;
|
|
28790
|
+
}
|
|
28791
|
+
token(at) {
|
|
28792
|
+
return this.tokens[Math.min(at, this.tokens.length - 1)] ?? { kind: "stop" };
|
|
28793
|
+
}
|
|
28794
|
+
/** The bare word `word` (lower case), in any ASCII case. */
|
|
28795
|
+
is(at, word) {
|
|
28796
|
+
const token = this.token(at);
|
|
28797
|
+
return token.kind === "word" && asciiLower(token.text) === word;
|
|
28798
|
+
}
|
|
28799
|
+
nameAt(at) {
|
|
28800
|
+
const token = this.token(at);
|
|
28801
|
+
return token.kind === "word" || token.kind === "name" ? token.text : void 0;
|
|
28802
|
+
}
|
|
28803
|
+
/**
|
|
28804
|
+
* SQLite's `fullname`. `main.` is the database a migration writes: the
|
|
28805
|
+
* name goes without it, and `main` says it was written (known-issues
|
|
28806
|
+
* #226); `temp.` (in any case, quoted or not, as sqlite3FindDbName
|
|
28807
|
+
* matches it) is a table that goes with the connection; `public.` is
|
|
28808
|
+
* what a Postgres migration writes, said in its own words; any other
|
|
28809
|
+
* schema is one D1 does not have.
|
|
28810
|
+
*/
|
|
28811
|
+
table(at) {
|
|
28812
|
+
const first = this.nameAt(at);
|
|
28813
|
+
if (first === void 0) return { ok: false, head: NO_NAME };
|
|
28814
|
+
if (this.token(at + 1).kind !== "dot")
|
|
28815
|
+
return { ok: true, name: first, next: at + 1, main: false };
|
|
28816
|
+
const second = this.nameAt(at + 2);
|
|
28817
|
+
if (second === void 0) return { ok: false, head: NO_NAME };
|
|
28818
|
+
const schema = asciiLower(first);
|
|
28819
|
+
if (schema === "main")
|
|
28820
|
+
return { ok: true, name: second, next: at + 3, main: true };
|
|
28821
|
+
let head = OTHER_SCHEMA;
|
|
28822
|
+
if (schema === "temp") head = OTHER;
|
|
28823
|
+
else if (schema === "public") head = PUBLIC_SCHEMA;
|
|
28824
|
+
return { ok: false, head, next: at + 3 };
|
|
28825
|
+
}
|
|
28826
|
+
};
|
|
28827
|
+
function inMain(read) {
|
|
28828
|
+
return read.main ? { main: true } : {};
|
|
28829
|
+
}
|
|
28830
|
+
function readCreate(head) {
|
|
28831
|
+
if (head.is(1, "policy")) return { kind: "create-policy" };
|
|
28832
|
+
if (head.is(1, "temp") || head.is(1, "temporary")) return OTHER;
|
|
28833
|
+
let at;
|
|
28834
|
+
if (head.is(1, "table")) at = 2;
|
|
28835
|
+
else if (head.is(1, "virtual") && head.is(2, "table")) at = 3;
|
|
28836
|
+
else return OTHER;
|
|
28837
|
+
if (head.is(at, "if")) {
|
|
28838
|
+
if (!head.is(at + 1, "not") || !head.is(at + 2, "exists")) return NO_NAME;
|
|
28839
|
+
at += 3;
|
|
28840
|
+
}
|
|
28841
|
+
const table = head.table(at);
|
|
28842
|
+
return table.ok ? { kind: "create-table", table: table.name } : table.head;
|
|
28843
|
+
}
|
|
28844
|
+
function readDrop(head) {
|
|
28845
|
+
if (head.is(1, "policy")) return { kind: "drop-policy" };
|
|
28846
|
+
if (!head.is(1, "table")) return OTHER;
|
|
28847
|
+
let at = 2;
|
|
28848
|
+
if (head.is(at, "if")) {
|
|
28849
|
+
if (!head.is(at + 1, "exists")) return NO_NAME;
|
|
28850
|
+
at += 2;
|
|
28851
|
+
}
|
|
28852
|
+
const table = head.table(at);
|
|
28853
|
+
return table.ok ? { kind: "drop-table", table: table.name, ...inMain(table) } : table.head;
|
|
28854
|
+
}
|
|
28855
|
+
var RLS_ACTIONS = ["enable", "disable", "force", "no"];
|
|
28856
|
+
function readRenameColumn(head, table, at) {
|
|
28857
|
+
let from = at;
|
|
28858
|
+
if (head.is(from, "column") && head.nameAt(from + 1) !== void 0 && head.is(from + 2, "to"))
|
|
28859
|
+
from += 1;
|
|
28860
|
+
const column2 = head.nameAt(from);
|
|
28861
|
+
if (column2 === void 0 || !head.is(from + 1, "to")) return NO_COLUMN;
|
|
28862
|
+
const to = head.nameAt(from + 2);
|
|
28863
|
+
return to === void 0 ? NO_COLUMN : {
|
|
28864
|
+
kind: "rename-column",
|
|
28865
|
+
table: table.name,
|
|
28866
|
+
column: column2,
|
|
28867
|
+
to,
|
|
28868
|
+
...inMain(table)
|
|
28869
|
+
};
|
|
28870
|
+
}
|
|
28871
|
+
function readAlter(head) {
|
|
28872
|
+
if (head.is(1, "policy")) return { kind: "alter-policy" };
|
|
28873
|
+
if (!head.is(1, "table")) return OTHER;
|
|
28874
|
+
const sqlite = head.table(2);
|
|
28875
|
+
const action = sqlite.next;
|
|
28876
|
+
if (action !== void 0) {
|
|
28877
|
+
if (head.is(action, "rename") && head.is(action + 1, "to")) {
|
|
28878
|
+
if (!sqlite.ok) return sqlite.head;
|
|
28879
|
+
const to = head.table(action + 2);
|
|
28880
|
+
return to.ok ? {
|
|
28881
|
+
kind: "rename-table",
|
|
28882
|
+
table: sqlite.name,
|
|
28883
|
+
to: to.name,
|
|
28884
|
+
...inMain(sqlite)
|
|
28885
|
+
} : to.head;
|
|
28886
|
+
}
|
|
28887
|
+
if (head.is(action, "rename")) {
|
|
28888
|
+
if (!sqlite.ok) return sqlite.head;
|
|
28889
|
+
return readRenameColumn(head, sqlite, action + 1);
|
|
28890
|
+
}
|
|
28891
|
+
if (head.is(action, "add") || head.is(action, "drop"))
|
|
28892
|
+
return sqlite.ok ? { kind: "alter-table", table: sqlite.name } : sqlite.head;
|
|
28893
|
+
}
|
|
28894
|
+
let at = 2;
|
|
28895
|
+
if (head.is(at, "if") && head.is(at + 1, "exists")) at += 2;
|
|
28896
|
+
if (head.is(at, "only")) at++;
|
|
28897
|
+
const next = head.table(at).next;
|
|
28898
|
+
if (next !== void 0 && RLS_ACTIONS.some((word) => head.is(next, word)))
|
|
28899
|
+
return { kind: "rls" };
|
|
28900
|
+
return sqlite.ok ? OTHER : sqlite.head;
|
|
28901
|
+
}
|
|
28902
|
+
function readSqlHead(text2) {
|
|
28903
|
+
if (text2.includes("\0"))
|
|
28904
|
+
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
28905
|
+
const head = new Head(headTokens(text2));
|
|
28906
|
+
if (head.is(0, "create")) return readCreate(head);
|
|
28907
|
+
if (head.is(0, "drop")) return readDrop(head);
|
|
28908
|
+
if (head.is(0, "alter")) return readAlter(head);
|
|
28909
|
+
if (head.is(0, "comment") && head.is(1, "on") && head.is(2, "policy"))
|
|
28910
|
+
return { kind: "comment-policy" };
|
|
28911
|
+
if (head.is(0, "grant")) return { kind: "grant" };
|
|
28912
|
+
if (head.is(0, "revoke")) return { kind: "revoke" };
|
|
28913
|
+
return OTHER;
|
|
28914
|
+
}
|
|
28915
|
+
|
|
28916
|
+
// ../../packages/data-core/src/functions-registry.ts
|
|
28917
|
+
var FUNCTIONS_TABLE = "_naro_functions";
|
|
28918
|
+
var FUNCTIONS_REGISTRY_VERSION = 9;
|
|
28919
|
+
var FUNCTIONS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ${FUNCTIONS_TABLE} (name TEXT NOT NULL PRIMARY KEY CHECK (name GLOB '[a-z]*' AND length(name) BETWEEN 1 AND 64), verify_jwt INTEGER NOT NULL DEFAULT 1 CHECK (verify_jwt IN (0, 1)), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`;
|
|
28920
|
+
var MAX_REGISTERED_FUNCTIONS = 2e3;
|
|
28921
|
+
var FUNCTIONS_REGISTRY_SQL = `SELECT name, verify_jwt FROM ${FUNCTIONS_TABLE} ORDER BY name LIMIT ${String(MAX_REGISTERED_FUNCTIONS)}`;
|
|
28922
|
+
|
|
27738
28923
|
// ../../packages/data-core/src/policy-index.ts
|
|
27739
28924
|
var INDEX_SQL = `SELECT t.name AS table_name, il.name AS index_name, il."partial" AS partial, ix.name AS column_name, ix.coll AS collation FROM pragma_table_list AS t JOIN pragma_index_list(t.name, t.schema) AS il JOIN pragma_index_xinfo(il.name, t.schema) AS ix WHERE t.schema = 'main' AND t.type = 'table' AND ${systemTablePredicate("t.name")} AND ix.seqno = 0`;
|
|
27740
28925
|
function indexesFromRows(rows) {
|
|
@@ -28011,8 +29196,34 @@ function unindexedSubqueries(snapshot, indexes, policies) {
|
|
|
28011
29196
|
return found;
|
|
28012
29197
|
}
|
|
28013
29198
|
|
|
29199
|
+
// ../../packages/data-core/src/realtime-filter.ts
|
|
29200
|
+
var FILTER_REASONS = {
|
|
29201
|
+
bytes: "must be a string of up to 1024 bytes in UTF-8",
|
|
29202
|
+
control: "must have no control character",
|
|
29203
|
+
syntax: "supports eq, neq, lt, lte, gt, gte and in, one condition",
|
|
29204
|
+
list: "needs in.(a,b) with 1 to 100 values"
|
|
29205
|
+
};
|
|
29206
|
+
|
|
28014
29207
|
// ../../packages/data-core/src/realtime-protocol.ts
|
|
28015
29208
|
var REALTIME_PROTOCOL_VERSION = "1.0.0";
|
|
29209
|
+
var MAX_CHANGE_CHANNELS_PER_TABLE = 4;
|
|
29210
|
+
var JOIN_CONFIG_REASONS = {
|
|
29211
|
+
object: "must be an object",
|
|
29212
|
+
unsupported: "is not supported",
|
|
29213
|
+
boolean: "must be true or false",
|
|
29214
|
+
presenceKey: "must be 1 to 128 characters",
|
|
29215
|
+
changes: "must be an array of up to 16 subscriptions",
|
|
29216
|
+
changeEvent: "must be *, INSERT, UPDATE or DELETE",
|
|
29217
|
+
schema: "must be public",
|
|
29218
|
+
table: "must be a table name of 1 to 128 characters, or *",
|
|
29219
|
+
filter: FILTER_REASONS.bytes,
|
|
29220
|
+
filterControl: FILTER_REASONS.control,
|
|
29221
|
+
filterSyntax: FILTER_REASONS.syntax,
|
|
29222
|
+
filterList: FILTER_REASONS.list,
|
|
29223
|
+
filterTable: "needs a table: a filter cannot go with *",
|
|
29224
|
+
/** The hub's, after `config.postgres_changes[<i>].table` (3-2c-1): `MAX_CHANGE_CHANNELS_PER_TABLE` against the connection's joins. */
|
|
29225
|
+
changeChannels: `would put more than ${String(MAX_CHANGE_CHANNELS_PER_TABLE)} channels of this connection on one table's changes (* counts for every table): add the subscription to a channel it already has`
|
|
29226
|
+
};
|
|
28016
29227
|
var REALTIME_CLOSE = {
|
|
28017
29228
|
UNSUPPORTED_PROTOCOL: {
|
|
28018
29229
|
code: 4e3,
|
|
@@ -29203,7 +30414,7 @@ function readClause(stream) {
|
|
|
29203
30414
|
checkExpressionSize(expr, open.position);
|
|
29204
30415
|
return { text: printExpr(expr), position: open.position };
|
|
29205
30416
|
}
|
|
29206
|
-
function
|
|
30417
|
+
function readCreate2(stream, start) {
|
|
29207
30418
|
stream.expectKeyword("policy");
|
|
29208
30419
|
const name = readPolicyName(stream);
|
|
29209
30420
|
stream.expectKeyword("on");
|
|
@@ -29263,7 +30474,7 @@ function readCreate(stream, start) {
|
|
|
29263
30474
|
throw new PolicySyntaxError(problem.replace(/\.$/, ""), position);
|
|
29264
30475
|
return { kind: "create", policy };
|
|
29265
30476
|
}
|
|
29266
|
-
function
|
|
30477
|
+
function readDrop2(stream) {
|
|
29267
30478
|
stream.expectKeyword("policy");
|
|
29268
30479
|
let ifExists = false;
|
|
29269
30480
|
if (stream.acceptKeyword("if")) {
|
|
@@ -29294,11 +30505,11 @@ function parsePolicyStatement(text2) {
|
|
|
29294
30505
|
position: 1
|
|
29295
30506
|
};
|
|
29296
30507
|
try {
|
|
29297
|
-
const stream = new TokenStream(
|
|
30508
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
29298
30509
|
const start = stream.next();
|
|
29299
30510
|
let statement2;
|
|
29300
|
-
if (isKeyword(start, "create")) statement2 =
|
|
29301
|
-
else if (isKeyword(start, "drop")) statement2 =
|
|
30511
|
+
if (isKeyword(start, "create")) statement2 = readCreate2(stream, start);
|
|
30512
|
+
else if (isKeyword(start, "drop")) statement2 = readDrop2(stream);
|
|
29302
30513
|
else if (isKeyword(start, "alter"))
|
|
29303
30514
|
throw new PolicySyntaxError(
|
|
29304
30515
|
"ALTER POLICY is not supported here yet: DROP POLICY and CREATE POLICY again",
|
|
@@ -29321,7 +30532,7 @@ function parsePolicyStatement(text2) {
|
|
|
29321
30532
|
function splitPolicyStatements(text2) {
|
|
29322
30533
|
let tokens;
|
|
29323
30534
|
try {
|
|
29324
|
-
tokens =
|
|
30535
|
+
tokens = tokenize2(text2);
|
|
29325
30536
|
} catch (error51) {
|
|
29326
30537
|
if (error51 instanceof PolicySyntaxError)
|
|
29327
30538
|
return { ok: false, reason: error51.reason, position: error51.position };
|
|
@@ -29350,13 +30561,13 @@ function ownerComment(text2, position) {
|
|
|
29350
30561
|
if (rest !== "" && !/^[ \t]/.test(rest)) return void 0;
|
|
29351
30562
|
let column2 = rest.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
29352
30563
|
if (column2.startsWith('"')) {
|
|
29353
|
-
const
|
|
29354
|
-
if (!
|
|
30564
|
+
const quoted3 = /^"((?:[^"]|"")*)"$/.exec(column2);
|
|
30565
|
+
if (!quoted3)
|
|
29355
30566
|
throw new PolicySyntaxError(
|
|
29356
30567
|
"naro:owner needs one column name, bare or in double quotes",
|
|
29357
30568
|
position
|
|
29358
30569
|
);
|
|
29359
|
-
column2 = (
|
|
30570
|
+
column2 = (quoted3[1] ?? "").replaceAll('""', '"');
|
|
29360
30571
|
}
|
|
29361
30572
|
if (column2 === "")
|
|
29362
30573
|
throw new PolicySyntaxError("naro:owner needs a column name", position);
|
|
@@ -29410,7 +30621,7 @@ function parseMigrationPolicyStatement(text2) {
|
|
|
29410
30621
|
position: 1
|
|
29411
30622
|
};
|
|
29412
30623
|
try {
|
|
29413
|
-
const stream = new TokenStream(
|
|
30624
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
29414
30625
|
let statement2;
|
|
29415
30626
|
if (stream.acceptKeyword("comment")) statement2 = readComment(stream);
|
|
29416
30627
|
else if (isKeyword(stream.peek(), "alter") && isKeyword(stream.peek(1), "table")) {
|
|
@@ -29427,230 +30638,11 @@ function parseMigrationPolicyStatement(text2) {
|
|
|
29427
30638
|
}
|
|
29428
30639
|
}
|
|
29429
30640
|
|
|
29430
|
-
// ../../packages/data-core/src/
|
|
29431
|
-
var
|
|
29432
|
-
var
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
}
|
|
29436
|
-
function isBlank(char) {
|
|
29437
|
-
return isBlankStart(char) || char === "\v";
|
|
29438
|
-
}
|
|
29439
|
-
function isWordStart2(char) {
|
|
29440
|
-
return /[A-Za-z_]/.test(char) || char.charCodeAt(0) >= 128;
|
|
29441
|
-
}
|
|
29442
|
-
function isWordPart2(char) {
|
|
29443
|
-
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
29444
|
-
}
|
|
29445
|
-
function skipBlanks(text2, from) {
|
|
29446
|
-
let index = from;
|
|
29447
|
-
for (; ; ) {
|
|
29448
|
-
const char = text2.charAt(index);
|
|
29449
|
-
if (isBlankStart(char)) {
|
|
29450
|
-
index++;
|
|
29451
|
-
while (index < text2.length && isBlank(text2.charAt(index))) index++;
|
|
29452
|
-
} else if (text2.charCodeAt(index) === BOM2) index++;
|
|
29453
|
-
else if (char === "-" && text2.charAt(index + 1) === "-") {
|
|
29454
|
-
const end = text2.indexOf("\n", index + 2);
|
|
29455
|
-
index = end < 0 ? text2.length : end;
|
|
29456
|
-
} else if (char === "/" && text2.charAt(index + 1) === "*") {
|
|
29457
|
-
const end = text2.indexOf("*/", index + 2);
|
|
29458
|
-
index = end < 0 ? text2.length : end + 2;
|
|
29459
|
-
} else return index;
|
|
29460
|
-
}
|
|
29461
|
-
}
|
|
29462
|
-
function readQuoted(text2, start, delimiter) {
|
|
29463
|
-
let value = "";
|
|
29464
|
-
let index = start + 1;
|
|
29465
|
-
for (; ; ) {
|
|
29466
|
-
const close = text2.indexOf(delimiter, index);
|
|
29467
|
-
if (close < 0) return void 0;
|
|
29468
|
-
value += text2.slice(index, close);
|
|
29469
|
-
if (text2.charAt(close + 1) === delimiter) {
|
|
29470
|
-
value += delimiter;
|
|
29471
|
-
index = close + 2;
|
|
29472
|
-
continue;
|
|
29473
|
-
}
|
|
29474
|
-
return { text: value, end: close + 1 };
|
|
29475
|
-
}
|
|
29476
|
-
}
|
|
29477
|
-
function headTokens(text2) {
|
|
29478
|
-
const tokens = [];
|
|
29479
|
-
let index = 0;
|
|
29480
|
-
while (tokens.length < HEAD_TOKENS) {
|
|
29481
|
-
index = skipBlanks(text2, index);
|
|
29482
|
-
const char = text2.charAt(index);
|
|
29483
|
-
if (index >= text2.length) break;
|
|
29484
|
-
if (char === '"' || char === "`" || char === "'") {
|
|
29485
|
-
const quoted2 = readQuoted(text2, index, char);
|
|
29486
|
-
if (!quoted2) break;
|
|
29487
|
-
tokens.push({ kind: "name", text: quoted2.text });
|
|
29488
|
-
index = quoted2.end;
|
|
29489
|
-
} else if (char === "[") {
|
|
29490
|
-
const close = text2.indexOf("]", index + 1);
|
|
29491
|
-
if (close < 0) break;
|
|
29492
|
-
tokens.push({ kind: "name", text: text2.slice(index + 1, close) });
|
|
29493
|
-
index = close + 1;
|
|
29494
|
-
} else if ((char === "x" || char === "X") && text2.charAt(index + 1) === "'") {
|
|
29495
|
-
break;
|
|
29496
|
-
} else if (isWordStart2(char)) {
|
|
29497
|
-
let end = index + 1;
|
|
29498
|
-
while (end < text2.length && isWordPart2(text2.charAt(end))) end++;
|
|
29499
|
-
tokens.push({ kind: "word", text: text2.slice(index, end) });
|
|
29500
|
-
index = end;
|
|
29501
|
-
} else if (char === "." && !/[0-9]/.test(text2.charAt(index + 1))) {
|
|
29502
|
-
tokens.push({ kind: "dot" });
|
|
29503
|
-
index++;
|
|
29504
|
-
} else break;
|
|
29505
|
-
}
|
|
29506
|
-
tokens.push({ kind: "stop" });
|
|
29507
|
-
return tokens;
|
|
29508
|
-
}
|
|
29509
|
-
var OTHER = { kind: "other" };
|
|
29510
|
-
var NO_NAME = {
|
|
29511
|
-
kind: "unreadable",
|
|
29512
|
-
reason: "the table name could not be read"
|
|
29513
|
-
};
|
|
29514
|
-
var NO_COLUMN = {
|
|
29515
|
-
kind: "unreadable",
|
|
29516
|
-
reason: "the column name could not be read"
|
|
29517
|
-
};
|
|
29518
|
-
var OTHER_SCHEMA = {
|
|
29519
|
-
kind: "unreadable",
|
|
29520
|
-
reason: "a table in another schema"
|
|
29521
|
-
};
|
|
29522
|
-
var PUBLIC_SCHEMA = {
|
|
29523
|
-
kind: "unreadable",
|
|
29524
|
-
reason: 'SQLite has no schema "public": write the table name without "public."'
|
|
29525
|
-
};
|
|
29526
|
-
var Head = class {
|
|
29527
|
-
constructor(tokens) {
|
|
29528
|
-
this.tokens = tokens;
|
|
29529
|
-
}
|
|
29530
|
-
token(at) {
|
|
29531
|
-
return this.tokens[Math.min(at, this.tokens.length - 1)] ?? { kind: "stop" };
|
|
29532
|
-
}
|
|
29533
|
-
/** The bare word `word` (lower case), in any ASCII case. */
|
|
29534
|
-
is(at, word) {
|
|
29535
|
-
const token = this.token(at);
|
|
29536
|
-
return token.kind === "word" && asciiLower(token.text) === word;
|
|
29537
|
-
}
|
|
29538
|
-
nameAt(at) {
|
|
29539
|
-
const token = this.token(at);
|
|
29540
|
-
return token.kind === "word" || token.kind === "name" ? token.text : void 0;
|
|
29541
|
-
}
|
|
29542
|
-
/**
|
|
29543
|
-
* SQLite's `fullname`. `main.` is the database a migration writes: the
|
|
29544
|
-
* name goes without it, and `main` says it was written (known-issues
|
|
29545
|
-
* #226); `temp.` (in any case, quoted or not, as sqlite3FindDbName
|
|
29546
|
-
* matches it) is a table that goes with the connection; `public.` is
|
|
29547
|
-
* what a Postgres migration writes, said in its own words; any other
|
|
29548
|
-
* schema is one D1 does not have.
|
|
29549
|
-
*/
|
|
29550
|
-
table(at) {
|
|
29551
|
-
const first = this.nameAt(at);
|
|
29552
|
-
if (first === void 0) return { ok: false, head: NO_NAME };
|
|
29553
|
-
if (this.token(at + 1).kind !== "dot")
|
|
29554
|
-
return { ok: true, name: first, next: at + 1, main: false };
|
|
29555
|
-
const second = this.nameAt(at + 2);
|
|
29556
|
-
if (second === void 0) return { ok: false, head: NO_NAME };
|
|
29557
|
-
const schema = asciiLower(first);
|
|
29558
|
-
if (schema === "main")
|
|
29559
|
-
return { ok: true, name: second, next: at + 3, main: true };
|
|
29560
|
-
let head = OTHER_SCHEMA;
|
|
29561
|
-
if (schema === "temp") head = OTHER;
|
|
29562
|
-
else if (schema === "public") head = PUBLIC_SCHEMA;
|
|
29563
|
-
return { ok: false, head, next: at + 3 };
|
|
29564
|
-
}
|
|
29565
|
-
};
|
|
29566
|
-
function inMain(read) {
|
|
29567
|
-
return read.main ? { main: true } : {};
|
|
29568
|
-
}
|
|
29569
|
-
function readCreate2(head) {
|
|
29570
|
-
if (head.is(1, "policy")) return { kind: "create-policy" };
|
|
29571
|
-
if (head.is(1, "temp") || head.is(1, "temporary")) return OTHER;
|
|
29572
|
-
let at;
|
|
29573
|
-
if (head.is(1, "table")) at = 2;
|
|
29574
|
-
else if (head.is(1, "virtual") && head.is(2, "table")) at = 3;
|
|
29575
|
-
else return OTHER;
|
|
29576
|
-
if (head.is(at, "if")) {
|
|
29577
|
-
if (!head.is(at + 1, "not") || !head.is(at + 2, "exists")) return NO_NAME;
|
|
29578
|
-
at += 3;
|
|
29579
|
-
}
|
|
29580
|
-
const table = head.table(at);
|
|
29581
|
-
return table.ok ? { kind: "create-table", table: table.name } : table.head;
|
|
29582
|
-
}
|
|
29583
|
-
function readDrop2(head) {
|
|
29584
|
-
if (head.is(1, "policy")) return { kind: "drop-policy" };
|
|
29585
|
-
if (!head.is(1, "table")) return OTHER;
|
|
29586
|
-
let at = 2;
|
|
29587
|
-
if (head.is(at, "if")) {
|
|
29588
|
-
if (!head.is(at + 1, "exists")) return NO_NAME;
|
|
29589
|
-
at += 2;
|
|
29590
|
-
}
|
|
29591
|
-
const table = head.table(at);
|
|
29592
|
-
return table.ok ? { kind: "drop-table", table: table.name, ...inMain(table) } : table.head;
|
|
29593
|
-
}
|
|
29594
|
-
var RLS_ACTIONS = ["enable", "disable", "force", "no"];
|
|
29595
|
-
function readRenameColumn(head, table, at) {
|
|
29596
|
-
let from = at;
|
|
29597
|
-
if (head.is(from, "column") && head.nameAt(from + 1) !== void 0 && head.is(from + 2, "to"))
|
|
29598
|
-
from += 1;
|
|
29599
|
-
const column2 = head.nameAt(from);
|
|
29600
|
-
if (column2 === void 0 || !head.is(from + 1, "to")) return NO_COLUMN;
|
|
29601
|
-
const to = head.nameAt(from + 2);
|
|
29602
|
-
return to === void 0 ? NO_COLUMN : {
|
|
29603
|
-
kind: "rename-column",
|
|
29604
|
-
table: table.name,
|
|
29605
|
-
column: column2,
|
|
29606
|
-
to,
|
|
29607
|
-
...inMain(table)
|
|
29608
|
-
};
|
|
29609
|
-
}
|
|
29610
|
-
function readAlter(head) {
|
|
29611
|
-
if (head.is(1, "policy")) return { kind: "alter-policy" };
|
|
29612
|
-
if (!head.is(1, "table")) return OTHER;
|
|
29613
|
-
const sqlite = head.table(2);
|
|
29614
|
-
const action = sqlite.next;
|
|
29615
|
-
if (action !== void 0) {
|
|
29616
|
-
if (head.is(action, "rename") && head.is(action + 1, "to")) {
|
|
29617
|
-
if (!sqlite.ok) return sqlite.head;
|
|
29618
|
-
const to = head.table(action + 2);
|
|
29619
|
-
return to.ok ? {
|
|
29620
|
-
kind: "rename-table",
|
|
29621
|
-
table: sqlite.name,
|
|
29622
|
-
to: to.name,
|
|
29623
|
-
...inMain(sqlite)
|
|
29624
|
-
} : to.head;
|
|
29625
|
-
}
|
|
29626
|
-
if (head.is(action, "rename")) {
|
|
29627
|
-
if (!sqlite.ok) return sqlite.head;
|
|
29628
|
-
return readRenameColumn(head, sqlite, action + 1);
|
|
29629
|
-
}
|
|
29630
|
-
if (head.is(action, "add") || head.is(action, "drop"))
|
|
29631
|
-
return sqlite.ok ? { kind: "alter-table", table: sqlite.name } : sqlite.head;
|
|
29632
|
-
}
|
|
29633
|
-
let at = 2;
|
|
29634
|
-
if (head.is(at, "if") && head.is(at + 1, "exists")) at += 2;
|
|
29635
|
-
if (head.is(at, "only")) at++;
|
|
29636
|
-
const next = head.table(at).next;
|
|
29637
|
-
if (next !== void 0 && RLS_ACTIONS.some((word) => head.is(next, word)))
|
|
29638
|
-
return { kind: "rls" };
|
|
29639
|
-
return sqlite.ok ? OTHER : sqlite.head;
|
|
29640
|
-
}
|
|
29641
|
-
function readSqlHead(text2) {
|
|
29642
|
-
if (text2.includes("\0"))
|
|
29643
|
-
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
29644
|
-
const head = new Head(headTokens(text2));
|
|
29645
|
-
if (head.is(0, "create")) return readCreate2(head);
|
|
29646
|
-
if (head.is(0, "drop")) return readDrop2(head);
|
|
29647
|
-
if (head.is(0, "alter")) return readAlter(head);
|
|
29648
|
-
if (head.is(0, "comment") && head.is(1, "on") && head.is(2, "policy"))
|
|
29649
|
-
return { kind: "comment-policy" };
|
|
29650
|
-
if (head.is(0, "grant")) return { kind: "grant" };
|
|
29651
|
-
if (head.is(0, "revoke")) return { kind: "revoke" };
|
|
29652
|
-
return OTHER;
|
|
29653
|
-
}
|
|
30641
|
+
// ../../packages/data-core/src/realtime-publication.ts
|
|
30642
|
+
var REALTIME_TABLES = "_naro_realtime_tables";
|
|
30643
|
+
var REALTIME_TABLES_SQL = `CREATE TABLE IF NOT EXISTS ${REALTIME_TABLES} (table_name TEXT NOT NULL PRIMARY KEY COLLATE NOCASE, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`;
|
|
30644
|
+
var MAX_PUBLISHED_TABLES = 100;
|
|
30645
|
+
var PUBLISHED_TABLES_SQL = `SELECT table_name FROM ${REALTIME_TABLES} ORDER BY rowid LIMIT ${String(MAX_PUBLISHED_TABLES)}`;
|
|
29654
30646
|
|
|
29655
30647
|
// ../../packages/data-core/src/system-sql.ts
|
|
29656
30648
|
async function convertLegacyPolicies(db, found) {
|
|
@@ -29685,6 +30677,7 @@ var MIGRATIONS_TABLE = "CREATE TABLE IF NOT EXISTS _naro_migrations (version INT
|
|
|
29685
30677
|
var STORAGE_SUBQUERIES_VERSION = 7;
|
|
29686
30678
|
var STORAGE_SUBQUERIES_NAME = "storage_subquery_policies";
|
|
29687
30679
|
var STORAGE_SUBQUERIES_SQL = `SELECT version FROM _naro_migrations WHERE version = ${String(STORAGE_SUBQUERIES_VERSION)} AND name = '${STORAGE_SUBQUERIES_NAME}'`;
|
|
30680
|
+
var REALTIME_TABLES_VERSION = 8;
|
|
29688
30681
|
var SYSTEM_MIGRATIONS = [
|
|
29689
30682
|
{
|
|
29690
30683
|
version: 1,
|
|
@@ -29773,6 +30766,23 @@ var SYSTEM_MIGRATIONS = [
|
|
|
29773
30766
|
version: STORAGE_SUBQUERIES_VERSION,
|
|
29774
30767
|
name: STORAGE_SUBQUERIES_NAME,
|
|
29775
30768
|
statements: []
|
|
30769
|
+
},
|
|
30770
|
+
{
|
|
30771
|
+
// Realtime 3-2b (original spec §9.1, design §7.1): the tables whose
|
|
30772
|
+
// Data API writes reach the hub. Empty until the publish API (3-2c)
|
|
30773
|
+
// writes it. A worker from before it never reads it.
|
|
30774
|
+
version: REALTIME_TABLES_VERSION,
|
|
30775
|
+
name: "realtime",
|
|
30776
|
+
statements: [REALTIME_TABLES_SQL]
|
|
30777
|
+
},
|
|
30778
|
+
{
|
|
30779
|
+
// Functions 3-3a (design 2026-10-29 §6.3): the function registry the
|
|
30780
|
+
// gate reads. Every backend gets it, Functions on or not; the control
|
|
30781
|
+
// plane (3-3b) writes it. A worker from before it never reads it, and a
|
|
30782
|
+
// worker that reads it on a D1 without it answers 409 BACKEND_OUTDATED.
|
|
30783
|
+
version: FUNCTIONS_REGISTRY_VERSION,
|
|
30784
|
+
name: "functions",
|
|
30785
|
+
statements: [FUNCTIONS_TABLE_SQL]
|
|
29776
30786
|
}
|
|
29777
30787
|
];
|
|
29778
30788
|
|
|
@@ -29972,6 +30982,10 @@ var ENTERPRISE_REALTIME_LIMITS = Object.freeze({
|
|
|
29972
30982
|
restRequestsPerSecond: 100
|
|
29973
30983
|
});
|
|
29974
30984
|
|
|
30985
|
+
// ../../packages/data-core/src/realtime-changes.ts
|
|
30986
|
+
var CHANGE_TYPES = Object.freeze({ insert: "INSERT", update: "UPDATE", delete: "DELETE" });
|
|
30987
|
+
var CHANGE_EVENT_CODES = Object.freeze({ "*": 0, INSERT: 1, UPDATE: 2, DELETE: 3 });
|
|
30988
|
+
|
|
29975
30989
|
// ../../packages/data-core/src/redirect-origins.ts
|
|
29976
30990
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
29977
30991
|
"localhost",
|
|
@@ -30540,13 +31554,13 @@ function compareMigrations(local, applied) {
|
|
|
30540
31554
|
rows.push({ name, status: "applied", appliedAt: exact.appliedAt });
|
|
30541
31555
|
continue;
|
|
30542
31556
|
}
|
|
30543
|
-
const
|
|
30544
|
-
if (
|
|
30545
|
-
ledger.delete(
|
|
31557
|
+
const bare2 = ledger.get(name.replace(/\.sql$/, ""));
|
|
31558
|
+
if (bare2) {
|
|
31559
|
+
ledger.delete(bare2.name);
|
|
30546
31560
|
rows.push({
|
|
30547
31561
|
name,
|
|
30548
31562
|
status: "applied",
|
|
30549
|
-
appliedAt:
|
|
31563
|
+
appliedAt: bare2.appliedAt,
|
|
30550
31564
|
warning: "ledger-name-missing-extension"
|
|
30551
31565
|
});
|
|
30552
31566
|
continue;
|
|
@@ -31489,6 +32503,10 @@ function failureCode(error51, fallback) {
|
|
|
31489
32503
|
const code = error51 && typeof error51 === "object" ? error51.code : void 0;
|
|
31490
32504
|
return typeof code === "string" && DEPLOYMENT_CODES.has(code) ? code : fallback;
|
|
31491
32505
|
}
|
|
32506
|
+
var SYSTEM_NAMES = new Set(NARO_SYSTEM_ENV_NAMES);
|
|
32507
|
+
function isNaroSystemName(key) {
|
|
32508
|
+
return SYSTEM_NAMES.has(key);
|
|
32509
|
+
}
|
|
31492
32510
|
function slugify2(value) {
|
|
31493
32511
|
const slug = value.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 54);
|
|
31494
32512
|
return slug || "app";
|
|
@@ -31671,6 +32689,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
31671
32689
|
const provided = options.env ?? {};
|
|
31672
32690
|
missing = scanned.filter((key) => !existing.has(key) && !(key in provided));
|
|
31673
32691
|
}
|
|
32692
|
+
if (environment !== void 0 && options.projectId && missing.some((key) => isNaroSystemName(key))) {
|
|
32693
|
+
const backend = await client.backend.info(options.projectId).catch(() => null);
|
|
32694
|
+
if (backend !== null && backend.status !== "none") {
|
|
32695
|
+
const set2 = missing.filter((key) => isNaroSystemName(key));
|
|
32696
|
+
missing = missing.filter((key) => !isNaroSystemName(key));
|
|
32697
|
+
log2(
|
|
32698
|
+
`\u25B8 naro sets ${set2.join(", ")} for ${environment} to its own backend's: not asked for`
|
|
32699
|
+
);
|
|
32700
|
+
}
|
|
32701
|
+
}
|
|
31674
32702
|
let collected = {};
|
|
31675
32703
|
if (missing.length > 0) {
|
|
31676
32704
|
log2(
|
|
@@ -31716,7 +32744,16 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
31716
32744
|
repositoryUrl: sourceDir,
|
|
31717
32745
|
branch: ctx.branch,
|
|
31718
32746
|
rootDirectory: options.rootDirectory ?? null,
|
|
31719
|
-
|
|
32747
|
+
// An environment's deploy: the control plane saved what this sent for
|
|
32748
|
+
// that environment and then chose its values, so a key sent and a key
|
|
32749
|
+
// chosen usually hold the same value — except the names naro sets for
|
|
32750
|
+
// an environment with a backend (named environments spec §8.3), which
|
|
32751
|
+
// must win here too, or this build would call another backend than
|
|
32752
|
+
// the worker's bindings, and a key the server failed to save
|
|
32753
|
+
// (`envSkipped`), whose stored value the worker gets and so this build
|
|
32754
|
+
// takes too. Production and preview keep a value sent over the stored
|
|
32755
|
+
// one, as before.
|
|
32756
|
+
env: ctx.environment ? { ...envToSend, ...ctx.env } : { ...ctx.env, ...envToSend },
|
|
31720
32757
|
strategy: ctx.strategy ?? null,
|
|
31721
32758
|
...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
|
|
31722
32759
|
},
|
|
@@ -31807,10 +32844,12 @@ function deploymentUrl(detail, ctx, production) {
|
|
|
31807
32844
|
}
|
|
31808
32845
|
|
|
31809
32846
|
export {
|
|
32847
|
+
environmentGuidance,
|
|
31810
32848
|
parseGitHubRemote,
|
|
31811
32849
|
schemaMigrationTitle,
|
|
31812
32850
|
projectPath,
|
|
31813
32851
|
splitStatements,
|
|
32852
|
+
environmentSlugProblem,
|
|
31814
32853
|
ANALYTICS_FILTER_KEYS,
|
|
31815
32854
|
NaroError,
|
|
31816
32855
|
promoteTargetUnsupported,
|
|
@@ -31847,11 +32886,11 @@ export {
|
|
|
31847
32886
|
summarizeStatement,
|
|
31848
32887
|
parseDotenvDetailed,
|
|
31849
32888
|
serializeDotenv,
|
|
32889
|
+
systemTablePredicate,
|
|
31850
32890
|
MAX_SINGLE_UPLOAD_BYTES,
|
|
31851
32891
|
checkObjectName,
|
|
31852
32892
|
parseCacheControl,
|
|
31853
32893
|
STORAGE_OBJECTS_TABLE,
|
|
31854
|
-
systemTablePredicate,
|
|
31855
32894
|
dataError,
|
|
31856
32895
|
SCHEMA_SQL,
|
|
31857
32896
|
tableNames,
|