@rebasepro/server 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/rebase-server.js +4 -2
- package/dist/{GCSStorageController-CLIJXwGS.js → GCSStorageController-BSiP1c-f.js} +57 -29
- package/dist/GCSStorageController-BSiP1c-f.js.map +1 -0
- package/dist/{S3StorageController-Dcuf8lMA.js → S3StorageController-CAwFRgjV.js} +19 -7
- package/dist/S3StorageController-CAwFRgjV.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +127 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +52 -8
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +158 -42
- package/dist/api/rest/auth-collection-writes.d.ts +85 -0
- package/dist/api/rest/field-access-query.d.ts +6 -2
- package/dist/api/rest/idempotency.d.ts +7 -1
- package/dist/api/rest/nested-write-access.d.ts +46 -0
- package/dist/api/rest/write-validation.d.ts +34 -2
- package/dist/api/types.d.ts +17 -1
- package/dist/{ast-schema-editor-CslO8Oje.js → ast-schema-editor-CWqS_sLJ.js} +411 -13
- package/dist/ast-schema-editor-CWqS_sLJ.js.map +1 -0
- package/dist/auth/access.d.ts +105 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +68 -0
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- package/dist/auth/admin-user-ops.d.ts +35 -2
- package/dist/auth/admin-users-route.d.ts +1 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +56 -55
- package/dist/auth/api-keys/api-key-routes.d.ts +41 -11
- package/dist/auth/api-keys/api-key-store.d.ts +31 -8
- package/dist/auth/api-keys/api-key-types.d.ts +14 -16
- package/dist/auth/api-keys/http-operation.d.ts +19 -0
- package/dist/auth/api-keys/index.d.ts +11 -11
- package/dist/auth/api-keys/key-grant.d.ts +41 -0
- package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
- package/dist/auth/auth-hooks.d.ts +50 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -0
- package/dist/auth/captcha.d.ts +5 -0
- package/dist/auth/cookie-utils.d.ts +7 -0
- package/dist/auth/deliverable-address.d.ts +6 -0
- package/dist/auth/email-change-routes.d.ts +41 -0
- package/dist/auth/expired-token-sweep.d.ts +67 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +146 -65
- package/dist/auth/jwt.d.ts +66 -3
- package/dist/auth/magic-link-routes.d.ts +2 -6
- package/dist/auth/mfa-routes.d.ts +2 -9
- package/dist/auth/middleware.d.ts +17 -5
- package/dist/auth/oauth-signin-policy.d.ts +25 -8
- package/dist/auth/otp-routes.d.ts +2 -6
- package/dist/auth/passwordless-signup.d.ts +27 -0
- package/dist/auth/platform-token.d.ts +122 -0
- package/dist/auth/rate-limiter.d.ts +72 -1
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +18 -6
- package/dist/auth/token-revocation.d.ts +53 -1
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-CCDpk2rn.js → auth-DMLngxn_.js} +2712 -711
- package/dist/auth-DMLngxn_.js.map +1 -0
- package/dist/backend-DTAOsLQc.js +30 -0
- package/dist/backend-DTAOsLQc.js.map +1 -0
- package/dist/backup/backup-common.d.ts +29 -0
- package/dist/backup/backup-routes.d.ts +24 -4
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +14 -0
- package/dist/backup/index.d.ts +2 -0
- package/dist/backup-CN0s50D2.js +444 -0
- package/dist/backup-CN0s50D2.js.map +1 -0
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +10 -0
- package/dist/boot/env.d.ts +51 -6
- package/dist/boot/fetch-bundle.d.ts +18 -1
- package/dist/boot/rls-audit-option.d.ts +26 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/sources.d.ts +1 -0
- package/dist/boot/static-routing.d.ts +56 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-eLxV0le1.js → contract-routes-fz8i4pxs.js} +17 -4
- package/dist/contract-routes-fz8i4pxs.js.map +1 -0
- package/dist/cron/cron-routes.d.ts +7 -2
- package/dist/cron/cron-scheduler.d.ts +146 -21
- package/dist/cron/cron-store.d.ts +76 -8
- package/dist/cron/index.d.ts +1 -1
- package/dist/{cron-loader-CQjvjpEw.js → cron-loader-CwaANlOG.js} +4 -4
- package/dist/cron-loader-CwaANlOG.js.map +1 -0
- package/dist/cron-routes-Bc-SB0Se.js +96 -0
- package/dist/cron-routes-Bc-SB0Se.js.map +1 -0
- package/dist/{cron-scheduler-COPQxlEq.js → cron-scheduler-CYQgco86.js} +427 -83
- package/dist/cron-scheduler-CYQgco86.js.map +1 -0
- package/dist/{cron-store-BYGZFNWk.js → cron-store-D2Q9-Aco.js} +139 -23
- package/dist/cron-store-D2Q9-Aco.js.map +1 -0
- package/dist/{ddl-bootstrap-CfNvxMuK.js → ddl-bootstrap-BaqMSa4Y.js} +3 -26
- package/dist/ddl-bootstrap-BaqMSa4Y.js.map +1 -0
- package/dist/email/index.d.ts +2 -2
- package/dist/email/link-base.d.ts +5 -4
- package/dist/email/smtp-email-service.d.ts +13 -1
- package/dist/email/templates.d.ts +31 -0
- package/dist/email/types.d.ts +29 -2
- package/dist/env.d.ts +25 -7
- package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
- package/dist/errors-D6_y86c5.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-D7o5Epjj.js} +2 -2
- package/dist/{function-loader-xnbDAPfa.js.map → function-loader-D7o5Epjj.js.map} +1 -1
- package/dist/{function-routes-Chet4-lB.js → function-routes-CaNG4waN.js} +24 -12
- package/dist/function-routes-CaNG4waN.js.map +1 -0
- package/dist/functions/context.d.ts +17 -6
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +2 -2
- package/dist/functions/index.js +90 -36
- package/dist/functions/index.js.map +1 -1
- package/dist/{history-recorder-BQmB0P_j.js → history-recorder-Nr8zLvoU.js} +9 -7
- package/dist/history-recorder-Nr8zLvoU.js.map +1 -0
- package/dist/{history-store-CetkrBBD.js → history-store-rcAm_xFR.js} +2 -2
- package/dist/{history-store-CetkrBBD.js.map → history-store-rcAm_xFR.js.map} +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.es.js +5733 -1551
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +5 -2
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +18 -3
- package/dist/init.d.ts +54 -0
- package/dist/jobs/index.d.ts +2 -2
- package/dist/jobs/job-queue.d.ts +23 -2
- package/dist/jobs/job-store.d.ts +37 -5
- package/dist/jobs/types.d.ts +8 -6
- package/dist/{jobs-Bjr8DZAi.js → jobs-DqYNfquG.js} +306 -167
- package/dist/jobs-DqYNfquG.js.map +1 -0
- package/dist/{jwt-C4OW-DNq.js → jwt-R6bSPMjk.js} +114 -38
- package/dist/{jwt-C4OW-DNq.js.map → jwt-R6bSPMjk.js.map} +1 -1
- package/dist/{keys-Qfc4XieN.js → keys-GAVZqbqx.js} +18 -17
- package/dist/{keys-Qfc4XieN.js.map → keys-GAVZqbqx.js.map} +1 -1
- package/dist/{logger-DO2PZc4i.js → logger-D-S-hO5e.js} +26 -3
- package/dist/logger-D-S-hO5e.js.map +1 -0
- package/dist/{logs-routes-3EEzPjhl.js → logs-routes-DAdv37GI.js} +54 -11
- package/dist/logs-routes-DAdv37GI.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +45 -2
- package/dist/mcp/mcp-tools.d.ts +22 -10
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +34 -1
- package/dist/mcp/oauth-store.d.ts +29 -13
- package/dist/metrics/history-recorder.d.ts +1 -1
- package/dist/{openapi-generator-D8uFz-LW.js → openapi-generator-DAq_XVDu.js} +135 -22
- package/dist/openapi-generator-DAq_XVDu.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-qRlqeUmO.js} +2 -2
- package/dist/{proxy-Czngl3p9.js.map → proxy-qRlqeUmO.js.map} +1 -1
- package/dist/{query-parser-BleZmY18.js → query-parser-BgiKJKvc.js} +41 -82
- package/dist/query-parser-BgiKJKvc.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-DgH7j8qO.js} +3 -3
- package/dist/{request-timeout-C_4C2BeR.js.map → request-timeout-DgH7j8qO.js.map} +1 -1
- package/dist/rls-audit/index.d.ts +4 -0
- package/dist/schema-edit/apply-schema-change.d.ts +63 -3
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +9 -4
- package/dist/{schema-editor-routes-DdLihzp0.js → schema-editor-routes-oIyuWl3L.js} +12 -7
- package/dist/schema-editor-routes-oIyuWl3L.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/routed-realtime-service.d.ts +11 -0
- package/dist/soft-delete-params-BWPilMPF.js +59 -0
- package/dist/soft-delete-params-BWPilMPF.js.map +1 -0
- package/dist/{src-Br6ARbs6.js → src-CatHFUym.js} +439 -45
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/{src-1vL-I1Po.js → src-I3aG1PcY.js} +371 -81
- package/dist/src-I3aG1PcY.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +13 -1
- package/dist/storage/LocalStorageController.d.ts +2 -0
- package/dist/storage/S3StorageController.d.ts +2 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/keys.d.ts +12 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +11 -1
- package/dist/storage/request-keys.d.ts +82 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +36 -18
- package/dist/storage/tus-handler.d.ts +30 -5
- package/dist/storage/types.d.ts +36 -1
- package/dist/types-BfKcm9do.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +9 -9
- package/dist/GCSStorageController-CLIJXwGS.js.map +0 -1
- package/dist/S3StorageController-Dcuf8lMA.js.map +0 -1
- package/dist/admin-roles-vYdp_Pil.js +0 -36
- package/dist/admin-roles-vYdp_Pil.js.map +0 -1
- package/dist/admin_block-DxKLmdiv.js +0 -206
- package/dist/admin_block-DxKLmdiv.js.map +0 -1
- package/dist/ast-schema-editor-CslO8Oje.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-CCDpk2rn.js.map +0 -1
- package/dist/backup-DzI9jLwc.js +0 -192
- package/dist/backup-DzI9jLwc.js.map +0 -1
- package/dist/contract-routes-eLxV0le1.js.map +0 -1
- package/dist/cron-loader-CQjvjpEw.js.map +0 -1
- package/dist/cron-routes-B7CRGfiq.js +0 -72
- package/dist/cron-routes-B7CRGfiq.js.map +0 -1
- package/dist/cron-scheduler-COPQxlEq.js.map +0 -1
- package/dist/cron-store-BYGZFNWk.js.map +0 -1
- package/dist/ddl-bootstrap-CfNvxMuK.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/history-recorder-BQmB0P_j.js.map +0 -1
- package/dist/jobs-Bjr8DZAi.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-3EEzPjhl.js.map +0 -1
- package/dist/openapi-generator-D8uFz-LW.js.map +0 -1
- package/dist/query-parser-BleZmY18.js.map +0 -1
- package/dist/schema-editor-routes-DdLihzp0.js.map +0 -1
- package/dist/src-1vL-I1Po.js.map +0 -1
- package/dist/src-Br6ARbs6.js.map +0 -1
|
@@ -3,7 +3,7 @@ import __rebaseProcess from "process";
|
|
|
3
3
|
globalThis.process ??= __rebaseProcess;
|
|
4
4
|
__rebaseCreateRequire(import.meta.url);
|
|
5
5
|
import { r as __require, t as __commonJSMin } from "./rolldown-runtime-dW7B1o5h.js";
|
|
6
|
-
import {
|
|
6
|
+
import { B as rewriteLegacyRlsFunctions, H as getDeclaredSubcollections, J as CANONICAL_TO_REST, K as getDataSourceCapabilities, M as resolveResourceRefs, Q as isRelationAggregateSort, U as isPostgresCollectionConfig, V as getCollectionDataPath, W as isRelationalCollectionConfig, X as NULL_OPS, Y as LIST_OPS, Z as REST_TO_CANONICAL, et as sortKeyToString, q as ALL_WHERE_FILTER_OPS, tt as toCanonicalOp } from "./src-CatHFUym.js";
|
|
7
7
|
//#region ../types/src/errors.ts
|
|
8
8
|
/**
|
|
9
9
|
* The single error type thrown across the entire Rebase client surface —
|
|
@@ -261,6 +261,10 @@ var Vector = class {
|
|
|
261
261
|
*/
|
|
262
262
|
var JUNCTION_PIVOT_KEY = "_pivot";
|
|
263
263
|
/** @group Models */
|
|
264
|
+
function hasForeignKeyOnTarget(relation) {
|
|
265
|
+
return relation.kind === "hasOne" || relation.kind === "hasMany";
|
|
266
|
+
}
|
|
267
|
+
/** @group Models */
|
|
264
268
|
function isManyToMany(relation) {
|
|
265
269
|
return relation.kind === "manyToMany";
|
|
266
270
|
}
|
|
@@ -3266,6 +3270,7 @@ var REBASE_INTERNAL_TABLES = [
|
|
|
3266
3270
|
"api_keys",
|
|
3267
3271
|
"cron_logs",
|
|
3268
3272
|
"cron_claims",
|
|
3273
|
+
"cron_job_state",
|
|
3269
3274
|
"jobs",
|
|
3270
3275
|
"rate_limit_hits",
|
|
3271
3276
|
"idempotency_keys",
|
|
@@ -4013,6 +4018,13 @@ var CollectionRegistry = class {
|
|
|
4013
4018
|
}
|
|
4014
4019
|
collectionsByTableName = /* @__PURE__ */ new Map();
|
|
4015
4020
|
collectionsBySlug = /* @__PURE__ */ new Map();
|
|
4021
|
+
/**
|
|
4022
|
+
* Root collections whose driver stores them under a `path` other than
|
|
4023
|
+
* their slug, by that path. A list, because the path is only unique within
|
|
4024
|
+
* a data source: two collections in different databases may both be stored
|
|
4025
|
+
* at `customer`.
|
|
4026
|
+
*/
|
|
4027
|
+
collectionsByDataPath = /* @__PURE__ */ new Map();
|
|
4016
4028
|
rootCollections = [];
|
|
4017
4029
|
cachedCollectionsList = null;
|
|
4018
4030
|
rawCollectionsByTableName = /* @__PURE__ */ new Map();
|
|
@@ -4037,6 +4049,7 @@ var CollectionRegistry = class {
|
|
|
4037
4049
|
reset() {
|
|
4038
4050
|
this.collectionsByTableName.clear();
|
|
4039
4051
|
this.collectionsBySlug.clear();
|
|
4052
|
+
this.collectionsByDataPath.clear();
|
|
4040
4053
|
this.rootCollections = [];
|
|
4041
4054
|
this.cachedCollectionsList = null;
|
|
4042
4055
|
this.rawCollectionsByTableName.clear();
|
|
@@ -4069,6 +4082,7 @@ var CollectionRegistry = class {
|
|
|
4069
4082
|
this.collectionsByTableName.set(getTableName(normalized), normalized);
|
|
4070
4083
|
this.rawCollectionsByTableName.set(getTableName(raw), raw);
|
|
4071
4084
|
if (normalized.slug) this.collectionsBySlug.set(normalized.slug, normalized);
|
|
4085
|
+
this.indexDataPath(normalized);
|
|
4072
4086
|
if (raw.slug) this.rawCollectionsBySlug.set(raw.slug, raw);
|
|
4073
4087
|
});
|
|
4074
4088
|
normalizedCollections.forEach((c) => {
|
|
@@ -4086,6 +4100,19 @@ var CollectionRegistry = class {
|
|
|
4086
4100
|
this.rootCollections.push(collection);
|
|
4087
4101
|
this.rawRootCollections.push(raw);
|
|
4088
4102
|
this._registerRecursively(collection, raw);
|
|
4103
|
+
const registered = this.collectionsByTableName.get(getTableName(collection));
|
|
4104
|
+
if (registered) this.indexDataPath(registered);
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Index a root collection by the path its driver stores it under, when
|
|
4108
|
+
* that is not its slug. Only roots: a subcollection's `path` is relative to
|
|
4109
|
+
* the record it hangs off, so it names nothing on its own.
|
|
4110
|
+
*/
|
|
4111
|
+
indexDataPath(collection) {
|
|
4112
|
+
const dataPath = getCollectionDataPath(collection);
|
|
4113
|
+
if (!dataPath || dataPath === collection.slug) return;
|
|
4114
|
+
const indexed = this.collectionsByDataPath.get(dataPath) ?? [];
|
|
4115
|
+
if (!indexed.includes(collection)) this.collectionsByDataPath.set(dataPath, [...indexed, collection]);
|
|
4089
4116
|
}
|
|
4090
4117
|
_registerRecursively(collection, rawCollection) {
|
|
4091
4118
|
if (this.collectionsByTableName.has(getTableName(collection))) return;
|
|
@@ -4139,7 +4166,24 @@ var CollectionRegistry = class {
|
|
|
4139
4166
|
}
|
|
4140
4167
|
return newProperty;
|
|
4141
4168
|
}
|
|
4142
|
-
|
|
4169
|
+
/**
|
|
4170
|
+
* The collection registered under `path`: by slug, by slug with hyphens
|
|
4171
|
+
* for underscores, by table name, and last by the `path` a root Firestore
|
|
4172
|
+
* or MongoDB collection declares for its driver.
|
|
4173
|
+
*
|
|
4174
|
+
* One string can name two collections. Declaring `slug: "fs_diagnosis",
|
|
4175
|
+
* path: "diagnosis"` is how a Firestore collection sits beside a Postgres
|
|
4176
|
+
* collection whose slug is `diagnosis` — and a reference read back from
|
|
4177
|
+
* Firestore carries `diagnosis`. The order above settles it in favour of the
|
|
4178
|
+
* slug unless `preferredDriver` is given: then the first collection whose
|
|
4179
|
+
* data source or engine is `preferredDriver` wins. Pass a reference's
|
|
4180
|
+
* `driver`, or the data source of the record the path was read from.
|
|
4181
|
+
*/
|
|
4182
|
+
get(path, preferredDriver) {
|
|
4183
|
+
if (preferredDriver) {
|
|
4184
|
+
const preferred = this.candidatesFor(path).find((collection) => collection.dataSource === preferredDriver || collection.engine === preferredDriver);
|
|
4185
|
+
if (preferred) return preferred;
|
|
4186
|
+
}
|
|
4143
4187
|
const bySlug = this.collectionsBySlug.get(path);
|
|
4144
4188
|
if (bySlug) return bySlug;
|
|
4145
4189
|
if (path.includes("-")) {
|
|
@@ -4147,7 +4191,84 @@ var CollectionRegistry = class {
|
|
|
4147
4191
|
const byNormalized = this.collectionsBySlug.get(normalized);
|
|
4148
4192
|
if (byNormalized) return byNormalized;
|
|
4149
4193
|
}
|
|
4150
|
-
|
|
4194
|
+
const byTableName = this.collectionsByTableName.get(path);
|
|
4195
|
+
if (byTableName) return byTableName;
|
|
4196
|
+
return this.collectionsByDataPath.get(path)?.[0];
|
|
4197
|
+
}
|
|
4198
|
+
/** Every collection {@link get} could answer `path` with, in its order. */
|
|
4199
|
+
candidatesFor(path) {
|
|
4200
|
+
const candidates = [
|
|
4201
|
+
this.collectionsBySlug.get(path),
|
|
4202
|
+
path.includes("-") ? this.collectionsBySlug.get(path.replace(/-/g, "_")) : void 0,
|
|
4203
|
+
this.collectionsByTableName.get(path),
|
|
4204
|
+
...this.collectionsByDataPath.get(path) ?? []
|
|
4205
|
+
];
|
|
4206
|
+
return candidates.filter((collection, index) => collection !== void 0 && candidates.indexOf(collection) === index);
|
|
4207
|
+
}
|
|
4208
|
+
/**
|
|
4209
|
+
* The path a collection's rows are stored under, for the path the admin
|
|
4210
|
+
* addresses it by.
|
|
4211
|
+
*
|
|
4212
|
+
* The admin addresses a collection by its slug — in routes, in
|
|
4213
|
+
* `data.collection(...)`, in the `path` of every entity it lists — because
|
|
4214
|
+
* the slug is what is unique. A Firestore or MongoDB collection may declare
|
|
4215
|
+
* a `path` of its own ({@link getCollectionDataPath}), and its driver has to
|
|
4216
|
+
* be handed that one, under every record and subcollection too:
|
|
4217
|
+
* `fs_diagnosis/abc/locales` is stored at `diagnosis/abc/locales`.
|
|
4218
|
+
*
|
|
4219
|
+
* A path that names no registered collection, or runs only through
|
|
4220
|
+
* collections stored under their slugs, comes back unchanged.
|
|
4221
|
+
*/
|
|
4222
|
+
resolveDataPath(path) {
|
|
4223
|
+
let resolved;
|
|
4224
|
+
const exact = this.get(path);
|
|
4225
|
+
if (exact) resolved = {
|
|
4226
|
+
collections: [exact],
|
|
4227
|
+
entityIds: []
|
|
4228
|
+
};
|
|
4229
|
+
else try {
|
|
4230
|
+
resolved = this.resolvePathToCollections(path);
|
|
4231
|
+
} catch {
|
|
4232
|
+
return path;
|
|
4233
|
+
}
|
|
4234
|
+
const { collections, entityIds } = resolved;
|
|
4235
|
+
if (collections.every((collection) => getCollectionDataPath(collection) === collection.slug)) return path;
|
|
4236
|
+
return collections.map((collection, index) => index < entityIds.length ? `${getCollectionDataPath(collection)}/${entityIds[index]}` : getCollectionDataPath(collection)).join("/");
|
|
4237
|
+
}
|
|
4238
|
+
/**
|
|
4239
|
+
* The path the admin addresses a collection by, for a path its rows are
|
|
4240
|
+
* stored under: the inverse of {@link resolveDataPath}, for a reference
|
|
4241
|
+
* read back from its driver.
|
|
4242
|
+
*
|
|
4243
|
+
* The root is the longest leading run of segments naming a collection, and
|
|
4244
|
+
* becomes its slug when the run is where that collection is stored. Each
|
|
4245
|
+
* subcollection after a record id is matched by where it is stored too. A
|
|
4246
|
+
* path that already names its collections by slug comes back unchanged.
|
|
4247
|
+
*
|
|
4248
|
+
* `preferredDriver` decides between a collection stored at the path and one
|
|
4249
|
+
* whose slug it is, as in {@link get}.
|
|
4250
|
+
*/
|
|
4251
|
+
resolveCollectionPath(path, preferredDriver) {
|
|
4252
|
+
const segments = path.split("/").filter(Boolean);
|
|
4253
|
+
for (let length = segments.length; length > 0; length--) {
|
|
4254
|
+
const prefix = segments.slice(0, length).join("/");
|
|
4255
|
+
const root = this.get(prefix, preferredDriver);
|
|
4256
|
+
if (!root) continue;
|
|
4257
|
+
const translated = [getCollectionDataPath(root) === prefix ? root.slug : prefix];
|
|
4258
|
+
let current = root;
|
|
4259
|
+
let rest = segments.slice(length);
|
|
4260
|
+
while (rest.length >= 2) {
|
|
4261
|
+
const [entityId, storedAt, ...after] = rest;
|
|
4262
|
+
const child = getSubcollections(current).find((subcollection) => getCollectionDataPath(subcollection) === storedAt);
|
|
4263
|
+
if (!child) break;
|
|
4264
|
+
translated.push(entityId, child.slug);
|
|
4265
|
+
current = child;
|
|
4266
|
+
rest = after;
|
|
4267
|
+
}
|
|
4268
|
+
const collectionPath = [...translated, ...rest].join("/");
|
|
4269
|
+
return collectionPath === segments.join("/") ? path : collectionPath;
|
|
4270
|
+
}
|
|
4271
|
+
return path;
|
|
4151
4272
|
}
|
|
4152
4273
|
/**
|
|
4153
4274
|
* Gets the pristine, un-normalized collection exactly as it was provided.
|
|
@@ -4195,29 +4316,41 @@ var CollectionRegistry = class {
|
|
|
4195
4316
|
}
|
|
4196
4317
|
/**
|
|
4197
4318
|
* Resolves a multi-segment path like "products/123/locales" and returns
|
|
4198
|
-
* information about the collections and entity IDs along the path
|
|
4319
|
+
* information about the collections and entity IDs along the path.
|
|
4320
|
+
*
|
|
4321
|
+
* A slug may contain slashes (`content/de-DE/podcasts`), so the root is
|
|
4322
|
+
* the longest leading run of segments naming a registered collection, and
|
|
4323
|
+
* each subcollection is matched the same way among its parent's.
|
|
4324
|
+
*
|
|
4325
|
+
* @param options.allowRecordPath accept a path that ends at a record
|
|
4326
|
+
* (`…/{id}`) and resolve it to the collection holding that record, with the
|
|
4327
|
+
* id last in `entityIds`. Without it such a path is an error. Only the
|
|
4328
|
+
* registry can tell the two shapes apart, because only it knows where each
|
|
4329
|
+
* slug ends.
|
|
4199
4330
|
*/
|
|
4200
|
-
resolvePathToCollections(path) {
|
|
4331
|
+
resolvePathToCollections(path, options) {
|
|
4201
4332
|
const pathSegments = path.split("/").filter((p) => p);
|
|
4202
4333
|
if (pathSegments.length === 0) throw new Error(`Invalid path: ${path}`);
|
|
4203
|
-
|
|
4204
|
-
|
|
4334
|
+
const root = splitAtLeadingCollection(pathSegments, (candidate) => this.get(candidate));
|
|
4335
|
+
if (!root) throw new Error(`Unknown collection path or slug: ${path}`);
|
|
4336
|
+
let currentCollection = root.collection;
|
|
4337
|
+
const collections = [currentCollection];
|
|
4205
4338
|
const entityIds = [];
|
|
4206
|
-
let
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
for (let i = 1; i < pathSegments.length; i += 2) {
|
|
4210
|
-
const entityId = pathSegments[i];
|
|
4339
|
+
let rest = root.rest;
|
|
4340
|
+
while (rest.length > 0) {
|
|
4341
|
+
const [entityId, ...afterEntity] = rest;
|
|
4211
4342
|
entityIds.push(entityId);
|
|
4212
|
-
if (
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
if (!subcollections || subcollections.length === 0) throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
|
|
4216
|
-
const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
|
|
4217
|
-
if (!subcollection) throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
|
|
4218
|
-
currentCollection = this.normalizeCollection(subcollection);
|
|
4219
|
-
collections.push(currentCollection);
|
|
4343
|
+
if (afterEntity.length === 0) {
|
|
4344
|
+
if (options?.allowRecordPath) break;
|
|
4345
|
+
throw new Error(`Invalid collection path: ${path}. It ends at the record '${entityId}' of '${currentCollection.slug}', not at a collection.`);
|
|
4220
4346
|
}
|
|
4347
|
+
const subcollections = getSubcollections(currentCollection);
|
|
4348
|
+
if (!subcollections || subcollections.length === 0) throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
|
|
4349
|
+
const child = splitAtLeadingCollection(afterEntity, (candidate) => subcollections.find((c) => c.slug === candidate));
|
|
4350
|
+
if (!child) throw new Error(`Subcollection '${afterEntity[0]}' not found in ${currentCollection.slug}`);
|
|
4351
|
+
currentCollection = this.normalizeCollection(child.collection);
|
|
4352
|
+
collections.push(currentCollection);
|
|
4353
|
+
rest = child.rest;
|
|
4221
4354
|
}
|
|
4222
4355
|
return {
|
|
4223
4356
|
collections,
|
|
@@ -4226,6 +4359,26 @@ var CollectionRegistry = class {
|
|
|
4226
4359
|
};
|
|
4227
4360
|
}
|
|
4228
4361
|
};
|
|
4362
|
+
/**
|
|
4363
|
+
* Split path segments at the collection they start with.
|
|
4364
|
+
*
|
|
4365
|
+
* A slug may contain slashes, and several drivers need it to: a Firestore
|
|
4366
|
+
* collection partitioned by locale is declared as `content/de-DE/podcasts`,
|
|
4367
|
+
* and that string is its *name*. So the collection a path starts with is not
|
|
4368
|
+
* its first segment but the longest run of whole leading segments that names
|
|
4369
|
+
* one, and what follows it is `entityId/subcollection/…`.
|
|
4370
|
+
*
|
|
4371
|
+
* Whole segments only, so `content` never matches the start of `contents/1`.
|
|
4372
|
+
*/
|
|
4373
|
+
function splitAtLeadingCollection(segments, find) {
|
|
4374
|
+
for (let length = segments.length; length > 0; length--) {
|
|
4375
|
+
const collection = find(segments.slice(0, length).join("/"));
|
|
4376
|
+
if (collection) return {
|
|
4377
|
+
collection,
|
|
4378
|
+
rest: segments.slice(length)
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4229
4382
|
//#endregion
|
|
4230
4383
|
//#region ../common/src/collections/engine-support.ts
|
|
4231
4384
|
/**
|
|
@@ -4317,18 +4470,36 @@ function canWriteField(property, viewer) {
|
|
|
4317
4470
|
* who knows the table can send `password_hash` as readily as `passwordHash`, and
|
|
4318
4471
|
* a rule that only knew the wire name would be one rename away from useless.
|
|
4319
4472
|
*
|
|
4473
|
+
* A restricted to-one relation takes its foreign key with it. `bandId: 7` names
|
|
4474
|
+
* the band exactly as `band: { id: 7 }` does, and the row carries the column
|
|
4475
|
+
* beside the relation — so a rule on the relation that left the key alone
|
|
4476
|
+
* withheld the value from the row and still let `?bandId=7` ask for it.
|
|
4477
|
+
*
|
|
4320
4478
|
* `kind` picks which half of the rule is read; nothing else differs.
|
|
4321
4479
|
*/
|
|
4322
4480
|
function restrictedFieldNames(collection, viewer, kind) {
|
|
4323
4481
|
const declared = [];
|
|
4324
4482
|
const refused = /* @__PURE__ */ new Set();
|
|
4325
4483
|
const allowed = kind === "read" ? canReadField : canWriteField;
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4484
|
+
const properties = collection.properties ?? {};
|
|
4485
|
+
let relations;
|
|
4486
|
+
const restrict = (name) => {
|
|
4487
|
+
if (name in properties && !declared.includes(name)) declared.push(name);
|
|
4329
4488
|
refused.add(name);
|
|
4489
|
+
};
|
|
4490
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
4491
|
+
if (allowed(property, viewer)) continue;
|
|
4492
|
+
restrict(name);
|
|
4330
4493
|
const columnName = property.columnName;
|
|
4331
4494
|
if (columnName) refused.add(columnName);
|
|
4495
|
+
if (property.type === "relation") {
|
|
4496
|
+
relations ??= resolveCollectionRelations(collection);
|
|
4497
|
+
const relation = findRelation(relations, name);
|
|
4498
|
+
if (relation?.kind === "belongsTo") {
|
|
4499
|
+
restrict(fieldKeyForColumn(collection, relation.localKey));
|
|
4500
|
+
refused.add(relation.localKey);
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4332
4503
|
}
|
|
4333
4504
|
return {
|
|
4334
4505
|
declared,
|
|
@@ -4762,23 +4933,6 @@ function includePaths(tree, prefix = "") {
|
|
|
4762
4933
|
}
|
|
4763
4934
|
return out;
|
|
4764
4935
|
}
|
|
4765
|
-
/**
|
|
4766
|
-
* The relation names an `include` asks for at the top level.
|
|
4767
|
-
*
|
|
4768
|
-
* `["author", "comments.author"]` and `{author: true, comments: {...}}` both
|
|
4769
|
-
* answer `["author", "comments"]` — a *hop*, not a path, because the only
|
|
4770
|
-
* consumer is `?fields=`, which names keys on the row being returned and a
|
|
4771
|
-
* nested relation is not one of those.
|
|
4772
|
-
*
|
|
4773
|
-
* Derived rather than passed: `include` has four spellings and three of them
|
|
4774
|
-
* are not a `string[]`, so every consumer that wants the plain names either
|
|
4775
|
-
* calls this or reimplements the flattening.
|
|
4776
|
-
*/
|
|
4777
|
-
function topLevelIncludeNames(spec) {
|
|
4778
|
-
const normalized = normalizeInclude(spec);
|
|
4779
|
-
if (!normalized) return [];
|
|
4780
|
-
return Object.keys(normalized.tree);
|
|
4781
|
-
}
|
|
4782
4936
|
/** Whether any node in the tree carries per-relation options. */
|
|
4783
4937
|
function hasOptions(tree) {
|
|
4784
4938
|
return Object.values(tree).some((node) => node.limit !== void 0 || node.where !== void 0 || node.logical !== void 0 || node.orderBy !== void 0 || node.fields !== void 0 || hasOptions(node.children));
|
|
@@ -5094,19 +5248,23 @@ function normalizeMaxRows(raw) {
|
|
|
5094
5248
|
* @param label the collection name, so an error says which walk failed
|
|
5095
5249
|
*/
|
|
5096
5250
|
async function* paginateFind(find, params, label = "collection") {
|
|
5097
|
-
const { pageSize, cursor, maxPages, ...rest } = params ?? {};
|
|
5251
|
+
const { pageSize, cursor, maxPages, after: startAfter, offset: startOffset, page: startPage, ...rest } = params ?? {};
|
|
5098
5252
|
const findParams = { ...rest };
|
|
5099
5253
|
const size = normalizePageSize(pageSize);
|
|
5100
5254
|
const pageCap = normalizeMaxPages(maxPages);
|
|
5101
|
-
const seekRequested = cursor !== void 0 && cursor !== null;
|
|
5102
|
-
if (
|
|
5255
|
+
const seekRequested = cursor !== void 0 && cursor !== null || !!startAfter;
|
|
5256
|
+
if (cursor !== void 0 && cursor !== null) {
|
|
5103
5257
|
const field = typeof cursor === "string" ? cursor : cursor.field;
|
|
5104
5258
|
const requested = typeof cursor === "object" && cursor !== null ? cursor.direction : void 0;
|
|
5105
5259
|
if (!normalizeOrderBy(findParams.orderBy)) findParams.orderBy = [field, requested ?? "asc"];
|
|
5106
5260
|
}
|
|
5107
|
-
let offset =
|
|
5261
|
+
let offset = resolveFindWindow({
|
|
5262
|
+
limit: size,
|
|
5263
|
+
offset: startOffset,
|
|
5264
|
+
page: startPage
|
|
5265
|
+
}).offset;
|
|
5108
5266
|
let pages = 0;
|
|
5109
|
-
let after;
|
|
5267
|
+
let after = startAfter || void 0;
|
|
5110
5268
|
for (;;) {
|
|
5111
5269
|
if (pages >= pageCap) throw new RebasePaginationError("max-pages", `Iterating "${label}" made ${pages} requests without the server reporting the end of the collection. Stopping rather than looping forever — raise \`maxPages\` if the walk is genuinely this long, or check that the backend sets \`meta.hasMore\`.`);
|
|
5112
5270
|
const pageParams = {
|
|
@@ -5171,9 +5329,16 @@ async function collectAllPages(find, params, label = "collection") {
|
|
|
5171
5329
|
* Relation values (`EntityRelation` instances or `{ __type: "relation", id, path }`
|
|
5172
5330
|
* objects) are serialized as their raw id — the wire format only carries the
|
|
5173
5331
|
* value to compare against the FK column.
|
|
5332
|
+
*
|
|
5333
|
+
* A `Date` is its ISO 8601 instant — what JSON makes of it on the socket and
|
|
5334
|
+
* in-process, so one filter names one instant through every door. `String()`
|
|
5335
|
+
* gave the viewer's local zone, no milliseconds and a parenthesised zone name
|
|
5336
|
+
* Postgres cannot parse. An invalid date keeps its "Invalid Date" spelling for
|
|
5337
|
+
* the server to refuse, rather than throwing a `RangeError` from here.
|
|
5174
5338
|
*/
|
|
5175
5339
|
function stringifyValue(value) {
|
|
5176
5340
|
if (value === null) return "null";
|
|
5341
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
|
|
5177
5342
|
const relation = normalizeToEntityRelation(value);
|
|
5178
5343
|
if (relation) return String(relation.id);
|
|
5179
5344
|
return String(value);
|
|
@@ -5231,6 +5396,41 @@ function unescapeWireValue(value) {
|
|
|
5231
5396
|
return result;
|
|
5232
5397
|
}
|
|
5233
5398
|
/**
|
|
5399
|
+
* Whether a scalar, sent raw as a query parameter's operand, would be read
|
|
5400
|
+
* back as something other than itself.
|
|
5401
|
+
*
|
|
5402
|
+
* A query parameter owns its whole value, so its scalar goes out unescaped —
|
|
5403
|
+
* `eq.a,b`, `eq.C:\x` — and the decoder returns it verbatim. Two shapes break
|
|
5404
|
+
* that. A parenthesised operand is a list for every operator (so that
|
|
5405
|
+
* `["==", ["a","b"]]` on an array column stays a list), so raw, the scalar
|
|
5406
|
+
* `"(deleted)"` would arrive as `["deleted"]`: an `==` matching nothing, a
|
|
5407
|
+
* `!=` returning the very rows it excludes. Such a scalar is sent escaped,
|
|
5408
|
+
* `eq.\(deleted\)`. And that spelling has to be told apart from a raw value
|
|
5409
|
+
* that looks the same, so a scalar of *that* shape is escaped in turn.
|
|
5410
|
+
*
|
|
5411
|
+
* Everything else stays raw, so a value an older client sends decodes as it
|
|
5412
|
+
* always has — unless it is exactly the escaped spelling of such a scalar,
|
|
5413
|
+
* which decodes to the scalar it spells.
|
|
5414
|
+
*/
|
|
5415
|
+
function scalarNeedsEscape(value) {
|
|
5416
|
+
if (value.startsWith("(") && value.endsWith(")")) return true;
|
|
5417
|
+
return decodeEscapedScalar(value) !== void 0;
|
|
5418
|
+
}
|
|
5419
|
+
/**
|
|
5420
|
+
* The scalar an operand is the escaped spelling of, when it is one.
|
|
5421
|
+
*
|
|
5422
|
+
* Exact on purpose: only an operand that {@link escapeWireValue} produces from
|
|
5423
|
+
* a scalar that {@link scalarNeedsEscape} is decoded. Any other operand —
|
|
5424
|
+
* including a raw backslash an older client sent, like `\\server\share` — is
|
|
5425
|
+
* left verbatim. Terminates because an operand that matches contains an
|
|
5426
|
+
* escape pair, so the value is strictly shorter than it.
|
|
5427
|
+
*/
|
|
5428
|
+
function decodeEscapedScalar(operand) {
|
|
5429
|
+
if (!operand.startsWith("\\")) return void 0;
|
|
5430
|
+
const value = unescapeWireValue(operand);
|
|
5431
|
+
return escapeWireValue(value) === operand && scalarNeedsEscape(value) ? value : void 0;
|
|
5432
|
+
}
|
|
5433
|
+
/**
|
|
5234
5434
|
* Split a parenthesized list string on unescaped commas.
|
|
5235
5435
|
* Input is the content between `(` and `)`.
|
|
5236
5436
|
*
|
|
@@ -5475,6 +5675,42 @@ function readTuple(field, raw) {
|
|
|
5475
5675
|
if (isOperatorShaped(op)) throw new UnknownFilterOperatorError(field, op);
|
|
5476
5676
|
}
|
|
5477
5677
|
/**
|
|
5678
|
+
* The query-parameter names a REST listing reads as something other than a
|
|
5679
|
+
* field filter: pagination, sorting, includes, projection, search, the logical
|
|
5680
|
+
* groups, the JSON `where` dialect, an aggregate's `select`/`groupBy`, and the
|
|
5681
|
+
* soft-delete switches.
|
|
5682
|
+
*
|
|
5683
|
+
* One list for both ends. The server skips these names when it collects
|
|
5684
|
+
* `?<field>=<op>.<value>` filters, and the SDK sends a filter on a column with
|
|
5685
|
+
* one of these names inside `?where=` instead — a collection is free to have a
|
|
5686
|
+
* `page`, `select` or `hard` column, and a filter on it sent as its own
|
|
5687
|
+
* parameter was read as the parameter and never applied.
|
|
5688
|
+
*/
|
|
5689
|
+
var RESERVED_QUERY_KEYS = /* @__PURE__ */ new Set([
|
|
5690
|
+
"limit",
|
|
5691
|
+
"offset",
|
|
5692
|
+
"page",
|
|
5693
|
+
"after",
|
|
5694
|
+
"orderBy",
|
|
5695
|
+
"include",
|
|
5696
|
+
"fields",
|
|
5697
|
+
"distinct",
|
|
5698
|
+
"searchString",
|
|
5699
|
+
"searchExplain",
|
|
5700
|
+
"vector_search",
|
|
5701
|
+
"vector",
|
|
5702
|
+
"vector_distance",
|
|
5703
|
+
"vector_threshold",
|
|
5704
|
+
"or",
|
|
5705
|
+
"and",
|
|
5706
|
+
"not",
|
|
5707
|
+
"where",
|
|
5708
|
+
"select",
|
|
5709
|
+
"groupBy",
|
|
5710
|
+
"deleted",
|
|
5711
|
+
"hard"
|
|
5712
|
+
]);
|
|
5713
|
+
/**
|
|
5478
5714
|
* Encode the `<op>.<value>` half of a wire condition.
|
|
5479
5715
|
*
|
|
5480
5716
|
* This is the single leaf encoder. Both wire positions that carry a condition
|
|
@@ -5502,7 +5738,7 @@ function serializeOperatorAndValue(op, value, { escapeScalar, where }) {
|
|
|
5502
5738
|
return `${restOp}.(${value.map((v) => escapeWireValue(stringifyValue(v))).join(",")})`;
|
|
5503
5739
|
}
|
|
5504
5740
|
const scalar = stringifyValue(value);
|
|
5505
|
-
return `${restOp}.${escapeScalar ? escapeWireValue(scalar) : scalar}`;
|
|
5741
|
+
return `${restOp}.${escapeScalar || !LIST_OPS.has(op) && scalarNeedsEscape(scalar) ? escapeWireValue(scalar) : scalar}`;
|
|
5506
5742
|
}
|
|
5507
5743
|
/**
|
|
5508
5744
|
* Serialize a single canonical condition tuple to a PostgREST dot-string.
|
|
@@ -5598,7 +5834,10 @@ var NULL_OPERANDS = /* @__PURE__ */ new Set([
|
|
|
5598
5834
|
* operand: only {@link NULL_OPERANDS}. `notnull.reason` is the value
|
|
5599
5835
|
* `"notnull.reason"`, not "reason is not null".
|
|
5600
5836
|
* - **Everything else** takes one scalar, and any remainder is one — including
|
|
5601
|
-
* the empty string, so `eq.` really is "equals the empty string".
|
|
5837
|
+
* the empty string, so `eq.` really is "equals the empty string". A
|
|
5838
|
+
* parenthesised remainder is a list for these too (`eq.(a,b)` is array
|
|
5839
|
+
* equality); a scalar that is itself parenthesised arrives escaped,
|
|
5840
|
+
* `eq.\(none\)`, and is decoded here — see {@link scalarNeedsEscape}.
|
|
5602
5841
|
*
|
|
5603
5842
|
* ### The one ambiguity that remains, and how to write past it
|
|
5604
5843
|
*
|
|
@@ -5633,7 +5872,7 @@ function deserializeSingle(raw) {
|
|
|
5633
5872
|
return [canonicalOp, inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner)];
|
|
5634
5873
|
}
|
|
5635
5874
|
if (LIST_OPS.has(canonicalOp)) return ["==", raw];
|
|
5636
|
-
return [canonicalOp, rest];
|
|
5875
|
+
return [canonicalOp, decodeEscapedScalar(rest) ?? rest];
|
|
5637
5876
|
}
|
|
5638
5877
|
/**
|
|
5639
5878
|
* Convert a PostgREST-style querystring record to `FilterValues`.
|
|
@@ -6002,7 +6241,7 @@ function inlineRelationRefs(row) {
|
|
|
6002
6241
|
}
|
|
6003
6242
|
return out ?? row;
|
|
6004
6243
|
}
|
|
6005
|
-
function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
6244
|
+
function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, storedPath = () => slug) {
|
|
6006
6245
|
const accessor = {
|
|
6007
6246
|
async find(params) {
|
|
6008
6247
|
const filter = params?.where ? deserializeFilter(params.where) : void 0;
|
|
@@ -6012,7 +6251,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6012
6251
|
const startAfter = cursor ? cursorToStartAfter(cursor) : void 0;
|
|
6013
6252
|
const probeLimit = startAfter ? limit + 1 : limit;
|
|
6014
6253
|
const fetchService = driver.restFetchService;
|
|
6015
|
-
const fetched = fetchService ? await fetchService.fetchCollectionForRest(
|
|
6254
|
+
const fetched = fetchService ? await fetchService.fetchCollectionForRest(storedPath(), {
|
|
6016
6255
|
filter,
|
|
6017
6256
|
logical: params?.logical,
|
|
6018
6257
|
limit: probeLimit,
|
|
@@ -6023,7 +6262,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6023
6262
|
fields: params?.fields,
|
|
6024
6263
|
distinct: params?.distinct
|
|
6025
6264
|
}, params?.include) : await driver.fetchCollection({
|
|
6026
|
-
path:
|
|
6265
|
+
path: storedPath(),
|
|
6027
6266
|
limit: probeLimit,
|
|
6028
6267
|
offset: startAfter ? void 0 : driverOffset,
|
|
6029
6268
|
startAfter,
|
|
@@ -6041,7 +6280,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6041
6280
|
let hasMore = seeking ? fetched.length > limit : rows.length >= limit;
|
|
6042
6281
|
if (driver.count) {
|
|
6043
6282
|
total = await driver.count({
|
|
6044
|
-
path:
|
|
6283
|
+
path: storedPath(),
|
|
6045
6284
|
filter,
|
|
6046
6285
|
logical: params?.logical,
|
|
6047
6286
|
searchString: params?.searchString
|
|
@@ -6049,7 +6288,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6049
6288
|
if (!seeking) hasMore = offset + rows.length < total;
|
|
6050
6289
|
}
|
|
6051
6290
|
const last = rows[rows.length - 1];
|
|
6052
|
-
const nextCursor = hasMore && last && driver.restFetchService?.cursorFor ? driver.restFetchService.cursorFor(
|
|
6291
|
+
const nextCursor = hasMore && last && driver.restFetchService?.cursorFor ? driver.restFetchService.cursorFor(storedPath(), last, orderBy) : void 0;
|
|
6053
6292
|
return {
|
|
6054
6293
|
data: rows.map((row) => rowToEntity(row, slug, getPks(), toViewModel)),
|
|
6055
6294
|
meta: {
|
|
@@ -6063,23 +6302,39 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6063
6302
|
},
|
|
6064
6303
|
async findById(id) {
|
|
6065
6304
|
const fetchService = driver.restFetchService;
|
|
6066
|
-
const row = fetchService ? await fetchService.fetchOneForRest(
|
|
6067
|
-
path:
|
|
6305
|
+
const row = fetchService ? await fetchService.fetchOneForRest(storedPath(), id) : await driver.fetchOne({
|
|
6306
|
+
path: storedPath(),
|
|
6068
6307
|
id
|
|
6069
6308
|
});
|
|
6070
6309
|
return row ? rowToEntity(row, slug, getPks(), toViewModel) : void 0;
|
|
6071
6310
|
},
|
|
6072
|
-
aggregate: driver.restFetchService?.aggregate ? async (params) =>
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6311
|
+
aggregate: driver.restFetchService?.aggregate ? async (params) => {
|
|
6312
|
+
const limit = (params.groupBy?.length ?? 0) > 0 ? params.limit : void 0;
|
|
6313
|
+
const rows = await driver.restFetchService.aggregate(storedPath(), {
|
|
6314
|
+
aggregates: params.select.map(toDriverAggregate),
|
|
6315
|
+
groupBy: params.groupBy,
|
|
6316
|
+
filter: params.where ? deserializeFilter(params.where) : void 0,
|
|
6317
|
+
logical: params.logical,
|
|
6318
|
+
searchString: params.searchString,
|
|
6319
|
+
limit: limit !== void 0 ? limit + 1 : params.limit,
|
|
6320
|
+
offset: params.offset
|
|
6321
|
+
});
|
|
6322
|
+
if (limit === void 0) return rows;
|
|
6323
|
+
const page = rows.slice(0, limit);
|
|
6324
|
+
const meta = {
|
|
6325
|
+
limit,
|
|
6326
|
+
offset: params.offset ?? 0,
|
|
6327
|
+
hasMore: rows.length > limit
|
|
6328
|
+
};
|
|
6329
|
+
Object.defineProperty(page, "meta", {
|
|
6330
|
+
value: meta,
|
|
6331
|
+
enumerable: false
|
|
6332
|
+
});
|
|
6333
|
+
return page;
|
|
6334
|
+
} : void 0,
|
|
6080
6335
|
async create(data, id) {
|
|
6081
6336
|
return rowToEntity(await driver.save({
|
|
6082
|
-
path:
|
|
6337
|
+
path: storedPath(),
|
|
6083
6338
|
values: data,
|
|
6084
6339
|
id,
|
|
6085
6340
|
status: "new"
|
|
@@ -6087,7 +6342,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6087
6342
|
},
|
|
6088
6343
|
createMany: driver.saveMany ? async (data, options) => {
|
|
6089
6344
|
return (await driver.saveMany({
|
|
6090
|
-
path:
|
|
6345
|
+
path: storedPath(),
|
|
6091
6346
|
rows: data,
|
|
6092
6347
|
upsert: options?.upsert,
|
|
6093
6348
|
onConflict: options?.onConflict
|
|
@@ -6095,7 +6350,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6095
6350
|
} : void 0,
|
|
6096
6351
|
async update(id, data) {
|
|
6097
6352
|
return rowToEntity(await driver.save({
|
|
6098
|
-
path:
|
|
6353
|
+
path: storedPath(),
|
|
6099
6354
|
values: data,
|
|
6100
6355
|
id,
|
|
6101
6356
|
status: "existing"
|
|
@@ -6104,12 +6359,12 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6104
6359
|
async delete(id) {
|
|
6105
6360
|
return driver.delete({ row: {
|
|
6106
6361
|
id,
|
|
6107
|
-
path:
|
|
6362
|
+
path: storedPath()
|
|
6108
6363
|
} });
|
|
6109
6364
|
},
|
|
6110
6365
|
updateMany: driver.updateMany ? async (updates) => {
|
|
6111
6366
|
return (await driver.updateMany({
|
|
6112
|
-
path:
|
|
6367
|
+
path: storedPath(),
|
|
6113
6368
|
updates: updates.map((u) => ({
|
|
6114
6369
|
id: u.id,
|
|
6115
6370
|
values: u.data
|
|
@@ -6118,14 +6373,14 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6118
6373
|
} : void 0,
|
|
6119
6374
|
deleteMany: driver.deleteMany ? async (ids) => {
|
|
6120
6375
|
await driver.deleteMany({
|
|
6121
|
-
path:
|
|
6376
|
+
path: storedPath(),
|
|
6122
6377
|
ids
|
|
6123
6378
|
});
|
|
6124
6379
|
} : void 0,
|
|
6125
6380
|
count: driver.count ? async (params) => {
|
|
6126
6381
|
const filter = params?.where ? deserializeFilter(params.where) : void 0;
|
|
6127
6382
|
return driver.count({
|
|
6128
|
-
path:
|
|
6383
|
+
path: storedPath(),
|
|
6129
6384
|
filter,
|
|
6130
6385
|
logical: params?.logical,
|
|
6131
6386
|
searchString: params?.searchString
|
|
@@ -6135,7 +6390,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6135
6390
|
const { limit, offset, driverOffset } = resolveFindWindow(params);
|
|
6136
6391
|
const normalize = driver.restFetchService ? inlineRelationRefs : (row) => row;
|
|
6137
6392
|
return driver.listenCollection({
|
|
6138
|
-
path:
|
|
6393
|
+
path: storedPath(),
|
|
6139
6394
|
limit,
|
|
6140
6395
|
offset: driverOffset,
|
|
6141
6396
|
filter: params?.where,
|
|
@@ -6161,7 +6416,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6161
6416
|
listenById: driver.listenOne ? (id, onUpdate, onError) => {
|
|
6162
6417
|
const normalize = driver.restFetchService ? inlineRelationRefs : (row) => row;
|
|
6163
6418
|
return driver.listenOne({
|
|
6164
|
-
path:
|
|
6419
|
+
path: storedPath(),
|
|
6165
6420
|
id,
|
|
6166
6421
|
onUpdate: (entity) => onUpdate(entity ? rowToEntity(normalize(entity), slug, getPks(), toViewModel) : void 0),
|
|
6167
6422
|
onError
|
|
@@ -6230,7 +6485,7 @@ function buildRebaseData(driver, options) {
|
|
|
6230
6485
|
function getAccessor(slug) {
|
|
6231
6486
|
let accessor = cache.get(slug);
|
|
6232
6487
|
if (!accessor) {
|
|
6233
|
-
accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug), viewModelFor(slug));
|
|
6488
|
+
accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug), viewModelFor(slug), () => options?.resolveDataPath?.(slug) ?? slug);
|
|
6234
6489
|
cache.set(slug, accessor);
|
|
6235
6490
|
}
|
|
6236
6491
|
return accessor;
|
|
@@ -6387,6 +6642,31 @@ var SdkQueryBuilder = class {
|
|
|
6387
6642
|
}
|
|
6388
6643
|
};
|
|
6389
6644
|
/**
|
|
6645
|
+
* Refuse a write option this door cannot honour, by name, before anything is
|
|
6646
|
+
* written.
|
|
6647
|
+
*
|
|
6648
|
+
* The in-process accessor implements the same `SDKCollectionClient` as the HTTP
|
|
6649
|
+
* client, whose `ifMatch` is "Honoured on `update` and `delete`" — and its
|
|
6650
|
+
* writes took no options at all, so `rebase.data.posts.update(id, data, {
|
|
6651
|
+
* ifMatch: staleEtag })` overwrote the row and resolved. A precondition the
|
|
6652
|
+
* caller wrote, dropped without a word, is the worst thing a door can do with
|
|
6653
|
+
* it. The driver's `save` has no precondition and no idempotency store to hand
|
|
6654
|
+
* these to, so they are refused rather than ignored. An `undefined` value is no
|
|
6655
|
+
* option, as it is over HTTP: `etagOf(row)` is `undefined` for a row that
|
|
6656
|
+
* carries no version, and that means "write unconditionally".
|
|
6657
|
+
*/
|
|
6658
|
+
function refuseUnhonouredWriteOptions(method, slug, options) {
|
|
6659
|
+
const unsupported = ["ifMatch", "idempotencyKey"].filter((key) => options?.[key]);
|
|
6660
|
+
if (unsupported.length === 0) return;
|
|
6661
|
+
throw new RebaseClientError(`${method}() on "${slug}" in-process (context.data, rebase.data) cannot honour ${unsupported.join(" or ")}: this door has no ${unsupported.includes("ifMatch") ? "precondition check" : "idempotency store"}, and ignoring the option would write as though it had been checked. Drop it here, or send the write through the HTTP client, which honours it.`, {
|
|
6662
|
+
code: "UNSUPPORTED_OPTION",
|
|
6663
|
+
details: {
|
|
6664
|
+
method,
|
|
6665
|
+
options: unsupported
|
|
6666
|
+
}
|
|
6667
|
+
});
|
|
6668
|
+
}
|
|
6669
|
+
/**
|
|
6390
6670
|
* Wrap a Entity-shaped {@link CollectionAccessor} into a flat
|
|
6391
6671
|
* {@link SDKCollectionClient}. Every returned record is unwrapped to a flat row
|
|
6392
6672
|
* so the backend SDK is byte-for-byte the same shape as the frontend client.
|
|
@@ -6418,14 +6698,19 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
6418
6698
|
});
|
|
6419
6699
|
return entityToRow(s);
|
|
6420
6700
|
},
|
|
6421
|
-
async create(data, id) {
|
|
6701
|
+
async create(data, id, options) {
|
|
6702
|
+
refuseUnhonouredWriteOptions("create", slug, options);
|
|
6422
6703
|
return entityToRow(await snap.create(data, id));
|
|
6423
6704
|
},
|
|
6424
6705
|
async createMany(data, options) {
|
|
6706
|
+
refuseUnhonouredWriteOptions("createMany", slug, options);
|
|
6425
6707
|
if (!Array.isArray(data)) throw new TypeError("createMany expects an array of records.");
|
|
6426
6708
|
if (data.length === 0) return [];
|
|
6427
6709
|
if (!snap.createMany) throw new Error("Bulk writes are not supported by this collection's data source. Fall back to create() per record.");
|
|
6428
|
-
return (await snap.createMany(data,
|
|
6710
|
+
return (await snap.createMany(data, {
|
|
6711
|
+
upsert: options?.upsert,
|
|
6712
|
+
onConflict: options?.onConflict
|
|
6713
|
+
})).map(entityToRow);
|
|
6429
6714
|
},
|
|
6430
6715
|
/**
|
|
6431
6716
|
* One row through the bulk path, because the bulk path is where the
|
|
@@ -6437,6 +6722,7 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
6437
6722
|
* one is exactly an upsert of one.
|
|
6438
6723
|
*/
|
|
6439
6724
|
async upsert(data, options) {
|
|
6725
|
+
refuseUnhonouredWriteOptions("upsert", slug, options);
|
|
6440
6726
|
if (!snap.createMany) throw new Error("Upsert is not supported by this collection's data source: it needs a bulk write, which this driver does not implement. Fall back to create() or update().");
|
|
6441
6727
|
const row = (await snap.createMany([data], {
|
|
6442
6728
|
upsert: true,
|
|
@@ -6445,10 +6731,12 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
6445
6731
|
if (!row) throw new Error(`Upsert into "${slug}" returned no row.`);
|
|
6446
6732
|
return entityToRow(row);
|
|
6447
6733
|
},
|
|
6448
|
-
async update(id, data) {
|
|
6734
|
+
async update(id, data, options) {
|
|
6735
|
+
refuseUnhonouredWriteOptions("update", slug, options);
|
|
6449
6736
|
return entityToRow(await snap.update(id, data));
|
|
6450
6737
|
},
|
|
6451
|
-
async updateMany(updates) {
|
|
6738
|
+
async updateMany(updates, options) {
|
|
6739
|
+
refuseUnhonouredWriteOptions("updateMany", slug, options);
|
|
6452
6740
|
if (!Array.isArray(updates)) throw new TypeError("updateMany expects an array of { id, data } entries.");
|
|
6453
6741
|
if (updates.length === 0) return [];
|
|
6454
6742
|
if (!snap.updateMany) throw new Error("Bulk updates are not supported by this collection's data source. Fall back to update() per record.");
|
|
@@ -6457,10 +6745,12 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
6457
6745
|
data: u.data
|
|
6458
6746
|
})))).map(entityToRow);
|
|
6459
6747
|
},
|
|
6460
|
-
delete(id) {
|
|
6748
|
+
async delete(id, options) {
|
|
6749
|
+
refuseUnhonouredWriteOptions("delete", slug, options);
|
|
6461
6750
|
return snap.delete(id);
|
|
6462
6751
|
},
|
|
6463
|
-
async deleteMany(ids) {
|
|
6752
|
+
async deleteMany(ids, options) {
|
|
6753
|
+
refuseUnhonouredWriteOptions("deleteMany", slug, options);
|
|
6464
6754
|
if (!Array.isArray(ids)) throw new TypeError("deleteMany expects an array of ids.");
|
|
6465
6755
|
if (ids.length === 0) return;
|
|
6466
6756
|
if (!snap.deleteMany) throw new Error("Bulk deletes are not supported by this collection's data source. Fall back to delete() per record.");
|
|
@@ -6575,6 +6865,6 @@ function buildRoutedRebaseData({ defaultData, sources, resolveKey }) {
|
|
|
6575
6865
|
}
|
|
6576
6866
|
new Set(REBASE_INTERNAL_TABLES);
|
|
6577
6867
|
//#endregion
|
|
6578
|
-
export {
|
|
6868
|
+
export { unref as $, isRelationalCollection as A, findRelation as B, decodeCursor as C, assertBeforeQueryIsPostgresOnly as D, restrictedFieldNames as E, getJunctionConfigForRelation as F, enumToObjectEntries as G, isRelationRequired as H, getEffectiveSecurityRules as I, hydrateRegExp as J, buildCompositeId as K, getTenantConfig as L, authSecretsMissingExclusion as M, REBASE_USER_ROLE as N, CollectionRegistry as O, revokeInternalTableSql as P, toSnakeCase as Q, securityRuleToConditions as R, cursorToStartAfter as S, effectiveAccess as T, relationDeclaringProperty as U, getTableName as V, resolveCollectionRelations as W, splitSearchTerms as X, isObject as Y, suggestNearMiss as Z, OrderBySpecError as _, RebaseApiError as _t, deserializeFilter as a, hasFieldOperation as at, CursorError as b, unsupportedMethod as bt, serializeLogicalCondition as c, isAnonymousUid as ct, resolveFindWindow as d, isManyToMany as dt, ListLimitError as et, IncludeSpecError as f, isToMany as ft, serializeInclude as g, Vector as gt, normalizeInclude as h, GeoPoint as ht, UnknownFilterOperatorError as i, FIELD_OPERATORS as it, resolveDataSource as j, createDataSourceRegistry as k, collectAllPages as l, JUNCTION_PIVOT_KEY as lt, mergeIncludeSpecs as m, EntityRelation as mt, buildSdkData as n, resolveClientListLimit as nt, deserializeLogicalCondition as o, isFieldOperation as ot, deserializeInclude as p, EntityReference as pt, resolvePrimaryKeys as q, RESERVED_QUERY_KEYS as r, BATCH_REF_KEY as rt, serializeFilter as s, ANONYMOUS_USER_ID as st, buildRoutedRebaseData as t, MAX_LIST_LIMIT as tt, paginateFind as u, hasForeignKeyOnTarget as ut, normalizeOrderBy as v, RebaseClientError as vt, reconcileCursorOrder as w, CursorMismatchError as x, serializeOrderBy as y, isUnsupported as yt, fieldKeyForColumn as z };
|
|
6579
6869
|
|
|
6580
|
-
//# sourceMappingURL=src-
|
|
6870
|
+
//# sourceMappingURL=src-I3aG1PcY.js.map
|