naro-cli 0.19.1 → 0.21.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 +179 -5
- package/dist/{chunk-ZO2BEC46.js → chunk-FFPOVA7I.js} +1296 -597
- package/dist/deploy.js +1 -1
- package/dist/index.js +2668 -122
- package/package.json +4 -4
|
@@ -345,6 +345,8 @@ var DEPLOYMENT_ERROR_CODES = [
|
|
|
345
345
|
"FORBIDDEN",
|
|
346
346
|
"NOT_FOUND",
|
|
347
347
|
"DEPLOYMENT_NOT_PROMOTABLE",
|
|
348
|
+
/** A promotion to another surface rebuilds the commit, and the source was not built from Git (spec 2026-10-18-named-environments §9.1). */
|
|
349
|
+
"PROMOTE_NEEDS_GIT_SOURCE",
|
|
348
350
|
"DEPLOYMENT_NOT_CANCELABLE",
|
|
349
351
|
"SUBDOMAIN_TAKEN",
|
|
350
352
|
"FLAG_EXISTS",
|
|
@@ -511,6 +513,9 @@ function projectPath(teamSlug, projectSlug) {
|
|
|
511
513
|
return `/${teamSlug}/projects/${projectSlug}`;
|
|
512
514
|
}
|
|
513
515
|
|
|
516
|
+
// ../../packages/core/src/platform-mail.ts
|
|
517
|
+
var encoder = new TextEncoder();
|
|
518
|
+
|
|
514
519
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
515
520
|
var external_exports = {};
|
|
516
521
|
__export(external_exports, {
|
|
@@ -18506,6 +18511,7 @@ function forkedAppRoot(checkout, appRoot, forkCheckout) {
|
|
|
18506
18511
|
}
|
|
18507
18512
|
|
|
18508
18513
|
// ../../packages/build/src/local-build-executor.ts
|
|
18514
|
+
var PINNED_COMMIT_SHA = /^[0-9a-f]{40}$/;
|
|
18509
18515
|
function concurrencyFromEnvironment() {
|
|
18510
18516
|
const raw = Number(process.env.NARO_RESOLVER_CONCURRENCY ?? "1");
|
|
18511
18517
|
return Number.isInteger(raw) && raw > 0 ? raw : 1;
|
|
@@ -18650,6 +18656,68 @@ var LocalBuildExecutor = class {
|
|
|
18650
18656
|
constructor(dependencies = defaultLocalBuildDependencies) {
|
|
18651
18657
|
this.dependencies = dependencies;
|
|
18652
18658
|
}
|
|
18659
|
+
/**
|
|
18660
|
+
* Checks out one commit, whichever ref reaches it: GitHub serves any
|
|
18661
|
+
* reachable commit by SHA, which `clone --branch` cannot ask for. The URL
|
|
18662
|
+
* carries the installation token and is only ever an argument — runCommand
|
|
18663
|
+
* logs a command's output (redacted), never the command line, and a failed
|
|
18664
|
+
* command's message names the command, not its arguments.
|
|
18665
|
+
*
|
|
18666
|
+
* The commit is an argument of `git fetch`, so it is checked before any git
|
|
18667
|
+
* command runs: a value git could read as an option (`--upload-pack=…`) or a
|
|
18668
|
+
* ref name must not get that far. The build container checks it on the way
|
|
18669
|
+
* in; the local runner passes claim's input straight through, so this is the
|
|
18670
|
+
* check that covers it.
|
|
18671
|
+
*/
|
|
18672
|
+
async checkoutPinnedCommit(commitSha, repositoryUrl, workspace, checkout, observer) {
|
|
18673
|
+
if (!PINNED_COMMIT_SHA.test(commitSha)) {
|
|
18674
|
+
throw new DeploymentError(
|
|
18675
|
+
"CLONE_FAILED",
|
|
18676
|
+
"The pinned commit is not a 40-character lowercase hex SHA."
|
|
18677
|
+
);
|
|
18678
|
+
}
|
|
18679
|
+
const git2 = (args) => this.dependencies.runCommand(
|
|
18680
|
+
{
|
|
18681
|
+
command: "git",
|
|
18682
|
+
args,
|
|
18683
|
+
cwd: workspace,
|
|
18684
|
+
timeoutMs: 12e4,
|
|
18685
|
+
stage: "CLONING",
|
|
18686
|
+
errorCode: "CLONE_FAILED"
|
|
18687
|
+
},
|
|
18688
|
+
observer
|
|
18689
|
+
);
|
|
18690
|
+
await git2(["init", "--quiet", checkout]);
|
|
18691
|
+
await git2(["-C", checkout, "remote", "add", "origin", "--", repositoryUrl]);
|
|
18692
|
+
try {
|
|
18693
|
+
await git2([
|
|
18694
|
+
"-C",
|
|
18695
|
+
checkout,
|
|
18696
|
+
"fetch",
|
|
18697
|
+
"--quiet",
|
|
18698
|
+
"--depth",
|
|
18699
|
+
"1",
|
|
18700
|
+
"origin",
|
|
18701
|
+
commitSha
|
|
18702
|
+
]);
|
|
18703
|
+
} catch (error51) {
|
|
18704
|
+
if (!(error51 instanceof CommandExecutionError)) throw error51;
|
|
18705
|
+
if (error51.timedOut) throw error51;
|
|
18706
|
+
throw new DeploymentError(
|
|
18707
|
+
"CLONE_FAILED",
|
|
18708
|
+
`Could not fetch commit ${commitSha.slice(0, 7)}. It may have been removed from the repository.`,
|
|
18709
|
+
{ cause: error51 }
|
|
18710
|
+
);
|
|
18711
|
+
}
|
|
18712
|
+
await git2([
|
|
18713
|
+
"-C",
|
|
18714
|
+
checkout,
|
|
18715
|
+
"checkout",
|
|
18716
|
+
"--quiet",
|
|
18717
|
+
"--detach",
|
|
18718
|
+
"FETCH_HEAD"
|
|
18719
|
+
]);
|
|
18720
|
+
}
|
|
18653
18721
|
async build(input, observer, cache) {
|
|
18654
18722
|
const workspace = await this.dependencies.createWorkspace();
|
|
18655
18723
|
const checkout = join18(workspace, "checkout");
|
|
@@ -18675,27 +18743,37 @@ var LocalBuildExecutor = class {
|
|
|
18675
18743
|
message: `Build cache disabled: ${compressor} is not installed. Install it to reuse dependencies between deploys.`
|
|
18676
18744
|
});
|
|
18677
18745
|
}
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
18687
|
-
|
|
18688
|
-
|
|
18689
|
-
|
|
18690
|
-
|
|
18691
|
-
|
|
18692
|
-
|
|
18693
|
-
|
|
18694
|
-
|
|
18695
|
-
|
|
18696
|
-
|
|
18697
|
-
|
|
18698
|
-
|
|
18746
|
+
if (input.commitSha !== void 0) {
|
|
18747
|
+
await this.checkoutPinnedCommit(
|
|
18748
|
+
input.commitSha,
|
|
18749
|
+
input.repositoryUrl,
|
|
18750
|
+
workspace,
|
|
18751
|
+
checkout,
|
|
18752
|
+
observer
|
|
18753
|
+
);
|
|
18754
|
+
} else {
|
|
18755
|
+
await this.dependencies.runCommand(
|
|
18756
|
+
{
|
|
18757
|
+
command: "git",
|
|
18758
|
+
args: [
|
|
18759
|
+
"clone",
|
|
18760
|
+
"--depth",
|
|
18761
|
+
"1",
|
|
18762
|
+
"--branch",
|
|
18763
|
+
input.branch,
|
|
18764
|
+
"--single-branch",
|
|
18765
|
+
"--",
|
|
18766
|
+
input.repositoryUrl,
|
|
18767
|
+
checkout
|
|
18768
|
+
],
|
|
18769
|
+
cwd: workspace,
|
|
18770
|
+
timeoutMs: 12e4,
|
|
18771
|
+
stage: "CLONING",
|
|
18772
|
+
errorCode: "CLONE_FAILED"
|
|
18773
|
+
},
|
|
18774
|
+
observer
|
|
18775
|
+
);
|
|
18776
|
+
}
|
|
18699
18777
|
const revision = await this.dependencies.runCommand(
|
|
18700
18778
|
{
|
|
18701
18779
|
command: "git",
|
|
@@ -18714,6 +18792,12 @@ var LocalBuildExecutor = class {
|
|
|
18714
18792
|
"Git returned an invalid commit SHA."
|
|
18715
18793
|
);
|
|
18716
18794
|
}
|
|
18795
|
+
if (input.commitSha !== void 0 && gitCommitSha !== input.commitSha) {
|
|
18796
|
+
throw new DeploymentError(
|
|
18797
|
+
"CLONE_FAILED",
|
|
18798
|
+
`Checked out ${gitCommitSha.slice(0, 7)} instead of the pinned commit ${input.commitSha.slice(0, 7)}.`
|
|
18799
|
+
);
|
|
18800
|
+
}
|
|
18717
18801
|
const reportedSha = gitCommitSha.length === 40 ? gitCommitSha : null;
|
|
18718
18802
|
if (!reportedSha)
|
|
18719
18803
|
await info2(
|
|
@@ -19129,6 +19213,14 @@ var NaroError = class extends Error {
|
|
|
19129
19213
|
if (options?.details !== void 0) this.details = options.details;
|
|
19130
19214
|
}
|
|
19131
19215
|
};
|
|
19216
|
+
var PROMOTE_TARGET_UNSUPPORTED_MESSAGE = "This control plane cannot promote to an environment yet. Deploy the latest web first, then try again.";
|
|
19217
|
+
function promoteTargetUnsupported(cause) {
|
|
19218
|
+
return new NaroError(
|
|
19219
|
+
"PROMOTE_TARGET_UNSUPPORTED",
|
|
19220
|
+
PROMOTE_TARGET_UNSUPPORTED_MESSAGE,
|
|
19221
|
+
cause !== void 0 ? { cause } : void 0
|
|
19222
|
+
);
|
|
19223
|
+
}
|
|
19132
19224
|
function isNaroError(error51) {
|
|
19133
19225
|
return error51 instanceof NaroError || error51 instanceof Error && error51.name === "NaroError" && typeof error51.code === "string";
|
|
19134
19226
|
}
|
|
@@ -19234,7 +19326,7 @@ var STYLES = `
|
|
|
19234
19326
|
*{box-sizing:border-box}
|
|
19235
19327
|
:root{
|
|
19236
19328
|
--bg:#000;--surface:#0a0a0a;--surface-2:#111;--border:#232323;
|
|
19237
|
-
--text:#ededed;--muted:#a1a1a1;--faint:#
|
|
19329
|
+
--text:#ededed;--muted:#a1a1a1;--faint:#808080;
|
|
19238
19330
|
--sans:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
19239
19331
|
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
|
19240
19332
|
}
|
|
@@ -19698,6 +19790,8 @@ function deploymentInfo(row) {
|
|
|
19698
19790
|
errorMessage: row.error_message,
|
|
19699
19791
|
gitCommitSha: row.git_commit_sha,
|
|
19700
19792
|
gitCommitMessage: row.git_commit_message,
|
|
19793
|
+
pinnedCommitSha: row.pinned_commit_sha ?? null,
|
|
19794
|
+
rebuiltFromDeploymentId: row.rebuilt_from_deployment_id ?? null,
|
|
19701
19795
|
createdAt: row.created_at,
|
|
19702
19796
|
startedAt: row.started_at,
|
|
19703
19797
|
finishedAt: row.finished_at
|
|
@@ -19730,6 +19824,7 @@ var NaroClient = class {
|
|
|
19730
19824
|
db;
|
|
19731
19825
|
backend;
|
|
19732
19826
|
storage;
|
|
19827
|
+
realtime;
|
|
19733
19828
|
insights;
|
|
19734
19829
|
analytics;
|
|
19735
19830
|
protection;
|
|
@@ -19749,6 +19844,7 @@ var NaroClient = class {
|
|
|
19749
19844
|
this.db = new DbApi(this);
|
|
19750
19845
|
this.backend = new BackendApi(this);
|
|
19751
19846
|
this.storage = new StorageApi(this);
|
|
19847
|
+
this.realtime = new RealtimeApi(this);
|
|
19752
19848
|
this.insights = new InsightsApi(this);
|
|
19753
19849
|
this.analytics = new AnalyticsApi(this);
|
|
19754
19850
|
this.protection = new ProtectionApi(this);
|
|
@@ -19949,16 +20045,65 @@ var DeploymentsApi = class {
|
|
|
19949
20045
|
return { current, target: production.slice(1).find(differs) ?? null };
|
|
19950
20046
|
}
|
|
19951
20047
|
/**
|
|
19952
|
-
*
|
|
19953
|
-
*
|
|
19954
|
-
* (older production build → production).
|
|
20048
|
+
* Promotes an existing READY deployment: with no `target` (or
|
|
20049
|
+
* `production`) it goes to production — `naro promote` (preview →
|
|
20050
|
+
* production) and `naro rollback` (older production build → production).
|
|
20051
|
+
* A deployment from an environment is rebuilt from its commit with
|
|
20052
|
+
* production's variables (`mode: "rebuild"`); any other is published as
|
|
20053
|
+
* stored (`mode: "reupload"`). With an environment `target` the control
|
|
20054
|
+
* plane likewise rebuilds the deployment's commit with that environment's
|
|
20055
|
+
* variables or publishes the stored build.
|
|
20056
|
+
*
|
|
20057
|
+
* No `target` sends no body, exactly as clients before targets did. A
|
|
20058
|
+
* control plane from before targets ignores `target` and promotes to
|
|
20059
|
+
* production, so an environment target is first checked with
|
|
20060
|
+
* `promoteCheck`: a 405 there (no such route) stops with
|
|
20061
|
+
* `PROMOTE_TARGET_UNSUPPORTED` before anything is promoted. The target is
|
|
20062
|
+
* trimmed once, as the control plane trims it, so `" production"` is
|
|
20063
|
+
* production and asks no preview.
|
|
20064
|
+
*/
|
|
20065
|
+
async promote(projectId, deploymentId, options = {}) {
|
|
20066
|
+
const target2 = options.target?.trim();
|
|
20067
|
+
if (target2 !== void 0 && target2 !== "production") {
|
|
20068
|
+
try {
|
|
20069
|
+
await this.promoteCheck(projectId, deploymentId, target2);
|
|
20070
|
+
} catch (error51) {
|
|
20071
|
+
if (isNaroError(error51) && error51.status === 405) {
|
|
20072
|
+
throw promoteTargetUnsupported(error51);
|
|
20073
|
+
}
|
|
20074
|
+
throw error51;
|
|
20075
|
+
}
|
|
20076
|
+
}
|
|
20077
|
+
const body = await this.client.request(this.promotePath(projectId, deploymentId), {
|
|
20078
|
+
method: "POST",
|
|
20079
|
+
...target2 !== void 0 ? { body: { target: target2 } } : {},
|
|
20080
|
+
context: "Promote deployment"
|
|
20081
|
+
});
|
|
20082
|
+
return {
|
|
20083
|
+
...deploymentInfo(body.deployment),
|
|
20084
|
+
mode: body.mode ?? "reupload"
|
|
20085
|
+
};
|
|
20086
|
+
}
|
|
20087
|
+
/**
|
|
20088
|
+
* What promoting to `target` would do, changing nothing: how it gets its
|
|
20089
|
+
* build, the migrations the target's database has not applied (null when
|
|
20090
|
+
* they cannot be compared) and the warnings to show first. Answers 405 from
|
|
20091
|
+
* a control plane that predates promote targets.
|
|
19955
20092
|
*/
|
|
19956
|
-
async
|
|
20093
|
+
async promoteCheck(projectId, deploymentId, target2) {
|
|
20094
|
+
const query = target2 !== void 0 ? `?target=${encodeURIComponent(target2)}` : "";
|
|
19957
20095
|
const body = await this.client.request(
|
|
19958
|
-
|
|
19959
|
-
{
|
|
20096
|
+
`${this.promotePath(projectId, deploymentId)}${query}`,
|
|
20097
|
+
{ context: "Check promotion" }
|
|
19960
20098
|
);
|
|
19961
|
-
return
|
|
20099
|
+
return {
|
|
20100
|
+
mode: body.mode ?? "reupload",
|
|
20101
|
+
pendingMigrations: body.pendingMigrations ?? null,
|
|
20102
|
+
warnings: body.warnings ?? []
|
|
20103
|
+
};
|
|
20104
|
+
}
|
|
20105
|
+
promotePath(projectId, deploymentId) {
|
|
20106
|
+
return `/api/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/promote`;
|
|
19962
20107
|
}
|
|
19963
20108
|
/**
|
|
19964
20109
|
* Stops an in-flight build (QUEUED through BUILDING) and marks the row
|
|
@@ -20713,6 +20858,7 @@ function storageInfo(body) {
|
|
|
20713
20858
|
return {
|
|
20714
20859
|
enabled: body.enabled,
|
|
20715
20860
|
bucket: body.bucket ?? null,
|
|
20861
|
+
disabledAt: body.disabledAt ?? null,
|
|
20716
20862
|
limits: {
|
|
20717
20863
|
bytes: body.limits.bytes,
|
|
20718
20864
|
objectBytes: body.limits.objectBytes,
|
|
@@ -20724,6 +20870,18 @@ function storageInfo(body) {
|
|
|
20724
20870
|
buckets: (body.buckets ?? []).map(storageBucket)
|
|
20725
20871
|
};
|
|
20726
20872
|
}
|
|
20873
|
+
function provisionFailure(error51, feature) {
|
|
20874
|
+
const details = isNaroError(error51) ? error51.details : void 0;
|
|
20875
|
+
if (isNaroError(error51) && error51.code === "BACKEND_PROVISION_FAILED" && isRecord(details?.[feature]) && isRecord(details.backend))
|
|
20876
|
+
return new NaroError(error51.code, error51.message, {
|
|
20877
|
+
...error51.status !== void 0 ? { status: error51.status } : {},
|
|
20878
|
+
details: {
|
|
20879
|
+
...details,
|
|
20880
|
+
backend: backendInfo(details.backend)
|
|
20881
|
+
}
|
|
20882
|
+
});
|
|
20883
|
+
return error51;
|
|
20884
|
+
}
|
|
20727
20885
|
function bucketFields(fields, into = {}) {
|
|
20728
20886
|
for (const key of ["public", "fileSizeLimit", "allowedMimeTypes"])
|
|
20729
20887
|
if (fields[key] !== void 0) into[key] = fields[key];
|
|
@@ -21152,16 +21310,45 @@ var StorageApi = class {
|
|
|
21152
21310
|
}
|
|
21153
21311
|
);
|
|
21154
21312
|
} catch (error51) {
|
|
21155
|
-
|
|
21156
|
-
|
|
21157
|
-
|
|
21158
|
-
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
21163
|
-
|
|
21164
|
-
|
|
21313
|
+
throw provisionFailure(error51, "storage");
|
|
21314
|
+
}
|
|
21315
|
+
return storageInfo(body.storage);
|
|
21316
|
+
}
|
|
21317
|
+
/**
|
|
21318
|
+
* Turns Storage off and keeps its files (Storage 3-1k): the Naro worker is
|
|
21319
|
+
* uploaded again without the bucket, so the app's Storage calls and file
|
|
21320
|
+
* URLs answer `404 STORAGE_NOT_ENABLED` until `enable` binds the same
|
|
21321
|
+
* bucket again. The bucket, its files and their storage cost stay; while
|
|
21322
|
+
* it is off, bucket writes and the object routes answer `409
|
|
21323
|
+
* STORAGE_OFF`, and reads go on. Safe to repeat: Storage never on, or
|
|
21324
|
+
* already off, comes back as it is, and a turn-off that stopped is
|
|
21325
|
+
* finished. Two minutes, like `enable`.
|
|
21326
|
+
*
|
|
21327
|
+
* Refusals, in the order they are checked: `BACKEND_DISABLED`,
|
|
21328
|
+
* `FORBIDDEN` (`storage.manage`), `DATABASE_NOT_ENABLED`,
|
|
21329
|
+
* `BACKEND_NOT_READY` (no backend), `BACKEND_OFF`, `BACKEND_BUSY` (another
|
|
21330
|
+
* change holds the backend — or turning Storage on again took this
|
|
21331
|
+
* turn-off over, and Storage is on), `STORAGE_DISABLE_FAILED` (502: the
|
|
21332
|
+
* turn-off could not be recorded; nothing changed), and
|
|
21333
|
+
* `BACKEND_PROVISION_FAILED` (502) when it is
|
|
21334
|
+
* recorded but the worker's upload — or a step after it — stopped:
|
|
21335
|
+
* `details` holds `{ storage: { applied: false, enabled: false }, backend
|
|
21336
|
+
* }`, and `disable` again finishes. Never `STORAGE_DISABLED`: the control
|
|
21337
|
+
* plane's Storage switch does not hold a removal back.
|
|
21338
|
+
*/
|
|
21339
|
+
async disable(projectId) {
|
|
21340
|
+
let body;
|
|
21341
|
+
try {
|
|
21342
|
+
body = await this.client.request(
|
|
21343
|
+
this.path(projectId),
|
|
21344
|
+
{
|
|
21345
|
+
method: "DELETE",
|
|
21346
|
+
context: "Turn off Storage",
|
|
21347
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21348
|
+
}
|
|
21349
|
+
);
|
|
21350
|
+
} catch (error51) {
|
|
21351
|
+
throw provisionFailure(error51, "storage");
|
|
21165
21352
|
}
|
|
21166
21353
|
return storageInfo(body.storage);
|
|
21167
21354
|
}
|
|
@@ -21320,6 +21507,114 @@ var StorageApi = class {
|
|
|
21320
21507
|
return body.objects;
|
|
21321
21508
|
}
|
|
21322
21509
|
};
|
|
21510
|
+
var RealtimeApi = class {
|
|
21511
|
+
constructor(client) {
|
|
21512
|
+
this.client = client;
|
|
21513
|
+
}
|
|
21514
|
+
path(projectId, suffix = "") {
|
|
21515
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/realtime${suffix}`;
|
|
21516
|
+
}
|
|
21517
|
+
/**
|
|
21518
|
+
* Like `StorageApi.bucketPath`: URL parsing folds `.` and `..`, an empty
|
|
21519
|
+
* name is the collection, and `encodeURIComponent` throws on a lone
|
|
21520
|
+
* surrogate — so a name no URL can carry is refused here, before a
|
|
21521
|
+
* request. SQLite does make tables named `.` and `..`, and one with the
|
|
21522
|
+
* empty name (`CREATE TABLE "" (a)`); such a table cannot be published
|
|
21523
|
+
* or unpublished through this API.
|
|
21524
|
+
*/
|
|
21525
|
+
tablePath(projectId, table) {
|
|
21526
|
+
if (table === "" || table === "." || table === ".." || LONE_SURROGATE.test(table))
|
|
21527
|
+
throw new NaroError("INVALID_PATH", "The table name is not valid.");
|
|
21528
|
+
return this.path(projectId, `/tables/${encodeURIComponent(table)}`);
|
|
21529
|
+
}
|
|
21530
|
+
/**
|
|
21531
|
+
* Whether Realtime is on, the limits the hub enforces, the published
|
|
21532
|
+
* tables and the hub's own numbers. A project that never turned it on
|
|
21533
|
+
* answers `enabled: false` with no tables.
|
|
21534
|
+
*/
|
|
21535
|
+
async info(projectId) {
|
|
21536
|
+
const body = await this.client.request(
|
|
21537
|
+
this.path(projectId),
|
|
21538
|
+
{ context: "Read realtime" }
|
|
21539
|
+
);
|
|
21540
|
+
return body.realtime;
|
|
21541
|
+
}
|
|
21542
|
+
/**
|
|
21543
|
+
* Turns Realtime on: the request and the plan's limits are recorded, and
|
|
21544
|
+
* the Naro worker is uploaded again with its hub (a Durable Object). Safe
|
|
21545
|
+
* to repeat, and a repair — also after a plan change. The project must have
|
|
21546
|
+
* a backend, turned on, with its database. A backend in `error` or with a
|
|
21547
|
+
* run that stalled is not refused: its provisioning is resumed on the way.
|
|
21548
|
+
* Two minutes, like `backend.enable`.
|
|
21549
|
+
*
|
|
21550
|
+
* Refusals, in the order they are checked: `BACKEND_DISABLED` (the control
|
|
21551
|
+
* plane's backend switch), `FORBIDDEN` (`database.write`),
|
|
21552
|
+
* `REALTIME_DISABLED` (the control plane's Realtime switch),
|
|
21553
|
+
* `DATABASE_NOT_ENABLED` (the project's database is external),
|
|
21554
|
+
* `BACKEND_NOT_READY` (409, only when the project has no backend at all),
|
|
21555
|
+
* `BACKEND_OFF`, `BACKEND_BUSY`, `REALTIME_ENABLE_FAILED` (502: the request
|
|
21556
|
+
* could not be recorded; nothing changed), and `BACKEND_PROVISION_FAILED`
|
|
21557
|
+
* (502) when Realtime is recorded as on but the worker's upload stopped.
|
|
21558
|
+
* The last one's `details` hold a `RealtimeProvisionFailure` — `{ realtime:
|
|
21559
|
+
* { applied: false, enabled: true }, backend }`, the backend read like
|
|
21560
|
+
* `backend.info` — and `enable` again finishes.
|
|
21561
|
+
*/
|
|
21562
|
+
async enable(projectId) {
|
|
21563
|
+
let body;
|
|
21564
|
+
try {
|
|
21565
|
+
body = await this.client.request(
|
|
21566
|
+
this.path(projectId),
|
|
21567
|
+
{
|
|
21568
|
+
method: "POST",
|
|
21569
|
+
context: "Turn on Realtime",
|
|
21570
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
21571
|
+
}
|
|
21572
|
+
);
|
|
21573
|
+
} catch (error51) {
|
|
21574
|
+
throw provisionFailure(error51, "realtime");
|
|
21575
|
+
}
|
|
21576
|
+
return body.realtime;
|
|
21577
|
+
}
|
|
21578
|
+
/**
|
|
21579
|
+
* The tables whose Data API writes are published, in the order they were
|
|
21580
|
+
* published. `exists: false` is a published name whose table was dropped.
|
|
21581
|
+
* `REALTIME_NOT_ENABLED` (409) before `enable`; it answers while the
|
|
21582
|
+
* backend is off, since the list is in the project's database.
|
|
21583
|
+
*/
|
|
21584
|
+
async listTables(projectId) {
|
|
21585
|
+
const body = await this.client.request(
|
|
21586
|
+
this.path(projectId, "/tables"),
|
|
21587
|
+
{ context: "List realtime tables" }
|
|
21588
|
+
);
|
|
21589
|
+
return body.tables;
|
|
21590
|
+
}
|
|
21591
|
+
/**
|
|
21592
|
+
* Publishes a table's changes to the subscribers of its channel. Safe to
|
|
21593
|
+
* repeat. Only changes made through the Data API are published — writes
|
|
21594
|
+
* the app makes with `env.DB` are not (`warnings` says so).
|
|
21595
|
+
* `TABLE_NOT_FOUND` (404) for a table that does not exist;
|
|
21596
|
+
* `VALIDATION_FAILED` (400) for a Naro system table, a virtual table, a
|
|
21597
|
+
* name over 256 characters, or the 100-table cap; `REALTIME_NOT_ENABLED`
|
|
21598
|
+
* (409) before `enable`.
|
|
21599
|
+
*/
|
|
21600
|
+
async publishTable(projectId, table) {
|
|
21601
|
+
return this.client.request(
|
|
21602
|
+
this.tablePath(projectId, table),
|
|
21603
|
+
{ method: "PUT", context: "Publish a realtime table" }
|
|
21604
|
+
);
|
|
21605
|
+
}
|
|
21606
|
+
/**
|
|
21607
|
+
* Stops publishing a table's changes; a table dropped since can still be
|
|
21608
|
+
* unpublished. `TABLE_NOT_PUBLISHED` (404) for a table that does not
|
|
21609
|
+
* publish, `REALTIME_NOT_ENABLED` (409) before `enable`.
|
|
21610
|
+
*/
|
|
21611
|
+
async unpublishTable(projectId, table) {
|
|
21612
|
+
return this.client.request(
|
|
21613
|
+
this.tablePath(projectId, table),
|
|
21614
|
+
{ method: "DELETE", context: "Stop publishing a realtime table" }
|
|
21615
|
+
);
|
|
21616
|
+
}
|
|
21617
|
+
};
|
|
21323
21618
|
|
|
21324
21619
|
// ../../packages/sdk/src/database.ts
|
|
21325
21620
|
var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
|
|
@@ -21717,93 +22012,272 @@ function detectFramework2(dir) {
|
|
|
21717
22012
|
};
|
|
21718
22013
|
}
|
|
21719
22014
|
|
|
21720
|
-
// ../../packages/data-core/src/
|
|
21721
|
-
|
|
21722
|
-
|
|
21723
|
-
|
|
21724
|
-
|
|
21725
|
-
}
|
|
21726
|
-
return false;
|
|
21727
|
-
}
|
|
21728
|
-
function hasBidiControl(text2) {
|
|
21729
|
-
for (let i = 0; i < text2.length; i += 1) {
|
|
21730
|
-
const code = text2.charCodeAt(i);
|
|
21731
|
-
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
21732
|
-
return true;
|
|
21733
|
-
}
|
|
21734
|
-
return false;
|
|
21735
|
-
}
|
|
21736
|
-
|
|
21737
|
-
// ../../packages/data-core/src/storage-path.ts
|
|
21738
|
-
var MAX_SINGLE_UPLOAD_BYTES = 52428800;
|
|
21739
|
-
var MAX_OBJECT_NAME_BYTES = 900;
|
|
21740
|
-
var STORAGE_RESERVED_WORDS = [
|
|
21741
|
-
"authenticated",
|
|
21742
|
-
"copy",
|
|
21743
|
-
"info",
|
|
21744
|
-
"list",
|
|
21745
|
-
"move",
|
|
21746
|
-
"public",
|
|
21747
|
-
"render",
|
|
21748
|
-
"sign",
|
|
21749
|
-
"upload"
|
|
22015
|
+
// ../../packages/data-core/src/system-tables.ts
|
|
22016
|
+
var SYSTEM_TABLE_PREFIXES = [
|
|
22017
|
+
"_naro_",
|
|
22018
|
+
"_cf_",
|
|
22019
|
+
"sqlite_"
|
|
21750
22020
|
];
|
|
21751
|
-
var
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
21755
|
-
function hasLoneSurrogate(text2) {
|
|
21756
|
-
return LONE_SURROGATE2.test(text2);
|
|
22021
|
+
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
22022
|
+
function asciiLower(text2) {
|
|
22023
|
+
return text2.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
21757
22024
|
}
|
|
21758
|
-
function
|
|
21759
|
-
|
|
22025
|
+
function isSystemTable(name) {
|
|
22026
|
+
const lower = asciiLower(name);
|
|
22027
|
+
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
21760
22028
|
}
|
|
21761
|
-
function
|
|
21762
|
-
|
|
21763
|
-
if (name.length > MAX_OBJECT_NAME_BYTES || ENCODER.encode(name).length > MAX_OBJECT_NAME_BYTES)
|
|
21764
|
-
return refuse(`is longer than ${String(MAX_OBJECT_NAME_BYTES)} bytes`);
|
|
21765
|
-
if (name.startsWith("/") || name.endsWith("/"))
|
|
21766
|
-
return refuse("starts or ends with /");
|
|
21767
|
-
if (name.includes("//")) return refuse("has an empty segment (//)");
|
|
21768
|
-
for (const segment of name.split("/"))
|
|
21769
|
-
if (segment === "." || segment === "..")
|
|
21770
|
-
return refuse("has a . or .. segment");
|
|
21771
|
-
if (hasControlCharacter(name)) return refuse("has a control character");
|
|
21772
|
-
if (name.includes("\\")) return refuse("has a backslash");
|
|
21773
|
-
if (hasBidiControl(name))
|
|
21774
|
-
return refuse("has a bidirectional control character");
|
|
21775
|
-
return { ok: true, name };
|
|
22029
|
+
function likeLiteral(text2) {
|
|
22030
|
+
return text2.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
21776
22031
|
}
|
|
21777
|
-
|
|
21778
|
-
|
|
21779
|
-
|
|
22032
|
+
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
22033
|
+
function systemTablePredicate(column2 = "name") {
|
|
22034
|
+
if (!COLUMN_REFERENCE.test(column2))
|
|
22035
|
+
throw new Error(`Not a column reference: ${column2}`);
|
|
22036
|
+
return [
|
|
22037
|
+
...SYSTEM_TABLE_PREFIXES.map(
|
|
22038
|
+
(prefix) => `${column2} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
22039
|
+
),
|
|
22040
|
+
...SYSTEM_TABLE_NAMES.map(
|
|
22041
|
+
(name) => `${column2} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
22042
|
+
),
|
|
22043
|
+
`${column2} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
22044
|
+
].join(" AND ");
|
|
21780
22045
|
}
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
22046
|
+
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
22047
|
+
|
|
22048
|
+
// ../../packages/data-core/src/collation.ts
|
|
22049
|
+
var COLLATIONS = ["BINARY", "NOCASE", "RTRIM"];
|
|
22050
|
+
function isSpace(char) {
|
|
22051
|
+
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
21784
22052
|
}
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
var MEDIA_TYPE = new RegExp(`^${TOKEN_CHARACTERS}+/${TOKEN_CHARACTERS}+$`);
|
|
21788
|
-
var DEFAULT_MAX_AGE = 3600;
|
|
21789
|
-
var MAX_MAX_AGE = 31536e3;
|
|
21790
|
-
function parseCacheControl(header) {
|
|
21791
|
-
if (header === null) return { ok: true, maxAge: DEFAULT_MAX_AGE };
|
|
21792
|
-
const match = /^[ \t]*max-age=(\d{1,8})[ \t]*$/i.exec(header);
|
|
21793
|
-
if (match?.[1] === void 0) return { ok: false };
|
|
21794
|
-
const maxAge = Number(match[1]);
|
|
21795
|
-
return maxAge <= MAX_MAX_AGE ? { ok: true, maxAge } : { ok: false };
|
|
22053
|
+
function isWordChar(char) {
|
|
22054
|
+
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
21796
22055
|
}
|
|
21797
|
-
|
|
21798
|
-
|
|
21799
|
-
if (text2.length <= MAX_NAME_IN_MESSAGE) return text2;
|
|
21800
|
-
const cut = text2.slice(0, MAX_NAME_IN_MESSAGE);
|
|
21801
|
-
const last = cut.charCodeAt(MAX_NAME_IN_MESSAGE - 1);
|
|
21802
|
-
return last >= 55296 && last <= 56319 ? cut.slice(0, MAX_NAME_IN_MESSAGE - 1) : cut;
|
|
22056
|
+
function asciiUpper2(text2) {
|
|
22057
|
+
return text2.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
21803
22058
|
}
|
|
21804
|
-
|
|
21805
|
-
|
|
21806
|
-
|
|
22059
|
+
function quoted(sql, start, close, doubles) {
|
|
22060
|
+
let value = "";
|
|
22061
|
+
for (let index = start + 1; index < sql.length; index++) {
|
|
22062
|
+
const char = sql.charAt(index);
|
|
22063
|
+
if (char !== close) {
|
|
22064
|
+
value += char;
|
|
22065
|
+
continue;
|
|
22066
|
+
}
|
|
22067
|
+
if (doubles && sql.charAt(index + 1) === close) {
|
|
22068
|
+
value += close;
|
|
22069
|
+
index++;
|
|
22070
|
+
continue;
|
|
22071
|
+
}
|
|
22072
|
+
return { value, end: index + 1 };
|
|
22073
|
+
}
|
|
22074
|
+
return void 0;
|
|
22075
|
+
}
|
|
22076
|
+
function tokenize(sql) {
|
|
22077
|
+
const tokens = [];
|
|
22078
|
+
let index = 0;
|
|
22079
|
+
while (index < sql.length) {
|
|
22080
|
+
const char = sql.charAt(index);
|
|
22081
|
+
if (isSpace(char)) {
|
|
22082
|
+
index++;
|
|
22083
|
+
} else if (char === "-" && sql.charAt(index + 1) === "-") {
|
|
22084
|
+
const end = sql.indexOf("\n", index);
|
|
22085
|
+
index = end < 0 ? sql.length : end + 1;
|
|
22086
|
+
} else if (char === "/" && sql.charAt(index + 1) === "*") {
|
|
22087
|
+
const end = sql.indexOf("*/", index + 2);
|
|
22088
|
+
if (end < 0) return void 0;
|
|
22089
|
+
index = end + 2;
|
|
22090
|
+
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22091
|
+
const close = char === "[" ? "]" : char;
|
|
22092
|
+
const read = quoted(sql, index, close, char !== "[");
|
|
22093
|
+
if (!read) return void 0;
|
|
22094
|
+
tokens.push({
|
|
22095
|
+
kind: char === "'" ? "string" : "name",
|
|
22096
|
+
value: read.value
|
|
22097
|
+
});
|
|
22098
|
+
index = read.end;
|
|
22099
|
+
} else if (char === "(" || char === ")" || char === ",") {
|
|
22100
|
+
tokens.push({ kind: "punct", value: char });
|
|
22101
|
+
index++;
|
|
22102
|
+
} else if (isWordChar(char)) {
|
|
22103
|
+
let end = index;
|
|
22104
|
+
while (end < sql.length && isWordChar(sql.charAt(end))) end++;
|
|
22105
|
+
tokens.push({ kind: "word", value: sql.slice(index, end) });
|
|
22106
|
+
index = end;
|
|
22107
|
+
} else {
|
|
22108
|
+
tokens.push({ kind: "other", value: char });
|
|
22109
|
+
index++;
|
|
22110
|
+
}
|
|
22111
|
+
}
|
|
22112
|
+
return tokens;
|
|
22113
|
+
}
|
|
22114
|
+
function isWord2(token, word) {
|
|
22115
|
+
return token?.kind === "word" && asciiLower(token.value) === word;
|
|
22116
|
+
}
|
|
22117
|
+
function definitions(tokens) {
|
|
22118
|
+
const open = tokens.findIndex(
|
|
22119
|
+
(token) => token.kind === "punct" && token.value === "("
|
|
22120
|
+
);
|
|
22121
|
+
if (open < 0) return void 0;
|
|
22122
|
+
const found = [[]];
|
|
22123
|
+
let depth = 0;
|
|
22124
|
+
for (const token of tokens.slice(open + 1)) {
|
|
22125
|
+
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22126
|
+
if (token.kind === "punct" && token.value === ")") {
|
|
22127
|
+
if (depth === 0) return found;
|
|
22128
|
+
depth--;
|
|
22129
|
+
}
|
|
22130
|
+
if (depth === 0 && token.kind === "punct" && token.value === ",") {
|
|
22131
|
+
found.push([]);
|
|
22132
|
+
continue;
|
|
22133
|
+
}
|
|
22134
|
+
found.at(-1)?.push(token);
|
|
22135
|
+
}
|
|
22136
|
+
return void 0;
|
|
22137
|
+
}
|
|
22138
|
+
function collationOf(definition) {
|
|
22139
|
+
const named = [];
|
|
22140
|
+
let depth = 0;
|
|
22141
|
+
for (const [index, token] of definition.entries()) {
|
|
22142
|
+
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22143
|
+
else if (token.kind === "punct" && token.value === ")") depth--;
|
|
22144
|
+
else if (depth === 0 && index > 0 && isWord2(token, "collate")) {
|
|
22145
|
+
const name = definition[index + 1];
|
|
22146
|
+
if (!name || name.kind === "punct" || name.kind === "other") return null;
|
|
22147
|
+
named.push(asciiUpper2(name.value));
|
|
22148
|
+
}
|
|
22149
|
+
}
|
|
22150
|
+
if (named.length === 0) return "BINARY";
|
|
22151
|
+
const [first] = named;
|
|
22152
|
+
if (named.some((name) => name !== first)) return null;
|
|
22153
|
+
return COLLATIONS.includes(first ?? "") ? first : null;
|
|
22154
|
+
}
|
|
22155
|
+
function declaresStrict(sql) {
|
|
22156
|
+
if (typeof sql !== "string") return void 0;
|
|
22157
|
+
const tokens = tokenize(sql);
|
|
22158
|
+
if (!tokens || !isWord2(tokens[0], "create")) return void 0;
|
|
22159
|
+
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22160
|
+
if (!isWord2(kind, "table")) return void 0;
|
|
22161
|
+
const open = tokens.findIndex(
|
|
22162
|
+
(token) => token.kind === "punct" && token.value === "("
|
|
22163
|
+
);
|
|
22164
|
+
if (open < 0) return void 0;
|
|
22165
|
+
let depth = 0;
|
|
22166
|
+
for (const [index, token] of tokens.entries()) {
|
|
22167
|
+
if (index < open || token.kind !== "punct") continue;
|
|
22168
|
+
if (token.value === "(") depth++;
|
|
22169
|
+
else if (token.value === ")" && --depth === 0)
|
|
22170
|
+
return tokens.slice(index + 1).some((option) => isWord2(option, "strict"));
|
|
22171
|
+
}
|
|
22172
|
+
return void 0;
|
|
22173
|
+
}
|
|
22174
|
+
function declaredCollations(sql, columns) {
|
|
22175
|
+
const unknown2 = columns.map(() => null);
|
|
22176
|
+
if (typeof sql !== "string") return unknown2;
|
|
22177
|
+
const tokens = tokenize(sql);
|
|
22178
|
+
if (!tokens || !isWord2(tokens[0], "create")) return unknown2;
|
|
22179
|
+
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22180
|
+
if (!isWord2(kind, "table")) return unknown2;
|
|
22181
|
+
const found = definitions(tokens);
|
|
22182
|
+
if (!found || found.length < columns.length) return unknown2;
|
|
22183
|
+
const collations = [];
|
|
22184
|
+
for (const [index, column2] of columns.entries()) {
|
|
22185
|
+
const definition = found[index] ?? [];
|
|
22186
|
+
const name = definition[0];
|
|
22187
|
+
if (!name || name.kind === "punct" || name.kind === "other" || asciiLower(name.value) !== asciiLower(column2))
|
|
22188
|
+
return unknown2;
|
|
22189
|
+
collations.push(collationOf(definition));
|
|
22190
|
+
}
|
|
22191
|
+
return collations;
|
|
22192
|
+
}
|
|
22193
|
+
|
|
22194
|
+
// ../../packages/data-core/src/unicode-controls.ts
|
|
22195
|
+
function hasControlCharacter(text2) {
|
|
22196
|
+
for (let i = 0; i < text2.length; i += 1) {
|
|
22197
|
+
const code = text2.charCodeAt(i);
|
|
22198
|
+
if (code <= 31 || code >= 127 && code <= 159) return true;
|
|
22199
|
+
}
|
|
22200
|
+
return false;
|
|
22201
|
+
}
|
|
22202
|
+
function hasBidiControl(text2) {
|
|
22203
|
+
for (let i = 0; i < text2.length; i += 1) {
|
|
22204
|
+
const code = text2.charCodeAt(i);
|
|
22205
|
+
if (code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)
|
|
22206
|
+
return true;
|
|
22207
|
+
}
|
|
22208
|
+
return false;
|
|
22209
|
+
}
|
|
22210
|
+
|
|
22211
|
+
// ../../packages/data-core/src/storage-path.ts
|
|
22212
|
+
var MAX_SINGLE_UPLOAD_BYTES = 52428800;
|
|
22213
|
+
var MAX_OBJECT_NAME_BYTES = 900;
|
|
22214
|
+
var STORAGE_RESERVED_WORDS = [
|
|
22215
|
+
"authenticated",
|
|
22216
|
+
"copy",
|
|
22217
|
+
"info",
|
|
22218
|
+
"list",
|
|
22219
|
+
"move",
|
|
22220
|
+
"public",
|
|
22221
|
+
"render",
|
|
22222
|
+
"sign",
|
|
22223
|
+
"upload"
|
|
22224
|
+
];
|
|
22225
|
+
var RESERVED = new Set(STORAGE_RESERVED_WORDS);
|
|
22226
|
+
var ENCODER = new TextEncoder();
|
|
22227
|
+
var LONE_SURROGATE2 = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
22228
|
+
var LONE_SURROGATES = new RegExp(LONE_SURROGATE2.source, "g");
|
|
22229
|
+
function hasLoneSurrogate(text2) {
|
|
22230
|
+
return LONE_SURROGATE2.test(text2);
|
|
22231
|
+
}
|
|
22232
|
+
function refuse(reason) {
|
|
22233
|
+
return { ok: false, reason };
|
|
22234
|
+
}
|
|
22235
|
+
function checkWellFormed(name) {
|
|
22236
|
+
if (name === "") return refuse("is empty");
|
|
22237
|
+
if (name.length > MAX_OBJECT_NAME_BYTES || ENCODER.encode(name).length > MAX_OBJECT_NAME_BYTES)
|
|
22238
|
+
return refuse(`is longer than ${String(MAX_OBJECT_NAME_BYTES)} bytes`);
|
|
22239
|
+
if (name.startsWith("/") || name.endsWith("/"))
|
|
22240
|
+
return refuse("starts or ends with /");
|
|
22241
|
+
if (name.includes("//")) return refuse("has an empty segment (//)");
|
|
22242
|
+
for (const segment of name.split("/"))
|
|
22243
|
+
if (segment === "." || segment === "..")
|
|
22244
|
+
return refuse("has a . or .. segment");
|
|
22245
|
+
if (hasControlCharacter(name)) return refuse("has a control character");
|
|
22246
|
+
if (name.includes("\\")) return refuse("has a backslash");
|
|
22247
|
+
if (hasBidiControl(name))
|
|
22248
|
+
return refuse("has a bidirectional control character");
|
|
22249
|
+
return { ok: true, name };
|
|
22250
|
+
}
|
|
22251
|
+
function checkObjectName(name) {
|
|
22252
|
+
if (hasLoneSurrogate(name)) return refuse("is not valid UTF-8");
|
|
22253
|
+
return checkWellFormed(name);
|
|
22254
|
+
}
|
|
22255
|
+
function objectParent(name) {
|
|
22256
|
+
const slash = name.lastIndexOf("/");
|
|
22257
|
+
return slash < 0 ? "" : name.slice(0, slash);
|
|
22258
|
+
}
|
|
22259
|
+
var TOKEN_CHARACTERS = "[!#$%&'*+\\-.^_`|~0-9A-Za-z]";
|
|
22260
|
+
var TOKEN = new RegExp(`^${TOKEN_CHARACTERS}+$`);
|
|
22261
|
+
var MEDIA_TYPE = new RegExp(`^${TOKEN_CHARACTERS}+/${TOKEN_CHARACTERS}+$`);
|
|
22262
|
+
var DEFAULT_MAX_AGE = 3600;
|
|
22263
|
+
var MAX_MAX_AGE = 31536e3;
|
|
22264
|
+
function parseCacheControl(header) {
|
|
22265
|
+
if (header === null) return { ok: true, maxAge: DEFAULT_MAX_AGE };
|
|
22266
|
+
const match = /^[ \t]*max-age=(\d{1,8})[ \t]*$/i.exec(header);
|
|
22267
|
+
if (match?.[1] === void 0) return { ok: false };
|
|
22268
|
+
const maxAge = Number(match[1]);
|
|
22269
|
+
return maxAge <= MAX_MAX_AGE ? { ok: true, maxAge } : { ok: false };
|
|
22270
|
+
}
|
|
22271
|
+
var MAX_NAME_IN_MESSAGE = 256;
|
|
22272
|
+
function storageName(text2) {
|
|
22273
|
+
if (text2.length <= MAX_NAME_IN_MESSAGE) return text2;
|
|
22274
|
+
const cut = text2.slice(0, MAX_NAME_IN_MESSAGE);
|
|
22275
|
+
const last = cut.charCodeAt(MAX_NAME_IN_MESSAGE - 1);
|
|
22276
|
+
return last >= 55296 && last <= 56319 ? cut.slice(0, MAX_NAME_IN_MESSAGE - 1) : cut;
|
|
22277
|
+
}
|
|
22278
|
+
|
|
22279
|
+
// ../../packages/data-core/src/storage-table.ts
|
|
22280
|
+
var STORAGE_OBJECTS = "storage.objects";
|
|
21807
22281
|
var STORAGE_OBJECTS_TABLE = "_naro_storage_objects";
|
|
21808
22282
|
var STORAGE_OBJECTS_QUALIFIER = "objects";
|
|
21809
22283
|
var STORAGE_UNAVAILABLE_MESSAGE = "storage.objects is not available on this backend yet. Run `naro backend enable` to update it.";
|
|
@@ -21861,39 +22335,6 @@ var STORAGE_BUCKET_DELETING_COLUMN = {
|
|
|
21861
22335
|
definition: "TEXT"
|
|
21862
22336
|
};
|
|
21863
22337
|
|
|
21864
|
-
// ../../packages/data-core/src/system-tables.ts
|
|
21865
|
-
var SYSTEM_TABLE_PREFIXES = [
|
|
21866
|
-
"_naro_",
|
|
21867
|
-
"_cf_",
|
|
21868
|
-
"sqlite_"
|
|
21869
|
-
];
|
|
21870
|
-
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
21871
|
-
function asciiLower(text2) {
|
|
21872
|
-
return text2.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
21873
|
-
}
|
|
21874
|
-
function isSystemTable(name) {
|
|
21875
|
-
const lower = asciiLower(name);
|
|
21876
|
-
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
21877
|
-
}
|
|
21878
|
-
function likeLiteral(text2) {
|
|
21879
|
-
return text2.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
21880
|
-
}
|
|
21881
|
-
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
21882
|
-
function systemTablePredicate(column2 = "name") {
|
|
21883
|
-
if (!COLUMN_REFERENCE.test(column2))
|
|
21884
|
-
throw new Error(`Not a column reference: ${column2}`);
|
|
21885
|
-
return [
|
|
21886
|
-
...SYSTEM_TABLE_PREFIXES.map(
|
|
21887
|
-
(prefix) => `${column2} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
21888
|
-
),
|
|
21889
|
-
...SYSTEM_TABLE_NAMES.map(
|
|
21890
|
-
(name) => `${column2} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
21891
|
-
),
|
|
21892
|
-
`${column2} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
21893
|
-
].join(" AND ");
|
|
21894
|
-
}
|
|
21895
|
-
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
21896
|
-
|
|
21897
22338
|
// ../../packages/data-core/src/errors.ts
|
|
21898
22339
|
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.";
|
|
21899
22340
|
var STORAGE_MIGRATION_PENDING_MESSAGE = "The project backend is partway through an update. Run `naro backend enable` to finish it, then try again.";
|
|
@@ -22171,7 +22612,7 @@ var OPERATORS = [
|
|
|
22171
22612
|
"*",
|
|
22172
22613
|
"-"
|
|
22173
22614
|
];
|
|
22174
|
-
function
|
|
22615
|
+
function isSpace2(char) {
|
|
22175
22616
|
return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f";
|
|
22176
22617
|
}
|
|
22177
22618
|
function isIdentStart(char) {
|
|
@@ -22184,7 +22625,7 @@ function isDigit(char) {
|
|
|
22184
22625
|
return char >= "0" && char <= "9";
|
|
22185
22626
|
}
|
|
22186
22627
|
var NUMBER = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
|
22187
|
-
function
|
|
22628
|
+
function tokenize2(text2) {
|
|
22188
22629
|
const tokens = [];
|
|
22189
22630
|
let index = 0;
|
|
22190
22631
|
const at = (offset = 0) => text2.charAt(index + offset);
|
|
@@ -22193,7 +22634,7 @@ function tokenize(text2) {
|
|
|
22193
22634
|
const position = index + 1;
|
|
22194
22635
|
if (char === "\0")
|
|
22195
22636
|
throw new PolicySyntaxError("NUL characters are not allowed", position);
|
|
22196
|
-
if (
|
|
22637
|
+
if (isSpace2(char)) {
|
|
22197
22638
|
index++;
|
|
22198
22639
|
continue;
|
|
22199
22640
|
}
|
|
@@ -22378,133 +22819,6 @@ var TokenStream = class {
|
|
|
22378
22819
|
}
|
|
22379
22820
|
};
|
|
22380
22821
|
|
|
22381
|
-
// ../../packages/data-core/src/collation.ts
|
|
22382
|
-
var COLLATIONS = ["BINARY", "NOCASE", "RTRIM"];
|
|
22383
|
-
function isSpace2(char) {
|
|
22384
|
-
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
22385
|
-
}
|
|
22386
|
-
function isWordChar(char) {
|
|
22387
|
-
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
22388
|
-
}
|
|
22389
|
-
function asciiUpper2(text2) {
|
|
22390
|
-
return text2.replace(/[a-z]/g, (letter) => letter.toUpperCase());
|
|
22391
|
-
}
|
|
22392
|
-
function quoted(sql, start, close, doubles) {
|
|
22393
|
-
let value = "";
|
|
22394
|
-
for (let index = start + 1; index < sql.length; index++) {
|
|
22395
|
-
const char = sql.charAt(index);
|
|
22396
|
-
if (char !== close) {
|
|
22397
|
-
value += char;
|
|
22398
|
-
continue;
|
|
22399
|
-
}
|
|
22400
|
-
if (doubles && sql.charAt(index + 1) === close) {
|
|
22401
|
-
value += close;
|
|
22402
|
-
index++;
|
|
22403
|
-
continue;
|
|
22404
|
-
}
|
|
22405
|
-
return { value, end: index + 1 };
|
|
22406
|
-
}
|
|
22407
|
-
return void 0;
|
|
22408
|
-
}
|
|
22409
|
-
function tokenize2(sql) {
|
|
22410
|
-
const tokens = [];
|
|
22411
|
-
let index = 0;
|
|
22412
|
-
while (index < sql.length) {
|
|
22413
|
-
const char = sql.charAt(index);
|
|
22414
|
-
if (isSpace2(char)) {
|
|
22415
|
-
index++;
|
|
22416
|
-
} else if (char === "-" && sql.charAt(index + 1) === "-") {
|
|
22417
|
-
const end = sql.indexOf("\n", index);
|
|
22418
|
-
index = end < 0 ? sql.length : end + 1;
|
|
22419
|
-
} else if (char === "/" && sql.charAt(index + 1) === "*") {
|
|
22420
|
-
const end = sql.indexOf("*/", index + 2);
|
|
22421
|
-
if (end < 0) return void 0;
|
|
22422
|
-
index = end + 2;
|
|
22423
|
-
} else if (char === '"' || char === "`" || char === "[" || char === "'") {
|
|
22424
|
-
const close = char === "[" ? "]" : char;
|
|
22425
|
-
const read = quoted(sql, index, close, char !== "[");
|
|
22426
|
-
if (!read) return void 0;
|
|
22427
|
-
tokens.push({
|
|
22428
|
-
kind: char === "'" ? "string" : "name",
|
|
22429
|
-
value: read.value
|
|
22430
|
-
});
|
|
22431
|
-
index = read.end;
|
|
22432
|
-
} else if (char === "(" || char === ")" || char === ",") {
|
|
22433
|
-
tokens.push({ kind: "punct", value: char });
|
|
22434
|
-
index++;
|
|
22435
|
-
} else if (isWordChar(char)) {
|
|
22436
|
-
let end = index;
|
|
22437
|
-
while (end < sql.length && isWordChar(sql.charAt(end))) end++;
|
|
22438
|
-
tokens.push({ kind: "word", value: sql.slice(index, end) });
|
|
22439
|
-
index = end;
|
|
22440
|
-
} else {
|
|
22441
|
-
tokens.push({ kind: "other", value: char });
|
|
22442
|
-
index++;
|
|
22443
|
-
}
|
|
22444
|
-
}
|
|
22445
|
-
return tokens;
|
|
22446
|
-
}
|
|
22447
|
-
function isWord2(token, word) {
|
|
22448
|
-
return token?.kind === "word" && asciiLower(token.value) === word;
|
|
22449
|
-
}
|
|
22450
|
-
function definitions(tokens) {
|
|
22451
|
-
const open = tokens.findIndex(
|
|
22452
|
-
(token) => token.kind === "punct" && token.value === "("
|
|
22453
|
-
);
|
|
22454
|
-
if (open < 0) return void 0;
|
|
22455
|
-
const found = [[]];
|
|
22456
|
-
let depth = 0;
|
|
22457
|
-
for (const token of tokens.slice(open + 1)) {
|
|
22458
|
-
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22459
|
-
if (token.kind === "punct" && token.value === ")") {
|
|
22460
|
-
if (depth === 0) return found;
|
|
22461
|
-
depth--;
|
|
22462
|
-
}
|
|
22463
|
-
if (depth === 0 && token.kind === "punct" && token.value === ",") {
|
|
22464
|
-
found.push([]);
|
|
22465
|
-
continue;
|
|
22466
|
-
}
|
|
22467
|
-
found.at(-1)?.push(token);
|
|
22468
|
-
}
|
|
22469
|
-
return void 0;
|
|
22470
|
-
}
|
|
22471
|
-
function collationOf(definition) {
|
|
22472
|
-
const named = [];
|
|
22473
|
-
let depth = 0;
|
|
22474
|
-
for (const [index, token] of definition.entries()) {
|
|
22475
|
-
if (token.kind === "punct" && token.value === "(") depth++;
|
|
22476
|
-
else if (token.kind === "punct" && token.value === ")") depth--;
|
|
22477
|
-
else if (depth === 0 && index > 0 && isWord2(token, "collate")) {
|
|
22478
|
-
const name = definition[index + 1];
|
|
22479
|
-
if (!name || name.kind === "punct" || name.kind === "other") return null;
|
|
22480
|
-
named.push(asciiUpper2(name.value));
|
|
22481
|
-
}
|
|
22482
|
-
}
|
|
22483
|
-
if (named.length === 0) return "BINARY";
|
|
22484
|
-
const [first] = named;
|
|
22485
|
-
if (named.some((name) => name !== first)) return null;
|
|
22486
|
-
return COLLATIONS.includes(first ?? "") ? first : null;
|
|
22487
|
-
}
|
|
22488
|
-
function declaredCollations(sql, columns) {
|
|
22489
|
-
const unknown2 = columns.map(() => null);
|
|
22490
|
-
if (typeof sql !== "string") return unknown2;
|
|
22491
|
-
const tokens = tokenize2(sql);
|
|
22492
|
-
if (!tokens || !isWord2(tokens[0], "create")) return unknown2;
|
|
22493
|
-
const kind = isWord2(tokens[1], "temp") || isWord2(tokens[1], "temporary") ? tokens[2] : tokens[1];
|
|
22494
|
-
if (!isWord2(kind, "table")) return unknown2;
|
|
22495
|
-
const found = definitions(tokens);
|
|
22496
|
-
if (!found || found.length < columns.length) return unknown2;
|
|
22497
|
-
const collations = [];
|
|
22498
|
-
for (const [index, column2] of columns.entries()) {
|
|
22499
|
-
const definition = found[index] ?? [];
|
|
22500
|
-
const name = definition[0];
|
|
22501
|
-
if (!name || name.kind === "punct" || name.kind === "other" || asciiLower(name.value) !== asciiLower(column2))
|
|
22502
|
-
return unknown2;
|
|
22503
|
-
collations.push(collationOf(definition));
|
|
22504
|
-
}
|
|
22505
|
-
return collations;
|
|
22506
|
-
}
|
|
22507
|
-
|
|
22508
22822
|
// ../../packages/data-core/src/realtime-table.ts
|
|
22509
22823
|
var REALTIME_MESSAGES = "realtime.messages";
|
|
22510
22824
|
var REALTIME_MESSAGES_NAMES = ["messages", REALTIME_MESSAGES];
|
|
@@ -22582,6 +22896,9 @@ function columnAffinity(column2) {
|
|
|
22582
22896
|
return "REAL";
|
|
22583
22897
|
return "NUMERIC";
|
|
22584
22898
|
}
|
|
22899
|
+
function isAnyType(type) {
|
|
22900
|
+
return asciiLower(type.trim()) === "any";
|
|
22901
|
+
}
|
|
22585
22902
|
function numberClass(type) {
|
|
22586
22903
|
const affinity2 = columnAffinity({ type });
|
|
22587
22904
|
if (affinity2 === "TEXT" || affinity2 === "BLOB") return void 0;
|
|
@@ -22783,6 +23100,9 @@ function utf8ByteLength(text2) {
|
|
|
22783
23100
|
function quoteIdentifier(name) {
|
|
22784
23101
|
return `"${name.replaceAll('"', '""')}"`;
|
|
22785
23102
|
}
|
|
23103
|
+
function qualifiedColumn(table, column2) {
|
|
23104
|
+
return `${quoteIdentifier(table)}.${quoteIdentifier(column2)}`;
|
|
23105
|
+
}
|
|
22786
23106
|
var SIDE_BY_SIDE = 8;
|
|
22787
23107
|
function joinConditions(terms, op) {
|
|
22788
23108
|
if (terms.length <= SIDE_BY_SIDE) return `(${terms.join(` ${op} `)})`;
|
|
@@ -22874,6 +23194,11 @@ function refusesText(item, text2) {
|
|
|
22874
23194
|
function readsAsIs(item, value) {
|
|
22875
23195
|
return typeof value === "string" && numberClass(item.type) === "fallback" && !storesAsNumber(value);
|
|
22876
23196
|
}
|
|
23197
|
+
function heldAsInteger(item, value) {
|
|
23198
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) return false;
|
|
23199
|
+
const affinity2 = columnAffinity(item);
|
|
23200
|
+
return affinity2 === "INTEGER" || affinity2 === "NUMERIC";
|
|
23201
|
+
}
|
|
22877
23202
|
function asStored(item, sql) {
|
|
22878
23203
|
switch (columnAffinity(item)) {
|
|
22879
23204
|
case "INTEGER":
|
|
@@ -22940,6 +23265,9 @@ function checkMarker(action, nonce, restrictive) {
|
|
|
22940
23265
|
function schemaMarker(nonce) {
|
|
22941
23266
|
return `json_extract('{}', 'naro:schema:${nonce}')`;
|
|
22942
23267
|
}
|
|
23268
|
+
function captureMarker(nonce) {
|
|
23269
|
+
return `json_extract('{}', 'naro:capture:${nonce}')`;
|
|
23270
|
+
}
|
|
22943
23271
|
|
|
22944
23272
|
// ../../packages/data-core/src/policy-expr.ts
|
|
22945
23273
|
var MAX_EXPRESSION_BYTES = 3 * 16384;
|
|
@@ -23867,7 +24195,7 @@ function checkExpressionSize(expr, position) {
|
|
|
23867
24195
|
}
|
|
23868
24196
|
function parseExpression(text2) {
|
|
23869
24197
|
try {
|
|
23870
|
-
const stream = new TokenStream(
|
|
24198
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
23871
24199
|
if (stream.peek().kind === "eof")
|
|
23872
24200
|
throw new PolicySyntaxError("the expression is empty", 1);
|
|
23873
24201
|
const expr = readCondition(stream);
|
|
@@ -24879,8 +25207,36 @@ function policyInvalid(policy, action) {
|
|
|
24879
25207
|
policy: policy.name
|
|
24880
25208
|
});
|
|
24881
25209
|
}
|
|
25210
|
+
var ROWID_NAMES = /* @__PURE__ */ new Set(["rowid", "oid", "_rowid_"]);
|
|
25211
|
+
function isRowidName(name) {
|
|
25212
|
+
return ROWID_NAMES.has(asciiLower(name));
|
|
25213
|
+
}
|
|
25214
|
+
function noteRowidColumn(state, table, column2) {
|
|
25215
|
+
const seen = state.rowidColumns.some(
|
|
25216
|
+
(item) => asciiLower(item.table) === asciiLower(table) && asciiLower(item.column) === asciiLower(column2)
|
|
25217
|
+
);
|
|
25218
|
+
if (!seen) state.rowidColumns.push({ table, column: column2 });
|
|
25219
|
+
}
|
|
25220
|
+
function rowidColumnsGuard(columns) {
|
|
25221
|
+
if (columns.length === 0) return null;
|
|
25222
|
+
const terms = columns.map(
|
|
25223
|
+
(item) => `EXISTS (SELECT 1 FROM pragma_table_xinfo(${quoteString(item.table)}) WHERE name = ${quoteString(item.column)} COLLATE NOCASE)`
|
|
25224
|
+
);
|
|
25225
|
+
return {
|
|
25226
|
+
sql: terms.length === 1 ? terms[0] ?? "" : joinConditions(terms, "AND"),
|
|
25227
|
+
params: []
|
|
25228
|
+
};
|
|
25229
|
+
}
|
|
25230
|
+
function rowidGuarded(columns, fragment) {
|
|
25231
|
+
const guard = rowidColumnsGuard(columns);
|
|
25232
|
+
if (!guard) return fragment;
|
|
25233
|
+
return {
|
|
25234
|
+
sql: `(CASE WHEN ${guard.sql} THEN ${fragment.sql} ELSE 0 END)`,
|
|
25235
|
+
params: [...guard.params, ...fragment.params]
|
|
25236
|
+
};
|
|
25237
|
+
}
|
|
24882
25238
|
function newState2() {
|
|
24883
|
-
return { aliases: 0, subqueries: 0, depth: 0, active: [] };
|
|
25239
|
+
return { aliases: 0, subqueries: 0, depth: 0, active: [], rowidColumns: [] };
|
|
24884
25240
|
}
|
|
24885
25241
|
function bind(env, value) {
|
|
24886
25242
|
env.params.push(value);
|
|
@@ -24890,10 +25246,15 @@ function blamed(env, reason) {
|
|
|
24890
25246
|
return fail(policyInvalid(env.blame.policy, env.blame.action), reason);
|
|
24891
25247
|
}
|
|
24892
25248
|
function storedSource(table, names, alias) {
|
|
25249
|
+
const qualifier = alias ?? table.name;
|
|
24893
25250
|
return {
|
|
24894
25251
|
table,
|
|
24895
25252
|
names,
|
|
24896
|
-
read: (column2,
|
|
25253
|
+
read: (column2, env) => {
|
|
25254
|
+
if (isRowidName(column2.name))
|
|
25255
|
+
noteRowidColumn(env.state, table.name, column2.name);
|
|
25256
|
+
return qualifiedColumn(qualifier, column2.name);
|
|
25257
|
+
},
|
|
24897
25258
|
operand: (column2) => ({
|
|
24898
25259
|
stable: true,
|
|
24899
25260
|
newColumnish: true,
|
|
@@ -24962,7 +25323,7 @@ function jwtValue(env, expr) {
|
|
|
24962
25323
|
if (expr.op === "->") return bind(env, JSON.stringify(value));
|
|
24963
25324
|
return bind(env, typeof value === "boolean" ? String(value) : value);
|
|
24964
25325
|
}
|
|
24965
|
-
function valueSql(env, expr, textual = null) {
|
|
25326
|
+
function valueSql(env, expr, textual = null, exact = false, ordered = false) {
|
|
24966
25327
|
switch (expr.kind) {
|
|
24967
25328
|
case "literal":
|
|
24968
25329
|
if (expr.value === null) return "NULL";
|
|
@@ -24972,8 +25333,9 @@ function valueSql(env, expr, textual = null) {
|
|
|
24972
25333
|
case "realtime": {
|
|
24973
25334
|
const { source, column: column2 } = columnOf(env, expr);
|
|
24974
25335
|
return source.read(column2, env, {
|
|
24975
|
-
|
|
24976
|
-
textual
|
|
25336
|
+
textual,
|
|
25337
|
+
exact: textual === null && exact,
|
|
25338
|
+
...textual === null && ordered ? { ordered: true } : {}
|
|
24977
25339
|
});
|
|
24978
25340
|
}
|
|
24979
25341
|
case "auth":
|
|
@@ -24984,7 +25346,10 @@ function valueSql(env, expr, textual = null) {
|
|
|
24984
25346
|
case "func": {
|
|
24985
25347
|
if (expr.name === "now") return "CURRENT_TIMESTAMP";
|
|
24986
25348
|
if (expr.name === "coalesce") {
|
|
24987
|
-
const
|
|
25349
|
+
const one = expr.args.length === 1;
|
|
25350
|
+
const args = expr.args.map(
|
|
25351
|
+
(arg2) => valueSql(env, arg2, textual, exact, one && ordered)
|
|
25352
|
+
);
|
|
24988
25353
|
return args.length === 1 ? `(${String(args[0])})` : `coalesce(${args.join(", ")})`;
|
|
24989
25354
|
}
|
|
24990
25355
|
const [arg] = expr.args;
|
|
@@ -25008,7 +25373,7 @@ function columnOf(env, expr) {
|
|
|
25008
25373
|
return expr.kind === "column" ? resolveColumn(env, expr) : realtimeTopic(env);
|
|
25009
25374
|
}
|
|
25010
25375
|
function pathArray(operand) {
|
|
25011
|
-
return `('[
|
|
25376
|
+
return `('[' || replace(json_quote(${operand()} || ''), '/', '","') || ']')`;
|
|
25012
25377
|
}
|
|
25013
25378
|
function storageHelperSql(fn, index, operand) {
|
|
25014
25379
|
switch (fn) {
|
|
@@ -25039,10 +25404,7 @@ function storageCallSql(env, expr) {
|
|
|
25039
25404
|
return storageHelperSql(
|
|
25040
25405
|
expr.fn,
|
|
25041
25406
|
index,
|
|
25042
|
-
() => source.read(column2, env, {
|
|
25043
|
-
qualified: env.scopes.length > 1,
|
|
25044
|
-
textual: "textual"
|
|
25045
|
-
})
|
|
25407
|
+
() => source.read(column2, env, { textual: "textual" })
|
|
25046
25408
|
);
|
|
25047
25409
|
}
|
|
25048
25410
|
function operandSide(env, value) {
|
|
@@ -25080,10 +25442,73 @@ var ARRAY_OPERATORS3 = {
|
|
|
25080
25442
|
"<@": "containedBy",
|
|
25081
25443
|
"&&": "overlaps"
|
|
25082
25444
|
};
|
|
25445
|
+
function readsBack(env) {
|
|
25446
|
+
return env.scopes.some((scope) => scope.readBack === true);
|
|
25447
|
+
}
|
|
25448
|
+
function textAffinity(env, value) {
|
|
25449
|
+
const expr = comparedValue(value);
|
|
25450
|
+
if (expr.kind === "cast") return true;
|
|
25451
|
+
if (expr.kind !== "column" && expr.kind !== "realtime") return false;
|
|
25452
|
+
try {
|
|
25453
|
+
return columnAffinity(columnOf(env, expr).column) === "TEXT";
|
|
25454
|
+
} catch {
|
|
25455
|
+
return true;
|
|
25456
|
+
}
|
|
25457
|
+
}
|
|
25458
|
+
function selectedTextAffinity(env, subquery) {
|
|
25459
|
+
if (subquery.select === "*") return true;
|
|
25460
|
+
const table = resolveSubqueryTable(env.snapshot, subquery.table);
|
|
25461
|
+
if (!table) return true;
|
|
25462
|
+
const source = storedSource(
|
|
25463
|
+
table,
|
|
25464
|
+
[asciiLower(subqueryName(subquery))],
|
|
25465
|
+
null
|
|
25466
|
+
);
|
|
25467
|
+
return textAffinity(
|
|
25468
|
+
{ ...env, scopes: [...env.scopes, source] },
|
|
25469
|
+
subquery.select
|
|
25470
|
+
);
|
|
25471
|
+
}
|
|
25472
|
+
var ORDERING = /* @__PURE__ */ new Set(["<", "<=", ">", ">="]);
|
|
25473
|
+
function orderSafe(env, value) {
|
|
25474
|
+
const expr = comparedValue(value);
|
|
25475
|
+
switch (expr.kind) {
|
|
25476
|
+
case "literal":
|
|
25477
|
+
return typeof expr.value !== "string" || !storesAsNumber(expr.value);
|
|
25478
|
+
case "func":
|
|
25479
|
+
return expr.name === "now";
|
|
25480
|
+
case "column": {
|
|
25481
|
+
let column2;
|
|
25482
|
+
try {
|
|
25483
|
+
column2 = columnOf(env, expr).column;
|
|
25484
|
+
} catch {
|
|
25485
|
+
return false;
|
|
25486
|
+
}
|
|
25487
|
+
const kind = numberClass(column2.type);
|
|
25488
|
+
return kind === "number" || kind === "fallback" && !isAnyType(column2.type);
|
|
25489
|
+
}
|
|
25490
|
+
default:
|
|
25491
|
+
return false;
|
|
25492
|
+
}
|
|
25493
|
+
}
|
|
25083
25494
|
function comparison(env, left, op, right) {
|
|
25084
|
-
const
|
|
25495
|
+
const back = readsBack(env);
|
|
25496
|
+
const ordered = back && ORDERING.has(op);
|
|
25497
|
+
const l = valueSql(
|
|
25498
|
+
env,
|
|
25499
|
+
left,
|
|
25500
|
+
null,
|
|
25501
|
+
back && !textAffinity(env, right),
|
|
25502
|
+
ordered && !orderSafe(env, right)
|
|
25503
|
+
);
|
|
25085
25504
|
const collate = env.check ? checkCollation(env, operandSide(env, left), operandSide(env, right)) : "";
|
|
25086
|
-
const r = valueSql(
|
|
25505
|
+
const r = valueSql(
|
|
25506
|
+
env,
|
|
25507
|
+
right,
|
|
25508
|
+
null,
|
|
25509
|
+
back && !textAffinity(env, left),
|
|
25510
|
+
ordered && !orderSafe(env, left)
|
|
25511
|
+
);
|
|
25087
25512
|
return `(${l}${collate} ${op} ${r})`;
|
|
25088
25513
|
}
|
|
25089
25514
|
function knownNullTest(env, expr) {
|
|
@@ -25128,22 +25553,36 @@ function conditionSql(env, expr) {
|
|
|
25128
25553
|
if (untestable !== void 0) return blamed(env, untestable);
|
|
25129
25554
|
const known = knownNullTest(env, expr);
|
|
25130
25555
|
if (known !== void 0) return known;
|
|
25131
|
-
const target2 = valueSql(env, expr.operand);
|
|
25556
|
+
const target2 = valueSql(env, expr.operand, null, true);
|
|
25132
25557
|
if (expr.value === null)
|
|
25133
25558
|
return `(${target2} IS ${expr.not ? "NOT " : ""}NULL)`;
|
|
25134
25559
|
const test = isBoolean(target2, expr.value);
|
|
25135
25560
|
return expr.not ? `(NOT (${test}))` : `(${test})`;
|
|
25136
25561
|
}
|
|
25137
25562
|
case "in": {
|
|
25138
|
-
const left = valueSql(env, expr.operand);
|
|
25563
|
+
const left = valueSql(env, expr.operand, null, true);
|
|
25139
25564
|
const collate = env.check ? checkCollation(env, operandSide(env, expr.operand), null) : "";
|
|
25140
|
-
const
|
|
25565
|
+
const itemsExact = readsBack(env) && !textAffinity(env, expr.operand);
|
|
25566
|
+
const list = expr.list.map(
|
|
25567
|
+
(item) => valueSql(env, item, null, itemsExact)
|
|
25568
|
+
);
|
|
25141
25569
|
return `(${left}${collate} ${expr.not ? "NOT " : ""}IN (${list.join(", ")}))`;
|
|
25142
25570
|
}
|
|
25143
25571
|
case "inSelect": {
|
|
25144
|
-
const
|
|
25572
|
+
const back = readsBack(env);
|
|
25573
|
+
const left = valueSql(
|
|
25574
|
+
env,
|
|
25575
|
+
expr.operand,
|
|
25576
|
+
null,
|
|
25577
|
+
back && !selectedTextAffinity(env, expr.subquery)
|
|
25578
|
+
);
|
|
25145
25579
|
const leftSide = env.check ? operandSide(env, expr.operand) : null;
|
|
25146
|
-
const sub = subquerySql(
|
|
25580
|
+
const sub = subquerySql(
|
|
25581
|
+
env,
|
|
25582
|
+
expr.subquery,
|
|
25583
|
+
"value",
|
|
25584
|
+
back && !textAffinity(env, expr.operand)
|
|
25585
|
+
);
|
|
25147
25586
|
const collate = leftSide && sub.item ? checkCollation(env, leftSide, sub.item) : "";
|
|
25148
25587
|
return `(${left}${collate} ${expr.not ? "NOT " : ""}IN (${sub.sql}))`;
|
|
25149
25588
|
}
|
|
@@ -25180,7 +25619,7 @@ function conditionSql(env, expr) {
|
|
|
25180
25619
|
return arraySql(
|
|
25181
25620
|
() => (() => {
|
|
25182
25621
|
const { source, column: column2 } = resolveColumn(env, expr.operand);
|
|
25183
|
-
return source.read(column2, env, {
|
|
25622
|
+
return source.read(column2, env, { textual: null });
|
|
25184
25623
|
})(),
|
|
25185
25624
|
ARRAY_OPERATORS3[expr.op],
|
|
25186
25625
|
values.length,
|
|
@@ -25192,7 +25631,7 @@ function conditionSql(env, expr) {
|
|
|
25192
25631
|
return `(EXISTS (${subquerySql(env, expr.subquery, "one").sql}))`;
|
|
25193
25632
|
}
|
|
25194
25633
|
}
|
|
25195
|
-
function subquerySql(env, subquery, select) {
|
|
25634
|
+
function subquerySql(env, subquery, select, selectExact = false) {
|
|
25196
25635
|
const table = resolveSubqueryTable(env.snapshot, subquery.table);
|
|
25197
25636
|
if (!table) return blamed(env, unresolvedSubqueryReason(subquery.table));
|
|
25198
25637
|
const { state } = env;
|
|
@@ -25217,7 +25656,7 @@ function subquerySql(env, subquery, select) {
|
|
|
25217
25656
|
if (select === "value") {
|
|
25218
25657
|
if (subquery.select === "*")
|
|
25219
25658
|
return blamed(env, "IN (SELECT \u2026) selects one value, not *");
|
|
25220
|
-
selected = valueSql(inner, subquery.select);
|
|
25659
|
+
selected = valueSql(inner, subquery.select, null, selectExact);
|
|
25221
25660
|
if (env.check) item = operandSide(inner, subquery.select);
|
|
25222
25661
|
}
|
|
25223
25662
|
const own2 = storedSource(table, ownNames(table), alias);
|
|
@@ -25570,7 +26009,9 @@ function selectCheckRefusal(path, column2, problem) {
|
|
|
25570
26009
|
generated: `is not available: the select policy reads the generated column ${column2}, which a new row cannot be checked against`,
|
|
25571
26010
|
collation: `is not available: the select policy reads ${column2}, whose collation naro cannot read, so a new row cannot be checked against it`,
|
|
25572
26011
|
pattern: `is not available: the select policy compares the number column ${column2} with like or ilike, which a new row cannot be checked against`,
|
|
25573
|
-
textual: `is not available: the select policy turns the number column ${column2} into text, which a new row cannot be checked against
|
|
26012
|
+
textual: `is not available: the select policy turns the number column ${column2} into text, which a new row cannot be checked against`,
|
|
26013
|
+
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`,
|
|
26014
|
+
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`
|
|
25574
26015
|
}[problem];
|
|
25575
26016
|
return invalidQuery(path, reason);
|
|
25576
26017
|
}
|
|
@@ -25632,6 +26073,15 @@ function decideQuery(context, query, policies, table) {
|
|
|
25632
26073
|
var MAX_PARAMETERS = 100;
|
|
25633
26074
|
var MAX_STATEMENT_BYTES = 1e5;
|
|
25634
26075
|
var MAX_INSERT_STATEMENTS = 100;
|
|
26076
|
+
var MAX_CAPTURE_ROWS = 1e3;
|
|
26077
|
+
var MAX_CAPTURE_BYTES = 8388608;
|
|
26078
|
+
function triggersRead(table) {
|
|
26079
|
+
const name = table.name;
|
|
26080
|
+
return statement(
|
|
26081
|
+
"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)",
|
|
26082
|
+
[name, name, name, name.replaceAll('"', '""'), name.replaceAll("`", "``")]
|
|
26083
|
+
);
|
|
26084
|
+
}
|
|
25635
26085
|
var OR_ABORT = "OR ABORT";
|
|
25636
26086
|
function column(table, name) {
|
|
25637
26087
|
return table.byName.get(asciiLower(name)) ?? fail(dataError("COLUMN_NOT_FOUND", { column: name, table: table.name }));
|
|
@@ -25647,7 +26097,13 @@ function columnList(table, names) {
|
|
|
25647
26097
|
}
|
|
25648
26098
|
return columns;
|
|
25649
26099
|
}
|
|
25650
|
-
function
|
|
26100
|
+
function columnSql(table, item) {
|
|
26101
|
+
return qualifiedColumn(table.name, item.name);
|
|
26102
|
+
}
|
|
26103
|
+
function outputList(table, columns) {
|
|
26104
|
+
return columns.map((item) => columnSql(table, item)).join(", ");
|
|
26105
|
+
}
|
|
26106
|
+
function columnNames(columns) {
|
|
25651
26107
|
return columns.map((item) => quoteIdentifier(item.name)).join(", ");
|
|
25652
26108
|
}
|
|
25653
26109
|
function returningClause(table, returning, action) {
|
|
@@ -25662,7 +26118,31 @@ function returningClause(table, returning, action) {
|
|
|
25662
26118
|
)
|
|
25663
26119
|
);
|
|
25664
26120
|
const columns = returning === "*" ? table.columns : columnList(table, returning);
|
|
25665
|
-
return ` RETURNING ${
|
|
26121
|
+
return ` RETURNING ${outputList(table, columns)}`;
|
|
26122
|
+
}
|
|
26123
|
+
function writeReturning(table, returning, action, capture) {
|
|
26124
|
+
const own2 = returningClause(table, returning, action);
|
|
26125
|
+
return capture ? ` RETURNING ${outputList(table, table.columns)}` : own2;
|
|
26126
|
+
}
|
|
26127
|
+
function balancedSum(terms) {
|
|
26128
|
+
if (terms.length === 0) return "0";
|
|
26129
|
+
if (terms.length === 1) return terms[0] ?? "0";
|
|
26130
|
+
const half = Math.ceil(terms.length / 2);
|
|
26131
|
+
return `(${balancedSum(terms.slice(0, half))} + ${balancedSum(terms.slice(half))})`;
|
|
26132
|
+
}
|
|
26133
|
+
function boundBytes(value) {
|
|
26134
|
+
return typeof value === "string" ? utf8ByteLength(value) : 8;
|
|
26135
|
+
}
|
|
26136
|
+
function captureGuard(table, where, params, nonce, set2) {
|
|
26137
|
+
const bytes = balancedSum(
|
|
26138
|
+
table.columns.map(
|
|
26139
|
+
(item) => set2.has(item.name) ? String(boundBytes(set2.get(item.name) ?? null)) : `coalesce(length(CAST(${qualifiedColumn(table.name, item.name)} AS BLOB)), 8)`
|
|
26140
|
+
)
|
|
26141
|
+
);
|
|
26142
|
+
return statement(
|
|
26143
|
+
`SELECT CASE WHEN n > ${String(MAX_CAPTURE_ROWS)} OR b > ${String(MAX_CAPTURE_BYTES)} THEN ${captureMarker(nonce)} ELSE 1 END AS ok FROM (SELECT count(*) AS n, coalesce(sum(r), 0) AS b FROM (SELECT ${bytes} AS r FROM ${quoteIdentifier(table.name)}${where} LIMIT ${String(MAX_CAPTURE_ROWS + 1)}))`,
|
|
26144
|
+
params
|
|
26145
|
+
);
|
|
25666
26146
|
}
|
|
25667
26147
|
function scalar(value, path) {
|
|
25668
26148
|
if (Array.isArray(value) || !isValue(value))
|
|
@@ -25683,7 +26163,7 @@ function arrayValues(value, path) {
|
|
|
25683
26163
|
}
|
|
25684
26164
|
function filterSql(table, filter, params, path) {
|
|
25685
26165
|
const resolved = column(table, filter.column);
|
|
25686
|
-
const target2 =
|
|
26166
|
+
const target2 = columnSql(table, resolved);
|
|
25687
26167
|
const valuePath = `${path}.value`;
|
|
25688
26168
|
switch (filter.operator) {
|
|
25689
26169
|
case "eq":
|
|
@@ -25735,10 +26215,9 @@ function filterSql(table, filter, params, path) {
|
|
|
25735
26215
|
case "containedBy":
|
|
25736
26216
|
case "overlaps": {
|
|
25737
26217
|
const values = arrayValues(filter.value, valuePath);
|
|
25738
|
-
const qualified = `${quoteIdentifier(table.name)}.${target2}`;
|
|
25739
26218
|
let next = 0;
|
|
25740
26219
|
return arraySql(
|
|
25741
|
-
() =>
|
|
26220
|
+
() => target2,
|
|
25742
26221
|
filter.operator,
|
|
25743
26222
|
values.length,
|
|
25744
26223
|
() => {
|
|
@@ -25761,8 +26240,12 @@ function nodeSql(table, node, params, path) {
|
|
|
25761
26240
|
);
|
|
25762
26241
|
return joinConditions(terms, key === "or" ? "OR" : "AND");
|
|
25763
26242
|
}
|
|
25764
|
-
function whereClause(table, filters, policy, params) {
|
|
26243
|
+
function whereClause(table, filters, policy, params, guard = null) {
|
|
25765
26244
|
const parts = [];
|
|
26245
|
+
if (guard) {
|
|
26246
|
+
parts.push(guard.sql);
|
|
26247
|
+
params.push(...guard.params);
|
|
26248
|
+
}
|
|
25766
26249
|
if (policy) {
|
|
25767
26250
|
parts.push(policy.sql);
|
|
25768
26251
|
params.push(...policy.params);
|
|
@@ -25778,7 +26261,7 @@ function orderClause(table, order) {
|
|
|
25778
26261
|
const terms = order.map((term) => {
|
|
25779
26262
|
const direction = term.ascending ? "ASC" : "DESC";
|
|
25780
26263
|
const nulls = term.nullsFirst === void 0 ? "" : term.nullsFirst ? " NULLS FIRST" : " NULLS LAST";
|
|
25781
|
-
return `${
|
|
26264
|
+
return `${columnSql(table, column(table, term.column))} ${direction}${nulls}`;
|
|
25782
26265
|
});
|
|
25783
26266
|
return ` ORDER BY ${terms.join(", ")}`;
|
|
25784
26267
|
}
|
|
@@ -25927,6 +26410,10 @@ function checkProblemReason(column2, problem) {
|
|
|
25927
26410
|
return `${column2} is a number column, which a WITH CHECK cannot compare with LIKE or ILIKE`;
|
|
25928
26411
|
case "textual":
|
|
25929
26412
|
return `${column2} is a number column, which a WITH CHECK cannot turn into text (lower, upper, length, ::text)`;
|
|
26413
|
+
case "readBack":
|
|
26414
|
+
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)`;
|
|
26415
|
+
case "orderedText":
|
|
26416
|
+
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)`;
|
|
25930
26417
|
}
|
|
25931
26418
|
}
|
|
25932
26419
|
function usingProblemReason(name, problem) {
|
|
@@ -25939,6 +26426,10 @@ function usingProblemReason(name, problem) {
|
|
|
25939
26426
|
return `${name} is a number column, which a USING judged on a row's values cannot compare with LIKE or ILIKE`;
|
|
25940
26427
|
case "textual":
|
|
25941
26428
|
return `${name} is a number column, which a USING judged on a row's values cannot turn into text (lower, upper, length, ::text)`;
|
|
26429
|
+
case "readBack":
|
|
26430
|
+
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)`;
|
|
26431
|
+
case "orderedText":
|
|
26432
|
+
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)`;
|
|
25942
26433
|
}
|
|
25943
26434
|
}
|
|
25944
26435
|
function isTrue(term) {
|
|
@@ -26347,6 +26838,19 @@ function schemaGuard(name, sql, kind, nonce) {
|
|
|
26347
26838
|
policy: void 0
|
|
26348
26839
|
};
|
|
26349
26840
|
}
|
|
26841
|
+
function rowidTerm(table, state, nonce, kind) {
|
|
26842
|
+
const own2 = newState2();
|
|
26843
|
+
for (const item of table.columns)
|
|
26844
|
+
if (isRowidName(item.name)) noteRowidColumn(own2, table.name, item.name);
|
|
26845
|
+
for (const read of state.rowidColumns)
|
|
26846
|
+
noteRowidColumn(own2, read.table, read.column);
|
|
26847
|
+
const guard = rowidColumnsGuard(own2.rowidColumns);
|
|
26848
|
+
return guard ? { ...guard, marker: schemaMarker(nonce), kind, policy: void 0 } : void 0;
|
|
26849
|
+
}
|
|
26850
|
+
function rowidWhere(table, state, nonce, kind) {
|
|
26851
|
+
const term = rowidTerm(table, state, nonce, kind);
|
|
26852
|
+
return term ? nestedChecks([term], { sql: "1", params: [] }) : null;
|
|
26853
|
+
}
|
|
26350
26854
|
function nestedChecks(terms, pass) {
|
|
26351
26855
|
let sql = pass.sql;
|
|
26352
26856
|
let params = pass.params;
|
|
@@ -26374,11 +26878,15 @@ function usingFragment(target2, req, snapshot, commands, alias) {
|
|
|
26374
26878
|
names: targetNames(target2),
|
|
26375
26879
|
alias
|
|
26376
26880
|
});
|
|
26377
|
-
return
|
|
26881
|
+
return rowidGuarded(
|
|
26882
|
+
rls.state.rowidColumns,
|
|
26883
|
+
rls.using(commands) ?? { sql: "1", params: [] }
|
|
26884
|
+
);
|
|
26378
26885
|
}
|
|
26379
|
-
function rowSource(target2, row, action, stored) {
|
|
26886
|
+
function rowSource(target2, row, action, stored, readBack = false) {
|
|
26380
26887
|
const { table } = target2;
|
|
26381
26888
|
const kept = new Set(stored?.columns ?? []);
|
|
26889
|
+
const strict = readBack ? declaresStrict(table.sql) : false;
|
|
26382
26890
|
const storedRow = stored ? storedSource(table, targetNames(target2), stored.alias) : void 0;
|
|
26383
26891
|
const source = {
|
|
26384
26892
|
table,
|
|
@@ -26403,14 +26911,21 @@ function rowSource(target2, row, action, stored) {
|
|
|
26403
26911
|
`the new row of ${target2.name} has no value for ${item.name}`
|
|
26404
26912
|
);
|
|
26405
26913
|
const bound = row[item.name] ?? null;
|
|
26406
|
-
|
|
26914
|
+
const anyStrict = readBack && isAnyType(item.type) ? strict : false;
|
|
26915
|
+
if (anyStrict !== true && typeof bound === "string" && refusesText(item, bound))
|
|
26407
26916
|
fail(invalidQuery(`row.${item.name}`, mustBeNumber(action)));
|
|
26917
|
+
if (readBack && typeof bound === "number" && (anyStrict === void 0 || (!isNumeric(item) || anyStrict) && how.exact !== true))
|
|
26918
|
+
check2.problem(item.name, "readBack", env.blame.policy);
|
|
26919
|
+
if (readBack && anyStrict !== true && how.ordered === true && readsAsIs(item, bound))
|
|
26920
|
+
check2.problem(item.name, "orderedText", env.blame.policy);
|
|
26408
26921
|
env.params.push(bound);
|
|
26409
|
-
|
|
26922
|
+
if (anyStrict === true || readsAsIs(item, bound)) return "?";
|
|
26923
|
+
return readBack && heldAsInteger(item, bound) ? "CAST(? AS INTEGER)" : asStored(item, "?");
|
|
26410
26924
|
},
|
|
26411
26925
|
// A stored column is the statement's own table's: SQLite compares it
|
|
26412
26926
|
// with its own collation, the snapshot's — which the guard holds to.
|
|
26413
|
-
operand: (item) => kept.has(item.name) ? { stable: true, newColumnish: true, newCollation: item.collation } : { stable: false, newColumnish: false, newCollation: null }
|
|
26927
|
+
operand: (item) => kept.has(item.name) ? { stable: true, newColumnish: true, newCollation: item.collation } : { stable: false, newColumnish: false, newCollation: null },
|
|
26928
|
+
...readBack ? { readBack } : {}
|
|
26414
26929
|
};
|
|
26415
26930
|
return source;
|
|
26416
26931
|
}
|
|
@@ -26450,7 +26965,17 @@ function checkFragment(target2, req, snapshot, command, row, nonce, stored, with
|
|
|
26450
26965
|
() => false,
|
|
26451
26966
|
() => false
|
|
26452
26967
|
);
|
|
26453
|
-
if (
|
|
26968
|
+
if (terms.length === 0) return terms;
|
|
26969
|
+
const rowid = rowidColumnsGuard(rls.state.rowidColumns);
|
|
26970
|
+
const guards = rowid ? [
|
|
26971
|
+
{
|
|
26972
|
+
...rowid,
|
|
26973
|
+
marker: schemaMarker(checked),
|
|
26974
|
+
kind: command,
|
|
26975
|
+
policy: void 0
|
|
26976
|
+
}
|
|
26977
|
+
] : [];
|
|
26978
|
+
if (!source.storedRead) return [...guards, ...terms];
|
|
26454
26979
|
const { name, sql } = target2.table;
|
|
26455
26980
|
if (sql === null) {
|
|
26456
26981
|
const first = terms.find((term) => term.policy !== void 0)?.policy;
|
|
@@ -26459,7 +26984,7 @@ function checkFragment(target2, req, snapshot, command, row, nonce, stored, with
|
|
|
26459
26984
|
`naro cannot read the schema of ${target2.name}, so a check of the stored row cannot be guarded`
|
|
26460
26985
|
);
|
|
26461
26986
|
}
|
|
26462
|
-
return [schemaGuard(name, sql, command, checked), ...terms];
|
|
26987
|
+
return [schemaGuard(name, sql, command, checked), ...guards, ...terms];
|
|
26463
26988
|
}
|
|
26464
26989
|
var Unsupported = class extends Error {
|
|
26465
26990
|
constructor(columnName, problem, policy) {
|
|
@@ -26469,9 +26994,15 @@ var Unsupported = class extends Error {
|
|
|
26469
26994
|
this.policy = policy;
|
|
26470
26995
|
}
|
|
26471
26996
|
};
|
|
26472
|
-
function policyValue(target2, req, snapshot, command, row, nonce, state = newState2()) {
|
|
26997
|
+
function policyValue(target2, req, snapshot, command, row, nonce, state = newState2(), options = {}) {
|
|
26473
26998
|
const checked = checkNonceOf(nonce, "policyValue");
|
|
26474
|
-
const source = rowSource(
|
|
26999
|
+
const source = rowSource(
|
|
27000
|
+
target2,
|
|
27001
|
+
row,
|
|
27002
|
+
"insert",
|
|
27003
|
+
void 0,
|
|
27004
|
+
options.readBack === true
|
|
27005
|
+
);
|
|
26475
27006
|
const rls = new Rls(
|
|
26476
27007
|
req,
|
|
26477
27008
|
snapshot,
|
|
@@ -26518,34 +27049,42 @@ function policyValue(target2, req, snapshot, command, row, nonce, state = newSta
|
|
|
26518
27049
|
].filter((term) => !isTrue(term));
|
|
26519
27050
|
const [only] = kept;
|
|
26520
27051
|
if (!only) return { ok: true, fragment: { sql: "1", params: [] } };
|
|
26521
|
-
|
|
27052
|
+
const read = state.rowidColumns;
|
|
27053
|
+
if (kept.length === 1)
|
|
27054
|
+
return { ok: true, fragment: rowidGuarded(read, only) };
|
|
26522
27055
|
return {
|
|
26523
27056
|
ok: true,
|
|
26524
|
-
fragment: {
|
|
27057
|
+
fragment: rowidGuarded(read, {
|
|
26525
27058
|
sql: `(${kept.map((term) => term.sql).join(" AND ")})`,
|
|
26526
27059
|
params: kept.flatMap((term) => term.params)
|
|
26527
|
-
}
|
|
27060
|
+
})
|
|
26528
27061
|
};
|
|
26529
27062
|
}
|
|
26530
|
-
function compileSelect(query, table, rls) {
|
|
27063
|
+
function compileSelect(query, table, rls, { state, nonce }) {
|
|
26531
27064
|
if (query.columns !== "*" && query.columns.length === 0)
|
|
26532
27065
|
fail(invalidQuery("columns", "must name at least one column"));
|
|
26533
27066
|
const columns = query.columns === "*" ? table.columns : columnList(table, query.columns);
|
|
26534
27067
|
const params = [];
|
|
26535
27068
|
const policy = rls?.where("select") ?? null;
|
|
26536
27069
|
if (rls && policy) rls.budget([policy]);
|
|
26537
|
-
const where = whereClause(
|
|
27070
|
+
const where = whereClause(
|
|
27071
|
+
table,
|
|
27072
|
+
query.filters ?? [],
|
|
27073
|
+
policy,
|
|
27074
|
+
params,
|
|
27075
|
+
rowidWhere(table, state, nonce, "select")
|
|
27076
|
+
);
|
|
26538
27077
|
const order = orderClause(table, query.order ?? []);
|
|
26539
27078
|
const limit = query.limit ?? MAX_LIMIT;
|
|
26540
27079
|
params.push(query.single ? Math.min(limit, 2) : limit, query.offset ?? 0);
|
|
26541
27080
|
return [
|
|
26542
27081
|
statement(
|
|
26543
|
-
`SELECT ${
|
|
27082
|
+
`SELECT ${outputList(table, columns)} FROM ${quoteIdentifier(table.name)}${where}${order} LIMIT ? OFFSET ?`,
|
|
26544
27083
|
params
|
|
26545
27084
|
)
|
|
26546
27085
|
];
|
|
26547
27086
|
}
|
|
26548
|
-
function compileInsert(query, table, rls, nonce) {
|
|
27087
|
+
function compileInsert(query, table, rls, { state, nonce }, capture) {
|
|
26549
27088
|
const uid = rls?.req.uid ?? null;
|
|
26550
27089
|
const ownerInsert = rls?.ownerOnly("insert");
|
|
26551
27090
|
const ownerRead = rls && query.returning !== void 0 ? rls.ownerOnly("select") : void 0;
|
|
@@ -26614,6 +27153,12 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26614
27153
|
]);
|
|
26615
27154
|
}
|
|
26616
27155
|
}
|
|
27156
|
+
const rowid = rowidTerm(table, state, nonce, "insert");
|
|
27157
|
+
if (rowid)
|
|
27158
|
+
where = nestedChecks(
|
|
27159
|
+
[rowid],
|
|
27160
|
+
where ?? { sql: width === 0 ? "0" : "1", params: [] }
|
|
27161
|
+
);
|
|
26617
27162
|
const checkParams = where?.params ?? [];
|
|
26618
27163
|
const room = MAX_PARAMETERS - checkParams.length;
|
|
26619
27164
|
if (width > 0 && room < width) fail(dataError("TOO_MANY_PARAMETERS"));
|
|
@@ -26630,7 +27175,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26630
27175
|
)
|
|
26631
27176
|
);
|
|
26632
27177
|
const into = `INSERT ${OR_ABORT} INTO ${quoteIdentifier(table.name)}`;
|
|
26633
|
-
const returning =
|
|
27178
|
+
const returning = writeReturning(table, query.returning, "insert", capture);
|
|
26634
27179
|
if (rls) {
|
|
26635
27180
|
for (const { group } of plans)
|
|
26636
27181
|
for (const [row, values] of group.rows.entries())
|
|
@@ -26656,7 +27201,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26656
27201
|
continue;
|
|
26657
27202
|
}
|
|
26658
27203
|
const tuple2 = `(${group.columns.map(() => "?").join(", ")})`;
|
|
26659
|
-
const names =
|
|
27204
|
+
const names = columnNames(group.columns);
|
|
26660
27205
|
for (let start = 0; start < group.rows.length; start += perStatement) {
|
|
26661
27206
|
const chunk = group.rows.slice(start, start + perStatement);
|
|
26662
27207
|
const values = chunk.map(() => tuple2).join(", ");
|
|
@@ -26673,7 +27218,7 @@ function compileInsert(query, table, rls, nonce) {
|
|
|
26673
27218
|
}
|
|
26674
27219
|
return statements;
|
|
26675
27220
|
}
|
|
26676
|
-
function compileUpdate(query, table, context, rls, nonce) {
|
|
27221
|
+
function compileUpdate(query, table, context, rls, { state, nonce }, capture) {
|
|
26677
27222
|
requireFilter(query, context);
|
|
26678
27223
|
const values = assignments(table, query.values, "values");
|
|
26679
27224
|
if (values.size === 0)
|
|
@@ -26711,8 +27256,9 @@ function compileUpdate(query, table, context, rls, nonce) {
|
|
|
26711
27256
|
{ sql: checkSql, params: terms.flatMap((term) => term.params) },
|
|
26712
27257
|
...policy ? [policy] : []
|
|
26713
27258
|
]);
|
|
26714
|
-
const
|
|
26715
|
-
const
|
|
27259
|
+
const rowid = rowidWhere(table, state, nonce, "update");
|
|
27260
|
+
const where = whereClause(table, query.filters ?? [], policy, params, rowid);
|
|
27261
|
+
const returning = writeReturning(table, query.returning, "update", capture);
|
|
26716
27262
|
for (const item of values.values())
|
|
26717
27263
|
rls?.refuseText(
|
|
26718
27264
|
"update",
|
|
@@ -26720,63 +27266,170 @@ function compileUpdate(query, table, context, rls, nonce) {
|
|
|
26720
27266
|
item.column,
|
|
26721
27267
|
toBoundValue(item.value)
|
|
26722
27268
|
);
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
26728
|
-
];
|
|
27269
|
+
const write = statement(
|
|
27270
|
+
`UPDATE ${OR_ABORT} ${quoteIdentifier(table.name)} SET ${assigned.join(", ")}${where}${returning}`,
|
|
27271
|
+
params
|
|
27272
|
+
);
|
|
27273
|
+
if (!capture) return { statements: [write], guard: null };
|
|
27274
|
+
const guardParams = [];
|
|
27275
|
+
const guardWhere = whereClause(
|
|
27276
|
+
table,
|
|
27277
|
+
query.filters ?? [],
|
|
27278
|
+
policy,
|
|
27279
|
+
guardParams,
|
|
27280
|
+
rowid
|
|
27281
|
+
);
|
|
27282
|
+
const sent = new Map(
|
|
27283
|
+
[...values.values()].map((item) => [
|
|
27284
|
+
item.column.name,
|
|
27285
|
+
toBoundValue(item.value)
|
|
27286
|
+
])
|
|
27287
|
+
);
|
|
27288
|
+
return {
|
|
27289
|
+
statements: [write],
|
|
27290
|
+
guard: captureGuard(table, guardWhere, guardParams, nonce, sent)
|
|
27291
|
+
};
|
|
26729
27292
|
}
|
|
26730
|
-
function compileDelete(query, table, context, rls) {
|
|
27293
|
+
function compileDelete(query, table, context, rls, { state, nonce }, capture) {
|
|
26731
27294
|
requireFilter(query, context);
|
|
26732
27295
|
const params = [];
|
|
26733
27296
|
const policy = rls?.where("delete") ?? null;
|
|
26734
27297
|
if (rls && policy) rls.budget([policy]);
|
|
26735
|
-
const
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
)
|
|
26741
|
-
];
|
|
26742
|
-
}
|
|
26743
|
-
function compile(query, snapshot, decision, context, options = {}) {
|
|
26744
|
-
const nonce = checkNonceOf(
|
|
26745
|
-
options.checkNonce ?? DEFAULT_CHECK_NONCE,
|
|
26746
|
-
"compile"
|
|
27298
|
+
const rowid = rowidWhere(table, state, nonce, "select");
|
|
27299
|
+
const where = whereClause(table, query.filters ?? [], policy, params, rowid);
|
|
27300
|
+
const write = statement(
|
|
27301
|
+
`DELETE FROM ${quoteIdentifier(table.name)}${where}${writeReturning(table, query.returning, "delete", capture)}`,
|
|
27302
|
+
params
|
|
26747
27303
|
);
|
|
26748
|
-
if (!
|
|
26749
|
-
|
|
26750
|
-
const
|
|
26751
|
-
|
|
26752
|
-
|
|
26753
|
-
|
|
26754
|
-
|
|
26755
|
-
|
|
27304
|
+
if (!capture) return { statements: [write], guard: null };
|
|
27305
|
+
const guardParams = [];
|
|
27306
|
+
const guardWhere = whereClause(
|
|
27307
|
+
table,
|
|
27308
|
+
query.filters ?? [],
|
|
27309
|
+
policy,
|
|
27310
|
+
guardParams,
|
|
27311
|
+
rowid
|
|
27312
|
+
);
|
|
27313
|
+
return {
|
|
27314
|
+
statements: [write],
|
|
27315
|
+
guard: captureGuard(table, guardWhere, guardParams, nonce, /* @__PURE__ */ new Map())
|
|
27316
|
+
};
|
|
27317
|
+
}
|
|
27318
|
+
function ownReturning(table, query) {
|
|
27319
|
+
if (query.action === "select" || query.returning === void 0) return null;
|
|
27320
|
+
if (query.returning === "*") return table.columns.map((item) => item.name);
|
|
27321
|
+
return columnList(table, query.returning).map((item) => item.name);
|
|
27322
|
+
}
|
|
27323
|
+
function bulkPlan(table) {
|
|
27324
|
+
return {
|
|
27325
|
+
columns: table.columns,
|
|
27326
|
+
guard: null,
|
|
27327
|
+
triggers: null,
|
|
27328
|
+
returning: null,
|
|
27329
|
+
bulk: true
|
|
27330
|
+
};
|
|
27331
|
+
}
|
|
27332
|
+
function compileWith(query, snapshot, decision, context, table, nonce, capture) {
|
|
26756
27333
|
try {
|
|
26757
|
-
const
|
|
27334
|
+
const state = newState2();
|
|
27335
|
+
const rls = decision.rls ? new Rls(
|
|
27336
|
+
decision.rls,
|
|
27337
|
+
snapshot,
|
|
27338
|
+
table,
|
|
27339
|
+
nonce,
|
|
27340
|
+
commandsOf(query),
|
|
27341
|
+
void 0,
|
|
27342
|
+
state
|
|
27343
|
+
) : void 0;
|
|
27344
|
+
const statementContext = { state, nonce };
|
|
26758
27345
|
let statements;
|
|
27346
|
+
let guard = null;
|
|
26759
27347
|
switch (query.action) {
|
|
26760
27348
|
case "select":
|
|
26761
|
-
statements = compileSelect(query, table, rls);
|
|
27349
|
+
statements = compileSelect(query, table, rls, statementContext);
|
|
26762
27350
|
break;
|
|
26763
27351
|
case "insert":
|
|
26764
|
-
statements = compileInsert(
|
|
27352
|
+
statements = compileInsert(
|
|
27353
|
+
query,
|
|
27354
|
+
table,
|
|
27355
|
+
rls,
|
|
27356
|
+
statementContext,
|
|
27357
|
+
capture
|
|
27358
|
+
);
|
|
26765
27359
|
break;
|
|
26766
27360
|
case "update":
|
|
26767
|
-
statements = compileUpdate(
|
|
27361
|
+
({ statements, guard } = compileUpdate(
|
|
27362
|
+
query,
|
|
27363
|
+
table,
|
|
27364
|
+
context,
|
|
27365
|
+
rls,
|
|
27366
|
+
statementContext,
|
|
27367
|
+
capture
|
|
27368
|
+
));
|
|
26768
27369
|
break;
|
|
26769
27370
|
case "delete":
|
|
26770
|
-
statements = compileDelete(
|
|
27371
|
+
({ statements, guard } = compileDelete(
|
|
27372
|
+
query,
|
|
27373
|
+
table,
|
|
27374
|
+
context,
|
|
27375
|
+
rls,
|
|
27376
|
+
statementContext,
|
|
27377
|
+
capture
|
|
27378
|
+
));
|
|
26771
27379
|
break;
|
|
26772
27380
|
}
|
|
26773
|
-
|
|
27381
|
+
const compiled = {
|
|
27382
|
+
ok: true,
|
|
27383
|
+
statements,
|
|
27384
|
+
restrictive: rls?.restrictive ?? []
|
|
27385
|
+
};
|
|
27386
|
+
if (!capture) return compiled;
|
|
27387
|
+
return {
|
|
27388
|
+
...compiled,
|
|
27389
|
+
capture: {
|
|
27390
|
+
columns: table.columns,
|
|
27391
|
+
guard,
|
|
27392
|
+
// A DELETE is judged on the row as it was before the write
|
|
27393
|
+
// (original spec §8.5), which its RETURNING is.
|
|
27394
|
+
triggers: query.action === "insert" || query.action === "update" ? triggersRead(table) : null,
|
|
27395
|
+
returning: ownReturning(table, query),
|
|
27396
|
+
bulk: false
|
|
27397
|
+
}
|
|
27398
|
+
};
|
|
26774
27399
|
} catch (error51) {
|
|
26775
27400
|
if (error51 instanceof Failure)
|
|
26776
27401
|
return error51.reason === void 0 ? { ok: false, error: error51.error } : { ok: false, error: error51.error, reason: error51.reason };
|
|
26777
27402
|
throw error51;
|
|
26778
27403
|
}
|
|
26779
27404
|
}
|
|
27405
|
+
function compile(query, snapshot, decision, context, options = {}) {
|
|
27406
|
+
const nonce = checkNonceOf(
|
|
27407
|
+
options.checkNonce ?? DEFAULT_CHECK_NONCE,
|
|
27408
|
+
"compile"
|
|
27409
|
+
);
|
|
27410
|
+
if (!decision.allow)
|
|
27411
|
+
return decision.reason === void 0 ? { ok: false, error: decision.error } : { ok: false, error: decision.error, reason: decision.reason };
|
|
27412
|
+
const table = resolveTable(snapshot, query.table);
|
|
27413
|
+
if (!table)
|
|
27414
|
+
return {
|
|
27415
|
+
ok: false,
|
|
27416
|
+
error: dataError("TABLE_NOT_FOUND", { table: query.table })
|
|
27417
|
+
};
|
|
27418
|
+
if (options.capture !== true || query.action === "select")
|
|
27419
|
+
return compileWith(query, snapshot, decision, context, table, nonce, false);
|
|
27420
|
+
const captured = table.kind === "virtual" && query.action !== "insert" ? void 0 : compileWith(query, snapshot, decision, context, table, nonce, true);
|
|
27421
|
+
if (captured?.ok) return captured;
|
|
27422
|
+
const plain = compileWith(
|
|
27423
|
+
query,
|
|
27424
|
+
snapshot,
|
|
27425
|
+
decision,
|
|
27426
|
+
context,
|
|
27427
|
+
table,
|
|
27428
|
+
nonce,
|
|
27429
|
+
false
|
|
27430
|
+
);
|
|
27431
|
+
return plain.ok ? { ...plain, capture: bulkPlan(table) } : plain;
|
|
27432
|
+
}
|
|
26780
27433
|
|
|
26781
27434
|
// ../../packages/data-core/src/sql-depth.ts
|
|
26782
27435
|
var D1_EXPRESSION_DEPTH = 100;
|
|
@@ -27550,6 +28203,231 @@ function expressionDepth(sql, { planner = true } = {}) {
|
|
|
27550
28203
|
}
|
|
27551
28204
|
}
|
|
27552
28205
|
|
|
28206
|
+
// ../../packages/data-core/src/sql-head.ts
|
|
28207
|
+
var HEAD_TOKENS = 12;
|
|
28208
|
+
var BOM2 = 65279;
|
|
28209
|
+
function isBlankStart(char) {
|
|
28210
|
+
return char === " " || char === " " || char === "\n" || char === "\f" || char === "\r";
|
|
28211
|
+
}
|
|
28212
|
+
function isBlank(char) {
|
|
28213
|
+
return isBlankStart(char) || char === "\v";
|
|
28214
|
+
}
|
|
28215
|
+
function isWordStart2(char) {
|
|
28216
|
+
return /[A-Za-z_]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28217
|
+
}
|
|
28218
|
+
function isWordPart2(char) {
|
|
28219
|
+
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
28220
|
+
}
|
|
28221
|
+
function skipBlanks(text2, from) {
|
|
28222
|
+
let index = from;
|
|
28223
|
+
for (; ; ) {
|
|
28224
|
+
const char = text2.charAt(index);
|
|
28225
|
+
if (isBlankStart(char)) {
|
|
28226
|
+
index++;
|
|
28227
|
+
while (index < text2.length && isBlank(text2.charAt(index))) index++;
|
|
28228
|
+
} else if (text2.charCodeAt(index) === BOM2) index++;
|
|
28229
|
+
else if (char === "-" && text2.charAt(index + 1) === "-") {
|
|
28230
|
+
const end = text2.indexOf("\n", index + 2);
|
|
28231
|
+
index = end < 0 ? text2.length : end;
|
|
28232
|
+
} else if (char === "/" && text2.charAt(index + 1) === "*") {
|
|
28233
|
+
const end = text2.indexOf("*/", index + 2);
|
|
28234
|
+
index = end < 0 ? text2.length : end + 2;
|
|
28235
|
+
} else return index;
|
|
28236
|
+
}
|
|
28237
|
+
}
|
|
28238
|
+
function readQuoted(text2, start, delimiter) {
|
|
28239
|
+
let value = "";
|
|
28240
|
+
let index = start + 1;
|
|
28241
|
+
for (; ; ) {
|
|
28242
|
+
const close = text2.indexOf(delimiter, index);
|
|
28243
|
+
if (close < 0) return void 0;
|
|
28244
|
+
value += text2.slice(index, close);
|
|
28245
|
+
if (text2.charAt(close + 1) === delimiter) {
|
|
28246
|
+
value += delimiter;
|
|
28247
|
+
index = close + 2;
|
|
28248
|
+
continue;
|
|
28249
|
+
}
|
|
28250
|
+
return { text: value, end: close + 1 };
|
|
28251
|
+
}
|
|
28252
|
+
}
|
|
28253
|
+
function headTokens(text2) {
|
|
28254
|
+
const tokens = [];
|
|
28255
|
+
let index = 0;
|
|
28256
|
+
while (tokens.length < HEAD_TOKENS) {
|
|
28257
|
+
index = skipBlanks(text2, index);
|
|
28258
|
+
const char = text2.charAt(index);
|
|
28259
|
+
if (index >= text2.length) break;
|
|
28260
|
+
if (char === '"' || char === "`" || char === "'") {
|
|
28261
|
+
const quoted2 = readQuoted(text2, index, char);
|
|
28262
|
+
if (!quoted2) break;
|
|
28263
|
+
tokens.push({ kind: "name", text: quoted2.text });
|
|
28264
|
+
index = quoted2.end;
|
|
28265
|
+
} else if (char === "[") {
|
|
28266
|
+
const close = text2.indexOf("]", index + 1);
|
|
28267
|
+
if (close < 0) break;
|
|
28268
|
+
tokens.push({ kind: "name", text: text2.slice(index + 1, close) });
|
|
28269
|
+
index = close + 1;
|
|
28270
|
+
} else if ((char === "x" || char === "X") && text2.charAt(index + 1) === "'") {
|
|
28271
|
+
break;
|
|
28272
|
+
} else if (isWordStart2(char)) {
|
|
28273
|
+
let end = index + 1;
|
|
28274
|
+
while (end < text2.length && isWordPart2(text2.charAt(end))) end++;
|
|
28275
|
+
tokens.push({ kind: "word", text: text2.slice(index, end) });
|
|
28276
|
+
index = end;
|
|
28277
|
+
} else if (char === "." && !/[0-9]/.test(text2.charAt(index + 1))) {
|
|
28278
|
+
tokens.push({ kind: "dot" });
|
|
28279
|
+
index++;
|
|
28280
|
+
} else break;
|
|
28281
|
+
}
|
|
28282
|
+
tokens.push({ kind: "stop" });
|
|
28283
|
+
return tokens;
|
|
28284
|
+
}
|
|
28285
|
+
var OTHER = { kind: "other" };
|
|
28286
|
+
var NO_NAME = {
|
|
28287
|
+
kind: "unreadable",
|
|
28288
|
+
reason: "the table name could not be read"
|
|
28289
|
+
};
|
|
28290
|
+
var NO_COLUMN = {
|
|
28291
|
+
kind: "unreadable",
|
|
28292
|
+
reason: "the column name could not be read"
|
|
28293
|
+
};
|
|
28294
|
+
var OTHER_SCHEMA = {
|
|
28295
|
+
kind: "unreadable",
|
|
28296
|
+
reason: "a table in another schema"
|
|
28297
|
+
};
|
|
28298
|
+
var PUBLIC_SCHEMA = {
|
|
28299
|
+
kind: "unreadable",
|
|
28300
|
+
reason: 'SQLite has no schema "public": write the table name without "public."'
|
|
28301
|
+
};
|
|
28302
|
+
var Head = class {
|
|
28303
|
+
constructor(tokens) {
|
|
28304
|
+
this.tokens = tokens;
|
|
28305
|
+
}
|
|
28306
|
+
token(at) {
|
|
28307
|
+
return this.tokens[Math.min(at, this.tokens.length - 1)] ?? { kind: "stop" };
|
|
28308
|
+
}
|
|
28309
|
+
/** The bare word `word` (lower case), in any ASCII case. */
|
|
28310
|
+
is(at, word) {
|
|
28311
|
+
const token = this.token(at);
|
|
28312
|
+
return token.kind === "word" && asciiLower(token.text) === word;
|
|
28313
|
+
}
|
|
28314
|
+
nameAt(at) {
|
|
28315
|
+
const token = this.token(at);
|
|
28316
|
+
return token.kind === "word" || token.kind === "name" ? token.text : void 0;
|
|
28317
|
+
}
|
|
28318
|
+
/**
|
|
28319
|
+
* SQLite's `fullname`. `main.` is the database a migration writes: the
|
|
28320
|
+
* name goes without it, and `main` says it was written (known-issues
|
|
28321
|
+
* #226); `temp.` (in any case, quoted or not, as sqlite3FindDbName
|
|
28322
|
+
* matches it) is a table that goes with the connection; `public.` is
|
|
28323
|
+
* what a Postgres migration writes, said in its own words; any other
|
|
28324
|
+
* schema is one D1 does not have.
|
|
28325
|
+
*/
|
|
28326
|
+
table(at) {
|
|
28327
|
+
const first = this.nameAt(at);
|
|
28328
|
+
if (first === void 0) return { ok: false, head: NO_NAME };
|
|
28329
|
+
if (this.token(at + 1).kind !== "dot")
|
|
28330
|
+
return { ok: true, name: first, next: at + 1, main: false };
|
|
28331
|
+
const second = this.nameAt(at + 2);
|
|
28332
|
+
if (second === void 0) return { ok: false, head: NO_NAME };
|
|
28333
|
+
const schema = asciiLower(first);
|
|
28334
|
+
if (schema === "main")
|
|
28335
|
+
return { ok: true, name: second, next: at + 3, main: true };
|
|
28336
|
+
let head = OTHER_SCHEMA;
|
|
28337
|
+
if (schema === "temp") head = OTHER;
|
|
28338
|
+
else if (schema === "public") head = PUBLIC_SCHEMA;
|
|
28339
|
+
return { ok: false, head, next: at + 3 };
|
|
28340
|
+
}
|
|
28341
|
+
};
|
|
28342
|
+
function inMain(read) {
|
|
28343
|
+
return read.main ? { main: true } : {};
|
|
28344
|
+
}
|
|
28345
|
+
function readCreate(head) {
|
|
28346
|
+
if (head.is(1, "policy")) return { kind: "create-policy" };
|
|
28347
|
+
if (head.is(1, "temp") || head.is(1, "temporary")) return OTHER;
|
|
28348
|
+
let at;
|
|
28349
|
+
if (head.is(1, "table")) at = 2;
|
|
28350
|
+
else if (head.is(1, "virtual") && head.is(2, "table")) at = 3;
|
|
28351
|
+
else return OTHER;
|
|
28352
|
+
if (head.is(at, "if")) {
|
|
28353
|
+
if (!head.is(at + 1, "not") || !head.is(at + 2, "exists")) return NO_NAME;
|
|
28354
|
+
at += 3;
|
|
28355
|
+
}
|
|
28356
|
+
const table = head.table(at);
|
|
28357
|
+
return table.ok ? { kind: "create-table", table: table.name } : table.head;
|
|
28358
|
+
}
|
|
28359
|
+
function readDrop(head) {
|
|
28360
|
+
if (head.is(1, "policy")) return { kind: "drop-policy" };
|
|
28361
|
+
if (!head.is(1, "table")) return OTHER;
|
|
28362
|
+
let at = 2;
|
|
28363
|
+
if (head.is(at, "if")) {
|
|
28364
|
+
if (!head.is(at + 1, "exists")) return NO_NAME;
|
|
28365
|
+
at += 2;
|
|
28366
|
+
}
|
|
28367
|
+
const table = head.table(at);
|
|
28368
|
+
return table.ok ? { kind: "drop-table", table: table.name, ...inMain(table) } : table.head;
|
|
28369
|
+
}
|
|
28370
|
+
var RLS_ACTIONS = ["enable", "disable", "force", "no"];
|
|
28371
|
+
function readRenameColumn(head, table, at) {
|
|
28372
|
+
let from = at;
|
|
28373
|
+
if (head.is(from, "column") && head.nameAt(from + 1) !== void 0 && head.is(from + 2, "to"))
|
|
28374
|
+
from += 1;
|
|
28375
|
+
const column2 = head.nameAt(from);
|
|
28376
|
+
if (column2 === void 0 || !head.is(from + 1, "to")) return NO_COLUMN;
|
|
28377
|
+
const to = head.nameAt(from + 2);
|
|
28378
|
+
return to === void 0 ? NO_COLUMN : {
|
|
28379
|
+
kind: "rename-column",
|
|
28380
|
+
table: table.name,
|
|
28381
|
+
column: column2,
|
|
28382
|
+
to,
|
|
28383
|
+
...inMain(table)
|
|
28384
|
+
};
|
|
28385
|
+
}
|
|
28386
|
+
function readAlter(head) {
|
|
28387
|
+
if (head.is(1, "policy")) return { kind: "alter-policy" };
|
|
28388
|
+
if (!head.is(1, "table")) return OTHER;
|
|
28389
|
+
const sqlite = head.table(2);
|
|
28390
|
+
const action = sqlite.next;
|
|
28391
|
+
if (action !== void 0) {
|
|
28392
|
+
if (head.is(action, "rename") && head.is(action + 1, "to")) {
|
|
28393
|
+
if (!sqlite.ok) return sqlite.head;
|
|
28394
|
+
const to = head.table(action + 2);
|
|
28395
|
+
return to.ok ? {
|
|
28396
|
+
kind: "rename-table",
|
|
28397
|
+
table: sqlite.name,
|
|
28398
|
+
to: to.name,
|
|
28399
|
+
...inMain(sqlite)
|
|
28400
|
+
} : to.head;
|
|
28401
|
+
}
|
|
28402
|
+
if (head.is(action, "rename")) {
|
|
28403
|
+
if (!sqlite.ok) return sqlite.head;
|
|
28404
|
+
return readRenameColumn(head, sqlite, action + 1);
|
|
28405
|
+
}
|
|
28406
|
+
if (head.is(action, "add") || head.is(action, "drop"))
|
|
28407
|
+
return sqlite.ok ? { kind: "alter-table", table: sqlite.name } : sqlite.head;
|
|
28408
|
+
}
|
|
28409
|
+
let at = 2;
|
|
28410
|
+
if (head.is(at, "if") && head.is(at + 1, "exists")) at += 2;
|
|
28411
|
+
if (head.is(at, "only")) at++;
|
|
28412
|
+
const next = head.table(at).next;
|
|
28413
|
+
if (next !== void 0 && RLS_ACTIONS.some((word) => head.is(next, word)))
|
|
28414
|
+
return { kind: "rls" };
|
|
28415
|
+
return sqlite.ok ? OTHER : sqlite.head;
|
|
28416
|
+
}
|
|
28417
|
+
function readSqlHead(text2) {
|
|
28418
|
+
if (text2.includes("\0"))
|
|
28419
|
+
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
28420
|
+
const head = new Head(headTokens(text2));
|
|
28421
|
+
if (head.is(0, "create")) return readCreate(head);
|
|
28422
|
+
if (head.is(0, "drop")) return readDrop(head);
|
|
28423
|
+
if (head.is(0, "alter")) return readAlter(head);
|
|
28424
|
+
if (head.is(0, "comment") && head.is(1, "on") && head.is(2, "policy"))
|
|
28425
|
+
return { kind: "comment-policy" };
|
|
28426
|
+
if (head.is(0, "grant")) return { kind: "grant" };
|
|
28427
|
+
if (head.is(0, "revoke")) return { kind: "revoke" };
|
|
28428
|
+
return OTHER;
|
|
28429
|
+
}
|
|
28430
|
+
|
|
27553
28431
|
// ../../packages/data-core/src/policy-index.ts
|
|
27554
28432
|
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`;
|
|
27555
28433
|
function indexesFromRows(rows) {
|
|
@@ -27826,8 +28704,34 @@ function unindexedSubqueries(snapshot, indexes, policies) {
|
|
|
27826
28704
|
return found;
|
|
27827
28705
|
}
|
|
27828
28706
|
|
|
28707
|
+
// ../../packages/data-core/src/realtime-filter.ts
|
|
28708
|
+
var FILTER_REASONS = {
|
|
28709
|
+
bytes: "must be a string of up to 1024 bytes in UTF-8",
|
|
28710
|
+
control: "must have no control character",
|
|
28711
|
+
syntax: "supports eq, neq, lt, lte, gt, gte and in, one condition",
|
|
28712
|
+
list: "needs in.(a,b) with 1 to 100 values"
|
|
28713
|
+
};
|
|
28714
|
+
|
|
27829
28715
|
// ../../packages/data-core/src/realtime-protocol.ts
|
|
27830
28716
|
var REALTIME_PROTOCOL_VERSION = "1.0.0";
|
|
28717
|
+
var MAX_CHANGE_CHANNELS_PER_TABLE = 4;
|
|
28718
|
+
var JOIN_CONFIG_REASONS = {
|
|
28719
|
+
object: "must be an object",
|
|
28720
|
+
unsupported: "is not supported",
|
|
28721
|
+
boolean: "must be true or false",
|
|
28722
|
+
presenceKey: "must be 1 to 128 characters",
|
|
28723
|
+
changes: "must be an array of up to 16 subscriptions",
|
|
28724
|
+
changeEvent: "must be *, INSERT, UPDATE or DELETE",
|
|
28725
|
+
schema: "must be public",
|
|
28726
|
+
table: "must be a table name of 1 to 128 characters, or *",
|
|
28727
|
+
filter: FILTER_REASONS.bytes,
|
|
28728
|
+
filterControl: FILTER_REASONS.control,
|
|
28729
|
+
filterSyntax: FILTER_REASONS.syntax,
|
|
28730
|
+
filterList: FILTER_REASONS.list,
|
|
28731
|
+
filterTable: "needs a table: a filter cannot go with *",
|
|
28732
|
+
/** The hub's, after `config.postgres_changes[<i>].table` (3-2c-1): `MAX_CHANGE_CHANNELS_PER_TABLE` against the connection's joins. */
|
|
28733
|
+
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`
|
|
28734
|
+
};
|
|
27831
28735
|
var REALTIME_CLOSE = {
|
|
27832
28736
|
UNSUPPORTED_PROTOCOL: {
|
|
27833
28737
|
code: 4e3,
|
|
@@ -29018,7 +29922,7 @@ function readClause(stream) {
|
|
|
29018
29922
|
checkExpressionSize(expr, open.position);
|
|
29019
29923
|
return { text: printExpr(expr), position: open.position };
|
|
29020
29924
|
}
|
|
29021
|
-
function
|
|
29925
|
+
function readCreate2(stream, start) {
|
|
29022
29926
|
stream.expectKeyword("policy");
|
|
29023
29927
|
const name = readPolicyName(stream);
|
|
29024
29928
|
stream.expectKeyword("on");
|
|
@@ -29078,7 +29982,7 @@ function readCreate(stream, start) {
|
|
|
29078
29982
|
throw new PolicySyntaxError(problem.replace(/\.$/, ""), position);
|
|
29079
29983
|
return { kind: "create", policy };
|
|
29080
29984
|
}
|
|
29081
|
-
function
|
|
29985
|
+
function readDrop2(stream) {
|
|
29082
29986
|
stream.expectKeyword("policy");
|
|
29083
29987
|
let ifExists = false;
|
|
29084
29988
|
if (stream.acceptKeyword("if")) {
|
|
@@ -29109,11 +30013,11 @@ function parsePolicyStatement(text2) {
|
|
|
29109
30013
|
position: 1
|
|
29110
30014
|
};
|
|
29111
30015
|
try {
|
|
29112
|
-
const stream = new TokenStream(
|
|
30016
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
29113
30017
|
const start = stream.next();
|
|
29114
30018
|
let statement2;
|
|
29115
|
-
if (isKeyword(start, "create")) statement2 =
|
|
29116
|
-
else if (isKeyword(start, "drop")) statement2 =
|
|
30019
|
+
if (isKeyword(start, "create")) statement2 = readCreate2(stream, start);
|
|
30020
|
+
else if (isKeyword(start, "drop")) statement2 = readDrop2(stream);
|
|
29117
30021
|
else if (isKeyword(start, "alter"))
|
|
29118
30022
|
throw new PolicySyntaxError(
|
|
29119
30023
|
"ALTER POLICY is not supported here yet: DROP POLICY and CREATE POLICY again",
|
|
@@ -29136,7 +30040,7 @@ function parsePolicyStatement(text2) {
|
|
|
29136
30040
|
function splitPolicyStatements(text2) {
|
|
29137
30041
|
let tokens;
|
|
29138
30042
|
try {
|
|
29139
|
-
tokens =
|
|
30043
|
+
tokens = tokenize2(text2);
|
|
29140
30044
|
} catch (error51) {
|
|
29141
30045
|
if (error51 instanceof PolicySyntaxError)
|
|
29142
30046
|
return { ok: false, reason: error51.reason, position: error51.position };
|
|
@@ -29225,7 +30129,7 @@ function parseMigrationPolicyStatement(text2) {
|
|
|
29225
30129
|
position: 1
|
|
29226
30130
|
};
|
|
29227
30131
|
try {
|
|
29228
|
-
const stream = new TokenStream(
|
|
30132
|
+
const stream = new TokenStream(tokenize2(text2));
|
|
29229
30133
|
let statement2;
|
|
29230
30134
|
if (stream.acceptKeyword("comment")) statement2 = readComment(stream);
|
|
29231
30135
|
else if (isKeyword(stream.peek(), "alter") && isKeyword(stream.peek(1), "table")) {
|
|
@@ -29242,230 +30146,11 @@ function parseMigrationPolicyStatement(text2) {
|
|
|
29242
30146
|
}
|
|
29243
30147
|
}
|
|
29244
30148
|
|
|
29245
|
-
// ../../packages/data-core/src/
|
|
29246
|
-
var
|
|
29247
|
-
var
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
}
|
|
29251
|
-
function isBlank(char) {
|
|
29252
|
-
return isBlankStart(char) || char === "\v";
|
|
29253
|
-
}
|
|
29254
|
-
function isWordStart2(char) {
|
|
29255
|
-
return /[A-Za-z_]/.test(char) || char.charCodeAt(0) >= 128;
|
|
29256
|
-
}
|
|
29257
|
-
function isWordPart2(char) {
|
|
29258
|
-
return /[A-Za-z0-9_$]/.test(char) || char.charCodeAt(0) >= 128;
|
|
29259
|
-
}
|
|
29260
|
-
function skipBlanks(text2, from) {
|
|
29261
|
-
let index = from;
|
|
29262
|
-
for (; ; ) {
|
|
29263
|
-
const char = text2.charAt(index);
|
|
29264
|
-
if (isBlankStart(char)) {
|
|
29265
|
-
index++;
|
|
29266
|
-
while (index < text2.length && isBlank(text2.charAt(index))) index++;
|
|
29267
|
-
} else if (text2.charCodeAt(index) === BOM2) index++;
|
|
29268
|
-
else if (char === "-" && text2.charAt(index + 1) === "-") {
|
|
29269
|
-
const end = text2.indexOf("\n", index + 2);
|
|
29270
|
-
index = end < 0 ? text2.length : end;
|
|
29271
|
-
} else if (char === "/" && text2.charAt(index + 1) === "*") {
|
|
29272
|
-
const end = text2.indexOf("*/", index + 2);
|
|
29273
|
-
index = end < 0 ? text2.length : end + 2;
|
|
29274
|
-
} else return index;
|
|
29275
|
-
}
|
|
29276
|
-
}
|
|
29277
|
-
function readQuoted(text2, start, delimiter) {
|
|
29278
|
-
let value = "";
|
|
29279
|
-
let index = start + 1;
|
|
29280
|
-
for (; ; ) {
|
|
29281
|
-
const close = text2.indexOf(delimiter, index);
|
|
29282
|
-
if (close < 0) return void 0;
|
|
29283
|
-
value += text2.slice(index, close);
|
|
29284
|
-
if (text2.charAt(close + 1) === delimiter) {
|
|
29285
|
-
value += delimiter;
|
|
29286
|
-
index = close + 2;
|
|
29287
|
-
continue;
|
|
29288
|
-
}
|
|
29289
|
-
return { text: value, end: close + 1 };
|
|
29290
|
-
}
|
|
29291
|
-
}
|
|
29292
|
-
function headTokens(text2) {
|
|
29293
|
-
const tokens = [];
|
|
29294
|
-
let index = 0;
|
|
29295
|
-
while (tokens.length < HEAD_TOKENS) {
|
|
29296
|
-
index = skipBlanks(text2, index);
|
|
29297
|
-
const char = text2.charAt(index);
|
|
29298
|
-
if (index >= text2.length) break;
|
|
29299
|
-
if (char === '"' || char === "`" || char === "'") {
|
|
29300
|
-
const quoted2 = readQuoted(text2, index, char);
|
|
29301
|
-
if (!quoted2) break;
|
|
29302
|
-
tokens.push({ kind: "name", text: quoted2.text });
|
|
29303
|
-
index = quoted2.end;
|
|
29304
|
-
} else if (char === "[") {
|
|
29305
|
-
const close = text2.indexOf("]", index + 1);
|
|
29306
|
-
if (close < 0) break;
|
|
29307
|
-
tokens.push({ kind: "name", text: text2.slice(index + 1, close) });
|
|
29308
|
-
index = close + 1;
|
|
29309
|
-
} else if ((char === "x" || char === "X") && text2.charAt(index + 1) === "'") {
|
|
29310
|
-
break;
|
|
29311
|
-
} else if (isWordStart2(char)) {
|
|
29312
|
-
let end = index + 1;
|
|
29313
|
-
while (end < text2.length && isWordPart2(text2.charAt(end))) end++;
|
|
29314
|
-
tokens.push({ kind: "word", text: text2.slice(index, end) });
|
|
29315
|
-
index = end;
|
|
29316
|
-
} else if (char === "." && !/[0-9]/.test(text2.charAt(index + 1))) {
|
|
29317
|
-
tokens.push({ kind: "dot" });
|
|
29318
|
-
index++;
|
|
29319
|
-
} else break;
|
|
29320
|
-
}
|
|
29321
|
-
tokens.push({ kind: "stop" });
|
|
29322
|
-
return tokens;
|
|
29323
|
-
}
|
|
29324
|
-
var OTHER = { kind: "other" };
|
|
29325
|
-
var NO_NAME = {
|
|
29326
|
-
kind: "unreadable",
|
|
29327
|
-
reason: "the table name could not be read"
|
|
29328
|
-
};
|
|
29329
|
-
var NO_COLUMN = {
|
|
29330
|
-
kind: "unreadable",
|
|
29331
|
-
reason: "the column name could not be read"
|
|
29332
|
-
};
|
|
29333
|
-
var OTHER_SCHEMA = {
|
|
29334
|
-
kind: "unreadable",
|
|
29335
|
-
reason: "a table in another schema"
|
|
29336
|
-
};
|
|
29337
|
-
var PUBLIC_SCHEMA = {
|
|
29338
|
-
kind: "unreadable",
|
|
29339
|
-
reason: 'SQLite has no schema "public": write the table name without "public."'
|
|
29340
|
-
};
|
|
29341
|
-
var Head = class {
|
|
29342
|
-
constructor(tokens) {
|
|
29343
|
-
this.tokens = tokens;
|
|
29344
|
-
}
|
|
29345
|
-
token(at) {
|
|
29346
|
-
return this.tokens[Math.min(at, this.tokens.length - 1)] ?? { kind: "stop" };
|
|
29347
|
-
}
|
|
29348
|
-
/** The bare word `word` (lower case), in any ASCII case. */
|
|
29349
|
-
is(at, word) {
|
|
29350
|
-
const token = this.token(at);
|
|
29351
|
-
return token.kind === "word" && asciiLower(token.text) === word;
|
|
29352
|
-
}
|
|
29353
|
-
nameAt(at) {
|
|
29354
|
-
const token = this.token(at);
|
|
29355
|
-
return token.kind === "word" || token.kind === "name" ? token.text : void 0;
|
|
29356
|
-
}
|
|
29357
|
-
/**
|
|
29358
|
-
* SQLite's `fullname`. `main.` is the database a migration writes: the
|
|
29359
|
-
* name goes without it, and `main` says it was written (known-issues
|
|
29360
|
-
* #226); `temp.` (in any case, quoted or not, as sqlite3FindDbName
|
|
29361
|
-
* matches it) is a table that goes with the connection; `public.` is
|
|
29362
|
-
* what a Postgres migration writes, said in its own words; any other
|
|
29363
|
-
* schema is one D1 does not have.
|
|
29364
|
-
*/
|
|
29365
|
-
table(at) {
|
|
29366
|
-
const first = this.nameAt(at);
|
|
29367
|
-
if (first === void 0) return { ok: false, head: NO_NAME };
|
|
29368
|
-
if (this.token(at + 1).kind !== "dot")
|
|
29369
|
-
return { ok: true, name: first, next: at + 1, main: false };
|
|
29370
|
-
const second = this.nameAt(at + 2);
|
|
29371
|
-
if (second === void 0) return { ok: false, head: NO_NAME };
|
|
29372
|
-
const schema = asciiLower(first);
|
|
29373
|
-
if (schema === "main")
|
|
29374
|
-
return { ok: true, name: second, next: at + 3, main: true };
|
|
29375
|
-
let head = OTHER_SCHEMA;
|
|
29376
|
-
if (schema === "temp") head = OTHER;
|
|
29377
|
-
else if (schema === "public") head = PUBLIC_SCHEMA;
|
|
29378
|
-
return { ok: false, head, next: at + 3 };
|
|
29379
|
-
}
|
|
29380
|
-
};
|
|
29381
|
-
function inMain(read) {
|
|
29382
|
-
return read.main ? { main: true } : {};
|
|
29383
|
-
}
|
|
29384
|
-
function readCreate2(head) {
|
|
29385
|
-
if (head.is(1, "policy")) return { kind: "create-policy" };
|
|
29386
|
-
if (head.is(1, "temp") || head.is(1, "temporary")) return OTHER;
|
|
29387
|
-
let at;
|
|
29388
|
-
if (head.is(1, "table")) at = 2;
|
|
29389
|
-
else if (head.is(1, "virtual") && head.is(2, "table")) at = 3;
|
|
29390
|
-
else return OTHER;
|
|
29391
|
-
if (head.is(at, "if")) {
|
|
29392
|
-
if (!head.is(at + 1, "not") || !head.is(at + 2, "exists")) return NO_NAME;
|
|
29393
|
-
at += 3;
|
|
29394
|
-
}
|
|
29395
|
-
const table = head.table(at);
|
|
29396
|
-
return table.ok ? { kind: "create-table", table: table.name } : table.head;
|
|
29397
|
-
}
|
|
29398
|
-
function readDrop2(head) {
|
|
29399
|
-
if (head.is(1, "policy")) return { kind: "drop-policy" };
|
|
29400
|
-
if (!head.is(1, "table")) return OTHER;
|
|
29401
|
-
let at = 2;
|
|
29402
|
-
if (head.is(at, "if")) {
|
|
29403
|
-
if (!head.is(at + 1, "exists")) return NO_NAME;
|
|
29404
|
-
at += 2;
|
|
29405
|
-
}
|
|
29406
|
-
const table = head.table(at);
|
|
29407
|
-
return table.ok ? { kind: "drop-table", table: table.name, ...inMain(table) } : table.head;
|
|
29408
|
-
}
|
|
29409
|
-
var RLS_ACTIONS = ["enable", "disable", "force", "no"];
|
|
29410
|
-
function readRenameColumn(head, table, at) {
|
|
29411
|
-
let from = at;
|
|
29412
|
-
if (head.is(from, "column") && head.nameAt(from + 1) !== void 0 && head.is(from + 2, "to"))
|
|
29413
|
-
from += 1;
|
|
29414
|
-
const column2 = head.nameAt(from);
|
|
29415
|
-
if (column2 === void 0 || !head.is(from + 1, "to")) return NO_COLUMN;
|
|
29416
|
-
const to = head.nameAt(from + 2);
|
|
29417
|
-
return to === void 0 ? NO_COLUMN : {
|
|
29418
|
-
kind: "rename-column",
|
|
29419
|
-
table: table.name,
|
|
29420
|
-
column: column2,
|
|
29421
|
-
to,
|
|
29422
|
-
...inMain(table)
|
|
29423
|
-
};
|
|
29424
|
-
}
|
|
29425
|
-
function readAlter(head) {
|
|
29426
|
-
if (head.is(1, "policy")) return { kind: "alter-policy" };
|
|
29427
|
-
if (!head.is(1, "table")) return OTHER;
|
|
29428
|
-
const sqlite = head.table(2);
|
|
29429
|
-
const action = sqlite.next;
|
|
29430
|
-
if (action !== void 0) {
|
|
29431
|
-
if (head.is(action, "rename") && head.is(action + 1, "to")) {
|
|
29432
|
-
if (!sqlite.ok) return sqlite.head;
|
|
29433
|
-
const to = head.table(action + 2);
|
|
29434
|
-
return to.ok ? {
|
|
29435
|
-
kind: "rename-table",
|
|
29436
|
-
table: sqlite.name,
|
|
29437
|
-
to: to.name,
|
|
29438
|
-
...inMain(sqlite)
|
|
29439
|
-
} : to.head;
|
|
29440
|
-
}
|
|
29441
|
-
if (head.is(action, "rename")) {
|
|
29442
|
-
if (!sqlite.ok) return sqlite.head;
|
|
29443
|
-
return readRenameColumn(head, sqlite, action + 1);
|
|
29444
|
-
}
|
|
29445
|
-
if (head.is(action, "add") || head.is(action, "drop"))
|
|
29446
|
-
return sqlite.ok ? { kind: "alter-table", table: sqlite.name } : sqlite.head;
|
|
29447
|
-
}
|
|
29448
|
-
let at = 2;
|
|
29449
|
-
if (head.is(at, "if") && head.is(at + 1, "exists")) at += 2;
|
|
29450
|
-
if (head.is(at, "only")) at++;
|
|
29451
|
-
const next = head.table(at).next;
|
|
29452
|
-
if (next !== void 0 && RLS_ACTIONS.some((word) => head.is(next, word)))
|
|
29453
|
-
return { kind: "rls" };
|
|
29454
|
-
return sqlite.ok ? OTHER : sqlite.head;
|
|
29455
|
-
}
|
|
29456
|
-
function readSqlHead(text2) {
|
|
29457
|
-
if (text2.includes("\0"))
|
|
29458
|
-
return { kind: "unreadable", reason: "the statement has a NUL character" };
|
|
29459
|
-
const head = new Head(headTokens(text2));
|
|
29460
|
-
if (head.is(0, "create")) return readCreate2(head);
|
|
29461
|
-
if (head.is(0, "drop")) return readDrop2(head);
|
|
29462
|
-
if (head.is(0, "alter")) return readAlter(head);
|
|
29463
|
-
if (head.is(0, "comment") && head.is(1, "on") && head.is(2, "policy"))
|
|
29464
|
-
return { kind: "comment-policy" };
|
|
29465
|
-
if (head.is(0, "grant")) return { kind: "grant" };
|
|
29466
|
-
if (head.is(0, "revoke")) return { kind: "revoke" };
|
|
29467
|
-
return OTHER;
|
|
29468
|
-
}
|
|
30149
|
+
// ../../packages/data-core/src/realtime-publication.ts
|
|
30150
|
+
var REALTIME_TABLES = "_naro_realtime_tables";
|
|
30151
|
+
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')))`;
|
|
30152
|
+
var MAX_PUBLISHED_TABLES = 100;
|
|
30153
|
+
var PUBLISHED_TABLES_SQL = `SELECT table_name FROM ${REALTIME_TABLES} ORDER BY rowid LIMIT ${String(MAX_PUBLISHED_TABLES)}`;
|
|
29469
30154
|
|
|
29470
30155
|
// ../../packages/data-core/src/system-sql.ts
|
|
29471
30156
|
async function convertLegacyPolicies(db, found) {
|
|
@@ -29500,6 +30185,7 @@ var MIGRATIONS_TABLE = "CREATE TABLE IF NOT EXISTS _naro_migrations (version INT
|
|
|
29500
30185
|
var STORAGE_SUBQUERIES_VERSION = 7;
|
|
29501
30186
|
var STORAGE_SUBQUERIES_NAME = "storage_subquery_policies";
|
|
29502
30187
|
var STORAGE_SUBQUERIES_SQL = `SELECT version FROM _naro_migrations WHERE version = ${String(STORAGE_SUBQUERIES_VERSION)} AND name = '${STORAGE_SUBQUERIES_NAME}'`;
|
|
30188
|
+
var REALTIME_TABLES_VERSION = 8;
|
|
29503
30189
|
var SYSTEM_MIGRATIONS = [
|
|
29504
30190
|
{
|
|
29505
30191
|
version: 1,
|
|
@@ -29588,6 +30274,14 @@ var SYSTEM_MIGRATIONS = [
|
|
|
29588
30274
|
version: STORAGE_SUBQUERIES_VERSION,
|
|
29589
30275
|
name: STORAGE_SUBQUERIES_NAME,
|
|
29590
30276
|
statements: []
|
|
30277
|
+
},
|
|
30278
|
+
{
|
|
30279
|
+
// Realtime 3-2b (original spec §9.1, design §7.1): the tables whose
|
|
30280
|
+
// Data API writes reach the hub. Empty until the publish API (3-2c)
|
|
30281
|
+
// writes it. A worker from before it never reads it.
|
|
30282
|
+
version: REALTIME_TABLES_VERSION,
|
|
30283
|
+
name: "realtime",
|
|
30284
|
+
statements: [REALTIME_TABLES_SQL]
|
|
29591
30285
|
}
|
|
29592
30286
|
];
|
|
29593
30287
|
|
|
@@ -29787,6 +30481,10 @@ var ENTERPRISE_REALTIME_LIMITS = Object.freeze({
|
|
|
29787
30481
|
restRequestsPerSecond: 100
|
|
29788
30482
|
});
|
|
29789
30483
|
|
|
30484
|
+
// ../../packages/data-core/src/realtime-changes.ts
|
|
30485
|
+
var CHANGE_TYPES = Object.freeze({ insert: "INSERT", update: "UPDATE", delete: "DELETE" });
|
|
30486
|
+
var CHANGE_EVENT_CODES = Object.freeze({ "*": 0, INSERT: 1, UPDATE: 2, DELETE: 3 });
|
|
30487
|
+
|
|
29790
30488
|
// ../../packages/data-core/src/redirect-origins.ts
|
|
29791
30489
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
29792
30490
|
"localhost",
|
|
@@ -31628,6 +32326,7 @@ export {
|
|
|
31628
32326
|
splitStatements,
|
|
31629
32327
|
ANALYTICS_FILTER_KEYS,
|
|
31630
32328
|
NaroError,
|
|
32329
|
+
promoteTargetUnsupported,
|
|
31631
32330
|
isNaroError,
|
|
31632
32331
|
toNaroError,
|
|
31633
32332
|
saveSession,
|
|
@@ -31661,11 +32360,11 @@ export {
|
|
|
31661
32360
|
summarizeStatement,
|
|
31662
32361
|
parseDotenvDetailed,
|
|
31663
32362
|
serializeDotenv,
|
|
32363
|
+
systemTablePredicate,
|
|
31664
32364
|
MAX_SINGLE_UPLOAD_BYTES,
|
|
31665
32365
|
checkObjectName,
|
|
31666
32366
|
parseCacheControl,
|
|
31667
32367
|
STORAGE_OBJECTS_TABLE,
|
|
31668
|
-
systemTablePredicate,
|
|
31669
32368
|
dataError,
|
|
31670
32369
|
SCHEMA_SQL,
|
|
31671
32370
|
tableNames,
|