@rebasepro/common 0.17.3 → 0.18.1
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 +4 -0
- package/dist/collections/CollectionRegistry.d.ts +1 -1
- package/dist/collections/default-collections.d.ts +15 -84
- package/dist/data/buildRebaseData.d.ts +1 -1
- package/dist/data/filter-dialect.d.ts +11 -0
- package/dist/data/sort-dialect.d.ts +15 -3
- package/dist/index.es.js +375 -63
- package/dist/index.es.js.map +1 -1
- package/dist/util/builders.d.ts +69 -24
- package/dist/util/callback-errors.d.ts +77 -0
- package/dist/util/callback-errors.test.d.ts +1 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/policy/evaluatePolicy.d.ts +6 -0
- package/dist/util/relations.d.ts +41 -0
- package/dist/util/table-name.test.d.ts +1 -0
- package/package.json +26 -22
- package/src/collections/CollectionRegistry.ts +0 -485
- package/src/collections/default-collections.ts +0 -109
- package/src/collections/index.ts +0 -2
- package/src/data/buildRebaseData.ts +0 -816
- package/src/data/buildRoutedRebaseData.ts +0 -103
- package/src/data/filter-conditions.ts +0 -46
- package/src/data/filter-dialect.ts +0 -737
- package/src/data/paginate.ts +0 -334
- package/src/data/query_builder.ts +0 -176
- package/src/data/resolveDataSource.ts +0 -135
- package/src/data/sort-dialect.ts +0 -237
- package/src/index.ts +0 -11
- package/src/table-classification.ts +0 -109
- package/src/types/json-logic-js.d.ts +0 -8
- package/src/util/auth-default-policies.ts +0 -215
- package/src/util/builders.ts +0 -82
- package/src/util/callbacks.ts +0 -122
- package/src/util/collections.ts +0 -117
- package/src/util/common.ts +0 -2
- package/src/util/conditions.ts +0 -168
- package/src/util/email.ts +0 -32
- package/src/util/entities.ts +0 -282
- package/src/util/enums.ts +0 -26
- package/src/util/identity.ts +0 -202
- package/src/util/index.ts +0 -21
- package/src/util/internal-tables.test.ts +0 -188
- package/src/util/internal-tables.ts +0 -197
- package/src/util/junction-policies.ts +0 -355
- package/src/util/paths.ts +0 -27
- package/src/util/permissions.test.ts +0 -866
- package/src/util/permissions.ts +0 -206
- package/src/util/pg-column-to-property.ts +0 -377
- package/src/util/policy/evaluatePolicy.ts +0 -194
- package/src/util/policy/index.ts +0 -4
- package/src/util/policy/policyToPostgres.ts +0 -263
- package/src/util/policy/securityRuleToConditions.ts +0 -67
- package/src/util/policy/sqlToPolicy.ts +0 -422
- package/src/util/relations.ts +0 -236
- package/src/util/resolutions.ts +0 -534
- package/src/util/resolve-relation.ts +0 -243
- package/src/util/storage.ts +0 -177
- package/src/util/string-column-length.ts +0 -31
package/dist/index.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, DEFAULT_LIST_LIMIT, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, RLS_ROLES_SQL, RLS_UID_SQL, getDataSourceCapabilities, getDeclaredSubcollections, isAnonymousUid, isManyToMany, isPostgresCollectionConfig, isRelationAggregateSort, isRelationalCollectionConfig, policy, rewriteLegacyRlsFunctions, sortKeyToString, toCanonicalOp } from "@rebasepro/types";
|
|
1
|
+
import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, DEFAULT_LIST_LIMIT, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, RLS_IS_ANONYMOUS_SQL, RLS_ROLES_SQL, RLS_UID_SQL, RebaseApiError, getDataSourceCapabilities, getDeclaredSubcollections, isAnonymousUid, isManyToMany, isPostgresCollectionConfig, isRelationAggregateSort, isRelationalCollectionConfig, isUnsupported, policy, resolveResourceRefs, rewriteLegacyRlsFunctions, sortKeyToString, toCanonicalOp, unsupportedMethod } from "@rebasepro/types";
|
|
2
2
|
import { deepClone, firstFreeKey, generateForeignKeyName, getIn, getPolicyNamesForRules, getPolicyOperations, isDefaultFieldConfigId, mergeDeep, prettifyIdentifier, randomString, removeFunctions, toSnakeCase, toWireKey } from "@rebasepro/utils";
|
|
3
3
|
import jsonLogic from "json-logic-js";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
@@ -490,8 +490,7 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
|
490
490
|
targetSlug: targetCollection.slug,
|
|
491
491
|
onUpdate: relation.onUpdate,
|
|
492
492
|
onDelete: relation.onDelete,
|
|
493
|
-
overrides: relation.overrides
|
|
494
|
-
validation: relation.validation
|
|
493
|
+
overrides: relation.overrides
|
|
495
494
|
};
|
|
496
495
|
const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
|
|
497
496
|
switch (relation.kind) {
|
|
@@ -669,6 +668,45 @@ function resolveCollectionRelations(collection) {
|
|
|
669
668
|
return relations;
|
|
670
669
|
}
|
|
671
670
|
/**
|
|
671
|
+
* The `type: "relation"` property that declares a link, or `undefined` for one
|
|
672
|
+
* that only exists in the collection's `relations` array.
|
|
673
|
+
*
|
|
674
|
+
* Both declaration sites end up in {@link resolveCollectionRelations}, and only
|
|
675
|
+
* one of them has a property to carry field-level facts — `name`, `admin`, and
|
|
676
|
+
* the one this exists for, `validation.required`.
|
|
677
|
+
*/
|
|
678
|
+
function relationDeclaringProperty(collection, relation) {
|
|
679
|
+
const resolved = resolveCollectionRelations(collection);
|
|
680
|
+
for (const [key, raw] of Object.entries(collection.properties ?? {})) {
|
|
681
|
+
const prop = raw;
|
|
682
|
+
if (prop?.type !== "relation") continue;
|
|
683
|
+
if (resolved[key] === relation) return prop;
|
|
684
|
+
const addressed = prop.relation?.relationName;
|
|
685
|
+
if (addressed && findRelation(resolved, addressed) === relation) return prop;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Must every row of this collection point at a target through this link?
|
|
690
|
+
*
|
|
691
|
+
* Read from the declaring property's `validation.required` — the same key every
|
|
692
|
+
* other field uses, and the only place it lives.
|
|
693
|
+
*
|
|
694
|
+
* `RelationBase` carried its own `validation.required` until 0.18, which made
|
|
695
|
+
* this two questions rather than one. They were answered by different readers:
|
|
696
|
+
* the Postgres DDL generator asked the property (so the foreign-key column was
|
|
697
|
+
* `NOT NULL`) and the SDK type generator asked the relation (so the generated
|
|
698
|
+
* `Insert` type made the field optional). A `create()` that left the relation
|
|
699
|
+
* out therefore typechecked and then failed at the database with a not-null
|
|
700
|
+
* violation, and the two `required`s had to be written twice, identically, for
|
|
701
|
+
* the pair to agree.
|
|
702
|
+
*
|
|
703
|
+
* A relation with no declaring property — an entry in `relations` nothing
|
|
704
|
+
* points at — is not required. There is no field to fill in.
|
|
705
|
+
*/
|
|
706
|
+
function isRelationRequired(collection, relation) {
|
|
707
|
+
return Boolean(relationDeclaringProperty(collection, relation)?.validation?.required);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
672
710
|
* The path of the collection a relation property points at, derived from the
|
|
673
711
|
* property alone.
|
|
674
712
|
*
|
|
@@ -695,9 +733,21 @@ function getRelationTargetPath(property) {
|
|
|
695
733
|
return;
|
|
696
734
|
}
|
|
697
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* The table a collection reads and writes.
|
|
738
|
+
*
|
|
739
|
+
* `table` when it is set, otherwise `toSnakeCase(slug)` — which is what made it
|
|
740
|
+
* safe to drop `table` from the required fields on the config type: the runtime
|
|
741
|
+
* had always derived it, and the type was demanding a value it did not need.
|
|
742
|
+
*
|
|
743
|
+
* The `||` chain is load-bearing. `toSnakeCase(undefined)` returns `""`, not
|
|
744
|
+
* `undefined`, so the previous `??` chain short-circuited on the empty string
|
|
745
|
+
* and the name fallback could never run — a safety net that read like one and
|
|
746
|
+
* caught nothing. It was unreachable while `slug` was required; it stops being
|
|
747
|
+
* unreachable the moment anything constructs a config without one.
|
|
748
|
+
*/
|
|
698
749
|
function getTableName(collection) {
|
|
699
|
-
|
|
700
|
-
return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
750
|
+
return (isRelationalCollectionConfig(collection) ? collection.table : void 0) || toSnakeCase(collection.slug) || toSnakeCase(collection.name);
|
|
701
751
|
}
|
|
702
752
|
/**
|
|
703
753
|
* A JavaScript identifier: what a generated `export const <name> =` needs.
|
|
@@ -1483,6 +1533,7 @@ function compile(expr, scope) {
|
|
|
1483
1533
|
case "rolesOverlap": return `string_to_array(${RLS_ROLES_SQL}, ',') && ${rolesArraySql(expr.roles)}`;
|
|
1484
1534
|
case "rolesContain": return `string_to_array(${RLS_ROLES_SQL}, ',') @> ${rolesArraySql(expr.roles)}`;
|
|
1485
1535
|
case "authenticated": return `${RLS_UID_SQL} IS NOT NULL AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(", ")})`;
|
|
1536
|
+
case "registered": return `${RLS_UID_SQL} IS NOT NULL AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(", ")}) AND NOT ${RLS_IS_ANONYMOUS_SQL}`;
|
|
1486
1537
|
case "serverContext": return `${RLS_UID_SQL} IS NULL`;
|
|
1487
1538
|
case "existsIn": return compileExistsIn(expr, scope);
|
|
1488
1539
|
case "raw": return rewriteLegacyRlsFunctions(expr.sql).replace(/\{(\w+)\}/g, (_, col) => `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);
|
|
@@ -1719,6 +1770,7 @@ function evaluatePolicy(expr, ctx) {
|
|
|
1719
1770
|
return expr.roles.every((r) => r === "public" || userRoles.includes(r));
|
|
1720
1771
|
}
|
|
1721
1772
|
case "authenticated": return ctx.uid != null && !isAnonymousUid(ctx.uid);
|
|
1773
|
+
case "registered": return ctx.uid != null && !isAnonymousUid(ctx.uid) && ctx.isAnonymous !== true;
|
|
1722
1774
|
case "serverContext": return false;
|
|
1723
1775
|
case "existsIn": return "unknown";
|
|
1724
1776
|
case "raw": return "unknown";
|
|
@@ -1895,11 +1947,13 @@ function canDeleteEntity(collection, authContext, path, entity) {
|
|
|
1895
1947
|
//#endregion
|
|
1896
1948
|
//#region src/util/builders.ts
|
|
1897
1949
|
/**
|
|
1898
|
-
*
|
|
1899
|
-
*
|
|
1950
|
+
* At runtime this is a plain identity function: a resource handle written where
|
|
1951
|
+
* a key belongs — `dataSource: analytics` — becomes its key, so past this point
|
|
1952
|
+
* a collection is plain data. The signature above is the rest of the point.
|
|
1953
|
+
* @group Builder
|
|
1900
1954
|
*/
|
|
1901
1955
|
function defineCollection(collection) {
|
|
1902
|
-
return collection;
|
|
1956
|
+
return resolveResourceRefs(collection);
|
|
1903
1957
|
}
|
|
1904
1958
|
//#endregion
|
|
1905
1959
|
//#region src/util/storage.ts
|
|
@@ -2055,6 +2109,107 @@ var buildPropertyCallbacks = (properties) => {
|
|
|
2055
2109
|
return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
|
|
2056
2110
|
};
|
|
2057
2111
|
//#endregion
|
|
2112
|
+
//#region src/util/callback-errors.ts
|
|
2113
|
+
/**
|
|
2114
|
+
* The code a write carries when a collection callback rejected it and did not
|
|
2115
|
+
* say how. Distinct from `INVALID_INPUT`, which the framework's own validation
|
|
2116
|
+
* raises: this one means *your* rule refused, so the message is the author's.
|
|
2117
|
+
*
|
|
2118
|
+
* `details.stage` names which callback refused — `beforeSave`, `beforeDelete`,
|
|
2119
|
+
* `afterSave` or `afterDelete`. An `after*` hook runs inside the write's
|
|
2120
|
+
* transaction, so a throw there rolls the row back too; the caller is told the
|
|
2121
|
+
* write did not happen and which hook decided that.
|
|
2122
|
+
*/
|
|
2123
|
+
var CALLBACK_REJECTED = "CALLBACK_REJECTED";
|
|
2124
|
+
/**
|
|
2125
|
+
* Turn whatever a user callback threw into something the API layer can answer
|
|
2126
|
+
* with.
|
|
2127
|
+
*
|
|
2128
|
+
* ### Why a plain `throw` has to mean 400
|
|
2129
|
+
*
|
|
2130
|
+
* Both `docs/collections/callbacks.md` ("Throw an error to **block the save**")
|
|
2131
|
+
* and `docs/backend/hooks.md` ("the operation is rejected with an HTTP 400
|
|
2132
|
+
* error response") promised this, and neither delivered it: an `Error` thrown
|
|
2133
|
+
* from `beforeSave` reached the client as
|
|
2134
|
+
*
|
|
2135
|
+
* 500 {"error":{"message":"Internal Server Error","code":"INTERNAL_ERROR"}}
|
|
2136
|
+
*
|
|
2137
|
+
* with the author's message visible only in the server log, because the error
|
|
2138
|
+
* normalizer masks 5xx bodies — correctly, since a 500 is by definition
|
|
2139
|
+
* something the caller must not be told about.
|
|
2140
|
+
*
|
|
2141
|
+
* But a callback is not the server failing. It is the application speaking, in
|
|
2142
|
+
* code its author wrote, about a request its author judged invalid. The
|
|
2143
|
+
* conservative reading — "an unrecognised throw might be a real bug, so 500" —
|
|
2144
|
+
* costs every validation rule its message and makes the documented example
|
|
2145
|
+
* wrong. A rule that wants a 500 can still raise one explicitly.
|
|
2146
|
+
*
|
|
2147
|
+
* ### Why `after*` comes through here too
|
|
2148
|
+
*
|
|
2149
|
+
* `afterSave` and `afterDelete` run inside the write's transaction and are
|
|
2150
|
+
* awaited, so a throw in one aborts the transaction: the row is not there when
|
|
2151
|
+
* the request ends. Left unconverted, the caller saw a 500 for a write that a
|
|
2152
|
+
* rule deliberately undid, and had no way to tell that from a database outage.
|
|
2153
|
+
* Converted, it is the same 400 `CALLBACK_REJECTED` a `before*` hook produces,
|
|
2154
|
+
* with `stage` naming the hook that refused.
|
|
2155
|
+
*
|
|
2156
|
+
* ### What passes through untouched
|
|
2157
|
+
*
|
|
2158
|
+
* Anything that already carries a status: `RebaseApiError` from
|
|
2159
|
+
* `@rebasepro/types` (the browser-safe class a `config/collections/*.ts` file
|
|
2160
|
+
* can import — the collection file is bundled into the admin SPA, so it may not
|
|
2161
|
+
* import the server package), and the server's own `ApiError`, recognised
|
|
2162
|
+
* structurally rather than by `instanceof` because a monorepo can resolve two
|
|
2163
|
+
* copies of a package and `instanceof` is false across them.
|
|
2164
|
+
*
|
|
2165
|
+
* @param error What the callback threw.
|
|
2166
|
+
* @param stage The callback name, for the log line.
|
|
2167
|
+
* @param path The collection path, for the log line.
|
|
2168
|
+
*/
|
|
2169
|
+
function toCallbackError(error, stage, path) {
|
|
2170
|
+
if (error !== null && typeof error === "object") {
|
|
2171
|
+
const carried = error;
|
|
2172
|
+
if (typeof carried.statusCode === "number" || typeof carried.status === "number") return error;
|
|
2173
|
+
}
|
|
2174
|
+
return new RebaseApiError(error instanceof Error ? error.message : typeof error === "string" ? error : `${stage} rejected the write`, {
|
|
2175
|
+
status: 400,
|
|
2176
|
+
code: CALLBACK_REJECTED,
|
|
2177
|
+
details: {
|
|
2178
|
+
stage,
|
|
2179
|
+
path
|
|
2180
|
+
},
|
|
2181
|
+
cause: error
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* The refusal a callback expresses by returning `false` rather than throwing.
|
|
2186
|
+
*
|
|
2187
|
+
* `beforeDelete` is typed `boolean | void` and documented as "return false or
|
|
2188
|
+
* throw to block deletion". Returning `false` did stop the delete — and then the
|
|
2189
|
+
* route answered `204 No Content`, which says the row is gone. The admin panel
|
|
2190
|
+
* removed it from the list, a client that trusted the status dropped it from its
|
|
2191
|
+
* cache, and the next reload brought it back. A veto that reports success is
|
|
2192
|
+
* worse than no veto.
|
|
2193
|
+
*
|
|
2194
|
+
* 403, not the 400 a throw produces: a throw carries the author's message and
|
|
2195
|
+
* reads as "this input is wrong", while `false` is a flat refusal with no
|
|
2196
|
+
* explanation — the server understood the request and will not perform it. The
|
|
2197
|
+
* code is the same either way, so a client can handle both in one branch.
|
|
2198
|
+
*
|
|
2199
|
+
* @param stage The callback name, for `details.stage`.
|
|
2200
|
+
* @param path The collection path, for `details.path`.
|
|
2201
|
+
*/
|
|
2202
|
+
function callbackRefusal(stage, path) {
|
|
2203
|
+
return new RebaseApiError(`${stage} refused the operation`, {
|
|
2204
|
+
status: 403,
|
|
2205
|
+
code: CALLBACK_REJECTED,
|
|
2206
|
+
details: {
|
|
2207
|
+
stage,
|
|
2208
|
+
path
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
//#endregion
|
|
2058
2213
|
//#region src/util/auth-default-policies.ts
|
|
2059
2214
|
/**
|
|
2060
2215
|
* Default RLS policies injected by the schema generator.
|
|
@@ -3048,7 +3203,7 @@ var CollectionRegistry = class {
|
|
|
3048
3203
|
dataSources = {};
|
|
3049
3204
|
/**
|
|
3050
3205
|
* Global lifecycle callbacks applied to every collection.
|
|
3051
|
-
* Runs on all data paths (REST, WebSocket, `rebase.
|
|
3206
|
+
* Runs on all data paths (REST, WebSocket, `rebase.dataAsAdmin`).
|
|
3052
3207
|
* Execution order: global → collection → property callbacks.
|
|
3053
3208
|
*/
|
|
3054
3209
|
_globalCallbacks;
|
|
@@ -3186,7 +3341,7 @@ var CollectionRegistry = class {
|
|
|
3186
3341
|
else {
|
|
3187
3342
|
const declared = resolveCollectionRelations(collection)[key];
|
|
3188
3343
|
if (declared) relationProperty.resolvedRelation = declared;
|
|
3189
|
-
else console.warn(`Relation property '${key}' on '${collection.slug}'
|
|
3344
|
+
else console.warn(`Relation property '${key}' on '${collection.slug}' names no relation: it has no \`relation\` block, and the collection's \`relations\` array has no entry called '${key}'. The field will render no picker, generate no foreign key, and return nothing from \`include()\`.`);
|
|
3190
3345
|
}
|
|
3191
3346
|
}
|
|
3192
3347
|
return newProperty;
|
|
@@ -3430,7 +3585,7 @@ function normalizeOrderBy(orderBy) {
|
|
|
3430
3585
|
if (!orderBy || orderBy.length === 0) return void 0;
|
|
3431
3586
|
const list = Array.isArray(orderBy[0]) ? orderBy : [orderBy];
|
|
3432
3587
|
if (list.length === 0) return void 0;
|
|
3433
|
-
return list.map((
|
|
3588
|
+
return list.map((entry, index) => toStrictTuple(entry, index));
|
|
3434
3589
|
}
|
|
3435
3590
|
/**
|
|
3436
3591
|
* The most significant sort key, for a caller that can only express one —
|
|
@@ -3533,23 +3688,37 @@ function serializeOrderBy(orderBy) {
|
|
|
3533
3688
|
/**
|
|
3534
3689
|
* Deserialize a wire-format `"field:direction"` string into an {@link OrderByTuple}.
|
|
3535
3690
|
*
|
|
3536
|
-
* Lenient parsing
|
|
3691
|
+
* Lenient parsing:
|
|
3537
3692
|
* - Bare field name (no colon): `"name"` → `["name", "asc"]`
|
|
3538
3693
|
* - Unknown direction: `"name:foo"` → `["name", "asc"]`
|
|
3539
|
-
* - Empty / falsy input: → `undefined`
|
|
3694
|
+
* - Empty / falsy input, or a blank field name: → `undefined`
|
|
3695
|
+
*
|
|
3696
|
+
* The leniency is this end's alone; the *server* refuses the same value. This
|
|
3697
|
+
* used to say "matches existing server behaviour", and it stopped being true
|
|
3698
|
+
* when `parseOrderByParam` grew a strict direction check: `?orderBy=name:foo`
|
|
3699
|
+
* now answers `400 INVALID_ORDER_BY` ("entry 0 has direction 'foo'"). The split
|
|
3700
|
+
* is deliberate — see {@link parseOrderBySpecStrict} — because a value this
|
|
3701
|
+
* function is handed was produced by {@link serializeOrderBy} a moment earlier,
|
|
3702
|
+
* and one that reaches the server came from a stranger.
|
|
3703
|
+
*
|
|
3704
|
+
* A blank field is `undefined` rather than `[" ", "asc"]`: whitespace is not a
|
|
3705
|
+
* field name, and the tuple it used to produce could not be re-encoded — the
|
|
3706
|
+
* only value in this codec that survived a decode and failed the next encode.
|
|
3540
3707
|
*
|
|
3541
3708
|
* Reads the single-key shorthand only. For a value that may carry several keys,
|
|
3542
3709
|
* use {@link deserializeOrderByList} — handed a JSON array this returns the
|
|
3543
3710
|
* whole array as one nonsensical field name.
|
|
3544
3711
|
*
|
|
3545
3712
|
* @param raw - The wire-format string from an HTTP query parameter.
|
|
3546
|
-
* @returns The canonical tuple, or `undefined` if the input
|
|
3713
|
+
* @returns The canonical tuple, or `undefined` if the input names no field.
|
|
3547
3714
|
*/
|
|
3548
3715
|
function deserializeOrderBy(raw) {
|
|
3549
3716
|
if (!raw) return void 0;
|
|
3550
3717
|
const idx = raw.indexOf(":");
|
|
3551
|
-
if (idx === -1) return [raw, "asc"];
|
|
3552
|
-
|
|
3718
|
+
if (idx === -1) return raw.trim() === "" ? void 0 : [raw, "asc"];
|
|
3719
|
+
const field = raw.slice(0, idx);
|
|
3720
|
+
if (field.trim() === "") return void 0;
|
|
3721
|
+
return [field, raw.slice(idx + 1) === "desc" ? "desc" : "asc"];
|
|
3553
3722
|
}
|
|
3554
3723
|
/**
|
|
3555
3724
|
* Deserialize either wire spelling — the single-key shorthand or the JSON
|
|
@@ -3607,7 +3776,11 @@ var QueryBuilder = class {
|
|
|
3607
3776
|
}
|
|
3608
3777
|
where(columnOrCondition, operator, value) {
|
|
3609
3778
|
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
3610
|
-
|
|
3779
|
+
const next = columnOrCondition;
|
|
3780
|
+
this.params.logical = this.params.logical ? {
|
|
3781
|
+
type: "and",
|
|
3782
|
+
conditions: [this.params.logical, next]
|
|
3783
|
+
} : next;
|
|
3611
3784
|
return this;
|
|
3612
3785
|
}
|
|
3613
3786
|
if (!this.params.where) this.params.where = {};
|
|
@@ -4219,6 +4392,36 @@ function readTuple(field, raw) {
|
|
|
4219
4392
|
if (isOperatorShaped(op)) throw new UnknownFilterOperatorError(field, op);
|
|
4220
4393
|
}
|
|
4221
4394
|
/**
|
|
4395
|
+
* Encode the `<op>.<value>` half of a wire condition.
|
|
4396
|
+
*
|
|
4397
|
+
* This is the single leaf encoder. Both wire positions that carry a condition
|
|
4398
|
+
* — a top-level query parameter (`?status=eq.active`) and a leaf inside an
|
|
4399
|
+
* `and(...)`/`or(...)` group (`or(status.eq.active,…)`) — go through it, so a
|
|
4400
|
+
* rule expressed here holds in both. The group serializer used to carry its
|
|
4401
|
+
* own copy, and the copy had drifted on every rule that matters: `null` went
|
|
4402
|
+
* out as the four-character string, the empty list as `()`, and an operator
|
|
4403
|
+
* this dialect does not have was silently rewritten to `eq` — a filter that
|
|
4404
|
+
* ran, returned rows, and answered a different question than the one asked.
|
|
4405
|
+
*
|
|
4406
|
+
* `escapeScalar` is the one thing the two positions legitimately disagree
|
|
4407
|
+
* about. A scalar in a query parameter owns the whole value and needs no
|
|
4408
|
+
* escaping; a scalar inside a group sits between the same commas a list item
|
|
4409
|
+
* does, so a comma in it would end the condition early.
|
|
4410
|
+
*/
|
|
4411
|
+
function serializeOperatorAndValue(op, value, { escapeScalar, where }) {
|
|
4412
|
+
if (typeof op !== "string") throw new TypeError(`${where}: operator must be a string, got ${typeof op}`);
|
|
4413
|
+
const restOp = CANONICAL_OP_LOOKUP.get(op);
|
|
4414
|
+
if (!restOp) throw new TypeError(`${where}: unknown operator "${op}". Valid operators: ${Object.keys(CANONICAL_TO_REST).join(", ")}`);
|
|
4415
|
+
if (value === null && (op === "==" || op === "!=")) return op === "==" ? "isnull.null" : "notnull.null";
|
|
4416
|
+
if (NULL_OPS.has(op)) return `${restOp}.null`;
|
|
4417
|
+
if (Array.isArray(value)) {
|
|
4418
|
+
if (value.length === 0) return `${restOp}.(${EMPTY_LIST_TOKEN})`;
|
|
4419
|
+
return `${restOp}.(${value.map((v) => escapeWireValue(stringifyValue(v))).join(",")})`;
|
|
4420
|
+
}
|
|
4421
|
+
const scalar = stringifyValue(value);
|
|
4422
|
+
return `${restOp}.${escapeScalar ? escapeWireValue(scalar) : scalar}`;
|
|
4423
|
+
}
|
|
4424
|
+
/**
|
|
4222
4425
|
* Serialize a single canonical condition tuple to a PostgREST dot-string.
|
|
4223
4426
|
*
|
|
4224
4427
|
* Throws `TypeError` if the input is not a valid `[WhereFilterOp, unknown]` tuple.
|
|
@@ -4231,15 +4434,10 @@ function readTuple(field, raw) {
|
|
|
4231
4434
|
function serializeTuple(tuple) {
|
|
4232
4435
|
if (!Array.isArray(tuple) || tuple.length !== 2) throw new TypeError(`serializeTuple: expected a [WhereFilterOp, value] tuple, got ${JSON.stringify(tuple)}`);
|
|
4233
4436
|
const [op, value] = tuple;
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
if (Array.isArray(value)) {
|
|
4239
|
-
if (value.length === 0) return `${restOp}.(${EMPTY_LIST_TOKEN})`;
|
|
4240
|
-
return `${restOp}.(${value.map((v) => escapeWireValue(stringifyValue(v))).join(",")})`;
|
|
4241
|
-
}
|
|
4242
|
-
return `${restOp}.${stringifyValue(value)}`;
|
|
4437
|
+
return serializeOperatorAndValue(op, value, {
|
|
4438
|
+
escapeScalar: false,
|
|
4439
|
+
where: "serializeTuple"
|
|
4440
|
+
});
|
|
4243
4441
|
}
|
|
4244
4442
|
/**
|
|
4245
4443
|
* Convert `FilterValues` (or `WireFilterValues`) to a PostgREST-style
|
|
@@ -4344,10 +4542,21 @@ function deserializeFilter(query) {
|
|
|
4344
4542
|
/**
|
|
4345
4543
|
* Serialize a `LogicalCondition` or `FilterCondition` to its wire-format string.
|
|
4346
4544
|
*
|
|
4545
|
+
* Leaf encoding is {@link serializeOperatorAndValue}, the same function
|
|
4546
|
+
* `serializeTuple` uses, so `null`, the empty list and an unknown operator
|
|
4547
|
+
* behave identically inside a group and in a query parameter.
|
|
4548
|
+
*
|
|
4549
|
+
* @throws {TypeError} when a leaf names an operator this dialect does not have.
|
|
4550
|
+
* It used to fall back to `eq`, which turned `age >= 18` into `age = 18` with
|
|
4551
|
+
* no diagnostic anywhere.
|
|
4552
|
+
*
|
|
4347
4553
|
* @example
|
|
4348
4554
|
* serializeLogicalCondition({ column: "status", operator: "==", value: "active" })
|
|
4349
4555
|
* // → "status.eq.active"
|
|
4350
4556
|
*
|
|
4557
|
+
* serializeLogicalCondition({ column: "deleted_at", operator: "==", value: null })
|
|
4558
|
+
* // → "deleted_at.isnull.null"
|
|
4559
|
+
*
|
|
4351
4560
|
* serializeLogicalCondition({ type: "or", conditions: [...] })
|
|
4352
4561
|
* // → "or(status.eq.active,status.eq.pending)"
|
|
4353
4562
|
*/
|
|
@@ -4356,12 +4565,60 @@ function serializeLogicalCondition(cond) {
|
|
|
4356
4565
|
const inner = (cond.conditions ?? []).map(serializeLogicalCondition).join(",");
|
|
4357
4566
|
return `${cond.type}(${inner})`;
|
|
4358
4567
|
}
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4568
|
+
return `${escapeWireValue(cond.column)}.${serializeOperatorAndValue(cond.operator, cond.value, {
|
|
4569
|
+
escapeScalar: true,
|
|
4570
|
+
where: "serializeLogicalCondition"
|
|
4571
|
+
})}`;
|
|
4572
|
+
}
|
|
4573
|
+
/**
|
|
4574
|
+
* Split a leaf condition into `column`, operator token and value.
|
|
4575
|
+
*
|
|
4576
|
+
* The naive reading — column is everything before the first dot, operator is
|
|
4577
|
+
* everything up to the second — cannot express a relation path. A filter on
|
|
4578
|
+
* `author.name` serializes to `author.name.eq.bob` and came back as the column
|
|
4579
|
+
* `author` with the operator `name`, which resolves to nothing, so the
|
|
4580
|
+
* fallback made it `author == "eq.bob"`: a condition that runs and matches
|
|
4581
|
+
* nothing, on a column the caller never named.
|
|
4582
|
+
*
|
|
4583
|
+
* So the operator is found rather than assumed: it is the first dot-separated
|
|
4584
|
+
* segment after the column that resolves to a real operator. Everything before
|
|
4585
|
+
* it is the column, everything after is the value. `version.eq.1.2.3` reads as
|
|
4586
|
+
* `version == "1.2.3"` because the scan stops at the first match — the `eq` at
|
|
4587
|
+
* offset 1, not a later segment — and `metadata->>x.eq.5` never had dots in the
|
|
4588
|
+
* column to begin with.
|
|
4589
|
+
*
|
|
4590
|
+
* Returns `undefined` when no segment resolves — `status.active`, an equality
|
|
4591
|
+
* written without an operator, which the caller handles.
|
|
4592
|
+
*/
|
|
4593
|
+
function splitLeafCondition(str) {
|
|
4594
|
+
let limit = str.length;
|
|
4595
|
+
for (let i = 0; i < str.length; i++) {
|
|
4596
|
+
if (str[i] === "\\") {
|
|
4597
|
+
i++;
|
|
4598
|
+
continue;
|
|
4599
|
+
}
|
|
4600
|
+
if (str[i] === "(") {
|
|
4601
|
+
limit = i;
|
|
4602
|
+
break;
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
const dots = [];
|
|
4606
|
+
for (let i = 0; i < limit; i++) {
|
|
4607
|
+
if (str[i] === "\\") {
|
|
4608
|
+
i++;
|
|
4609
|
+
continue;
|
|
4610
|
+
}
|
|
4611
|
+
if (str[i] === ".") dots.push(i);
|
|
4612
|
+
}
|
|
4613
|
+
for (let i = 1; i < dots.length; i++) {
|
|
4614
|
+
const operator = toCanonicalOp(str.substring(dots[i - 1] + 1, dots[i]));
|
|
4615
|
+
if (!operator) continue;
|
|
4616
|
+
return {
|
|
4617
|
+
column: unescapeWireValue(str.substring(0, dots[i - 1])),
|
|
4618
|
+
operator,
|
|
4619
|
+
value: str.substring(dots[i] + 1)
|
|
4620
|
+
};
|
|
4363
4621
|
}
|
|
4364
|
-
return `${cond.column}.${restOp}.${escapeWireValue(stringifyValue(cond.value))}`;
|
|
4365
4622
|
}
|
|
4366
4623
|
/**
|
|
4367
4624
|
* Parse a logical condition wire-format string back into a
|
|
@@ -4399,28 +4656,34 @@ function deserializeLogicalCondition(str, nesting = 0) {
|
|
|
4399
4656
|
conditions: splitGroupItems(innerStr).map((part) => deserializeLogicalCondition(part, nesting + 1))
|
|
4400
4657
|
};
|
|
4401
4658
|
}
|
|
4402
|
-
const
|
|
4403
|
-
if (
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
}
|
|
4416
|
-
const
|
|
4417
|
-
|
|
4418
|
-
const operator = toCanonicalOp(opStr) ?? "==";
|
|
4419
|
-
if (valueStr.startsWith("(") && valueStr.endsWith(")")) return {
|
|
4659
|
+
const leaf = splitLeafCondition(str);
|
|
4660
|
+
if (!leaf) {
|
|
4661
|
+
const firstDot = str.indexOf(".");
|
|
4662
|
+
if (firstDot === -1) return {
|
|
4663
|
+
column: unescapeWireValue(str),
|
|
4664
|
+
operator: "==",
|
|
4665
|
+
value: true
|
|
4666
|
+
};
|
|
4667
|
+
return {
|
|
4668
|
+
column: unescapeWireValue(str.substring(0, firstDot)),
|
|
4669
|
+
operator: "==",
|
|
4670
|
+
value: unescapeWireValue(str.substring(firstDot + 1))
|
|
4671
|
+
};
|
|
4672
|
+
}
|
|
4673
|
+
const { column, operator, value: valueStr } = leaf;
|
|
4674
|
+
if (NULL_OPS.has(operator)) return {
|
|
4420
4675
|
column,
|
|
4421
4676
|
operator,
|
|
4422
|
-
value:
|
|
4677
|
+
value: null
|
|
4423
4678
|
};
|
|
4679
|
+
if (valueStr.startsWith("(") && valueStr.endsWith(")")) {
|
|
4680
|
+
const inner = valueStr.slice(1, -1);
|
|
4681
|
+
return {
|
|
4682
|
+
column,
|
|
4683
|
+
operator,
|
|
4684
|
+
value: inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner)
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4424
4687
|
return {
|
|
4425
4688
|
column,
|
|
4426
4689
|
operator,
|
|
@@ -4429,6 +4692,15 @@ function deserializeLogicalCondition(str, nesting = 0) {
|
|
|
4429
4692
|
}
|
|
4430
4693
|
//#endregion
|
|
4431
4694
|
//#region src/data/buildRebaseData.ts
|
|
4695
|
+
/**
|
|
4696
|
+
* What a client says when its data source cannot subscribe.
|
|
4697
|
+
*
|
|
4698
|
+
* Named rather than inlined so the sentence a caller sees does not depend on
|
|
4699
|
+
* which of the two adapters below happened to build the client.
|
|
4700
|
+
*/
|
|
4701
|
+
var noRealtime = (slug) => `Realtime is not available for "${slug}": its data source does not support subscriptions.`;
|
|
4702
|
+
/** What a client says when its data source cannot count. */
|
|
4703
|
+
var noCount = (slug) => `Counting is not available for "${slug}": its data source does not support it.`;
|
|
4432
4704
|
function createPrimaryKeyResolver(options) {
|
|
4433
4705
|
const cache = /* @__PURE__ */ new Map();
|
|
4434
4706
|
const warned = /* @__PURE__ */ new Set();
|
|
@@ -4723,7 +4995,11 @@ var SdkQueryBuilder = class {
|
|
|
4723
4995
|
}
|
|
4724
4996
|
where(columnOrCondition, operator, value) {
|
|
4725
4997
|
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
4726
|
-
|
|
4998
|
+
const next = columnOrCondition;
|
|
4999
|
+
this.params.logical = this.params.logical ? {
|
|
5000
|
+
type: "and",
|
|
5001
|
+
conditions: [this.params.logical, next]
|
|
5002
|
+
} : next;
|
|
4727
5003
|
return this;
|
|
4728
5004
|
}
|
|
4729
5005
|
if (!this.params.where) this.params.where = {};
|
|
@@ -4743,7 +5019,7 @@ var SdkQueryBuilder = class {
|
|
|
4743
5019
|
/** Called again, this adds a tie-breaker rather than replacing the sort. */
|
|
4744
5020
|
orderBy(column, direction = "asc") {
|
|
4745
5021
|
const existing = normalizeOrderBy(this.params.orderBy) ?? [];
|
|
4746
|
-
this.params.orderBy = [...existing, [column, direction]];
|
|
5022
|
+
this.params.orderBy = [...existing, [sortKeyToString(column), direction]];
|
|
4747
5023
|
return this;
|
|
4748
5024
|
}
|
|
4749
5025
|
limit(count) {
|
|
@@ -4775,11 +5051,39 @@ var SdkQueryBuilder = class {
|
|
|
4775
5051
|
async find() {
|
|
4776
5052
|
return this.client.find(this.params);
|
|
4777
5053
|
}
|
|
5054
|
+
/**
|
|
5055
|
+
* Page through everything this query matches, one row at a time.
|
|
5056
|
+
*
|
|
5057
|
+
* `.limit()` on the builder becomes the page size, so the ceiling on a
|
|
5058
|
+
* single `find()` is not a ceiling on what the query can read.
|
|
5059
|
+
*/
|
|
5060
|
+
iterate(options) {
|
|
5061
|
+
return this.client.iterate({
|
|
5062
|
+
...this.params,
|
|
5063
|
+
...this.params.limit !== void 0 && { pageSize: this.params.limit },
|
|
5064
|
+
...options
|
|
5065
|
+
});
|
|
5066
|
+
}
|
|
5067
|
+
/** Collect everything this query matches into one array. */
|
|
5068
|
+
findAll(options) {
|
|
5069
|
+
return this.client.findAll({
|
|
5070
|
+
...this.params,
|
|
5071
|
+
...this.params.limit !== void 0 && { pageSize: this.params.limit },
|
|
5072
|
+
...options
|
|
5073
|
+
});
|
|
5074
|
+
}
|
|
5075
|
+
/**
|
|
5076
|
+
* Count the records matching this query.
|
|
5077
|
+
*
|
|
5078
|
+
* This used to answer `0` when the client had no `count` — a number, from a
|
|
5079
|
+
* source that had not counted anything, indistinguishable from an empty
|
|
5080
|
+
* collection. It now does what the client does, which on a source that
|
|
5081
|
+
* cannot count is throw and say so.
|
|
5082
|
+
*/
|
|
4778
5083
|
async count() {
|
|
4779
|
-
return this.client.count
|
|
5084
|
+
return this.client.count(this.params);
|
|
4780
5085
|
}
|
|
4781
5086
|
listen(onUpdate, onError) {
|
|
4782
|
-
if (!this.client.listen) throw new Error("Listen is only available when the driver supports realtime.");
|
|
4783
5087
|
return this.client.listen(this.params, onUpdate, onError);
|
|
4784
5088
|
}
|
|
4785
5089
|
};
|
|
@@ -4807,6 +5111,14 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
4807
5111
|
const s = await snap.findById(id);
|
|
4808
5112
|
return s ? entityToRow(s) : void 0;
|
|
4809
5113
|
},
|
|
5114
|
+
async get(id) {
|
|
5115
|
+
const s = await snap.findById(id);
|
|
5116
|
+
if (!s) throw new RebaseApiError(`No record with id ${JSON.stringify(String(id))} in "${slug}".`, {
|
|
5117
|
+
status: 404,
|
|
5118
|
+
code: "NOT_FOUND"
|
|
5119
|
+
});
|
|
5120
|
+
return entityToRow(s);
|
|
5121
|
+
},
|
|
4810
5122
|
async create(data, id) {
|
|
4811
5123
|
return entityToRow(await snap.create(data, id));
|
|
4812
5124
|
},
|
|
@@ -4837,12 +5149,12 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
4837
5149
|
if (!snap.deleteMany) throw new Error("Bulk deletes are not supported by this collection's data source. Fall back to delete() per record.");
|
|
4838
5150
|
await snap.deleteMany(ids);
|
|
4839
5151
|
},
|
|
4840
|
-
count: snap.count ? (params) => snap.count(params) :
|
|
5152
|
+
count: snap.count ? (params) => snap.count(params) : unsupportedMethod(noCount(slug)),
|
|
4841
5153
|
listen: snap.listen ? (params, onUpdate, onError) => snap.listen(params, (res) => onUpdate({
|
|
4842
5154
|
data: res.data.map(entityToRow),
|
|
4843
5155
|
meta: res.meta
|
|
4844
|
-
}), onError) :
|
|
4845
|
-
listenById: snap.listenById ? (id, onUpdate, onError) => snap.listenById(id, (s) => onUpdate(s ? entityToRow(s) : void 0), onError) :
|
|
5156
|
+
}), onError) : unsupportedMethod(noRealtime(slug)),
|
|
5157
|
+
listenById: snap.listenById ? (id, onUpdate, onError) => snap.listenById(id, (s) => onUpdate(s ? entityToRow(s) : void 0), onError) : unsupportedMethod(noRealtime(slug)),
|
|
4846
5158
|
where(columnOrCondition, operator, value) {
|
|
4847
5159
|
const builder = new SdkQueryBuilder(client);
|
|
4848
5160
|
if (typeof columnOrCondition === "object") return builder.where(columnOrCondition);
|
|
@@ -4889,12 +5201,12 @@ function toEntityAccessor(sdk, slug, getPks = () => []) {
|
|
|
4889
5201
|
delete(id) {
|
|
4890
5202
|
return sdk.delete(id);
|
|
4891
5203
|
},
|
|
4892
|
-
count: sdk.count ? (params) => sdk.count(params)
|
|
4893
|
-
listen: sdk.listen ? (params, onUpdate, onError) => sdk.listen(params, (res) => onUpdate({
|
|
5204
|
+
count: isUnsupported(sdk.count) ? void 0 : (params) => sdk.count(params),
|
|
5205
|
+
listen: isUnsupported(sdk.listen) ? void 0 : (params, onUpdate, onError) => sdk.listen(params, (res) => onUpdate({
|
|
4894
5206
|
data: res.data.map((row) => rowToEntity(row, slug, getPks())),
|
|
4895
5207
|
meta: res.meta
|
|
4896
|
-
}), onError)
|
|
4897
|
-
listenById: sdk.listenById ? (id, onUpdate, onError) => sdk.listenById(id, (row) => onUpdate(row ? rowToEntity(row, slug, getPks()) : void 0), onError)
|
|
5208
|
+
}), onError),
|
|
5209
|
+
listenById: isUnsupported(sdk.listenById) ? void 0 : (id, onUpdate, onError) => sdk.listenById(id, (row) => onUpdate(row ? rowToEntity(row, slug, getPks()) : void 0), onError),
|
|
4898
5210
|
where(columnOrCondition, operator, value) {
|
|
4899
5211
|
const builder = new QueryBuilder(accessor);
|
|
4900
5212
|
if (typeof columnOrCondition === "object") return builder.where(columnOrCondition);
|
|
@@ -4974,7 +5286,7 @@ function wrapAsSdkData(entityData) {
|
|
|
4974
5286
|
* Build a flat {@link RebaseSdkData} from a `DataDriver`.
|
|
4975
5287
|
*
|
|
4976
5288
|
* This is the developer-facing SDK data layer used by backend framework
|
|
4977
|
-
* callbacks & scripts (`context.data` / `rebase.
|
|
5289
|
+
* callbacks & scripts (`context.data` / `rebase.dataAsAdmin`). It returns flat rows —
|
|
4978
5290
|
* identical in shape to the frontend SDK client, down to how a relation is
|
|
4979
5291
|
* served: a foreign key stays a foreign key, and a relation named in `include`
|
|
4980
5292
|
* arrives as the target's own columns. The `{ __type: "relation" }` envelope is
|
|
@@ -5126,6 +5438,6 @@ async function detectJunctionTables(executeSql) {
|
|
|
5126
5438
|
return junctionTables;
|
|
5127
5439
|
}
|
|
5128
5440
|
//#endregion
|
|
5129
|
-
export { COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, DEFAULT_STRING_COLUMN_LENGTH, JUNCTION_TABLES_SQL, MAX_LOGICAL_NESTING_DEPTH, OrderBySpecError, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_TABLES, REBASE_USER_ROLE, RebasePaginationError, UnknownFilterOperatorError, and, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, defaultUsersCollection, defineCollection, deserializeFilter, deserializeLogicalCondition, deserializeOrderBy, deserializeOrderByList, detectJunctionTables, embedParentExpression, enumToObjectEntries, evaluateCondition, evaluatePolicy, fieldKeyForColumn, findAnonymousGrants, findRelation, fullPathToCollectionSegments, getArrayResolvedProperties, getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getGeneratedPolicyNames, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, getTableVarName, isAddressableId, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, isRelationalCollection, normalizeDriverOrderBy, normalizeEmail, normalizeOrderBy, normalizeToEntityRelation, or, paginateFind, parseIdValues, parseOrderBySpecStrict, policyToPostgres, primaryOrderBy, registerConditionOperations, relationalCollections, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveFindWindow, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, resolveStringColumnLength, revokeInternalTableAccess, revokeInternalTableSql, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeLogicalCondition, serializeOrderBy, sortCollectionsBySlug, sortProperties, sqlToPolicy, stripCollectionPath, toFilterTuples, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, wrapAsEntityData, wrapAsSdkData };
|
|
5441
|
+
export { CALLBACK_REJECTED, COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, DEFAULT_STRING_COLUMN_LENGTH, JUNCTION_TABLES_SQL, MAX_LOGICAL_NESTING_DEPTH, OrderBySpecError, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_TABLES, REBASE_USER_ROLE, RebasePaginationError, UnknownFilterOperatorError, and, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, callbackRefusal, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, defaultUsersCollection, defineCollection, deserializeFilter, deserializeLogicalCondition, deserializeOrderBy, deserializeOrderByList, detectJunctionTables, embedParentExpression, enumToObjectEntries, evaluateCondition, evaluatePolicy, fieldKeyForColumn, findAnonymousGrants, findRelation, fullPathToCollectionSegments, getArrayResolvedProperties, getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getGeneratedPolicyNames, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, getTableVarName, isAddressableId, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, isRelationRequired, isRelationalCollection, normalizeDriverOrderBy, normalizeEmail, normalizeOrderBy, normalizeToEntityRelation, or, paginateFind, parseIdValues, parseOrderBySpecStrict, policyToPostgres, primaryOrderBy, registerConditionOperations, relationDeclaringProperty, relationalCollections, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveFindWindow, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, resolveStringColumnLength, revokeInternalTableAccess, revokeInternalTableSql, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeLogicalCondition, serializeOrderBy, sortCollectionsBySlug, sortProperties, sqlToPolicy, stripCollectionPath, toCallbackError, toFilterTuples, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, wrapAsEntityData, wrapAsSdkData };
|
|
5130
5442
|
|
|
5131
5443
|
//# sourceMappingURL=index.es.js.map
|