@rebasepro/common 0.19.2-canary.gef769df → 0.20.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/dist/data/buildRebaseData.d.ts +2 -12
- package/dist/index.es.js +153 -30
- package/dist/index.es.js.map +1 -1
- package/dist/util/entities.d.ts +17 -0
- package/package.json +3 -3
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","names":[],"sources":["../src/util/common.ts","../src/util/entities.ts","../src/util/collections.ts","../src/util/identity.ts","../src/util/email.ts","../src/util/enums.ts","../src/util/paths.ts","../src/util/resolve-relation.ts","../src/util/relations.ts","../src/util/resolutions.ts","../src/util/policy/sqlToPolicy.ts","../src/util/policy/securityRuleToConditions.ts","../src/util/policy/policyToPostgres.ts","../src/util/policy/evaluatePolicy.ts","../src/util/permissions.ts","../src/util/builders.ts","../src/util/storage.ts","../src/util/callbacks.ts","../src/util/callback-errors.ts","../src/util/tenant.ts","../src/util/auth-default-policies.ts","../src/util/junction-policies.ts","../src/util/conditions.ts","../src/util/pg-column-to-property.ts","../src/util/string-column-length.ts","../src/util/internal-tables.ts","../src/data/resolveDataSource.ts","../src/collections/CollectionRegistry.ts","../src/collections/default-collections.ts","../src/collections/field-access.ts","../src/data/cursor.ts","../src/data/sort-dialect.ts","../src/data/include-spec.ts","../src/data/query_builder.ts","../src/data/paginate.ts","../src/data/filter-dialect.ts","../src/data/buildRebaseData.ts","../src/data/buildRoutedRebaseData.ts","../src/data/filter-conditions.ts","../src/table-classification.ts"],"sourcesContent":["export const DEFAULT_ONE_OF_TYPE = \"type\"\nexport const DEFAULT_ONE_OF_VALUE = \"value\"\n","import {\n DataType,\n Entity,\n EntityReference,\n EntityRelation,\n EntityStatus,\n EntityValues,\n Properties,\n Property\n} from \"@rebasepro/types\";\nimport { DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE } from \"./common\";\nimport { mergeDeep } from \"@rebasepro/utils\";\n\nexport function isPropertyBuilder(property?: Property) {\n return typeof property?.dynamicProps === \"function\";\n}\n\nexport function getDefaultValuesFor<M extends Record<string, unknown>>(properties: Properties): Partial<EntityValues<M>> {\n if (!properties) return {};\n return Object.entries(properties)\n .map(([key, property]) => {\n if (!property) return {};\n const value = getDefaultValueFor(property);\n return value === undefined ? {} : { [key]: value };\n })\n .reduce((a, b) => ({ ...a,\n...b }), {}) as EntityValues<M>;\n}\n\nexport function getDefaultValueFor(property?: Property): unknown {\n if (!property) return undefined;\n if (isPropertyBuilder(property)) return undefined;\n // `defaultValue !== undefined`, not truthiness. The test used to be\n // `property.defaultValue || property.defaultValue === null`, which special-\n // cased exactly one falsy value and dropped the rest: `defaultValue: 0`\n // fell through to the per-type default and became `null`, `defaultValue: \"\"`\n // became `null`, and `defaultValue: false` survived only by coincidence\n // (the per-type default for a boolean is also `false`). A default of zero\n // is the most ordinary default a number column has.\n if (property.defaultValue !== undefined) {\n return property.defaultValue;\n } else if (property.type === \"map\" && property.properties) {\n const defaultValuesFor = getDefaultValuesFor(property.properties as Properties);\n if (Object.keys(defaultValuesFor).length === 0) return undefined;\n return defaultValuesFor;\n } else {\n return getDefaultValueFortype(property.type);\n }\n}\n\nexport function getDefaultValueFortype(type: DataType): unknown {\n if (type === \"string\") {\n return null;\n } else if (type === \"number\") {\n return null;\n } else if (type === \"boolean\") {\n return false;\n } else if (type === \"date\") {\n return null;\n } else if (type === \"array\") {\n return [];\n } else if (type === \"map\") {\n return {};\n } else if (type === \"vector\") {\n return null;\n } else if (type === \"binary\") {\n return null;\n } else {\n return null;\n }\n}\n\n/**\n * Update the automatic values in a entity before save\n * @group Driver\n */\nexport function updateDateAutoValues<M extends Record<string, unknown>>({\n inputValues,\n properties,\n status,\n timestampNowValue\n}:\n {\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n status: EntityStatus,\n timestampNowValue: unknown\n }): EntityValues<M> {\n return traverseValuesProperties(\n inputValues,\n properties,\n (inputValue, property) => {\n if (property.type === \"date\") {\n if (status === \"existing\" && property.autoValue === \"on_update\") {\n return timestampNowValue;\n } else if ((status === \"new\" || status === \"copy\") &&\n (property.autoValue === \"on_update\" || property.autoValue === \"on_create\")) {\n return timestampNowValue;\n } else {\n return inputValue;\n }\n } else {\n return inputValue;\n }\n }\n ) ?? {} as M;\n}\n\n/**\n * Stamp the acting user's uid into the `user_on_create` / `user_on_update`\n * columns a collection declares.\n *\n * A deliberate sibling of {@link updateDateAutoValues} rather than another\n * branch inside it. The two share a shape and nothing else: one takes an\n * instant the server generates and the other takes an identity the request\n * carries, so overloading the timestamp function would have meant threading a\n * second, unrelated argument through every one of its callers and letting a\n * `date` property and a `string` property compete for the same `autoValue`\n * union. Called side by side in the driver.\n *\n * The stamped value overwrites whatever arrived in the body. A caller who can\n * set `createdBy` is a caller who can attribute their write to somebody else,\n * which is the one thing an audit column must not allow.\n *\n * `uid` is `undefined` for an anonymous request, a service token or an\n * in-process write; the column is set to an explicit `null` there. Explicit\n * matters on an update: leaving the key absent would keep whatever uid the\n * column already held, so an anonymous edit would be recorded as the previous\n * editor's. Refusing that write outright is `required`'s job, not this\n * function's — see `assertWriteValuesValid`.\n *\n * Top-level properties only, deliberately, unlike {@link updateDateAutoValues}.\n * `traverseValuesProperties` cannot express \"set this key to null\" — a `null`\n * from its operation means \"leave the key out\" — and an audit column nested\n * inside a `map` is not a column at all, so there is nothing down there to\n * stamp.\n *\n * @group Driver\n */\nexport function updateUserAutoValues<M extends Record<string, unknown>>({\n inputValues,\n properties,\n status,\n uid\n}:\n {\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n status: EntityStatus,\n uid: string | undefined\n }): EntityValues<M> {\n const result = { ...(inputValues ?? {}) } as Record<string, unknown>;\n for (const [key, property] of Object.entries(properties ?? {})) {\n const prop = property as (Property & { autoValue?: string }) | undefined;\n if (!prop || prop.type !== \"string\") continue;\n const autoValue = prop.autoValue;\n if (autoValue !== \"user_on_create\" && autoValue !== \"user_on_update\") continue;\n // `user_on_create` says nothing about an update: the column holds the\n // creator's uid and this write is not rewriting it.\n if (status === \"existing\" && autoValue === \"user_on_create\") continue;\n // A copy is a new row and gets a new author, exactly as it gets a new\n // `created_on`.\n result[key] = uid ?? null;\n }\n return result as EntityValues<M>;\n}\n\n/**\n * Fill in the `defaultValue`s a create left unset.\n *\n * `defaultValue` was read by exactly one thing: the Studio's form, which uses it\n * to prefill inputs. Every other way into the same collection — the REST create,\n * the SDK, the socket, an import — stored whatever arrived and nothing where the\n * key was absent. So `active: { type: \"boolean\", defaultValue: true }` produced\n * rows with `active` unset through the API and `true` through the panel, from\n * one declaration that reads like a promise about the data.\n *\n * Only genuinely absent keys are filled. An explicit `null` is a caller saying\n * \"no value\", which is a different statement from not mentioning the field, and\n * overwriting it would make the default impossible to opt out of.\n *\n * `getDefaultValuesFor` also invents a per-type default for properties with no\n * `defaultValue` at all (`false` for a boolean, `[]` for an array, `null` for\n * the rest) — right for a form, which must render *something* in every input,\n * and wrong here, where an absent key must stay absent so the column's own\n * DEFAULT applies. Only declared defaults are taken.\n *\n * @param values the caller's payload\n * @param properties the collection's declared properties\n * @group Driver\n */\nexport function applyDefaultValuesOnCreate<M extends Record<string, unknown>>(\n values: Partial<EntityValues<M>> | undefined,\n properties: Properties\n): Partial<EntityValues<M>> {\n if (!properties) return values ?? {};\n const result = { ...(values ?? {}) } as Record<string, unknown>;\n const defaults = getDefaultValuesFor(properties) as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(properties)) {\n if (!property) continue;\n const declared = declaresDefault(property as Property);\n if (!declared) continue;\n if (result[key] !== undefined) {\n // A map whose own sub-properties carry defaults is filled in\n // field by field, so `{ notify: false }` keeps `notify` and still\n // gains the siblings it did not mention.\n if ((property as Property).type === \"map\" &&\n (property as Property & { defaultValue?: unknown }).defaultValue === undefined &&\n isPlainObject(result[key])) {\n result[key] = {\n ...(defaults[key] as Record<string, unknown> ?? {}),\n ...(result[key] as Record<string, unknown>)\n };\n }\n continue;\n }\n if (defaults[key] !== undefined) result[key] = defaults[key];\n }\n return result as Partial<EntityValues<M>>;\n}\n\n/** Does this property, or something nested under it, state a `defaultValue`? */\nfunction declaresDefault(property: Property): boolean {\n if (isPropertyBuilder(property)) return false;\n if (property.defaultValue !== undefined) return true;\n if (property.type === \"map\" && property.properties) {\n return Object.values(property.properties as Properties)\n .some(child => child && declaresDefault(child as Property));\n }\n return false;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Add missing required fields, expected in the collection, to the values of a entity\n * @param values\n * @param properties\n * @group Driver\n */\nexport function sanitizeData<M extends Record<string, unknown>>\n (\n values: EntityValues<M>,\n properties: Properties\n ) {\n const result = values as Record<string, unknown>;\n Object.entries(properties)\n .forEach(([key, property]) => {\n if (values && values[key] !== undefined) result[key] = values[key];\n else if ((property as Property).validation?.required) result[key] = null;\n });\n return result;\n}\n\nexport function getReferenceFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityReference {\n if (typeof entity.id !== \"string\")\n throw new Error(\"Only string IDs are supported in references\");\n return new EntityReference({\n id: entity.id,\n path: entity.path,\n driver: entity.driver,\n databaseId: entity.databaseId\n });\n}\n\nexport function getRelationFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityRelation {\n return new EntityRelation(entity.id, entity.path, entity as unknown as Record<string, unknown>);\n}\n\n/**\n * Normalize a value into a proper EntityRelation instance.\n * Handles EntityRelation class instances, and plain objects\n * with `__type === \"relation\"` or an `isEntityRelation()` method.\n *\n * When `propertyType` is `\"relation\"`, also accepts plain objects that\n * have `id` and `path` fields — these are relation-shaped objects from\n * edge cases in the data pipeline (REST fallback, stale cache, custom data source).\n *\n * When `targetPath` is given, also accepts a bare id. A relation column is a\n * foreign key, and the REST layer returns it as the scalar it is; only some\n * fetch paths hydrate it into an object. Which form a caller sees therefore\n * depends on how the row was loaded, and a caller that only accepted objects\n * reported half of its own data as a type error. The declared target is the\n * missing half: with it, an id is a relation that has not been fetched yet.\n *\n * Returns null if the value cannot be coerced.\n */\nexport function normalizeToEntityRelation(value: unknown, propertyType?: string, targetPath?: string): EntityRelation | null {\n if (value instanceof EntityRelation) return value;\n\n if (targetPath && (typeof value === \"string\" || typeof value === \"number\")) {\n // An empty string is an unset foreign key, not row \"\".\n if (value === \"\") return null;\n return new EntityRelation(value, targetPath);\n }\n\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n\n const obj = value as Record<string, unknown>;\n const isRelationLike =\n obj.__type === \"relation\" ||\n obj.__type === \"reference\" ||\n (typeof obj.isEntityRelation === \"function\" && (obj.isEntityRelation as () => boolean)()) ||\n (typeof obj.isEntityReference === \"function\" && (obj.isEntityReference as () => boolean)()) ||\n (propertyType === \"relation\" && typeof obj.id !== \"undefined\" && typeof obj.path === \"string\");\n\n if (!isRelationLike) return null;\n\n return new EntityRelation(\n obj.id as string | number,\n obj.path as string,\n obj.data as Record<string, unknown> | undefined\n );\n}\n\nexport function traverseValuesProperties<M extends Record<string, unknown>>(\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n operation: (value: unknown, property: Property) => unknown\n): EntityValues<M> | undefined {\n // Handle null/undefined inputValues - use empty object as base for mergeDeep\n const safeInputValues = inputValues ?? {};\n\n const updatedValues = Object.entries(properties)\n .map(([key, property]) => {\n const inputValue = safeInputValues && (safeInputValues)[key];\n const updatedValue = traverseValueProperty(inputValue, property as Property, operation);\n if (updatedValue === null) return null;\n if (updatedValue === undefined) return undefined;\n return ({ [key]: updatedValue });\n })\n .reduce((a, b) => ({ ...a,\n...b }), {}) as EntityValues<M>;\n // Use mergeDeep to preserve class instances like EntityReference, GeoPoint\n const result = mergeDeep(safeInputValues, updatedValues);\n if (!result || Object.keys(result).length === 0) return undefined;\n return result;\n}\n\nexport function traverseValueProperty(inputValue: unknown,\n property: Property,\n operation: (value: unknown, property: Property) => unknown): unknown {\n\n let value;\n if (property.type === \"map\" && property.properties) {\n value = traverseValuesProperties(inputValue as Partial<Record<string, unknown>>, property.properties, operation);\n } else if (property.type === \"array\") {\n const of = property.of;\n if (of && Array.isArray(inputValue) && !Array.isArray(of)) {\n value = inputValue.map((e) => traverseValueProperty(e, of, operation));\n } else if (of && Array.isArray(inputValue) && Array.isArray(of)) {\n value = inputValue.map((e, i) => {\n if (i < of.length)\n return traverseValueProperty(e, of[i], operation);\n return null\n }).filter(Boolean);\n } else if (property.oneOf && Array.isArray(inputValue)) {\n const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;\n const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE;\n value = inputValue.map((e) => {\n if (e === null) return null;\n if (typeof e !== \"object\") return e;\n const rec = e as Record<string, unknown>;\n const type = rec[typeField] as string;\n const childProperty = property.oneOf?.properties[type];\n if (!type || !childProperty) return e;\n return {\n [typeField]: type,\n [valueField]: traverseValueProperty(rec[valueField], childProperty, operation)\n };\n });\n } else {\n value = inputValue;\n }\n } else {\n value = operation(inputValue, property);\n }\n\n return value;\n}\n\n/**\n * Relation reference types used throughout the server layer.\n * These replace the 50+ manual `{ id, path, __type: \"relation\" }` constructions.\n */\nexport interface RelationRef {\n readonly id: string | number;\n readonly path: string;\n readonly __type: \"relation\";\n}\n\nexport interface RelationRefWithData extends RelationRef {\n readonly data: Entity;\n}\n\n/**\n * Create a lightweight relation stub for admin views.\n * Replaces inline `{ id, path, __type: \"relation\" }` object literals.\n */\nexport function createRelationRef(id: string | number, path: string): RelationRef {\n return { id,\npath,\n__type: \"relation\" };\n}\n\n/**\n * Create a hydrated relation reference that includes the full entity data.\n * Used when entity data has been pre-fetched (e.g., via batch loading or JOINs).\n */\nexport function createRelationRefWithData(id: string | number, path: string, data: Entity): RelationRefWithData {\n return { id,\npath,\n__type: \"relation\",\ndata };\n}\n","import {\n CollectionConfig,\n Properties,\n Property\n} from \"@rebasepro/types\";\nimport { isPropertyBuilder } from \"./entities\";\n\nexport function sortProperties<M extends Record<string, unknown>>(properties: Properties, propertiesOrder?: string[]): Properties {\n try {\n const propertiesKeys = Object.keys(properties);\n // If no propertiesOrder, just use the original keys order\n if (!propertiesOrder || propertiesOrder.length === 0) {\n return propertiesKeys\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n }\n\n // Filter propertiesOrder to only include TOP-LEVEL property keys that exist\n // (ignore nested keys like \"data.mode\" - they are for column ordering, not property filtering)\n const validOrderKeys = (propertiesOrder as string[]).filter(key => {\n // Only include top-level keys (no dots) that exist in properties\n return !key.includes(\".\") && properties[key];\n });\n\n // Track which properties we've processed\n const processedKeys = new Set<string>(validOrderKeys);\n\n // Build result starting with ordered properties\n const orderedResult = validOrderKeys\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n\n // Append any properties that were NOT in propertiesOrder (so they don't disappear!)\n const missingProperties = propertiesKeys\n .filter(key => !processedKeys.has(key))\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n\n return { ...orderedResult,\n...missingProperties };\n } catch (e) {\n console.error(\"Error sorting properties\", e);\n return properties;\n }\n}\n\n/**\n * A copy of `collections` ordered by slug.\n *\n * Every generator that turns collections into a file is order-dependent, and\n * every one of them is compared against its own output — `rebase doctor`\n * regenerates in memory and diffs, `generate-sdk && git diff --exit-code` gates\n * CI. While only the *writers* sorted, a project whose `readdirSync` order\n * differed from its slug order was reported permanently out of date, and the\n * fix the message printed rewrote the file in the order it was already in. The\n * generators sort themselves now, so no caller can get this wrong.\n *\n * A slug-less collection is left to the generator's own validation, which names\n * the offending collection; sorting must not throw first.\n */\nexport function sortCollectionsBySlug<C extends { slug?: string }>(collections: readonly C[]): C[] {\n return [...collections].sort((a, b) => (a.slug ?? \"\").localeCompare(b.slug ?? \"\"));\n}\n\nexport function getPrimaryKeys<M extends Record<string, unknown>>(collection: CollectionConfig<M>): Extract<keyof M, string>[] {\n const properties = collection.properties;\n if (!properties) {\n return [\"id\"] as Extract<keyof M, string>[];\n }\n const ids = Object.entries(properties)\n .filter(([key, prop]) => typeof prop === \"object\" && prop !== null && \"isId\" in prop && Boolean(prop.isId))\n .map(([key]) => key);\n\n if (ids.length > 0) {\n return ids as Extract<keyof M, string>[];\n }\n return [\"id\"] as Extract<keyof M, string>[];\n}\n","/**\n * Row identity: the address of a row, and how to derive it.\n *\n * Postgres has no `id`. A row is identified by its primary key — one or more\n * columns, with any names and any types. `id` is something we synthesize on top\n * of that: a single string token, because the admin needs *one* value it can put\n * in a URL (`/products/1:::2`), use as a cache key, and hang a relation ref off.\n *\n * That token is an address, not data. It is derived from the row's columns and\n * never stored in them — a row is exactly its columns, with their real types.\n * Writing the address back into the row is what used to rename primary keys\n * (`sku` → `id`) and restringify them (`42` → `\"42\"`) on the way out.\n *\n * These live in `common` because both sides need them and must agree exactly:\n * the driver parses an incoming address back into key columns, and the admin\n * derives the address from a row it was served.\n */\n\n/**\n * A primary-key column: its name, the type it round-trips as, and whether it is\n * a UUID (which is a string despite sometimes being described as an id \"number\").\n */\nexport interface PrimaryKeyInfo {\n fieldName: string;\n type: \"string\" | \"number\";\n isUUID?: boolean;\n}\n\n/** Separator between the parts of a composite address. */\nexport const COMPOSITE_ID_SEPARATOR = \":::\";\n\n/** The eight-four-four-four-twelve shape of a UUID, any version. */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Whether one address part can be a value of the column it addresses. */\nfunction partIsAddressable(part: string | number, pk: PrimaryKeyInfo): boolean {\n if (pk.isUUID) return UUID_PATTERN.test(String(part));\n if (pk.type === \"number\") {\n return typeof part === \"number\"\n ? Number.isFinite(part)\n : !isNaN(parseInt(String(part), 10));\n }\n return true;\n}\n\n/**\n * Whether an address could name a row at all, before asking the database.\n *\n * A `uuid` column cannot hold `\"new\"`, and an `integer` column cannot hold\n * `\"abc\"` — so the answer to \"which row is this\" is \"none\", and that is a 404,\n * not a failure. Postgres cannot say so politely: the comparison never runs, it\n * raises `22P02` and aborts the enclosing transaction, after which every\n * further statement returns the far less helpful `25P02`.\n *\n * `isUUID` must come from the column, not from `isId: \"uuid\"` in a config: the\n * config is a claim about a key, and a `text` column that holds ids of some\n * other shape is a working app this must not start rejecting.\n */\nexport function isAddressableId(idValue: string | number, primaryKeys: PrimaryKeyInfo[]): boolean {\n if (primaryKeys.length === 0) return false;\n if (primaryKeys.length === 1) return partIsAddressable(idValue, primaryKeys[0]);\n\n const parts = String(idValue).split(COMPOSITE_ID_SEPARATOR);\n if (parts.length !== primaryKeys.length) return false;\n return parts.every((part, i) => partIsAddressable(part, primaryKeys[i]));\n}\n\n/**\n * Derive a row's address from its key columns.\n *\n * Single key → the value as a string. Composite → each part joined by\n * {@link COMPOSITE_ID_SEPARATOR}, in primary-key order, which is what\n * {@link parseIdValues} expects to invert.\n */\nexport function buildCompositeId(values: Record<string, unknown>, primaryKeys: PrimaryKeyInfo[]): string {\n if (primaryKeys.length === 0) {\n return \"\";\n }\n if (primaryKeys.length === 1) {\n return String(values[primaryKeys[0].fieldName] ?? \"\");\n }\n return primaryKeys.map(pk => String(values[pk.fieldName] ?? \"\")).join(COMPOSITE_ID_SEPARATOR);\n}\n\n/**\n * Invert {@link buildCompositeId}: turn an address back into key columns, each\n * coerced to the type its column actually round-trips as.\n *\n * This is the boundary where a URL segment becomes a query parameter, so a\n * malformed address must throw rather than silently produce a query that\n * matches the wrong row (or none).\n */\nexport function parseIdValues(idValue: string | number, primaryKeys: PrimaryKeyInfo[]): Record<string, string | number> {\n const result: Record<string, string | number> = {};\n\n if (primaryKeys.length === 0) {\n return result;\n }\n\n if (primaryKeys.length === 1) {\n const pk = primaryKeys[0];\n if (pk.type === \"number\" && !pk.isUUID) {\n const parsed = typeof idValue === \"number\" ? idValue : parseInt(String(idValue), 10);\n if (isNaN(parsed)) {\n throw new Error(`Invalid numeric ID: ${idValue}`);\n }\n result[pk.fieldName] = parsed;\n } else {\n result[pk.fieldName] = String(idValue);\n }\n return result;\n }\n\n // Composite key\n const parts = String(idValue).split(COMPOSITE_ID_SEPARATOR);\n if (parts.length !== primaryKeys.length) {\n throw new Error(`Composite ID parts mismatch. Expected ${primaryKeys.length}, got ${parts.length} for ID: ${idValue}`);\n }\n\n for (let i = 0; i < primaryKeys.length; i++) {\n const pk = primaryKeys[i];\n const val = parts[i];\n if (pk.type === \"number\" && !pk.isUUID) {\n const parsed = parseInt(val, 10);\n if (isNaN(parsed)) {\n throw new Error(`Invalid numeric ID component: ${val}`);\n }\n result[pk.fieldName] = parsed;\n } else {\n result[pk.fieldName] = val;\n }\n }\n\n return result;\n}\n\n/**\n * The primary keys of a collection, as declared by its properties.\n *\n * This is the only tier both sides can read, because it is the only one written\n * in the config: the postgres driver can also infer keys from the Drizzle\n * schema, which the browser never sees and is never sent — the admin compiles\n * the collection files into its own bundle rather than being served them. A key\n * that lives only in the Drizzle schema is therefore invisible here, and the\n * server says so at boot (`warnOnKeysTheAdminCannotResolve`) naming the `isId`\n * to add.\n *\n * Returns an empty array when a collection declares none, which callers must\n * treat as \"not addressable\" rather than defaulting to `id`: guessing a key\n * that is not the real one produces confidently wrong addresses.\n */\nexport function getDeclaredPrimaryKeys(collection: {\n properties?: Record<string, unknown>;\n}): PrimaryKeyInfo[] {\n const properties = collection.properties;\n if (!properties) return [];\n\n const keys: PrimaryKeyInfo[] = [];\n for (const [fieldName, propRaw] of Object.entries(properties)) {\n const prop = propRaw as { type?: string; isId?: unknown } | undefined;\n if (!prop || typeof prop !== \"object\") continue;\n if (!(\"isId\" in prop) || !prop.isId) continue;\n keys.push({\n fieldName,\n type: prop.type === \"number\" ? \"number\" : \"string\",\n isUUID: prop.isId === \"uuid\"\n });\n }\n return keys;\n}\n\n/**\n * The keys to address a collection's rows with, resolved the way the driver\n * resolves them — minus the tier the browser cannot reach.\n *\n * The postgres driver tries, in order: properties marked `isId`; the primary\n * keys of the Drizzle schema; and finally a column literally named `id`. Only\n * the first and last are visible in a `CollectionConfig`, which is what both\n * sides share.\n *\n * So the two agree except on a collection that declares no `isId` and whose key\n * is known only to Drizzle. There, the driver reads the real key, and this\n * either resolves nothing (reported to the console by the caller) or — if the\n * table happens to have an unrelated `id` property — resolves `id`, which is\n * the wrong key and cannot be detected from here: the addresses look right and\n * route wrong. Only the config can settle it, so the server names both cases\n * at boot (`warnOnKeysTheAdminCannotResolve`) with the `isId` to add.\n */\nexport function resolvePrimaryKeys(collection: {\n properties?: Record<string, unknown>;\n}): PrimaryKeyInfo[] {\n const declared = getDeclaredPrimaryKeys(collection);\n if (declared.length > 0) return declared;\n\n const idProp = collection.properties?.id as { type?: string } | undefined;\n if (idProp && typeof idProp === \"object\") {\n return [{ fieldName: \"id\",\ntype: idProp.type === \"number\" ? \"number\" : \"string\" }];\n }\n\n return [];\n}\n","/**\n * Email normalization — one implementation, because the database enforces it.\n *\n * `ensureAuthTablesExist` puts a `UNIQUE INDEX ON users (lower(email))` on the\n * auth table. That index decides what \"the same address\" means, and it does not\n * trim: to Postgres, `' foo@bar.com'` and `'foo@bar.com'` are two addresses and\n * both may exist. So every write that reaches the column has to agree with\n * every read, exactly, or the two disagree in the one direction that matters —\n * a row that exists and cannot be found.\n *\n * That is not hypothetical. The lookup path trimmed and the admin create paths\n * did not, so a user created through `POST /api/data/users` or\n * `POST /api/auth/admin/users` with a stray space was stored untrimmed,\n * survived the unique index alongside the real address, and was unreachable by\n * login forever after. The HTTP auth routes were unaffected only because Zod's\n * `.email()` happens to reject surrounding whitespace — a guard on a different\n * layer, for a different reason, that the admin paths do not sit behind.\n *\n * It lives in `common` because `server`, `server-postgres` and `server-mongo`\n * all write this column and must agree exactly, and `common` is the only\n * package all three already depend on.\n */\n\n/**\n * Canonical form of an email address: trimmed, lower-cased.\n *\n * Non-strings pass through untouched, so this is safe to apply to a value out\n * of a partial update payload whose type is not known yet.\n */\nexport function normalizeEmail<T>(email: T): T | string {\n return typeof email === \"string\" ? email.trim().toLowerCase() : email;\n}\n","import { EnumValueConfig, EnumValues } from \"@rebasepro/types\";\n\nexport function enumToObjectEntries(enumValues: EnumValues): EnumValueConfig[] {\n if (Array.isArray(enumValues)) {\n return enumValues;\n } else {\n return Object.entries(enumValues).map(([id, value]) => {\n if (typeof value === \"string\") {\n return {\n id,\n label: value\n }\n } else {\n return {\n ...value,\n id\n }\n }\n });\n }\n}\n\nexport function getLabelOrConfigFrom(enumValues: EnumValueConfig[], key?: string | number): EnumValueConfig | undefined {\n if (key === null || key === undefined) return undefined;\n return enumValues.find((entry) => String(entry.id) === String(key));\n}\n","export const COLLECTION_PATH_SEPARATOR = \"::\";\n\n/**\n * Remove the entity ids from a given path\n * `products/B44RG6APH/locales` => `products::locales`\n * @param path\n */\nexport function stripCollectionPath(path: string): string {\n return segmentsToStrippedPath(fullPathToCollectionSegments(path));\n}\n\nexport function segmentsToStrippedPath(paths: string[]) {\n if (paths.length === 1)\n return paths[0];\n return paths.reduce((a, b) => `${a}${COLLECTION_PATH_SEPARATOR}${b}`);\n}\n\n/**\n * Extract the collection path routes\n * `products/B44RG6APH/locales` => [`products`, `locales`]\n * @param path\n */\nexport function fullPathToCollectionSegments(path: string): string[] {\n return path\n .split(\"/\")\n .filter((e, i) => i % 2 === 0);\n}\n","import {\n CollectionConfig,\n Relation,\n ResolvedRelation\n} from \"@rebasepro/types\";\nimport { generateForeignKeyName, toSnakeCase } from \"@rebasepro/utils\";\n\nimport { getTableName } from \"./relations\";\n\n/**\n * Fill in a relation's defaults.\n *\n * This replaces `sanitizeRelation`, which had to work out *which kind of link\n * you meant* from whichever optional fields happened to be set — 194 lines of\n * it, including a pass that inspected the target collection's own relations to\n * decide whether a `many`/`inverse` pair was a one-to-many or the far side of a\n * many-to-many, wrapped in a `try/catch` that fell through to the wrong answer\n * when it could not tell. Two consumers running that logic at different moments\n * could reach different conclusions about the same relation.\n *\n * With the kind declared there is nothing to work out. What remains is\n * defaulting — a table name, a column name — which is deterministic, depends\n * only on the relation and its two endpoints, and cannot fail. That is why this\n * function returns rather than throws, and why it needs no cache to be\n * consistent.\n */\nexport function resolveRelation(\n relation: Relation,\n sourceCollection: CollectionConfig,\n propertyKey?: string\n): ResolvedRelation {\n const target = relation.target;\n if (typeof target !== \"function\") {\n throw new Error(\n `Relation${relation.relationName ? ` '${relation.relationName}'` : \"\"} on ` +\n `'${sourceCollection.slug}' has no \\`target\\`. Give it a thunk: \\`target: () => otherCollection\\`.`\n );\n }\n\n const targetCollection = callTarget(relation, sourceCollection, propertyKey, target);\n\n // The name is the address: the `include` key, the admin tab, and the\n // segment of a nested path. Declared name wins, then the declaring\n // property's key, then the target's slug.\n const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);\n\n const shared: Pick<ResolvedRelation, \"relationName\" | \"target\" | \"targetSlug\" | \"onUpdate\" | \"onDelete\" | \"overrides\"> = {\n relationName,\n // Normalised, not the thunk as written. Resolution reads the target once\n // and every later consumer calls it again — the driver building a join,\n // the DDL and policy generators, the admin's relation fields — so\n // handing back the raw thunk would give all of them the module namespace\n // `callTarget` just looked past, and the fix would hold only for the\n // fields resolution happens to read here. Still lazy: same call at the\n // same moment, one unwrap on the way out.\n target: () => unwrapModuleNamespace(target()) as CollectionConfig,\n targetSlug: targetCollection.slug,\n onUpdate: relation.onUpdate,\n onDelete: relation.onDelete,\n overrides: relation.overrides\n // No `validation`. Whether the link is required is a fact about the\n // *property*, and copying it onto the resolved relation gave the\n // question two answers that were free to disagree. Ask\n // `isRelationRequired(collection, relation)`, which reads the one.\n };\n\n const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);\n\n switch (relation.kind) {\n case \"belongsTo\":\n return {\n ...shared,\n kind: \"belongsTo\",\n cardinality: \"one\",\n writable: true,\n shared: false,\n localKey: relation.localKey ?? generateForeignKeyName(relationName)\n };\n\n case \"hasOne\":\n return {\n ...shared,\n kind: \"hasOne\",\n cardinality: \"one\",\n writable: true,\n shared: false,\n foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),\n sourceKey: relation.sourceKey\n };\n\n case \"hasMany\":\n return {\n ...shared,\n kind: \"hasMany\",\n cardinality: \"many\",\n writable: true,\n shared: false,\n foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),\n // Not defaulted: the source's primary key needs the driver's\n // schema to resolve, which resolution does not have. `undefined`\n // means \"the primary key\" — see `ResolvedHasMany.sourceKey`.\n sourceKey: relation.sourceKey\n };\n\n case \"manyToMany\": {\n const sourceTable = getTableName(sourceCollection);\n const targetTable = getTableName(targetCollection);\n return {\n ...shared,\n kind: \"manyToMany\",\n cardinality: \"many\",\n writable: true,\n shared: true,\n through: {\n // Sorted so both sides of the same link derive the same\n // table without having to agree in advance.\n table: relation.through?.table ?? [sourceTable, targetTable].sort().join(\"_\"),\n sourceColumn: relation.through?.sourceColumn ?? generateForeignKeyName(sourceName),\n targetColumn: relation.through?.targetColumn ?? generateForeignKeyName(relationName),\n // `{}` rather than `undefined`, for the reason every other\n // field here is filled in: a consumer reads one shape and\n // does not have to decide what an absent payload means.\n properties: relation.through?.properties ?? {}\n }\n };\n }\n\n case \"via\":\n return {\n ...shared,\n kind: \"via\",\n cardinality: relation.cardinality,\n writable: false,\n // A join chain reaches rows that other parents reach too, and\n // Rebase does not know which hop, if any, is a link it owns.\n shared: true,\n joinPath: relation.joinPath\n };\n\n default: {\n // Exhaustive: a new kind is a compile error here, not a silent\n // fall-through to whatever shape happened to match first.\n const exhaustive: never = relation;\n throw new Error(`Unknown relation kind: ${JSON.stringify(exhaustive)}`);\n }\n }\n}\n\n/** How this relation is addressed in an error message, before it has a resolved name. */\nfunction describe(relation: Relation, sourceCollection: CollectionConfig, propertyKey?: string): string {\n const name = relation.relationName ?? propertyKey;\n return `Relation${name ? ` '${name}'` : \"\"} on '${sourceCollection.slug}'`;\n}\n\n/**\n * A module namespace, unwrapped to the collection it exports.\n *\n * A cycle transpiled to CommonJS does not hand the importing module the\n * *default export* — it hands it the module object, `{ __esModule: true,\n * default: … }`, captured before the exporting module finished evaluating. The\n * `default` slot fills in later, so by the time a lazy `target` thunk runs the\n * collection is sitting right there, one level down. Returning the namespace is\n * never a thing a thunk means to do, and there is exactly one reading of it.\n *\n * Only unwrapped when the inner value is itself a collection: a `default` that\n * is not one is a genuinely wrong thunk, and it should reach the error below\n * rather than be quietly swapped in.\n */\nfunction unwrapModuleNamespace(value: unknown): unknown {\n if (!value || typeof value !== \"object\") return value;\n if ((value as { slug?: unknown }).slug) return value;\n const inner = (value as { default?: unknown }).default;\n return inner && typeof inner === \"object\" && (inner as { slug?: unknown }).slug ? inner : value;\n}\n\n/**\n * Call the `target` thunk, and translate the ways an import cycle breaks it into\n * an error that names the cause — or, where the value is recoverable, into the\n * collection the thunk meant.\n *\n * The thunk exists to defer the reference until every module has finished\n * evaluating, and for a cycle that closes at import time it does. Two cycles\n * leave the binding permanently unusable:\n *\n * - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are\n * in the temporal dead zone, so reading one throws `ReferenceError: x is not\n * defined`. The stack points at the thunk — a one-line arrow function that is\n * obviously fine — and says nothing about the cycle that made it throw.\n * - **CJS interop, unresolved.** The half-initialised module object has no\n * `default` yet, the import resolves to `undefined`, and the thunk returns it\n * without complaint. That one used to surface here as \"did not resolve to a\n * collection\", which is true and unhelpful.\n *\n * Both mean the same thing, and the fix for both is the same: break the cycle,\n * or move the relation into the collection that does not close it.\n *\n * A third shape is *not* an error, and used to be reported as one. A loader that\n * transpiles ESM to CJS — jiti, which is what `rebase generate-sdk` and\n * `rebase build` load collections with — gives the module entered second in a\n * cycle a namespace object rather than the default export, and never replaces it\n * with a live binding. The thunk then returns `{ __esModule: true, default: … }`\n * holding the fully-initialised collection. Native ESM resolves the same thunk\n * to the collection directly, so this was a loader artefact reported as an\n * authoring mistake, and the advice it gave — make the target a lazy thunk — was\n * already satisfied by the code it was rejecting. Bidirectional relations make\n * these cycles unavoidable, and the lazy thunk is this framework's own answer to\n * them, so {@link unwrapModuleNamespace} takes the collection and moves on.\n */\nfunction callTarget(\n relation: Relation,\n sourceCollection: CollectionConfig,\n propertyKey: string | undefined,\n target: Relation[\"target\"]\n): ReturnType<Relation[\"target\"]> {\n let targetCollection: ReturnType<Relation[\"target\"]> | undefined;\n try {\n targetCollection = unwrapModuleNamespace(target()) as ReturnType<Relation[\"target\"]>;\n } catch (error) {\n // A ReferenceError from inside the thunk is a binding that was never\n // initialised — nothing else in a one-expression arrow can raise one.\n if (error instanceof ReferenceError) {\n throw new Error(\n `${describe(relation, sourceCollection, propertyKey)} targets a collection that is not ` +\n `initialized yet — almost always an import cycle between the two collection files. ` +\n `Break the cycle (move the shared piece into a third module, or import the target ` +\n `lazily) so the target's module finishes evaluating before the registry is built.`,\n { cause: error }\n );\n }\n throw error;\n }\n\n if (!targetCollection?.slug) {\n throw new Error(\n `${describe(relation, sourceCollection, propertyKey)} has a \\`target\\` that resolved to ` +\n `${targetCollection === undefined ? \"`undefined`\" : \"something that is not a collection\"}. ` +\n (targetCollection === undefined\n ? \"Under CommonJS interop an import cycle resolves the default import to `undefined`, \" +\n \"so check whether this collection and its target import each other. Otherwise the thunk \" +\n \"is returning the wrong value — it must return the collection itself, not a promise or a module.\"\n : typeof (targetCollection as { then?: unknown }).then === \"function\"\n ? \"The thunk returned a promise — `target: () => import(\\\"./other\\\")` is asynchronous. \" +\n \"Import the collection at the top of the file and return the binding: \" +\n \"`target: () => otherCollection`.\"\n : \"The thunk must return a collection config with a `slug`.\")\n );\n }\n\n return targetCollection;\n}\n","import { CollectionConfig, isRelationalCollectionConfig, Property, ResolvedRelation, RelationProperty } from \"@rebasepro/types\";\nimport { toSnakeCase, toWireKey } from \"@rebasepro/utils\";\n\nimport { resolveRelation } from \"./resolve-relation\";\n\n/**\n * Whether the target rows are shared with other parents — a many-to-many, or a\n * multi-hop `via` chain.\n *\n * Decides what a write \"through\" the relation may touch: a shared target\n * belongs to every parent that links it, so the parent owns the *link* and not\n * the row. The backend enforces that (an unlink rather than a delete) and the\n * admin renders it (remove-from-parent rather than delete).\n *\n * Now a field on the resolved relation rather than a re-derivation, so both\n * sides read the same answer instead of each computing one.\n */\nexport function isJunctionBackedRelation(relation: ResolvedRelation): boolean {\n return relation.shared;\n}\n\n/** WeakMap cache — same collection instance always yields the same relation map. */\nconst _resolvedRelationsCache = new WeakMap<CollectionConfig, Record<string, ResolvedRelation>>();\n\n/**\n * Every relation a collection declares, keyed by the name it is addressed by.\n *\n * A relation reaches the map from either of two places — the collection's\n * `relations` array, or a `relation` property that declares one inline — and is\n * keyed by its resolved `relationName`, which is what a nested path segment,\n * an `include` key and an admin tab all match against.\n *\n * Resolution no longer swallows failures. It used to wrap each relation in a\n * `try/catch` that dropped anything it could not work out, so a\n * mis-declared relation silently vanished instead of being reported; with the\n * kind declared, the only remaining failure is a `target` that does not resolve,\n * which is worth hearing about.\n */\nexport function resolveCollectionRelations(\n collection: CollectionConfig\n): Record<string, ResolvedRelation> {\n const cached = _resolvedRelationsCache.get(collection);\n if (cached) return cached;\n\n if (!isRelationalCollectionConfig(collection)) return {};\n\n const relations: Record<string, ResolvedRelation> = {};\n\n for (const relation of collection.relations ?? []) {\n const resolved = resolveRelation(relation, collection);\n relations[resolved.relationName] = resolved;\n }\n\n // A property declaring a relation inline is registered under the property\n // key as well: the fetch layer hydrates the result back onto that key, and\n // it is the name the admin addresses the field by.\n for (const [propertyKey, property] of Object.entries(collection.properties ?? {})) {\n if ((property as Property)?.type !== \"relation\") continue;\n const declared = (property as RelationProperty).relation;\n if (!declared || relations[propertyKey]) continue;\n\n relations[propertyKey] = resolveRelation(declared, collection, propertyKey);\n }\n\n _resolvedRelationsCache.set(collection, relations);\n return relations;\n}\n\n/**\n * The `type: \"relation\"` property that declares a link, or `undefined` for one\n * that only exists in the collection's `relations` array.\n *\n * Both declaration sites end up in {@link resolveCollectionRelations}, and only\n * one of them has a property to carry field-level facts — `name`, `admin`, and\n * the one this exists for, `validation.required`.\n */\nexport function relationDeclaringProperty(\n collection: CollectionConfig,\n relation: ResolvedRelation\n): RelationProperty | undefined {\n const resolved = resolveCollectionRelations(collection);\n for (const [key, raw] of Object.entries(collection.properties ?? {})) {\n const prop = raw as Property | undefined;\n if (prop?.type !== \"relation\") continue;\n // A relation declared inline is keyed by the property; one declared in\n // `relations` is keyed by its name, which the property addresses.\n if (resolved[key] === relation) return prop as RelationProperty;\n const addressed = (prop as RelationProperty).relation?.relationName;\n if (addressed && findRelation(resolved, addressed) === relation) return prop as RelationProperty;\n }\n return undefined;\n}\n\n/**\n * Must every row of this collection point at a target through this link?\n *\n * Read from the declaring property's `validation.required` — the same key every\n * other field uses, and the only place it lives.\n *\n * `RelationBase` carried its own `validation.required` until 0.18, which made\n * this two questions rather than one. They were answered by different readers:\n * the Postgres DDL generator asked the property (so the foreign-key column was\n * `NOT NULL`) and the SDK type generator asked the relation (so the generated\n * `Insert` type made the field optional). A `create()` that left the relation\n * out therefore typechecked and then failed at the database with a not-null\n * violation, and the two `required`s had to be written twice, identically, for\n * the pair to agree.\n *\n * A relation with no declaring property — an entry in `relations` nothing\n * points at — is not required. There is no field to fill in.\n */\nexport function isRelationRequired(collection: CollectionConfig, relation: ResolvedRelation): boolean {\n return Boolean(relationDeclaringProperty(collection, relation)?.validation?.required);\n}\n\n/**\n * The path of the collection a relation property points at, derived from the\n * property alone.\n *\n * A preview holds a property and a value and no collection, so it cannot call\n * `resolveRelationProperty`. It does not need to: both forms that carry a\n * target — the stamped `resolvedRelation` and the inline `relation` — name it\n * directly. Only the third form, a relation declared by name in the\n * collection's `relations` array, is out of reach, and that one has no target\n * to read without the collection anyway.\n *\n * This is what lets a preview render a relation column that arrived as a bare\n * foreign key: the id says *which* row, the declared target says *which\n * collection*, and `RelationPreview` fetches the rest. Without it a scalar id\n * is indistinguishable from a value of the wrong type.\n */\nexport function getRelationTargetPath(property: RelationProperty): string | undefined {\n const stamped = property.resolvedRelation?.targetSlug;\n if (stamped) return stamped;\n\n const target = property.relation?.target;\n if (typeof target !== \"function\") return undefined;\n try {\n return target()?.slug;\n } catch (_e) {\n // A thunk reaching into a module that has not finished initialising:\n // there is no target to name yet, and a preview is not worth throwing over.\n return undefined;\n }\n}\n\n/**\n * The table a collection reads and writes.\n *\n * `table` when it is set, otherwise `toSnakeCase(slug)` — which is what made it\n * safe to drop `table` from the required fields on the config type: the runtime\n * had always derived it, and the type was demanding a value it did not need.\n *\n * The `||` chain is load-bearing. `toSnakeCase(undefined)` returns `\"\"`, not\n * `undefined`, so the previous `??` chain short-circuited on the empty string\n * and the name fallback could never run — a safety net that read like one and\n * caught nothing. It was unreachable while `slug` was required; it stops being\n * unreachable the moment anything constructs a config without one.\n */\nexport function getTableName(collection: CollectionConfig): string {\n const declared = isRelationalCollectionConfig(collection) ? collection.table : undefined;\n return declared || toSnakeCase(collection.slug) || toSnakeCase(collection.name);\n}\n\n/**\n * A JavaScript identifier: what a generated `export const <name> =` needs.\n *\n * Deliberately the same shape the two schema generators already define\n * privately — this is the third place that needed it, and the first two guard\n * property keys and member accesses while nothing guarded the variable name\n * itself.\n */\nconst JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * The variable name a generated table is bound to.\n *\n * Camel-cases underscores, and then guarantees the result is a legal\n * identifier. It did only the first, so a table name that is legal in Postgres\n * and not in JavaScript produced a `schema.generated.ts` that does not parse:\n *\n * `2024_archive` → `export const 2024Archive = pgTable(…)`\n * \"An identifier or keyword cannot immediately follow\n * a numeric literal\"\n * `reporting.events` → `export const reporting.events = pgTable(…)`\n * \"',' expected\"\n *\n * That file is imported by the server, so the failure is not one broken\n * collection — `rebase build` and `db push` fail at tsc for the whole\n * directory. And it is reachable from a documented flow: `rebase init` against\n * a database holding a table called `2024_archive` writes a collection file\n * that parses and a schema file that does not.\n *\n * **A no-op for every name that already worked**, which is what makes changing\n * a derived name safe here: the only inputs whose output changes are the ones\n * that produced a syntax error, and nothing can be running against those.\n * Separators become camel case rather than disappearing, so `reporting.events`\n * and `reporting_events` do not collide into one variable.\n */\nexport function getTableVarName(tableName: string): string {\n const camel = tableName.replace(/_([a-z])/g, (_, char: string) => char.toUpperCase());\n if (JS_IDENTIFIER.test(camel)) return camel;\n\n const sanitised = camel\n // Any other separator gets the same treatment `_` did, so two tables\n // differing only by separator keep differing.\n .replace(/[^A-Za-z0-9_$]+([A-Za-z0-9])?/g, (_, char?: string) =>\n (char ? char.toUpperCase() : \"\"))\n // A leading digit is legal in Postgres and not in JavaScript. Prefixed\n // rather than stripped, so `2024_archive` and `archive` stay distinct.\n .replace(/^([0-9])/, \"t$1\");\n\n return JS_IDENTIFIER.test(sanitised) ? sanitised : `t${sanitised}`;\n}\n\nexport function getEnumVarName(tableName: string, propName: string): string {\n const tableVar = getTableVarName(tableName);\n const propVar = propName.charAt(0).toUpperCase() + propName.slice(1);\n return `${tableVar}${propVar}`;\n}\n\nexport function getColumnName(fullColumn: string): string {\n return fullColumn.includes(\".\") ? fullColumn.split(\".\").pop()! : fullColumn;\n}\n\n/**\n * The field key a database column is served and addressed under.\n *\n * A column has two names and they are not the same name. `author_id` is what\n * Postgres stores; `authorId` is the key on the JSON row, the key in the\n * generated Drizzle table, and the key a caller writes in `where` and\n * `orderBy`. Every place that starts from a column and has to reach a row, a\n * Drizzle table or a payload goes through here, so there is one answer rather\n * than one per call site — the two that disagreed put `displayName` and\n * `author_id` on the same API.\n *\n * A declared property is the authority when there is one, because its key *is*\n * the wire name and `columnName` is the only thing that ever renamed the\n * column:\n *\n * 1. an explicit `columnName` equal to this column;\n * 2. a property whose key is literally the column (an author who wrote\n * `author_id:` meant `author_id` on the wire, and gets it);\n * 3. a property whose key snake-cases to the column, which is the default\n * mapping — `authorId` → `author_id`.\n *\n * With no property in the way — a foreign key derived from a relation, which\n * usually has none — the name is derived: {@link toWireKey}.\n *\n * Note the fallback is *not* the column verbatim. That was the old behaviour\n * and it is precisely the defect: a derived foreign key reached the wire under\n * its column name while every hand-authored field beside it was camelCase.\n */\nexport function fieldKeyForColumn(collection: CollectionConfig | undefined, column: string): string {\n const properties = collection?.properties;\n if (properties) {\n for (const [key, prop] of Object.entries(properties)) {\n const columnName = (prop as { columnName?: unknown } | undefined)?.columnName;\n if (typeof columnName === \"string\" && columnName === column) return key;\n }\n for (const key of Object.keys(properties)) {\n if (key === column) return key;\n if (toSnakeCase(key) === column) return key;\n }\n }\n return toWireKey(column);\n}\n\n/**\n * Look up a relation by key with forgiving normalization.\n *\n * `resolveCollectionRelations` stores each relation under a single canonical\n * key (no aliases). This helper tries the given key as-is, then falls back to\n * slug form (underscores → hyphens) and snake_case form (hyphens → underscores)\n * so that callers that receive a key from external input (URL path segments,\n * user-provided config, etc.) can still find the right entry.\n */\nexport function findRelation(\n resolvedRelations: Record<string, ResolvedRelation>,\n key: string\n): ResolvedRelation | undefined {\n // Exact match first\n if (resolvedRelations[key]) return resolvedRelations[key];\n\n // Try slug form (e.g. \"company_id\" → \"company-id\")\n const slugKey = key.replace(/_/g, \"-\");\n if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];\n\n // Try snake_case form (e.g. \"company-id\" → \"company_id\")\n const snakeKey = key.replace(/-/g, \"_\");\n if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];\n\n return undefined;\n}\n","import {\n ArrayProperty,\n AuthState,\n CollectionConfig,\n EnumValueConfig,\n EnumValues,\n NumberProperty,\n Properties,\n Property,\n RelationProperty,\n ResolvedRelation,\n StringProperty,\n getDataSourceCapabilities,\n getDeclaredSubcollections,\n type EntityChildView\n} from \"@rebasepro/types\";\n\ntype PropertyConfig = { property: unknown; [key: string]: unknown };\nimport { isPropertyBuilder } from \"./entities\";\nimport { enumToObjectEntries } from \"./enums\";\nimport { DEFAULT_ONE_OF_TYPE } from \"./common\";\nimport { isDefaultFieldConfigId } from \"@rebasepro/utils\";\nimport { getIn, mergeDeep } from \"@rebasepro/utils\";\nimport { isJunctionBackedRelation, resolveCollectionRelations } from \"./relations\";\nimport { resolveRelation } from \"./resolve-relation\";\n\n/**\n * Resolve property builders, enums and arrays.\n */\n\nexport type ResolvePropertyProps<M extends Record<string, unknown> = Record<string, unknown>> = {\n property: Property\n propertyKey?: string,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}\n\nexport function resolveProperty<M extends Record<string, unknown> = Record<string, unknown>>(props: ResolvePropertyProps<M>): Property | null {\n\n const {\n property,\n ignoreMissingFields = false,\n ...rest\n } = props;\n\n let resultProperty: Property;\n\n if (isPropertyBuilder(property)) {\n const path = rest.path;\n if (!path) {\n // When path is not available (e.g. in preview contexts), skip dynamic\n // resolution and use the property as-is without dynamic modifications.\n resultProperty = property as Property;\n } else {\n const usedPropertyValue = rest.propertyKey ? getIn(rest.values, rest.propertyKey) : undefined;\n const dynamicProps = property.dynamicProps?.({\n ...rest,\n path,\n propertyValue: usedPropertyValue,\n values: rest.values ?? {},\n previousValues: rest.previousValues ?? rest.values ?? {}\n });\n resultProperty = mergeDeep(property, dynamicProps ?? {});\n }\n } else {\n resultProperty = property as Property;\n }\n\n // Apply dynamic properties if they exist\n if (resultProperty?.dynamicProps && rest.path) {\n const path = rest.path;\n const usedPropertyValue = rest.propertyKey ? getIn(rest.values, rest.propertyKey) : undefined;\n const dynamicPropsResult = resultProperty.dynamicProps({\n ...rest,\n path,\n propertyValue: usedPropertyValue,\n values: rest.values ?? {},\n previousValues: rest.previousValues ?? rest.values ?? {}\n });\n\n if (dynamicPropsResult) {\n resultProperty = mergeDeep(resultProperty, dynamicPropsResult);\n }\n }\n\n let resolvedProperty: Property | null;\n\n if (resultProperty?.type === \"map\" && resultProperty.properties) {\n const properties = resolveProperties({\n ignoreMissingFields,\n ...rest,\n properties: resultProperty.properties\n });\n resolvedProperty = {\n ...resultProperty,\n properties\n } as Property;\n } else if (resultProperty?.type === \"array\") {\n resolvedProperty = resultProperty;\n } else if ((resultProperty?.type === \"string\" || resultProperty?.type === \"number\") && resultProperty.enum) {\n resolvedProperty = resolvePropertyEnum(resultProperty);\n } else {\n resolvedProperty = resultProperty;\n }\n\n if (resolvedProperty?.propertyConfig && !isDefaultFieldConfigId(resolvedProperty.propertyConfig)) {\n const cmsFields = rest.propertyConfigs;\n if (!cmsFields && !ignoreMissingFields) {\n throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`);\n }\n const customField: PropertyConfig | undefined = cmsFields?.[resolvedProperty.propertyConfig];\n if (!customField) {\n console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`)\n return resolvedProperty;\n }\n if (customField.property) {\n const restConfigProperty = { ...customField.property } as Record<string, unknown>;\n delete restConfigProperty.propertyConfig;\n const customFieldProperty = resolveProperty({\n property: { name: \"\",\n...restConfigProperty } as Property,\n ignoreMissingFields,\n ...rest\n });\n if (customFieldProperty) {\n resolvedProperty = mergeDeep(customFieldProperty, resolvedProperty);\n }\n }\n\n }\n\n return resolvedProperty;\n}\n\n/**\n * The resolved relation a relation property refers to.\n *\n * Normalization stamps `resolvedRelation` onto the property, so this is usually\n * a field read. It falls back to resolving from the collection for properties\n * that never went through the registry — a preview, or a form rendered straight\n * from an authored config.\n */\nexport function resolveRelationProperty(\n property: RelationProperty,\n collection: CollectionConfig,\n propertyKey?: string\n): ResolvedRelation {\n if (property.resolvedRelation) return property.resolvedRelation;\n\n if (property.relation) {\n return resolveRelation(property.relation, collection, propertyKey);\n }\n\n const name = propertyKey ?? \"\";\n const declared = resolveCollectionRelations(collection)[name];\n if (!declared) {\n throw Error(\n `Relation property '${name || \"(unnamed)\"}' on '${collection.slug}' declares no \\`relation\\`, ` +\n \"and the collection has no relation of that name.\"\n );\n }\n return declared;\n}\n\n/**\n * Resolve enum aliases for a string or number property\n * @param property\n */\nexport function resolvePropertyEnum(property: StringProperty | NumberProperty): StringProperty | NumberProperty {\n if (typeof property.enum === \"object\") {\n return {\n ...property,\n enum: enumToObjectEntries(property.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? []\n };\n }\n return property as StringProperty | NumberProperty;\n}\n\n/**\n * Resolve enums and arrays for properties\n * @param properties\n * @param value\n */\nexport function resolveProperties<M extends Record<string, unknown>>({\n propertyKey,\n properties,\n ignoreMissingFields,\n ...props\n}: {\n propertyKey?: string,\n properties: Properties,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}): Properties {\n return Object.entries<Property>(properties as Record<string, Property>)\n .map(([key, property]) => {\n const childResolvedProperty = resolveProperty({\n propertyKey: propertyKey ? `${propertyKey}.${key}` : undefined,\n property: property,\n ignoreMissingFields,\n ...props\n });\n if (!childResolvedProperty) return {};\n return {\n [key]: childResolvedProperty\n };\n })\n .filter((a) => a !== null)\n .reduce((a, b) => ({ ...a,\n...b }), {}) as Properties;\n}\n\nexport function resolveArrayProperties<M>({\n propertyKey,\n property,\n ignoreMissingFields = false,\n ...props\n}: {\n propertyKey?: string,\n property: ArrayProperty,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}): Property[] {\n const propertyValue = propertyKey ? getIn(props.values, propertyKey) : undefined;\n\n if (property.of) {\n if (Array.isArray(property.of)) {\n return property.of.map((p, index) => {\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: p as Property,\n ignoreMissingFields,\n ...props,\n index\n });\n }) as Property[];\n } else {\n const of = property.of;\n const resolvedProperties = getArrayResolvedProperties({\n propertyValue,\n propertyKey,\n property,\n ignoreMissingFields,\n ...props\n });\n // Destructured to be *excluded* from `...rest`, not to be used —\n // see the comment below. Said explicitly so the discarded-value\n // ratchet does not carry a finding that is working as intended.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { values, previousValues, ...rest } = props;\n const ofProperty = resolveProperty({ // we don't want to pass the values of the parent entity\n property: of,\n ignoreMissingFields,\n ...rest\n });\n if (!ofProperty && !ignoreMissingFields)\n throw Error(\"When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property\")\n return resolvedProperties;\n }\n } else if (property.oneOf) {\n const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;\n const resolvedProperties: Property[] = Array.isArray(propertyValue)\n ? propertyValue.map((v, index) => {\n const type = v && v[typeField];\n const childProperty = property.oneOf?.properties[type];\n if (!type || !childProperty) return null;\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: childProperty,\n ignoreMissingFields,\n ...props\n });\n }).filter(e => Boolean(e)) as Property[]\n : [];\n return resolvedProperties;\n } else if (!property.columnType) {\n // An array with neither `of`/`oneOf` nor a `columnType` describes no element\n // type, so nothing can be generated or rendered from it.\n //\n // The escape hatch used to be `ui.Field` — \"a custom component can render\n // anything\" — which made a *presentation* field decide whether a schema was\n // valid, in code the Postgres generator runs. `columnType` is the same escape\n // hatch stated as data: `columnType: \"text[]\"` says what the column holds,\n // which is what both the generator and the form actually need.\n throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or a \\`columnType\\` such as \"text[]\"`);\n } else {\n return [];\n }\n\n}\n\nexport function getArrayResolvedProperties({\n propertyKey,\n propertyValue,\n property,\n ...props\n}: {\n propertyValue: unknown,\n propertyKey?: string,\n property: ArrayProperty,\n ignoreMissingFields: boolean,\n values?: object;\n previousValues?: object;\n path?: string;\n entityId?: string | number;\n index?: number;\n propertyConfigs?: Record<string, PropertyConfig>;\n authController: AuthState;\n}) {\n\n const of = property.of;\n if (!of)\n throw Error(\n `Trying to resolve an array property (${propertyKey}) without providing an 'of' property`\n )\n return Array.isArray(propertyValue)\n ? propertyValue.map((v: unknown, index: number) => {\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: Array.isArray(of) ? of[index] : of,\n ...props,\n index\n });\n }).filter(e => Boolean(e)) as Property[]\n : [];\n}\n\nexport function resolveEnumValues(input: EnumValues): EnumValueConfig[] | undefined {\n if (typeof input === \"object\") {\n return Object.entries(input).map(([id, value]) =>\n (typeof value === \"string\"\n ? {\n id,\n label: value\n }\n : value));\n } else if (Array.isArray(input)) {\n return input as EnumValueConfig[];\n } else {\n return undefined;\n }\n}\n\n\n/**\n * The lists rendered inside an entity view of `collection` — its tabs.\n *\n * The single derivation. There used to be two that disagreed: this one, and a\n * copy in `CollectionRegistry.normalizeCollection` that stamped each child with\n * the *target collection's* slug instead of the relation key. Since the\n * registry ran first and cached its answer onto `childCollections`, its version\n * was the one that won, and the frontend addressed child listings by a segment\n * the backend could not resolve.\n *\n * Order of precedence:\n * 1. `childCollections` — the explicit escape hatch for custom drivers.\n * 2. `subcollections` on an engine that has real containment (Firestore).\n * 3. many-relations on an engine that has relations (SQL).\n */\nexport function getEntityChildViews<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): EntityChildView[] {\n const asSubcollections = (collections: CollectionConfig<Record<string, unknown>>[]): EntityChildView[] =>\n collections.filter(Boolean).map(child => ({\n key: child.slug,\n collection: child,\n source: { kind: \"subcollection\" as const }\n }));\n\n if (collection.childCollections) {\n return asSubcollections(collection.childCollections() ?? []);\n }\n\n const capabilities = getDataSourceCapabilities(collection.engine);\n\n const declaredSubcollections = getDeclaredSubcollections(collection);\n if (capabilities.supportsSubcollections && declaredSubcollections) {\n return asSubcollections(declaredSubcollections() ?? []);\n }\n\n if (!capabilities.supportsRelations) return [];\n\n const resolvedRelations = resolveCollectionRelations(collection);\n const views: EntityChildView[] = [];\n const seen = new Set<string>();\n\n // Keyed by the map key, not by `relationName`: the map key is what\n // `findRelation` matches a path segment against, so it is the only one that\n // addresses the same relation on both sides of the wire. The map registers\n // some relations twice — once canonically, once under the declaring\n // property key — so dedupe on the underlying relation.\n for (const [relationKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.cardinality !== \"many\") continue;\n\n const identity = relation.relationName ?? relationKey;\n if (seen.has(identity)) continue;\n\n let target: CollectionConfig | undefined;\n try {\n target = relation.target();\n } catch {\n continue;\n }\n if (!target) continue;\n seen.add(identity);\n\n // A name given to the declaring property is the author naming the tab.\n const declaringProperty = Object.entries((collection.properties ?? {}) as Record<string, Property>)\n .find(([propKey, p]) => p.type === \"relation\" && ((p as RelationProperty).relation?.relationName ?? propKey) === identity);\n const customName = declaringProperty?.[1]?.name;\n\n const base: CollectionConfig<Record<string, unknown>> = {\n ...target,\n slug: relationKey,\n ...(customName ? { name: customName,\nsingularName: customName } : {})\n } as CollectionConfig<Record<string, unknown>>;\n\n views.push({\n key: relationKey,\n collection: (relation.overrides ? mergeDeep(base, relation.overrides) : base) as CollectionConfig<Record<string, unknown>>,\n source: {\n kind: \"relation\",\n relationKey,\n mode: isJunctionBackedRelation(relation) ? \"linked\" : \"owned\",\n targetSlug: target.slug\n }\n });\n }\n\n return views;\n}\n\n/**\n * Each of `collection`'s tabs paired with the property that declared it, when a\n * property declared it: child view key → property key.\n *\n * A many-relation can only be declared as a property — that is the documented\n * and only mechanism — and {@link getEntityChildViews} promotes it to a tab. So\n * one declaration reaches the panel twice, and neither surface knew about the\n * other. The form rendered a relation picker beside the tab, and the collection\n * table rendered *two* columns under one heading: the relation's own column,\n * showing the child rows, and a jump-to-tab button carrying the same name.\n *\n * The pairing is what lets each surface decide which half is redundant, and it\n * has to be a pairing rather than two sets because the two keys differ whenever\n * a relation is named. The match is on the resolved `relationName` — the\n * identity `getEntityChildViews` itself dedupes on — so a relation declared in\n * `relations` and pointed at by a differently-named property is recognised too.\n *\n * A relation with no property of its own is absent here, which is the point: it\n * has exactly one surface already, and nothing to weigh it against.\n *\n * Only top-level properties: a relation nested inside a `map` gets no tab.\n */\nexport function getChildViewDeclaringProperties<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): Map<string, string> {\n const pairs = new Map<string, string>();\n\n const relationProperties = Object.entries((collection.properties ?? {}) as Record<string, Property>)\n .filter(([, property]) => property?.type === \"relation\");\n if (relationProperties.length === 0) return pairs;\n\n const relationViews = getEntityChildViews(collection)\n .filter(view => view.source.kind === \"relation\");\n if (relationViews.length === 0) return pairs;\n\n const resolvedRelations = resolveCollectionRelations(collection);\n const identityOf = (relationKey: string): string =>\n resolvedRelations[relationKey]?.relationName ?? relationKey;\n\n const declaringPropertyByIdentity = new Map<string, string>();\n for (const [propertyKey, property] of relationProperties) {\n const relation = (property as RelationProperty).resolvedRelation ?? resolvedRelations[propertyKey];\n // A to-one relation is a foreign key the author edits, never a tab. No\n // view will match it — the views here are many-relations only — but\n // reading the cardinality says so where someone is looking.\n if (relation?.cardinality !== \"many\") continue;\n const identity = relation.relationName ?? propertyKey;\n if (!declaringPropertyByIdentity.has(identity)) declaringPropertyByIdentity.set(identity, propertyKey);\n }\n\n for (const view of relationViews) {\n const propertyKey = declaringPropertyByIdentity.get(\n identityOf((view.source as { relationKey: string }).relationKey));\n if (propertyKey) pairs.set(view.key, propertyKey);\n }\n\n return pairs;\n}\n\n/**\n * The property keys of `collection` whose relation is already one of its tabs.\n *\n * What a form asks: the tab is the treatment for a list of child rows, so the\n * picker beside it is the redundant half. See\n * {@link getChildViewDeclaringProperties}.\n */\nexport function getChildViewRelationPropertyKeys<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): Set<string> {\n return new Set(getChildViewDeclaringProperties(collection).values());\n}\n\n/**\n * The child views of `collection` as bare collections.\n *\n * The flattened view of {@link getEntityChildViews}, for navigation code that\n * only needs to match a path segment against a slug. Anything that cares *what\n * kind* of list it is showing — chiefly the admin, which must not offer a\n * global delete on a shared row — should read the views instead.\n */\nexport function getSubcollections<M extends Record<string, unknown> = Record<string, unknown>>(collection: CollectionConfig<M>): CollectionConfig<Record<string, unknown>>[] {\n return getEntityChildViews(collection).map(view => view.collection);\n}\n","import { ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, LiteralPolicyOperand, PolicyExpression, policy, rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * A tiny, regex-based SQL \"parser\" for security rules.\n *\n * This is NOT a full SQL parser. It is designed to handle the subset of SQL\n * commonly used in `USING` and `WITH CHECK` clauses, enough to drive the\n * optimistic client-side UI decision.\n *\n * It handles:\n * - `field = 'literal'`\n * - `field != 'literal'`\n * - `field = current_setting('app.uid')` (or the legacy `app.user_id`)\n * - `A AND B`, `A OR B` — only where the keyword is at the top level\n * - `true`\n * - `IN (...)` (as optimistic true)\n *\n * For anything it doesn't understand, it returns a `raw` expression, which\n * the evaluator treats as \"unknown\" (and usually optimistic true).\n *\n * **This output also round-trips back into DDL** via `policyToPostgres` (the\n * schema/policy generators), so decomposing a clause the parser only partly\n * understands is not a cosmetic mistake — it emits invalid SQL. When in doubt,\n * prefer `raw`: it is reproduced verbatim.\n */\n/** True when `keyword` starts at `i` as a standalone word. */\nfunction isKeywordAt(upper: string, i: number, keyword: string): boolean {\n if (!upper.startsWith(keyword, i)) return false;\n const before = i === 0 ? \" \" : upper[i - 1];\n const after = upper[i + keyword.length] ?? \" \";\n return /[\\s()]/.test(before) && /[\\s()]/.test(after);\n}\n\n/**\n * Split `sql` on a boolean keyword, but only where it sits at paren depth 0 and\n * outside a string literal. Returns null when it never does, so the caller\n * leaves the clause alone.\n *\n * This used to be `sql.split(/ AND /i)`, which tore subqueries in half: the\n * `AND` inside\n * `EXISTS (SELECT 1 FROM organization_members m WHERE m.org = t.org AND m.user_id = rebase.uid())`\n * split the expression, and re-emitting the halves produced\n * `(EXISTS (...) AND m.user_id = rebase.uid())`\n * where `m` is no longer in scope — SQL that Postgres rejects outright with\n * \"missing FROM-clause entry for table\". Returning null instead keeps such a\n * clause as a `raw` expression, which round-trips verbatim.\n */\nfunction splitTopLevel(sql: string, keyword: \"AND\" | \"OR\"): string[] | null {\n const upper = sql.toUpperCase();\n const parts: string[] = [];\n let depth = 0;\n let inString = false;\n let start = 0;\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n if (inString) {\n if (ch === \"'\") {\n if (sql[i + 1] === \"'\") i++; // '' escapes a quote inside a literal\n else inString = false;\n }\n continue;\n }\n if (ch === \"'\") { inString = true; continue; }\n if (ch === \"(\") { depth++; continue; }\n if (ch === \")\") { depth--; continue; }\n if (depth === 0 && isKeywordAt(upper, i, keyword)) {\n parts.push(sql.slice(start, i));\n i += keyword.length - 1;\n start = i + 1;\n }\n }\n\n if (parts.length === 0) return null;\n parts.push(sql.slice(start));\n const trimmedParts = parts.map(p => p.trim()).filter(p => p.length > 0);\n return trimmedParts.length > 1 ? trimmedParts : null;\n}\n\n/** Drop redundant wrapping parens (`(a AND b)` → `a AND b`), never `(a) AND (b)`. */\nfunction stripOuterParens(sql: string): string {\n let s = sql.trim();\n for (;;) {\n if (!s.startsWith(\"(\") || !s.endsWith(\")\")) return s;\n let depth = 0;\n let inString = false;\n let wraps = true;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (inString) {\n if (ch === \"'\") {\n if (s[i + 1] === \"'\") i++;\n else inString = false;\n }\n continue;\n }\n if (ch === \"'\") { inString = true; continue; }\n if (ch === \"(\") depth++;\n else if (ch === \")\") {\n depth--;\n if (depth === 0 && i < s.length - 1) { wraps = false; break; }\n }\n }\n if (!wraps) return s;\n s = s.slice(1, -1).trim();\n }\n}\n\nexport function sqlToPolicy(sql: string): PolicyExpression {\n // Normalised before anything else looks at it, so every pattern below only\n // has to know the current spelling. A database migrated by a pre-1.0 release\n // still holds `auth.uid()` in its policy bodies until the next push or boot\n // recompiles them — and until then the admin UI reads those bodies back\n // through here. Without this they parse as opaque `raw`, and the framework's\n // own policies get badged as hand-written drift.\n //\n // Normalising rather than accepting both spellings throughout is deliberate:\n // it also means a legacy policy that falls through to `raw` is stored in the\n // new spelling, so editing and saving one in the Studio migrates it.\n const trimmed = stripOuterParens(rewriteLegacyRlsFunctions(sql).trim());\n\n if (trimmed.toLowerCase() === \"true\") return policy.true();\n if (trimmed.toLowerCase() === \"false\") return policy.false();\n\n // Handle roles overlap (&&)\n // Matches: string_to_array(rebase.roles(), ',') && ARRAY['admin', 'editor']\n const overlapMatch = trimmed.match(/^string_to_array\\s*\\(\\s*rebase\\.roles\\(\\)\\s*,\\s*','\\s*\\)\\s*&&\\s*ARRAY\\s*\\[(.+)\\]$/i);\n if (overlapMatch) {\n const roles = overlapMatch[1].split(\",\").map(s => s.trim().replace(/^'|'$/g, \"\"));\n return policy.rolesOverlap(roles);\n }\n\n // Handle roles containment (@>)\n // Matches: string_to_array(rebase.roles(), ',') @> ARRAY['admin']\n const containMatch = trimmed.match(/^string_to_array\\s*\\(\\s*rebase\\.roles\\(\\)\\s*,\\s*','\\s*\\)\\s*@>\\s*ARRAY\\s*\\[(.+)\\]$/i);\n if (containMatch) {\n const roles = containMatch[1].split(\",\").map(s => s.trim().replace(/^'|'$/g, \"\"));\n return policy.rolesContain(roles);\n }\n\n // OR binds looser than AND, so it splits first.\n const orParts = splitTopLevel(trimmed, \"OR\");\n if (orParts) return policy.or(...orParts.map(sqlToPolicy));\n\n const andParts = splitTopLevel(trimmed, \"AND\");\n if (andParts) return policy.and(...andParts.map(sqlToPolicy));\n\n // Handle = and !=\n const match = trimmed.match(/^(.+?)\\s*(!?=)\\s*(.+)$/);\n if (match) {\n const [, leftStr, op, rightStr] = match;\n const left = parseOperand(leftStr.trim());\n const right = parseOperand(rightStr.trim());\n if (left && right) {\n return policy.compare(left, op === \"=\" ? \"eq\" : \"neq\", right);\n }\n }\n\n // Fallback to raw — the NORMALISED text, not the input. Storing the input\n // verbatim would mean a legacy policy read out of a database, edited in the\n // Studio and saved, writes `auth.uid()` back into the project's config: a\n // call to a function 1.0 no longer creates.\n return policy.raw(trimmed);\n}\n\n/**\n * Literals from other BaaS platforms that people compare `rebase.uid()` against\n * out of habit. Mirrors the driver's `FOREIGN_CONVENTION_ROLES` guard on\n * `pgRoles`, one surface over: the same muscle memory inside a `using:` string\n * is the more dangerous spelling, because it inverts a rule instead of\n * emptying a table.\n */\n/**\n * A `Map`, not an object literal.\n *\n * As `Record<string, string>` this was indexed with a literal taken straight\n * out of a policy, so every key on `Object.prototype` answered: a rule\n * comparing `rebase.uid()` to `\"valueOf\"`, `\"toString\"`, `\"constructor\"` or\n * `\"hasOwnProperty\"` found a truthy \"platform\" and reported an anonymous-grant\n * risk that does not exist — with the matched function interpolated into the\n * explanation as the platform's name. A security warning that fires on\n * innocent input is worse than none: it is what teaches people to skip the\n * warnings that are real.\n *\n * Same shape as the prototype-pollution class swept out of `setIn`, `getIn`,\n * `mergeDeep` and `unflattenObject` — a data-derived key reaching a plain\n * object. Found by a property test, on the input `\"valueOf\"`.\n */\nconst FOREIGN_CONVENTION_UIDS = new Map<string, string>([\n [\"anon\", \"Supabase\"],\n [\"authenticated\", \"Supabase\"],\n [\"service_role\", \"Supabase\"]\n]);\n\n/**\n * The same foreign literals, as a pattern for SQL that could not be parsed\n * back into structure.\n */\nconst FOREIGN_UID_LITERAL_SQL = new RegExp(\n String.raw`rebase\\.uid\\(\\)\\s*=\\s*'(${[...FOREIGN_CONVENTION_UIDS.keys()].join(\"|\")})'`,\n \"i\"\n);\n\n/** A clause that reads as a lockdown but admits anonymous callers. */\nexport interface AnonymousGrantRisk {\n /** Which spelling was found. */\n pattern: \"foreign-uid-literal\" | \"uid-not-null\";\n /** The offending fragment — the literal, or the SQL that is a tautology. */\n detail: string;\n /** Why it admits anonymous callers, and what to write instead. */\n explanation: string;\n}\n\n/**\n * `rebase.uid() IS NOT NULL` in raw SQL, the clause that is always true.\n *\n * Both schema spellings, because this runs over policy bodies read back from a\n * database, and one migrated by a pre-1.0 release still holds `auth.uid()`.\n * A security check that stops recognising a dangerous clause because the\n * framework renamed a function is a check that silently turns off.\n */\nconst UID_NOT_NULL = /\\b(?:rebase|auth)\\.uid\\(\\)\\s+IS\\s+NOT\\s+NULL/i;\n\n/**\n * Find clauses that read as \"signed-in users only\" but admit anonymous callers.\n *\n * Both spellings come from the same place — Supabase, where its own `auth.uid()`\n * really is NULL for an anonymous request. Rebase substitutes\n * {@link ANONYMOUS_USER_ID} instead (a blank id would read back as NULL, which\n * is how the trusted *server* context is recognised), so:\n *\n * - `rebase.uid() IS NOT NULL` is a tautology on the user path, and\n * - `rebase.uid() != 'anon'` excludes one spelling of anonymous and admits the\n * other. This one is not hypothetical and was not only a foreign habit:\n * rebase's own request path reported `'anon'` while everything that compiled\n * or checked a policy used `'anonymous'`, so whichever literal an author\n * picked, half the anonymous callers walked through. See\n * {@link ANONYMOUS_USER_IDS}.\n *\n * Either one turns a lockdown into a full grant, and neither looks wrong. No\n * real user id is ever one of these literals, and a user-context request is\n * never NULL, so a match is always a mistake rather than a deliberate check.\n *\n * Structured expressions are checked too, not just parsed SQL: `policy.compare`\n * can spell the same mistake.\n */\nexport function findAnonymousGrants(expr: PolicyExpression): AnonymousGrantRisk[] {\n const found: AnonymousGrantRisk[] = [];\n\n const visit = (e: PolicyExpression): void => {\n switch (e.kind) {\n case \"and\":\n case \"or\":\n e.operands.forEach(visit);\n return;\n case \"not\":\n visit(e.operand);\n return;\n case \"existsIn\":\n visit(e.where);\n return;\n case \"raw\": {\n if (UID_NOT_NULL.test(e.sql)) {\n found.push({\n pattern: \"uid-not-null\",\n detail: e.sql,\n explanation: \"`rebase.uid() IS NOT NULL` is true for every request that came from a client, \" +\n `including anonymous ones — they carry '${ANONYMOUS_USER_ID}', not NULL. ` +\n \"Use `condition: policy.authenticated()` to mean \\\"signed in\\\".\"\n });\n }\n // The foreign literals have to be looked for here too, not only\n // in `compare`. `sqlToPolicy` falls back to `raw` for anything\n // it cannot structure — an `EXISTS (...)` subquery always does —\n // so a policy read back from the database arrives as one opaque\n // string. Checking only the tautology meant a genuine\n // `rebase.uid() = 'anon'` inside an `existsIn` was structurally\n // undetectable once round-tripped, and the caller read the empty\n // result as \"no risks found\".\n const foreign = FOREIGN_UID_LITERAL_SQL.exec(e.sql);\n if (foreign) {\n const literal = foreign[1];\n found.push({\n pattern: \"foreign-uid-literal\",\n detail: literal,\n explanation: `'${literal}' is a ${FOREIGN_CONVENTION_UIDS.get(literal)} convention. Rebase ` +\n `reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against ` +\n `'${literal}' passes for every caller. Use \\`condition: policy.authenticated()\\` to ` +\n \"mean \\\"signed in\\\".\"\n });\n }\n return;\n }\n case \"compare\": {\n const literal = [e.left, e.right].find(o => o.kind === \"literal\") as LiteralPolicyOperand | undefined;\n const comparesUid = e.left.kind === \"authUid\" || e.right.kind === \"authUid\";\n if (!comparesUid || typeof literal?.value !== \"string\") return;\n const platform = FOREIGN_CONVENTION_UIDS.get(literal.value);\n if (!platform) return;\n found.push({\n pattern: \"foreign-uid-literal\",\n detail: literal.value,\n explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous ` +\n `request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for ` +\n \"every caller. Use `condition: policy.authenticated()` to mean \\\"signed in\\\" — it \" +\n `compiles to NOT IN (${ANONYMOUS_USER_IDS.map(v => `'${v}'`).join(\", \")}), covering ` +\n \"every spelling rebase has reported rather than whichever one you remember.\"\n });\n return;\n }\n default:\n return;\n }\n };\n\n visit(expr);\n return found;\n}\n\nfunction parseOperand(str: string) {\n // current_setting('app.uid') or rebase.uid(). `app.user_id` is the\n // pre-rename spelling and stays parseable: policies are data, so a\n // database provisioned before the rename still holds rules written\n // against it, and round-tripping one must not silently drop the operand.\n //\n // ANCHORED, and that is the whole point. These tests used to be\n // unanchored — `.test(str)` rather than `^…$` — so any operand text that\n // merely *contained* a uid call was replaced wholesale by the call itself.\n // Everything else in the expression was discarded with it, including a\n // leading `NOT (`:\n //\n // NOT (rebase.uid() = rebase.uid()) parsed as rebase.uid() = rebase.uid()\n //\n // A deny became an unconditional grant. The realistic spelling is a\n // hand-written defensive rule with a uid call on both sides —\n // COALESCE(rebase.uid(), '') = COALESCE(owner_id, rebase.uid())\n // — which collapsed to the same tautology. This is not confined to the\n // admin UI: `securityRuleToConditions` feeds a rule's raw `using:` string\n // through here, and the Postgres DDL generators compile the result, so the\n // tautology was written into the database as the policy body.\n //\n // An operand this cannot identify exactly must return null, which drops the\n // whole clause to `raw` and reproduces it verbatim. That is the rule the\n // rest of this file already follows: when in doubt, prefer `raw`.\n if (/^current_setting\\s*\\(\\s*'app\\.(uid|user_id)'\\s*\\)$/i.test(str) || /^rebase\\.uid\\(\\)$/i.test(str)) {\n return policy.authUid();\n }\n\n // Literal string: 'value', with `''` decoded back to a single quote.\n //\n // `quoteLiteral` doubles every quote on the way out, and this did not undo\n // it, so a literal containing an apostrophe grew on every trip: O'Brien →\n // O''Brien → O''''Brien, doubling each time a policy was read back and\n // recompiled. Past the first trip the emitted policy compares against a\n // string no row holds.\n const literal = parseSingleQuoted(str);\n if (literal !== null) {\n return policy.literal(literal);\n }\n\n // Unquoted literals, which must be recognised BEFORE the bare-word branch\n // below or they are read as column names.\n //\n // `quoteLiteral` emits booleans, numbers and null unquoted, so `a = false`\n // came back as a comparison against a *field* called `false`, and `a = 42`\n // against a field called `42`. The recompiled SQL is identical either way,\n // which is why this survived a round-trip check on the SQL — but the\n // expression is now wrong, and the expression is what the admin UI\n // evaluates. Against a row with no `a`, Postgres denies (`NULL = false` is\n // not true) while the JS evaluator compared two missing columns, found them\n // equal, and allowed. That is precisely the client/database drift the\n // shared PolicyExpression model exists to make impossible.\n //\n // Unambiguous in both directions: a SQL identifier cannot begin with a\n // digit, and bare `true`/`false`/`null` are always the literals — a column\n // so named would have to be double-quoted to be referenced at all.\n if (/^-?\\d+$/.test(str)) return policy.literal(Number(str));\n if (/^-?\\d*\\.\\d+$/.test(str)) return policy.literal(Number(str));\n if (/^true$/i.test(str)) return policy.literal(true);\n if (/^false$/i.test(str)) return policy.literal(false);\n if (/^null$/i.test(str)) return policy.literal(null);\n\n // Bare field name — but only one that survives the snake-casing the\n // compiler will apply to it. `toSnakeCase(\"_\")` is the empty string, and a\n // field that compiles to an empty column reference emits `= 'x'`, which is\n // a syntax error at CREATE POLICY time. Such a name is left to `raw`, where\n // it round-trips verbatim instead. `toSnakeCase` itself is not touched:\n // column names derived by it are already in shipped databases.\n if (/^\\w+$/.test(str) && toSnakeCase(str) !== \"\") {\n return policy.field(str);\n }\n\n return null;\n}\n\n/**\n * Decode a single-quoted SQL literal, or null when `str` is not exactly one.\n *\n * Rejecting is as important as decoding: `'a' = 'b'` is two literals and an\n * operator, not one literal whose body contains a quote, and a regex anchored\n * on the outer quotes would happily read it as the latter. Every interior quote\n * must therefore be part of a `''` pair.\n */\nfunction parseSingleQuoted(str: string): string | null {\n if (str.length < 2 || !str.startsWith(\"'\") || !str.endsWith(\"'\")) return null;\n const body = str.slice(1, -1);\n let out = \"\";\n for (let i = 0; i < body.length; i++) {\n if (body[i] !== \"'\") {\n out += body[i];\n continue;\n }\n if (body[i + 1] === \"'\") {\n out += \"'\";\n i++;\n continue;\n }\n return null; // a bare quote — `str` is not a single literal\n }\n return out;\n}\n","import { PolicyExpression, SecurityRule, policy } from \"@rebasepro/types\";\nimport { sqlToPolicy } from \"./sqlToPolicy\";\n\n/**\n * The normalized `USING` / `WITH CHECK` conditions for a single security rule,\n * expressed in the engine-agnostic {@link PolicyExpression} model.\n *\n * A `null` clause means \"this rule contributes no condition for that clause\";\n * consumers apply the default (Postgres denies with `false`).\n */\nexport interface RuleConditions {\n usingExpr: PolicyExpression | null;\n withCheckExpr: PolicyExpression | null;\n}\n\n/**\n * Desugars a {@link SecurityRule} — its `access`/`ownerField`/`roles` shortcuts,\n * structured `condition`/`check`, and raw `using`/`withCheck` — into a single\n * normalized {@link PolicyExpression} pair.\n *\n * **This is the linchpin against drift:** both the Postgres DDL generators and\n * the client-side evaluator consume this one function, so there is exactly one\n * definition of what a rule means. In particular, application `roles` are folded\n * into the expression here (AND'd with the base condition, matching how Postgres\n * generates the clause) rather than being handled separately by each consumer.\n */\nexport function securityRuleToConditions(rule: SecurityRule): RuleConditions {\n return {\n usingExpr: withRoles(baseUsing(rule), rule),\n withCheckExpr: withRoles(baseWithCheck(rule), rule)\n };\n}\n\nfunction baseUsing(rule: SecurityRule): PolicyExpression | null {\n if (rule.condition) return rule.condition;\n if (rule.using != null) return sqlToPolicy(rule.using);\n if (rule.access === \"public\") return policy.true();\n if (rule.ownerField) return policy.compare(policy.field(rule.ownerField), \"eq\", policy.authUid());\n return null;\n}\n\nfunction baseWithCheck(rule: SecurityRule): PolicyExpression | null {\n if (rule.check) return rule.check;\n if (rule.withCheck != null) return sqlToPolicy(rule.withCheck);\n // No explicit WITH CHECK → fall back to the USING condition, matching\n // PostgreSQL's own default behavior.\n return baseUsing(rule);\n}\n\n/**\n * AND the base condition with an application-role check, or produce a roles-only\n * condition when there is no base. Mirrors the Postgres generator so that a\n * role-scoped restrictive rule denies exactly the same set of users on both\n * sides.\n */\nfunction withRoles(base: PolicyExpression | null, rule: SecurityRule): PolicyExpression | null {\n if (!rule.roles || rule.roles.length === 0) return base;\n const rolesExpr = policy.rolesOverlap(rule.roles);\n if (rule.mode === \"restrictive\") {\n // Restrictive rule: applies ONLY if user has the roles.\n // If user DOES NOT have the roles, they are NOT restricted (passes).\n // If user HAS the roles, they must pass the base condition.\n // Logical equivalent: NOT(roles) OR base\n return base ? policy.or(policy.not(rolesExpr), base) : policy.not(rolesExpr);\n }\n return base ? policy.and(base, rolesExpr) : rolesExpr;\n}\n","import { ANONYMOUS_USER_IDS, CollectionConfig, PolicyExpression, PolicyOperand, PolicyCompareOperator, Property, ExistsInPolicyExpression, RLS_IS_ANONYMOUS_SQL, RLS_JWT_SQL, RLS_ROLES_SQL, RLS_UID_SQL, rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { getTableName } from \"../relations\";\n\n/**\n * Options for {@link policyToPostgres}.\n */\nexport interface PolicyCompileOptions {\n /**\n * Resolve a collection by slug. Required to compile\n * {@link ExistsInPolicyExpression} (`policy.existsIn`) — the compiler needs\n * the joined collection to derive its table name / schema. When omitted, the\n * join table falls back to a snake_cased slug.\n */\n resolveCollection?: (slug: string) => CollectionConfig | undefined;\n}\n\n/**\n * The lexical scope threaded through compilation. It changes when we descend\n * into an `existsIn` subquery: inside it, `field` refers to the joined table\n * (aliased) while `outerField` refers to the outer RLS row (table-qualified).\n */\ninterface CompileScope {\n /** Collection whose columns a bare `field` operand resolves against. */\n fieldCollection?: CollectionConfig;\n /** SQL prefix for `field` operands (`\"\"` at top level, `\"alias\".` in a subquery). */\n fieldPrefix: string;\n /** The outer RLS collection, for `outerField` operands. */\n outerCollection?: CollectionConfig;\n /** SQL prefix for `outerField` operands (`\"\"` at top level, `\"schema\".\"table\".` in a subquery). */\n outerPrefix: string;\n resolveCollection?: (slug: string) => CollectionConfig | undefined;\n /** Monotonic counter for generating unique subquery aliases. */\n alias: { n: number };\n}\n\n/**\n * Compiles a {@link PolicyExpression} to a PostgreSQL boolean SQL string,\n * suitable for a `USING (...)` / `WITH CHECK (...)` clause.\n *\n * This is one of the two consumers of the shared policy model (the other being\n * {@link evaluatePolicy}); the Postgres schema generators call it so that DDL\n * and the admin UI derive from the exact same expression.\n */\nexport function policyToPostgres(expr: PolicyExpression, collection?: CollectionConfig, options?: PolicyCompileOptions): string {\n return compile(expr, {\n fieldCollection: collection,\n fieldPrefix: \"\",\n outerCollection: collection,\n outerPrefix: \"\",\n resolveCollection: options?.resolveCollection,\n alias: { n: 0 }\n });\n}\n\nfunction compile(expr: PolicyExpression, scope: CompileScope): string {\n switch (expr.kind) {\n case \"true\":\n return \"true\";\n case \"false\":\n return \"false\";\n case \"and\":\n return expr.operands.length === 0\n ? \"true\"\n : expr.operands.map(o => `(${compile(o, scope)})`).join(\" AND \");\n case \"or\":\n return expr.operands.length === 0\n ? \"false\"\n : expr.operands.map(o => `(${compile(o, scope)})`).join(\" OR \");\n case \"not\":\n return `NOT (${compile(expr.operand, scope)})`;\n case \"compare\": {\n // `rebase.uid()` returns text; cast the column side so uuid / integer\n // id columns compare cleanly instead of failing with\n // \"operator does not exist: uuid = text\" at CREATE POLICY time.\n const castForAuthUid = (operand: PolicyOperand, sqlText: string, other: PolicyOperand): string =>\n other.kind === \"authUid\" && (operand.kind === \"field\" || operand.kind === \"outerField\")\n ? `(${sqlText})::text`\n : sqlText;\n // A claim is text too, and the same mismatch applies — but here the\n // cast goes the OTHER way, onto the claim. Casting the column would\n // compile and would take the index off it, and this operand exists\n // to carry a tenancy predicate that is ANDed into every read of the\n // table. See {@link claimCastType}.\n const claimSql = (operand: PolicyOperand, other: PolicyOperand): string | undefined =>\n operand.kind === \"authClaim\"\n ? authClaimSql(operand.name, claimCastType(other, scope))\n : undefined;\n const leftSql = claimSql(expr.left, expr.right)\n ?? castForAuthUid(expr.left, operandToSql(expr.left, scope), expr.right);\n const rightSql = claimSql(expr.right, expr.left)\n ?? castForAuthUid(expr.right, operandToSql(expr.right, scope), expr.left);\n return `${leftSql} ${COMPARE_SQL[expr.op]} ${rightSql}`;\n }\n case \"rolesOverlap\":\n return `string_to_array(${RLS_ROLES_SQL}, ',') && ${rolesArraySql(expr.roles)}`;\n case \"rolesContain\":\n return `string_to_array(${RLS_ROLES_SQL}, ',') @> ${rolesArraySql(expr.roles)}`;\n case \"authenticated\":\n // `IS NOT NULL` alone is a tautology on the user path: every\n // user-context request sets `app.uid`, and an anonymous one sets\n // it to a sentinel. Excluding the sentinels is what makes this mean\n // \"signed in\" rather than \"anyone at all\".\n //\n // Every sentinel, not just the current one. This clause is written\n // into the database and outlives the server that generated it: a\n // policy compiled here may be enforced against an older server that\n // still reports `'anon'`, which is exactly how excluding one\n // spelling turned this helper into a grant. See ANONYMOUS_USER_IDS.\n return `${RLS_UID_SQL} IS NOT NULL AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`;\n case \"registered\":\n // \"Signed in\" AND \"not a guest\". The first half is the same clause\n // `authenticated` compiles to; the second is the fact anonymous\n // sign-in used not to put anywhere the database could see, so a\n // guest and an account were one principal inside every policy.\n //\n // `rebase.is_anonymous()` defaults to false when its GUC is unset,\n // so a policy compiled here and enforced by an older server reads\n // every session as an account — which is the behaviour that\n // deployment already had, rather than a lockout.\n return `${RLS_UID_SQL} IS NOT NULL`\n + ` AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`\n + ` AND NOT ${RLS_IS_ANONYMOUS_SQL}`;\n case \"serverContext\":\n // Only the built-in server flows leave `app.uid` unset.\n return `${RLS_UID_SQL} IS NULL`;\n case \"existsIn\":\n return compileExistsIn(expr, scope);\n case \"raw\": {\n // A project written against a pre-1.0 release may still spell the\n // helpers `auth.uid()`. Rewritten rather than rejected: the rule\n // means exactly the same thing, the developer cannot be expected to\n // have read a changelog mid-deploy, and the alternative is a policy\n // that compiles cleanly and then denies every row at runtime because\n // it calls a function that no longer exists.\n //\n // The counterpart is `warnOnLegacyRlsFunctions`, which says so once\n // at boot with the file to edit — silence here would leave the old\n // spelling working forever and make the migration permanent.\n const sqlText = rewriteLegacyRlsFunctions(expr.sql);\n\n // Full-power escape hatch: `{column}` denotes a column of the outer\n // RLS row. It must be table-qualified, not bare: raw SQL may open its\n // own subquery over the same table, and there a bare name binds to the\n // inner scope, collapsing `m.x = {x}` into the tautology `m.x = m.x`.\n return sqlText.replace(/\\{(\\w+)\\}/g, (_, col) =>\n `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);\n }\n }\n}\n\n/**\n * Compiles `existsIn` to a correlated `EXISTS (SELECT 1 FROM <join> WHERE ...)`.\n * Inside the subquery, `field` operands bind to the aliased join table and\n * `outerField` operands bind to the (table-qualified) outer RLS row.\n */\nfunction compileExistsIn(expr: ExistsInPolicyExpression, scope: CompileScope): string {\n const join = scope.resolveCollection?.(expr.collection);\n const joinTable = join ? getTableName(join) : toSnakeCase(expr.collection);\n const joinSchema = schemaOf(join) ?? schemaOf(scope.outerCollection) ?? \"public\";\n const alias = `_ex${scope.alias.n++}`;\n\n // `outerField` inside the subquery must be qualified with the outer table,\n // otherwise a bare column name would bind to the joined table instead.\n const outerPrefix = outerQualifier(scope);\n\n const innerScope: CompileScope = {\n fieldCollection: join,\n fieldPrefix: `\"${alias}\".`,\n outerCollection: scope.outerCollection,\n outerPrefix,\n resolveCollection: scope.resolveCollection,\n alias: scope.alias\n };\n return `EXISTS (SELECT 1 FROM \"${joinSchema}\".\"${joinTable}\" \"${alias}\" WHERE ${compile(expr.where, innerScope)})`;\n}\n\nconst COMPARE_SQL: Record<PolicyCompareOperator, string> = {\n eq: \"=\",\n neq: \"!=\",\n lt: \"<\",\n lte: \"<=\",\n gt: \">\",\n gte: \">=\"\n};\n\nfunction operandToSql(operand: PolicyOperand, scope: CompileScope): string {\n switch (operand.kind) {\n case \"field\":\n return `${scope.fieldPrefix}${resolveColumnName(operand.name, scope.fieldCollection)}`;\n case \"outerField\":\n return `${scope.outerPrefix}${resolveColumnName(operand.name, scope.outerCollection)}`;\n case \"literal\":\n return quoteLiteral(operand.value);\n case \"authUid\":\n return RLS_UID_SQL;\n case \"authRoles\":\n return `string_to_array(${RLS_ROLES_SQL}, ',')`;\n case \"authClaim\":\n // Uncast — the shape a claim has when nothing says what it is being\n // compared against (a claim on both sides, or against a literal).\n // The `compare` arm replaces this whenever the other operand names\n // a typed column.\n return authClaimSql(operand.name, \"text\");\n }\n}\n\n/** Postgres types a text claim is cast to. `\"text\"` is the no-cast case. */\ntype ClaimCastType = \"text\" | \"uuid\" | \"bigint\" | \"numeric\";\n\n/**\n * The Postgres type a claim has to be cast to, to be compared with `operand`.\n *\n * `\"text\"` means \"no cast\": a claim already is text, and a `text` / `varchar`\n * column compares with it directly and keeps using its index.\n *\n * Resolved from the *property*, and through a relation's target when the column\n * is a foreign key — a `belongsTo` tenant field is the ordinary shape, and its\n * column's type is the target collection's primary key type rather than\n * anything visible on the property itself. Unknown resolves to `\"text\"`, which\n * is the safe direction: a redundant `text` comparison costs nothing, while a\n * missing `uuid` cast is a `CREATE POLICY` that fails and leaves a table with\n * RLS enabled and no policy — which denies every row.\n */\nfunction claimCastType(operand: PolicyOperand, scope: CompileScope): ClaimCastType {\n if (operand.kind !== \"field\" && operand.kind !== \"outerField\") return \"text\";\n const collection = operand.kind === \"field\" ? scope.fieldCollection : scope.outerCollection;\n return propertyClaimCastType(operand.name, collection, scope.resolveCollection);\n}\n\n/**\n * What `name` on `collection` compares against a text claim as.\n *\n * `depth` stops a `reference` cycle — two collections whose keys point at each\n * other — from recursing forever. Two hops is more than any real declaration\n * needs.\n */\nfunction propertyClaimCastType(\n name: string,\n collection: CollectionConfig | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined,\n depth = 0\n): ClaimCastType {\n const prop = collection?.properties?.[name] as Property | undefined;\n if (!prop || depth > 2) return \"text\";\n\n switch (prop.type) {\n case \"string\": {\n const sp = prop as { isId?: unknown; columnType?: unknown; enum?: unknown };\n if (sp.enum) return \"text\";\n return sp.isId === \"uuid\" || sp.columnType === \"uuid\" ? \"uuid\" : \"text\";\n }\n case \"number\": {\n const np = prop as { columnType?: string; isId?: unknown; validation?: { integer?: boolean } };\n if (np.columnType === \"numeric\") return \"numeric\";\n if (np.columnType || np.validation?.integer || np.isId) return \"bigint\";\n // A `number` with no `columnType` and no `validation.integer` is\n // NUMERIC — see `numberType` in the schema planner.\n return \"numeric\";\n }\n case \"reference\":\n return primaryKeyClaimCastType(\n resolveTargetCollection((prop as { path?: string }).path, resolveCollection),\n resolveCollection,\n depth\n );\n case \"relation\":\n return primaryKeyClaimCastType(\n resolveTargetCollection(\n relationTargetSlug((prop as { relation?: { target?: unknown } }).relation),\n resolveCollection\n ),\n resolveCollection,\n depth\n );\n default:\n return \"text\";\n }\n}\n\n/** The cast a column pointing at `target`'s primary key needs. */\nfunction primaryKeyClaimCastType(\n target: CollectionConfig | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined,\n depth: number\n): ClaimCastType {\n if (!target) return \"text\";\n for (const [key, property] of Object.entries(target.properties ?? {})) {\n if (!(property as { isId?: unknown })?.isId) continue;\n return propertyClaimCastType(key, target, resolveCollection, depth + 1);\n }\n // No declared key: the implicit `id TEXT PRIMARY KEY`.\n return \"text\";\n}\n\n/**\n * A relation's target slug, whatever form the declaration took.\n *\n * `target` is a slug on a plain object and a thunk on a builder — the two\n * shapes `resolveRelation` normalises — and this runs on the raw property,\n * before that resolution.\n */\nfunction relationTargetSlug(relation: { target?: unknown } | undefined): string | undefined {\n const target = relation?.target;\n if (typeof target === \"string\") return target;\n if (typeof target !== \"function\") return undefined;\n try {\n const slug = ((target as () => unknown)() as { slug?: unknown })?.slug;\n return typeof slug === \"string\" ? slug : undefined;\n } catch {\n // A thunk needing a registry this compilation does not have. `text` is\n // the fallback, and a fallback is not worth failing a compile over.\n return undefined;\n }\n}\n\nfunction resolveTargetCollection(\n slug: string | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined\n): CollectionConfig | undefined {\n if (!slug || !resolveCollection) return undefined;\n // A `reference` path may be nested; the collection is its last segment.\n return resolveCollection(slug) ?? resolveCollection(slug.split(\"/\").pop() as string);\n}\n\n/**\n * `NULLIF(rebase.jwt() ->> 'name', '')`, cast to the column's type.\n *\n * Two things here are load-bearing beyond the cast:\n *\n * - **`NULLIF(…, '')`.** An absent claim already reads as NULL, but one set to\n * the empty string does not, and `''::uuid` raises rather than denying. Both\n * spellings of \"this caller has no tenant\" have to reach the comparison as\n * NULL, which is never true and therefore never a grant.\n * - **The guard.** The cast sits inside a `CASE` that first checks the text is\n * well-formed, because `'nonsense'::uuid` raises `invalid input syntax` — and\n * a policy that raises does not deny a row, it fails the whole statement. A\n * caller holding a malformed claim would get a 500 on every read of the table\n * instead of an empty list. `CASE` rather than an `AND` guard because only\n * `CASE` is guaranteed not to evaluate its arms out of order.\n *\n * The whole expression is STABLE (`rebase.jwt()` is), so Postgres evaluates it\n * once per query and can still use a btree index on the column it is compared\n * against — which is the entire reason the cast is on this side.\n */\nfunction authClaimSql(name: string, cast: ClaimCastType): string {\n const claim = `NULLIF(${RLS_JWT_SQL} ->> ${quoteLiteral(name)}, '')`;\n if (cast === \"text\") return claim;\n return `CASE WHEN ${claim} ~ '${CLAIM_CAST_GUARDS[cast]}' THEN (${claim})::${cast} END`;\n}\n\n/**\n * The text a claim must match before it is cast, per target type.\n *\n * The `bigint` guard caps the digit run at 18 rather than matching any run of\n * digits: `'99999999999999999999'::bigint` is a range error, which fails the\n * statement exactly as the syntax error would have.\n */\nconst CLAIM_CAST_GUARDS: Record<Exclude<ClaimCastType, \"text\">, string> = {\n uuid: \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n bigint: \"^-?[0-9]{1,18}$\",\n numeric: \"^-?[0-9]+(\\\\.[0-9]+)?$\"\n};\n\n/**\n * SQL prefix that qualifies a column of the outer RLS row (`\"schema\".\"table\".`),\n * or `\"\"` when the collection is unknown.\n */\nfunction outerQualifier(scope: CompileScope): string {\n const table = scope.outerCollection ? getTableName(scope.outerCollection) : undefined;\n if (!table) return \"\";\n return `\"${schemaOf(scope.outerCollection) ?? \"public\"}\".\"${table}\".`;\n}\n\nfunction schemaOf(collection?: CollectionConfig): string | undefined {\n return (collection as { schema?: string } | undefined)?.schema || undefined;\n}\n\nfunction resolveColumnName(propName: string, collection?: CollectionConfig): string {\n const prop = collection?.properties?.[propName] as Property | undefined;\n if (prop && \"columnName\" in prop && typeof (prop as { columnName?: unknown }).columnName === \"string\") {\n return quoteColumnIdentifier((prop as { columnName: string }).columnName);\n }\n return quoteColumnIdentifier(toSnakeCase(propName));\n}\n\n/**\n * Every PostgreSQL keyword that cannot stand as a bare column reference.\n * Appendix C's two reserved categories — plain \"reserved\", and \"reserved (can\n * be function or type name)\" — since neither may name a column unquoted.\n */\nconst RESERVED_SQL_WORDS = new Set([\n \"all\", \"analyse\", \"analyze\", \"and\", \"any\", \"array\", \"as\", \"asc\", \"asymmetric\", \"authorization\",\n \"binary\", \"both\", \"case\", \"cast\", \"check\", \"collate\", \"collation\", \"column\", \"concurrently\",\n \"constraint\", \"create\", \"cross\", \"current_catalog\", \"current_date\", \"current_role\",\n \"current_schema\", \"current_time\", \"current_timestamp\", \"current_user\", \"default\", \"deferrable\",\n \"desc\", \"distinct\", \"do\", \"else\", \"end\", \"except\", \"false\", \"fetch\", \"for\", \"foreign\", \"freeze\",\n \"from\", \"full\", \"grant\", \"group\", \"having\", \"ilike\", \"in\", \"initially\", \"inner\", \"intersect\",\n \"into\", \"is\", \"isnull\", \"join\", \"lateral\", \"leading\", \"left\", \"like\", \"limit\", \"localtime\",\n \"localtimestamp\", \"natural\", \"not\", \"notnull\", \"null\", \"offset\", \"on\", \"only\", \"or\", \"order\",\n \"outer\", \"overlaps\", \"placing\", \"primary\", \"references\", \"returning\", \"right\", \"select\",\n \"session_user\", \"similar\", \"some\", \"symmetric\", \"system_user\", \"table\", \"tablesample\", \"then\",\n \"to\", \"trailing\", \"true\", \"union\", \"unique\", \"user\", \"using\", \"variadic\", \"verbose\", \"when\",\n \"where\", \"window\", \"with\"\n]);\n\n/** An identifier Postgres reads back unchanged without quotes. */\nconst BARE_IDENTIFIER = /^[a-z_][a-z0-9_$]*$/;\n\n/**\n * Quote a column reference when Postgres would not read the bare name as that\n * column — and only then.\n *\n * Three ways a bare name goes wrong, in ascending order of how long it takes to\n * notice:\n *\n * - **Case.** `columnName` is used verbatim, and `rebase schema introspect`\n * populates it from a live database, so a legacy `\"createdAt\"` column arrives\n * spelled exactly that way. Unquoted, Postgres folds it to `createdat` and\n * `CREATE POLICY` fails with \"column does not exist\" — the collection keeps\n * RLS enabled with no policy, which denies every row.\n * - **Syntax.** A column named `order` or `default` is a syntax error mid-clause.\n * - **Silent rebinding.** `user`, `current_user`, `session_user`, `current_date`\n * and friends are *valid bare expressions*, so the policy compiles, applies,\n * and is reported as a success — while comparing against the connected role\n * or the wall clock instead of the column. Under RLS every request runs as the\n * same `rebase_user` role, so `USING (user = rebase.uid())` is a constant: it\n * denies everything, and its negation admits everything.\n *\n * Only the names that need it are quoted, so an ordinary snake_case policy body\n * is emitted byte-for-byte as before. That keeps generated artifacts and the\n * policies already stored in shipped databases stable — this fix reaches the\n * clauses that were broken and no others.\n */\nfunction quoteColumnIdentifier(name: string): string {\n if (BARE_IDENTIFIER.test(name) && !RESERVED_SQL_WORDS.has(name)) return name;\n return `\"${name.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\nfunction quoteLiteral(value: string | number | boolean | null): string {\n if (value === null) return \"NULL\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") return String(value);\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/** Sorted, single-quoted `ARRAY['a','b']` — matches the generators' output. */\nfunction rolesArraySql(roles: readonly string[]): string {\n return `ARRAY[${[...roles].sort().map(r => `'${r}'`).join(\",\")}]`;\n}\n","import { ANONYMOUS_USER_ID, isAnonymousUid, Entity, PolicyCompareOperator, PolicyExpression, PolicyOperand } from \"@rebasepro/types\";\n\n/**\n * Result of evaluating a policy client-side. `\"unknown\"` means the expression\n * could not be decided without more information — either a raw-SQL escape-hatch\n * node (which the client deliberately never guesses) or a row-column reference\n * with no entity in hand (e.g. list-level gating). Callers decide how to resolve\n * `\"unknown\"`: fail-closed for an enforcement decision, optimistic for pure\n * visibility gating.\n */\nexport type TriState = boolean | \"unknown\";\n\n/**\n * Context for {@link evaluatePolicy}: the acting user (or none) and the row\n * being evaluated (or none, for collection-level gating).\n */\nexport interface PolicyEvalContext {\n /**\n * The current user's id, or null/undefined when no user is signed in.\n *\n * Null here means *anonymous visitor*, not \"server context\" — a client is\n * never the server context. `authUid` operands therefore resolve to\n * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`\n * the database would see for the same request.\n */\n uid?: string | null;\n /** The current user's application roles. */\n roles?: string[];\n /**\n * Whether this session is a GUEST — anonymous sign-in rather than an\n * account. Optional, and absent means \"not a guest\", so a caller that does\n * not know keeps the behaviour it had.\n */\n isAnonymous?: boolean;\n /** The row being evaluated, or null when no specific row is available. */\n entity: Entity | null;\n}\n\n/**\n * Evaluates a {@link PolicyExpression} against a user + row, using three-valued\n * (Kleene) logic so that `\"unknown\"` sub-results propagate soundly.\n *\n * This is the JavaScript twin of {@link policyToPostgres}: both derive from the\n * same expression, so the admin UI matches database enforcement by construction\n * for every non-raw rule.\n */\nexport function evaluatePolicy(expr: PolicyExpression, ctx: PolicyEvalContext): TriState {\n switch (expr.kind) {\n case \"true\":\n return true;\n case \"false\":\n return false;\n case \"and\":\n return kleeneAnd(expr.operands.map(o => evaluatePolicy(o, ctx)));\n case \"or\":\n return kleeneOr(expr.operands.map(o => evaluatePolicy(o, ctx)));\n case \"not\":\n return kleeneNot(evaluatePolicy(expr.operand, ctx));\n case \"compare\":\n return evaluateCompare(expr.op, expr.left, expr.right, ctx);\n case \"rolesOverlap\": {\n const userRoles = ctx.roles ?? [];\n return expr.roles.some(r => r === \"public\" || userRoles.includes(r));\n }\n case \"rolesContain\": {\n const userRoles = ctx.roles ?? [];\n return expr.roles.every(r => r === \"public\" || userRoles.includes(r));\n }\n case \"authenticated\":\n // Every anonymous spelling, matching what this node compiles to in\n // Postgres — the two evaluators disagreeing about who is signed in\n // is the client optimistically rendering a row the database will\n // refuse, or hiding one it would have allowed.\n return ctx.uid != null && !isAnonymousUid(ctx.uid);\n case \"registered\":\n // The same two halves the Postgres compilation has. A client that\n // disagreed with the database here would optimistically render a\n // row the database refuses, or hide one it would have allowed.\n return ctx.uid != null && !isAnonymousUid(ctx.uid) && ctx.isAnonymous !== true;\n case \"serverContext\":\n // A client is never the server context. Postgres decides this by\n // `rebase.uid() IS NULL`, which a client request can never produce:\n // the driver substitutes ANONYMOUS_USER_ID for a missing id.\n return false;\n case \"existsIn\":\n // A membership subquery cannot be run client-side — server-authoritative.\n return \"unknown\";\n case \"raw\":\n // Arbitrary SQL cannot be evaluated client-side — never guess.\n return \"unknown\";\n }\n}\n\n// ── Three-valued logic ───────────────────────────────────────────────\n\nfunction kleeneAnd(values: TriState[]): TriState {\n if (values.some(v => v === false)) return false;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return true;\n}\n\nfunction kleeneOr(values: TriState[]): TriState {\n if (values.some(v => v === true)) return true;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return false;\n}\n\nfunction kleeneNot(value: TriState): TriState {\n if (value === \"unknown\") return \"unknown\";\n return !value;\n}\n\n// ── Comparison ───────────────────────────────────────────────────────\n\ntype ResolvedOperand = { known: false } | { known: true; value: unknown };\n\nfunction resolveOperand(operand: PolicyOperand, ctx: PolicyEvalContext): ResolvedOperand {\n switch (operand.kind) {\n case \"literal\":\n return { known: true, value: operand.value };\n case \"authUid\":\n // The sentinel, not null: `rebase.uid()` is never NULL for a request\n // that came from a client, so comparing against null here would\n // disagree with the database on exactly the rules that test for it\n // (e.g. `rebase.uid() <> 'anonymous'`).\n return { known: true, value: ctx.uid ?? ANONYMOUS_USER_ID };\n case \"authRoles\":\n return { known: true, value: ctx.roles ?? [] };\n case \"field\":\n // Can't resolve a row column without the row.\n if (!ctx.entity) return { known: false };\n return { known: true, value: ctx.entity.values[operand.name] };\n case \"outerField\":\n // Only meaningful inside an `existsIn` subquery (server-authoritative).\n return { known: false };\n case \"authClaim\":\n // Server-authoritative, like `existsIn` and `raw`, and for a reason\n // worth stating: a claim is TEXT, and Postgres compares it to the\n // column after casting it to the column's type. Reproducing that\n // here means reproducing uuid case-folding, numeric widening and\n // the `NULLIF`, in JavaScript, from a `PolicyEvalContext` that does\n // not know the column's type. A second implementation of a cast\n // that is subtly wrong is worse than no answer: it would render a\n // row the database refuses, or hide one it would have allowed.\n return { known: false };\n }\n}\n\nfunction evaluateCompare(\n op: PolicyCompareOperator,\n left: PolicyOperand,\n right: PolicyOperand,\n ctx: PolicyEvalContext\n): TriState {\n const l = resolveOperand(left, ctx);\n const r = resolveOperand(right, ctx);\n if (!l.known || !r.known) return \"unknown\";\n\n const a = l.value;\n const b = r.value;\n\n if (a === null || b === null) {\n // SQL answers NULL for *every* comparison against NULL, and a policy\n // that answers NULL does not grant the row. So the only question here\n // is which JavaScript answer reproduces that outcome.\n //\n // This used to answer `false` for `eq` and `true` for `neq`, which is\n // JavaScript's two-valued reading of a three-valued question.\n //\n // `neq` was a grant the database does not give:\n // `owner_id != rebase.uid()` on a row whose `owner_id` is NULL read as\n // *permitted* in the admin panel and was refused by Postgres — on every\n // row where the column is null, which for a nullable column is usually\n // most of them.\n //\n // `false` for `eq` looked safe, because false denies and NULL denies.\n // It is not, because it does not survive negation: `not(a = NULL)`\n // became `true` while `NOT NULL` stays NULL, so the same grant reappears\n // one operator up. A local answer that is only right in a positive\n // position is not right — it just moves.\n //\n // \"unknown\" is what SQL actually says, it composes correctly through\n // Kleene negation, and enforcement callers already resolve it\n // fail-closed. Both were found by the exhaustive Postgres differential\n // in `policy-agreement-exhaustive.test.ts`, the second only after the\n // first was fixed.\n return \"unknown\";\n }\n\n if (op === \"eq\") return a === b;\n if (op === \"neq\") return a !== b;\n\n if (typeof a === \"string\" && typeof b === \"string\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n if (typeof a === \"number\" && typeof b === \"number\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n if (typeof a === \"bigint\" && typeof b === \"bigint\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n return \"unknown\";\n}\n","import { AuthState, Entity, CollectionConfig, SecurityOperation, SecurityRule, User } from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"./policy/securityRuleToConditions\";\nimport { evaluatePolicy, PolicyEvalContext, TriState } from \"./policy/evaluatePolicy\";\n\n/**\n * Minimal auth context for permission checking.\n * Only requires the user object — avoids forcing callers to construct\n * a full AuthController just to check permissions.\n *\n * An alias, not a second definition: {@link AuthState} in `@rebasepro/types` is\n * the same shape and is what `dynamicProps` and the JSON-Logic condition context\n * now take, so declaring it twice would be the `WhereFilterOp` mistake again —\n * two copies that agree only by luck.\n */\nexport type AuthContext<USER extends User = User> = AuthState<USER>;\n\n/**\n * How to resolve a policy result that cannot be decided client-side (a raw-SQL\n * escape-hatch rule, or a row-column reference with no row in hand).\n *\n * - `\"allow\"` (default): optimistic — used for admin-UI gating, where Postgres\n * remains the authoritative gate and hiding a working action is worse than\n * showing one the server may reject.\n * - `\"deny\"`: fail-closed — used by real enforcement callers (e.g. a driver\n * applying policies in-process), so an undecidable rule never silently allows.\n */\nexport type UnknownResolution = \"allow\" | \"deny\";\n\n/**\n * Which half of a rule to evaluate.\n *\n * Postgres evaluates `USING` against the row as it is *now* and `WITH CHECK`\n * against the row as it *will be*, both inside the transaction. A driver\n * enforcing an update in-process has two different rows in hand and therefore\n * needs to ask the two questions separately — asking one question about one row\n * either checks the new values against the old row's ownership or the reverse.\n *\n * - `\"both\"` (default): what a single-row decision means (`USING ∧ WITH CHECK`).\n * - `\"using\"`: the read/target clause only — ask it about the stored row.\n * - `\"withCheck\"`: the write clause only — ask it about the row being written.\n *\n * Rule *selection* is unaffected: the target operation still decides which rules\n * apply, so `\"using\"` on an `update` evaluates the update rules' USING clause,\n * not the delete rules'.\n */\nexport type PolicyClauses = \"both\" | \"using\" | \"withCheck\";\n\nexport interface CheckOperationOptions {\n onUnknown?: UnknownResolution;\n clauses?: PolicyClauses;\n}\n\n/** Combine clause results with AND under three-valued (Kleene) logic. */\nfunction kleeneAnd(values: TriState[]): TriState {\n if (values.some(v => v === false)) return false;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return true;\n}\n\n/** The operations a rule covers, mirroring the Postgres generator's resolution. */\nfunction ruleOperations(rule: SecurityRule): readonly SecurityOperation[] {\n return rule.operations && rule.operations.length > 0\n ? rule.operations\n : [rule.operation ?? \"all\"];\n}\n\nfunction ruleApplies(rule: SecurityRule, targetOperation: SecurityOperation): boolean {\n const ops = ruleOperations(rule);\n return ops.includes(targetOperation) || ops.includes(\"all\");\n}\n\n/**\n * Evaluate a single rule for one operation, returning a tri-state.\n *\n * A `null` clause (the rule contributes no condition for a required clause)\n * denies — matching Postgres, which emits `USING (false)` / `WITH CHECK (false)`\n * in that case. USING applies to SELECT/UPDATE/DELETE; WITH CHECK to\n * INSERT/UPDATE; both must pass for UPDATE.\n */\nfunction evaluateRuleForOperation(\n rule: SecurityRule,\n ctx: PolicyEvalContext,\n targetOperation: SecurityOperation,\n clauses: PolicyClauses = \"both\"\n): TriState {\n const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);\n const clause = (expr: typeof usingExpr): TriState => expr === null ? false : evaluatePolicy(expr, ctx);\n\n const needsUsing = targetOperation !== \"insert\" && clauses !== \"withCheck\";\n const needsWithCheck = (targetOperation === \"insert\" || targetOperation === \"update\") && clauses !== \"using\";\n\n const results: TriState[] = [];\n if (needsUsing) results.push(clause(usingExpr));\n if (needsWithCheck) results.push(clause(withCheckExpr));\n return kleeneAnd(results);\n}\n\nfunction resolveTriState(value: TriState, onUnknown: UnknownResolution): boolean {\n if (value === \"unknown\") return onUnknown === \"allow\";\n return value;\n}\n\n/**\n * Decide whether an operation is permitted for a user on a (possibly null) row,\n * by evaluating the collection's security rules with the shared policy model —\n * the same model compiled to Postgres RLS DDL, so the decision matches database\n * enforcement for every non-raw rule.\n *\n * Engine-independent by design. `securityRules` are a declaration about the\n * data, not about Postgres: the engine decides *who* enforces them (Postgres\n * compiles them to RLS DDL, a document driver applies them in-process), never\n * *whether* they hold. Gating this function on the engine's `supportsRLS`\n * capability is what made every `{ onUnknown: \"deny\" }` call site in the Mongo\n * driver return `true` before it evaluated anything — and it did so only for\n * collections that spelled their engine out, so declaring `engine: \"mongodb\"`\n * was what switched authorization off.\n *\n * @param options.onUnknown how to treat rules that cannot be decided\n * client-side (raw SQL, or row predicates with no row). Defaults to `\"allow\"`\n * for optimistic UI gating; enforcement callers should pass `\"deny\"`.\n * @param options.clauses which half of each rule to evaluate. See\n * {@link PolicyClauses}; defaults to `\"both\"`.\n */\nexport function checkOperation<M extends Record<string, unknown>, USER extends User>(\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n entity: Entity<M> | null,\n targetOperation: SecurityOperation,\n options?: CheckOperationOptions\n): boolean {\n const onUnknown = options?.onUnknown ?? \"allow\";\n const clauses = options?.clauses ?? \"both\";\n const securityRules = collection.securityRules;\n if (!securityRules || securityRules.length === 0) {\n return true;\n }\n\n const applicableRules = securityRules.filter((r: SecurityRule) => ruleApplies(r, targetOperation));\n if (applicableRules.length === 0) return false;\n\n const ctx: PolicyEvalContext = {\n uid: authContext.user?.uid,\n roles: authContext.user?.roles ?? [],\n entity\n };\n\n let grantedByPermissive = false;\n let deniedByRestrictive = false;\n let hasPermissive = false;\n\n for (const rule of applicableRules) {\n const mode = rule.mode || \"permissive\";\n const passed = resolveTriState(evaluateRuleForOperation(rule, ctx, targetOperation, clauses), onUnknown);\n\n if (mode === \"restrictive\") {\n if (!passed) {\n deniedByRestrictive = true;\n break;\n }\n } else {\n hasPermissive = true;\n if (passed) grantedByPermissive = true;\n }\n }\n\n if (deniedByRestrictive) return false;\n return hasPermissive ? grantedByPermissive : false;\n}\n\nexport function canReadCollection<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>\n ): boolean {\n return checkOperation(collection, authContext, null, \"select\");\n}\n\nexport function canEditEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"update\");\n}\n\nexport function canCreateEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"insert\");\n}\n\nexport function canDeleteEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"delete\");\n}\n","import {\n CollectionConfig,\n FirebaseCollectionConfig,\n FirebaseProperties,\n FirebaseProperty,\n InferEntityType,\n MongoDBCollectionConfig,\n MongoProperties,\n MongoProperty,\n PostgresCollectionConfig,\n PostgresProperties,\n PostgresProperty,\n Properties,\n Property,\n StrictProperties,\n User,\n resolveResourceRefs,\n type ResourceRef\n} from \"@rebasepro/types\";\n\n\n// ── defineCollection ─────────────────────────────────────────────────────\n// A smarter builder that uses `const` type-parameter inference (TS 5.0+)\n// to capture literal property types automatically. This gives you\n// autocomplete on `display.title`, `sort`, `propertiesOrder`, `fixedFilter`,\n// callbacks, etc. — without writing `as const` or passing manual generics.\n\n/** The engines a collection can declare. `postgres` when it says nothing. */\ntype CollectionEngine = \"postgres\" | \"firestore\" | \"mongodb\";\n\n/**\n * The concrete collection type an `engine` selects.\n *\n * This builder used to be three overloads — one per engine — and overload\n * resolution is what made its errors unreadable. When no overload matches,\n * TypeScript emits **one** diagnostic at the call site listing each overload's\n * *first* failure, so a misspelled key on a Postgres collection came back as\n * three paragraphs of `No overload matches this call. Overload 1 of 3 … Overload\n * 3 of 3, '(collection: Omit<MongoDBCollectionConfig<…>>)'` — pointing at\n * `defineCollection(` and blaming a database the project does not use.\n *\n * One signature, with the engine as a type parameter, reports the error at the\n * key instead. Same fix as `@rebasepro/cms-types`, and deliberately the same\n * shape: this is the builder a headless (`--headless`) scaffold, `rebase schema\n * introspect` output and the example app's own collections use, so the two must\n * not diverge.\n */\ntype CollectionConfigForEngine<E, P, USER extends User> =\n E extends \"firestore\" ? FirebaseCollectionConfig<EntityShapeOf<P>, USER>\n : E extends \"mongodb\" ? MongoDBCollectionConfig<EntityShapeOf<P>, USER>\n : PostgresCollectionConfig<EntityShapeOf<P>, USER>;\n\n/**\n * `InferEntityType`, tolerant of a property map that has an error in it.\n *\n * The key set has to survive a bad property, or one mistake hides every other\n * check that reads it. See `KEYS` on the signature below.\n */\ntype EntityShapeOf<P> = InferEntityType<{\n [K in keyof P]: P[K] extends Property ? P[K] : Property;\n}>;\n\n/** The property union an engine admits — the engine gate, as a type. */\ntype PropertyForEngine<E> =\n E extends \"firestore\" ? FirebaseProperty\n : E extends \"mongodb\" ? MongoProperty\n : PostgresProperty;\n\n/** {@link PropertyForEngine} as a property map, for the `P` constraint. */\ntype PropertiesForEngine<E> =\n E extends \"firestore\" ? FirebaseProperties\n : E extends \"mongodb\" ? MongoProperties\n : PostgresProperties;\n\n/**\n * Define a collection with full type inference. Postgres unless `engine` says\n * otherwise.\n *\n * The `const P` generic captures literal property types from your\n * `properties` object, so every key that names a property — a security rule's\n * `ownerField`, a relation's `localKey`, an entity callback's `value` — is\n * checked against the collection's own property names rather than `string`.\n *\n * This is the builder for a project with no admin panel. One with an admin\n * panel wants `defineCollection` from `@rebasepro/cms-types`, which is the same\n * function with the `admin` block type-checked.\n *\n * @example\n * ```ts\n * const products = defineCollection({\n * name: \"Products\",\n * slug: \"products\",\n * table: \"products\",\n * properties: {\n * name: { name: \"Name\", type: \"string\", validation: { required: true } },\n * price: { name: \"Price\", type: \"number\" },\n * },\n * securityRules: [{ operation: \"select\", access: \"public\" }]\n * });\n * ```\n *\n * @group Builder\n */\nexport function defineCollection<\n const E extends CollectionEngine = \"postgres\",\n /**\n * The properties, **constrained**. This is what checks them, and what\n * supplies the contextual type inside them: without a constraint the\n * parameter of an inline `callbacks: { beforeSave: ({ value }) => … }` has\n * nothing to be typed from, and TypeScript reports an implicit `any` on a\n * callback the author wrote correctly.\n */\n const P extends PropertiesForEngine<E> & Properties = PropertiesForEngine<E> & Properties,\n /**\n * The properties again, **unconstrained**, and this is why there are two.\n *\n * A constraint TypeScript cannot satisfy is one it silently falls back\n * from: one property with a bad `defaultValue` made `P` become\n * `PostgresProperties`, the entity shape become `Record<string, unknown>`,\n * and every key that is checked against the property names — `display.title`,\n * `propertiesOrder`, `sort` — widen to `string` and stop being checked.\n *\n * `KEYS` has no constraint to fall back from, so `keyof KEYS` survives a bad\n * property and the rest of the collection is still checked against the real\n * key set.\n */\n const KEYS = Properties,\n USER extends User = User\n>(\n collection: Omit<CollectionConfigForEngine<E, KEYS, USER>, \"properties\" | \"engine\" | \"dataSource\">\n & {\n engine?: E;\n properties: StrictProperties<P, PropertyForEngine<E>> & KEYS;\n dataSource?: ResourceRef;\n }\n): CollectionConfigForEngine<E, KEYS, USER> & { properties: KEYS };\n\n/**\n * At runtime this is a plain identity function: a resource handle written where\n * a key belongs — `dataSource: analytics` — becomes its key, so past this point\n * a collection is plain data. The signature above is the rest of the point.\n * @group Builder\n */\nexport function defineCollection(\n collection: Omit<CollectionConfig, \"dataSource\"> & { dataSource?: ResourceRef }\n): CollectionConfig {\n return resolveResourceRefs(collection) as CollectionConfig;\n}\n\n","import { ArrayProperty, EntityValues, StorageConfig, StorageSource, StorageSourceRegistry, StringProperty, UploadedFileContext } from \"@rebasepro/types\";\nimport { randomString } from \"@rebasepro/utils\";\n\n/**\n * Resolve the {@link StorageSource} to use for a property, given the key\n * referenced by `StorageConfig.storageSource`.\n *\n * Resolution priority:\n * 1. No `sourceKey` → the default source (backward compatible).\n * 2. An explicit {@link StorageSourceRegistry} (e.g. `client.storageRegistry`).\n * 3. A `sources` lookup map (e.g. the `StorageSourcesContext`).\n * 4. Fall back to the default source.\n *\n * Shared by the upload hook, the markdown editor, and the read-only previews\n * so the resolution logic lives in one place.\n *\n * @group Storage\n */\nexport function resolveStorageSource(params: {\n /** Key from `StorageConfig.storageSource`. */\n sourceKey?: string | null;\n /** Built sources keyed by storage-source key (e.g. from context). */\n sources?: Record<string, StorageSource>;\n /** Optional explicit registry — takes precedence over `sources`. */\n registry?: StorageSourceRegistry;\n /** Default source, used when no key is set or the key cannot be resolved. */\n defaultSource: StorageSource;\n}): StorageSource {\n const { sourceKey, sources, registry, defaultSource } = params;\n if (!sourceKey) return defaultSource;\n if (registry) return registry.getOrDefault(sourceKey);\n const fromSources = sources?.[sourceKey];\n if (fromSources) return fromSources;\n return defaultSource;\n}\n\ninterface ResolveFilenameStringParams<M extends Record<string, unknown>> {\n input: string | ((context: UploadedFileContext) => (Promise<string> | string));\n storage: StorageConfig;\n values: EntityValues<M>;\n entityId?: string | number;\n path?: string;\n property: StringProperty | ArrayProperty,\n file: File;\n propertyKey: string;\n}\n\nexport async function resolveStorageFilenameString<M extends Record<string, unknown>>(\n {\n input,\n storage,\n values,\n entityId,\n path,\n property,\n file,\n propertyKey\n }: ResolveFilenameStringParams<M>): Promise<string> {\n let result;\n\n if (typeof input === \"function\") {\n result = await input({\n path,\n entityId,\n values,\n property,\n file,\n storage,\n propertyKey\n });\n if (!result)\n console.warn(\"Storage callback returned empty result. Using default name value\")\n } else {\n result = replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n });\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n\ninterface ResolveStoragePathStringParams<M extends Record<string, unknown>> {\n input: string | ((context: UploadedFileContext) => string);\n storage: StorageConfig;\n values: EntityValues<M>;\n entityId?: string | number;\n path?: string;\n property: StringProperty | ArrayProperty;\n file: File;\n propertyKey: string;\n}\n\nexport function resolveStoragePathString<M extends Record<string, unknown>>(\n {\n input,\n storage,\n values,\n entityId,\n path,\n property,\n file,\n propertyKey\n }: ResolveStoragePathStringParams<M>): string {\n let result;\n if (typeof input === \"function\") {\n result = input({\n path,\n entityId,\n values,\n property,\n file,\n storage,\n propertyKey\n });\n if (!result)\n console.warn(\"Storage callback returned empty result. Using default name value\")\n } else {\n result = replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n });\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n\ninterface Placeholders {\n file: File;\n input: string;\n entityId?: string | number;\n propertyKey: string;\n path?: string;\n}\n\nfunction replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n}: Placeholders) {\n const ext = file.name.split(\".\").pop();\n let result = input\n .replace(\"{propertyKey}\", propertyKey)\n .replace(\"{rand}\", randomString())\n .replace(\"{file}\", file.name)\n .replace(\"{file.type}\", file.type);\n if (entityId) {\n result = result.replace(\"{entityId}\", String(entityId));\n }\n if (path) {\n result = result.replace(\"{path}\", path);\n }\n if (ext) {\n result = result.replace(\"{file.ext}\", ext);\n const name = file.name.replace(`.${ext}`, \"\");\n result = result.replace(\"{file.name}\", name)\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n","import { CollectionCallbacks, Properties, RebaseCallContext } from \"@rebasepro/types\";\n\n/**\n * Context passed to entity lifecycle callbacks.\n * @group Models\n */\nexport type EntityCallbackContext = RebaseCallContext;\n\n\n/**\n * Helper function to recursively check if there are any callbacks in the properties.\n */\nfunction hasPropertyCallbacks(properties: Properties, callbackName: \"afterRead\" | \"beforeSave\"): boolean {\n if (!properties) return false;\n for (const property of Object.values(properties)) {\n if (property.callbacks?.[callbackName]) return true;\n if (property.type === \"map\" && property.properties) {\n if (hasPropertyCallbacks(property.properties, callbackName)) return true;\n } else if (property.type === \"array\" && property.of) {\n const ofs = Array.isArray(property.of) ? property.of : [property.of];\n for (const of of ofs) {\n if (of.callbacks?.[callbackName]) return true;\n if (of.type === \"map\" && of.properties && hasPropertyCallbacks(of.properties, callbackName)) return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Recursively process properties to apply field-level hooks.\n */\nasync function processProperties(\n properties: Properties,\n values: Record<string, unknown>,\n previousValues: Record<string, unknown>,\n propsContext: unknown,\n callbackName: \"afterRead\" | \"beforeSave\"\n): Promise<Record<string, unknown>> {\n if (!values || typeof values !== \"object\") return values;\n\n const result = { ...values };\n\n for (const [key, property] of Object.entries(properties)) {\n if (result[key] === undefined) continue;\n\n let currentValue = result[key];\n const previousValue = previousValues?.[key];\n\n // 1. Array Property\n if (property.type === \"array\" && Array.isArray(currentValue)) {\n // We only support traversing single-type arrays for hooks currently to avoid complex union matching\n if (property.of && !Array.isArray(property.of)) {\n currentValue = await Promise.all(currentValue.map(async (item, index) => {\n const prevItem = Array.isArray(previousValue) ? previousValue[index] : undefined;\n // Mock a properties object to process a single item\n const singlePropData = { \"_tmp\": property.of } as Properties;\n const res = await processProperties(singlePropData, { \"_tmp\": item }, { \"_tmp\": prevItem }, propsContext, callbackName);\n return res[\"_tmp\"];\n }));\n }\n }\n // 2. Map Property\n else if (property.type === \"map\" && property.properties && typeof currentValue === \"object\") {\n currentValue = await processProperties(property.properties, currentValue as Record<string, unknown>, (previousValue ?? {}) as Record<string, unknown>, propsContext, callbackName);\n }\n\n // 3. Property's own callback\n if (property.callbacks?.[callbackName]) {\n\n const cbRes = await Promise.resolve(property.callbacks[callbackName]({\n ...(propsContext as Record<string, unknown>),\n value: currentValue,\n previousValue\n } as never));\n if (cbRes !== undefined) {\n currentValue = cbRes;\n }\n }\n\n result[key] = currentValue;\n }\n return result;\n}\n\n/**\n * Helper function to extract field-level PropertyCallbacks from a properties schema\n * and wrap them into an CollectionCallbacks object recursively.\n */\nexport const buildPropertyCallbacks = (properties: Properties): CollectionCallbacks | undefined => {\n if (!properties) return undefined;\n\n const propertyCallbacks: CollectionCallbacks = {};\n\n if (hasPropertyCallbacks(properties, \"afterRead\")) {\n propertyCallbacks.afterRead = async (props) => {\n const row = props.row;\n const processedValues = await processProperties(\n properties,\n row,\n row,\n props as unknown,\n \"afterRead\"\n );\n return { ...props.row, ...processedValues };\n };\n }\n\n if (hasPropertyCallbacks(properties, \"beforeSave\")) {\n propertyCallbacks.beforeSave = async (props) => {\n return await processProperties(\n properties,\n props.values as Record<string, unknown>,\n (props.previousValues ?? {}) as Record<string, unknown>,\n props as unknown,\n \"beforeSave\"\n );\n };\n }\n\n return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : undefined;\n};\n","import { RebaseApiError } from \"@rebasepro/types\";\n\n/**\n * The code a write carries when a collection callback rejected it and did not\n * say how. Distinct from `INVALID_INPUT`, which the framework's own validation\n * raises: this one means *your* rule refused, so the message is the author's.\n *\n * `details.stage` names which callback refused — `beforeSave`, `beforeDelete`,\n * `afterSave` or `afterDelete`. An `after*` hook runs inside the write's\n * transaction, so a throw there rolls the row back too; the caller is told the\n * write did not happen and which hook decided that.\n */\nexport const CALLBACK_REJECTED = \"CALLBACK_REJECTED\";\n\n/**\n * Turn whatever a user callback threw into something the API layer can answer\n * with.\n *\n * ### Why a plain `throw` has to mean 400\n *\n * Both `docs/collections/callbacks.md` (\"Throw an error to **block the save**\")\n * and `docs/backend/hooks.md` (\"the operation is rejected with an HTTP 400\n * error response\") promised this, and neither delivered it: an `Error` thrown\n * from `beforeSave` reached the client as\n *\n * 500 {\"error\":{\"message\":\"Internal Server Error\",\"code\":\"INTERNAL_ERROR\"}}\n *\n * with the author's message visible only in the server log, because the error\n * normalizer masks 5xx bodies — correctly, since a 500 is by definition\n * something the caller must not be told about.\n *\n * But a callback is not the server failing. It is the application speaking, in\n * code its author wrote, about a request its author judged invalid. The\n * conservative reading — \"an unrecognised throw might be a real bug, so 500\" —\n * costs every validation rule its message and makes the documented example\n * wrong. A rule that wants a 500 can still raise one explicitly.\n *\n * ### Why `after*` comes through here too\n *\n * `afterSave` and `afterDelete` run inside the write's transaction and are\n * awaited, so a throw in one aborts the transaction: the row is not there when\n * the request ends. Left unconverted, the caller saw a 500 for a write that a\n * rule deliberately undid, and had no way to tell that from a database outage.\n * Converted, it is the same 400 `CALLBACK_REJECTED` a `before*` hook produces,\n * with `stage` naming the hook that refused.\n *\n * ### What passes through untouched\n *\n * Anything that already carries a status: `RebaseApiError` from\n * `@rebasepro/types` (the browser-safe class a `config/collections/*.ts` file\n * can import — the collection file is bundled into the admin SPA, so it may not\n * import the server package), and the server's own `ApiError`, recognised\n * structurally rather than by `instanceof` because a monorepo can resolve two\n * copies of a package and `instanceof` is false across them.\n *\n * @param error What the callback threw.\n * @param stage The callback name, for the log line.\n * @param path The collection path, for the log line.\n */\nexport function toCallbackError(error: unknown, stage: string, path: string): unknown {\n if (error !== null && typeof error === \"object\") {\n const carried = error as { status?: unknown; statusCode?: unknown };\n // Already an answerable HTTP outcome — the author chose the status.\n if (typeof carried.statusCode === \"number\" || typeof carried.status === \"number\") {\n return error;\n }\n }\n\n const message = error instanceof Error\n ? error.message\n : typeof error === \"string\" ? error : `${stage} rejected the write`;\n\n return new RebaseApiError(message, {\n status: 400,\n code: CALLBACK_REJECTED,\n details: { stage, path },\n cause: error\n });\n}\n\n/**\n * The refusal a callback expresses by returning `false` rather than throwing.\n *\n * `beforeDelete` is typed `boolean | void` and documented as \"return false or\n * throw to block deletion\". Returning `false` did stop the delete — and then the\n * route answered `204 No Content`, which says the row is gone. The admin panel\n * removed it from the list, a client that trusted the status dropped it from its\n * cache, and the next reload brought it back. A veto that reports success is\n * worse than no veto.\n *\n * 403, not the 400 a throw produces: a throw carries the author's message and\n * reads as \"this input is wrong\", while `false` is a flat refusal with no\n * explanation — the server understood the request and will not perform it. The\n * code is the same either way, so a client can handle both in one branch.\n *\n * @param stage The callback name, for `details.stage`.\n * @param path The collection path, for `details.path`.\n */\nexport function callbackRefusal(stage: string, path: string): RebaseApiError {\n return new RebaseApiError(`${stage} refused the operation`, {\n status: 403,\n code: CALLBACK_REJECTED,\n details: { stage, path }\n });\n}\n","/**\n * The one reading of `collection.tenant`.\n *\n * Four things have to agree for a tenant-scoped collection to work — the\n * column, the RLS policy, the value stamped on insert and the index — and\n * before this they were four hand-written declarations that nothing compared.\n * The three that are schema become a {@link SecurityRule} and a column effect\n * derived here and in `planSchema`; the fourth, the write path, is\n * {@link resolveTenantWrite}.\n *\n * Everything in this module is pure. The policy it builds is a `SecurityRule`\n * like any other, which is what makes `db push`, the doctor, boot-ensure, the\n * drift detector and the Studio treat the tenancy policy as what it is —\n * generated, named, and recognisable — rather than as somebody's hand-written\n * SQL that a push should offer to drop.\n */\nimport {\n DEFAULT_TENANT_BYPASS_ROLES,\n isTenantClaimSource,\n policy,\n type CollectionConfig,\n type CollectionTenantConfig,\n type EntityStatus,\n type PolicyExpression,\n type SecurityRule\n} from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\n\n/**\n * The collection's tenancy declaration, or nothing.\n *\n * Read through this rather than off the object, so the one shape check —\n * `tenant` is an object carrying a `field` and a `from` — is in one place. A\n * config that is *wrong* is refused by `validateCollectionConfig` with a\n * message; this is only asking whether there is one.\n */\nexport function getTenantConfig(collection: CollectionConfig | undefined): CollectionTenantConfig | undefined {\n const tenant = (collection as { tenant?: unknown } | undefined)?.tenant as CollectionTenantConfig | undefined;\n if (!tenant || typeof tenant !== \"object\") return undefined;\n if (typeof tenant.field !== \"string\" || !tenant.field) return undefined;\n if (!tenant.from || typeof tenant.from !== \"object\") return undefined;\n return tenant;\n}\n\n/** The roles tenancy does not apply to, defaulted. */\nexport function tenantBypassRoles(tenant: CollectionTenantConfig): readonly string[] {\n return tenant.bypassRoles ?? DEFAULT_TENANT_BYPASS_ROLES;\n}\n\n/**\n * The name of the policy a tenant declaration compiles to.\n *\n * Explicit — not a `getPolicyNameHash` of the rule — precisely because the\n * rule's *body* is compiled with more information in some callers than in\n * others (`planSchema` can resolve a relation's target collection and so knows\n * the column's type; the Studio, asking only for names, cannot). A hashed name\n * would then differ between the two, and the same policy would read as drift.\n * A frozen identifier: see `contracts/derived-names.txt`.\n */\nexport function tenantPolicyName(tableName: string): string {\n return `${tableName}_tenant_scope`;\n}\n\n/** The `reason` on the index a tenant column gets. Rendered into `schema.sql`. */\nexport const TENANT_INDEX_REASON = \"tenant scope\";\n\n/**\n * The condition a tenant declaration means, as a policy expression.\n *\n * `serverContext()` first, for the same reason every injected baseline rule\n * carries it: the trusted plane runs migrations, the auth flows and the boot,\n * and a restrictive policy that excluded it would not protect a tenant, it\n * would stop the server from starting.\n *\n * Then the bypass roles, then the tenancy test itself — a claim comparison or a\n * correlated `EXISTS` over the membership table, which are the two ways a\n * deployment answers \"which tenant is this caller in\".\n */\nexport function tenantScopeExpression(tenant: CollectionTenantConfig): PolicyExpression {\n const match: PolicyExpression = isTenantClaimSource(tenant.from)\n ? policy.compare(policy.field(tenant.field), \"eq\", policy.authClaim(tenant.from.claim))\n : policy.existsIn({\n collection: tenant.from.membership.collection,\n where: policy.and(\n policy.compare(\n policy.field(tenant.from.membership.tenantField),\n \"eq\",\n policy.outerField(tenant.field)\n ),\n policy.compare(\n policy.field(tenant.from.membership.userField),\n \"eq\",\n policy.authUid()\n )\n )\n });\n\n const bypass = tenantBypassRoles(tenant);\n return bypass.length > 0\n ? policy.or(policy.serverContext(), policy.rolesOverlap(bypass), match)\n : policy.or(policy.serverContext(), match);\n}\n\n/**\n * The rule a tenant declaration compiles to, or nothing when there is none.\n *\n * **Restrictive**, and that is the whole design. A restrictive policy is ANDed\n * with every other policy on the table, so tenancy narrows what the\n * collection's own `securityRules` allow and can never widen it. A permissive\n * one would OR with them, and a single `access: \"public\"` rule elsewhere in the\n * file would take the entire tenancy boundary off without contradicting\n * anything a reader could see.\n *\n * One rule with `operation: \"all\"` rather than four with `operations: [...]`:\n * `FOR ALL` gives Postgres the USING clause for SELECT/UPDATE/DELETE and the\n * WITH CHECK clause for INSERT/UPDATE, which is exactly the coverage wanted,\n * as one policy with one name instead of four.\n */\nexport function buildTenantSecurityRule(collection: CollectionConfig): SecurityRule | undefined {\n const tenant = getTenantConfig(collection);\n if (!tenant) return undefined;\n const expression = tenantScopeExpression(tenant);\n return {\n name: tenantPolicyName(getTableName(collection)),\n mode: \"restrictive\",\n operation: \"all\",\n condition: expression,\n check: expression\n };\n}\n\n// ── The write path ───────────────────────────────────────────────────────────\n\n/** Why a write was refused by tenancy. */\nexport interface TenantWriteRefusal {\n code: \"TENANT_REQUIRED\" | \"TENANT_MISMATCH\" | \"TENANT_IMMUTABLE\";\n /** The property, for a `violations` entry and for the message. */\n field: string;\n message: string;\n}\n\n/** What {@link resolveTenantWrite} decided. */\nexport type TenantWriteDecision =\n /** The values to write, with the tenant stamped if it was missing. */\n | { values: Record<string, unknown>; refusal?: undefined }\n | { refusal: TenantWriteRefusal; values?: undefined };\n\nexport interface TenantWriteInput {\n tenant: CollectionTenantConfig;\n /** The write's values, after defaults and hooks. */\n values: Record<string, unknown>;\n status: EntityStatus;\n /**\n * Every tenant the caller may write into.\n *\n * One entry for a claim, however many memberships they hold for the\n * membership form, and none for a caller carrying neither.\n */\n callerTenants: readonly unknown[];\n /**\n * Whether `callerTenants` is the whole list.\n *\n * A membership lookup is capped — a caller with more memberships than the\n * cap would otherwise make every write of theirs a large read. When the cap\n * is hit this is `false`, and a value that is not in the list is **let\n * through** rather than refused: the list is no longer evidence of absence,\n * and the policy's `WITH CHECK` is what actually decides. The API check is\n * an earlier, clearer refusal of the same writes, never a second authority.\n */\n callerTenantsComplete?: boolean;\n /**\n * True when tenancy does not apply to this caller — a bypass role, or the\n * trusted server context. The same set the policy lets through, so the API\n * and the database refuse the same writes.\n */\n bypass: boolean;\n /** The row's current values, on an update. */\n previousValues?: Record<string, unknown>;\n /** The collection slug, for the message. */\n slug: string;\n}\n\n/**\n * An id, however it arrived.\n *\n * A tenant field may be a `belongsTo` relation or a `reference`, and those\n * arrive over the wire as `{ id }` envelopes as often as bare ids. Comparing\n * the envelope to a bare id would refuse every correct write with\n * `TENANT_MISMATCH`, which is the most confusing possible failure — the caller\n * sent exactly the tenant they belong to.\n */\nfunction tenantIdOf(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"object\") {\n const id = (value as { id?: unknown }).id;\n return id === undefined ? value : id;\n }\n return value;\n}\n\n/**\n * Compare two tenant ids as the database will.\n *\n * Stringified, because JSON has one number type and Postgres has several: a\n * caller sending `\"42\"` for a `bigint` tenant column is writing the same row as\n * one sending `42`, and Postgres agrees after the cast. Refusing one of them\n * would be an API rule the database does not have.\n */\nfunction sameTenant(a: unknown, b: unknown): boolean {\n if (a === null || a === undefined || b === null || b === undefined) return false;\n return String(tenantIdOf(a)) === String(tenantIdOf(b));\n}\n\n/**\n * Stamp, or refuse, the tenant on a write.\n *\n * Three refusals, and each exists because the alternative lands somewhere\n * worse:\n *\n * - **`TENANT_REQUIRED`** — the caller has no tenant, or belongs to several and\n * named none. Stamping a guess would put the row in the wrong tenant; letting\n * it through would write a NULL into a `NOT NULL` column and surface as a\n * 23502 naming a column the caller never wrote.\n * - **`TENANT_MISMATCH`** — the caller named a tenant that is not theirs. The\n * database refuses this too, through the policy's `WITH CHECK`, but as a\n * 42501 \"new row violates row-level security policy\" with no mention of which\n * field or why. Refused here so the answer names the field.\n * - **`TENANT_IMMUTABLE`** — an update that moves a row to another tenant. RLS\n * would allow it whenever the caller belongs to both, and it is almost never\n * what anybody meant: it takes the row out of one tenant's history and drops\n * it into another's, with no trace on either side. A deliberate move is a\n * `bypassRoles` operation.\n *\n * A bypass caller is exempt from all three: they are trusted across tenants by\n * declaration, and stamping their write would silently confine a support\n * operator's row to whichever tenant they happen to carry.\n */\nexport function resolveTenantWrite(input: TenantWriteInput): TenantWriteDecision {\n const { tenant, values, status, callerTenants, bypass, previousValues, slug } = input;\n const field = tenant.field;\n const complete = input.callerTenantsComplete !== false;\n /** Is `value` one the caller may write? Unknown counts as yes — see `callerTenantsComplete`. */\n const callerHas = (value: unknown): boolean =>\n callerTenants.some(t => sameTenant(t, value)) || !complete;\n\n if (bypass) return { values };\n\n const provided = values[field];\n const creating = status !== \"existing\";\n\n if (!creating) {\n // An update that does not mention the field cannot move the row, and\n // the row's own tenant is already what RLS checked to let the update\n // through. Nothing to do.\n if (provided === undefined) return { values };\n\n const previous = previousValues?.[field];\n if (previous !== undefined && !sameTenant(provided, previous)) {\n return {\n refusal: {\n code: \"TENANT_IMMUTABLE\",\n field,\n message:\n `'${field}' is the tenant '${slug}' rows belong to, and a row cannot change tenant. ` +\n `This update would move it from '${String(tenantIdOf(previous))}' to ` +\n `'${String(tenantIdOf(provided))}'. Create the row in the other tenant and delete ` +\n \"this one, or perform the move with a role listed in `tenant.bypassRoles`.\"\n }\n };\n }\n if (previous === undefined && !callerHas(provided)) {\n return { refusal: mismatch(field, slug, provided, callerTenants) };\n }\n return { values };\n }\n\n if (provided === undefined || provided === null || provided === \"\") {\n if (callerTenants.length === 1) {\n return { values: { ...values, [field]: tenantIdOf(callerTenants[0]) } };\n }\n return {\n refusal: {\n code: \"TENANT_REQUIRED\",\n field,\n message: callerTenants.length === 0\n ? `'${slug}' is scoped to a tenant and this request carries none, so there is nothing ` +\n `to write into '${field}'. ` + sourceHint(tenant)\n : `'${slug}' is scoped to a tenant and this caller belongs to ${callerTenants.length} ` +\n `of them, so '${field}' cannot be inferred. Send it on the write — it must be one ` +\n \"the caller belongs to.\"\n }\n };\n }\n\n if (!callerHas(provided)) {\n return { refusal: mismatch(field, slug, provided, callerTenants) };\n }\n\n return { values };\n}\n\nfunction mismatch(\n field: string,\n slug: string,\n provided: unknown,\n callerTenants: readonly unknown[]\n): TenantWriteRefusal {\n return {\n code: \"TENANT_MISMATCH\",\n field,\n message:\n `'${field}' names tenant '${String(tenantIdOf(provided))}', which this caller does not belong ` +\n `to, so the write to '${slug}' would be refused by the database as well. ` +\n (callerTenants.length === 0\n ? \"This request carries no tenant at all.\"\n : `The caller's ${callerTenants.length === 1 ? \"tenant is\" : \"tenants are\"} ` +\n callerTenants.map(t => `'${String(tenantIdOf(t))}'`).join(\", \") + \".\")\n };\n}\n\n/** Where a caller's tenant was supposed to come from, for the 400. */\nfunction sourceHint(tenant: CollectionTenantConfig): string {\n return isTenantClaimSource(tenant.from)\n ? `The tenant comes from the '${tenant.from.claim}' claim on the caller's token; this one has no ` +\n \"such claim. Sign in, or add the claim in the custom-claims hook.\"\n : `The tenant comes from rows of '${tenant.from.membership.collection}' whose ` +\n `'${tenant.from.membership.userField}' is the caller; this caller has none.`;\n}\n","import { CollectionConfig, SecurityRule, SecurityOperation, AuthCollectionConfig, PolicyExpression, isPostgresCollectionConfig, policy } from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\nimport { buildTenantSecurityRule } from \"./tenant\";\nimport { getPolicyNamesForRules } from \"@rebasepro/utils\";\n\n/**\n * Default RLS policies injected by the schema generator.\n *\n * Rebase's enforcement model is unified: authenticated (user-context) requests\n * run under the restricted `rebase_user` role, so Postgres RLS binds *every*\n * statement — reads and writes. A collection's `securityRules` are the whole\n * authorization model. The server context (auth flows, migrations, raw\n * `rebase.sql`) runs as the owner and bypasses RLS.\n *\n * `rebase.dataAsAdmin` is **not** in that set, despite the name: it is scoped as\n * `{ uid: \"service\", roles: [\"admin\"] }`, so it runs as `rebase_user` like any\n * other caller and clears the baseline below through the *admin* arm, not the\n * server arm. Which is why `disableDefaultPolicies` plus a lone\n * `policy.serverContext()` rule locks it out too.\n *\n * Because RLS default-denies, every collection is **locked by default**: with\n * no rules, only the server context and admins can touch it. The generator\n * injects that safe baseline:\n *\n * **For every collection**\n * 1. A permissive **server-or-admin SELECT** grant.\n * 2. A permissive **server-or-admin write** grant (insert/update/delete).\n *\n * Author `securityRules` are permissive and OR together, so explicit rules only\n * *broaden* access from this locked baseline (e.g. \"users read/write their own\n * rows\").\n *\n * **For auth collections additionally**\n * 3. A permissive **self SELECT** grant (`id = rebase.uid()`), so users can read\n * their own row (profile, session bootstrap) without every app re-declaring\n * it.\n * 4. A **restrictive** admin write gate. Restrictive policies are AND'd with\n * every other policy, so a write is rejected unless the caller is an admin\n * (or the server context) — even if the author also wrote a permissive rule\n * such as \"a user may edit their own row\". Without this, a permissive owner\n * rule would let a user change their own `roles`.\n *\n * The server context is recognised as `rebase.uid() IS NULL` (`policy.serverContext()`)\n * — the built-in flows that run without a user (signup, migrations) set no user\n * GUC — which also lets the owner connection satisfy these policies even under\n * FORCE RLS. A *user* request never reaches that state: an anonymous one carries\n * `ANONYMOUS_USER_ID`, precisely so it cannot pass for the server here.\n *\n * **For a collection declaring `tenant`, additionally**\n * 5. A **restrictive** tenancy gate for every operation. Same kind of thing as\n * the admin write gate and injected for the same reason: it is ANDed with\n * every other policy, so it narrows what the author's permissive rules\n * grant and can never widen them. See `./tenant.ts`.\n *\n * Opt out with `disableDefaultPolicies: true` to take full responsibility for\n * the collection's RLS. The *restrictive* rules are not part of that opt-out:\n * dropping a rule that can only remove access could express nothing but \"let\n * more people in\", which is what the flag already does by removing the grants.\n */\n// Expressed structurally (not as raw SQL) so the admin UI can evaluate it\n// exactly — the framework's most security-critical policies must be reflected\n// precisely, not left as un-evaluable raw clauses. Compiles to\n// `rebase.uid() IS NULL OR (string_to_array(rebase.roles(), ',') && ARRAY['admin'])`.\n//\n// `serverContext()`, emphatically not `not(authenticated())`: the server arm of\n// this grant must match the server context and nothing else. Anonymous visitors\n// are not signed in either, so a negated `authenticated()` would hand them the\n// server-or-admin grant on every collection's default policy.\nconst SERVER_OR_ADMIN_EXPR: PolicyExpression = policy.or(\n policy.serverContext(),\n policy.rolesOverlap([\"admin\"])\n);\n\n/** Write operations that must be admin-gated by default on auth collections. */\nconst DEFAULT_GUARDED_OPS: SecurityOperation[] = [\"insert\", \"update\", \"delete\"];\n\n/** Whether a collection is flagged as an authentication collection. */\nfunction isAuthCollection(collection: CollectionConfig): boolean {\n const auth = collection.auth;\n return auth === true || (typeof auth === \"object\" && (auth as AuthCollectionConfig)?.enabled === true);\n}\n\n/** The property marked as the row id (falls back to `id`). */\nfunction getIdPropertyName(collection: CollectionConfig): string {\n for (const [name, prop] of Object.entries(collection.properties ?? {})) {\n if (prop && typeof prop === \"object\" && \"isId\" in prop && (prop as { isId?: unknown }).isId) {\n return name;\n }\n }\n return \"id\";\n}\n\n/**\n * Returns the security rules that should be applied to a collection: the\n * author's explicit `securityRules` plus the framework defaults described in\n * the module doc (baseline server/admin read for all collections; self-read\n * and the admin write gate for auth collections).\n *\n * Collections that opt out via `disableDefaultPolicies` are returned unchanged.\n */\n/**\n * The restrictive write gate for an auth collection.\n *\n * Restrictive, so it is ANDed with everything else: whatever an author's\n * permissive rules allow, a write to this table still has to satisfy this too.\n * It is the only thing standing between \"users may edit their own row\" and\n * \"users may grant themselves any role\".\n */\nfunction adminWriteGate(tableName: string): SecurityRule {\n return {\n name: `${tableName}_require_admin_write`,\n mode: \"restrictive\",\n operations: [...DEFAULT_GUARDED_OPS],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n };\n}\n\n/**\n * The restrictive tenancy policy, as a list of zero or one.\n *\n * A list so the two call sites can splice it in without a conditional, and a\n * separate function so it is obvious that it is injected on *both* paths —\n * including the `disableDefaultPolicies` one, where it is the only permissive-\n * looking thing that stays. See `./tenant.ts`.\n */\nfunction tenantRule(collection: CollectionConfig): SecurityRule[] {\n const rule = buildTenantSecurityRule(collection);\n return rule ? [rule] : [];\n}\n\nexport function getEffectiveSecurityRules(collection: CollectionConfig): SecurityRule[] {\n const explicit = [...(collection.securityRules ?? [])];\n\n const tableName = getTableName(collection);\n const injected: SecurityRule[] = [];\n\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n // The opt-out drops the *permissive* defaults — the ones that grant.\n // The restrictive admin-write gate on an auth collection is not among\n // them, because it is different in kind: a restrictive policy is ANDed\n // with every other policy and can only ever remove access, so opting\n // out of it cannot express anything except \"let more people write\".\n //\n // Dropping it did exactly that. `{ disableDefaultPolicies: true,\n // securityRules: [{ operation: \"all\", ownerField: \"id\" }] }` — an\n // ordinary \"users may edit their own row\" configuration — let any\n // signed-in user set their own `roles` to `[\"admin\"]`, with no warning\n // from any boot guard, doctor check or validator.\n //\n // An author who needs a different gate can add their own restrictive\n // rule; they cannot end up with none by accident.\n // Tenancy survives the opt-out for exactly the reason the write gate\n // does: it is restrictive, so it can only ever remove access. Dropping\n // it could express nothing except \"let every tenant read every other\n // tenant's rows\", which is not a thing `disableDefaultPolicies` is for\n // — that flag is about taking over the *grants*.\n return [...explicit, ...tenantRule(collection), ...(isAuthCollection(collection)\n ? [adminWriteGate(tableName)]\n : [])];\n }\n\n // Baseline read + write: the server context and admins can always operate.\n // RLS default-denies under the user role, so without these a rule-less\n // collection would be locked to everyone — including the admin studio.\n // Author rules are permissive and broaden access from here.\n injected.push({\n name: `${tableName}_default_admin_read`,\n operations: [\"select\"],\n condition: SERVER_OR_ADMIN_EXPR\n });\n injected.push({\n name: `${tableName}_default_admin_write`,\n operations: [...DEFAULT_GUARDED_OPS],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n });\n\n if (isAuthCollection(collection)) {\n // Self-read: a user can always read their own row.\n injected.push({\n name: `${tableName}_default_self_read`,\n operations: [\"select\"],\n condition: policy.compare(policy.field(getIdPropertyName(collection)), \"eq\", policy.authUid())\n });\n\n // Restrictive gate: AND'd with all other policies, so no permissive rule\n // (e.g. an owner \"edit your own row\" rule) can let a non-admin change\n // privileged columns like `roles`. Survives `disableDefaultPolicies` —\n // see the note above the opt-out.\n injected.push(adminWriteGate(tableName));\n }\n\n // Last, so it reads as what it is: a restriction ANDed over everything\n // above it, author rules included.\n injected.push(...tenantRule(collection));\n\n return [...explicit, ...injected];\n}\n\n/**\n * The framework defaults that {@link getEffectiveSecurityRules} would add to a\n * collection, without the author's own rules.\n *\n * These policies appear in the database under names the author never wrote, and\n * a permissive policy ORs with every other permissive policy — so someone\n * reading their `securityRules` and then the real ACL sees more access than they\n * declared. Dropping them by hand does nothing either: `db push` is declarative,\n * so the next push asserts them again. Callers use this to say, in the generated\n * DDL, which policies are injected and how to take them off.\n */\nexport function getInjectedSecurityRules(collection: CollectionConfig): SecurityRule[] {\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n // Not empty for an auth collection, nor for a tenant-scoped one: both\n // restrictive rules are still injected, and the generated DDL has to\n // say so — a policy in the database that the author never wrote and\n // cannot find in this list is exactly the surprise this function exists\n // to prevent.\n return [...tenantRule(collection), ...(isAuthCollection(collection)\n ? [adminWriteGate(getTableName(collection))]\n : [])];\n }\n\n const explicitCount = (collection.securityRules ?? []).length;\n // getEffectiveSecurityRules appends the defaults after the author's rules,\n // so everything past the author's count is injected.\n return getEffectiveSecurityRules(collection).slice(explicitCount);\n}\n\n/**\n * Every policy name `rebase db push` would write for a collection.\n *\n * This is the answer to \"did the codebase produce this live policy?\", and it is\n * more than `securityRules.map(r => r.name)` for two reasons:\n *\n * - a rule without an explicit `name` compiles to `<table>_<op>_<hash>`, one\n * per operation, so comparing `rule.name` to `policyname` never matches it;\n * - the generator also injects the safe-by-default baseline\n * (`<table>_default_admin_*`), which is in no collection's `securityRules`.\n *\n * Every UI that flags drift has to get both right, and each one that derived it\n * by hand got a different subset — which is how four policies *Rebase itself\n * wrote* came to be badged as hand-written drift on every table in a project,\n * with a button offering to import them back into the codebase that produced\n * them. There is one derivation now, and this is it.\n */\nexport function getGeneratedPolicyNames(collection: CollectionConfig): Set<string> {\n return getPolicyNamesForRules(getEffectiveSecurityRules(collection), getTableName(collection));\n}\n","import {\n CollectionConfig,\n JUNCTION_PIVOT_KEY,\n PolicyExpression,\n PolicyOperand,\n Properties,\n Property,\n Relation,\n SecurityRule,\n isPostgresCollectionConfig,\n policy\n} from \"@rebasepro/types\";\nimport { getPolicyOperations } from \"@rebasepro/utils\";\nimport { getTableName } from \"./relations\";\nimport { resolveCollectionRelations } from \"./relations\";\nimport { isManyToMany } from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"./policy/securityRuleToConditions\";\n\n/**\n * RLS derivation for many-to-many junction tables.\n *\n * A `through` relation makes the generator create a table nobody declared as a\n * collection — `posts_tags`, `user_roles`. Those tables used to be the one kind\n * of generated table with **no** RLS at all: `rebase_user` holds full DML grants,\n * so with the endpoints locked down, any signed-up user could still read or wipe\n * every edge between them. There is also nowhere in the config to write rules\n * for a junction, so the author could not even fix it by hand.\n *\n * The architecture here is that a junction's security is *derived*, never\n * hand-written:\n *\n * 1. **Locked baseline.** The same server-or-admin `default_admin` grants every\n * collection gets, so the invariant holds again: every table the generator\n * creates is default-deny, and rules only broaden.\n *\n * 2. **Reads follow the endpoints.** An edge is visible iff *both* endpoint\n * rows are visible — two correlated `EXISTS` subqueries. The subqueries run\n * under the caller's role, so each endpoint's own RLS filters them: junction\n * visibility delegates to the endpoints' policies, whatever they become,\n * with nothing duplicated. A public blog keeps rendering its tags; a private\n * CRM's edges are exactly as hidden as its rows.\n *\n * 3. **Writes follow the owning side's update rules.** Linking or unlinking an\n * edge *is* an edit of the owning row — tagging a post is editing the post —\n * so edge writes inherit the declaring collection's explicit permissive\n * `update` rules, each wrapped in an `EXISTS` against the owning row. Where\n * a rule cannot be embedded faithfully (see below) it is dropped, so the\n * failure mode is always *too locked*, never open. Explicit **restrictive**\n * update rules are inherited as restrictive junction rules; if one of them\n * cannot be embedded, the whole derived write grant for that side is\n * suppressed — granting without the author's gate would be looser than the\n * parent itself.\n *\n * **Embeddability.** A parent rule is embedded by moving its condition inside\n * `EXISTS (SELECT 1 FROM parent WHERE parent.pk = junction.fk AND <condition>)`.\n * In that scope, `field` operands bind to the parent — which is what the author\n * meant. But `outerField` operands and `{column}` placeholders in `raw` SQL bind\n * to the RLS row, which is now the junction, not the parent the author wrote\n * them against. So: `raw` anywhere disqualifies a rule; a top-level `outerField`\n * (equivalent to `field` outside a subquery) is rewritten to `field`; an\n * `outerField` inside a nested `existsIn` cannot be re-scoped and disqualifies\n * the rule.\n *\n * Injected parent defaults are never inherited — the junction's own baseline\n * already covers the server/admin plane, and an auth collection's restrictive\n * `require_admin_write` gate exists to protect privileged parent *columns*,\n * which an edge write cannot touch. Inheriting it would stop users managing\n * e.g. their own interests through a `users_interests` junction for no gain.\n *\n * Everything flows through the shared naming machinery, so the Studio\n * recognises these policies as generated instead of offering to \"import\" them.\n */\n\n/** One side of a junction: the collection and the FK column pointing at it. */\nexport interface JunctionEndpoint {\n collection: CollectionConfig;\n /** Junction column holding this endpoint's key. */\n junctionColumn: string;\n}\n\n/** A collection that declares the `through` relation (owns the edge semantics). */\nexport interface JunctionDeclaringSide extends JunctionEndpoint {\n relation: Relation;\n}\n\nexport interface JunctionSpec {\n /** Bare table name (schema stripped). */\n table: string;\n /** Schema the junction is created in — mirrors the CREATE TABLE path. */\n schema: string;\n /** The two endpoints, in [source, target] order of the first declaring relation. */\n endpoints: [JunctionEndpoint, JunctionEndpoint];\n /** Every collection that declares a relation through this table. */\n declaringSides: JunctionDeclaringSide[];\n /**\n * The junction's own columns beyond the two keys — `through.properties`,\n * merged across every declaring side. `{}` when there are none.\n *\n * See {@link ManyToManyRelation.through} for what they are; every side that\n * names a key has to describe the same column, which is checked when the\n * specs are resolved rather than left for `CREATE TABLE` to discover.\n */\n properties: Properties;\n}\n\n// Mirrors auth-default-policies: the server context or an admin.\nconst SERVER_OR_ADMIN_EXPR: PolicyExpression = policy.or(\n policy.serverContext(),\n policy.rolesOverlap([\"admin\"])\n);\n\n/**\n * Walk every collection's resolved relations and aggregate the junction tables\n * they declare. Two collections may declare the same junction from opposite\n * sides (posts→tags and tags→posts through `posts_tags`); both become\n * `declaringSides` of one spec, so derived write grants consider both.\n */\nexport function resolveJunctionSpecs(collections: CollectionConfig[]): Map<string, JunctionSpec> {\n const specs = new Map<string, JunctionSpec>();\n\n for (const collection of collections) {\n const resolved = resolveCollectionRelations(collection);\n for (const relation of Object.values(resolved)) {\n // Narrowed rather than probed: only a many-to-many has a junction,\n // and only after narrowing is `through` guaranteed complete.\n if (!isManyToMany(relation)) continue;\n\n const targetCollection: CollectionConfig | undefined = relation.target();\n if (!targetCollection) continue;\n\n const rawName = relation.through.table;\n // The CREATE TABLE path strips a schema prefix from the name but\n // still creates in \"public\"; the policies must target the same\n // table, so mirror that behaviour exactly.\n const table = rawName.includes(\".\") ? rawName.split(\".\").pop()! : rawName;\n const schema = \"public\";\n\n const source: JunctionDeclaringSide = {\n collection,\n junctionColumn: relation.through.sourceColumn,\n relation\n };\n const target: JunctionEndpoint = {\n collection: targetCollection,\n junctionColumn: relation.through.targetColumn\n };\n\n const existing = specs.get(table);\n if (!existing) {\n specs.set(table, {\n table,\n schema,\n endpoints: [source, target],\n declaringSides: [source],\n properties: mergeJunctionPayload({}, relation.through.properties, table, collection)\n });\n } else {\n // Merged whether or not this side is new: the same collection\n // can reach one junction under two relation names, and the\n // columns each of them asks for all have to exist.\n existing.properties = mergeJunctionPayload(\n existing.properties, relation.through.properties, table, collection);\n if (!existing.declaringSides.some(s => s.collection === collection)) {\n existing.declaringSides.push(source);\n }\n }\n }\n }\n\n return specs;\n}\n\n/**\n * Fold one side's `through.properties` into the junction's, refusing a\n * disagreement rather than picking a winner.\n *\n * Both ends of a link may declare it — `posts.tags` and `tags.posts` are one\n * junction — and each end may name the payload. Only one table gets created, so\n * two descriptions of `role` that are not the same description are a question\n * with no correct answer: whichever won, one of the two collections would be\n * writing through a column it does not think it has. Compared structurally, so\n * two sides that spell the same property twice (the normal case, and the one\n * the docs recommend) are fine.\n */\nfunction mergeJunctionPayload(\n into: Properties,\n incoming: Properties | undefined,\n table: string,\n collection: CollectionConfig\n): Properties {\n if (!incoming || Object.keys(incoming).length === 0) return into;\n const merged: Properties = { ...into };\n for (const [key, property] of Object.entries(incoming)) {\n const already = merged[key as keyof Properties] as Property | undefined;\n if (already && JSON.stringify(already) !== JSON.stringify(property)) {\n throw new Error(\n `The junction table \"${table}\" is declared from more than one side, and they disagree ` +\n `about the payload column \"${key}\": \"${collection.slug ?? collection.name}\" describes it ` +\n \"differently than another declaring collection does. One table is created, so both \" +\n \"`through.properties` blocks have to describe the same column — or only one side should \" +\n \"declare it.\"\n );\n }\n (merged as Record<string, Property>)[key] = property as Property;\n }\n return merged;\n}\n\n/**\n * A synthetic CollectionConfig standing in for the junction during policy\n * compilation and naming. Its two FK columns carry explicit `columnName`s so\n * `outerField` operands resolve to the exact columns the CREATE TABLE emitted,\n * whatever their casing.\n *\n * The payload columns are here too, exactly as authored. That is what lets one\n * reading of a `Property` serve the junction as well as a collection: the\n * schema planner plans these columns with the same function it plans a\n * collection's with, and the write path validates a `_pivot` against them with\n * the same validator a row's values go through. A second description of a\n * payload column anywhere is a second description that can disagree.\n */\nexport function getJunctionCollectionConfig(spec: JunctionSpec): CollectionConfig {\n const properties: Record<string, unknown> = {};\n for (const endpoint of spec.endpoints) {\n properties[endpoint.junctionColumn] = {\n type: \"string\",\n columnName: endpoint.junctionColumn\n };\n }\n // After the keys, so a payload property that collides with a key column\n // cannot quietly replace it — `checkJunctionPayload` refuses that config at\n // boot, and this ordering means the key column survives if one gets past.\n for (const [key, property] of Object.entries(spec.properties)) {\n if (key === JUNCTION_PIVOT_KEY || key in properties) continue;\n properties[key] = property;\n }\n return {\n slug: spec.table,\n name: spec.table,\n table: spec.table,\n schema: spec.schema,\n properties\n } as unknown as CollectionConfig;\n}\n\n/** The property marked as the row id (falls back to `id`). */\nfunction getIdPropertyName(collection: CollectionConfig): string {\n for (const [name, prop] of Object.entries(collection.properties ?? {})) {\n if (prop && typeof prop === \"object\" && \"isId\" in prop && (prop as { isId?: unknown }).isId) {\n return name;\n }\n }\n return \"id\";\n}\n\n/** `EXISTS (SELECT 1 FROM endpoint WHERE endpoint.pk = junction.fk [AND extra])`. */\nfunction existsEndpoint(endpoint: JunctionEndpoint, extra?: PolicyExpression): PolicyExpression {\n const correlation = policy.compare(\n policy.field(getIdPropertyName(endpoint.collection)),\n \"eq\",\n policy.outerField(endpoint.junctionColumn)\n );\n return policy.existsIn({\n collection: endpoint.collection.slug,\n where: extra ? policy.and(correlation, extra) : correlation\n });\n}\n\n/**\n * Whether a parent-rule expression keeps its meaning when moved inside the\n * junction's `EXISTS` subquery — and the re-scoped copy if it does.\n *\n * Returns `null` when the rule cannot be embedded faithfully: `raw` SQL\n * anywhere (its `{column}` placeholders would bind to the junction), or an\n * `outerField` inside a nested `existsIn` (it would bind to the junction while\n * the author meant the parent, and no operand can express \"the middle scope\").\n * Top-level `outerField`s are rewritten to `field`, which is what they meant.\n */\nexport function embedParentExpression(expr: PolicyExpression, depth = 0): PolicyExpression | null {\n switch (expr.kind) {\n case \"raw\":\n return null;\n case \"and\":\n case \"or\": {\n const parts: PolicyExpression[] = [];\n for (const child of expr.operands) {\n const embedded = embedParentExpression(child, depth);\n if (!embedded) return null;\n parts.push(embedded);\n }\n return expr.kind === \"and\" ? policy.and(...parts) : policy.or(...parts);\n }\n case \"not\": {\n const embedded = embedParentExpression(expr.operand, depth);\n return embedded ? policy.not(embedded) : null;\n }\n case \"existsIn\": {\n const where = embedParentExpression(expr.where, depth + 1);\n return where ? policy.existsIn({ collection: expr.collection, where }) : null;\n }\n case \"compare\": {\n const left = embedOperand(expr.left, depth);\n const right = embedOperand(expr.right, depth);\n if (!left || !right) return null;\n return { ...expr, left, right };\n }\n default:\n // Leaf expressions with no field references (true, false,\n // serverContext, authenticated, rolesOverlap, rolesContain) are\n // position-independent.\n return expr;\n }\n}\n\n/** Re-scope an operand, or return `null` if its binding cannot be preserved. */\nfunction embedOperand(operand: PolicyOperand, depth: number): PolicyOperand | null {\n if (operand.kind === \"outerField\") {\n // Outside a subquery, outerField ≡ field: the author meant their own\n // row, which after embedding is the EXISTS's joined table → field.\n if (depth === 0) return policy.field(operand.name);\n // Inside the author's own existsIn it meant the parent row; after\n // embedding it would bind to the junction. Not expressible.\n return null;\n }\n return operand;\n}\n\n/** Does the rule cover the `update` operation? */\nfunction coversUpdate(rule: SecurityRule): boolean {\n return getPolicyOperations(rule).some(op => op === \"update\" || op === \"all\");\n}\n\n/**\n * The full derived policy set for a junction table: the locked server/admin\n * baseline, the endpoint-visibility read grant, inherited write grants, and\n * inherited restrictive gates. Returns `[]` when every declaring collection set\n * `disableDefaultPolicies` — the junction is then the author's to police, and\n * stays locked (RLS is still enabled) until they write policies for it.\n */\nexport function getJunctionSecurityRules(spec: JunctionSpec): SecurityRule[] {\n if (spec.declaringSides.every(side => isPostgresCollectionConfig(side.collection) && side.collection.disableDefaultPolicies)) {\n return [];\n }\n\n const rules: SecurityRule[] = [];\n\n // 1. Locked baseline — same shape and naming as every collection's.\n rules.push({\n name: `${spec.table}_default_admin_read`,\n operations: [\"select\"],\n condition: SERVER_OR_ADMIN_EXPR\n });\n rules.push({\n name: `${spec.table}_default_admin_write`,\n operations: [\"insert\", \"update\", \"delete\"],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n });\n\n // 2. Reads follow the endpoints: the edge is visible iff both rows are.\n // The EXISTS subqueries run under the caller's role, so each endpoint's\n // own RLS applies inside them — visibility is delegated, not copied.\n rules.push({\n name: `${spec.table}_default_edge_read`,\n operations: [\"select\"],\n condition: policy.and(\n existsEndpoint(spec.endpoints[0]),\n existsEndpoint(spec.endpoints[1])\n )\n });\n\n // 3. Writes follow the owning side's explicit update rules.\n const writeGrants: PolicyExpression[] = [];\n for (const side of spec.declaringSides) {\n const explicitRules = (isPostgresCollectionConfig(side.collection)\n ? side.collection.securityRules\n : undefined) ?? [];\n const updateRules = explicitRules.filter(coversUpdate);\n\n const permissive = updateRules.filter(r => r.mode !== \"restrictive\");\n const restrictive = updateRules.filter(r => r.mode === \"restrictive\");\n\n // Embed the restrictive gates first: if any of them cannot be carried\n // over, granting writes from this side would be looser than the parent\n // itself allows — so the whole side's grant is suppressed.\n const embeddedGates: PolicyExpression[] = [];\n let gatesEmbeddable = true;\n for (const gate of restrictive) {\n const using = securityRuleToConditions(gate).usingExpr;\n const embedded = using ? embedParentExpression(using) : null;\n if (!embedded) {\n gatesEmbeddable = false;\n break;\n }\n embeddedGates.push(embedded);\n }\n if (!gatesEmbeddable) continue;\n\n const grants: PolicyExpression[] = [];\n for (const rule of permissive) {\n const using = securityRuleToConditions(rule).usingExpr;\n const embedded = using ? embedParentExpression(using) : null;\n if (embedded) grants.push(embedded);\n }\n if (grants.length === 0) continue;\n\n // \"May update the owning row\": any permissive grant, AND every gate.\n const condition = embeddedGates.length > 0\n ? policy.and(policy.or(...grants), ...embeddedGates)\n : policy.or(...grants);\n\n writeGrants.push(existsEndpoint(side, condition));\n }\n\n if (writeGrants.length > 0) {\n rules.push({\n name: `${spec.table}_default_edge_write`,\n operations: [\"insert\", \"update\", \"delete\"],\n condition: writeGrants.length === 1 ? writeGrants[0] : policy.or(...writeGrants),\n check: writeGrants.length === 1 ? writeGrants[0] : policy.or(...writeGrants)\n });\n }\n\n return rules;\n}\n","import jsonLogic from \"json-logic-js\";\nimport {\n ArrayProperty,\n AuthState,\n ConditionContext,\n ConditionRule,\n EnumValueConfig,\n NumberProperty,\n PropertyConditions,\n Property,\n ReferenceProperty,\n StringProperty\n} from \"@rebasepro/types\";\n\n/**\n * Access a nested property from an object via dot notation.\n */\nfunction getIn(obj: Record<string, unknown> | unknown, path: string): unknown {\n if (!obj || !path) return undefined;\n return path.split(\".\").reduce((acc: unknown, part: string) => acc && (acc as Record<string, unknown>)[part], obj);\n}\n\nlet operationsRegistered = false;\n\n/**\n * Register custom JSON Logic operations for Rebase.\n * Call this once at app initialization.\n */\nexport function registerConditionOperations(): void {\n if (operationsRegistered) return;\n\n // Check if user has a specific role by ID\n jsonLogic.add_operation(\"hasRole\", function (this: ConditionContext, roleId: string) {\n return this?.user?.roles?.includes(roleId) ?? false;\n });\n\n // Check if user has any of the specified roles\n jsonLogic.add_operation(\"hasAnyRole\", function (this: ConditionContext, roleIds: string[]) {\n if (!this?.user?.roles || !Array.isArray(roleIds)) return false;\n return roleIds.some(role => this.user.roles.includes(role));\n });\n\n // Check if a timestamp is today\n jsonLogic.add_operation(\"isToday\", (timestamp: number) => {\n if (!timestamp) return false;\n const date = new Date(timestamp);\n const today = new Date();\n return date.getFullYear() === today.getFullYear() &&\n date.getMonth() === today.getMonth() &&\n date.getDate() === today.getDate();\n });\n\n // Check if a timestamp is in the past\n jsonLogic.add_operation(\"isPast\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp < Date.now();\n });\n\n // Check if a timestamp is in the future\n jsonLogic.add_operation(\"isFuture\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp > Date.now();\n });\n\n operationsRegistered = true;\n}\n\n/**\n * Evaluate a condition against the given context.\n *\n * A condition may be stated as a literal instead of a rule — `hidden: true`\n * rather than `hidden: { \"==\": [1, 1] }` — and a literal is already its own\n * answer, so it is returned rather than handed to the evaluator.\n */\nexport function evaluateCondition(rule: ConditionRule, context: ConditionContext): unknown {\n if (typeof rule === \"boolean\") return rule;\n // Ensure operations are registered\n registerConditionOperations();\n return jsonLogic.apply(rule, context);\n}\n\n/**\n * Convert a value to a format suitable for JSON Logic evaluation.\n * Specifically handles Date objects by converting them to Unix timestamps.\n */\nfunction serializeValueForConditions(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.getTime();\n }\n\n // Handle Firestore Timestamp-like objects (have toDate or toMillis)\n if (typeof (value as { toMillis?: () => number })?.toMillis === \"function\") {\n return (value as { toMillis: () => number }).toMillis();\n }\n if (typeof (value as { toDate?: () => Date })?.toDate === \"function\") {\n return (value as { toDate: () => Date }).toDate().getTime();\n }\n\n // Handle arrays recursively\n if (Array.isArray(value)) {\n return value.map(serializeValueForConditions);\n }\n\n // Handle plain objects recursively\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValueForConditions((value as Record<string, unknown>)[key]);\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Build a ConditionContext from the current property resolution context.\n */\nexport function buildConditionContext(params: {\n propertyKey?: string;\n values?: Record<string, unknown>;\n previousValues?: Record<string, unknown>;\n path: string;\n entityId?: string;\n index?: number;\n authController: AuthState;\n}): ConditionContext {\n const {\n propertyKey,\n values,\n previousValues,\n path,\n entityId,\n index,\n authController\n } = params;\n\n const user = authController.user;\n const serializedValues = serializeValueForConditions(values ?? {});\n const serializedPreviousValues = serializeValueForConditions(previousValues ?? values ?? {});\n\n return {\n values: serializedValues as Record<string, unknown>,\n previousValues: serializedPreviousValues as Record<string, unknown>,\n propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : undefined,\n path,\n entityId,\n isNew: !entityId,\n index,\n user: {\n uid: user?.uid ?? \"\",\n email: user?.email ?? null,\n displayName: user?.displayName ?? null,\n photoURL: user?.photoURL ?? null,\n roles: (user?.roles ?? []).map((r: unknown) => typeof r === \"string\" ? r : (r as { id: string }).id)\n },\n now: Date.now()\n };\n}\n\n/**\n * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.\n */\n","import { rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport type {\n ArrayProperty,\n NumberProperty,\n PostgresProperties,\n Property,\n Relation,\n SecurityOperation,\n SecurityRule,\n StringProperty,\n TableColumnInfo,\n TableMetadata\n} from \"@rebasepro/types\";\nimport { firstFreeKey, prettifyIdentifier, toWireKey } from \"@rebasepro/utils\";\n\n/**\n * A collection as introspection can describe it: the table, its columns, the\n * relations its foreign keys imply, and the RLS policies already on it.\n *\n * Deliberately not `Partial<AdminCollection>`, which is what this returned\n * while it lived in `@rebasepro/studio`. `propertiesOrder` is the only admin\n * key it produces, and naming the admin view model for one field would put\n * `@rebasepro/cms-types` on the dependency path of a package the backend\n * loads.\n */\nexport interface IntrospectedCollection {\n name: string;\n slug: string;\n table: string;\n properties: PostgresProperties;\n propertiesOrder: string[];\n relations?: Relation[];\n securityRules?: SecurityRule[];\n}\n\n/**\n * Maps a PostgreSQL column data type to a Rebase property type.\n */\nfunction pgTypeToRebaseProperty(column: TableColumnInfo): Property | null {\n const {\n column_name,\n data_type,\n udt_name,\n is_nullable,\n column_default,\n character_maximum_length,\n enum_values\n } = column;\n\n const required = is_nullable === \"NO\";\n const prettifiedName = prettifyIdentifier(column_name);\n\n // Detect if this column is a primary key (auto-generated id)\n const isAutoId = column_default != null && (\n column_default.includes(\"nextval\") ||\n column_default.includes(\"gen_random_uuid\") ||\n column_default.includes(\"uuid_generate\") ||\n column_default.includes(\"identity\")\n );\n\n // USER-DEFINED = PostgreSQL enums\n if (data_type === \"USER-DEFINED\" && enum_values && enum_values.length > 0) {\n return {\n type: \"string\",\n name: prettifiedName,\n enum: enum_values.map((v: string) => ({ id: v,\nlabel: prettifyIdentifier(v) })),\n validation: required ? { required: true } : undefined\n } as StringProperty;\n }\n\n const dt = data_type.toLowerCase();\n switch (dt) {\n case \"character varying\":\n case \"varchar\":\n case \"text\":\n case \"char\":\n case \"character\":\n case \"citext\": {\n let colType: \"varchar\" | \"text\" | \"char\" = \"varchar\";\n if (dt === \"text\" || dt === \"citext\") colType = \"text\";\n if (dt === \"char\" || dt === \"character\") colType = \"char\";\n // Carry the declared width across. Dropping it made introspection\n // lossy in the one direction that costs data: a `character\n // varying(500)` column read back as a bare `varchar` regenerates as\n // `VARCHAR(255)`, narrowing a column that already holds longer\n // values. TEXT has no width, and reporting one would invent a limit\n // the database does not have.\n const declaredLength = colType === \"text\" ? null : character_maximum_length;\n const prop: StringProperty = {\n type: \"string\",\n name: prettifiedName,\n columnType: colType,\n validation: required || declaredLength\n ? {\n ...(required ? { required: true } : {}),\n ...(declaredLength ? { max: declaredLength } : {})\n }\n : undefined\n };\n if (isAutoId) {\n prop.isId = \"manual\";\n }\n return prop;\n }\n\n case \"uuid\": {\n const prop: StringProperty = {\n type: \"string\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n if (isAutoId) {\n prop.isId = \"uuid\";\n }\n return prop;\n }\n\n case \"integer\":\n case \"bigint\":\n case \"smallint\": {\n const colType = dt === \"bigint\" ? \"bigint\" : \"integer\";\n const prop: NumberProperty = {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n validation: {\n ...(required ? { required: true } : {}),\n integer: true\n }\n };\n if (isAutoId) {\n prop.isId = \"increment\";\n }\n return prop;\n }\n\n case \"serial\":\n case \"bigserial\":\n case \"smallserial\": {\n const colType = dt === \"bigserial\" ? \"bigserial\" : \"serial\";\n return {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n isId: \"increment\",\n validation: {\n ...(required ? { required: true } : {}),\n integer: true\n }\n } as NumberProperty;\n }\n\n case \"numeric\":\n case \"decimal\":\n case \"real\":\n case \"double precision\": {\n let colType: \"numeric\" | \"real\" | \"double precision\" = \"numeric\";\n if (dt === \"real\") colType = \"real\";\n if (dt === \"double precision\") colType = \"double precision\";\n return {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n validation: required ? { required: true } : undefined\n };\n }\n\n case \"boolean\":\n return {\n type: \"boolean\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n\n case \"timestamp with time zone\":\n case \"timestamp without time zone\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"date\":\n case \"time with time zone\":\n case \"time without time zone\":\n case \"time\": {\n let colType: \"timestamp\" | \"date\" | \"time\" = \"timestamp\";\n if (dt.startsWith(\"date\")) colType = \"date\";\n if (dt.startsWith(\"time \") || dt === \"time\") colType = \"time\";\n return {\n type: \"date\",\n name: prettifiedName,\n columnType: colType,\n validation: required ? { required: true } : undefined\n };\n }\n\n case \"jsonb\":\n case \"json\":\n return {\n type: \"map\",\n name: prettifiedName,\n columnType: dt === \"jsonb\" ? \"jsonb\" : \"json\",\n keyValue: true,\n properties: {}\n };\n\n case \"array\":\n case \"ARRAY\": {\n let innerType = \"string\";\n let colType: ArrayProperty[\"columnType\"] = undefined;\n if (udt_name === \"_text\" || udt_name === \"_varchar\") {\n innerType = \"string\";\n colType = \"text[]\";\n } else if (udt_name === \"_int4\" || udt_name === \"_int2\" || udt_name === \"_int8\") {\n innerType = \"number\";\n colType = \"integer[]\";\n } else if (udt_name === \"_bool\") {\n innerType = \"boolean\";\n colType = \"boolean[]\";\n } else if (udt_name === \"_numeric\") {\n innerType = \"number\";\n colType = \"numeric[]\";\n }\n return {\n type: \"array\",\n name: prettifiedName,\n columnType: colType,\n of: { type: innerType }\n } as ArrayProperty;\n }\n\n default:\n // Fallback: treat unknown types as string\n return {\n type: \"string\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n }\n}\n\n/**\n * Builds a collection description from PostgreSQL table metadata.\n * This is used when creating a new collection from an existing database table.\n */\nexport function buildCollectionFromTableMetadata(\n tableName: string,\n metadata: TableMetadata\n): IntrospectedCollection {\n const properties: Record<string, Property> = {};\n const propertiesOrder: string[] = [];\n // Introspection can only ever produce two shapes: a foreign key on this\n // table, or a junction between two. Both are named by their kind.\n const relations: Array<{\n id: string;\n relationName: string;\n target: string;\n kind: \"belongsTo\" | \"manyToMany\";\n localKey?: string;\n through?: { table: string; sourceColumn: string; targetColumn: string };\n }> = [];\n const securityRules: SecurityRule[] = [];\n\n // Parse columns\n for (const column of metadata.columns) {\n const property = pgTypeToRebaseProperty(column);\n if (property) {\n const propRecord = property as unknown as Record<string, unknown>;\n Object.keys(propRecord).forEach(key => propRecord[key] === undefined && delete propRecord[key]);\n\n // The key is the wire name; `columnName` carries the column. This\n // used to key by the column and rely on the two being the same\n // string, which is what put `user_id` on the API of an imported\n // collection and `displayName` on the API of an authored one.\n //\n // `columnName` is stamped unconditionally rather than left to the\n // snake_case default, because the default is not the inverse of\n // camel-casing for every name — the mapping has to be recorded, not\n // recomputed.\n //\n // First free candidate: `user_id` and `userId` as two real columns\n // camel-case to one key, and one of them would otherwise overwrite\n // the other and be silently dropped.\n const key = firstFreeKey(\n [toWireKey(column.column_name), column.column_name],\n { has: (candidate: string) => candidate in properties }\n );\n if (key !== column.column_name) propRecord.columnName = column.column_name;\n properties[key] = property;\n propertiesOrder.push(key);\n }\n }\n\n // Parse Outgoing Foreign Keys -> Many-to-One / One-to-One\n if (metadata.foreignKeys) {\n for (const fk of metadata.foreignKeys) {\n const relName = toWireKey(\n fk.column_name.endsWith(\"_id\")\n ? fk.column_name.substring(0, fk.column_name.length - 3)\n : fk.column_name\n );\n relations.push({\n id: fk.column_name,\n relationName: relName,\n target: fk.foreign_table_name, // Will be hydrated later\n kind: \"belongsTo\",\n localKey: fk.column_name\n });\n }\n }\n\n // Parse Incoming Junctions -> Many-to-Many\n if (metadata.junctions) {\n for (const junction of metadata.junctions) {\n const relName = junction.target_table_name; // E.g., 'roles'\n relations.push({\n id: junction.target_table_name + \"_relation\",\n relationName: relName,\n target: junction.target_table_name, // Will be hydrated later\n kind: \"manyToMany\",\n through: {\n table: junction.junction_table_name,\n sourceColumn: junction.source_column_name,\n targetColumn: junction.target_column_name\n }\n });\n }\n }\n\n // Parse RLS Policies\n if (metadata.policies) {\n for (const policy of metadata.policies) {\n // Attempt to map typical cmds to operations.\n // Postgres cmd: SELECT, INSERT, UPDATE, DELETE, ALL\n let operations: SecurityOperation[] = [];\n switch (policy.cmd) {\n case \"ALL\": operations = [\"all\"]; break;\n case \"SELECT\": operations = [\"select\"]; break;\n case \"INSERT\": operations = [\"insert\"]; break;\n case \"UPDATE\": operations = [\"update\"]; break;\n case \"DELETE\": operations = [\"delete\"]; break;\n }\n // Normalised on the way in, the same way `sqlToPolicy` normalises\n // what the admin UI reads back. Without it, importing a table from a\n // database provisioned before 1.0 copies `auth.uid()` straight into\n // the project's config — a call to a function the framework no\n // longer creates, which then boots with a legacy-helper warning\n // forever and holds the `auth` schema open.\n const qual = policy.qual ? rewriteLegacyRlsFunctions(policy.qual) : undefined;\n const withCheck = policy.with_check ? rewriteLegacyRlsFunctions(policy.with_check) : undefined;\n if (qual) {\n securityRules.push({\n name: policy.policy_name,\n operations,\n roles: policy.roles ?? [],\n using: qual,\n ...(withCheck ? { withCheck } : {})\n });\n } else {\n securityRules.push({\n name: policy.policy_name,\n operations,\n roles: policy.roles ?? []\n });\n }\n }\n }\n\n return {\n name: prettifyIdentifier(tableName),\n slug: tableName,\n table: tableName,\n properties: properties as PostgresProperties,\n propertiesOrder,\n // `target` is still a slug here — the caller hydrates it into a thunk.\n ...(relations.length > 0 ? { relations: relations as unknown as Relation[] } : {}),\n ...(securityRules.length > 0 ? { securityRules } : {})\n };\n}\n","import type { StringProperty } from \"@rebasepro/types\";\n\n/**\n * The length a bounded string column is declared with when the property does\n * not say. Historical: it is what the DDL generator hardcoded, kept so that\n * regenerating an existing schema does not silently redefine its columns.\n */\nexport const DEFAULT_STRING_COLUMN_LENGTH = 255;\n\n/**\n * How wide a `varchar`/`char` column should be for a given property.\n *\n * One definition, three call sites, because they used to disagree. For the same\n * `columnType: \"varchar\"` property the DDL generator emitted `VARCHAR(255)`\n * while the Drizzle generator emitted a bare `varchar(\"col\")` — which Postgres\n * reads as *unbounded* — so which of the two you ran decided whether the column\n * had a limit at all. Introspection then dropped the length entirely, so reading\n * an existing `character varying(500)` column back and regenerating it produced\n * a `VARCHAR(255)`: a silent narrowing of a column with data already in it.\n *\n * `validation.max` is the property's own statement about how long the value may\n * be, so it is the only sensible source for the column's width — and it keeps\n * the constraint the database enforces in step with the one the app enforces,\n * rather than inventing a second, different limit underneath it.\n */\nexport function resolveStringColumnLength(prop: Pick<StringProperty, \"validation\">): number {\n const max = prop.validation?.max;\n return typeof max === \"number\" && Number.isInteger(max) && max > 0\n ? max\n : DEFAULT_STRING_COLUMN_LENGTH;\n}\n","/**\n * The tables Rebase creates for its own bookkeeping, and the SQL that keeps the\n * end-user role away from them.\n *\n * ## Why this exists\n *\n * Authenticated requests run as {@link REBASE_USER_ROLE}, and the boot-time role\n * provisioning grants that role `SELECT, INSERT, UPDATE, DELETE` on every table\n * in the schemas a project uses — including `rebase`, because a project's own\n * collections are allowed to live there (the scaffold puts `users` there). It\n * also sets `ALTER DEFAULT PRIVILEGES`, so a table created *later* by the\n * migrating role inherits the same grant.\n *\n * Every framework-internal table is created later: auth's tables come up during\n * `initializeAuth`, `api_keys` during route mounting, `cron_logs` when the first\n * job registers, `idempotency_keys` on the first request that carries a key. So\n * they all inherited full DML for the end-user role — and none of them enables\n * row-level security, because none of them is a collection with\n * `securityRules`. Measured on a freshly provisioned database, `SET ROLE\n * rebase_user` could read `rebase.refresh_tokens` (session token hashes),\n * `rebase.mfa_factors` (`secret_encrypted`), `rebase.recovery_codes`, and\n * `rebase.api_keys` (including its `admin` flag), and insert into\n * `rebase.app_config`.\n *\n * Nothing routes a user-context query at those tables today, so this was not\n * reachable over the API. That is the wrong thing to depend on: the documented\n * model is that RLS is the authorization boundary, and these tables sat outside\n * it. The boundary is now a privilege boundary instead — the role simply cannot\n * address them.\n *\n * ## Why REVOKE rather than ENABLE ROW LEVEL SECURITY\n *\n * RLS with no policy denies every row, which is the same outcome, but it is the\n * *weaker* statement: it leaves the grant in place, so a later policy — or a\n * `FORCE` flag cleared by some future migration — reopens the table. There is no\n * row of `refresh_tokens` any end user should ever reach, so the honest encoding\n * is \"this role has no privilege here at all\". It also keeps the owner\n * connection (which auth actually runs on) completely unaffected.\n *\n * ## Keeping it true\n *\n * `packages/rls-check` scans the `rebase` schema — it used to skip it as a\n * \"platform\" schema — and its `rls-disabled` check fires on exactly the\n * condition this module removes: RLS off *and* a DML grant to a reachable role.\n * So a table added here without a revoke is caught by `pnpm rls:check`, not by\n * someone re-reading this file.\n */\n\n/**\n * The Postgres role authenticated requests run as.\n *\n * Defined here rather than in the Postgres driver because both the driver (which\n * provisions the role) and this module (which revokes on its behalf) need it,\n * and a second spelling of a role name is a silent no-op waiting to happen.\n */\nexport const REBASE_USER_ROLE = \"rebase_user\";\n\n/**\n * Framework-internal table names, unqualified.\n *\n * Deliberately NOT including `users`: the auth user table is also a collection,\n * with `securityRules`, RLS enabled and policies applied. Users read their own\n * row through it — revoking there would break sign-in. `revokeInternalTableSql`\n * now skips any table with RLS enabled, so that exception is enforced rather\n * than merely remembered — and so is the same hazard for every other name here,\n * any of which a project may legitimately use for a collection of its own.\n *\n * `atlas_schema_revisions` is Atlas's migration ledger, which lands in `rebase`\n * because `db migrate apply` passes `--revisions-schema rebase`.\n *\n * Every entry here must also be revoked by whatever creates it, and vice versa:\n * the creation-time revoke fires once, on the boot that first makes the table,\n * so it cannot help a database provisioned before that revoke existed. This\n * list is what the boot-time sweep in `ensureAppRole` iterates, and the sweep\n * is the only thing that can repair an already-granted table. A table revoked\n * at creation but missing here is therefore permanently stranded on any\n * database that predates its revoke.\n *\n * These names are unqualified, and the boot-time sweep applies them to every\n * schema a project uses — so an entry here is a claim on that name in `public`\n * as much as in `rebase`. `jobs` is Rebase's queue at `rebase.jobs` AND a\n * perfectly ordinary collection name, and revoking `public.jobs` from a project\n * that owns it leaves every read failing 42501 with correct policies applied\n * and nothing in the RLS logs to explain it. The `relrowsecurity` guard in\n * `revokeInternalTableSql` is what makes a common noun safe here — it is not a\n * licence to claim more of them.\n */\nexport const REBASE_INTERNAL_TABLES: readonly string[] = [\n // auth\n \"user_identities\",\n \"refresh_tokens\",\n \"password_reset_tokens\",\n \"magic_link_tokens\",\n \"mfa_factors\",\n \"mfa_challenges\",\n \"recovery_codes\",\n \"app_config\",\n \"schema_meta\",\n // platform services\n \"api_keys\",\n \"cron_logs\",\n \"cron_claims\",\n \"jobs\",\n \"rate_limit_hits\",\n \"idempotency_keys\",\n \"entity_history\",\n \"branches\",\n \"metric_samples\",\n // realtime channels — authorization for these lives in the channel rules the\n // server evaluates before it reads or writes, never in a row policy\n \"channel_messages\",\n \"channel_cursors\",\n \"channel_presence\",\n // migration bookkeeping\n \"atlas_schema_revisions\"\n];\n\n/** Postgres identifiers this module is willing to interpolate. */\nconst SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$]*$/;\n\n/**\n * A single statement that takes every privilege on `schema.table` away from the\n * end-user role.\n *\n * Wrapped in a `DO` block guarded on `pg_roles` for two reasons, both of which\n * happen in practice:\n *\n * - the role does not exist when the connection is unprivileged (Rebase then\n * relies on native RLS rather than a role switch), and a bare `REVOKE` on a\n * missing role is an error, not a no-op;\n * - the table may not exist yet — `cron_logs` never appears in a project with\n * no cron jobs — and `to_regclass` returning NULL has to be tolerated too.\n *\n * The third guard is the one that decides whether the *right* table is being\n * revoked. The names in {@link REBASE_INTERNAL_TABLES} are unqualified, and the\n * boot-time sweep in `ensureAppRole` applies all of them to every schema a\n * project uses — including the schema its own collections live in. A project is\n * free to call a collection `jobs`, `branches` or `api_keys`, and when it does,\n * the sweep was revoking `rebase_user`'s DML on the project's table on every\n * single boot. That is not a subtle degradation: the collection's whole API\n * answers 500 `permission denied for table …` from then on, which is what\n * happened to a public job board whose vacancies live in `public.jobs`.\n *\n * `relrowsecurity` separates the two cleanly, and it is the same fact this\n * module already relies on. Framework-internal tables carry no RLS — that is the\n * premise stated at the top of this file, and the reason a revoke is needed at\n * all. Every collection table has it enabled, because that is how Rebase\n * enforces `securityRules`. So \"RLS is off\" is exactly \"this is not somebody's\n * collection\", and the guard also subsumes the hand-carved `users` exception:\n * the auth user table is a collection, has RLS, and would now be skipped on its\n * own merits rather than by being kept off a list.\n *\n * One command, so it is safe on handles that speak the extended query protocol\n * and reject multi-statement strings.\n */\nexport function revokeInternalTableSql(schema: string, table: string): string {\n if (!SAFE_IDENTIFIER.test(schema)) {\n throw new Error(`Refusing to build SQL with an unsafe schema name: ${JSON.stringify(schema)}`);\n }\n if (!SAFE_IDENTIFIER.test(table)) {\n throw new Error(`Refusing to build SQL with an unsafe table name: ${JSON.stringify(table)}`);\n }\n const qualified = `\"${schema}\".\"${table}\"`;\n return `\n DO $rebase_revoke$\n BEGIN\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${REBASE_USER_ROLE}')\n AND to_regclass('${qualified}') IS NOT NULL\n AND NOT (SELECT relrowsecurity FROM pg_class WHERE oid = to_regclass('${qualified}')) THEN\n EXECUTE 'REVOKE ALL ON ${qualified} FROM ${REBASE_USER_ROLE}';\n END IF;\n END\n $rebase_revoke$;\n `.trim();\n}\n\n/**\n * Revoke on every internal table in `schema`, one statement at a time.\n *\n * Best-effort per table: a connection that does not own one of them (a\n * pre-provisioned database, a platform-managed ledger) cannot revoke on it, and\n * that must not take down a boot. The caller decides how loud to be — `onError`\n * exists so the driver can warn without this module importing a logger.\n */\nexport async function revokeInternalTableAccess(\n execute: (sql: string) => Promise<unknown>,\n schema: string,\n options?: { tables?: readonly string[]; onError?: (table: string, error: unknown) => void }\n): Promise<void> {\n for (const table of options?.tables ?? REBASE_INTERNAL_TABLES) {\n try {\n await execute(revokeInternalTableSql(schema, table));\n } catch (error) {\n options?.onError?.(table, error);\n }\n }\n}\n","import {\n DataSourceDefinition,\n ResolvedDataSource,\n DEFAULT_DATA_SOURCE_KEY,\n getDataSourceCapabilities\n} from \"@rebasepro/types\";\n\n/**\n * The subset of a collection needed to resolve its data source. Accepting a\n * structural type (rather than the full `CollectionConfig`) keeps this usable\n * from anywhere — frontend router, backend registry, editor — without coupling\n * to the collection union.\n */\nexport interface DataSourceResolvable {\n /** Preferred routing key. */\n dataSource?: string;\n /** Engine type discriminant (set on variant collection types). */\n engine?: string;\n /** Within-engine instance. */\n databaseId?: string;\n}\n\n/** A lookup of data-source definitions by key. */\nexport type DataSourceRegistry = Record<string, DataSourceDefinition>;\n\n/**\n * Build a keyed registry from a list of {@link DataSourceDefinition}s.\n * Later entries win on key collision.\n */\nexport function createDataSourceRegistry(definitions?: DataSourceDefinition[]): DataSourceRegistry {\n const registry: DataSourceRegistry = {};\n for (const def of definitions ?? []) {\n registry[def.key] = def;\n }\n return registry;\n}\n\n/**\n * Resolve the effective data source for a collection — the single source of\n * truth shared by the frontend router, the backend driver registry, and the\n * editor's capability lookups.\n *\n * Resolution order:\n * 1. The routing **key** is `collection.dataSource`, else\n * {@link DEFAULT_DATA_SOURCE_KEY}.\n * 2. If a definition is registered for that key, it provides `engine`,\n * `transport`, and `databaseId`.\n * 3. Otherwise values are synthesized: `engine` from `collection.engine`\n * (or the key, or `\"postgres\"`), `transport` defaults to `\"server\"`,\n * and `databaseId` from the collection.\n *\n * `capabilities` are always derived from the resolved `engine`, so two\n * data sources sharing an engine share capabilities.\n *\n * @param collection the collection (or any object carrying the routing fields)\n * @param registry optional registry of declared data sources\n */\nexport function resolveDataSource(\n collection: DataSourceResolvable | undefined,\n registry?: DataSourceRegistry\n): ResolvedDataSource {\n const key = collection?.dataSource ?? DEFAULT_DATA_SOURCE_KEY;\n const def = registry?.[key];\n\n const engine = def?.engine\n ?? collection?.engine\n ?? (key !== DEFAULT_DATA_SOURCE_KEY ? key : \"postgres\");\n\n const transport = def?.transport ?? \"server\";\n const databaseId = collection?.databaseId ?? def?.databaseId;\n\n return {\n key,\n engine,\n transport,\n databaseId,\n capabilities: getDataSourceCapabilities(engine)\n };\n}\n\n/**\n * Does a SQL toolchain own this collection's storage?\n *\n * \"Owns the storage\" means: something generates a table for it, pushes that\n * table to a database, plans its RLS policies, and reports it as drifted when\n * the two disagree. That is true of a Postgres collection and false of a\n * Firestore or MongoDB one, whose documents live in a store Rebase never\n * migrates — and the two were never told apart. Every stage of the SQL\n * toolchain took \"the collections\" to mean *all* of them, so a Firestore\n * collection declared next to the Postgres ones got a `pgTable` in the\n * generated schema, a `CREATE TABLE` at boot, RLS policies, and a place in the\n * `db push` include list — where its name shielding a same-named real table\n * from Atlas's exclude list is the one that can lose data.\n *\n * The answer is the resolved engine's {@link DataSourceCapabilities}, not a\n * name check: an engine registered through `registerDataSourceCapabilities`\n * gets the same treatment as the built-in ones.\n *\n * Deliberately answers **true** for an engine nobody has heard of. Build-time\n * tooling (the CLI, the schema generator) has no data-source registry to\n * resolve a `dataSource` key against, so an unknown key resolves to an unknown\n * engine — and the cost of the two mistakes is not symmetric. Wrongly\n * including a collection generates a table nothing writes to; wrongly excluding\n * one silently stops generating a table the app is serving from. Declare\n * `engine` on a collection that is not SQL-backed and this is exact.\n */\nexport function isRelationalCollection(\n collection: DataSourceResolvable | undefined,\n registry?: DataSourceRegistry\n): boolean {\n // The collection's own `engine` wins over a registered definition's. That\n // is the opposite of {@link resolveDataSource}'s precedence, deliberately:\n // there a definition describes where the data *goes*, so it should override;\n // here the question is what the author said this collection is, and a\n // collection declaring `engine: \"firestore\"` with no `dataSource` must not\n // come back as the default source's engine and be handed a table.\n const engine = collection?.engine\n ?? (collection?.dataSource ? resolveDataSource(collection, registry).engine : undefined);\n return getDataSourceCapabilities(engine).supportsRelations;\n}\n\n/**\n * The subset of `collections` a SQL toolchain owns — see\n * {@link isRelationalCollection}.\n *\n * Every stage that generates SQL from collections starts by calling this, so\n * the rule lives in one place rather than being re-decided per generator. It\n * keeps the input order.\n */\nexport function relationalCollections<C extends DataSourceResolvable>(\n collections: readonly C[],\n registry?: DataSourceRegistry\n): C[] {\n return collections.filter(collection => isRelationalCollection(collection, registry));\n}\n","import {\n ArrayProperty,\n CollectionCallbacks,\n EngineProperties,\n CollectionConfig,\n getDataSourceCapabilities,\n getDeclaredSubcollections,\n NumberProperty,\n Properties,\n Property,\n Relation,\n RelationProperty,\n StringProperty\n} from \"@rebasepro/types\";\nimport { deepEqual } from \"fast-equals\";\n\nimport {\n enumToObjectEntries,\n findRelation,\n getSubcollections,\n getTableName,\n resolveCollectionRelations,\n resolveRelation\n} from \"../util\";\nimport { deepClone, mergeDeep, removeFunctions } from \"@rebasepro/utils\";\nimport { DataSourceRegistry, resolveDataSource } from \"../data/resolveDataSource\";\n\nexport class CollectionRegistry {\n\n /**\n * Declared data sources, used during normalization to resolve each\n * collection's engine (so `dataSource`-only collections get the right\n * capabilities). Empty by default.\n */\n private dataSources: DataSourceRegistry = {};\n\n /**\n * Global lifecycle callbacks applied to every collection.\n * Runs on all data paths (REST, WebSocket, `rebase.dataAsAdmin`).\n * Execution order: global → collection → property callbacks.\n */\n private _globalCallbacks?: CollectionCallbacks;\n\n /**\n * Set global lifecycle callbacks that apply to every collection.\n * Typically called once during backend initialization.\n */\n setGlobalCallbacks(callbacks: CollectionCallbacks): void {\n this._globalCallbacks = callbacks;\n }\n\n /**\n * Get the currently registered global callbacks, if any.\n */\n getGlobalCallbacks(): CollectionCallbacks | undefined {\n return this._globalCallbacks;\n }\n\n // Normalized runtime layer (used by Data Grid / UI)\n private collectionsByTableName = new Map<string, CollectionConfig>();\n private collectionsBySlug = new Map<string, CollectionConfig>();\n private rootCollections: CollectionConfig[] = [];\n private cachedCollectionsList: CollectionConfig[] | null = null;\n\n // Raw configuration layer (used by Collection Editor AST generator)\n private rawCollectionsByTableName = new Map<string, CollectionConfig>();\n private rawCollectionsBySlug = new Map<string, CollectionConfig>();\n private rawRootCollections: CollectionConfig[] = [];\n private cachedRawCollectionsList: CollectionConfig[] | null = null;\n\n // Entity of raw input for idempotency check — compared BEFORE normalization\n // to avoid the issue where normalization creates new objects that always fail equality.\n private lastRawInputEntity: ReturnType<typeof removeFunctions>[] | null = null;\n\n constructor(collections?: CollectionConfig[], dataSources?: DataSourceRegistry) {\n if (dataSources) this.dataSources = dataSources;\n if (collections) {\n this.registerMultiple(collections);\n }\n }\n\n /**\n * Provide the declared data sources used to resolve each collection's\n * engine during normalization. Set this before registering collections.\n * Returns true if the registry changed (callers may re-register).\n */\n setDataSources(dataSources: DataSourceRegistry): boolean {\n if (deepEqual(this.dataSources, dataSources)) return false;\n this.dataSources = dataSources ?? {};\n return true;\n }\n\n reset() {\n this.collectionsByTableName.clear();\n this.collectionsBySlug.clear();\n this.rootCollections = [];\n this.cachedCollectionsList = null;\n\n this.rawCollectionsByTableName.clear();\n this.rawCollectionsBySlug.clear();\n this.rawRootCollections = [];\n this.cachedRawCollectionsList = null;\n }\n\n /**\n * Registers a collection and its subcollections recursively.\n * Returns true if the collections have changed, false otherwise.\n *\n * Idempotent: compares the raw input (before normalization) against a stored\n * entity. Only re-normalizes and re-registers when the raw input actually changed.\n * @param collections\n */\n registerMultiple(collections: CollectionConfig[]): boolean {\n // Compare raw input BEFORE normalization to detect actual changes.\n // This avoids the old issue where normalization creates new objects\n // that always fail deep-equal even when the source data is identical.\n const rawEntity = collections.map(c => removeFunctions(c));\n if (this.lastRawInputEntity && deepEqual(this.lastRawInputEntity, rawEntity)) {\n return false;\n }\n\n this.reset();\n // Phase 0: Populate maps with raw collections first for string target resolution\n collections.forEach((c) => {\n if (c.slug) {\n this.collectionsBySlug.set(c.slug, c);\n }\n this.collectionsByTableName.set(getTableName(c), c);\n });\n\n const normalizedCollections = collections.map(c => this.normalizeCollection({ ...c }));\n\n // Phase 1: Register all top-level collections first (without recursion).\n // This ensures that injected entityViews (e.g. History tab) are preserved.\n // Without this, _registerRecursively could register a relation-target collection\n // (e.g. Tags from Posts.relations) using the raw module object (without injected views)\n // before the top-level Tags collection (with injected views) gets its turn.\n normalizedCollections.forEach((c, index) => {\n const raw = deepClone(collections[index]);\n this.rootCollections.push(c);\n this.rawRootCollections.push(raw);\n\n const normalized = this.normalizeCollection(c);\n this.collectionsByTableName.set(getTableName(normalized), normalized);\n this.rawCollectionsByTableName.set(getTableName(raw), raw);\n if (normalized.slug) {\n this.collectionsBySlug.set(normalized.slug, normalized);\n }\n if (raw.slug) {\n this.rawCollectionsBySlug.set(raw.slug, raw);\n }\n });\n\n // Phase 2: Now recurse into subcollections (relations, etc.)\n normalizedCollections.forEach((c) => {\n const subcollections = getSubcollections(c);\n if (subcollections && subcollections.length > 0) {\n subcollections.forEach((subCollection) => {\n if (!subCollection) return;\n // Spread to avoid mutating the original target() return value\n this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));\n });\n }\n });\n\n // Store the entity for future comparisons\n this.lastRawInputEntity = rawEntity;\n\n return true;\n }\n\n register(collection: CollectionConfig, rawCollection?: CollectionConfig) {\n const raw = rawCollection ? deepClone(rawCollection) : deepClone(collection);\n\n this.rootCollections.push(collection);\n this.rawRootCollections.push(raw);\n\n this._registerRecursively(collection, raw);\n }\n\n private _registerRecursively(collection: CollectionConfig, rawCollection: CollectionConfig) {\n if (this.collectionsByTableName.has(getTableName(collection))) {\n return;\n }\n\n const normalizedCollection = this.normalizeCollection(collection);\n this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);\n this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);\n\n if (normalizedCollection.slug) {\n this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);\n }\n if (rawCollection.slug) {\n this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);\n }\n\n // Use the normalized collection for subcollection discovery so that\n // both inline-extracted and explicit relations are considered.\n const subcollections = getSubcollections(normalizedCollection);\n\n if (subcollections && subcollections.length > 0) {\n subcollections.forEach((subCollection) => {\n if (!subCollection) return;\n // Spread to avoid mutating the original target() return value\n this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));\n });\n }\n }\n\n public normalizeCollection(collection: CollectionConfig): CollectionConfig {\n // Work on a shallow copy to avoid mutating the caller's reference.\n // This is critical for idempotency (the raw input must not be changed)\n // and for preventing mutation of module-level collection singletons.\n const result = { ...collection } as CollectionConfig;\n\n // 0. Resolve and stamp `dataSource` and `engine` on the normalized copy.\n // After this block every normalized collection has both fields set,\n // so downstream code can read them directly without calling\n // `resolveDataSource()`. Only the normalized layer is affected —\n // the raw layer used by the collection editor keeps the author's\n // original fields.\n {\n const resolved = resolveDataSource(result, this.dataSources);\n if (!result.dataSource) (result as { dataSource?: string }).dataSource = resolved.key;\n if (!result.engine) (result as { engine?: string }).engine = resolved.engine;\n }\n\n // Relations are left exactly as authored.\n //\n // This used to hoist every inline relation property into\n // `collection.relations`, merge it with the declared ones, and run each\n // through `sanitizeRelation` — a pass that guessed at missing fields and\n // fell back to the raw relation when it threw. `resolveCollectionRelations`\n // now reads both sources itself and defaults deterministically, so there\n // is nothing to hoist, nothing to merge and nothing to guess.\n //\n // The hoisting also had a defect worth not reinstating: it flattened\n // relations declared inside a `map` up to the collection's top level,\n // where they became child-view tabs keyed by the inner property key.\n\n // Stamp each relation property with its resolved relation.\n const properties: Properties = this.normalizeProperties(result.properties, result);\n result.properties = properties as EngineProperties;\n\n // `childCollections` is deliberately NOT populated here.\n //\n // It used to be, from the same many-relations `getEntityChildViews`\n // reads — but stamped with the *target's* slug rather than the relation\n // key, and then cached onto the collection, so the registry's version\n // shadowed the correct one for every consumer downstream. Deriving on\n // read leaves one implementation and keeps `childCollections` meaning\n // what it documents: a custom driver's explicit override.\n return result;\n }\n\n private normalizeProperties(properties: Properties, collection: CollectionConfig): Properties {\n const newProperties: Properties = {};\n for (const key in properties) {\n newProperties[key] = this.normalizeProperty(key, properties[key], collection);\n }\n return newProperties;\n }\n\n private normalizeProperty(key: string, property: Property, collection: CollectionConfig): Property {\n const newProperty = { ...property };\n\n if (newProperty.type === \"map\" && newProperty.properties) {\n newProperty.properties = this.normalizeProperties(newProperty.properties, collection);\n } else if (newProperty.type === \"array\") {\n // Cast to get a properly typed mutable reference\n const arrayProp = newProperty as ArrayProperty;\n if (arrayProp.of) {\n if (Array.isArray(arrayProp.of)) {\n (arrayProp as { of: Property | Property[] }).of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, collection));\n } else {\n arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, collection);\n }\n } else if (arrayProp.oneOf && arrayProp.oneOf.properties) {\n arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, collection);\n }\n } else if ((newProperty.type === \"string\" || newProperty.type === \"number\") && newProperty.enum) {\n const stringOrNumberProperty = newProperty as StringProperty | NumberProperty;\n if (typeof stringOrNumberProperty.enum === \"object\" && !Array.isArray(stringOrNumberProperty.enum)) {\n stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];\n }\n } else if (newProperty.type === \"relation\") {\n const relationProperty = newProperty as RelationProperty;\n\n // A property either declares its link inline, or names one the\n // collection declares. Resolve the first directly; look the second\n // up by name. Either way the property carries the fully-defaulted\n // relation, so no consumer has to re-derive it.\n if (relationProperty.relation) {\n relationProperty.resolvedRelation = resolveRelation(relationProperty.relation, collection, key);\n } else {\n const declared = resolveCollectionRelations(collection)[key];\n if (declared) {\n relationProperty.resolvedRelation = declared;\n } else {\n // The boot validator refuses this shape outright, naming the\n // property and both ways to fix it — see\n // `checkRelationPropertiesResolve` in @rebasepro/server. This\n // stays as the second line, for the registries built outside\n // a validated boot: the panel's, and the collection editor's\n // preview of a config being written.\n //\n // Still `console.warn`. There is no logger below\n // @rebasepro/server, and this package runs in the browser as\n // well as on the server, so acquiring one is a design\n // decision rather than a substitution.\n console.warn(\n `Relation property '${key}' on '${collection.slug}' names no relation: it has no ` +\n \"`relation` block, and the collection's `relations` array has no entry called \" +\n `'${key}'. The field will render no picker, generate no foreign key, and return ` +\n \"nothing from `include()`.\"\n );\n }\n }\n }\n\n return newProperty;\n }\n\n get(path: string): CollectionConfig | undefined {\n // First try slug lookup\n const bySlug = this.collectionsBySlug.get(path);\n if (bySlug) return bySlug;\n\n // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)\n if (path.includes(\"-\")) {\n const normalized = path.replace(/-/g, \"_\");\n const byNormalized = this.collectionsBySlug.get(normalized);\n if (byNormalized) return byNormalized;\n }\n\n // Fallback to table name lookup\n return this.collectionsByTableName.get(path);\n }\n\n /**\n * Gets the pristine, un-normalized collection exactly as it was provided.\n * Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.\n */\n getRaw(path: string): CollectionConfig | undefined {\n const bySlug = this.rawCollectionsBySlug.get(path);\n if (bySlug) return bySlug;\n\n // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)\n if (path.includes(\"-\")) {\n const normalized = path.replace(/-/g, \"_\");\n const byNormalized = this.rawCollectionsBySlug.get(normalized);\n if (byNormalized) return byNormalized;\n }\n\n return this.rawCollectionsByTableName.get(path);\n }\n\n /**\n * Get collection by resolving multi-segment paths through relations\n * e.g., \"authors/70/posts\" resolves to the posts collection\n */\n getCollectionByPath(collectionPath: string): CollectionConfig | undefined {\n // Handle simple single collection path\n if (!collectionPath.includes(\"/\")) {\n return this.get(collectionPath);\n }\n\n // Handle multi-segment paths by resolving through relations\n const pathSegments = collectionPath.split(\"/\").filter(p => p);\n\n if (pathSegments.length < 3 || pathSegments.length % 2 === 0) {\n throw new Error(`Invalid relation path: ${collectionPath}. Expected format: collection/id/relation or collection/id/relation/id/relation`);\n }\n\n // Start with the root collection\n const rootCollectionPath = pathSegments[0];\n let currentCollection = this.get(rootCollectionPath);\n\n if (!currentCollection) {\n throw new Error(`Root collection not found: ${rootCollectionPath}`);\n }\n\n // Navigate through the path using relations\n for (let i = 2; i < pathSegments.length; i += 2) {\n const relationKey = pathSegments[i];\n\n // Get relations for current collection\n if (!getDataSourceCapabilities(currentCollection.engine).supportsRelations) {\n throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses engine '${currentCollection.engine}'`);\n }\n const resolvedRelations = resolveCollectionRelations(currentCollection);\n const relation = findRelation(resolvedRelations, relationKey);\n\n if (!relation) {\n throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);\n }\n\n // Move to the target collection.\n //\n // By the relation's own target, never by a slug lookup on its\n // *name*: `this.get(relation.relationName)` searches the global slug\n // map, so a relation named `people` that targets `notes` resolved to\n // an unrelated root collection called `people` — and a nested write\n // then ran that collection's callbacks against its properties.\n // The registered instance is preferred, matched by table, to pick up\n // whatever normalization and injection it received.\n const target = relation.target();\n currentCollection = this.collectionsByTableName.get(getTableName(target))\n ?? this.normalizeCollection(target);\n\n // If there are more segments, continue navigation\n if (i + 1 < pathSegments.length) {\n // Skip entity ID segment\n }\n }\n\n return currentCollection;\n }\n\n getCollections(): CollectionConfig[] {\n if (!this.cachedCollectionsList) {\n this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());\n }\n return this.cachedCollectionsList;\n }\n\n getRawCollections(): CollectionConfig[] {\n if (!this.cachedRawCollectionsList) {\n this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());\n }\n return this.cachedRawCollectionsList;\n }\n\n /**\n * Resolves a multi-segment path like \"products/123/locales\" and returns\n * information about the collections and entity IDs along the path\n */\n resolvePathToCollections(path: string): {\n collections: CollectionConfig[],\n entityIds: (string | number)[],\n finalCollection: CollectionConfig\n } {\n const pathSegments = path.split(\"/\").filter(p => p);\n\n if (pathSegments.length === 0) {\n throw new Error(`Invalid path: ${path}`);\n }\n\n if (pathSegments.length % 2 !== 1) {\n throw new Error(`Invalid collection path: ${path}. It must have an odd number of segments.`);\n }\n\n const collections: CollectionConfig[] = [];\n const entityIds: (string | number)[] = [];\n\n // Start with the first collection\n let currentCollection = this.get(pathSegments[0]);\n\n if (!currentCollection) {\n throw new Error(`Unknown collection path or slug: ${pathSegments[0]}`);\n }\n\n collections.push(currentCollection);\n\n // Process the rest of the path in pairs (entityId, subcollectionSlug)\n for (let i = 1; i < pathSegments.length; i += 2) {\n const entityId = pathSegments[i];\n entityIds.push(entityId);\n\n if (i + 1 < pathSegments.length) {\n const subcollectionSlug = pathSegments[i + 1];\n const subcollections: CollectionConfig[] | undefined = getSubcollections(currentCollection);\n if (!subcollections || subcollections.length === 0) {\n throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);\n }\n\n const subcollection: CollectionConfig | undefined = subcollections.find(c => c.slug === subcollectionSlug);\n if (!subcollection) {\n throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);\n }\n // The child as resolved, not whatever root collection happens to\n // share its slug. Re-looking it up globally both risked the wrong\n // collection and discarded the relation's `overrides`, which are\n // applied when the child view is built.\n currentCollection = this.normalizeCollection(subcollection);\n collections.push(currentCollection);\n }\n }\n\n return {\n collections,\n entityIds,\n finalCollection: currentCollection\n };\n }\n\n}\n\n","import { defineCollection } from \"../util/builders\";\n\n/**\n * Default users collection.\n *\n * Prepended to the developer's collections array by the admin and server.\n * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers\n * override by defining their own collection with `slug: \"users\"`.\n *\n * Schema only — no `admin` block. This package is on the backend's dependency path,\n * where that field does not exist: `@rebasepro/cms-types` adds it by declaration\n * merging, and a BaaS install never installs that. The scaffolded\n * `config/collections/users.ts` carries the presentation for projects that want this\n * collection in their panel, which is also where it is editable.\n */\nexport const defaultUsersCollection = defineCollection({\n name: \"Users\",\n singularName: \"User\",\n slug: \"users\",\n auth: true,\n table: \"users\",\n schema: \"rebase\",\n securityRules: [\n { operation: \"select\",\nroles: [\"admin\"] },\n { operations: [\"insert\", \"update\", \"delete\"],\nroles: [\"admin\"] }\n ],\n properties: {\n id: {\n name: \"ID\",\n type: \"string\",\n isId: \"uuid\"\n },\n email: {\n name: \"Email\",\n type: \"string\",\n validation: { required: true,\nunique: true }\n },\n displayName: {\n name: \"Name\",\n type: \"string\",\n columnName: \"display_name\",\n validation: { required: true }\n },\n photoURL: {\n name: \"Photo URL\",\n type: \"string\",\n columnName: \"photo_url\"\n },\n roles: {\n name: \"Roles\",\n type: \"array\",\n columnType: \"text[]\",\n of: {\n name: \"Role\",\n type: \"string\",\n enum: {\n admin: \"Admin\",\n editor: \"Editor\",\n viewer: \"Viewer\"\n }\n }\n },\n passwordHash: {\n name: \"Password Hash\",\n type: \"string\",\n columnName: \"password_hash\",\n excludeFromApi: true\n },\n emailVerified: {\n name: \"Email Verified\",\n type: \"boolean\",\n columnName: \"email_verified\",\n defaultValue: false\n },\n emailVerificationToken: {\n name: \"Email Verification Token\",\n type: \"string\",\n columnName: \"email_verification_token\",\n excludeFromApi: true\n },\n emailVerificationSentAt: {\n name: \"Email Verification Sent At\",\n type: \"date\",\n columnName: \"email_verification_sent_at\"\n },\n metadata: {\n name: \"Metadata\",\n type: \"map\",\n keyValue: true,\n properties: {},\n defaultValue: {}\n },\n createdAt: {\n name: \"Created At\",\n type: \"date\",\n columnName: \"created_at\",\n autoValue: \"on_create\"\n },\n updatedAt: {\n name: \"Updated At\",\n type: \"date\",\n columnName: \"updated_at\",\n autoValue: \"on_update\"\n }\n }\n});\n","import type { CollectionConfig, FieldAccess, Property } from \"@rebasepro/types\";\n\n/**\n * Field-level access control: one mechanism, read by every enforcement point.\n *\n * A collection's `securityRules` decide which *rows* a caller reaches;\n * `property.access` decides which *fields* of a reached row they see and may\n * set. The two are independent — a field rule never widens row access, and a\n * row a caller cannot read has no fields to talk about.\n *\n * `excludeFromApi` is sugar for `access: { read: [], write: [] }` and is\n * normalised into it by {@link effectiveAccess}, which is the only place either\n * spelling is read. It used to be its own code path in five files — the read\n * strip, the write refusal, the SDK generator, the OpenAPI schema builder and\n * the filter-parameter builder — and the second rule would have made ten.\n * There is one predicate now, and the flag is a shorthand for it.\n *\n * @module\n */\n\n/**\n * The caller a field rule is judged against: whatever the call context carries\n * as the user's application roles.\n *\n * `undefined` is the trusted server plane — an in-process `rebase.data` call\n * with no request behind it, the auth adapter writing a password hash, a\n * migration. Every API boundary has a viewer: an unauthenticated REST request is\n * scoped as `{ uid: ANONYMOUS_USER_ID, roles: [\"anon\"] }` before it reaches a\n * driver, so \"no viewer\" cannot be reached from outside.\n */\nexport interface FieldViewer {\n roles?: readonly string[];\n}\n\n/**\n * The role that satisfies any non-empty list.\n *\n * The same arm every baseline policy carries: `security_rules` injects\n * `rolesOverlap(['admin'])` into the default read and write policies, and\n * `rebase.dataAsAdmin` is scoped with `{ uid: \"service\", roles: [\"admin\"] }`.\n * Without this an author could declare `access: { read: [\"hr\"] }` and lock the\n * administrator out of a column of their own database — and lock the Studio out\n * of rendering it.\n */\nexport const ADMIN_ROLE = \"admin\";\n\n/**\n * What a property's access rules actually are, with `excludeFromApi` expanded.\n *\n * Returns `undefined` when the property constrains nothing, so callers can skip\n * the whole check for the overwhelmingly common case.\n */\nexport function effectiveAccess(property: Property | undefined): FieldAccess | undefined {\n if (!property) return undefined;\n if (property.excludeFromApi) return EXCLUDED_ACCESS;\n const access = property.access;\n if (!access) return undefined;\n if (access.read === undefined && access.write === undefined) return undefined;\n return access;\n}\n\n/** The rule `excludeFromApi: true` expands to. Frozen: it is shared by every caller. */\nconst EXCLUDED_ACCESS: FieldAccess = Object.freeze({ read: Object.freeze([]), write: Object.freeze([]) });\n\n/**\n * Does a caller holding `roles` satisfy `allowed`?\n *\n * Three cases, and the middle one is the one worth stating out loud:\n *\n * - `allowed` omitted — the field carries no rule of its own, so the row's\n * policies have already answered. True.\n * - `allowed` empty — nobody, at any privilege, through any API. Not the admin,\n * not the service key, not the trusted plane reading on a caller's behalf.\n * This is what `excludeFromApi` has always meant on the read side, and\n * collapsing the two spellings means the empty list has to keep meaning it.\n * - `allowed` non-empty — one of the named roles, or `admin`, or no viewer at\n * all (the trusted server plane, which is not an API caller).\n */\nfunction satisfies(allowed: readonly string[] | undefined, viewer: FieldViewer | undefined): boolean {\n if (allowed === undefined) return true;\n if (allowed.length === 0) return false;\n if (!viewer) return true;\n const roles = viewer.roles;\n if (!roles || roles.length === 0) return false;\n return roles.includes(ADMIN_ROLE) || allowed.some(role => roles.includes(role));\n}\n\n/** May this caller receive this field's value? */\nexport function canReadField(property: Property | undefined, viewer: FieldViewer | undefined): boolean {\n const access = effectiveAccess(property);\n return access ? satisfies(access.read, viewer) : true;\n}\n\n/** May this caller set this field's value? */\nexport function canWriteField(property: Property | undefined, viewer: FieldViewer | undefined): boolean {\n const access = effectiveAccess(property);\n return access ? satisfies(access.write, viewer) : true;\n}\n\n/**\n * The names on this collection a caller may not touch, in the two spellings a\n * caller can write them in.\n *\n * `declared` is the property keys, which is what has to leave a *known-fields*\n * set. `refused` is those plus the physical column names behind them: a caller\n * who knows the table can send `password_hash` as readily as `passwordHash`, and\n * a rule that only knew the wire name would be one rename away from useless.\n *\n * `kind` picks which half of the rule is read; nothing else differs.\n */\nexport function restrictedFieldNames(\n collection: CollectionConfig,\n viewer: FieldViewer | undefined,\n kind: \"read\" | \"write\"\n): { declared: string[]; refused: Set<string> } {\n const declared: string[] = [];\n const refused = new Set<string>();\n const allowed = kind === \"read\" ? canReadField : canWriteField;\n\n for (const [name, property] of Object.entries(collection.properties ?? {})) {\n if (allowed(property as Property, viewer)) continue;\n declared.push(name);\n refused.add(name);\n const columnName = (property as Property).columnName;\n if (columnName) refused.add(columnName);\n }\n return { declared, refused };\n}\n\n/**\n * True when nothing on this collection restricts a field, for either direction.\n *\n * Every read of every row runs through the strip, so the collection that has no\n * rules — which is almost all of them — has to cost one property walk and no\n * allocation.\n */\nexport function hasFieldAccessRules(collection: CollectionConfig): boolean {\n for (const property of Object.values(collection.properties ?? {})) {\n if (effectiveAccess(property as Property)) return true;\n }\n return false;\n}\n","import type { OrderByTuple } from \"@rebasepro/types\";\n\n/**\n * The keyset-cursor wire codec.\n *\n * ## Why this is one module\n *\n * Keyset pagination was implemented three times and reachable once. The driver\n * has a NULL-correct multi-key comparison (`FetchService.buildKeysetComparison`)\n * that only a WebSocket `startAfter` could reach; REST could not seek at all;\n * and the SDK's `iterate({cursor})` re-implemented a *single*-column keyset as a\n * `where` clause, which threw on any multi-key sort and silently dropped rows\n * whose sort value was NULL. Three implementations, three answers to \"what is\n * page two\".\n *\n * There is now one. The driver's comparison is the implementation; this module\n * is the only thing that says how a cursor is written down, and every transport\n * — the REST `?after=`, the WebSocket `startAfter`, the SDK's `iterate()` —\n * carries the string this produces and hands it back unread.\n *\n * ## What a cursor holds\n *\n * The sort keys the query was ordered by, the last served row's value for each\n * of them, and that row's id. The keys travel *with* the values because a\n * cursor that carried only values would be silently reinterpretable: paging a\n * `created_at DESC` listing and then asking for `title ASC` would seek on the\n * dates as though they were titles. Carrying the keys makes that a refusal\n * ({@link CursorMismatchError}) rather than a page of arbitrary rows.\n *\n * ## Opacity\n *\n * The encoding is base64url of JSON, and it is **not** API. It is opaque so it\n * can change — adding a key, changing how a value is tagged — without every\n * client that learned to read it breaking. Nothing outside this file parses it.\n *\n * @module\n */\n\n/** The decoded contents of a cursor. */\nexport interface DecodedCursor {\n /** The sort keys the cursor was produced under, in order of significance. */\n orderBy: OrderByTuple[];\n /** The last served row's value for each sort key, by field name. */\n values: Record<string, unknown>;\n /** The last served row's id, which breaks ties on the last key. */\n id: unknown;\n}\n\n/** A cursor that cannot be read at all — truncated, re-encoded, or invented. */\nexport class CursorError extends Error {\n readonly code = \"INVALID_CURSOR\";\n constructor(detail: string) {\n super(\n `Invalid \\`after\\` cursor: ${detail}. Pass back the \\`meta.nextCursor\\` ` +\n \"from the previous page unchanged — it is opaque and must not be built by hand.\"\n );\n this.name = \"CursorError\";\n Object.setPrototypeOf(this, CursorError.prototype);\n }\n}\n\n/**\n * A cursor that reads fine but describes a different query.\n *\n * Separate from {@link CursorError} because the fix is different: this one is\n * not a corrupt string, it is a correct cursor used against a sort it was not\n * produced under. Seeking anyway would return rows in an order nobody asked\n * for, and — worse — would look like it worked.\n */\nexport class CursorMismatchError extends Error {\n readonly code = \"CURSOR_ORDER_MISMATCH\";\n constructor(cursorKeys: string[], queryKeys: string[]) {\n super(\n `The \\`after\\` cursor was produced by a query ordered by ` +\n `${cursorKeys.map(k => `\"${k}\"`).join(\", \") || \"(nothing)\"}, but this query orders by ` +\n `${queryKeys.map(k => `\"${k}\"`).join(\", \") || \"(nothing)\"}. A cursor only continues the ` +\n \"listing it came from — keep `orderBy` identical across pages, or drop `after` to start over.\"\n );\n this.name = \"CursorMismatchError\";\n Object.setPrototypeOf(this, CursorMismatchError.prototype);\n }\n}\n\n/**\n * Tag for a value whose JSON round-trip would otherwise lose its type.\n *\n * A `timestamp` column comes back from the driver as a `Date`; JSON turns it\n * into a string, and the string would then be compared against the column by\n * whatever cast Postgres chose. Round-tripping it as a `Date` keeps the\n * comparison the one the ORDER BY made.\n */\nconst DATE_TAG = \"$date\";\n\nfunction encodeValue(value: unknown): unknown {\n if (value instanceof Date) return { [DATE_TAG]: value.toISOString() };\n return value;\n}\n\nfunction decodeValue(value: unknown): unknown {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const tagged = (value as Record<string, unknown>)[DATE_TAG];\n if (typeof tagged === \"string\") {\n const date = new Date(tagged);\n return Number.isNaN(date.getTime()) ? tagged : date;\n }\n }\n return value;\n}\n\n/** base64url, without depending on Node's Buffer (this package runs in browsers). */\nfunction toBase64Url(text: string): string {\n const bytes = new TextEncoder().encode(text);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(encoded: string): string {\n const padded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\")\n + \"=\".repeat((4 - (encoded.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode \"everything strictly after this row, in this order\".\n *\n * @param orderBy the sort keys the listing ran under, in order of significance\n * @param row the last row served, which the next page picks up after\n * @param id that row's id — the tiebreaker every keyset comparison ends on\n * @returns the opaque cursor, or `undefined` when no cursor can describe the\n * page. That is not a failure: a listing sorted by relevance has no stored\n * value to compare a later page against (scores are computed per query and\n * are not on the same scale between two of them), so it pages by offset and\n * `meta.nextCursor` is simply absent.\n */\nexport function encodeCursor(\n orderBy: OrderByTuple[] | undefined,\n row: Record<string, unknown>,\n id: unknown\n): string | undefined {\n if (id === undefined || id === null) return undefined;\n const keys = orderBy ?? [];\n // A key whose value is not on the row cannot be seeked past. Rather than\n // emit a cursor that the next request would refuse, emit none — the caller\n // falls back to offset paging, which is what it did before cursors existed.\n const values: Record<string, unknown> = {};\n for (const [field] of keys) {\n if (!(field in row)) return undefined;\n values[field] = encodeValue(row[field]);\n }\n return toBase64Url(JSON.stringify({ k: keys, v: values, i: encodeValue(id) }));\n}\n\n/**\n * Read a cursor produced by {@link encodeCursor}.\n *\n * @throws {CursorError} when the string is not a cursor this codec wrote.\n */\nexport function decodeCursor(raw: string): DecodedCursor {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fromBase64Url(raw.trim()));\n } catch {\n throw new CursorError(\"it is not a cursor this API issued\");\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new CursorError(\"it does not decode to a cursor\");\n }\n const body = parsed as { k?: unknown; v?: unknown; i?: unknown };\n if (!Array.isArray(body.k)) throw new CursorError(\"it carries no sort keys\");\n if (body.i === undefined) throw new CursorError(\"it carries no row id\");\n\n const orderBy: OrderByTuple[] = [];\n for (const entry of body.k) {\n if (!Array.isArray(entry) || typeof entry[0] !== \"string\") {\n throw new CursorError(\"one of its sort keys is malformed\");\n }\n const direction = entry[1] === \"desc\" ? \"desc\" : \"asc\";\n orderBy.push(entry[2] === \"first\" || entry[2] === \"last\"\n ? [entry[0], direction, entry[2]]\n : [entry[0], direction]);\n }\n\n const rawValues = (body.v && typeof body.v === \"object\" && !Array.isArray(body.v))\n ? body.v as Record<string, unknown>\n : {};\n const values: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(rawValues)) values[field] = decodeValue(value);\n\n return { orderBy, values, id: decodeValue(body.i) };\n}\n\n/**\n * The `orderBy` a request should run under, given a cursor and whatever sort\n * the request itself named.\n *\n * A request that names no sort **adopts the cursor's** — that is what makes\n * `find({ after })` work without restating the `orderBy` from the previous\n * call, and it cannot be wrong, since the cursor is the only sort in play.\n * A request that names one must name the *same* one, key for key, direction for\n * direction, nulls for nulls; anything else is {@link CursorMismatchError}.\n *\n * @throws {CursorMismatchError}\n */\nexport function reconcileCursorOrder(\n cursor: DecodedCursor,\n requested: OrderByTuple[] | undefined\n): OrderByTuple[] {\n if (!requested || requested.length === 0) return cursor.orderBy;\n const spell = (keys: OrderByTuple[]) =>\n keys.map(([field, direction, nulls]) => `${field}:${direction}${nulls ? `:${nulls}` : \"\"}`);\n const cursorKeys = spell(cursor.orderBy);\n const queryKeys = spell(requested);\n if (cursorKeys.length !== queryKeys.length\n || cursorKeys.some((key, i) => key !== queryKeys[i])) {\n throw new CursorMismatchError(cursorKeys, queryKeys);\n }\n return requested;\n}\n\n/**\n * The `startAfter` shape the driver contract takes, built from a cursor.\n *\n * The driver has always accepted `{ id, values }`; this is the one place that\n * shape is produced, so the REST route and the WebSocket ingress cannot drift\n * into two spellings of the same seek.\n */\nexport function cursorToStartAfter(cursor: DecodedCursor): Record<string, unknown> {\n return { id: cursor.id, values: cursor.values };\n}\n","import type { NullsPlacement, OrderBySortTuple, OrderBySpec, OrderByTuple } from \"@rebasepro/types\";\nimport { isRelationAggregateSort, sortKeyToString } from \"@rebasepro/types\";\n\n/**\n * Sort-order wire codec.\n *\n * This is the ONLY module that knows about the colon-delimited wire format\n * (`\"field:direction\"`) used in HTTP query parameters, and about the JSON-array\n * form that carries a multi-column sort over the same parameter.\n * Everything else speaks {@link OrderByTuple} exclusively.\n *\n * Mirrors the filter architecture in `filter-dialect.ts`.\n *\n * @module\n */\n\n/**\n * Collapse the one-key and many-key spellings of a sort into the list form.\n *\n * `[\"a\", \"desc\"]` and `[[\"a\", \"desc\"]]` mean the same thing and normalize to\n * the same value; the two are told apart by whether the first element is\n * itself an array, which no field name ever is.\n *\n * This is also where a {@link RelationAggregateSort} object stops being an\n * object. Above this function a sort key may be either spelling; below it,\n * every key is a string — which is what `OrderByTuple`, the REST parameter, the\n * driver contract and the cursor all already were. Doing it here means the one\n * place that already collapses the two *shapes* of a sort also collapses the\n * two *spellings* of a key, rather than every consumer learning about both.\n *\n * @returns The keys in order of significance, or `undefined` for no sort. An\n * empty list also returns `undefined` — \"sort by nothing\" is no sort, and\n * letting `[]` through would have every layer below re-deciding what it meant.\n */\nexport function normalizeOrderBy(orderBy?: OrderBySpec): OrderByTuple[] | undefined {\n if (!orderBy || orderBy.length === 0) return undefined;\n // An aggregate key is an object, so the first element being an array still\n // tells the list form from the single-tuple one — no field name is an\n // array, and neither is an aggregate key.\n const list = Array.isArray(orderBy[0])\n ? orderBy as OrderBySortTuple[]\n : [orderBy as OrderBySortTuple];\n if (list.length === 0) return undefined;\n // Through `toStrictTuple`, not a destructure. `([key, direction]) => …` over\n // whatever it was handed is only safe for a caller the types checked, and\n // this is reached straight from `find({ orderBy })` — where the plausible\n // mistakes are an object (`{ title: \"asc\" }`, which is how every other\n // query API spells a sort) and a bare number. Both used to come back as\n // `TypeError: object is not iterable`, from a package the caller has never\n // heard of, with no `code` and no field name, while the same call's `where`\n // clause answers with a `RebaseClientError` naming the field and the fix.\n return list.map((entry, index) => toStrictTuple(entry, index));\n}\n\n/**\n * The most significant sort key, for a caller that can only express one —\n * a column header's arrow, a URL parameter, a driver that has not been taught\n * the list form.\n */\nexport function primaryOrderBy(orderBy?: OrderBySpec): OrderByTuple | undefined {\n return normalizeOrderBy(orderBy)?.[0];\n}\n\n/**\n * Collapse the driver-level `{orderBy, order}` pair into the list form.\n *\n * The driver contract spells a single-column sort as a field name plus a\n * separate direction, and a multi-column one as a list of tuples that leaves\n * `order` meaningless. Every driver reads both through here so neither\n * spelling has to be handled twice.\n *\n * An absent direction means ascending — the same thing a bare `?orderBy=name`\n * has always meant over HTTP. The Postgres driver used to read the same pair as\n * *descending* while Mongo read it as ascending, so one field name and no\n * direction described two different queries depending on which database was\n * underneath. Neither had a caller: every path in the workspace passes a\n * direction, which is why the disagreement went unnoticed rather than being\n * load-bearing.\n */\nexport function normalizeDriverOrderBy(\n orderBy?: string | OrderByTuple[],\n order?: \"asc\" | \"desc\"\n): OrderByTuple[] | undefined {\n if (!orderBy) return undefined;\n if (typeof orderBy === \"string\") return [[orderBy, order === \"desc\" ? \"desc\" : \"asc\"]];\n return orderBy.length > 0 ? orderBy : undefined;\n}\n\n/** A sort whose *shape* is unusable, as opposed to one naming a field that does not exist. */\nexport class OrderBySpecError extends Error {\n readonly code = \"INVALID_ORDER_BY\";\n constructor(detail: string) {\n super(\n `Invalid \\`orderBy\\`: ${detail}. Expected a field name, or a list of ` +\n \"[field, direction] pairs like [[\\\"roles\\\",\\\"asc\\\"],[\\\"created_at\\\",\\\"desc\\\"]]\"\n );\n this.name = \"OrderBySpecError\";\n }\n}\n\n/**\n * Validate an `orderBy` that arrived from outside this process — a WebSocket\n * subscribe frame, a driver call from untyped JavaScript — and return it in the\n * list form.\n *\n * Strict on purpose, in the same way the REST `parseOrderByParam` is: the\n * failure mode for a shape nobody checks is not a crash but a *silently\n * different query*. A malformed entry read as a field name resolves to no\n * column, and under the lenient unknown-field mode the sort is then dropped and\n * the rows come back in whatever order the database pleased — sorted, as far as\n * the subscriber can tell, by whatever they asked for.\n */\nexport function parseOrderBySpecStrict(raw: unknown, order?: \"asc\" | \"desc\"): OrderByTuple[] | undefined {\n if (raw === undefined || raw === null || raw === \"\") return undefined;\n // The string spelling is the driver contract's, so it takes its direction\n // from the same companion `order` — and defaults the same way it does.\n if (typeof raw === \"string\") return normalizeDriverOrderBy(raw, order);\n if (!Array.isArray(raw) || raw.length === 0) {\n throw new OrderBySpecError(`${typeof raw} is not a field name or a list of sort keys`);\n }\n\n // The single-tuple spelling, `[\"created_at\", \"desc\"]` — or the same shape\n // with an aggregate key in place of the field name.\n if (typeof raw[0] === \"string\" || isRelationAggregateSort(raw[0])) return [toStrictTuple(raw, 0)];\n\n return raw.map(toStrictTuple);\n}\n\n/** `first`/`last`, or a refusal naming the entry — see {@link NullsPlacement}. */\nfunction toStrictNulls(raw: unknown, index: number): NullsPlacement | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (raw !== \"first\" && raw !== \"last\") {\n throw new OrderBySpecError(\n `entry ${index} has nulls '${String(raw)}' — expected \"first\" or \"last\"`\n );\n }\n return raw;\n}\n\nfunction toStrictTuple(raw: unknown, index: number): OrderByTuple {\n if (!Array.isArray(raw)) {\n throw new OrderBySpecError(`entry ${index} has no field name`);\n }\n // The object spelling of an aggregate key, from an untyped caller that did\n // not go through `normalizeOrderBy`. Encoded rather than refused: it is a\n // sort this understands, and rejecting the shape a typed caller writes\n // would be a distinction between the two spellings that nothing else makes.\n const key = isRelationAggregateSort(raw[0]) ? sortKeyToString(raw[0]) : raw[0];\n if (typeof key !== \"string\" || key.trim() === \"\") {\n throw new OrderBySpecError(`entry ${index} has no field name`);\n }\n const direction = raw[1];\n if (direction !== undefined && direction !== \"asc\" && direction !== \"desc\") {\n throw new OrderBySpecError(`entry ${index} has direction '${String(direction)}'`);\n }\n const nulls = toStrictNulls(raw[2], index);\n // Omitted rather than defaulted: absent means \"the direction's convention\",\n // and writing one in here would make an explicit `NULLS LAST` on a\n // descending key indistinguishable from having said nothing — which the\n // keyset comparison and the ORDER BY both have to agree about.\n return nulls ? [key, direction ?? \"asc\", nulls] : [key, direction ?? \"asc\"];\n}\n\n/**\n * Serialize a sort to the wire.\n *\n * A single key keeps the `\"field:direction\"` shorthand it has always used —\n * short, readable in a URL, and what every existing client and test expects.\n * Several keys are emitted as the canonical JSON array the server already\n * accepts, because the shorthand has no separator to spare: a comma-joined\n * `\"a:asc,b:desc\"` parses as one field named `a` with the direction\n * `\"asc,b:desc\"`, which the server refuses.\n *\n * **Runtime tolerance:** if the input is already a well-formed wire string\n * (from an untyped JS caller), it is returned unchanged.\n * This is undocumented tolerance, not public API — don't rely on it.\n *\n * @param orderBy - A canonical tuple or list of tuples, or at runtime\n * possibly a pre-serialized string (undocumented tolerance).\n * @returns The wire-format string, or `undefined` if the input is falsy.\n *\n * @remarks\n * Field names containing `:` are representable in the tuple form but\n * **not** in the single-key wire encoding — this is an inherent limitation of\n * the colon-delimited shorthand and is not resolved here.\n */\nexport function serializeOrderBy(orderBy?: OrderBySpec | string): string | undefined {\n if (!orderBy) return undefined;\n // Runtime tolerance: pass through a pre-serialized wire string unchanged.\n if (typeof orderBy === \"string\") return orderBy;\n // `normalizeOrderBy` has already encoded any aggregate key to its string\n // spelling, which is why the shorthand below can assume a string: neither\n // `min(applications.created_at)` nor `count(applications)` contains a `:`.\n const list = normalizeOrderBy(orderBy);\n if (!list) return undefined;\n // `field:direction:nulls` — the third segment appears only when the key\n // asked for a placement, so every sort written before nulls existed still\n // serializes to exactly the string it always did.\n if (list.length === 1) {\n const [field, direction, nulls] = list[0];\n return nulls ? `${field}:${direction}:${nulls}` : `${field}:${direction}`;\n }\n return JSON.stringify(list.map(([field, direction, nulls]) => (nulls\n ? { field, direction, nulls }\n : { field, direction })));\n}\n\n/**\n * Deserialize a wire-format `\"field:direction\"` string into an {@link OrderByTuple}.\n *\n * Lenient parsing:\n * - Bare field name (no colon): `\"name\"` → `[\"name\", \"asc\"]`\n * - Unknown direction: `\"name:foo\"` → `[\"name\", \"asc\"]`\n * - Empty / falsy input, or a blank field name: → `undefined`\n *\n * The leniency is this end's alone; the *server* refuses the same value. This\n * used to say \"matches existing server behaviour\", and it stopped being true\n * when `parseOrderByParam` grew a strict direction check: `?orderBy=name:foo`\n * now answers `400 INVALID_ORDER_BY` (\"entry 0 has direction 'foo'\"). The split\n * is deliberate — see {@link parseOrderBySpecStrict} — because a value this\n * function is handed was produced by {@link serializeOrderBy} a moment earlier,\n * and one that reaches the server came from a stranger.\n *\n * A blank field is `undefined` rather than `[\" \", \"asc\"]`: whitespace is not a\n * field name, and the tuple it used to produce could not be re-encoded — the\n * only value in this codec that survived a decode and failed the next encode.\n *\n * Reads the single-key shorthand only. For a value that may carry several keys,\n * use {@link deserializeOrderByList} — handed a JSON array this returns the\n * whole array as one nonsensical field name.\n *\n * @param raw - The wire-format string from an HTTP query parameter.\n * @returns The canonical tuple, or `undefined` if the input names no field.\n */\nexport function deserializeOrderBy(raw?: string): OrderByTuple | undefined {\n if (!raw) return undefined;\n const idx = raw.indexOf(\":\");\n if (idx === -1) return raw.trim() === \"\" ? undefined : [raw, \"asc\"];\n const field = raw.slice(0, idx);\n if (field.trim() === \"\") return undefined;\n const rest = raw.slice(idx + 1);\n // `field:direction:nulls`. The nulls segment is optional, and — leniently,\n // as everything else on this end of the codec is — anything that is not\n // \"first\"/\"last\" is read as \"unspecified\" rather than refused. The *server*\n // end (`parseOrderByParam`) refuses it, for the reason in the docblock.\n const nullsIdx = rest.indexOf(\":\");\n const dir = nullsIdx === -1 ? rest : rest.slice(0, nullsIdx);\n const nulls = nullsIdx === -1 ? undefined : rest.slice(nullsIdx + 1);\n const direction = dir === \"desc\" ? \"desc\" : \"asc\";\n return nulls === \"first\" || nulls === \"last\"\n ? [field, direction, nulls]\n : [field, direction];\n}\n\n/**\n * Deserialize either wire spelling — the single-key shorthand or the JSON\n * array — into the list form.\n *\n * Lenient in the same way {@link deserializeOrderBy} is: this is the client end\n * of the codec, where the value was produced by {@link serializeOrderBy} a\n * moment earlier. The *server* end parses the same shapes strictly, in\n * `parseOrderByParam`, because there the value came from a stranger and a\n * direction it cannot read has to be refused rather than quietly turned into\n * `\"asc\"`.\n */\nexport function deserializeOrderByList(raw?: string): OrderByTuple[] | undefined {\n if (!raw) return undefined;\n const trimmed = raw.trim();\n if (trimmed.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n const list = parsed\n .map((entry): OrderByTuple | undefined => {\n if (typeof entry === \"string\") return deserializeOrderBy(entry);\n if (entry && typeof entry === \"object\" && typeof entry.field === \"string\") {\n const direction = entry.direction === \"desc\" ? \"desc\" : \"asc\";\n return entry.nulls === \"first\" || entry.nulls === \"last\"\n ? [entry.field, direction, entry.nulls]\n : [entry.field, direction];\n }\n return undefined;\n })\n .filter((entry): entry is OrderByTuple => entry !== undefined);\n return list.length > 0 ? list : undefined;\n }\n } catch {\n // Not JSON after all — fall through to the shorthand, which is what\n // a field name that merely begins with \"[\" would be.\n }\n }\n const single = deserializeOrderBy(trimmed);\n return single ? [single] : undefined;\n}\n","import { MAX_INCLUDE_DEPTH } from \"@rebasepro/types\";\nimport type { FilterValues, IncludeOptions, IncludeSpec, LogicalCondition, OrderByTuple } from \"@rebasepro/types\";\nimport { deserializeOrderByList, normalizeOrderBy } from \"./sort-dialect\";\n\n/**\n * The `include` codec: one shape, whatever spelling it arrived in.\n *\n * `include` reaches the driver by four routes — the REST `?include=` parameter,\n * a WebSocket subscribe frame, the SDK's `include(...)`, and the admin panel's\n * \"all relations\" — and each used to hand the driver something slightly\n * different. This normalises all four to one tree, so the fetch pipeline has a\n * single thing to read and `find()`, `findById()` and `listen()` cannot disagree\n * about what \"include the author\" means.\n *\n * @module\n */\n\n/**\n * One relation to load, and how.\n *\n * `children` is the nesting: `comments.author` is a `comments` node with an\n * `author` child. Every other field narrows the rows *of this relation* — the\n * same knobs a top-level query has, which is the point.\n */\nexport interface IncludeNode {\n /** Rows to load per parent row. */\n limit?: number;\n /** Filter over the related rows. */\n where?: FilterValues<string>;\n /** An `and`/`or`/`not` group over the related rows. */\n logical?: LogicalCondition;\n /** Sort for the related rows. */\n orderBy?: OrderByTuple[];\n /** Columns of the related row to return. */\n fields?: string[];\n /** Relations of the related row, loaded in turn. */\n children: Record<string, IncludeNode>;\n}\n\n/**\n * A whole `include` request: the tree, plus whether the caller asked for\n * *every* relation.\n *\n * The wildcard is kept as a flag rather than expanded into names here, because\n * expanding it needs the collection — which this package does not have. The\n * driver expands it against the relations it actually resolved.\n */\nexport interface NormalizedInclude {\n /** `include=*` — every relation of the collection, one hop deep. */\n wildcard: boolean;\n /** The named relations. Empty when `wildcard` is set alone. */\n tree: Record<string, IncludeNode>;\n}\n\n/** An `include` that cannot be read, as opposed to one naming a relation that does not exist. */\nexport class IncludeSpecError extends Error {\n readonly code: string;\n constructor(detail: string, code = \"INVALID_INCLUDE\") {\n super(`Invalid \\`include\\`: ${detail}`);\n this.name = \"IncludeSpecError\";\n this.code = code;\n Object.setPrototypeOf(this, IncludeSpecError.prototype);\n }\n}\n\nconst emptyNode = (): IncludeNode => ({ children: {} });\n\nfunction ensureNode(tree: Record<string, IncludeNode>, key: string): IncludeNode {\n return (tree[key] ??= emptyNode());\n}\n\n/**\n * Merge one dotted path (`\"comments.author\"`) into a tree.\n *\n * Merging rather than assigning is what makes `include=comments,comments.author`\n * mean the same thing as `include=comments.author`: the second path deepens the\n * node the first created instead of replacing it and losing its options.\n */\nfunction addPath(tree: Record<string, IncludeNode>, path: string): void {\n const segments = path.split(\".\").map(s => s.trim()).filter(Boolean);\n if (segments.length === 0) return;\n if (segments.length > MAX_INCLUDE_DEPTH) {\n throw new IncludeSpecError(\n `\"${path}\" nests ${segments.length} relations deep; the limit is ${MAX_INCLUDE_DEPTH}. ` +\n \"Each hop is another query, and an unbounded one walks a self-referencing relation forever.\",\n \"INCLUDE_TOO_DEEP\"\n );\n }\n let level = tree;\n for (const segment of segments) {\n level = ensureNode(level, segment).children;\n }\n}\n\nfunction normalizeOptions(key: string, options: IncludeOptions, depth: number): IncludeNode {\n if (depth > MAX_INCLUDE_DEPTH) {\n throw new IncludeSpecError(\n `\"${key}\" nests more than ${MAX_INCLUDE_DEPTH} relations deep.`,\n \"INCLUDE_TOO_DEEP\"\n );\n }\n if (options.limit !== undefined\n && (!Number.isInteger(options.limit) || options.limit < 1)) {\n throw new IncludeSpecError(\n `\"${key}\" has limit ${JSON.stringify(options.limit)} — expected a whole number of 1 or more.`\n );\n }\n const node: IncludeNode = { children: {} };\n if (options.limit !== undefined) node.limit = options.limit;\n if (options.where) node.where = options.where;\n if (options.logical) node.logical = options.logical;\n if (options.fields && options.fields.length > 0) node.fields = [...options.fields];\n // The same two spellings the top-level `?orderBy=` accepts: the\n // `field:direction[:nulls]` shorthand a caller writes into a query string,\n // and the tuple form a typed caller writes in code. Accepting only the\n // tuples made the JSON include form — the one that exists *because* it\n // travels over a query string — unable to express the shorthand beside it.\n const orderBy = typeof options.orderBy === \"string\"\n ? deserializeOrderByList(options.orderBy)\n : normalizeOrderBy(options.orderBy);\n if (orderBy) node.orderBy = orderBy;\n if (options.include) {\n const nested = normalizeIncludeAt(options.include, depth + 1);\n if (nested.wildcard) {\n // `*` inside a nested include has no bound: it would load every\n // relation of every related row, of every related row. The outer\n // wildcard is already the widest thing this API offers.\n throw new IncludeSpecError(\n `\"${key}\" asks for \\`*\\` inside a nested include. Name the relations you need.`\n );\n }\n node.children = nested.tree;\n }\n return node;\n}\n\nfunction normalizeIncludeAt(spec: IncludeSpec, depth: number): NormalizedInclude {\n if (Array.isArray(spec)) {\n const tree: Record<string, IncludeNode> = {};\n let wildcard = false;\n for (const raw of spec) {\n if (typeof raw !== \"string\") {\n throw new IncludeSpecError(`${typeof raw} is not a relation name`);\n }\n const name = raw.trim();\n if (!name) continue;\n if (name === \"*\") { wildcard = true; continue; }\n addPath(tree, name);\n }\n return { wildcard, tree };\n }\n if (typeof spec !== \"object\" || spec === null) {\n throw new IncludeSpecError(`${typeof spec} is not a list of relations or an include tree`);\n }\n\n const tree: Record<string, IncludeNode> = {};\n let wildcard = false;\n for (const [key, value] of Object.entries(spec)) {\n if (key === \"*\") {\n if (value) wildcard = true;\n continue;\n }\n if (value === true) { ensureNode(tree, key); continue; }\n // `false`/`null` are not in `IncludeSpec`, but this reads values that\n // arrived as JSON off a query string, where they are exactly what a\n // caller writes to turn one relation off in a tree they built by\n // spreading another. Skipping is what they mean.\n if ((value as unknown) === false || value === undefined || value === null) continue;\n if (typeof value !== \"object\" || Array.isArray(value)) {\n throw new IncludeSpecError(`\"${key}\" must be \\`true\\` or an options object`);\n }\n tree[key] = normalizeOptions(key, value as IncludeOptions, depth);\n }\n return { wildcard, tree };\n}\n\n/**\n * Collapse any {@link IncludeSpec} spelling into one tree.\n *\n * `[\"author\", \"comments.author\"]` and\n * `{ author: true, comments: { include: { author: true } } }` normalize to the\n * same value — which is the whole point: the REST parameter can only carry the\n * flat spelling, the SDK prefers the tree, and the driver should never learn\n * about either.\n *\n * @throws {IncludeSpecError} for a shape that is not an include at all, or one\n * that nests past {@link MAX_INCLUDE_DEPTH}.\n */\nexport function normalizeInclude(spec?: IncludeSpec): NormalizedInclude | undefined {\n if (spec === undefined || spec === null) return undefined;\n const normalized = normalizeIncludeAt(spec, 1);\n if (!normalized.wildcard && Object.keys(normalized.tree).length === 0) return undefined;\n return normalized;\n}\n\n/**\n * Every relation name a tree names, as dotted paths — `[\"comments\",\n * \"comments.author\"]`.\n *\n * Used to report which names an `include` asked for when one of them is not a\n * relation, and to serialize a tree that carries no per-relation options back\n * to the flat wire spelling.\n */\nexport function includePaths(tree: Record<string, IncludeNode>, prefix = \"\"): string[] {\n const out: string[] = [];\n for (const [key, node] of Object.entries(tree)) {\n const path = prefix ? `${prefix}.${key}` : key;\n out.push(path);\n out.push(...includePaths(node.children, path));\n }\n return out;\n}\n\n/**\n * The relation names an `include` asks for at the top level.\n *\n * `[\"author\", \"comments.author\"]` and `{author: true, comments: {...}}` both\n * answer `[\"author\", \"comments\"]` — a *hop*, not a path, because the only\n * consumer is `?fields=`, which names keys on the row being returned and a\n * nested relation is not one of those.\n *\n * Derived rather than passed: `include` has four spellings and three of them\n * are not a `string[]`, so every consumer that wants the plain names either\n * calls this or reimplements the flattening.\n */\nexport function topLevelIncludeNames(spec?: IncludeSpec): string[] {\n const normalized = normalizeInclude(spec);\n if (!normalized) return [];\n return Object.keys(normalized.tree);\n}\n\n/** Whether any node in the tree carries per-relation options. */\nfunction hasOptions(tree: Record<string, IncludeNode>): boolean {\n return Object.values(tree).some(node =>\n node.limit !== undefined || node.where !== undefined || node.logical !== undefined\n || node.orderBy !== undefined || node.fields !== undefined\n || hasOptions(node.children));\n}\n\n/**\n * Serialize an {@link IncludeSpec} for the REST `?include=` parameter.\n *\n * Two spellings, and which one is used is decided by the request rather than\n * chosen:\n *\n * - **Comma-separated dotted paths** — `include=author,comments.author`. What a\n * plain include is, what a human types, and what every existing client sends.\n * - **JSON**, when any relation carries options — `include={\"comments\":{\"limit\":5,\n * \"include\":{\"author\":true}}}`. The flat spelling has nowhere to put a\n * `limit`, and inventing a punctuation for it (`comments(limit:5)`) would be a\n * third grammar to learn beside the two this API already has.\n *\n * The server accepts both on every list and get route, and tells them apart the\n * same way this does: a value starting with `{` is JSON.\n */\nexport function serializeInclude(spec?: IncludeSpec): string | undefined {\n const normalized = normalizeInclude(spec);\n if (!normalized) return undefined;\n if (normalized.wildcard && Object.keys(normalized.tree).length === 0) return \"*\";\n if (!hasOptions(normalized.tree)) {\n const paths = includePaths(normalized.tree);\n // Only the leaves: `comments.author` already implies `comments`, and\n // sending both is the same request twice.\n const leaves = paths.filter(path => !paths.some(other => other.startsWith(`${path}.`)));\n const all = normalized.wildcard ? [\"*\", ...leaves] : leaves;\n return all.length > 0 ? all.join(\",\") : undefined;\n }\n return JSON.stringify(toWireTree(normalized));\n}\n\n/**\n * A normalized tree, back in the {@link IncludeSpec} spelling a caller writes.\n *\n * The round trip is what lets a builder accumulate `include` calls: normalize\n * each, merge, and hand the result back as a spec the next layer can normalize\n * again. Idempotent, so doing it twice changes nothing.\n */\nexport function denormalizeInclude(normalized: NormalizedInclude): IncludeSpec {\n return toWireTree(normalized) as IncludeSpec;\n}\n\nfunction mergeTrees(\n into: Record<string, IncludeNode>,\n from: Record<string, IncludeNode>\n): Record<string, IncludeNode> {\n for (const [key, node] of Object.entries(from)) {\n const existing = into[key];\n if (!existing) { into[key] = node; continue; }\n // The later call wins on each option it names, and says nothing about\n // the ones it does not — so `.include(\"comments\")` after\n // `.include({comments:{limit:5}})` keeps the limit rather than erasing\n // it, which is the behaviour that makes accumulating calls safe.\n if (node.limit !== undefined) existing.limit = node.limit;\n if (node.where !== undefined) existing.where = node.where;\n if (node.logical !== undefined) existing.logical = node.logical;\n if (node.orderBy !== undefined) existing.orderBy = node.orderBy;\n if (node.fields !== undefined) existing.fields = node.fields;\n existing.children = mergeTrees(existing.children, node.children);\n }\n return into;\n}\n\n/**\n * Combine several `include` requests into one.\n *\n * Repeated `.include(...)` calls on a query builder are additive: each names\n * more of the graph to load, and a later one must not discard what an earlier\n * one asked for. Assigning instead of merging is why `.include(\"author\")\n * .include(\"tags\")` used to load only tags.\n */\nexport function mergeIncludeSpecs(\n existing: IncludeSpec | undefined,\n additions: (string | IncludeSpec)[]\n): IncludeSpec | undefined {\n const merged: NormalizedInclude = { wildcard: false, tree: {} };\n const absorb = (spec?: IncludeSpec) => {\n const normalized = normalizeInclude(spec);\n if (!normalized) return;\n merged.wildcard ||= normalized.wildcard;\n mergeTrees(merged.tree, normalized.tree);\n };\n absorb(existing);\n // A bare string is one relation name; anything else is a spec in its own\n // right. `.include(\"a\", \"b\")` and `.include([\"a\",\"b\"])` are the same call.\n const names = additions.filter((a): a is string => typeof a === \"string\");\n if (names.length > 0) absorb(names);\n for (const addition of additions) {\n if (typeof addition !== \"string\") absorb(addition);\n }\n if (!merged.wildcard && Object.keys(merged.tree).length === 0) return undefined;\n return denormalizeInclude(merged);\n}\n\nfunction toWireTree(normalized: NormalizedInclude): Record<string, unknown> {\n const emit = (tree: Record<string, IncludeNode>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [key, node] of Object.entries(tree)) {\n const options: Record<string, unknown> = {};\n if (node.limit !== undefined) options.limit = node.limit;\n if (node.where) options.where = node.where;\n if (node.logical) options.logical = node.logical;\n if (node.orderBy) options.orderBy = node.orderBy;\n if (node.fields) options.fields = node.fields;\n const children = emit(node.children);\n if (Object.keys(children).length > 0) options.include = children;\n out[key] = Object.keys(options).length > 0 ? options : true;\n }\n return out;\n };\n const tree = emit(normalized.tree);\n if (normalized.wildcard) tree[\"*\"] = true;\n return tree;\n}\n\n/**\n * Read the REST `?include=` parameter, in either spelling.\n *\n * @throws {IncludeSpecError} for malformed JSON or a tree that nests too deep.\n */\nexport function deserializeInclude(raw?: string): IncludeSpec | undefined {\n if (raw === undefined || raw === null) return undefined;\n const text = raw.trim();\n if (!text) return undefined;\n if (text.startsWith(\"{\")) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new IncludeSpecError(\n \"the parametrised form must be a JSON object, e.g. \"\n + \"{\\\"comments\\\":{\\\"limit\\\":5,\\\"include\\\":{\\\"author\\\":true}}}\"\n );\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new IncludeSpecError(\"the parametrised form must be a JSON object\");\n }\n return parsed as IncludeSpec;\n }\n return text.split(\",\").map(s => s.trim()).filter(Boolean);\n}\n","import {\n CollectionAccessor,\n FilterCondition,\n FindParams,\n FindResponse,\n LogicalCondition,\n OrderByTuple,\n QueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n type ComputedSortField\n} from \"@rebasepro/types\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\n\nexport function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"or\",\nconditions };\n}\n\nexport function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"and\",\nconditions };\n}\n\n/**\n * Negate a group: `not(a)` is `NOT a`, and `not(a, b)` is `NOT (a AND b)`.\n *\n * The conjunction, not the disjunction — one rule, stated on\n * {@link LogicalCondition} and applied identically by the wire codec, the REST\n * `?not=` parameter and every driver compiler. Groups nest, so De Morgan's\n * other half is `not(or(a, b))`.\n *\n * It compiles to a real SQL `NOT (...)` rather than to inverted operators,\n * which matters more than it looks: SQL is three-valued, so `NOT (a AND b)` and\n * `(NOT a) OR (NOT b)` stop agreeing the moment a NULL is involved, and only\n * one of them is the query the caller wrote. It also means a negation includes\n * rows whose column is NULL — which is what `NOT` means.\n */\nexport function not(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"not\",\nconditions };\n}\n\nexport function cond(column: string, operator: WhereFilterOp, value: unknown): FilterCondition {\n return { column,\noperator,\nvalue };\n}\n\nexport class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {\n // Keyed by plain `string` on purpose: it is written in place by the\n // methods below, whose own parameters are typed against `M`, and a\n // `Partial<Record<FieldPath<M>, …>>` is read-only under a generic `M`\n // (TS2862). The typing users see is on the methods; this is the buffer\n // behind them, cast once at each handoff.\n private params: FindParams = { where: {} };\n\n constructor(private collection: CollectionAccessor<M>) {}\n\n /**\n * Add a filter condition to your query.\n * @example\n * client.collection('users').where('age', '>=', 18).find()\n */\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n // Handle LogicalCondition signature\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK builder\n // in `@rebasepro/client`, which had the same defect: every other\n // `.where()` adds a condition, so the one that silently dropped the\n // previous group was also the one that widened the result set.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n\n if (!this.params.where) {\n this.params.where = {};\n }\n\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n // Convert existing single tuple/value into array of tuples\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n\n return this;\n }\n\n /**\n * Order the results by a specific column.\n *\n * Called again, this adds a tie-breaker rather than replacing the sort:\n * keys apply in the order they were added.\n *\n * @example\n * client.collection('users').orderBy('createdAt', 'desc').find()\n * @example\n * client.collection('users').orderBy('roles').orderBy('createdAt', 'desc').find()\n */\n orderBy(column: (keyof M & string) | ComputedSortField, direction: \"asc\" | \"desc\" = \"asc\"): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n this.params.orderBy = [...existing, [column, direction] as OrderByTuple];\n return this;\n }\n\n /**\n * Limit the number of results returned.\n */\n limit(count: number): this {\n this.params.limit = count;\n return this;\n }\n\n /**\n * Skip the first N results.\n */\n offset(count: number): this {\n this.params.offset = count;\n return this;\n }\n\n /**\n * Set a free-text search string if supported by the backend.\n */\n search(searchString: string, options?: { explain?: boolean }): this {\n this.params.searchString = searchString;\n if (options?.explain !== undefined) this.params.searchExplain = options.explain;\n return this;\n }\n\n /**\n * Order rows by nearest-neighbour distance to `vector`, closest first.\n *\n * Postgres only, over a property declared as `type: \"vector\"`. Rows come\n * back with a `_distance`; `where` filters before the ordering.\n */\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n\n /**\n * Include related entities in the response.\n * Relations will be populated with full entity data instead of just IDs.\n *\n * @param relations - Relation names to include, or \"*\" for all.\n * @example\n * // Include specific relations\n * client.data.posts.include(\"tags\", \"author\").find()\n *\n * // Include all relations\n * client.data.posts.include(\"*\").find()\n */\n include(...relations: string[]): this {\n this.params.include = relations;\n return this;\n }\n\n /**\n * Execute the find query and return the results.\n */\n async find(): Promise<FindResponse<M>> {\n return this.collection.find(this.params as FindParams<M>) as Promise<FindResponse<M>>;\n }\n\n /**\n * Listen to realtime updates matching this query.\n */\n listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.collection.listen) {\n throw new Error(\"Listen is only available when RebaseClient is configured with a websocketUrl.\");\n }\n return this.collection.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n","import {\n DEFAULT_LIST_LIMIT,\n FindAllParams,\n FindParams,\n FindResult,\n IterateParams\n} from \"@rebasepro/types\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\n\n/**\n * The pagination engine behind `iterate()` / `findAll()`.\n *\n * It lives here, above both transports, on purpose: the HTTP client and the\n * in-process accessor implement the same `SDKCollectionClient` contract, and a\n * helper written twice is a helper that drifts. Both call into this file, so\n * \"the SDK paginates like *this*\" has exactly one definition.\n *\n * Everything below is expressed in terms of a single `find(params)` function,\n * which is all either transport has to supply.\n */\n\n/** Rows requested per page when the caller does not say. */\nexport const DEFAULT_PAGE_SIZE = 200;\n\n/** Rows `findAll()` will materialise before it refuses to continue. */\nexport const DEFAULT_FIND_ALL_MAX_ROWS = 10_000;\n\n/**\n * Requests one walk may make before it gives up on the server ever saying\n * `hasMore: false`. At the default page size that is two million rows — far\n * past any legitimate walk, and short of running forever.\n */\nexport const DEFAULT_MAX_PAGES = 10_000;\n\n/** Why a pagination walk refused to continue. */\nexport type PaginationErrorCode =\n /** `findAll()` matched more rows than its ceiling allows. */\n | \"max-rows\"\n /** The walk made its maximum number of requests without the server finishing. */\n | \"max-pages\"\n /**\n * The server said there was another page but issued no cursor to reach it.\n *\n * A query whose ordering has no stored value to seek on — relevance — is the\n * case that produces this. Page it by offset instead.\n */\n | \"cursor-missing\"\n /** Two consecutive pages returned the same cursor, so the walk cannot advance. */\n | \"cursor-stalled\";\n\n/**\n * Thrown when a walk stops for a reason the caller needs to know about.\n *\n * Every one of these is a case where the alternative would be silent: a\n * truncated array that looks complete, or a loop that never returns. Check\n * {@link code} to tell them apart.\n */\nexport class RebasePaginationError extends Error {\n readonly code: PaginationErrorCode;\n\n constructor(code: PaginationErrorCode, message: string) {\n super(message);\n this.name = \"RebasePaginationError\";\n this.code = code;\n // Keeps `instanceof` working when this is compiled down for an older\n // target, where extending a builtin otherwise loses the prototype.\n Object.setPrototypeOf(this, RebasePaginationError.prototype);\n }\n}\n\n/** The one thing a transport has to provide to be paginated. */\nexport type PageFinder<M extends Record<string, unknown> = Record<string, unknown>> =\n (params: FindParams<M>) => Promise<FindResult<M>>;\n\n/**\n * Resolve `limit`/`offset`/`page` into the window a read will actually use.\n *\n * Lives here, next to the walk, for the reason at the top of this file: every\n * transport has to mean the same thing by \"page two\". Four of them did not —\n * the REST layer strode by {@link DEFAULT_LIST_LIMIT}, the local-first\n * evaluator by {@link DEFAULT_PAGE_SIZE}, the in-process accessor by 20, and\n * the published type documented a fourth number. Pages that overlap or skip\n * rows are the mildest of those outcomes.\n *\n * `page` wins over `offset`, as {@link FindParams} documents. `driverOffset`\n * is the value to hand a driver: it stays `undefined` when the caller named no\n * offset, because keyset pagination seeks with a `where` clause and must not\n * look like it is paging by offset.\n */\nexport function resolveFindWindow(\n params?: Pick<FindParams, \"limit\" | \"offset\" | \"page\">\n): { limit: number; offset: number; driverOffset: number | undefined } {\n const limit = params?.limit ?? DEFAULT_LIST_LIMIT;\n const offset = params?.page != null\n ? Math.max(0, (params.page - 1) * limit)\n : (params?.offset ?? 0);\n return {\n limit,\n offset,\n driverOffset: params?.page != null ? offset : params?.offset\n };\n}\n\nfunction normalizePageSize(raw: number | undefined): number {\n if (raw === undefined || !Number.isFinite(raw)) return DEFAULT_PAGE_SIZE;\n return Math.max(1, Math.floor(raw));\n}\n\nfunction normalizeMaxPages(raw: number | undefined): number {\n if (raw === undefined) return DEFAULT_MAX_PAGES;\n if (raw === Number.POSITIVE_INFINITY) return raw;\n if (!Number.isFinite(raw)) return DEFAULT_MAX_PAGES;\n return Math.max(1, Math.floor(raw));\n}\n\nfunction normalizeMaxRows(raw: number | undefined): number {\n if (raw === undefined) return DEFAULT_FIND_ALL_MAX_ROWS;\n if (raw === Number.POSITIVE_INFINITY) return raw;\n if (!Number.isFinite(raw)) return DEFAULT_FIND_ALL_MAX_ROWS;\n return Math.max(0, Math.floor(raw));\n}\n\n/**\n * Walk every row a query matches, yielding one row at a time and fetching the\n * next page only when the consumer asks for it.\n *\n * See {@link SDKCollectionClient.iterate} for the caller-facing contract,\n * including the offset-drift caveat and the `cursor` alternative.\n *\n * @param find the transport's single-page read\n * @param params `find()` parameters minus the window, plus the walk options\n * @param label the collection name, so an error says which walk failed\n */\nexport async function* paginateFind<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n params?: IterateParams<M>,\n label = \"collection\"\n): AsyncGenerator<M, void, undefined> {\n const {\n pageSize,\n cursor,\n maxPages,\n ...rest\n } = (params ?? {}) as IterateParams<M> & Record<string, unknown>;\n\n const findParams = { ...rest } as FindParams<M>;\n const size = normalizePageSize(pageSize as number | undefined);\n const pageCap = normalizeMaxPages(maxPages as number | undefined);\n\n // ── Cursor (keyset) setup ────────────────────────────────────────────────\n //\n // The walk no longer builds a keyset of its own. It used to: a `>`/`<` on\n // one column, expressed as an extra `where`, which threw on any multi-key\n // sort (\"keyset pagination advances along a single column\") and dropped\n // every row whose sort value was NULL, because `> value` answers *unknown*\n // against NULL. The driver has had a NULL-correct multi-key comparison all\n // along and nothing over HTTP could reach it.\n //\n // So this is now a *request* for seeking, not an implementation of it: the\n // server issues `meta.nextCursor` and the walk hands it back as `after`.\n // Multi-key sorts and nullable keys work because the comparison is the\n // driver's, and there is one of it.\n const seekRequested = cursor !== undefined && cursor !== null;\n if (seekRequested) {\n // A named column still means \"sort by this and seek along it\", which is\n // what every existing caller wrote. It is an `orderBy` now rather than\n // a second pagination mode — the seeking itself needs no column named,\n // since the cursor carries whatever keys the sort used.\n const field = typeof cursor === \"string\" ? cursor : cursor.field;\n const requested = (typeof cursor === \"object\" && cursor !== null) ? cursor.direction : undefined;\n const explicit = normalizeOrderBy(findParams.orderBy);\n // An explicit `orderBy` wins and the named column is redundant, not\n // wrong: seeking follows whatever the query is sorted by, so there is\n // no longer a mismatch to refuse.\n if (!explicit) {\n findParams.orderBy = [field, requested ?? \"asc\"] as FindParams<M>[\"orderBy\"];\n }\n }\n\n let offset = 0;\n let pages = 0;\n let after: string | undefined;\n\n for (;;) {\n if (pages >= pageCap) {\n throw new RebasePaginationError(\n \"max-pages\",\n `Iterating \"${label}\" made ${pages} requests without the server reporting the end of ` +\n `the collection. Stopping rather than looping forever — raise \\`maxPages\\` if the walk ` +\n `is genuinely this long, or check that the backend sets \\`meta.hasMore\\`.`\n );\n }\n\n const pageParams: FindParams<M> = { ...findParams, limit: size };\n if (seekRequested) {\n if (after) pageParams.after = after;\n } else {\n pageParams.offset = offset;\n }\n\n const page = await find(pageParams);\n pages += 1;\n\n const rows = page?.data ?? [];\n // A page with nothing on it always ends the walk, whatever the server\n // claims about `hasMore` — there is no cursor to advance and no offset\n // that would ever move past it.\n if (rows.length === 0) return;\n\n for (const row of rows) {\n yield row;\n }\n\n // The server is the only authority on whether more rows exist. Never\n // infer it from `rows.length >= size`: a last page that happens to be\n // exactly full is indistinguishable from a middle one, and guessing\n // there drops every row after it.\n if (page?.meta?.hasMore !== true) return;\n\n if (seekRequested) {\n const next = page.meta.nextCursor;\n if (!next) {\n throw new RebasePaginationError(\n \"cursor-missing\",\n `Cannot seek past the last row of \"${label}\": the server reported another page but ` +\n `issued no cursor for it. An ordering with no stored value to compare against — ` +\n `relevance (\\`_score\\`) — cannot key a cursor. Drop \\`cursor\\` to page by offset.`\n );\n }\n if (next === after) {\n throw new RebasePaginationError(\n \"cursor-stalled\",\n `Iterating \"${label}\" is stuck: two pages in a row ended on the same cursor, so the ` +\n `walk cannot advance. Continuing would loop forever. Page by offset instead, or ` +\n `report this — a cursor that does not move is a server-side bug.`\n );\n }\n after = next;\n } else {\n // Advance by what actually arrived, not by the page size: a server\n // free to return fewer rows than asked for would otherwise leave a\n // hole in the walk.\n offset += rows.length;\n }\n }\n}\n\n/**\n * {@link paginateFind}, collected into an array under a ceiling.\n *\n * See {@link SDKCollectionClient.findAll}.\n */\nexport async function collectAllPages<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n params?: FindAllParams<M>,\n label = \"collection\"\n): Promise<M[]> {\n const { maxRows, ...rest } = (params ?? {}) as FindAllParams<M> & Record<string, unknown>;\n const cap = normalizeMaxRows(maxRows as number | undefined);\n\n const out: M[] = [];\n for await (const row of paginateFind<M>(find, rest as IterateParams<M>, label)) {\n out.push(row);\n if (out.length > cap) {\n throw new RebasePaginationError(\n \"max-rows\",\n `findAll(\"${label}\") matched more than ${cap} rows. Returning the first ${cap} would ` +\n `look like the whole answer and quietly not be one, so this throws instead. Raise ` +\n `\\`maxRows\\` if you meant to load them all, or stream with \\`iterate()\\`.`\n );\n }\n }\n return out;\n}\n\n/**\n * Build the `iterate` / `findAll` pair for one collection from its `find`.\n *\n * Both transports call this, which is what keeps the two implementations from\n * being two implementations.\n */\nexport function createPaginationHelpers<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n label: string\n): {\n iterate: (params?: IterateParams<M>) => AsyncIterableIterator<M>;\n findAll: (params?: FindAllParams<M>) => Promise<M[]>;\n} {\n return {\n iterate: (params?: IterateParams<M>) => paginateFind<M>(find, params, label),\n findAll: (params?: FindAllParams<M>) => collectAllPages<M>(find, params, label)\n };\n}\n","/**\n * REST wire-format adapter for the unified filter system.\n *\n * This module is the ONLY code in the entire codebase that knows about\n * PostgREST-style dot-syntax strings (`eq.active`, `gt.18`, `in.(a,b)`).\n * Everything else speaks `FilterValues` exclusively.\n *\n * Wire-format values are always strings — the wire format carries no type\n * metadata, so type coercion is the responsibility of the server-side data\n * driver which has access to the collection schema.\n *\n * Structural characters inside a value are backslash-escaped: `,` → `\\,`,\n * `(` → `\\(`, `)` → `\\)`, and a literal backslash as `\\\\`. Decoding is\n * deliberately conservative — only those four sequences are decoded, so a\n * backslash that arrives unescaped from an older client survives intact.\n *\n * @module\n */\n\nimport {\n WhereFilterOp,\n FilterValues,\n ALL_WHERE_FILTER_OPS,\n CANONICAL_TO_REST,\n REST_TO_CANONICAL,\n RestFilterOp,\n toCanonicalOp,\n LogicalCondition,\n FilterCondition,\n LIST_OPS,\n NULL_OPS\n} from \"@rebasepro/types\";\nimport { normalizeToEntityRelation } from \"../util/entities\";\n\n// ---------------------------------------------------------------------------\n// Value stringification\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a JS value to its querystring representation.\n * `null` is serialized as the literal string `\"null\"`.\n * Relation values (`EntityRelation` instances or `{ __type: \"relation\", id, path }`\n * objects) are serialized as their raw id — the wire format only carries the\n * value to compare against the FK column.\n */\nfunction stringifyValue(value: unknown): string {\n if (value === null) return \"null\";\n const relation = normalizeToEntityRelation(value);\n if (relation) return String(relation.id);\n return String(value);\n}\n\n// ---------------------------------------------------------------------------\n// Comma escaping for list values\n// ---------------------------------------------------------------------------\n\n/**\n * Characters that carry structure in the wire format and must therefore be\n * escaped inside a value: the separator, the group delimiters, and the escape\n * character itself.\n *\n * Parentheses are here because `and(...)`/`or(...)` groups are parsed by\n * tracking paren depth. A value containing one is not merely ambiguous, it\n * moves where the parser thinks the group ends.\n */\nconst WIRE_SPECIALS = /[\\\\,()]/g;\n\n/**\n * Escape a value for the wire format: `\\` → `\\\\`, `,` → `\\,`, `(` → `\\(`,\n * `)` → `\\)`.\n */\n/**\n * The wire spelling of an empty list.\n *\n * A lone backslash: unproducible by {@link escapeWireValue}, which doubles\n * every backslash it emits, so it cannot collide with any real item.\n */\nconst EMPTY_LIST_TOKEN = \"\\\\\";\n\nfunction escapeWireValue(value: string): string {\n return value.replace(WIRE_SPECIALS, ch => `\\\\${ch}`);\n}\n\n/**\n * Unescape a wire-format value.\n *\n * **Conservative**, and deliberately so: only the four sequences\n * {@link escapeWireValue} actually produces are decoded. A backslash followed\n * by anything else is left exactly as it is.\n *\n * This used to consume the backslash before *any* character, which is\n * indistinguishable for anything this codec emitted — it only ever emits those\n * four — but not for input arriving from elsewhere. A client on an older\n * release sends a Windows path or a LIKE pattern with a literal `C:\\x`\n * unescaped, and greedy unescaping silently turned it into `C:x`, changing\n * which rows matched. Decoding only what the encoder can produce makes the two\n * directions agree across versions.\n */\nfunction unescapeWireValue(value: string): string {\n let result = \"\";\n for (let i = 0; i < value.length; i++) {\n const next = value[i + 1];\n if (value[i] === \"\\\\\" && (next === \"\\\\\" || next === \",\" || next === \"(\" || next === \")\")) {\n result += next;\n i++;\n continue;\n }\n result += value[i];\n }\n return result;\n}\n\n/**\n * Split a parenthesized list string on unescaped commas.\n * Input is the content between `(` and `)`.\n *\n * @example\n * splitListItems(\"admin,editor\") // [\"admin\", \"editor\"]\n * splitListItems(\"hello\\\\, world,foo\") // [\"hello, world\", \"foo\"]\n */\nfunction splitListItems(inner: string): string[] {\n const items: string[] = [];\n let current = \"\";\n for (let i = 0; i < inner.length; i++) {\n if (inner[i] === \"\\\\\" && i + 1 < inner.length) {\n // Escaped pair — consume both chars so the comma in `\\,` is not\n // read as a separator. Kept verbatim; decoding happens once, below.\n current += inner[i] + inner[i + 1];\n i++;\n } else if (inner[i] === \",\") {\n items.push(unescapeWireValue(current));\n current = \"\";\n } else {\n current += inner[i];\n }\n }\n items.push(unescapeWireValue(current));\n return items;\n}\n\n/**\n * Split a group body on commas at paren depth 0, honouring escapes.\n *\n * The escape-awareness is the point. The splitter used to track only paren\n * depth, so a comma inside a scalar value ended a condition:\n * `or(name.eq.Doe, John,age.gte.18)` parsed as *three* conditions, the middle\n * one a fabricated `\" John\" == true`. On an `or` that widens the result set,\n * and nothing anywhere reports an error — the query simply stops meaning what\n * the caller wrote.\n */\nfunction splitGroupItems(inner: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < inner.length; i++) {\n const ch = inner[i];\n if (ch === \"\\\\\" && i + 1 < inner.length) { i++; continue; }\n if (ch === \"(\") depth++;\n else if (ch === \")\") depth--;\n else if (ch === \",\" && depth === 0) {\n parts.push(inner.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(inner.slice(start));\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// Typed operator map lookups (no `as any`)\n// ---------------------------------------------------------------------------\n\n/**\n * Operator tables as `Map`s, because the key comes off the wire.\n *\n * Indexed as plain objects, every `Object.prototype` member answered: a query\n * string of `?f=valueOf.x` found a truthy \"operator\" — the inherited function —\n * and `deserializeTuple` returned it *as the operator*, so a function object\n * travelled on into the compilers in place of a `WhereFilterOp`. The guard one\n * line below (`if (!canonicalOp)`) reads as though it rejects anything unknown,\n * and does not: `Object.prototype` is not unknown to a plain object.\n *\n * Same shape as the prototype-key defects swept out of `setIn`, `getIn`,\n * `mergeDeep`, `unflattenObject` and `FOREIGN_CONVENTION_UIDS`.\n */\nconst REST_OP_LOOKUP = new Map<string, WhereFilterOp>(\n Object.entries(REST_TO_CANONICAL) as [string, WhereFilterOp][]\n);\nconst CANONICAL_OP_LOOKUP = new Map<string, RestFilterOp>(\n Object.entries(CANONICAL_TO_REST) as [string, RestFilterOp][]\n);\n\n// ---------------------------------------------------------------------------\n// Unknown operators\n// ---------------------------------------------------------------------------\n\n/** The operator spellings a rejection lists back to the caller. */\nconst VALID_OPERATOR_LIST = ALL_WHERE_FILTER_OPS.join(\", \");\n\n/**\n * A filter condition named an operator this dialect does not have.\n *\n * ## Why this throws, rather than returning a typed rejection\n *\n * `deserializeFilter` is the *shared* codec: the REST ingress\n * (`packages/server/src/api/rest/query-parser.ts`), the browser SDK and the\n * admin panel (`buildRebaseData.ts`) all decode through it. Two constraints\n * follow.\n *\n * - It cannot throw the server's `ApiError`. `@rebasepro/common` does not\n * depend on `@rebasepro/server` (the dependency runs the other way), and a\n * browser client has no error handler to render an `ApiError` with. So the\n * rejection is this plain `Error` subclass, whose `message` reads correctly\n * wherever it surfaces — a rejected promise in an app, a 400 body over HTTP.\n * - It cannot be a returned rejection *value*. Every caller assigns the result\n * straight into a query it is about to run; a sentinel that none of them\n * check would be ignored, which is exactly the silently-wrong-filter failure\n * this exists to stop. Throwing is also what this file already does for the\n * sibling cases — `serializeTuple` on an unknown canonical operator,\n * `deserializeLogicalCondition` past the nesting bound — and the REST parser\n * already converts the latter into a 400.\n *\n * `statusCode`, `code` and `details` are carried as fields because the server's\n * Hono error handler duck-types those off any thrown error: a decode path that\n * forgets to convert still answers 400 with the canonical envelope instead of a\n * 500 that says \"An unexpected error occurred\". `query-parser.ts` converts\n * explicitly all the same — that is the path the contract is stated on, and an\n * incidental 400 is not a contract.\n */\nexport class UnknownFilterOperatorError extends Error {\n /** The field the condition was written against. */\n public readonly field: string;\n /** The operator string as it arrived, verbatim. */\n public readonly operator: string;\n /** Every operator this dialect accepts, in canonical spelling. */\n public readonly validOperators: readonly WhereFilterOp[] = ALL_WHERE_FILTER_OPS;\n /** See the class docblock: read by the server's error handler. */\n public readonly statusCode = 400;\n public readonly code = \"UNKNOWN_FILTER_OPERATOR\";\n public readonly details: { field: string; operator: string; validOperators: readonly WhereFilterOp[] };\n\n constructor(field: string, operator: string) {\n super(\n `Unknown filter operator '${operator}' on field '${field}'. `\n + `Valid operators: ${VALID_OPERATOR_LIST}`\n );\n this.name = \"UnknownFilterOperatorError\";\n this.field = field;\n this.operator = operator;\n this.details = { field, operator, validOperators: ALL_WHERE_FILTER_OPS };\n }\n}\n\n/**\n * Two to three characters of ASCII punctuation and nothing else — the shape\n * every symbolic operator has (`==`, `>=`, `<>`, `~~`, `!!`, `>>`, `===`), and\n * one a column value effectively never has.\n *\n * Two characters minimum on purpose. A *single* punctuation character is a\n * perfectly ordinary value — `{ grade: [\"-\", \"+\"] }` is a two-item list, not a\n * condition — and the only single-character operator anyone actually mistypes\n * is `=`, which is named separately below. `<` and `>` need no special case:\n * they are real operators and resolve.\n */\nconst SYMBOLIC_OPERATOR = /^[^\\p{L}\\p{N}\\s]{2,3}$/u;\n\n/** Lowercase, strip everything that is not a letter or digit. */\nfunction normalizeOperatorName(op: string): string {\n return op.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\n/**\n * Every real operator name with its case and separators removed, so a\n * respelling of one — `arrayContains`, `not_in`, `NOT-LIKE`, `isNull` — is\n * recognised as an attempt at an operator rather than read as a value.\n *\n * These are rejected rather than accepted: admitting a second spelling of an\n * operator would leave two wire spellings of one thing, and the rejection\n * message names the one that works.\n */\nconst RESPELLED_OPERATORS: ReadonlySet<string> = new Set(\n [...ALL_WHERE_FILTER_OPS, ...Object.keys(REST_TO_CANONICAL)].map(normalizeOperatorName)\n);\n\n/**\n * Operator names *other* query dialects use, which this one does not have.\n *\n * This list is curated, and deliberately so. For a word-shaped string there is\n * no rule that separates \"an operator the caller guessed\" from \"a value that\n * happens to be a word\": `{ tags: [\"a\", \"b\"] }` has to keep meaning a two-item\n * `in` list, so the codec cannot simply refuse every unrecognised word in\n * position 0. The line is therefore drawn by name, and only around names whose\n * use as an operator is far more likely than their use as one of two sibling\n * values. `contains` is the motivating case — the first thing a developer\n * reaches for, and until now it compiled to `title IN ('contains', 'Hell')`.\n *\n * Genuinely ambiguous single words (`any`, `all`, `exists`, `search`, `not`)\n * are left off: as operators they are rare, and as enum values they are common.\n * Everywhere else the tie goes to *rejecting*, because a 400 naming the\n * supported set costs the caller one round trip, and the alternative — which is\n * what every name on this list used to produce — is a query that runs, returns\n * rows, and is wrong.\n */\nconst NEAR_MISS_OPERATORS: ReadonlySet<string> = new Set([\n \"contains\", \"notcontains\", \"doesnotcontain\", \"doesnotcontains\",\n \"includes\", \"notincludes\",\n \"startswith\", \"notstartswith\", \"beginswith\", \"startingwith\",\n \"endswith\", \"notendswith\",\n \"matches\", \"notmatches\", \"regex\", \"regexp\",\n \"between\", \"notbetween\",\n \"equals\", \"notequals\", \"equalto\", \"isequalto\", \"isnotequalto\",\n \"greaterthan\", \"greaterthanorequal\", \"greaterthanorequalto\",\n \"lessthan\", \"lessthanorequal\", \"lessthanorequalto\",\n \"isempty\", \"isnotempty\",\n \"oneof\", \"noneof\", \"anyof\", \"allof\",\n \"null\", \"isnullorempty\"\n]);\n\n/**\n * Was this string *meant* as an operator?\n *\n * Only consulted after {@link toCanonicalOp} has already failed to resolve it,\n * so a `true` here is always a rejection.\n */\nfunction isOperatorShaped(op: string): boolean {\n if (op === \"=\") return true;\n if (SYMBOLIC_OPERATOR.test(op)) return true;\n const normalized = normalizeOperatorName(op);\n if (!normalized) return false;\n return RESPELLED_OPERATORS.has(normalized) || NEAR_MISS_OPERATORS.has(normalized);\n}\n\n/**\n * Read a `[op, value]` tuple, if that is what this is.\n *\n * Three outcomes, and the middle one is the defect this function exists for:\n *\n * - the operator resolves (canonical *or* REST spelling) → the canonical tuple;\n * - the operator does not resolve but was plainly meant as one → throw;\n * - it does not look like an operator at all → `undefined`, and the caller\n * falls back to reading the array as a list of values.\n *\n * The old test was `toCanonicalOp(raw[0]) === raw[0]`, i.e. canonical spelling\n * only, with *everything else* — including every REST short-code — dropping\n * through to `[\"in\", raw]`. So the operator string itself became a value in a\n * membership test: `[\"!!\", \"Hello\"]` compiled to `title IN ('!!','Hello')`,\n * which matches, and the caller got back rows their filter was written to\n * exclude. `[\"eq\", \"active\"]` had the same shape of failure.\n */\nfunction readTuple(field: string, raw: unknown): [WhereFilterOp, unknown] | undefined {\n if (!Array.isArray(raw) || raw.length !== 2) return undefined;\n const [op, value] = raw;\n if (typeof op !== \"string\") return undefined;\n\n const canonical = toCanonicalOp(op);\n if (canonical) return [canonical, value];\n\n // A dot means this is a *wire* string, not an operator token: two repeated\n // query params arrive as `[\"gte.18\", \"lt.65\"]`, which is a two-element array\n // of strings and therefore tuple-shaped. Deferred with exactly the test the\n // repeated-dot-string branch below uses, so the two cannot disagree.\n //\n // The property test found this: `[\"ilike\", \"\"]` serializes to `\"ilike.\"`,\n // whose normalized form is a real operator name, so a well-formed\n // round-trip was being rejected as a bad operator.\n if (op.includes(\".\")) return undefined;\n\n if (isOperatorShaped(op)) throw new UnknownFilterOperatorError(field, op);\n\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Serialize: FilterValues → REST querystring\n// ---------------------------------------------------------------------------\n\n/**\n * Encode the `<op>.<value>` half of a wire condition.\n *\n * This is the single leaf encoder. Both wire positions that carry a condition\n * — a top-level query parameter (`?status=eq.active`) and a leaf inside an\n * `and(...)`/`or(...)` group (`or(status.eq.active,…)`) — go through it, so a\n * rule expressed here holds in both. The group serializer used to carry its\n * own copy, and the copy had drifted on every rule that matters: `null` went\n * out as the four-character string, the empty list as `()`, and an operator\n * this dialect does not have was silently rewritten to `eq` — a filter that\n * ran, returned rows, and answered a different question than the one asked.\n *\n * `escapeScalar` is the one thing the two positions legitimately disagree\n * about. A scalar in a query parameter owns the whole value and needs no\n * escaping; a scalar inside a group sits between the same commas a list item\n * does, so a comma in it would end the condition early.\n */\nfunction serializeOperatorAndValue(\n op: WhereFilterOp,\n value: unknown,\n { escapeScalar, where }: { escapeScalar: boolean; where: string }\n): string {\n if (typeof op !== \"string\") {\n throw new TypeError(\n `${where}: operator must be a string, got ${typeof op}`\n );\n }\n\n // Canonical spellings only, on purpose: this codec parses liberally and\n // emits strictly. `deserializeFilter` accepts a REST short-code because one\n // arrives off the wire; a *caller* handing one to the serializer has a\n // condition object built by hand, and the spelling it wants is the one the\n // types name.\n //\n // The throw is the fix. `serializeLogicalCondition` used to end this lookup\n // with `?? \"eq\"`, so `{ operator: \"gte\" }` — the spelling the wire uses, and\n // therefore the one most often guessed — was sent as `age.eq.18`: a query\n // that ran, returned rows, and answered a different question.\n const restOp = CANONICAL_OP_LOOKUP.get(op);\n if (!restOp) {\n throw new TypeError(\n `${where}: unknown operator \"${op}\". Valid operators: ${Object.keys(CANONICAL_TO_REST).join(\", \")}`\n );\n }\n\n // `== null` and `!= null` go out as the null-testing operators.\n //\n // They used to serialize as `eq.null`, and `deserializeTuple` had no way to\n // tell that from a search for the four-character string \"null\" — so it\n // returned the string, and `.where(\"deleted_at\", \"==\", null)` compiled to\n // `deleted_at = 'null'` over HTTP. The typed builder allows it, the Postgres\n // compiler implements it as IS NULL, and only the wire trip broke it.\n //\n // These are the same query: SQL `= NULL` is never true, so `== null` can\n // only mean IS NULL. Emitting it as such is unambiguous in both directions\n // and leaves `eq.null` free to mean the literal string, which it now does.\n if (value === null && (op === \"==\" || op === \"!=\")) {\n return op === \"==\" ? \"isnull.null\" : \"notnull.null\";\n }\n\n // A null test has no operand. Whatever was parked in `value` is dropped\n // here rather than on the way back, so the encoding is stable: both\n // deserializers normalize `isnull.<anything>` to `null`, and re-encoding\n // that must land on the same string it came from.\n if (NULL_OPS.has(op)) return `${restOp}.null`;\n\n if (Array.isArray(value)) {\n // The empty list needs a spelling of its own.\n //\n // A comma-joined format has no way to write \"zero items\": `()` is the\n // empty string between the parens, which splits to `[\"\"]`. So\n // `.where(\"id\", \"in\", [])` — which matches nothing — used to arrive as\n // a search for the empty string: a 500 on a uuid column, silently the\n // wrong rows on a text one.\n //\n // `EMPTY_LIST_TOKEN` is a single unescaped backslash, which no real\n // value can produce: `escapeWireValue` doubles every backslash, so a\n // one-item list holding `\\` serializes as `(\\\\)`. That keeps both\n // directions exact — `[]` and `[\"\"]` stay distinct — rather than\n // trading one lossy reading for another.\n if (value.length === 0) return `${restOp}.(${EMPTY_LIST_TOKEN})`;\n const items = value.map(v => escapeWireValue(stringifyValue(v))).join(\",\");\n return `${restOp}.(${items})`;\n }\n\n const scalar = stringifyValue(value);\n return `${restOp}.${escapeScalar ? escapeWireValue(scalar) : scalar}`;\n}\n\n/**\n * Serialize a single canonical condition tuple to a PostgREST dot-string.\n *\n * Throws `TypeError` if the input is not a valid `[WhereFilterOp, unknown]` tuple.\n *\n * @example\n * serializeTuple([\"==\", \"active\"]) // \"eq.active\"\n * serializeTuple([\"in\", [\"admin\",\"editor\"]]) // \"in.(admin,editor)\"\n * serializeTuple([\">=\", 18]) // \"gte.18\"\n */\nfunction serializeTuple(tuple: [WhereFilterOp, unknown]): string {\n if (!Array.isArray(tuple) || tuple.length !== 2) {\n throw new TypeError(\n `serializeTuple: expected a [WhereFilterOp, value] tuple, got ${JSON.stringify(tuple)}`\n );\n }\n\n const [op, value] = tuple;\n return serializeOperatorAndValue(op, value, {\n escapeScalar: false,\n where: \"serializeTuple\"\n });\n}\n\n/**\n * Convert `FilterValues` (or `WireFilterValues`) to a PostgREST-style\n * querystring record.\n *\n * - Canonical `[WhereFilterOp, value]` tuples are serialized strictly.\n * - Pre-serialized PostgREST strings (e.g. `\"eq.published\"`) are passed through.\n * - Single conditions produce a string value.\n * - Multiple conditions on the same field produce a string array (repeated params).\n *\n * @example\n * serializeFilter({ status: [\"==\", \"active\"] })\n * // → { status: \"eq.active\" }\n *\n * serializeFilter({ age: [[\">=\", 18], [\"<\", 65]] })\n * // → { age: [\"gte.18\", \"lt.65\"] }\n *\n * // Pre-serialized strings pass through unchanged:\n * serializeFilter({ status: \"eq.published\" })\n * // → { status: \"eq.published\" }\n */\nexport function serializeFilter(\n filter: FilterValues<string> | Record<string, unknown>\n): Record<string, string | string[]> {\n const result: Record<string, string | string[]> = {};\n\n for (const [field, condition] of Object.entries(filter)) {\n if (condition === undefined) continue;\n\n // Pre-serialized PostgREST string — pass through unchanged.\n // This supports WireFilterValues where values may already be\n // serialized dot-strings like \"eq.active\" or raw strings like \"true\".\n if (typeof condition === \"string\") {\n result[field] = condition;\n continue;\n }\n\n // Multiple conditions on the same field: array of tuples\n // We detect this by checking if the first element is also an array.\n if (Array.isArray(condition) && condition.length > 0 && Array.isArray(condition[0])) {\n result[field] = (condition as [WhereFilterOp, unknown][]).map(serializeTuple);\n } else {\n // Single condition — must be a [WhereFilterOp, value] tuple\n result[field] = serializeTuple(condition as [WhereFilterOp, unknown]);\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize: REST querystring → FilterValues\n// ---------------------------------------------------------------------------\n\n/**\n * The spellings a null-testing operator's operand may take.\n *\n * The serializer writes `isnull.null`; a hand-written `isnull.true` means the\n * same thing and has always been accepted. Anything else after the operator is\n * not an operand it has — `notnull.reason` is a *value* — see\n * {@link deserializeSingle}.\n */\nconst NULL_OPERANDS: ReadonlySet<string> = new Set([\"null\", \"true\", \"false\", \"\"]);\n\n/**\n * Parse a single PostgREST dot-string into a `[WhereFilterOp, unknown]` tuple.\n *\n * All values are returned as strings — the wire format carries no type\n * metadata, so coercion is the data driver's responsibility.\n *\n * ## When a leading segment is an operator, and when it is part of the value\n *\n * `?status=in.progress` and `?status=in.(a,b)` differ by one character and mean\n * entirely different things, and the reading here decides which. The rule, in\n * full:\n *\n * > A dot-string is read as `operator.operand` **only** when its first segment\n * > names a known REST operator **and** what follows is a well-formed operand\n * > *for that operator's arity*. Otherwise the whole string is the value.\n *\n * Arity, per operator family:\n *\n * - **List** operators (`in`, `nin`, `csa` — `LIST_OPS`) take a parenthesised\n * list and nothing else. `in.(draft,review)` is the operator; `in.progress`\n * is the *value* `\"in.progress\"`, because there is no list there and so no\n * `in` filter that could have been written. That case used to compile to\n * `status IN ('progress')` — a filter the caller never wrote, quietly\n * matching the wrong rows and, on a status field, hiding every row they were\n * looking for.\n * - **Null** operators (`isnull`, `notnull` — {@link NULL_OPS}) take no\n * operand: only {@link NULL_OPERANDS}. `notnull.reason` is the value\n * `\"notnull.reason\"`, not \"reason is not null\".\n * - **Everything else** takes one scalar, and any remainder is one — including\n * the empty string, so `eq.` really is \"equals the empty string\".\n *\n * ### The one ambiguity that remains, and how to write past it\n *\n * A scalar operator's operand is unconstrained, so `?status=like.that` is a\n * `LIKE 'that'` and no rule at this layer can tell it from the literal value\n * `\"like.that\"` — both are well-formed encodings, and picking either by guess\n * would break the other. Two spellings say \"value\" unambiguously, and both\n * round-trip:\n *\n * - `?status=eq.like.that` — name the operator. The *first* segment is consumed\n * as the operator and everything after it is the value, dots and all. This is\n * what `serializeFilter` emits, which is why the SDK never meets the\n * ambiguity at all.\n * - `?where={\"status\":[\"==\",\"like.that\"]}` — the JSON dialect's tuple form.\n *\n * Values that merely *contain* dots (`user@host.com`, `1.2.3`) were never\n * ambiguous: their first segment names no operator to begin with.\n */\nfunction deserializeSingle(raw: string): [WhereFilterOp, unknown] {\n const dotIndex = raw.indexOf(\".\");\n if (dotIndex === -1) {\n // No dot → equality on the raw value (kept as string)\n return [\"==\", raw];\n }\n\n const prefix = raw.substring(0, dotIndex);\n const rest = raw.substring(dotIndex + 1);\n\n // Check if the prefix is a known REST operator.\n // This is the key defense against values like \"eq.something\" or \"gt.foo\"\n // being misinterpreted — only known REST short-codes are treated as operators.\n const canonicalOp = REST_OP_LOOKUP.get(prefix);\n if (!canonicalOp) {\n // Not a known operator (e.g., email \"user@host.com\" or version \"1.2.3\")\n // Treat the entire string as an equality value\n return [\"==\", raw];\n }\n\n // Null-testing operators ignore their serialized value — normalize to null\n // so the tuple round-trips stably (`isnull.null` → [\"is-null\", null]).\n if (NULL_OPS.has(canonicalOp)) {\n // ...but only when what follows is an operand this operator has. See\n // the docblock: `notnull.reason` names no null test, so it is a value.\n if (!NULL_OPERANDS.has(rest)) return [\"==\", raw];\n return [canonicalOp, null];\n }\n\n // Parse list values: \"(admin,editor)\" → [\"admin\", \"editor\"]\n if (rest.startsWith(\"(\") && rest.endsWith(\")\")) {\n const inner = rest.slice(1, -1);\n // See EMPTY_LIST_TOKEN: `(\\)` is the empty list. `()` remains a list\n // holding one empty string, which is what splitting it yields anyway.\n const items = inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner);\n return [canonicalOp, items];\n }\n\n // A list operator with no list is not that operator — see the docblock.\n // `?status=in.progress` is the value \"in.progress\"; the `in` filter it used\n // to compile to was never written by anyone.\n if (LIST_OPS.has(canonicalOp)) return [\"==\", raw];\n\n return [canonicalOp, rest];\n}\n\n/**\n * Convert a PostgREST-style querystring record to `FilterValues`.\n *\n * - String values are parsed as single conditions.\n * - String arrays (repeated query params) become multiple conditions on the same field.\n *\n * @example\n * deserializeFilter({ status: \"eq.active\" })\n * // → { status: [\"==\", \"active\"] }\n *\n * deserializeFilter({ age: [\"gte.18\", \"lt.65\"] })\n * // → { age: [[\">=\", \"18\"], [\"<\", \"65\"]] }\n *\n * @throws {UnknownFilterOperatorError} when a condition names an operator this\n * dialect does not have. See that class for why a rejection here is a throw.\n */\nexport function deserializeFilter(\n query: Record<string, unknown>\n): FilterValues<string> {\n const result: FilterValues<string> = {};\n\n for (const [field, raw] of Object.entries(query)) {\n if (raw === undefined) continue;\n\n // A single `[op, value]` condition.\n const tuple = readTuple(field, raw);\n if (tuple) {\n result[field] = tuple;\n continue;\n }\n\n if (Array.isArray(raw)) {\n if (raw.length === 0) continue;\n\n // An array of tuples: several conditions on the same field. Every\n // element is checked, not just the first — the old test read\n // `raw[0]` and cast the whole array, so one bad operator among\n // several travelled on untouched.\n if (Array.isArray(raw[0])) {\n const tuples = raw.map(item => readTuple(field, item));\n if (tuples.every((t): t is [WhereFilterOp, unknown] => t !== undefined)) {\n result[field] = tuples;\n continue;\n }\n }\n\n if (raw.length === 1) {\n result[field] = typeof raw[0] === \"string\" ? deserializeSingle(raw[0]) : [\"==\", raw[0]];\n } else {\n // If the elements are strings, they might be PostgREST dot-strings (repeated params)\n if (typeof raw[0] === \"string\" && raw[0].includes(\".\")) {\n result[field] = raw.map(r => typeof r === \"string\" ? deserializeSingle(r) : ([\"==\", r] as [WhereFilterOp, unknown])) as [WhereFilterOp, unknown][];\n } else {\n // Otherwise assume it's a list of values for an implicit\n // \"in\" — `{ tags: [\"a\",\"b\"] }`, and `?tags=a&tags=b`, which\n // arrives here identically.\n //\n // A two-element array reaches this line only after\n // `readTuple` has decided its first element was not meant\n // as an operator. Everything longer never had the\n // ambiguity: an operator tuple has exactly two slots.\n result[field] = [\"in\", raw];\n }\n }\n } else if (typeof raw === \"string\") {\n result[field] = deserializeSingle(raw);\n } else {\n result[field] = [\"==\", raw];\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Logical conditions: serialize / deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a `LogicalCondition` or `FilterCondition` to its wire-format string.\n *\n * Leaf encoding is {@link serializeOperatorAndValue}, the same function\n * `serializeTuple` uses, so `null`, the empty list and an unknown operator\n * behave identically inside a group and in a query parameter.\n *\n * @throws {TypeError} when a leaf names an operator this dialect does not have.\n * It used to fall back to `eq`, which turned `age >= 18` into `age = 18` with\n * no diagnostic anywhere.\n *\n * @example\n * serializeLogicalCondition({ column: \"status\", operator: \"==\", value: \"active\" })\n * // → \"status.eq.active\"\n *\n * serializeLogicalCondition({ column: \"deleted_at\", operator: \"==\", value: null })\n * // → \"deleted_at.isnull.null\"\n *\n * serializeLogicalCondition({ type: \"or\", conditions: [...] })\n * // → \"or(status.eq.active,status.eq.pending)\"\n */\nexport function serializeLogicalCondition(\n cond: LogicalCondition | FilterCondition\n): string {\n if (\"type\" in cond) {\n // LogicalCondition (and/or)\n const inner = (cond.conditions ?? [])\n .map(serializeLogicalCondition)\n .join(\",\");\n return `${cond.type}(${inner})`;\n }\n\n // FilterCondition. The leaf goes through the shared encoder, so a group\n // condition and a query parameter agree on nulls, empty lists and unknown\n // operators — see `serializeOperatorAndValue`.\n //\n // The column is escaped like a value: it is not one, but it shares the\n // delimiters, and a comma or paren in it would move where the group parser\n // thinks the condition ends. Dots are deliberately *not* escaped — a\n // relation path is `author.name` on the wire, and the parser below finds\n // the operator rather than assuming it is the second segment.\n return `${escapeWireValue(cond.column)}.${serializeOperatorAndValue(cond.operator, cond.value, {\n escapeScalar: true,\n where: \"serializeLogicalCondition\"\n })}`;\n}\n\n/**\n * Split a leaf condition into `column`, operator token and value.\n *\n * The naive reading — column is everything before the first dot, operator is\n * everything up to the second — cannot express a relation path. A filter on\n * `author.name` serializes to `author.name.eq.bob` and came back as the column\n * `author` with the operator `name`, which resolves to nothing, so the\n * fallback made it `author == \"eq.bob\"`: a condition that runs and matches\n * nothing, on a column the caller never named.\n *\n * So the operator is found rather than assumed: it is the first dot-separated\n * segment after the column that resolves to a real operator. Everything before\n * it is the column, everything after is the value. `version.eq.1.2.3` reads as\n * `version == \"1.2.3\"` because the scan stops at the first match — the `eq` at\n * offset 1, not a later segment — and `metadata->>x.eq.5` never had dots in the\n * column to begin with.\n *\n * Returns `undefined` when no segment resolves — `status.active`, an equality\n * written without an operator, which the caller handles.\n */\nfunction splitLeafCondition(str: string): { column: string; operator: WhereFilterOp; value: string } | undefined {\n // Dots inside a list value (`in.(1.5,2.5)`) are not separators. The value\n // always follows the operator, so the search only needs the region before\n // the first unescaped paren.\n let limit = str.length;\n for (let i = 0; i < str.length; i++) {\n if (str[i] === \"\\\\\") { i++; continue; }\n if (str[i] === \"(\") { limit = i; break; }\n }\n\n const dots: number[] = [];\n for (let i = 0; i < limit; i++) {\n if (str[i] === \"\\\\\") { i++; continue; }\n if (str[i] === \".\") dots.push(i);\n }\n\n // Segment 0 is always the column, and an operator needs a value after it,\n // so a candidate is bounded on both sides by a dot.\n for (let i = 1; i < dots.length; i++) {\n const operator = toCanonicalOp(str.substring(dots[i - 1] + 1, dots[i]));\n if (!operator) continue;\n return {\n column: unescapeWireValue(str.substring(0, dots[i - 1])),\n operator,\n value: str.substring(dots[i] + 1)\n };\n }\n\n return undefined;\n}\n\n/**\n * Parse a logical condition wire-format string back into a\n * `LogicalCondition` or `FilterCondition`.\n *\n * @example\n * deserializeLogicalCondition(\"status.eq.active\")\n * // → { column: \"status\", operator: \"==\", value: \"active\" }\n *\n * deserializeLogicalCondition(\"or(status.eq.active,age.gte.18)\")\n * // → { type: \"or\", conditions: [...] }\n */\n/**\n * How deeply `or(...)`/`and(...)` groups may nest.\n *\n * This parser recurses once per level, on a value that arrives in a query\n * string. Unbounded, twenty thousand levels reached `RangeError: Maximum call\n * stack size exceeded`, which a caller sees as a 500 about the call stack\n * rather than a 400 about their filter. Node's 16 KB header cap keeps a GET\n * below that in practice, but \"the HTTP layer happens to stop it\" is not a\n * bound this parser should rely on.\n *\n * Thirty-two is far past anything a real filter expresses; the deepest in this\n * repository's own tests is three.\n */\nexport const MAX_LOGICAL_NESTING_DEPTH = 32;\n\nexport function deserializeLogicalCondition(\n str: string,\n // Not `depth`: the body already uses that name for paren tracking, inside a\n // block that shadows a parameter of the same name — so the recursion\n // counter silently became the paren counter and never grew.\n nesting = 0\n): LogicalCondition | FilterCondition {\n if (nesting > MAX_LOGICAL_NESTING_DEPTH) {\n throw new Error(\n `Filter groups nest more than ${MAX_LOGICAL_NESTING_DEPTH} levels deep. ` +\n \"Flatten the condition — `or(a,or(b,c))` is `or(a,b,c)`.\"\n );\n }\n // Check for logical group: \"and(...)\", \"or(...)\" or \"not(...)\"\n const logicalMatch = str.match(/^(and|or|not)\\((.+)\\)$/);\n if (logicalMatch) {\n const type = logicalMatch[1] as \"and\" | \"or\" | \"not\";\n const innerStr = logicalMatch[2];\n\n const conditions = splitGroupItems(innerStr)\n .map(part => deserializeLogicalCondition(part, nesting + 1));\n\n return { type, conditions };\n }\n\n // FilterCondition: \"column.op.value\"\n const leaf = splitLeafCondition(str);\n if (!leaf) {\n const firstDot = str.indexOf(\".\");\n if (firstDot === -1) {\n return { column: unescapeWireValue(str), operator: \"==\", value: true };\n }\n // \"column.value\" — no segment resolved as an operator, so this is an\n // equality written without one. The value keeps its dots.\n return {\n column: unescapeWireValue(str.substring(0, firstDot)),\n operator: \"==\",\n value: unescapeWireValue(str.substring(firstDot + 1))\n };\n }\n\n const { column, operator, value: valueStr } = leaf;\n\n // A null test has no operand: `isnull.null` is what the serializer writes,\n // but a hand-written `isnull.true` means the same thing. Normalizing here\n // is what makes the tuple stable through a re-encode, and it matches\n // `deserializeSingle`, which has done it for query parameters all along.\n if (NULL_OPS.has(operator)) {\n return { column, operator, value: null };\n }\n\n // Parse list values with escape-aware splitting. The wrapping parens are\n // written by the serializer *after* the items are escaped, so an escaped\n // paren inside an item can never be mistaken for them.\n if (valueStr.startsWith(\"(\") && valueStr.endsWith(\")\")) {\n const inner = valueStr.slice(1, -1);\n // See EMPTY_LIST_TOKEN: `(\\)` is the empty list, which is not the same\n // query as a search for the empty string.\n const items = inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner);\n return { column, operator, value: items };\n }\n\n return { column, operator, value: unescapeWireValue(valueStr) };\n}\n","import { CollectionAccessor, DataDriver, Entity, EntityValues, FindAllParams, FindParams, FindResponse, FindResult, IterateParams, LogicalCondition, OrderByTuple, PageWalkOptions, RebaseApiError, RebaseData, RebaseSdkData, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, sortKeyToString, type AggregateParams, type AggregateRow, type AggregateSelect, type ComputedSortField, type FieldPath, type IncludeSpec, type NonColumnFieldPath, type NullsPlacement, type SearchMatch, type UpdateValues, type UpsertOptions, WhereFilterOp, WhereValueFor, isUnsupported, unsupportedMethod } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { cursorToStartAfter, decodeCursor, reconcileCursorOrder } from \"./cursor\";\nimport { mergeIncludeSpecs } from \"./include-spec\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind, resolveFindWindow } from \"./paginate\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\n\n/**\n * What a client says when its data source cannot subscribe.\n *\n * Named rather than inlined so the sentence a caller sees does not depend on\n * which of the two adapters below happened to build the client.\n */\nconst noRealtime = (slug: string): string =>\n `Realtime is not available for \"${slug}\": its data source does not support subscriptions.`;\n\n/** What a client says when its data source cannot count. */\nconst noCount = (slug: string): string =>\n `Counting is not available for \"${slug}\": its data source does not support it.`;\n\n/**\n * Derive the response key an aggregate comes back under.\n *\n * `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because\n * the REST parser derives the same alias from `?select=sum(total)` and the two\n * have to agree — a caller reading `row.sum_total` off an SDK result and off an\n * HTTP response is reading the same key or the SDK is broken.\n */\nexport function aggregateAlias(fn: string, field?: string): string {\n return field ? `${fn}_${field}` : fn;\n}\n\nfunction toDriverAggregate(\n select: AggregateSelect<Record<string, unknown>>\n): { fn: \"count\" | \"sum\" | \"avg\" | \"min\" | \"max\"; field?: string; alias: string } {\n const field = select.field as string | undefined;\n return { fn: select.fn, field, alias: aggregateAlias(select.fn, field) };\n}\n\n/**\n * What a client says when its data source cannot aggregate.\n *\n * A stub rather than a fallback that fetches and reduces in JavaScript: that\n * would be wrong under a `limit` and unaffordable without one, and it would look\n * like it had worked.\n */\nconst noAggregate = (slug: string): string =>\n `Aggregates are not available for \"${slug}\": its data source does not implement them.`;\n\nexport interface EntityDataOptions {\n /**\n * Look up a collection's config by slug, to derive row addresses from its\n * primary keys.\n *\n * Called lazily rather than up front: the data layer is created by `Rebase`,\n * which sits *above* the admin that owns the collections, so a resolver\n * registered on mount would otherwise arrive too late to be seen.\n */\n resolveCollection?: (slug: string) => { properties?: Record<string, unknown> } | undefined;\n}\n\nfunction createPrimaryKeyResolver(options?: EntityDataOptions) {\n const cache = new Map<string, PrimaryKeyInfo[]>();\n const warned = new Set<string>();\n\n return function primaryKeysFor(slug: string): PrimaryKeyInfo[] {\n const cached = cache.get(slug);\n if (cached) return cached;\n\n const collection = options?.resolveCollection?.(slug);\n if (!collection) {\n // The registry may not have been registered yet. Don't memoize a\n // miss, or the collection would stay address-less for this session.\n return [];\n }\n\n const keys = resolvePrimaryKeys(collection);\n if (keys.length > 0) {\n // Memoized for the session: a collection's key does not change\n // while the app runs, and this is called once per row. Editing\n // `isId` in the schema editor needs a reload to take effect here.\n cache.set(slug, keys);\n return keys;\n }\n\n if (!warned.has(slug)) {\n warned.add(slug);\n // Silence here surfaces much later as rows that cannot be opened,\n // linked, or saved, with nothing pointing back at the cause.\n console.warn(\n `[rebase] Collection '${slug}' declares no primary key, so its rows have no address: ` +\n `detail links, caching and relations will not work for it. ` +\n `Mark the key property with \\`isId\\` in its collection config — the server logs which ` +\n `column to mark at boot, if its schema knows the key.`\n );\n }\n return keys;\n };\n}\n\n/**\n * Give a flat row the Entity view-model the admin renders.\n *\n * The address is *derived here* — it is not a column, and the row it came from\n * does not contain one. Rows carry exactly what the table has, with the types\n * Postgres returned; the id is this layer's invention, and this is the only\n * place it is minted.\n *\n * `primaryKeys` empty falls back to a literal `id` on the row: drivers other\n * than postgres still serve rows with one, and this keeps them working.\n */\nfunction rowToEntity<M extends Record<string, unknown>>(\n row: Record<string, unknown>,\n slug: string,\n primaryKeys: PrimaryKeyInfo[] = []\n): Entity<M> {\n // Query-computed metadata rides in on the row because that is how the wire\n // carries it, but it is not a column: it belongs beside `values`, not in\n // them. Left inside, `_matches` would show up in the record inspector as a\n // field the collection never declared.\n const { _matches, ...values } = row as Record<string, unknown> & { _matches?: SearchMatch[] };\n\n return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: values as EntityValues<M>,\n ...(_matches ? { searchMatches: _matches } : {})\n };\n}\n\n/**\n * The relation envelope `toFlatRow` writes where a relation was:\n * `{ id, path, __type: \"relation\", data: { id, path, values } }`. It is the\n * admin's view-model, and the only pipeline that produces one is postgres'.\n */\nfunction isRelationEnvelope(\n value: unknown\n): value is { __type: \"relation\"; data?: { values?: Record<string, unknown> } } {\n return typeof value === \"object\"\n && value !== null\n && !Array.isArray(value)\n && (value as { __type?: unknown }).__type === \"relation\";\n}\n\n/** The target's own columns, as `toRestRow` would have inlined them. */\nfunction inlineEnvelope(envelope: { data?: { values?: Record<string, unknown> } }): Record<string, unknown> {\n return envelope.data?.values ?? {};\n}\n\n/**\n * Replace every relation envelope on a row with the target's flat columns.\n *\n * The SDK serves one relation shape — the inlined one (see\n * {@link RestFetchService}) — and reads that come back through a *driver*\n * method rather than the REST pipeline still carry envelopes. Realtime is the\n * one such read left: there is no `listenForRest`, so the rows arrive shaped\n * for the admin and are flattened here instead.\n *\n * Only applied where the REST pipeline is the contract (see `find`); a driver\n * without a `restFetchService` keeps whatever it returns, so the admin's own\n * path through {@link buildRebaseData} is untouched.\n */\nfunction inlineRelationRefs(row: Record<string, unknown>): Record<string, unknown> {\n let out: Record<string, unknown> | undefined;\n for (const [key, value] of Object.entries(row)) {\n if (isRelationEnvelope(value)) {\n out = out ?? { ...row };\n out[key] = inlineEnvelope(value);\n } else if (Array.isArray(value) && value.some(isRelationEnvelope)) {\n out = out ?? { ...row };\n out[key] = value.map((item) => isRelationEnvelope(item) ? inlineEnvelope(item) : item);\n }\n }\n return out ?? row;\n}\n\nfunction createDriverAccessor<M extends Record<string, unknown> = Record<string, unknown>>(\n driver: DataDriver,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => []\n): CollectionAccessor<M> {\n const accessor: CollectionAccessor<M> = {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n // Ensure filters are in canonical [op, value] format even if passed as PostgREST strings\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n\n // Keyset paging, through the same codec and the same driver\n // comparison the HTTP route uses. The in-process accessor is a\n // transport like any other: a walk that seeked differently here\n // than over the wire would be a difference the types cannot see.\n const cursor = params?.after ? decodeCursor(params.after) : undefined;\n const orderBy = cursor\n ? reconcileCursorOrder(cursor, normalizeOrderBy(params?.orderBy))\n : normalizeOrderBy(params?.orderBy);\n const startAfter = cursor ? cursorToStartAfter(cursor) : undefined;\n\n // One relation shape, whatever the call looks like.\n //\n // This used to fork on `include`: asking for one ran the REST\n // pipeline, which inlines a relation as the target's own columns;\n // not asking ran the driver's own fetch, which eagerly loaded\n // *every* relation and put a `{ __type: \"relation\" }` envelope\n // where the foreign key was. The same method answered in two\n // shapes, the generated types described only one, and a column\n // typed `string` arrived as an object.\n //\n // The REST pipeline is the published contract — the shape the HTTP\n // API serves for this same query, and what `RestFetchService`\n // documents — so every read goes through it when the driver has\n // one. Drivers without one (every browser driver, and so the\n // admin's own path through `buildRebaseData`) are untouched.\n //\n // One row past the page, when seeking.\n //\n // `hasMore` on an offset page is `offset + rows.length < total`, and\n // under a cursor that arithmetic is simply false: every seeked page\n // runs at offset 0, so it compares one page against the whole\n // collection and says \"more\" forever. Asking for `limit + 1` and\n // looking at whether the extra row arrived is the answer keyset\n // paging actually has — and it costs nothing, where the count it\n // replaces was a second query per page.\n const probeLimit = startAfter ? limit + 1 : limit;\n\n const fetchService = driver.restFetchService;\n const fetched = fetchService\n ? await fetchService.fetchCollectionForRest(\n slug,\n {\n filter,\n // Without this the group was dropped and the read ran\n // unfiltered — every row the caller's policies allow,\n // in place of the ones they asked for.\n logical: params?.logical,\n limit: probeLimit,\n // A cursor and an offset describe the same window two\n // incompatible ways; seeking wins and the offset is not\n // sent, or the page would start `offset` rows past\n // where the cursor pointed.\n offset: startAfter ? undefined : driverOffset,\n startAfter,\n orderBy,\n searchString: params?.searchString,\n fields: params?.fields,\n distinct: params?.distinct\n },\n params?.include\n )\n : await driver.fetchCollection<M>({\n path: slug,\n limit: probeLimit,\n offset: startAfter ? undefined : driverOffset,\n startAfter,\n filter,\n logical: params?.logical,\n orderBy,\n searchString: params?.searchString,\n include: params?.include,\n fields: params?.fields,\n distinct: params?.distinct\n });\n\n // The probe row is evidence, not data — it is never served.\n const seeking = startAfter !== undefined;\n const rows = seeking ? fetched.slice(0, limit) : fetched;\n\n // Compute real total when count is available\n let total = rows.length + offset;\n let hasMore = seeking ? fetched.length > limit : rows.length >= limit;\n if (driver.count) {\n // The same narrowing the rows were read with. Counting only by\n // `filter` reported the whole collection beside a narrowed\n // page, and `hasMore` is derived from it — so the list offered\n // a next page that did not exist.\n total = await driver.count({\n path: slug,\n filter,\n logical: params?.logical,\n searchString: params?.searchString\n });\n // ...but only for an *offset* page. `offset` is 0 on every\n // seeked page, so this arithmetic compares one page against the\n // whole collection and says \"more\" forever; the probe row above\n // is what answers it under a cursor.\n if (!seeking) hasMore = offset + rows.length < total;\n }\n\n // The cursor for the *next* page, from the last row served. Issued\n // by the driver, which is the only layer that knows which columns\n // address a row; absent where it cannot describe one, and the\n // caller then pages by offset.\n const last = rows[rows.length - 1] as Record<string, unknown> | undefined;\n const nextCursor = (hasMore && last && driver.restFetchService?.cursorFor)\n ? driver.restFetchService.cursorFor(slug, last, orderBy)\n : undefined;\n\n return {\n data: rows.map((row: Record<string, unknown>) => rowToEntity<M>(row, slug, getPks())),\n meta: { total, limit, offset, hasMore, ...(nextCursor && { nextCursor }) }\n };\n },\n\n async findById(id: string | number): Promise<Entity<M> | undefined> {\n // Same contract as `find` above: one row read the same way the\n // collection read is, so `find()[0]` and `findById()` agree.\n const fetchService = driver.restFetchService;\n const row = fetchService\n ? await fetchService.fetchOneForRest(slug, id)\n : await driver.fetchOne<M>({ path: slug, id: id });\n return row ? rowToEntity<M>(row, slug, getPks()) : undefined;\n },\n\n // Present only when the driver's fetch service implements it — the SDK\n // wrapper turns an absent one into a stub that names the capability.\n aggregate: driver.restFetchService?.aggregate\n ? async (params: AggregateParams<M>): Promise<AggregateRow[]> =>\n driver.restFetchService!.aggregate!(slug, {\n aggregates: params.select.map(toDriverAggregate),\n groupBy: params.groupBy as string[] | undefined,\n filter: params.where\n ? deserializeFilter(params.where as Record<string, unknown>)\n : undefined,\n logical: params.logical,\n searchString: params.searchString,\n limit: params.limit\n })\n : undefined,\n\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: slug,\n values: data,\n id: id,\n status: \"new\"\n });\n return rowToEntity<M>(row, slug, getPks());\n },\n\n createMany: driver.saveMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: slug,\n rows: data,\n upsert: options?.upsert,\n // Dropped here, an `upsert` on a natural key silently\n // became an upsert on the primary key — which for a serial\n // id is a plain insert, so the re-runnable import the\n // option exists for duplicated every row instead.\n onConflict: options?.onConflict\n });\n return rows.map((row) => rowToEntity<M>(row, slug, getPks()));\n }\n : undefined,\n\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: slug,\n values: data,\n id: id,\n status: \"existing\"\n });\n return rowToEntity<M>(row, slug, getPks());\n },\n\n async delete(id: string | number): Promise<void> {\n return driver.delete({\n row: { id,\npath: slug,\nvalues: {} as Record<string, unknown> }\n });\n },\n\n // Present only when the driver is: exposing these unconditionally and\n // looping single writes underneath would give a caller neither the\n // atomicity nor the single round trip they reached for a batch to get,\n // while looking exactly like it had.\n updateMany: driver.updateMany\n ? async (updates: { id: string | number; data: Partial<EntityValues<M>> }[]): Promise<Entity<M>[]> => {\n const rows = await driver.updateMany!<M>({\n path: slug,\n updates: updates.map(u => ({ id: u.id,\nvalues: u.data })),\n });\n return rows.map(row => rowToEntity<M>(row, slug, getPks()));\n }\n : undefined,\n\n deleteMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: slug,\nids });\n }\n : undefined,\n\n count: driver.count\n ? async (params?: FindParams<M>): Promise<number> => {\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n // Every narrowing `find()` applies has to apply here too, or\n // the count describes a different query than the one it is\n // reported against.\n return driver.count!({\n path: slug,\n filter,\n logical: params?.logical,\n searchString: params?.searchString\n });\n }\n : undefined,\n\n listen: driver.listenCollection\n ? (params: FindParams<M> | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void) => {\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n // Realtime has no REST-pipeline equivalent, so the rows arrive\n // admin-shaped. Flatten them to the one shape the rest of this\n // accessor serves.\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenCollection!<M>({\n path: slug,\n limit,\n offset: driverOffset,\n filter: params?.where,\n logical: params?.logical,\n orderBy: normalizeOrderBy(params?.orderBy),\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Forwarded so the SERVER can refuse it. `realtimeService`\n // rejects a subscription carrying `vectorSearch` — a\n // subscription is re-run on every matching write and\n // nothing there computes distances — and the docs promise\n // that refusal. Both producers hand-list their fields and\n // both omitted this one, so the guard could not fire and\n // `.vectorSearch(…).listen()` returned an ordinary\n // `id DESC` listing with no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks())),\n meta: {\n // No count is issued on this path, so the total\n // is unknown; the lower bound is the rows in\n // hand plus the ones paged past to reach them.\n // Reporting `entities.length` claimed a read at\n // offset 100 had found a collection of two.\n total: offset + entities.length,\n limit,\n offset,\n hasMore: entities.length >= limit\n }\n });\n },\n onError\n });\n } : undefined,\n\n listenById: driver.listenOne\n ? (id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void) => {\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenOne!<M>({\n path: slug,\n id: id,\n onUpdate: (entity) => onUpdate(entity ? rowToEntity<M>(normalize(entity), slug, getPks()) : undefined),\n onError\n });\n } : undefined,\n\n // Fluent Query Builder\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, ascending?: \"asc\" | \"desc\") {\n return new QueryBuilder<M>(accessor).orderBy(column, ascending);\n },\n limit(count: number) {\n return new QueryBuilder<M>(accessor).limit(count);\n },\n offset(count: number) {\n return new QueryBuilder<M>(accessor).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new QueryBuilder<M>(accessor).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new QueryBuilder<M>(accessor).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new QueryBuilder<M>(accessor).include(...relations);\n }\n };\n\n return accessor;\n}\n\n/**\n * Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.\n *\n * This is the key bridge: any property access like `data.products` returns\n * a `CollectionAccessor` backed by the underlying DataDriver, without\n * needing per-collection code generation.\n *\n * @example\n * const data = buildRebaseData(driver);\n * await data.products.create({ name: \"Camera\", price: 299 });\n * const { data: items } = await data.products.find({ where: { status: [\"==\", \"published\"] } });\n */\nexport function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug));\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = {\n collection: getAccessor\n } as RebaseData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n // Ignore Symbol properties (e.g. Symbol.toPrimitive, Symbol.iterator)\n if (typeof prop === \"symbol\") return undefined;\n // Ignore internal JS properties\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n\n // Convert camelCase property names to snake_case slugs\n const slug = toSnakeCase(prop);\n return getAccessor(slug);\n }\n });\n}\n\n// =============================================================================\n// SDK data — flat rows (symmetric with the frontend SDK client)\n// =============================================================================\n\n/**\n * Unwrap a Entity back into the flat row it was built from. `rowToEntity` keeps\n * the row untouched under `.values` and derives `.id` alongside it, so dropping\n * the wrapper is the whole operation — the address was never part of the row.\n */\nfunction entityToRow<M extends Record<string, unknown>>(entity: Entity<M>): M {\n return entity.values as unknown as M;\n}\n\n/**\n * Fluent query builder for the flat SDK data layer. Mirrors {@link QueryBuilder}\n * but resolves to `FindResult<M>` (flat rows) instead of Entity-wrapped\n * `FindResponse<M>`.\n */\nclass SdkQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n private params: FindParams = { where: {} };\n\n constructor(private client: SDKCollectionClient<M>) {}\n\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n /** A relation path (`author.name`) or a JSON path (`metadata->>tier`). */\n where(column: NonColumnFieldPath, operator: WhereFilterOp, value: unknown): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK\n // builder in `@rebasepro/client`, which had the same defect.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n if (!this.params.where) this.params.where = {};\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n return this;\n }\n\n /** Called again, this adds a tie-breaker rather than replacing the sort. */\n orderBy(\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction: \"asc\" | \"desc\" = \"asc\",\n nulls?: NullsPlacement\n ): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n const key = sortKeyToString(column);\n this.params.orderBy = [...existing, (nulls\n ? [key, direction, nulls]\n : [key, direction]) as OrderByTuple];\n return this;\n }\n\n limit(count: number): this { this.params.limit = count; return this; }\n offset(count: number): this { this.params.offset = count; return this; }\n search(searchString: string, options?: { explain?: boolean }): this { this.params.searchString = searchString; if (options?.explain !== undefined) this.params.searchExplain = options.explain; return this; }\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n /**\n * Load relations. Merges rather than replaces, so `.include(\"author\")` then\n * `.include({ comments: { limit: 5 } })` asks for both — a builder call that\n * silently discarded an earlier one is the same defect `where` had.\n */\n include(...relations: (string | IncludeSpec)[]): this {\n this.params.include = mergeIncludeSpecs(this.params.include, relations);\n return this;\n }\n\n fields(...columns: (FieldPath<M> | string)[]): this {\n this.params.fields = [...(this.params.fields ?? []), ...columns as string[]];\n return this;\n }\n\n distinct(enabled = true): this { this.params.distinct = enabled; return this; }\n\n after(cursor: string): this { this.params.after = cursor; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n /** Aggregate the matching rows. See {@link SDKCollectionClient.aggregate}. */\n async aggregate(\n params: Omit<AggregateParams<M>, \"where\" | \"logical\" | \"searchString\">\n ): Promise<AggregateRow[]> {\n return this.client.aggregate({\n ...params,\n where: this.params.where as AggregateParams<M>[\"where\"],\n logical: this.params.logical,\n searchString: this.params.searchString\n });\n }\n\n /**\n * Page through everything this query matches, one row at a time.\n *\n * `.limit()` on the builder becomes the page size, so the ceiling on a\n * single `find()` is not a ceiling on what the query can read.\n */\n iterate(options?: PageWalkOptions<M>): AsyncIterableIterator<M> {\n return this.client.iterate({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as IterateParams<M>);\n }\n\n /** Collect everything this query matches into one array. */\n findAll(options?: PageWalkOptions<M> & { maxRows?: number }): Promise<M[]> {\n return this.client.findAll({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as FindAllParams<M>);\n }\n\n /**\n * Count the records matching this query.\n *\n * This used to answer `0` when the client had no `count` — a number, from a\n * source that had not counted anything, indistinguishable from an empty\n * collection. It now does what the client does, which on a source that\n * cannot count is throw and say so.\n */\n async count(): Promise<number> {\n return this.client.count(this.params as FindParams<M>);\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n return this.client.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n\n/**\n * Wrap a Entity-shaped {@link CollectionAccessor} into a flat\n * {@link SDKCollectionClient}. Every returned record is unwrapped to a flat row\n * so the backend SDK is byte-for-byte the same shape as the frontend client.\n */\nfunction toSdkCollectionClient<M extends Record<string, unknown>>(\n snap: CollectionAccessor<M>,\n slug = \"collection\"\n): SDKCollectionClient<M> {\n const client: SDKCollectionClient<M> = {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const res = await snap.find(params);\n return { data: res.data.map(entityToRow), meta: res.meta };\n },\n // Pagination is shared with the HTTP client rather than reimplemented:\n // both transports satisfy the same `SDKCollectionClient`, so a walk that\n // behaved differently in-process than over the wire would be a bug the\n // type system could not see.\n iterate(params?: IterateParams<M>) {\n return paginateFind<M>((p) => client.find(p), params, slug);\n },\n findAll(params?: FindAllParams<M>) {\n return collectAllPages<M>((p) => client.find(p), params, slug);\n },\n async findById(id: string | number): Promise<M | undefined> {\n const s = await snap.findById(id);\n return s ? entityToRow(s) : undefined;\n },\n async get(id: string | number): Promise<M> {\n // The same contract server-side as in the browser SDK, deliberately:\n // a callback, a cron and an app all read a row by id, and the shape\n // of \"it is not there\" should not depend on which one is asking.\n const s = await snap.findById(id);\n if (!s) {\n throw new RebaseApiError(\n `No record with id ${JSON.stringify(String(id))} in \"${slug}\".`,\n { status: 404, code: \"NOT_FOUND\" }\n );\n }\n return entityToRow(s);\n },\n async create(data: Partial<M>, id?: string | number): Promise<M> {\n return entityToRow(await snap.create(data as Partial<EntityValues<M>>, id));\n },\n async createMany(data: Partial<M>[], options?: { upsert?: boolean }): Promise<M[]> {\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n if (!snap.createMany) {\n throw new Error(\n \"Bulk writes are not supported by this collection's data source. \" +\n \"Fall back to create() per record.\"\n );\n }\n const rows = await snap.createMany(data as Partial<EntityValues<M>>[], options);\n return rows.map(entityToRow);\n },\n /**\n * One row through the bulk path, because the bulk path is where the\n * conflict target lives.\n *\n * `CollectionAccessor` has no single-row upsert and adding one would\n * mean a second way to say the same thing to the same driver method —\n * `saveMany` already takes `upsert` and `onConflict`, and a batch of\n * one is exactly an upsert of one.\n */\n async upsert(data: Partial<M>, options?: UpsertOptions): Promise<M> {\n if (!snap.createMany) {\n throw new Error(\n \"Upsert is not supported by this collection's data source: it needs a bulk write, \" +\n \"which this driver does not implement. Fall back to create() or update().\"\n );\n }\n const rows = await snap.createMany(\n [data as Partial<EntityValues<M>>],\n { upsert: true, onConflict: options?.onConflict }\n );\n const row = rows[0];\n if (!row) throw new Error(`Upsert into \"${slug}\" returned no row.`);\n return entityToRow(row);\n },\n async update(id: string | number, data: Partial<M> | UpdateValues<Partial<M>>): Promise<M> {\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n async updateMany(updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[]): Promise<M[]> {\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n if (!snap.updateMany) {\n throw new Error(\n \"Bulk updates are not supported by this collection's data source. \" +\n \"Fall back to update() per record.\"\n );\n }\n const rows = await snap.updateMany(\n updates.map(u => ({ id: u.id,\ndata: u.data as Partial<EntityValues<M>> }))\n );\n return rows.map(entityToRow);\n },\n delete(id: string | number): Promise<void> {\n return snap.delete(id);\n },\n async deleteMany(ids: (string | number)[]): Promise<void> {\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n if (!snap.deleteMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to delete() per record.\"\n );\n }\n await snap.deleteMany(ids);\n },\n // The three are non-optional on `SDKCollectionClient`: where the\n // underlying accessor cannot serve one, a stub says so when called\n // rather than being absent. `isUnsupported()` is how an adapter asks\n // the capability question — see `toEntityAccessor` below, which has to.\n count: snap.count\n ? (params?: FindParams<M>) => snap.count!(params)\n : unsupportedMethod(noCount(slug)),\n listen: snap.listen\n ? (params: FindParams<M> | undefined, onUpdate: (r: FindResult<M>) => void, onError?: (e: Error) => void) =>\n snap.listen!(params, (res) => onUpdate({ data: res.data.map(entityToRow), meta: res.meta }), onError)\n : unsupportedMethod(noRealtime(slug)),\n listenById: snap.listenById\n ? (id: string | number, onUpdate: (r: M | undefined) => void, onError?: (e: Error) => void) =>\n snap.listenById!(id, (s) => onUpdate(s ? entityToRow(s) : undefined), onError)\n : unsupportedMethod(noRealtime(slug)),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SdkQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction?: \"asc\" | \"desc\",\n nulls?: NullsPlacement\n ) => new SdkQueryBuilder<M>(client).orderBy(column, direction, nulls),\n limit: (count: number) => new SdkQueryBuilder<M>(client).limit(count),\n offset: (count: number) => new SdkQueryBuilder<M>(client).offset(count),\n search: (searchString: string) => new SdkQueryBuilder<M>(client).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new SdkQueryBuilder<M>(client).vectorSearch(property, vector, options),\n include: (...relations: (string | IncludeSpec)[]) => new SdkQueryBuilder<M>(client).include(...relations),\n fields: (...columns: (FieldPath<M> | string)[]) => new SdkQueryBuilder<M>(client).fields(...columns),\n distinct: (enabled?: boolean) => new SdkQueryBuilder<M>(client).distinct(enabled),\n after: (cursor: string) => new SdkQueryBuilder<M>(client).after(cursor),\n aggregate: snap.aggregate\n ? (params: AggregateParams<M>) => snap.aggregate!(params)\n : unsupportedMethod(noAggregate(slug))\n };\n return client;\n}\n\n/**\n * Wrap a flat {@link SDKCollectionClient} into a Entity-shaped\n * {@link CollectionAccessor}. Every returned row is re-wrapped into the\n * `{ id, path, values }` view-model the admin panel renders.\n */\nfunction toEntityAccessor<M extends Record<string, unknown>>(\n sdk: SDKCollectionClient<M>,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => []\n): CollectionAccessor<M> {\n const accessor: CollectionAccessor<M> = {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n const res = await sdk.find(params);\n return { data: res.data.map((row) => rowToEntity<M>(row, slug, getPks())), meta: res.meta };\n },\n async findById(id: string | number): Promise<Entity<M> | undefined> {\n const row = await sdk.findById(id);\n return row ? rowToEntity<M>(row, slug, getPks()) : undefined;\n },\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n return rowToEntity<M>(await sdk.create(data as Partial<M>, id), slug, getPks());\n },\n // Declared on `CollectionAccessor` and, until now, never implemented on\n // this side of the boundary — so the admin's own import wrote one HTTP\n // request per row and could neither be atomic nor upsert. It forwards to\n // the same `/bulk` route the SDK client uses.\n createMany: sdk.createMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await sdk.createMany!(data as Partial<M>[], options);\n return rows.map((row) => rowToEntity<M>(row, slug, getPks()));\n }\n : undefined,\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await sdk.update(id, data as Partial<M>);\n if (!row) throw new Error(`Update returned no data for id ${id}`);\n return rowToEntity<M>(row, slug, getPks());\n },\n delete(id: string | number): Promise<void> {\n return sdk.delete(id);\n },\n // `CollectionAccessor` keeps these optional, and the optionality is\n // load-bearing: the admin panel picks between subscribing and a\n // one-shot `find()` on exactly this property, and a UI that subscribes\n // into a throw is worse than one that polls. The client's method is\n // always present now, so the capability is read off the stub instead.\n count: isUnsupported(sdk.count) ? undefined : (params?: FindParams<M>) => sdk.count(params),\n aggregate: isUnsupported(sdk.aggregate)\n ? undefined\n : (params: AggregateParams<M>) => sdk.aggregate(params),\n listen: isUnsupported(sdk.listen)\n ? undefined\n : (params: FindParams<M> | undefined, onUpdate: (r: FindResponse<M>) => void, onError?: (e: Error) => void) =>\n sdk.listen(params, (res) => onUpdate({ data: res.data.map((row) => rowToEntity<M>(row, slug, getPks())), meta: res.meta }), onError),\n listenById: isUnsupported(sdk.listenById)\n ? undefined\n : (id: string | number, onUpdate: (s: Entity<M> | undefined) => void, onError?: (e: Error) => void) =>\n sdk.listenById(id, (row) => onUpdate(row ? rowToEntity<M>(row, slug, getPks()) : undefined), onError),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new QueryBuilder<M>(accessor).orderBy(column, direction),\n limit: (count: number) => new QueryBuilder<M>(accessor).limit(count),\n offset: (count: number) => new QueryBuilder<M>(accessor).offset(count),\n search: (searchString: string) => new QueryBuilder<M>(accessor).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new QueryBuilder<M>(accessor).vectorSearch(property, vector, options),\n include: (...relations: string[]) => new QueryBuilder<M>(accessor).include(...relations)\n };\n return accessor;\n}\n\n/**\n * Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.\n *\n * This is the **admin boundary**: the SDK client (`client.data`) returns flat\n * rows, but the admin renders the `Entity` view-model (`entity.values.*`).\n * `core/Rebase.tsx` wraps `client.data` through this before handing it to the\n * admin `RebaseDataContext` — without it the admin renders rows with only their\n * `id`.\n */\n/**\n * Only the by-slug accessor is asked for, so only that is required.\n *\n * Taking a whole `RebaseSdkData` meant taking `RebaseSdkData<unknown>`, whose\n * dynamic branch is an index signature — and no `RebaseSdkData<DB>` satisfies\n * it, because its own `collection` method is not a `SDKCollectionClient`. So a\n * caller holding a *typed* client could not pass it to a function that reads\n * one method off it, and that method is identical on every instantiation.\n */\nexport function wrapAsEntityData(sdkData: Pick<RebaseSdkData, \"collection\">, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug));\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = { collection: getAccessor } as RebaseData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n if (typeof prop === \"symbol\") return undefined;\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n return getAccessor(toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Wrap a Entity-shaped {@link RebaseData} into a flat {@link RebaseSdkData}.\n *\n * Every collection accessor is adapted to return flat rows. Use this to derive\n * the flat SDK data layer (`context.data`) from an existing Entity data layer\n * — e.g. the admin routes its Entity data via `useData()` and exposes the\n * same routing as flat `context.data` for callbacks by wrapping it here.\n */\nexport function wrapAsSdkData(entityData: RebaseData): RebaseSdkData {\n const cache = new Map<string, SDKCollectionClient>();\n\n function getAccessor(slug: string): SDKCollectionClient {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toSdkCollectionClient(entityData.collection(slug), slug);\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = { collection: getAccessor } as RebaseSdkData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n if (typeof prop === \"symbol\") return undefined;\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n return getAccessor(toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Build a flat {@link RebaseSdkData} from a `DataDriver`.\n *\n * This is the developer-facing SDK data layer used by backend framework\n * callbacks & scripts (`context.data` / `rebase.dataAsAdmin`). It returns flat rows —\n * identical in shape to the frontend SDK client, down to how a relation is\n * served: a foreign key stays a foreign key, and a relation named in `include`\n * arrives as the target's own columns. The `{ __type: \"relation\" }` envelope is\n * the admin's view-model and never reaches here.\n *\n * The admin uses {@link buildRebaseData} (Entity) over its own driver.\n */\nexport function buildSdkData(driver: DataDriver): RebaseSdkData {\n return wrapAsSdkData(buildRebaseData(driver));\n}\n","import { RebaseData, RebaseSdkData } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The two data-layer shapes that can be routed: the Entity-shaped admin\n * {@link RebaseData} or the flat SDK {@link RebaseSdkData}. Both expose a\n * `.collection(slug)` accessor, which is all the router needs.\n */\nexport type RoutableData = RebaseData | RebaseSdkData;\n\n/**\n * Parameters for {@link buildRoutedRebaseData}.\n */\nexport interface RoutedRebaseDataParams<T extends RoutableData = RebaseData> {\n /**\n * The default data source. Handles every collection that does not\n * resolve to an entry in `sources` (i.e. server-transport collections,\n * which ride the Rebase client).\n */\n defaultData: T;\n\n /**\n * Per-data-source instances for direct and custom transports, keyed by\n * data-source key (e.g. `\"analytics\"`). Server-mediated sources are not\n * listed here — they fall through to `defaultData`.\n */\n sources: Record<string, T>;\n\n /**\n * Resolve the data-source key for a given collection slug or path.\n * Typically backed by the collection registry + `resolveDataSource`\n * (`resolveDataSource(registry.getCollection(path), defs).key`).\n *\n * Return `undefined` (or a key absent from `sources`) to route to the\n * default data source.\n */\n resolveKey: (slugOrPath: string) => string | undefined;\n}\n\n/**\n * Build a {@link RebaseData} that routes each collection to the right\n * backend based on its resolved data source.\n *\n * `.collection(path)` (and dynamic `data.products`-style access) resolves the\n * collection's data-source key via `resolveKey` and delegates to the matching\n * entry in `sources`, falling back to `defaultData` when there is no match.\n * Because routing keys off the *path being accessed*, a reference widget\n * inside a Firestore form that points at a Postgres collection is still\n * served by Postgres — routing follows the target, not the ancestor.\n *\n * When `sources` is empty this returns `defaultData` untouched, so the\n * single-driver setup keeps identical behaviour and identity (important for\n * effect dependencies that key off the data instance).\n *\n * @example\n * const data = buildRoutedRebaseData({\n * defaultData: client.data,\n * sources: { analytics: buildRebaseData(firestoreDriver) },\n * resolveKey: (path) => resolveDataSource(registry.getCollection(path), defs).key\n * });\n * await data.products.find(); // → default (server / Postgres)\n * await data.events.find(); // → Firestore, if `events.dataSource === \"analytics\"`\n */\nexport function buildRoutedRebaseData<T extends RoutableData = RebaseData>({\n defaultData,\n sources,\n resolveKey\n}: RoutedRebaseDataParams<T>): T {\n\n // Fast path: nothing to route → return the default untouched (preserves\n // referential identity for effect dependencies).\n if (!sources || Object.keys(sources).length === 0) {\n return defaultData;\n }\n\n function resolve(slugOrPath: string): T {\n const key = resolveKey(slugOrPath);\n if (key && sources[key]) return sources[key];\n return defaultData;\n }\n\n function getAccessor(slugOrPath: string) {\n return (resolve(slugOrPath) as RoutableData).collection(slugOrPath);\n }\n\n const target = {\n collection: getAccessor\n } as unknown as T;\n\n return new Proxy(target as object, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n // Ignore Symbol properties (e.g. Symbol.toPrimitive, Symbol.iterator)\n if (typeof prop === \"symbol\") return undefined;\n // Ignore internal JS properties\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n\n // Convert camelCase property names to snake_case slugs, mirroring\n // buildRebaseData so dynamic access routes consistently.\n return getAccessor(toSnakeCase(prop));\n }\n }) as T;\n}\n","/**\n * The `FilterValues` grammar, one level below the wire codec.\n *\n * A field's filter is either one `[op, value]` tuple or an **array** of them —\n * `{ age: [[\">=\", 18], [\"<\", 65]] }` — which is what the fluent builder produces\n * from two `.where()` calls on the same column. Reading that shape is grammar,\n * not a driver detail, so every compiler reads it through here.\n *\n * It lived only inside the Postgres compiler, and the Mongo one destructured\n * `const [op, value] = filterParam` regardless: given the array-of-tuples form\n * `op` bound to `[\">=\", 18]`, no operator matched, and the condition was\n * dropped. Both of them. A read asking for adults under 65 returned every row\n * of the collection with a 200.\n *\n * @module\n */\n\nimport type { WhereFilterOp } from \"@rebasepro/types\";\n\n/** One `[operator, value]` condition. */\nexport type FilterTuple = [WhereFilterOp, unknown];\n\n/**\n * Read one field's filter as the list of conditions it stands for.\n *\n * Accepts both declared shapes and normalises them to a list:\n *\n * ```ts\n * toFilterTuples([\"==\", \"active\"]) // [[\"==\", \"active\"]]\n * toFilterTuples([[\">=\", 18], [\"<\", 65]]) // [[\">=\", 18], [\"<\", 65]]\n * ```\n *\n * A falsy, non-array or empty param has no conditions in it — the empty list,\n * so a caller iterating adds nothing rather than compiling a tuple of\n * `undefined`s and logging about an operator nobody sent.\n */\nexport function toFilterTuples(filterParam: unknown): FilterTuple[] {\n if (!filterParam || !Array.isArray(filterParam) || filterParam.length === 0) return [];\n // The first element discriminates: a condition starts with an operator\n // string, a list of conditions starts with a condition. `[\"in\", [\"a\",\"b\"]]`\n // is one condition whose value happens to be a list.\n if (Array.isArray(filterParam[0])) {\n return filterParam as FilterTuple[];\n }\n return [filterParam as FilterTuple];\n}\n","/**\n * Table Classification\n *\n * Shared constants and pure functions for classifying database tables.\n * Used by both the server-side PostgresBackendDriver and the Studio RLS editor.\n */\n\n/** Possible categories a database table can belong to. */\nexport type TableCategory = \"rebase-internal\" | \"junction\" | \"user\";\n\n/** Schemas that are always considered Rebase-internal. */\nexport const REBASE_INTERNAL_SCHEMAS: readonly string[] = [\"rebase\", \"auth\"];\n\n/** Table-name prefixes that mark a table as Rebase-internal regardless of schema. */\nexport const REBASE_INTERNAL_PREFIXES: readonly string[] = [\n \"_rebase_\",\n \"_auth_\",\n \"drizzle_\",\n];\n\n/**\n * Synchronously classify a table based on naming conventions.\n *\n * @param tableName - The unqualified name of the table.\n * @param schemaName - The schema the table belongs to (e.g. `\"public\"`, `\"rebase\"`).\n * @returns `\"rebase-internal\"` when the table belongs to a reserved schema or\n * carries a reserved prefix; `\"user\"` otherwise.\n *\n * @remarks\n * Junction-table detection requires an async database query and is therefore\n * **not** handled by this function. Use {@link detectJunctionTables} to obtain\n * the set of junction tables, then reclassify as needed.\n */\nexport function classifyTable(\n tableName: string,\n schemaName: string,\n): TableCategory {\n if (\n REBASE_INTERNAL_SCHEMAS.includes(schemaName) ||\n REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))\n ) {\n return \"rebase-internal\";\n }\n\n return \"user\";\n}\n\n/**\n * Convenience predicate that checks whether a table is Rebase-internal.\n *\n * @param tableName - The unqualified name of the table.\n * @param schemaName - The schema the table belongs to.\n * @returns `true` if the table is classified as `\"rebase-internal\"`.\n */\nexport function isRebaseInternalTable(\n tableName: string,\n schemaName: string,\n): boolean {\n return classifyTable(tableName, schemaName) === \"rebase-internal\";\n}\n\n/** SQL query that detects junction tables in the `public` schema. */\nexport const JUNCTION_TABLES_SQL = `\n SELECT t.table_name\n FROM information_schema.tables t\n WHERE t.table_schema = 'public'\n AND t.table_type = 'BASE TABLE'\n AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns c\n WHERE c.table_schema = t.table_schema\n AND c.table_name = t.table_name\n AND c.column_name NOT IN (\n SELECT kcu.column_name\n FROM information_schema.key_column_usage kcu\n JOIN information_schema.table_constraints tc\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY'\n AND kcu.table_schema = t.table_schema\n AND kcu.table_name = t.table_name\n )\n )\n`;\n\n/**\n * Asynchronously detect junction (link) tables in the `public` schema.\n *\n * A junction table is defined as a table where **every** column participates in\n * at least one foreign-key constraint.\n *\n * @param executeSql - A callback that executes a raw SQL string and returns the\n * resulting rows.\n * @returns A `Set` containing the names of all detected junction tables.\n */\nexport async function detectJunctionTables(\n executeSql: (sql: string) => Promise<Record<string, unknown>[]>,\n): Promise<Set<string>> {\n const rows = await executeSql(JUNCTION_TABLES_SQL);\n const junctionTables = new Set<string>();\n\n for (const row of rows) {\n if (typeof row.table_name === \"string\") {\n junctionTables.add(row.table_name);\n }\n }\n\n return junctionTables;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","names":[],"sources":["../src/util/common.ts","../src/util/entities.ts","../src/util/collections.ts","../src/util/identity.ts","../src/util/email.ts","../src/util/enums.ts","../src/util/paths.ts","../src/util/resolve-relation.ts","../src/util/relations.ts","../src/util/resolutions.ts","../src/util/policy/sqlToPolicy.ts","../src/util/policy/securityRuleToConditions.ts","../src/util/policy/policyToPostgres.ts","../src/util/policy/evaluatePolicy.ts","../src/util/permissions.ts","../src/util/builders.ts","../src/util/storage.ts","../src/util/callbacks.ts","../src/util/callback-errors.ts","../src/util/tenant.ts","../src/util/auth-default-policies.ts","../src/util/junction-policies.ts","../src/util/conditions.ts","../src/util/pg-column-to-property.ts","../src/util/string-column-length.ts","../src/util/internal-tables.ts","../src/data/resolveDataSource.ts","../src/collections/CollectionRegistry.ts","../src/collections/default-collections.ts","../src/collections/field-access.ts","../src/data/cursor.ts","../src/data/sort-dialect.ts","../src/data/include-spec.ts","../src/data/query_builder.ts","../src/data/paginate.ts","../src/data/filter-dialect.ts","../src/data/buildRebaseData.ts","../src/data/buildRoutedRebaseData.ts","../src/data/filter-conditions.ts","../src/table-classification.ts"],"sourcesContent":["export const DEFAULT_ONE_OF_TYPE = \"type\"\nexport const DEFAULT_ONE_OF_VALUE = \"value\"\n","import {\n DataType,\n Entity,\n EntityReference,\n EntityRelation,\n EntityStatus,\n EntityValues,\n Properties,\n Property\n} from \"@rebasepro/types\";\nimport { DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE } from \"./common\";\nimport { mergeDeep } from \"@rebasepro/utils\";\n\nexport function isPropertyBuilder(property?: Property) {\n return typeof property?.dynamicProps === \"function\";\n}\n\n/**\n * What a form opens with: a value for every property it can write.\n *\n * `excludeFromApi` columns are left out, and that is the whole of the rule —\n * they are not part of the API surface in either direction, so there is nothing\n * for a form to open showing and nothing it may send back. Including them was\n * not cosmetic: the baseline is what gets submitted, so a new record carried\n * `passwordHash: null` and `emailVerificationToken: null` into the create, and\n * the server refused the whole write with \"these columns are the server's to\n * set\" — the users collection could not be added to from the panel at all. The\n * fields were invisible on screen (`admin.disabled.hidden`), which is what made\n * the error read as being about the roles the operator *had* just edited.\n *\n * Server-side defaulting does not come through here: `applyDefaultValuesOnCreate`\n * asks each property for its own default, so an excluded column with a declared\n * `defaultValue` is still filled in on an in-process write.\n */\nexport function getDefaultValuesFor<M extends Record<string, unknown>>(properties: Properties): Partial<EntityValues<M>> {\n if (!properties) return {};\n return Object.entries(properties)\n .map(([key, property]) => {\n if (!property) return {};\n if ((property as Property).excludeFromApi) return {};\n const value = getDefaultValueFor(property);\n return value === undefined ? {} : { [key]: value };\n })\n .reduce((a, b) => ({ ...a,\n...b }), {}) as EntityValues<M>;\n}\n\nexport function getDefaultValueFor(property?: Property): unknown {\n if (!property) return undefined;\n if (isPropertyBuilder(property)) return undefined;\n // `defaultValue !== undefined`, not truthiness. The test used to be\n // `property.defaultValue || property.defaultValue === null`, which special-\n // cased exactly one falsy value and dropped the rest: `defaultValue: 0`\n // fell through to the per-type default and became `null`, `defaultValue: \"\"`\n // became `null`, and `defaultValue: false` survived only by coincidence\n // (the per-type default for a boolean is also `false`). A default of zero\n // is the most ordinary default a number column has.\n if (property.defaultValue !== undefined) {\n return property.defaultValue;\n } else if (property.type === \"map\" && property.properties) {\n const defaultValuesFor = getDefaultValuesFor(property.properties as Properties);\n if (Object.keys(defaultValuesFor).length === 0) return undefined;\n return defaultValuesFor;\n } else {\n return getDefaultValueFortype(property.type);\n }\n}\n\nexport function getDefaultValueFortype(type: DataType): unknown {\n if (type === \"string\") {\n return null;\n } else if (type === \"number\") {\n return null;\n } else if (type === \"boolean\") {\n return false;\n } else if (type === \"date\") {\n return null;\n } else if (type === \"array\") {\n return [];\n } else if (type === \"map\") {\n return {};\n } else if (type === \"vector\") {\n return null;\n } else if (type === \"binary\") {\n return null;\n } else {\n return null;\n }\n}\n\n/**\n * Update the automatic values in a entity before save\n * @group Driver\n */\nexport function updateDateAutoValues<M extends Record<string, unknown>>({\n inputValues,\n properties,\n status,\n timestampNowValue\n}:\n {\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n status: EntityStatus,\n timestampNowValue: unknown\n }): EntityValues<M> {\n return traverseValuesProperties(\n inputValues,\n properties,\n (inputValue, property) => {\n if (property.type === \"date\") {\n if (status === \"existing\" && property.autoValue === \"on_update\") {\n return timestampNowValue;\n } else if ((status === \"new\" || status === \"copy\") &&\n (property.autoValue === \"on_update\" || property.autoValue === \"on_create\")) {\n return timestampNowValue;\n } else {\n return inputValue;\n }\n } else {\n return inputValue;\n }\n }\n ) ?? {} as M;\n}\n\n/**\n * Stamp the acting user's uid into the `user_on_create` / `user_on_update`\n * columns a collection declares.\n *\n * A deliberate sibling of {@link updateDateAutoValues} rather than another\n * branch inside it. The two share a shape and nothing else: one takes an\n * instant the server generates and the other takes an identity the request\n * carries, so overloading the timestamp function would have meant threading a\n * second, unrelated argument through every one of its callers and letting a\n * `date` property and a `string` property compete for the same `autoValue`\n * union. Called side by side in the driver.\n *\n * The stamped value overwrites whatever arrived in the body. A caller who can\n * set `createdBy` is a caller who can attribute their write to somebody else,\n * which is the one thing an audit column must not allow.\n *\n * `uid` is `undefined` for an anonymous request, a service token or an\n * in-process write; the column is set to an explicit `null` there. Explicit\n * matters on an update: leaving the key absent would keep whatever uid the\n * column already held, so an anonymous edit would be recorded as the previous\n * editor's. Refusing that write outright is `required`'s job, not this\n * function's — see `assertWriteValuesValid`.\n *\n * Top-level properties only, deliberately, unlike {@link updateDateAutoValues}.\n * `traverseValuesProperties` cannot express \"set this key to null\" — a `null`\n * from its operation means \"leave the key out\" — and an audit column nested\n * inside a `map` is not a column at all, so there is nothing down there to\n * stamp.\n *\n * @group Driver\n */\nexport function updateUserAutoValues<M extends Record<string, unknown>>({\n inputValues,\n properties,\n status,\n uid\n}:\n {\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n status: EntityStatus,\n uid: string | undefined\n }): EntityValues<M> {\n const result = { ...(inputValues ?? {}) } as Record<string, unknown>;\n for (const [key, property] of Object.entries(properties ?? {})) {\n const prop = property as (Property & { autoValue?: string }) | undefined;\n if (!prop || prop.type !== \"string\") continue;\n const autoValue = prop.autoValue;\n if (autoValue !== \"user_on_create\" && autoValue !== \"user_on_update\") continue;\n // `user_on_create` says nothing about an update: the column holds the\n // creator's uid and this write is not rewriting it.\n if (status === \"existing\" && autoValue === \"user_on_create\") continue;\n // A copy is a new row and gets a new author, exactly as it gets a new\n // `created_on`.\n result[key] = uid ?? null;\n }\n return result as EntityValues<M>;\n}\n\n/**\n * Fill in the `defaultValue`s a create left unset.\n *\n * `defaultValue` was read by exactly one thing: the Studio's form, which uses it\n * to prefill inputs. Every other way into the same collection — the REST create,\n * the SDK, the socket, an import — stored whatever arrived and nothing where the\n * key was absent. So `active: { type: \"boolean\", defaultValue: true }` produced\n * rows with `active` unset through the API and `true` through the panel, from\n * one declaration that reads like a promise about the data.\n *\n * Only genuinely absent keys are filled. An explicit `null` is a caller saying\n * \"no value\", which is a different statement from not mentioning the field, and\n * overwriting it would make the default impossible to opt out of.\n *\n * `getDefaultValuesFor` also invents a per-type default for properties with no\n * `defaultValue` at all (`false` for a boolean, `[]` for an array, `null` for\n * the rest) — right for a form, which must render *something* in every input,\n * and wrong here, where an absent key must stay absent so the column's own\n * DEFAULT applies. Only declared defaults are taken.\n *\n * @param values the caller's payload\n * @param properties the collection's declared properties\n * @group Driver\n */\nexport function applyDefaultValuesOnCreate<M extends Record<string, unknown>>(\n values: Partial<EntityValues<M>> | undefined,\n properties: Properties\n): Partial<EntityValues<M>> {\n if (!properties) return values ?? {};\n const result = { ...(values ?? {}) } as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(properties)) {\n if (!property) continue;\n const declared = declaresDefault(property as Property);\n if (!declared) continue;\n // Asked of the property, not read out of `getDefaultValuesFor`: that\n // one answers for a *form*, and leaves out the columns the API excludes.\n // A server-owned column with a declared default is still defaulted here.\n const defaultValue = getDefaultValueFor(property as Property);\n if (result[key] !== undefined) {\n // A map whose own sub-properties carry defaults is filled in\n // field by field, so `{ notify: false }` keeps `notify` and still\n // gains the siblings it did not mention.\n if ((property as Property).type === \"map\" &&\n (property as Property & { defaultValue?: unknown }).defaultValue === undefined &&\n isPlainObject(result[key])) {\n result[key] = {\n ...(defaultValue as Record<string, unknown> ?? {}),\n ...(result[key] as Record<string, unknown>)\n };\n }\n continue;\n }\n if (defaultValue !== undefined) result[key] = defaultValue;\n }\n return result as Partial<EntityValues<M>>;\n}\n\n/** Does this property, or something nested under it, state a `defaultValue`? */\nfunction declaresDefault(property: Property): boolean {\n if (isPropertyBuilder(property)) return false;\n if (property.defaultValue !== undefined) return true;\n if (property.type === \"map\" && property.properties) {\n return Object.values(property.properties as Properties)\n .some(child => child && declaresDefault(child as Property));\n }\n return false;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Add missing required fields, expected in the collection, to the values of a entity\n * @param values\n * @param properties\n * @group Driver\n */\nexport function sanitizeData<M extends Record<string, unknown>>\n (\n values: EntityValues<M>,\n properties: Properties\n ) {\n const result = values as Record<string, unknown>;\n Object.entries(properties)\n .forEach(([key, property]) => {\n if (values && values[key] !== undefined) result[key] = values[key];\n else if ((property as Property).validation?.required) result[key] = null;\n });\n return result;\n}\n\nexport function getReferenceFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityReference {\n if (typeof entity.id !== \"string\")\n throw new Error(\"Only string IDs are supported in references\");\n return new EntityReference({\n id: entity.id,\n path: entity.path,\n driver: entity.driver,\n databaseId: entity.databaseId\n });\n}\n\nexport function getRelationFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityRelation {\n return new EntityRelation(entity.id, entity.path, entity as unknown as Record<string, unknown>);\n}\n\n/**\n * Normalize a value into a proper EntityRelation instance.\n * Handles EntityRelation class instances, and plain objects\n * with `__type === \"relation\"` or an `isEntityRelation()` method.\n *\n * When `propertyType` is `\"relation\"`, also accepts plain objects that\n * have `id` and `path` fields — these are relation-shaped objects from\n * edge cases in the data pipeline (REST fallback, stale cache, custom data source).\n *\n * When `targetPath` is given, also accepts a bare id. A relation column is a\n * foreign key, and the REST layer returns it as the scalar it is; only some\n * fetch paths hydrate it into an object. Which form a caller sees therefore\n * depends on how the row was loaded, and a caller that only accepted objects\n * reported half of its own data as a type error. The declared target is the\n * missing half: with it, an id is a relation that has not been fetched yet.\n *\n * Returns null if the value cannot be coerced.\n */\nexport function normalizeToEntityRelation(value: unknown, propertyType?: string, targetPath?: string): EntityRelation | null {\n if (value instanceof EntityRelation) return value;\n\n if (targetPath && (typeof value === \"string\" || typeof value === \"number\")) {\n // An empty string is an unset foreign key, not row \"\".\n if (value === \"\") return null;\n return new EntityRelation(value, targetPath);\n }\n\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n\n const obj = value as Record<string, unknown>;\n const isRelationLike =\n obj.__type === \"relation\" ||\n obj.__type === \"reference\" ||\n (typeof obj.isEntityRelation === \"function\" && (obj.isEntityRelation as () => boolean)()) ||\n (typeof obj.isEntityReference === \"function\" && (obj.isEntityReference as () => boolean)()) ||\n (propertyType === \"relation\" && typeof obj.id !== \"undefined\" && typeof obj.path === \"string\");\n\n if (!isRelationLike) return null;\n\n return new EntityRelation(\n obj.id as string | number,\n obj.path as string,\n obj.data as Record<string, unknown> | undefined\n );\n}\n\nexport function traverseValuesProperties<M extends Record<string, unknown>>(\n inputValues: Partial<EntityValues<M>>,\n properties: Properties,\n operation: (value: unknown, property: Property) => unknown\n): EntityValues<M> | undefined {\n // Handle null/undefined inputValues - use empty object as base for mergeDeep\n const safeInputValues = inputValues ?? {};\n\n const updatedValues = Object.entries(properties)\n .map(([key, property]) => {\n const inputValue = safeInputValues && (safeInputValues)[key];\n const updatedValue = traverseValueProperty(inputValue, property as Property, operation);\n if (updatedValue === null) return null;\n if (updatedValue === undefined) return undefined;\n return ({ [key]: updatedValue });\n })\n .reduce((a, b) => ({ ...a,\n...b }), {}) as EntityValues<M>;\n // Use mergeDeep to preserve class instances like EntityReference, GeoPoint\n const result = mergeDeep(safeInputValues, updatedValues);\n if (!result || Object.keys(result).length === 0) return undefined;\n return result;\n}\n\nexport function traverseValueProperty(inputValue: unknown,\n property: Property,\n operation: (value: unknown, property: Property) => unknown): unknown {\n\n let value;\n if (property.type === \"map\" && property.properties) {\n value = traverseValuesProperties(inputValue as Partial<Record<string, unknown>>, property.properties, operation);\n } else if (property.type === \"array\") {\n const of = property.of;\n if (of && Array.isArray(inputValue) && !Array.isArray(of)) {\n value = inputValue.map((e) => traverseValueProperty(e, of, operation));\n } else if (of && Array.isArray(inputValue) && Array.isArray(of)) {\n value = inputValue.map((e, i) => {\n if (i < of.length)\n return traverseValueProperty(e, of[i], operation);\n return null\n }).filter(Boolean);\n } else if (property.oneOf && Array.isArray(inputValue)) {\n const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;\n const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE;\n value = inputValue.map((e) => {\n if (e === null) return null;\n if (typeof e !== \"object\") return e;\n const rec = e as Record<string, unknown>;\n const type = rec[typeField] as string;\n const childProperty = property.oneOf?.properties[type];\n if (!type || !childProperty) return e;\n return {\n [typeField]: type,\n [valueField]: traverseValueProperty(rec[valueField], childProperty, operation)\n };\n });\n } else {\n value = inputValue;\n }\n } else {\n value = operation(inputValue, property);\n }\n\n return value;\n}\n\n/**\n * Relation reference types used throughout the server layer.\n * These replace the 50+ manual `{ id, path, __type: \"relation\" }` constructions.\n */\nexport interface RelationRef {\n readonly id: string | number;\n readonly path: string;\n readonly __type: \"relation\";\n}\n\nexport interface RelationRefWithData extends RelationRef {\n readonly data: Entity;\n}\n\n/**\n * Create a lightweight relation stub for admin views.\n * Replaces inline `{ id, path, __type: \"relation\" }` object literals.\n */\nexport function createRelationRef(id: string | number, path: string): RelationRef {\n return { id,\npath,\n__type: \"relation\" };\n}\n\n/**\n * Create a hydrated relation reference that includes the full entity data.\n * Used when entity data has been pre-fetched (e.g., via batch loading or JOINs).\n */\nexport function createRelationRefWithData(id: string | number, path: string, data: Entity): RelationRefWithData {\n return { id,\npath,\n__type: \"relation\",\ndata };\n}\n","import {\n CollectionConfig,\n Properties,\n Property\n} from \"@rebasepro/types\";\nimport { isPropertyBuilder } from \"./entities\";\n\nexport function sortProperties<M extends Record<string, unknown>>(properties: Properties, propertiesOrder?: string[]): Properties {\n try {\n const propertiesKeys = Object.keys(properties);\n // If no propertiesOrder, just use the original keys order\n if (!propertiesOrder || propertiesOrder.length === 0) {\n return propertiesKeys\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n }\n\n // Filter propertiesOrder to only include TOP-LEVEL property keys that exist\n // (ignore nested keys like \"data.mode\" - they are for column ordering, not property filtering)\n const validOrderKeys = (propertiesOrder as string[]).filter(key => {\n // Only include top-level keys (no dots) that exist in properties\n return !key.includes(\".\") && properties[key];\n });\n\n // Track which properties we've processed\n const processedKeys = new Set<string>(validOrderKeys);\n\n // Build result starting with ordered properties\n const orderedResult = validOrderKeys\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n\n // Append any properties that were NOT in propertiesOrder (so they don't disappear!)\n const missingProperties = propertiesKeys\n .filter(key => !processedKeys.has(key))\n .map((key) => {\n const property = properties[key] as Property;\n if (!isPropertyBuilder(property) && property?.type === \"map\" && property.properties) {\n return ({\n [key]: {\n ...property,\n properties: sortProperties(property.properties, property.propertiesOrder)\n }\n });\n } else {\n return ({ [key]: property });\n }\n })\n .reduce((a: Properties, b: Properties) => ({ ...a,\n...b }), {}) as Properties;\n\n return { ...orderedResult,\n...missingProperties };\n } catch (e) {\n console.error(\"Error sorting properties\", e);\n return properties;\n }\n}\n\n/**\n * A copy of `collections` ordered by slug.\n *\n * Every generator that turns collections into a file is order-dependent, and\n * every one of them is compared against its own output — `rebase doctor`\n * regenerates in memory and diffs, `generate-sdk && git diff --exit-code` gates\n * CI. While only the *writers* sorted, a project whose `readdirSync` order\n * differed from its slug order was reported permanently out of date, and the\n * fix the message printed rewrote the file in the order it was already in. The\n * generators sort themselves now, so no caller can get this wrong.\n *\n * A slug-less collection is left to the generator's own validation, which names\n * the offending collection; sorting must not throw first.\n */\nexport function sortCollectionsBySlug<C extends { slug?: string }>(collections: readonly C[]): C[] {\n return [...collections].sort((a, b) => (a.slug ?? \"\").localeCompare(b.slug ?? \"\"));\n}\n\nexport function getPrimaryKeys<M extends Record<string, unknown>>(collection: CollectionConfig<M>): Extract<keyof M, string>[] {\n const properties = collection.properties;\n if (!properties) {\n return [\"id\"] as Extract<keyof M, string>[];\n }\n const ids = Object.entries(properties)\n .filter(([key, prop]) => typeof prop === \"object\" && prop !== null && \"isId\" in prop && Boolean(prop.isId))\n .map(([key]) => key);\n\n if (ids.length > 0) {\n return ids as Extract<keyof M, string>[];\n }\n return [\"id\"] as Extract<keyof M, string>[];\n}\n","/**\n * Row identity: the address of a row, and how to derive it.\n *\n * Postgres has no `id`. A row is identified by its primary key — one or more\n * columns, with any names and any types. `id` is something we synthesize on top\n * of that: a single string token, because the admin needs *one* value it can put\n * in a URL (`/products/1:::2`), use as a cache key, and hang a relation ref off.\n *\n * That token is an address, not data. It is derived from the row's columns and\n * never stored in them — a row is exactly its columns, with their real types.\n * Writing the address back into the row is what used to rename primary keys\n * (`sku` → `id`) and restringify them (`42` → `\"42\"`) on the way out.\n *\n * These live in `common` because both sides need them and must agree exactly:\n * the driver parses an incoming address back into key columns, and the admin\n * derives the address from a row it was served.\n */\n\n/**\n * A primary-key column: its name, the type it round-trips as, and whether it is\n * a UUID (which is a string despite sometimes being described as an id \"number\").\n */\nexport interface PrimaryKeyInfo {\n fieldName: string;\n type: \"string\" | \"number\";\n isUUID?: boolean;\n}\n\n/** Separator between the parts of a composite address. */\nexport const COMPOSITE_ID_SEPARATOR = \":::\";\n\n/** The eight-four-four-four-twelve shape of a UUID, any version. */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Whether one address part can be a value of the column it addresses. */\nfunction partIsAddressable(part: string | number, pk: PrimaryKeyInfo): boolean {\n if (pk.isUUID) return UUID_PATTERN.test(String(part));\n if (pk.type === \"number\") {\n return typeof part === \"number\"\n ? Number.isFinite(part)\n : !isNaN(parseInt(String(part), 10));\n }\n return true;\n}\n\n/**\n * Whether an address could name a row at all, before asking the database.\n *\n * A `uuid` column cannot hold `\"new\"`, and an `integer` column cannot hold\n * `\"abc\"` — so the answer to \"which row is this\" is \"none\", and that is a 404,\n * not a failure. Postgres cannot say so politely: the comparison never runs, it\n * raises `22P02` and aborts the enclosing transaction, after which every\n * further statement returns the far less helpful `25P02`.\n *\n * `isUUID` must come from the column, not from `isId: \"uuid\"` in a config: the\n * config is a claim about a key, and a `text` column that holds ids of some\n * other shape is a working app this must not start rejecting.\n */\nexport function isAddressableId(idValue: string | number, primaryKeys: PrimaryKeyInfo[]): boolean {\n if (primaryKeys.length === 0) return false;\n if (primaryKeys.length === 1) return partIsAddressable(idValue, primaryKeys[0]);\n\n const parts = String(idValue).split(COMPOSITE_ID_SEPARATOR);\n if (parts.length !== primaryKeys.length) return false;\n return parts.every((part, i) => partIsAddressable(part, primaryKeys[i]));\n}\n\n/**\n * Derive a row's address from its key columns.\n *\n * Single key → the value as a string. Composite → each part joined by\n * {@link COMPOSITE_ID_SEPARATOR}, in primary-key order, which is what\n * {@link parseIdValues} expects to invert.\n */\nexport function buildCompositeId(values: Record<string, unknown>, primaryKeys: PrimaryKeyInfo[]): string {\n if (primaryKeys.length === 0) {\n return \"\";\n }\n if (primaryKeys.length === 1) {\n return String(values[primaryKeys[0].fieldName] ?? \"\");\n }\n return primaryKeys.map(pk => String(values[pk.fieldName] ?? \"\")).join(COMPOSITE_ID_SEPARATOR);\n}\n\n/**\n * Invert {@link buildCompositeId}: turn an address back into key columns, each\n * coerced to the type its column actually round-trips as.\n *\n * This is the boundary where a URL segment becomes a query parameter, so a\n * malformed address must throw rather than silently produce a query that\n * matches the wrong row (or none).\n */\nexport function parseIdValues(idValue: string | number, primaryKeys: PrimaryKeyInfo[]): Record<string, string | number> {\n const result: Record<string, string | number> = {};\n\n if (primaryKeys.length === 0) {\n return result;\n }\n\n if (primaryKeys.length === 1) {\n const pk = primaryKeys[0];\n if (pk.type === \"number\" && !pk.isUUID) {\n const parsed = typeof idValue === \"number\" ? idValue : parseInt(String(idValue), 10);\n if (isNaN(parsed)) {\n throw new Error(`Invalid numeric ID: ${idValue}`);\n }\n result[pk.fieldName] = parsed;\n } else {\n result[pk.fieldName] = String(idValue);\n }\n return result;\n }\n\n // Composite key\n const parts = String(idValue).split(COMPOSITE_ID_SEPARATOR);\n if (parts.length !== primaryKeys.length) {\n throw new Error(`Composite ID parts mismatch. Expected ${primaryKeys.length}, got ${parts.length} for ID: ${idValue}`);\n }\n\n for (let i = 0; i < primaryKeys.length; i++) {\n const pk = primaryKeys[i];\n const val = parts[i];\n if (pk.type === \"number\" && !pk.isUUID) {\n const parsed = parseInt(val, 10);\n if (isNaN(parsed)) {\n throw new Error(`Invalid numeric ID component: ${val}`);\n }\n result[pk.fieldName] = parsed;\n } else {\n result[pk.fieldName] = val;\n }\n }\n\n return result;\n}\n\n/**\n * The primary keys of a collection, as declared by its properties.\n *\n * This is the only tier both sides can read, because it is the only one written\n * in the config: the postgres driver can also infer keys from the Drizzle\n * schema, which the browser never sees and is never sent — the admin compiles\n * the collection files into its own bundle rather than being served them. A key\n * that lives only in the Drizzle schema is therefore invisible here, and the\n * server says so at boot (`warnOnKeysTheAdminCannotResolve`) naming the `isId`\n * to add.\n *\n * Returns an empty array when a collection declares none, which callers must\n * treat as \"not addressable\" rather than defaulting to `id`: guessing a key\n * that is not the real one produces confidently wrong addresses.\n */\nexport function getDeclaredPrimaryKeys(collection: {\n properties?: Record<string, unknown>;\n}): PrimaryKeyInfo[] {\n const properties = collection.properties;\n if (!properties) return [];\n\n const keys: PrimaryKeyInfo[] = [];\n for (const [fieldName, propRaw] of Object.entries(properties)) {\n const prop = propRaw as { type?: string; isId?: unknown } | undefined;\n if (!prop || typeof prop !== \"object\") continue;\n if (!(\"isId\" in prop) || !prop.isId) continue;\n keys.push({\n fieldName,\n type: prop.type === \"number\" ? \"number\" : \"string\",\n isUUID: prop.isId === \"uuid\"\n });\n }\n return keys;\n}\n\n/**\n * The keys to address a collection's rows with, resolved the way the driver\n * resolves them — minus the tier the browser cannot reach.\n *\n * The postgres driver tries, in order: properties marked `isId`; the primary\n * keys of the Drizzle schema; and finally a column literally named `id`. Only\n * the first and last are visible in a `CollectionConfig`, which is what both\n * sides share.\n *\n * So the two agree except on a collection that declares no `isId` and whose key\n * is known only to Drizzle. There, the driver reads the real key, and this\n * either resolves nothing (reported to the console by the caller) or — if the\n * table happens to have an unrelated `id` property — resolves `id`, which is\n * the wrong key and cannot be detected from here: the addresses look right and\n * route wrong. Only the config can settle it, so the server names both cases\n * at boot (`warnOnKeysTheAdminCannotResolve`) with the `isId` to add.\n */\nexport function resolvePrimaryKeys(collection: {\n properties?: Record<string, unknown>;\n}): PrimaryKeyInfo[] {\n const declared = getDeclaredPrimaryKeys(collection);\n if (declared.length > 0) return declared;\n\n const idProp = collection.properties?.id as { type?: string } | undefined;\n if (idProp && typeof idProp === \"object\") {\n return [{ fieldName: \"id\",\ntype: idProp.type === \"number\" ? \"number\" : \"string\" }];\n }\n\n return [];\n}\n","/**\n * Email normalization — one implementation, because the database enforces it.\n *\n * `ensureAuthTablesExist` puts a `UNIQUE INDEX ON users (lower(email))` on the\n * auth table. That index decides what \"the same address\" means, and it does not\n * trim: to Postgres, `' foo@bar.com'` and `'foo@bar.com'` are two addresses and\n * both may exist. So every write that reaches the column has to agree with\n * every read, exactly, or the two disagree in the one direction that matters —\n * a row that exists and cannot be found.\n *\n * That is not hypothetical. The lookup path trimmed and the admin create paths\n * did not, so a user created through `POST /api/data/users` or\n * `POST /api/auth/admin/users` with a stray space was stored untrimmed,\n * survived the unique index alongside the real address, and was unreachable by\n * login forever after. The HTTP auth routes were unaffected only because Zod's\n * `.email()` happens to reject surrounding whitespace — a guard on a different\n * layer, for a different reason, that the admin paths do not sit behind.\n *\n * It lives in `common` because `server`, `server-postgres` and `server-mongo`\n * all write this column and must agree exactly, and `common` is the only\n * package all three already depend on.\n */\n\n/**\n * Canonical form of an email address: trimmed, lower-cased.\n *\n * Non-strings pass through untouched, so this is safe to apply to a value out\n * of a partial update payload whose type is not known yet.\n */\nexport function normalizeEmail<T>(email: T): T | string {\n return typeof email === \"string\" ? email.trim().toLowerCase() : email;\n}\n","import { EnumValueConfig, EnumValues } from \"@rebasepro/types\";\n\nexport function enumToObjectEntries(enumValues: EnumValues): EnumValueConfig[] {\n if (Array.isArray(enumValues)) {\n return enumValues;\n } else {\n return Object.entries(enumValues).map(([id, value]) => {\n if (typeof value === \"string\") {\n return {\n id,\n label: value\n }\n } else {\n return {\n ...value,\n id\n }\n }\n });\n }\n}\n\nexport function getLabelOrConfigFrom(enumValues: EnumValueConfig[], key?: string | number): EnumValueConfig | undefined {\n if (key === null || key === undefined) return undefined;\n return enumValues.find((entry) => String(entry.id) === String(key));\n}\n","export const COLLECTION_PATH_SEPARATOR = \"::\";\n\n/**\n * Remove the entity ids from a given path\n * `products/B44RG6APH/locales` => `products::locales`\n * @param path\n */\nexport function stripCollectionPath(path: string): string {\n return segmentsToStrippedPath(fullPathToCollectionSegments(path));\n}\n\nexport function segmentsToStrippedPath(paths: string[]) {\n if (paths.length === 1)\n return paths[0];\n return paths.reduce((a, b) => `${a}${COLLECTION_PATH_SEPARATOR}${b}`);\n}\n\n/**\n * Extract the collection path routes\n * `products/B44RG6APH/locales` => [`products`, `locales`]\n * @param path\n */\nexport function fullPathToCollectionSegments(path: string): string[] {\n return path\n .split(\"/\")\n .filter((e, i) => i % 2 === 0);\n}\n","import {\n CollectionConfig,\n Relation,\n ResolvedRelation\n} from \"@rebasepro/types\";\nimport { generateForeignKeyName, toSnakeCase } from \"@rebasepro/utils\";\n\nimport { getTableName } from \"./relations\";\n\n/**\n * Fill in a relation's defaults.\n *\n * This replaces `sanitizeRelation`, which had to work out *which kind of link\n * you meant* from whichever optional fields happened to be set — 194 lines of\n * it, including a pass that inspected the target collection's own relations to\n * decide whether a `many`/`inverse` pair was a one-to-many or the far side of a\n * many-to-many, wrapped in a `try/catch` that fell through to the wrong answer\n * when it could not tell. Two consumers running that logic at different moments\n * could reach different conclusions about the same relation.\n *\n * With the kind declared there is nothing to work out. What remains is\n * defaulting — a table name, a column name — which is deterministic, depends\n * only on the relation and its two endpoints, and cannot fail. That is why this\n * function returns rather than throws, and why it needs no cache to be\n * consistent.\n */\nexport function resolveRelation(\n relation: Relation,\n sourceCollection: CollectionConfig,\n propertyKey?: string\n): ResolvedRelation {\n const target = relation.target;\n if (typeof target !== \"function\") {\n throw new Error(\n `Relation${relation.relationName ? ` '${relation.relationName}'` : \"\"} on ` +\n `'${sourceCollection.slug}' has no \\`target\\`. Give it a thunk: \\`target: () => otherCollection\\`.`\n );\n }\n\n const targetCollection = callTarget(relation, sourceCollection, propertyKey, target);\n\n // The name is the address: the `include` key, the admin tab, and the\n // segment of a nested path. Declared name wins, then the declaring\n // property's key, then the target's slug.\n const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);\n\n const shared: Pick<ResolvedRelation, \"relationName\" | \"target\" | \"targetSlug\" | \"onUpdate\" | \"onDelete\" | \"overrides\"> = {\n relationName,\n // Normalised, not the thunk as written. Resolution reads the target once\n // and every later consumer calls it again — the driver building a join,\n // the DDL and policy generators, the admin's relation fields — so\n // handing back the raw thunk would give all of them the module namespace\n // `callTarget` just looked past, and the fix would hold only for the\n // fields resolution happens to read here. Still lazy: same call at the\n // same moment, one unwrap on the way out.\n target: () => unwrapModuleNamespace(target()) as CollectionConfig,\n targetSlug: targetCollection.slug,\n onUpdate: relation.onUpdate,\n onDelete: relation.onDelete,\n overrides: relation.overrides\n // No `validation`. Whether the link is required is a fact about the\n // *property*, and copying it onto the resolved relation gave the\n // question two answers that were free to disagree. Ask\n // `isRelationRequired(collection, relation)`, which reads the one.\n };\n\n const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);\n\n switch (relation.kind) {\n case \"belongsTo\":\n return {\n ...shared,\n kind: \"belongsTo\",\n cardinality: \"one\",\n writable: true,\n shared: false,\n localKey: relation.localKey ?? generateForeignKeyName(relationName)\n };\n\n case \"hasOne\":\n return {\n ...shared,\n kind: \"hasOne\",\n cardinality: \"one\",\n writable: true,\n shared: false,\n foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),\n sourceKey: relation.sourceKey\n };\n\n case \"hasMany\":\n return {\n ...shared,\n kind: \"hasMany\",\n cardinality: \"many\",\n writable: true,\n shared: false,\n foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),\n // Not defaulted: the source's primary key needs the driver's\n // schema to resolve, which resolution does not have. `undefined`\n // means \"the primary key\" — see `ResolvedHasMany.sourceKey`.\n sourceKey: relation.sourceKey\n };\n\n case \"manyToMany\": {\n const sourceTable = getTableName(sourceCollection);\n const targetTable = getTableName(targetCollection);\n return {\n ...shared,\n kind: \"manyToMany\",\n cardinality: \"many\",\n writable: true,\n shared: true,\n through: {\n // Sorted so both sides of the same link derive the same\n // table without having to agree in advance.\n table: relation.through?.table ?? [sourceTable, targetTable].sort().join(\"_\"),\n sourceColumn: relation.through?.sourceColumn ?? generateForeignKeyName(sourceName),\n targetColumn: relation.through?.targetColumn ?? generateForeignKeyName(relationName),\n // `{}` rather than `undefined`, for the reason every other\n // field here is filled in: a consumer reads one shape and\n // does not have to decide what an absent payload means.\n properties: relation.through?.properties ?? {}\n }\n };\n }\n\n case \"via\":\n return {\n ...shared,\n kind: \"via\",\n cardinality: relation.cardinality,\n writable: false,\n // A join chain reaches rows that other parents reach too, and\n // Rebase does not know which hop, if any, is a link it owns.\n shared: true,\n joinPath: relation.joinPath\n };\n\n default: {\n // Exhaustive: a new kind is a compile error here, not a silent\n // fall-through to whatever shape happened to match first.\n const exhaustive: never = relation;\n throw new Error(`Unknown relation kind: ${JSON.stringify(exhaustive)}`);\n }\n }\n}\n\n/** How this relation is addressed in an error message, before it has a resolved name. */\nfunction describe(relation: Relation, sourceCollection: CollectionConfig, propertyKey?: string): string {\n const name = relation.relationName ?? propertyKey;\n return `Relation${name ? ` '${name}'` : \"\"} on '${sourceCollection.slug}'`;\n}\n\n/**\n * A module namespace, unwrapped to the collection it exports.\n *\n * A cycle transpiled to CommonJS does not hand the importing module the\n * *default export* — it hands it the module object, `{ __esModule: true,\n * default: … }`, captured before the exporting module finished evaluating. The\n * `default` slot fills in later, so by the time a lazy `target` thunk runs the\n * collection is sitting right there, one level down. Returning the namespace is\n * never a thing a thunk means to do, and there is exactly one reading of it.\n *\n * Only unwrapped when the inner value is itself a collection: a `default` that\n * is not one is a genuinely wrong thunk, and it should reach the error below\n * rather than be quietly swapped in.\n */\nfunction unwrapModuleNamespace(value: unknown): unknown {\n if (!value || typeof value !== \"object\") return value;\n if ((value as { slug?: unknown }).slug) return value;\n const inner = (value as { default?: unknown }).default;\n return inner && typeof inner === \"object\" && (inner as { slug?: unknown }).slug ? inner : value;\n}\n\n/**\n * Call the `target` thunk, and translate the ways an import cycle breaks it into\n * an error that names the cause — or, where the value is recoverable, into the\n * collection the thunk meant.\n *\n * The thunk exists to defer the reference until every module has finished\n * evaluating, and for a cycle that closes at import time it does. Two cycles\n * leave the binding permanently unusable:\n *\n * - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are\n * in the temporal dead zone, so reading one throws `ReferenceError: x is not\n * defined`. The stack points at the thunk — a one-line arrow function that is\n * obviously fine — and says nothing about the cycle that made it throw.\n * - **CJS interop, unresolved.** The half-initialised module object has no\n * `default` yet, the import resolves to `undefined`, and the thunk returns it\n * without complaint. That one used to surface here as \"did not resolve to a\n * collection\", which is true and unhelpful.\n *\n * Both mean the same thing, and the fix for both is the same: break the cycle,\n * or move the relation into the collection that does not close it.\n *\n * A third shape is *not* an error, and used to be reported as one. A loader that\n * transpiles ESM to CJS — jiti, which is what `rebase generate-sdk` and\n * `rebase build` load collections with — gives the module entered second in a\n * cycle a namespace object rather than the default export, and never replaces it\n * with a live binding. The thunk then returns `{ __esModule: true, default: … }`\n * holding the fully-initialised collection. Native ESM resolves the same thunk\n * to the collection directly, so this was a loader artefact reported as an\n * authoring mistake, and the advice it gave — make the target a lazy thunk — was\n * already satisfied by the code it was rejecting. Bidirectional relations make\n * these cycles unavoidable, and the lazy thunk is this framework's own answer to\n * them, so {@link unwrapModuleNamespace} takes the collection and moves on.\n */\nfunction callTarget(\n relation: Relation,\n sourceCollection: CollectionConfig,\n propertyKey: string | undefined,\n target: Relation[\"target\"]\n): ReturnType<Relation[\"target\"]> {\n let targetCollection: ReturnType<Relation[\"target\"]> | undefined;\n try {\n targetCollection = unwrapModuleNamespace(target()) as ReturnType<Relation[\"target\"]>;\n } catch (error) {\n // A ReferenceError from inside the thunk is a binding that was never\n // initialised — nothing else in a one-expression arrow can raise one.\n if (error instanceof ReferenceError) {\n throw new Error(\n `${describe(relation, sourceCollection, propertyKey)} targets a collection that is not ` +\n `initialized yet — almost always an import cycle between the two collection files. ` +\n `Break the cycle (move the shared piece into a third module, or import the target ` +\n `lazily) so the target's module finishes evaluating before the registry is built.`,\n { cause: error }\n );\n }\n throw error;\n }\n\n if (!targetCollection?.slug) {\n throw new Error(\n `${describe(relation, sourceCollection, propertyKey)} has a \\`target\\` that resolved to ` +\n `${targetCollection === undefined ? \"`undefined`\" : \"something that is not a collection\"}. ` +\n (targetCollection === undefined\n ? \"Under CommonJS interop an import cycle resolves the default import to `undefined`, \" +\n \"so check whether this collection and its target import each other. Otherwise the thunk \" +\n \"is returning the wrong value — it must return the collection itself, not a promise or a module.\"\n : typeof (targetCollection as { then?: unknown }).then === \"function\"\n ? \"The thunk returned a promise — `target: () => import(\\\"./other\\\")` is asynchronous. \" +\n \"Import the collection at the top of the file and return the binding: \" +\n \"`target: () => otherCollection`.\"\n : \"The thunk must return a collection config with a `slug`.\")\n );\n }\n\n return targetCollection;\n}\n","import { CollectionConfig, isRelationalCollectionConfig, Property, ResolvedRelation, RelationProperty } from \"@rebasepro/types\";\nimport { toSnakeCase, toWireKey } from \"@rebasepro/utils\";\n\nimport { resolveRelation } from \"./resolve-relation\";\n\n/**\n * Whether the target rows are shared with other parents — a many-to-many, or a\n * multi-hop `via` chain.\n *\n * Decides what a write \"through\" the relation may touch: a shared target\n * belongs to every parent that links it, so the parent owns the *link* and not\n * the row. The backend enforces that (an unlink rather than a delete) and the\n * admin renders it (remove-from-parent rather than delete).\n *\n * Now a field on the resolved relation rather than a re-derivation, so both\n * sides read the same answer instead of each computing one.\n */\nexport function isJunctionBackedRelation(relation: ResolvedRelation): boolean {\n return relation.shared;\n}\n\n/** WeakMap cache — same collection instance always yields the same relation map. */\nconst _resolvedRelationsCache = new WeakMap<CollectionConfig, Record<string, ResolvedRelation>>();\n\n/**\n * Every relation a collection declares, keyed by the name it is addressed by.\n *\n * A relation reaches the map from either of two places — the collection's\n * `relations` array, or a `relation` property that declares one inline — and is\n * keyed by its resolved `relationName`, which is what a nested path segment,\n * an `include` key and an admin tab all match against.\n *\n * Resolution no longer swallows failures. It used to wrap each relation in a\n * `try/catch` that dropped anything it could not work out, so a\n * mis-declared relation silently vanished instead of being reported; with the\n * kind declared, the only remaining failure is a `target` that does not resolve,\n * which is worth hearing about.\n */\nexport function resolveCollectionRelations(\n collection: CollectionConfig\n): Record<string, ResolvedRelation> {\n const cached = _resolvedRelationsCache.get(collection);\n if (cached) return cached;\n\n if (!isRelationalCollectionConfig(collection)) return {};\n\n const relations: Record<string, ResolvedRelation> = {};\n\n for (const relation of collection.relations ?? []) {\n const resolved = resolveRelation(relation, collection);\n relations[resolved.relationName] = resolved;\n }\n\n // A property declaring a relation inline is registered under the property\n // key as well: the fetch layer hydrates the result back onto that key, and\n // it is the name the admin addresses the field by.\n for (const [propertyKey, property] of Object.entries(collection.properties ?? {})) {\n if ((property as Property)?.type !== \"relation\") continue;\n const declared = (property as RelationProperty).relation;\n if (!declared || relations[propertyKey]) continue;\n\n relations[propertyKey] = resolveRelation(declared, collection, propertyKey);\n }\n\n _resolvedRelationsCache.set(collection, relations);\n return relations;\n}\n\n/**\n * The `type: \"relation\"` property that declares a link, or `undefined` for one\n * that only exists in the collection's `relations` array.\n *\n * Both declaration sites end up in {@link resolveCollectionRelations}, and only\n * one of them has a property to carry field-level facts — `name`, `admin`, and\n * the one this exists for, `validation.required`.\n */\nexport function relationDeclaringProperty(\n collection: CollectionConfig,\n relation: ResolvedRelation\n): RelationProperty | undefined {\n const resolved = resolveCollectionRelations(collection);\n for (const [key, raw] of Object.entries(collection.properties ?? {})) {\n const prop = raw as Property | undefined;\n if (prop?.type !== \"relation\") continue;\n // A relation declared inline is keyed by the property; one declared in\n // `relations` is keyed by its name, which the property addresses.\n if (resolved[key] === relation) return prop as RelationProperty;\n const addressed = (prop as RelationProperty).relation?.relationName;\n if (addressed && findRelation(resolved, addressed) === relation) return prop as RelationProperty;\n }\n return undefined;\n}\n\n/**\n * Must every row of this collection point at a target through this link?\n *\n * Read from the declaring property's `validation.required` — the same key every\n * other field uses, and the only place it lives.\n *\n * `RelationBase` carried its own `validation.required` until 0.18, which made\n * this two questions rather than one. They were answered by different readers:\n * the Postgres DDL generator asked the property (so the foreign-key column was\n * `NOT NULL`) and the SDK type generator asked the relation (so the generated\n * `Insert` type made the field optional). A `create()` that left the relation\n * out therefore typechecked and then failed at the database with a not-null\n * violation, and the two `required`s had to be written twice, identically, for\n * the pair to agree.\n *\n * A relation with no declaring property — an entry in `relations` nothing\n * points at — is not required. There is no field to fill in.\n */\nexport function isRelationRequired(collection: CollectionConfig, relation: ResolvedRelation): boolean {\n return Boolean(relationDeclaringProperty(collection, relation)?.validation?.required);\n}\n\n/**\n * The path of the collection a relation property points at, derived from the\n * property alone.\n *\n * A preview holds a property and a value and no collection, so it cannot call\n * `resolveRelationProperty`. It does not need to: both forms that carry a\n * target — the stamped `resolvedRelation` and the inline `relation` — name it\n * directly. Only the third form, a relation declared by name in the\n * collection's `relations` array, is out of reach, and that one has no target\n * to read without the collection anyway.\n *\n * This is what lets a preview render a relation column that arrived as a bare\n * foreign key: the id says *which* row, the declared target says *which\n * collection*, and `RelationPreview` fetches the rest. Without it a scalar id\n * is indistinguishable from a value of the wrong type.\n */\nexport function getRelationTargetPath(property: RelationProperty): string | undefined {\n const stamped = property.resolvedRelation?.targetSlug;\n if (stamped) return stamped;\n\n const target = property.relation?.target;\n if (typeof target !== \"function\") return undefined;\n try {\n return target()?.slug;\n } catch (_e) {\n // A thunk reaching into a module that has not finished initialising:\n // there is no target to name yet, and a preview is not worth throwing over.\n return undefined;\n }\n}\n\n/**\n * The table a collection reads and writes.\n *\n * `table` when it is set, otherwise `toSnakeCase(slug)` — which is what made it\n * safe to drop `table` from the required fields on the config type: the runtime\n * had always derived it, and the type was demanding a value it did not need.\n *\n * The `||` chain is load-bearing. `toSnakeCase(undefined)` returns `\"\"`, not\n * `undefined`, so the previous `??` chain short-circuited on the empty string\n * and the name fallback could never run — a safety net that read like one and\n * caught nothing. It was unreachable while `slug` was required; it stops being\n * unreachable the moment anything constructs a config without one.\n */\nexport function getTableName(collection: CollectionConfig): string {\n const declared = isRelationalCollectionConfig(collection) ? collection.table : undefined;\n return declared || toSnakeCase(collection.slug) || toSnakeCase(collection.name);\n}\n\n/**\n * A JavaScript identifier: what a generated `export const <name> =` needs.\n *\n * Deliberately the same shape the two schema generators already define\n * privately — this is the third place that needed it, and the first two guard\n * property keys and member accesses while nothing guarded the variable name\n * itself.\n */\nconst JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * The variable name a generated table is bound to.\n *\n * Camel-cases underscores, and then guarantees the result is a legal\n * identifier. It did only the first, so a table name that is legal in Postgres\n * and not in JavaScript produced a `schema.generated.ts` that does not parse:\n *\n * `2024_archive` → `export const 2024Archive = pgTable(…)`\n * \"An identifier or keyword cannot immediately follow\n * a numeric literal\"\n * `reporting.events` → `export const reporting.events = pgTable(…)`\n * \"',' expected\"\n *\n * That file is imported by the server, so the failure is not one broken\n * collection — `rebase build` and `db push` fail at tsc for the whole\n * directory. And it is reachable from a documented flow: `rebase init` against\n * a database holding a table called `2024_archive` writes a collection file\n * that parses and a schema file that does not.\n *\n * **A no-op for every name that already worked**, which is what makes changing\n * a derived name safe here: the only inputs whose output changes are the ones\n * that produced a syntax error, and nothing can be running against those.\n * Separators become camel case rather than disappearing, so `reporting.events`\n * and `reporting_events` do not collide into one variable.\n */\nexport function getTableVarName(tableName: string): string {\n const camel = tableName.replace(/_([a-z])/g, (_, char: string) => char.toUpperCase());\n if (JS_IDENTIFIER.test(camel)) return camel;\n\n const sanitised = camel\n // Any other separator gets the same treatment `_` did, so two tables\n // differing only by separator keep differing.\n .replace(/[^A-Za-z0-9_$]+([A-Za-z0-9])?/g, (_, char?: string) =>\n (char ? char.toUpperCase() : \"\"))\n // A leading digit is legal in Postgres and not in JavaScript. Prefixed\n // rather than stripped, so `2024_archive` and `archive` stay distinct.\n .replace(/^([0-9])/, \"t$1\");\n\n return JS_IDENTIFIER.test(sanitised) ? sanitised : `t${sanitised}`;\n}\n\nexport function getEnumVarName(tableName: string, propName: string): string {\n const tableVar = getTableVarName(tableName);\n const propVar = propName.charAt(0).toUpperCase() + propName.slice(1);\n return `${tableVar}${propVar}`;\n}\n\nexport function getColumnName(fullColumn: string): string {\n return fullColumn.includes(\".\") ? fullColumn.split(\".\").pop()! : fullColumn;\n}\n\n/**\n * The field key a database column is served and addressed under.\n *\n * A column has two names and they are not the same name. `author_id` is what\n * Postgres stores; `authorId` is the key on the JSON row, the key in the\n * generated Drizzle table, and the key a caller writes in `where` and\n * `orderBy`. Every place that starts from a column and has to reach a row, a\n * Drizzle table or a payload goes through here, so there is one answer rather\n * than one per call site — the two that disagreed put `displayName` and\n * `author_id` on the same API.\n *\n * A declared property is the authority when there is one, because its key *is*\n * the wire name and `columnName` is the only thing that ever renamed the\n * column:\n *\n * 1. an explicit `columnName` equal to this column;\n * 2. a property whose key is literally the column (an author who wrote\n * `author_id:` meant `author_id` on the wire, and gets it);\n * 3. a property whose key snake-cases to the column, which is the default\n * mapping — `authorId` → `author_id`.\n *\n * With no property in the way — a foreign key derived from a relation, which\n * usually has none — the name is derived: {@link toWireKey}.\n *\n * Note the fallback is *not* the column verbatim. That was the old behaviour\n * and it is precisely the defect: a derived foreign key reached the wire under\n * its column name while every hand-authored field beside it was camelCase.\n */\nexport function fieldKeyForColumn(collection: CollectionConfig | undefined, column: string): string {\n const properties = collection?.properties;\n if (properties) {\n for (const [key, prop] of Object.entries(properties)) {\n const columnName = (prop as { columnName?: unknown } | undefined)?.columnName;\n if (typeof columnName === \"string\" && columnName === column) return key;\n }\n for (const key of Object.keys(properties)) {\n if (key === column) return key;\n if (toSnakeCase(key) === column) return key;\n }\n }\n return toWireKey(column);\n}\n\n/**\n * Look up a relation by key with forgiving normalization.\n *\n * `resolveCollectionRelations` stores each relation under a single canonical\n * key (no aliases). This helper tries the given key as-is, then falls back to\n * slug form (underscores → hyphens) and snake_case form (hyphens → underscores)\n * so that callers that receive a key from external input (URL path segments,\n * user-provided config, etc.) can still find the right entry.\n */\nexport function findRelation(\n resolvedRelations: Record<string, ResolvedRelation>,\n key: string\n): ResolvedRelation | undefined {\n // Exact match first\n if (resolvedRelations[key]) return resolvedRelations[key];\n\n // Try slug form (e.g. \"company_id\" → \"company-id\")\n const slugKey = key.replace(/_/g, \"-\");\n if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];\n\n // Try snake_case form (e.g. \"company-id\" → \"company_id\")\n const snakeKey = key.replace(/-/g, \"_\");\n if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];\n\n return undefined;\n}\n","import {\n ArrayProperty,\n AuthState,\n CollectionConfig,\n EnumValueConfig,\n EnumValues,\n NumberProperty,\n Properties,\n Property,\n RelationProperty,\n ResolvedRelation,\n StringProperty,\n getDataSourceCapabilities,\n getDeclaredSubcollections,\n type EntityChildView\n} from \"@rebasepro/types\";\n\ntype PropertyConfig = { property: unknown; [key: string]: unknown };\nimport { isPropertyBuilder } from \"./entities\";\nimport { enumToObjectEntries } from \"./enums\";\nimport { DEFAULT_ONE_OF_TYPE } from \"./common\";\nimport { isDefaultFieldConfigId } from \"@rebasepro/utils\";\nimport { getIn, mergeDeep } from \"@rebasepro/utils\";\nimport { isJunctionBackedRelation, resolveCollectionRelations } from \"./relations\";\nimport { resolveRelation } from \"./resolve-relation\";\n\n/**\n * Resolve property builders, enums and arrays.\n */\n\nexport type ResolvePropertyProps<M extends Record<string, unknown> = Record<string, unknown>> = {\n property: Property\n propertyKey?: string,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}\n\nexport function resolveProperty<M extends Record<string, unknown> = Record<string, unknown>>(props: ResolvePropertyProps<M>): Property | null {\n\n const {\n property,\n ignoreMissingFields = false,\n ...rest\n } = props;\n\n let resultProperty: Property;\n\n if (isPropertyBuilder(property)) {\n const path = rest.path;\n if (!path) {\n // When path is not available (e.g. in preview contexts), skip dynamic\n // resolution and use the property as-is without dynamic modifications.\n resultProperty = property as Property;\n } else {\n const usedPropertyValue = rest.propertyKey ? getIn(rest.values, rest.propertyKey) : undefined;\n const dynamicProps = property.dynamicProps?.({\n ...rest,\n path,\n propertyValue: usedPropertyValue,\n values: rest.values ?? {},\n previousValues: rest.previousValues ?? rest.values ?? {}\n });\n resultProperty = mergeDeep(property, dynamicProps ?? {});\n }\n } else {\n resultProperty = property as Property;\n }\n\n // Apply dynamic properties if they exist\n if (resultProperty?.dynamicProps && rest.path) {\n const path = rest.path;\n const usedPropertyValue = rest.propertyKey ? getIn(rest.values, rest.propertyKey) : undefined;\n const dynamicPropsResult = resultProperty.dynamicProps({\n ...rest,\n path,\n propertyValue: usedPropertyValue,\n values: rest.values ?? {},\n previousValues: rest.previousValues ?? rest.values ?? {}\n });\n\n if (dynamicPropsResult) {\n resultProperty = mergeDeep(resultProperty, dynamicPropsResult);\n }\n }\n\n let resolvedProperty: Property | null;\n\n if (resultProperty?.type === \"map\" && resultProperty.properties) {\n const properties = resolveProperties({\n ignoreMissingFields,\n ...rest,\n properties: resultProperty.properties\n });\n resolvedProperty = {\n ...resultProperty,\n properties\n } as Property;\n } else if (resultProperty?.type === \"array\") {\n resolvedProperty = resultProperty;\n } else if ((resultProperty?.type === \"string\" || resultProperty?.type === \"number\") && resultProperty.enum) {\n resolvedProperty = resolvePropertyEnum(resultProperty);\n } else {\n resolvedProperty = resultProperty;\n }\n\n if (resolvedProperty?.propertyConfig && !isDefaultFieldConfigId(resolvedProperty.propertyConfig)) {\n const cmsFields = rest.propertyConfigs;\n if (!cmsFields && !ignoreMissingFields) {\n throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`);\n }\n const customField: PropertyConfig | undefined = cmsFields?.[resolvedProperty.propertyConfig];\n if (!customField) {\n console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`)\n return resolvedProperty;\n }\n if (customField.property) {\n const restConfigProperty = { ...customField.property } as Record<string, unknown>;\n delete restConfigProperty.propertyConfig;\n const customFieldProperty = resolveProperty({\n property: { name: \"\",\n...restConfigProperty } as Property,\n ignoreMissingFields,\n ...rest\n });\n if (customFieldProperty) {\n resolvedProperty = mergeDeep(customFieldProperty, resolvedProperty);\n }\n }\n\n }\n\n return resolvedProperty;\n}\n\n/**\n * The resolved relation a relation property refers to.\n *\n * Normalization stamps `resolvedRelation` onto the property, so this is usually\n * a field read. It falls back to resolving from the collection for properties\n * that never went through the registry — a preview, or a form rendered straight\n * from an authored config.\n */\nexport function resolveRelationProperty(\n property: RelationProperty,\n collection: CollectionConfig,\n propertyKey?: string\n): ResolvedRelation {\n if (property.resolvedRelation) return property.resolvedRelation;\n\n if (property.relation) {\n return resolveRelation(property.relation, collection, propertyKey);\n }\n\n const name = propertyKey ?? \"\";\n const declared = resolveCollectionRelations(collection)[name];\n if (!declared) {\n throw Error(\n `Relation property '${name || \"(unnamed)\"}' on '${collection.slug}' declares no \\`relation\\`, ` +\n \"and the collection has no relation of that name.\"\n );\n }\n return declared;\n}\n\n/**\n * Resolve enum aliases for a string or number property\n * @param property\n */\nexport function resolvePropertyEnum(property: StringProperty | NumberProperty): StringProperty | NumberProperty {\n if (typeof property.enum === \"object\") {\n return {\n ...property,\n enum: enumToObjectEntries(property.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? []\n };\n }\n return property as StringProperty | NumberProperty;\n}\n\n/**\n * Resolve enums and arrays for properties\n * @param properties\n * @param value\n */\nexport function resolveProperties<M extends Record<string, unknown>>({\n propertyKey,\n properties,\n ignoreMissingFields,\n ...props\n}: {\n propertyKey?: string,\n properties: Properties,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}): Properties {\n return Object.entries<Property>(properties as Record<string, Property>)\n .map(([key, property]) => {\n const childResolvedProperty = resolveProperty({\n propertyKey: propertyKey ? `${propertyKey}.${key}` : undefined,\n property: property,\n ignoreMissingFields,\n ...props\n });\n if (!childResolvedProperty) return {};\n return {\n [key]: childResolvedProperty\n };\n })\n .filter((a) => a !== null)\n .reduce((a, b) => ({ ...a,\n...b }), {}) as Properties;\n}\n\nexport function resolveArrayProperties<M>({\n propertyKey,\n property,\n ignoreMissingFields = false,\n ...props\n}: {\n propertyKey?: string,\n property: ArrayProperty,\n values?: Partial<M>,\n previousValues?: Partial<M>,\n path?: string,\n entityId?: string | number,\n index?: number,\n propertyConfigs?: Record<string, PropertyConfig>;\n ignoreMissingFields?: boolean;\n authController: AuthState;\n}): Property[] {\n const propertyValue = propertyKey ? getIn(props.values, propertyKey) : undefined;\n\n if (property.of) {\n if (Array.isArray(property.of)) {\n return property.of.map((p, index) => {\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: p as Property,\n ignoreMissingFields,\n ...props,\n index\n });\n }) as Property[];\n } else {\n const of = property.of;\n const resolvedProperties = getArrayResolvedProperties({\n propertyValue,\n propertyKey,\n property,\n ignoreMissingFields,\n ...props\n });\n // Destructured to be *excluded* from `...rest`, not to be used —\n // see the comment below. Said explicitly so the discarded-value\n // ratchet does not carry a finding that is working as intended.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { values, previousValues, ...rest } = props;\n const ofProperty = resolveProperty({ // we don't want to pass the values of the parent entity\n property: of,\n ignoreMissingFields,\n ...rest\n });\n if (!ofProperty && !ignoreMissingFields)\n throw Error(\"When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property\")\n return resolvedProperties;\n }\n } else if (property.oneOf) {\n const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;\n const resolvedProperties: Property[] = Array.isArray(propertyValue)\n ? propertyValue.map((v, index) => {\n const type = v && v[typeField];\n const childProperty = property.oneOf?.properties[type];\n if (!type || !childProperty) return null;\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: childProperty,\n ignoreMissingFields,\n ...props\n });\n }).filter(e => Boolean(e)) as Property[]\n : [];\n return resolvedProperties;\n } else if (!property.columnType) {\n // An array with neither `of`/`oneOf` nor a `columnType` describes no element\n // type, so nothing can be generated or rendered from it.\n //\n // The escape hatch used to be `ui.Field` — \"a custom component can render\n // anything\" — which made a *presentation* field decide whether a schema was\n // valid, in code the Postgres generator runs. `columnType` is the same escape\n // hatch stated as data: `columnType: \"text[]\"` says what the column holds,\n // which is what both the generator and the form actually need.\n throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or a \\`columnType\\` such as \"text[]\"`);\n } else {\n return [];\n }\n\n}\n\nexport function getArrayResolvedProperties({\n propertyKey,\n propertyValue,\n property,\n ...props\n}: {\n propertyValue: unknown,\n propertyKey?: string,\n property: ArrayProperty,\n ignoreMissingFields: boolean,\n values?: object;\n previousValues?: object;\n path?: string;\n entityId?: string | number;\n index?: number;\n propertyConfigs?: Record<string, PropertyConfig>;\n authController: AuthState;\n}) {\n\n const of = property.of;\n if (!of)\n throw Error(\n `Trying to resolve an array property (${propertyKey}) without providing an 'of' property`\n )\n return Array.isArray(propertyValue)\n ? propertyValue.map((v: unknown, index: number) => {\n return resolveProperty({\n propertyKey: `${propertyKey}.${index}`,\n property: Array.isArray(of) ? of[index] : of,\n ...props,\n index\n });\n }).filter(e => Boolean(e)) as Property[]\n : [];\n}\n\nexport function resolveEnumValues(input: EnumValues): EnumValueConfig[] | undefined {\n if (typeof input === \"object\") {\n return Object.entries(input).map(([id, value]) =>\n (typeof value === \"string\"\n ? {\n id,\n label: value\n }\n : value));\n } else if (Array.isArray(input)) {\n return input as EnumValueConfig[];\n } else {\n return undefined;\n }\n}\n\n\n/**\n * The lists rendered inside an entity view of `collection` — its tabs.\n *\n * The single derivation. There used to be two that disagreed: this one, and a\n * copy in `CollectionRegistry.normalizeCollection` that stamped each child with\n * the *target collection's* slug instead of the relation key. Since the\n * registry ran first and cached its answer onto `childCollections`, its version\n * was the one that won, and the frontend addressed child listings by a segment\n * the backend could not resolve.\n *\n * Order of precedence:\n * 1. `childCollections` — the explicit escape hatch for custom drivers.\n * 2. `subcollections` on an engine that has real containment (Firestore).\n * 3. many-relations on an engine that has relations (SQL).\n */\nexport function getEntityChildViews<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): EntityChildView[] {\n const asSubcollections = (collections: CollectionConfig<Record<string, unknown>>[]): EntityChildView[] =>\n collections.filter(Boolean).map(child => ({\n key: child.slug,\n collection: child,\n source: { kind: \"subcollection\" as const }\n }));\n\n if (collection.childCollections) {\n return asSubcollections(collection.childCollections() ?? []);\n }\n\n const capabilities = getDataSourceCapabilities(collection.engine);\n\n const declaredSubcollections = getDeclaredSubcollections(collection);\n if (capabilities.supportsSubcollections && declaredSubcollections) {\n return asSubcollections(declaredSubcollections() ?? []);\n }\n\n if (!capabilities.supportsRelations) return [];\n\n const resolvedRelations = resolveCollectionRelations(collection);\n const views: EntityChildView[] = [];\n const seen = new Set<string>();\n\n // Keyed by the map key, not by `relationName`: the map key is what\n // `findRelation` matches a path segment against, so it is the only one that\n // addresses the same relation on both sides of the wire. The map registers\n // some relations twice — once canonically, once under the declaring\n // property key — so dedupe on the underlying relation.\n for (const [relationKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.cardinality !== \"many\") continue;\n\n const identity = relation.relationName ?? relationKey;\n if (seen.has(identity)) continue;\n\n let target: CollectionConfig | undefined;\n try {\n target = relation.target();\n } catch {\n continue;\n }\n if (!target) continue;\n seen.add(identity);\n\n // A name given to the declaring property is the author naming the tab.\n const declaringProperty = Object.entries((collection.properties ?? {}) as Record<string, Property>)\n .find(([propKey, p]) => p.type === \"relation\" && ((p as RelationProperty).relation?.relationName ?? propKey) === identity);\n const customName = declaringProperty?.[1]?.name;\n\n const base: CollectionConfig<Record<string, unknown>> = {\n ...target,\n slug: relationKey,\n ...(customName ? { name: customName,\nsingularName: customName } : {})\n } as CollectionConfig<Record<string, unknown>>;\n\n views.push({\n key: relationKey,\n collection: (relation.overrides ? mergeDeep(base, relation.overrides) : base) as CollectionConfig<Record<string, unknown>>,\n source: {\n kind: \"relation\",\n relationKey,\n mode: isJunctionBackedRelation(relation) ? \"linked\" : \"owned\",\n targetSlug: target.slug\n }\n });\n }\n\n return views;\n}\n\n/**\n * Each of `collection`'s tabs paired with the property that declared it, when a\n * property declared it: child view key → property key.\n *\n * A many-relation can only be declared as a property — that is the documented\n * and only mechanism — and {@link getEntityChildViews} promotes it to a tab. So\n * one declaration reaches the panel twice, and neither surface knew about the\n * other. The form rendered a relation picker beside the tab, and the collection\n * table rendered *two* columns under one heading: the relation's own column,\n * showing the child rows, and a jump-to-tab button carrying the same name.\n *\n * The pairing is what lets each surface decide which half is redundant, and it\n * has to be a pairing rather than two sets because the two keys differ whenever\n * a relation is named. The match is on the resolved `relationName` — the\n * identity `getEntityChildViews` itself dedupes on — so a relation declared in\n * `relations` and pointed at by a differently-named property is recognised too.\n *\n * A relation with no property of its own is absent here, which is the point: it\n * has exactly one surface already, and nothing to weigh it against.\n *\n * Only top-level properties: a relation nested inside a `map` gets no tab.\n */\nexport function getChildViewDeclaringProperties<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): Map<string, string> {\n const pairs = new Map<string, string>();\n\n const relationProperties = Object.entries((collection.properties ?? {}) as Record<string, Property>)\n .filter(([, property]) => property?.type === \"relation\");\n if (relationProperties.length === 0) return pairs;\n\n const relationViews = getEntityChildViews(collection)\n .filter(view => view.source.kind === \"relation\");\n if (relationViews.length === 0) return pairs;\n\n const resolvedRelations = resolveCollectionRelations(collection);\n const identityOf = (relationKey: string): string =>\n resolvedRelations[relationKey]?.relationName ?? relationKey;\n\n const declaringPropertyByIdentity = new Map<string, string>();\n for (const [propertyKey, property] of relationProperties) {\n const relation = (property as RelationProperty).resolvedRelation ?? resolvedRelations[propertyKey];\n // A to-one relation is a foreign key the author edits, never a tab. No\n // view will match it — the views here are many-relations only — but\n // reading the cardinality says so where someone is looking.\n if (relation?.cardinality !== \"many\") continue;\n const identity = relation.relationName ?? propertyKey;\n if (!declaringPropertyByIdentity.has(identity)) declaringPropertyByIdentity.set(identity, propertyKey);\n }\n\n for (const view of relationViews) {\n const propertyKey = declaringPropertyByIdentity.get(\n identityOf((view.source as { relationKey: string }).relationKey));\n if (propertyKey) pairs.set(view.key, propertyKey);\n }\n\n return pairs;\n}\n\n/**\n * The property keys of `collection` whose relation is already one of its tabs.\n *\n * What a form asks: the tab is the treatment for a list of child rows, so the\n * picker beside it is the redundant half. See\n * {@link getChildViewDeclaringProperties}.\n */\nexport function getChildViewRelationPropertyKeys<M extends Record<string, unknown> = Record<string, unknown>>(\n collection: CollectionConfig<M>\n): Set<string> {\n return new Set(getChildViewDeclaringProperties(collection).values());\n}\n\n/**\n * The child views of `collection` as bare collections.\n *\n * The flattened view of {@link getEntityChildViews}, for navigation code that\n * only needs to match a path segment against a slug. Anything that cares *what\n * kind* of list it is showing — chiefly the admin, which must not offer a\n * global delete on a shared row — should read the views instead.\n */\nexport function getSubcollections<M extends Record<string, unknown> = Record<string, unknown>>(collection: CollectionConfig<M>): CollectionConfig<Record<string, unknown>>[] {\n return getEntityChildViews(collection).map(view => view.collection);\n}\n","import { ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, LiteralPolicyOperand, PolicyExpression, policy, rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * A tiny, regex-based SQL \"parser\" for security rules.\n *\n * This is NOT a full SQL parser. It is designed to handle the subset of SQL\n * commonly used in `USING` and `WITH CHECK` clauses, enough to drive the\n * optimistic client-side UI decision.\n *\n * It handles:\n * - `field = 'literal'`\n * - `field != 'literal'`\n * - `field = current_setting('app.uid')` (or the legacy `app.user_id`)\n * - `A AND B`, `A OR B` — only where the keyword is at the top level\n * - `true`\n * - `IN (...)` (as optimistic true)\n *\n * For anything it doesn't understand, it returns a `raw` expression, which\n * the evaluator treats as \"unknown\" (and usually optimistic true).\n *\n * **This output also round-trips back into DDL** via `policyToPostgres` (the\n * schema/policy generators), so decomposing a clause the parser only partly\n * understands is not a cosmetic mistake — it emits invalid SQL. When in doubt,\n * prefer `raw`: it is reproduced verbatim.\n */\n/** True when `keyword` starts at `i` as a standalone word. */\nfunction isKeywordAt(upper: string, i: number, keyword: string): boolean {\n if (!upper.startsWith(keyword, i)) return false;\n const before = i === 0 ? \" \" : upper[i - 1];\n const after = upper[i + keyword.length] ?? \" \";\n return /[\\s()]/.test(before) && /[\\s()]/.test(after);\n}\n\n/**\n * Split `sql` on a boolean keyword, but only where it sits at paren depth 0 and\n * outside a string literal. Returns null when it never does, so the caller\n * leaves the clause alone.\n *\n * This used to be `sql.split(/ AND /i)`, which tore subqueries in half: the\n * `AND` inside\n * `EXISTS (SELECT 1 FROM organization_members m WHERE m.org = t.org AND m.user_id = rebase.uid())`\n * split the expression, and re-emitting the halves produced\n * `(EXISTS (...) AND m.user_id = rebase.uid())`\n * where `m` is no longer in scope — SQL that Postgres rejects outright with\n * \"missing FROM-clause entry for table\". Returning null instead keeps such a\n * clause as a `raw` expression, which round-trips verbatim.\n */\nfunction splitTopLevel(sql: string, keyword: \"AND\" | \"OR\"): string[] | null {\n const upper = sql.toUpperCase();\n const parts: string[] = [];\n let depth = 0;\n let inString = false;\n let start = 0;\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n if (inString) {\n if (ch === \"'\") {\n if (sql[i + 1] === \"'\") i++; // '' escapes a quote inside a literal\n else inString = false;\n }\n continue;\n }\n if (ch === \"'\") { inString = true; continue; }\n if (ch === \"(\") { depth++; continue; }\n if (ch === \")\") { depth--; continue; }\n if (depth === 0 && isKeywordAt(upper, i, keyword)) {\n parts.push(sql.slice(start, i));\n i += keyword.length - 1;\n start = i + 1;\n }\n }\n\n if (parts.length === 0) return null;\n parts.push(sql.slice(start));\n const trimmedParts = parts.map(p => p.trim()).filter(p => p.length > 0);\n return trimmedParts.length > 1 ? trimmedParts : null;\n}\n\n/** Drop redundant wrapping parens (`(a AND b)` → `a AND b`), never `(a) AND (b)`. */\nfunction stripOuterParens(sql: string): string {\n let s = sql.trim();\n for (;;) {\n if (!s.startsWith(\"(\") || !s.endsWith(\")\")) return s;\n let depth = 0;\n let inString = false;\n let wraps = true;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (inString) {\n if (ch === \"'\") {\n if (s[i + 1] === \"'\") i++;\n else inString = false;\n }\n continue;\n }\n if (ch === \"'\") { inString = true; continue; }\n if (ch === \"(\") depth++;\n else if (ch === \")\") {\n depth--;\n if (depth === 0 && i < s.length - 1) { wraps = false; break; }\n }\n }\n if (!wraps) return s;\n s = s.slice(1, -1).trim();\n }\n}\n\nexport function sqlToPolicy(sql: string): PolicyExpression {\n // Normalised before anything else looks at it, so every pattern below only\n // has to know the current spelling. A database migrated by a pre-1.0 release\n // still holds `auth.uid()` in its policy bodies until the next push or boot\n // recompiles them — and until then the admin UI reads those bodies back\n // through here. Without this they parse as opaque `raw`, and the framework's\n // own policies get badged as hand-written drift.\n //\n // Normalising rather than accepting both spellings throughout is deliberate:\n // it also means a legacy policy that falls through to `raw` is stored in the\n // new spelling, so editing and saving one in the Studio migrates it.\n const trimmed = stripOuterParens(rewriteLegacyRlsFunctions(sql).trim());\n\n if (trimmed.toLowerCase() === \"true\") return policy.true();\n if (trimmed.toLowerCase() === \"false\") return policy.false();\n\n // Handle roles overlap (&&)\n // Matches: string_to_array(rebase.roles(), ',') && ARRAY['admin', 'editor']\n const overlapMatch = trimmed.match(/^string_to_array\\s*\\(\\s*rebase\\.roles\\(\\)\\s*,\\s*','\\s*\\)\\s*&&\\s*ARRAY\\s*\\[(.+)\\]$/i);\n if (overlapMatch) {\n const roles = overlapMatch[1].split(\",\").map(s => s.trim().replace(/^'|'$/g, \"\"));\n return policy.rolesOverlap(roles);\n }\n\n // Handle roles containment (@>)\n // Matches: string_to_array(rebase.roles(), ',') @> ARRAY['admin']\n const containMatch = trimmed.match(/^string_to_array\\s*\\(\\s*rebase\\.roles\\(\\)\\s*,\\s*','\\s*\\)\\s*@>\\s*ARRAY\\s*\\[(.+)\\]$/i);\n if (containMatch) {\n const roles = containMatch[1].split(\",\").map(s => s.trim().replace(/^'|'$/g, \"\"));\n return policy.rolesContain(roles);\n }\n\n // OR binds looser than AND, so it splits first.\n const orParts = splitTopLevel(trimmed, \"OR\");\n if (orParts) return policy.or(...orParts.map(sqlToPolicy));\n\n const andParts = splitTopLevel(trimmed, \"AND\");\n if (andParts) return policy.and(...andParts.map(sqlToPolicy));\n\n // Handle = and !=\n const match = trimmed.match(/^(.+?)\\s*(!?=)\\s*(.+)$/);\n if (match) {\n const [, leftStr, op, rightStr] = match;\n const left = parseOperand(leftStr.trim());\n const right = parseOperand(rightStr.trim());\n if (left && right) {\n return policy.compare(left, op === \"=\" ? \"eq\" : \"neq\", right);\n }\n }\n\n // Fallback to raw — the NORMALISED text, not the input. Storing the input\n // verbatim would mean a legacy policy read out of a database, edited in the\n // Studio and saved, writes `auth.uid()` back into the project's config: a\n // call to a function 1.0 no longer creates.\n return policy.raw(trimmed);\n}\n\n/**\n * Literals from other BaaS platforms that people compare `rebase.uid()` against\n * out of habit. Mirrors the driver's `FOREIGN_CONVENTION_ROLES` guard on\n * `pgRoles`, one surface over: the same muscle memory inside a `using:` string\n * is the more dangerous spelling, because it inverts a rule instead of\n * emptying a table.\n */\n/**\n * A `Map`, not an object literal.\n *\n * As `Record<string, string>` this was indexed with a literal taken straight\n * out of a policy, so every key on `Object.prototype` answered: a rule\n * comparing `rebase.uid()` to `\"valueOf\"`, `\"toString\"`, `\"constructor\"` or\n * `\"hasOwnProperty\"` found a truthy \"platform\" and reported an anonymous-grant\n * risk that does not exist — with the matched function interpolated into the\n * explanation as the platform's name. A security warning that fires on\n * innocent input is worse than none: it is what teaches people to skip the\n * warnings that are real.\n *\n * Same shape as the prototype-pollution class swept out of `setIn`, `getIn`,\n * `mergeDeep` and `unflattenObject` — a data-derived key reaching a plain\n * object. Found by a property test, on the input `\"valueOf\"`.\n */\nconst FOREIGN_CONVENTION_UIDS = new Map<string, string>([\n [\"anon\", \"Supabase\"],\n [\"authenticated\", \"Supabase\"],\n [\"service_role\", \"Supabase\"]\n]);\n\n/**\n * The same foreign literals, as a pattern for SQL that could not be parsed\n * back into structure.\n */\nconst FOREIGN_UID_LITERAL_SQL = new RegExp(\n String.raw`rebase\\.uid\\(\\)\\s*=\\s*'(${[...FOREIGN_CONVENTION_UIDS.keys()].join(\"|\")})'`,\n \"i\"\n);\n\n/** A clause that reads as a lockdown but admits anonymous callers. */\nexport interface AnonymousGrantRisk {\n /** Which spelling was found. */\n pattern: \"foreign-uid-literal\" | \"uid-not-null\";\n /** The offending fragment — the literal, or the SQL that is a tautology. */\n detail: string;\n /** Why it admits anonymous callers, and what to write instead. */\n explanation: string;\n}\n\n/**\n * `rebase.uid() IS NOT NULL` in raw SQL, the clause that is always true.\n *\n * Both schema spellings, because this runs over policy bodies read back from a\n * database, and one migrated by a pre-1.0 release still holds `auth.uid()`.\n * A security check that stops recognising a dangerous clause because the\n * framework renamed a function is a check that silently turns off.\n */\nconst UID_NOT_NULL = /\\b(?:rebase|auth)\\.uid\\(\\)\\s+IS\\s+NOT\\s+NULL/i;\n\n/**\n * Find clauses that read as \"signed-in users only\" but admit anonymous callers.\n *\n * Both spellings come from the same place — Supabase, where its own `auth.uid()`\n * really is NULL for an anonymous request. Rebase substitutes\n * {@link ANONYMOUS_USER_ID} instead (a blank id would read back as NULL, which\n * is how the trusted *server* context is recognised), so:\n *\n * - `rebase.uid() IS NOT NULL` is a tautology on the user path, and\n * - `rebase.uid() != 'anon'` excludes one spelling of anonymous and admits the\n * other. This one is not hypothetical and was not only a foreign habit:\n * rebase's own request path reported `'anon'` while everything that compiled\n * or checked a policy used `'anonymous'`, so whichever literal an author\n * picked, half the anonymous callers walked through. See\n * {@link ANONYMOUS_USER_IDS}.\n *\n * Either one turns a lockdown into a full grant, and neither looks wrong. No\n * real user id is ever one of these literals, and a user-context request is\n * never NULL, so a match is always a mistake rather than a deliberate check.\n *\n * Structured expressions are checked too, not just parsed SQL: `policy.compare`\n * can spell the same mistake.\n */\nexport function findAnonymousGrants(expr: PolicyExpression): AnonymousGrantRisk[] {\n const found: AnonymousGrantRisk[] = [];\n\n const visit = (e: PolicyExpression): void => {\n switch (e.kind) {\n case \"and\":\n case \"or\":\n e.operands.forEach(visit);\n return;\n case \"not\":\n visit(e.operand);\n return;\n case \"existsIn\":\n visit(e.where);\n return;\n case \"raw\": {\n if (UID_NOT_NULL.test(e.sql)) {\n found.push({\n pattern: \"uid-not-null\",\n detail: e.sql,\n explanation: \"`rebase.uid() IS NOT NULL` is true for every request that came from a client, \" +\n `including anonymous ones — they carry '${ANONYMOUS_USER_ID}', not NULL. ` +\n \"Use `condition: policy.authenticated()` to mean \\\"signed in\\\".\"\n });\n }\n // The foreign literals have to be looked for here too, not only\n // in `compare`. `sqlToPolicy` falls back to `raw` for anything\n // it cannot structure — an `EXISTS (...)` subquery always does —\n // so a policy read back from the database arrives as one opaque\n // string. Checking only the tautology meant a genuine\n // `rebase.uid() = 'anon'` inside an `existsIn` was structurally\n // undetectable once round-tripped, and the caller read the empty\n // result as \"no risks found\".\n const foreign = FOREIGN_UID_LITERAL_SQL.exec(e.sql);\n if (foreign) {\n const literal = foreign[1];\n found.push({\n pattern: \"foreign-uid-literal\",\n detail: literal,\n explanation: `'${literal}' is a ${FOREIGN_CONVENTION_UIDS.get(literal)} convention. Rebase ` +\n `reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against ` +\n `'${literal}' passes for every caller. Use \\`condition: policy.authenticated()\\` to ` +\n \"mean \\\"signed in\\\".\"\n });\n }\n return;\n }\n case \"compare\": {\n const literal = [e.left, e.right].find(o => o.kind === \"literal\") as LiteralPolicyOperand | undefined;\n const comparesUid = e.left.kind === \"authUid\" || e.right.kind === \"authUid\";\n if (!comparesUid || typeof literal?.value !== \"string\") return;\n const platform = FOREIGN_CONVENTION_UIDS.get(literal.value);\n if (!platform) return;\n found.push({\n pattern: \"foreign-uid-literal\",\n detail: literal.value,\n explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous ` +\n `request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for ` +\n \"every caller. Use `condition: policy.authenticated()` to mean \\\"signed in\\\" — it \" +\n `compiles to NOT IN (${ANONYMOUS_USER_IDS.map(v => `'${v}'`).join(\", \")}), covering ` +\n \"every spelling rebase has reported rather than whichever one you remember.\"\n });\n return;\n }\n default:\n return;\n }\n };\n\n visit(expr);\n return found;\n}\n\nfunction parseOperand(str: string) {\n // current_setting('app.uid') or rebase.uid(). `app.user_id` is the\n // pre-rename spelling and stays parseable: policies are data, so a\n // database provisioned before the rename still holds rules written\n // against it, and round-tripping one must not silently drop the operand.\n //\n // ANCHORED, and that is the whole point. These tests used to be\n // unanchored — `.test(str)` rather than `^…$` — so any operand text that\n // merely *contained* a uid call was replaced wholesale by the call itself.\n // Everything else in the expression was discarded with it, including a\n // leading `NOT (`:\n //\n // NOT (rebase.uid() = rebase.uid()) parsed as rebase.uid() = rebase.uid()\n //\n // A deny became an unconditional grant. The realistic spelling is a\n // hand-written defensive rule with a uid call on both sides —\n // COALESCE(rebase.uid(), '') = COALESCE(owner_id, rebase.uid())\n // — which collapsed to the same tautology. This is not confined to the\n // admin UI: `securityRuleToConditions` feeds a rule's raw `using:` string\n // through here, and the Postgres DDL generators compile the result, so the\n // tautology was written into the database as the policy body.\n //\n // An operand this cannot identify exactly must return null, which drops the\n // whole clause to `raw` and reproduces it verbatim. That is the rule the\n // rest of this file already follows: when in doubt, prefer `raw`.\n if (/^current_setting\\s*\\(\\s*'app\\.(uid|user_id)'\\s*\\)$/i.test(str) || /^rebase\\.uid\\(\\)$/i.test(str)) {\n return policy.authUid();\n }\n\n // Literal string: 'value', with `''` decoded back to a single quote.\n //\n // `quoteLiteral` doubles every quote on the way out, and this did not undo\n // it, so a literal containing an apostrophe grew on every trip: O'Brien →\n // O''Brien → O''''Brien, doubling each time a policy was read back and\n // recompiled. Past the first trip the emitted policy compares against a\n // string no row holds.\n const literal = parseSingleQuoted(str);\n if (literal !== null) {\n return policy.literal(literal);\n }\n\n // Unquoted literals, which must be recognised BEFORE the bare-word branch\n // below or they are read as column names.\n //\n // `quoteLiteral` emits booleans, numbers and null unquoted, so `a = false`\n // came back as a comparison against a *field* called `false`, and `a = 42`\n // against a field called `42`. The recompiled SQL is identical either way,\n // which is why this survived a round-trip check on the SQL — but the\n // expression is now wrong, and the expression is what the admin UI\n // evaluates. Against a row with no `a`, Postgres denies (`NULL = false` is\n // not true) while the JS evaluator compared two missing columns, found them\n // equal, and allowed. That is precisely the client/database drift the\n // shared PolicyExpression model exists to make impossible.\n //\n // Unambiguous in both directions: a SQL identifier cannot begin with a\n // digit, and bare `true`/`false`/`null` are always the literals — a column\n // so named would have to be double-quoted to be referenced at all.\n if (/^-?\\d+$/.test(str)) return policy.literal(Number(str));\n if (/^-?\\d*\\.\\d+$/.test(str)) return policy.literal(Number(str));\n if (/^true$/i.test(str)) return policy.literal(true);\n if (/^false$/i.test(str)) return policy.literal(false);\n if (/^null$/i.test(str)) return policy.literal(null);\n\n // Bare field name — but only one that survives the snake-casing the\n // compiler will apply to it. `toSnakeCase(\"_\")` is the empty string, and a\n // field that compiles to an empty column reference emits `= 'x'`, which is\n // a syntax error at CREATE POLICY time. Such a name is left to `raw`, where\n // it round-trips verbatim instead. `toSnakeCase` itself is not touched:\n // column names derived by it are already in shipped databases.\n if (/^\\w+$/.test(str) && toSnakeCase(str) !== \"\") {\n return policy.field(str);\n }\n\n return null;\n}\n\n/**\n * Decode a single-quoted SQL literal, or null when `str` is not exactly one.\n *\n * Rejecting is as important as decoding: `'a' = 'b'` is two literals and an\n * operator, not one literal whose body contains a quote, and a regex anchored\n * on the outer quotes would happily read it as the latter. Every interior quote\n * must therefore be part of a `''` pair.\n */\nfunction parseSingleQuoted(str: string): string | null {\n if (str.length < 2 || !str.startsWith(\"'\") || !str.endsWith(\"'\")) return null;\n const body = str.slice(1, -1);\n let out = \"\";\n for (let i = 0; i < body.length; i++) {\n if (body[i] !== \"'\") {\n out += body[i];\n continue;\n }\n if (body[i + 1] === \"'\") {\n out += \"'\";\n i++;\n continue;\n }\n return null; // a bare quote — `str` is not a single literal\n }\n return out;\n}\n","import { PolicyExpression, SecurityRule, policy } from \"@rebasepro/types\";\nimport { sqlToPolicy } from \"./sqlToPolicy\";\n\n/**\n * The normalized `USING` / `WITH CHECK` conditions for a single security rule,\n * expressed in the engine-agnostic {@link PolicyExpression} model.\n *\n * A `null` clause means \"this rule contributes no condition for that clause\";\n * consumers apply the default (Postgres denies with `false`).\n */\nexport interface RuleConditions {\n usingExpr: PolicyExpression | null;\n withCheckExpr: PolicyExpression | null;\n}\n\n/**\n * Desugars a {@link SecurityRule} — its `access`/`ownerField`/`roles` shortcuts,\n * structured `condition`/`check`, and raw `using`/`withCheck` — into a single\n * normalized {@link PolicyExpression} pair.\n *\n * **This is the linchpin against drift:** both the Postgres DDL generators and\n * the client-side evaluator consume this one function, so there is exactly one\n * definition of what a rule means. In particular, application `roles` are folded\n * into the expression here (AND'd with the base condition, matching how Postgres\n * generates the clause) rather than being handled separately by each consumer.\n */\nexport function securityRuleToConditions(rule: SecurityRule): RuleConditions {\n return {\n usingExpr: withRoles(baseUsing(rule), rule),\n withCheckExpr: withRoles(baseWithCheck(rule), rule)\n };\n}\n\nfunction baseUsing(rule: SecurityRule): PolicyExpression | null {\n if (rule.condition) return rule.condition;\n if (rule.using != null) return sqlToPolicy(rule.using);\n if (rule.access === \"public\") return policy.true();\n if (rule.ownerField) return policy.compare(policy.field(rule.ownerField), \"eq\", policy.authUid());\n return null;\n}\n\nfunction baseWithCheck(rule: SecurityRule): PolicyExpression | null {\n if (rule.check) return rule.check;\n if (rule.withCheck != null) return sqlToPolicy(rule.withCheck);\n // No explicit WITH CHECK → fall back to the USING condition, matching\n // PostgreSQL's own default behavior.\n return baseUsing(rule);\n}\n\n/**\n * AND the base condition with an application-role check, or produce a roles-only\n * condition when there is no base. Mirrors the Postgres generator so that a\n * role-scoped restrictive rule denies exactly the same set of users on both\n * sides.\n */\nfunction withRoles(base: PolicyExpression | null, rule: SecurityRule): PolicyExpression | null {\n if (!rule.roles || rule.roles.length === 0) return base;\n const rolesExpr = policy.rolesOverlap(rule.roles);\n if (rule.mode === \"restrictive\") {\n // Restrictive rule: applies ONLY if user has the roles.\n // If user DOES NOT have the roles, they are NOT restricted (passes).\n // If user HAS the roles, they must pass the base condition.\n // Logical equivalent: NOT(roles) OR base\n return base ? policy.or(policy.not(rolesExpr), base) : policy.not(rolesExpr);\n }\n return base ? policy.and(base, rolesExpr) : rolesExpr;\n}\n","import { ANONYMOUS_USER_IDS, CollectionConfig, PolicyExpression, PolicyOperand, PolicyCompareOperator, Property, ExistsInPolicyExpression, RLS_IS_ANONYMOUS_SQL, RLS_JWT_SQL, RLS_ROLES_SQL, RLS_UID_SQL, rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { getTableName } from \"../relations\";\n\n/**\n * Options for {@link policyToPostgres}.\n */\nexport interface PolicyCompileOptions {\n /**\n * Resolve a collection by slug. Required to compile\n * {@link ExistsInPolicyExpression} (`policy.existsIn`) — the compiler needs\n * the joined collection to derive its table name / schema. When omitted, the\n * join table falls back to a snake_cased slug.\n */\n resolveCollection?: (slug: string) => CollectionConfig | undefined;\n}\n\n/**\n * The lexical scope threaded through compilation. It changes when we descend\n * into an `existsIn` subquery: inside it, `field` refers to the joined table\n * (aliased) while `outerField` refers to the outer RLS row (table-qualified).\n */\ninterface CompileScope {\n /** Collection whose columns a bare `field` operand resolves against. */\n fieldCollection?: CollectionConfig;\n /** SQL prefix for `field` operands (`\"\"` at top level, `\"alias\".` in a subquery). */\n fieldPrefix: string;\n /** The outer RLS collection, for `outerField` operands. */\n outerCollection?: CollectionConfig;\n /** SQL prefix for `outerField` operands (`\"\"` at top level, `\"schema\".\"table\".` in a subquery). */\n outerPrefix: string;\n resolveCollection?: (slug: string) => CollectionConfig | undefined;\n /** Monotonic counter for generating unique subquery aliases. */\n alias: { n: number };\n}\n\n/**\n * Compiles a {@link PolicyExpression} to a PostgreSQL boolean SQL string,\n * suitable for a `USING (...)` / `WITH CHECK (...)` clause.\n *\n * This is one of the two consumers of the shared policy model (the other being\n * {@link evaluatePolicy}); the Postgres schema generators call it so that DDL\n * and the admin UI derive from the exact same expression.\n */\nexport function policyToPostgres(expr: PolicyExpression, collection?: CollectionConfig, options?: PolicyCompileOptions): string {\n return compile(expr, {\n fieldCollection: collection,\n fieldPrefix: \"\",\n outerCollection: collection,\n outerPrefix: \"\",\n resolveCollection: options?.resolveCollection,\n alias: { n: 0 }\n });\n}\n\nfunction compile(expr: PolicyExpression, scope: CompileScope): string {\n switch (expr.kind) {\n case \"true\":\n return \"true\";\n case \"false\":\n return \"false\";\n case \"and\":\n return expr.operands.length === 0\n ? \"true\"\n : expr.operands.map(o => `(${compile(o, scope)})`).join(\" AND \");\n case \"or\":\n return expr.operands.length === 0\n ? \"false\"\n : expr.operands.map(o => `(${compile(o, scope)})`).join(\" OR \");\n case \"not\":\n return `NOT (${compile(expr.operand, scope)})`;\n case \"compare\": {\n // `rebase.uid()` returns text; cast the column side so uuid / integer\n // id columns compare cleanly instead of failing with\n // \"operator does not exist: uuid = text\" at CREATE POLICY time.\n const castForAuthUid = (operand: PolicyOperand, sqlText: string, other: PolicyOperand): string =>\n other.kind === \"authUid\" && (operand.kind === \"field\" || operand.kind === \"outerField\")\n ? `(${sqlText})::text`\n : sqlText;\n // A claim is text too, and the same mismatch applies — but here the\n // cast goes the OTHER way, onto the claim. Casting the column would\n // compile and would take the index off it, and this operand exists\n // to carry a tenancy predicate that is ANDed into every read of the\n // table. See {@link claimCastType}.\n const claimSql = (operand: PolicyOperand, other: PolicyOperand): string | undefined =>\n operand.kind === \"authClaim\"\n ? authClaimSql(operand.name, claimCastType(other, scope))\n : undefined;\n const leftSql = claimSql(expr.left, expr.right)\n ?? castForAuthUid(expr.left, operandToSql(expr.left, scope), expr.right);\n const rightSql = claimSql(expr.right, expr.left)\n ?? castForAuthUid(expr.right, operandToSql(expr.right, scope), expr.left);\n return `${leftSql} ${COMPARE_SQL[expr.op]} ${rightSql}`;\n }\n case \"rolesOverlap\":\n return `string_to_array(${RLS_ROLES_SQL}, ',') && ${rolesArraySql(expr.roles)}`;\n case \"rolesContain\":\n return `string_to_array(${RLS_ROLES_SQL}, ',') @> ${rolesArraySql(expr.roles)}`;\n case \"authenticated\":\n // `IS NOT NULL` alone is a tautology on the user path: every\n // user-context request sets `app.uid`, and an anonymous one sets\n // it to a sentinel. Excluding the sentinels is what makes this mean\n // \"signed in\" rather than \"anyone at all\".\n //\n // Every sentinel, not just the current one. This clause is written\n // into the database and outlives the server that generated it: a\n // policy compiled here may be enforced against an older server that\n // still reports `'anon'`, which is exactly how excluding one\n // spelling turned this helper into a grant. See ANONYMOUS_USER_IDS.\n return `${RLS_UID_SQL} IS NOT NULL AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`;\n case \"registered\":\n // \"Signed in\" AND \"not a guest\". The first half is the same clause\n // `authenticated` compiles to; the second is the fact anonymous\n // sign-in used not to put anywhere the database could see, so a\n // guest and an account were one principal inside every policy.\n //\n // `rebase.is_anonymous()` defaults to false when its GUC is unset,\n // so a policy compiled here and enforced by an older server reads\n // every session as an account — which is the behaviour that\n // deployment already had, rather than a lockout.\n return `${RLS_UID_SQL} IS NOT NULL`\n + ` AND ${RLS_UID_SQL} NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`\n + ` AND NOT ${RLS_IS_ANONYMOUS_SQL}`;\n case \"serverContext\":\n // Only the built-in server flows leave `app.uid` unset.\n return `${RLS_UID_SQL} IS NULL`;\n case \"existsIn\":\n return compileExistsIn(expr, scope);\n case \"raw\": {\n // A project written against a pre-1.0 release may still spell the\n // helpers `auth.uid()`. Rewritten rather than rejected: the rule\n // means exactly the same thing, the developer cannot be expected to\n // have read a changelog mid-deploy, and the alternative is a policy\n // that compiles cleanly and then denies every row at runtime because\n // it calls a function that no longer exists.\n //\n // The counterpart is `warnOnLegacyRlsFunctions`, which says so once\n // at boot with the file to edit — silence here would leave the old\n // spelling working forever and make the migration permanent.\n const sqlText = rewriteLegacyRlsFunctions(expr.sql);\n\n // Full-power escape hatch: `{column}` denotes a column of the outer\n // RLS row. It must be table-qualified, not bare: raw SQL may open its\n // own subquery over the same table, and there a bare name binds to the\n // inner scope, collapsing `m.x = {x}` into the tautology `m.x = m.x`.\n return sqlText.replace(/\\{(\\w+)\\}/g, (_, col) =>\n `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);\n }\n }\n}\n\n/**\n * Compiles `existsIn` to a correlated `EXISTS (SELECT 1 FROM <join> WHERE ...)`.\n * Inside the subquery, `field` operands bind to the aliased join table and\n * `outerField` operands bind to the (table-qualified) outer RLS row.\n */\nfunction compileExistsIn(expr: ExistsInPolicyExpression, scope: CompileScope): string {\n const join = scope.resolveCollection?.(expr.collection);\n const joinTable = join ? getTableName(join) : toSnakeCase(expr.collection);\n const joinSchema = schemaOf(join) ?? schemaOf(scope.outerCollection) ?? \"public\";\n const alias = `_ex${scope.alias.n++}`;\n\n // `outerField` inside the subquery must be qualified with the outer table,\n // otherwise a bare column name would bind to the joined table instead.\n const outerPrefix = outerQualifier(scope);\n\n const innerScope: CompileScope = {\n fieldCollection: join,\n fieldPrefix: `\"${alias}\".`,\n outerCollection: scope.outerCollection,\n outerPrefix,\n resolveCollection: scope.resolveCollection,\n alias: scope.alias\n };\n return `EXISTS (SELECT 1 FROM \"${joinSchema}\".\"${joinTable}\" \"${alias}\" WHERE ${compile(expr.where, innerScope)})`;\n}\n\nconst COMPARE_SQL: Record<PolicyCompareOperator, string> = {\n eq: \"=\",\n neq: \"!=\",\n lt: \"<\",\n lte: \"<=\",\n gt: \">\",\n gte: \">=\"\n};\n\nfunction operandToSql(operand: PolicyOperand, scope: CompileScope): string {\n switch (operand.kind) {\n case \"field\":\n return `${scope.fieldPrefix}${resolveColumnName(operand.name, scope.fieldCollection)}`;\n case \"outerField\":\n return `${scope.outerPrefix}${resolveColumnName(operand.name, scope.outerCollection)}`;\n case \"literal\":\n return quoteLiteral(operand.value);\n case \"authUid\":\n return RLS_UID_SQL;\n case \"authRoles\":\n return `string_to_array(${RLS_ROLES_SQL}, ',')`;\n case \"authClaim\":\n // Uncast — the shape a claim has when nothing says what it is being\n // compared against (a claim on both sides, or against a literal).\n // The `compare` arm replaces this whenever the other operand names\n // a typed column.\n return authClaimSql(operand.name, \"text\");\n }\n}\n\n/** Postgres types a text claim is cast to. `\"text\"` is the no-cast case. */\ntype ClaimCastType = \"text\" | \"uuid\" | \"bigint\" | \"numeric\";\n\n/**\n * The Postgres type a claim has to be cast to, to be compared with `operand`.\n *\n * `\"text\"` means \"no cast\": a claim already is text, and a `text` / `varchar`\n * column compares with it directly and keeps using its index.\n *\n * Resolved from the *property*, and through a relation's target when the column\n * is a foreign key — a `belongsTo` tenant field is the ordinary shape, and its\n * column's type is the target collection's primary key type rather than\n * anything visible on the property itself. Unknown resolves to `\"text\"`, which\n * is the safe direction: a redundant `text` comparison costs nothing, while a\n * missing `uuid` cast is a `CREATE POLICY` that fails and leaves a table with\n * RLS enabled and no policy — which denies every row.\n */\nfunction claimCastType(operand: PolicyOperand, scope: CompileScope): ClaimCastType {\n if (operand.kind !== \"field\" && operand.kind !== \"outerField\") return \"text\";\n const collection = operand.kind === \"field\" ? scope.fieldCollection : scope.outerCollection;\n return propertyClaimCastType(operand.name, collection, scope.resolveCollection);\n}\n\n/**\n * What `name` on `collection` compares against a text claim as.\n *\n * `depth` stops a `reference` cycle — two collections whose keys point at each\n * other — from recursing forever. Two hops is more than any real declaration\n * needs.\n */\nfunction propertyClaimCastType(\n name: string,\n collection: CollectionConfig | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined,\n depth = 0\n): ClaimCastType {\n const prop = collection?.properties?.[name] as Property | undefined;\n if (!prop || depth > 2) return \"text\";\n\n switch (prop.type) {\n case \"string\": {\n const sp = prop as { isId?: unknown; columnType?: unknown; enum?: unknown };\n if (sp.enum) return \"text\";\n return sp.isId === \"uuid\" || sp.columnType === \"uuid\" ? \"uuid\" : \"text\";\n }\n case \"number\": {\n const np = prop as { columnType?: string; isId?: unknown; validation?: { integer?: boolean } };\n if (np.columnType === \"numeric\") return \"numeric\";\n if (np.columnType || np.validation?.integer || np.isId) return \"bigint\";\n // A `number` with no `columnType` and no `validation.integer` is\n // NUMERIC — see `numberType` in the schema planner.\n return \"numeric\";\n }\n case \"reference\":\n return primaryKeyClaimCastType(\n resolveTargetCollection((prop as { path?: string }).path, resolveCollection),\n resolveCollection,\n depth\n );\n case \"relation\":\n return primaryKeyClaimCastType(\n resolveTargetCollection(\n relationTargetSlug((prop as { relation?: { target?: unknown } }).relation),\n resolveCollection\n ),\n resolveCollection,\n depth\n );\n default:\n return \"text\";\n }\n}\n\n/** The cast a column pointing at `target`'s primary key needs. */\nfunction primaryKeyClaimCastType(\n target: CollectionConfig | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined,\n depth: number\n): ClaimCastType {\n if (!target) return \"text\";\n for (const [key, property] of Object.entries(target.properties ?? {})) {\n if (!(property as { isId?: unknown })?.isId) continue;\n return propertyClaimCastType(key, target, resolveCollection, depth + 1);\n }\n // No declared key: the implicit `id TEXT PRIMARY KEY`.\n return \"text\";\n}\n\n/**\n * A relation's target slug, whatever form the declaration took.\n *\n * `target` is a slug on a plain object and a thunk on a builder — the two\n * shapes `resolveRelation` normalises — and this runs on the raw property,\n * before that resolution.\n */\nfunction relationTargetSlug(relation: { target?: unknown } | undefined): string | undefined {\n const target = relation?.target;\n if (typeof target === \"string\") return target;\n if (typeof target !== \"function\") return undefined;\n try {\n const slug = ((target as () => unknown)() as { slug?: unknown })?.slug;\n return typeof slug === \"string\" ? slug : undefined;\n } catch {\n // A thunk needing a registry this compilation does not have. `text` is\n // the fallback, and a fallback is not worth failing a compile over.\n return undefined;\n }\n}\n\nfunction resolveTargetCollection(\n slug: string | undefined,\n resolveCollection: ((slug: string) => CollectionConfig | undefined) | undefined\n): CollectionConfig | undefined {\n if (!slug || !resolveCollection) return undefined;\n // A `reference` path may be nested; the collection is its last segment.\n return resolveCollection(slug) ?? resolveCollection(slug.split(\"/\").pop() as string);\n}\n\n/**\n * `NULLIF(rebase.jwt() ->> 'name', '')`, cast to the column's type.\n *\n * Two things here are load-bearing beyond the cast:\n *\n * - **`NULLIF(…, '')`.** An absent claim already reads as NULL, but one set to\n * the empty string does not, and `''::uuid` raises rather than denying. Both\n * spellings of \"this caller has no tenant\" have to reach the comparison as\n * NULL, which is never true and therefore never a grant.\n * - **The guard.** The cast sits inside a `CASE` that first checks the text is\n * well-formed, because `'nonsense'::uuid` raises `invalid input syntax` — and\n * a policy that raises does not deny a row, it fails the whole statement. A\n * caller holding a malformed claim would get a 500 on every read of the table\n * instead of an empty list. `CASE` rather than an `AND` guard because only\n * `CASE` is guaranteed not to evaluate its arms out of order.\n *\n * The whole expression is STABLE (`rebase.jwt()` is), so Postgres evaluates it\n * once per query and can still use a btree index on the column it is compared\n * against — which is the entire reason the cast is on this side.\n */\nfunction authClaimSql(name: string, cast: ClaimCastType): string {\n const claim = `NULLIF(${RLS_JWT_SQL} ->> ${quoteLiteral(name)}, '')`;\n if (cast === \"text\") return claim;\n return `CASE WHEN ${claim} ~ '${CLAIM_CAST_GUARDS[cast]}' THEN (${claim})::${cast} END`;\n}\n\n/**\n * The text a claim must match before it is cast, per target type.\n *\n * The `bigint` guard caps the digit run at 18 rather than matching any run of\n * digits: `'99999999999999999999'::bigint` is a range error, which fails the\n * statement exactly as the syntax error would have.\n */\nconst CLAIM_CAST_GUARDS: Record<Exclude<ClaimCastType, \"text\">, string> = {\n uuid: \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n bigint: \"^-?[0-9]{1,18}$\",\n numeric: \"^-?[0-9]+(\\\\.[0-9]+)?$\"\n};\n\n/**\n * SQL prefix that qualifies a column of the outer RLS row (`\"schema\".\"table\".`),\n * or `\"\"` when the collection is unknown.\n */\nfunction outerQualifier(scope: CompileScope): string {\n const table = scope.outerCollection ? getTableName(scope.outerCollection) : undefined;\n if (!table) return \"\";\n return `\"${schemaOf(scope.outerCollection) ?? \"public\"}\".\"${table}\".`;\n}\n\nfunction schemaOf(collection?: CollectionConfig): string | undefined {\n return (collection as { schema?: string } | undefined)?.schema || undefined;\n}\n\nfunction resolveColumnName(propName: string, collection?: CollectionConfig): string {\n const prop = collection?.properties?.[propName] as Property | undefined;\n if (prop && \"columnName\" in prop && typeof (prop as { columnName?: unknown }).columnName === \"string\") {\n return quoteColumnIdentifier((prop as { columnName: string }).columnName);\n }\n return quoteColumnIdentifier(toSnakeCase(propName));\n}\n\n/**\n * Every PostgreSQL keyword that cannot stand as a bare column reference.\n * Appendix C's two reserved categories — plain \"reserved\", and \"reserved (can\n * be function or type name)\" — since neither may name a column unquoted.\n */\nconst RESERVED_SQL_WORDS = new Set([\n \"all\", \"analyse\", \"analyze\", \"and\", \"any\", \"array\", \"as\", \"asc\", \"asymmetric\", \"authorization\",\n \"binary\", \"both\", \"case\", \"cast\", \"check\", \"collate\", \"collation\", \"column\", \"concurrently\",\n \"constraint\", \"create\", \"cross\", \"current_catalog\", \"current_date\", \"current_role\",\n \"current_schema\", \"current_time\", \"current_timestamp\", \"current_user\", \"default\", \"deferrable\",\n \"desc\", \"distinct\", \"do\", \"else\", \"end\", \"except\", \"false\", \"fetch\", \"for\", \"foreign\", \"freeze\",\n \"from\", \"full\", \"grant\", \"group\", \"having\", \"ilike\", \"in\", \"initially\", \"inner\", \"intersect\",\n \"into\", \"is\", \"isnull\", \"join\", \"lateral\", \"leading\", \"left\", \"like\", \"limit\", \"localtime\",\n \"localtimestamp\", \"natural\", \"not\", \"notnull\", \"null\", \"offset\", \"on\", \"only\", \"or\", \"order\",\n \"outer\", \"overlaps\", \"placing\", \"primary\", \"references\", \"returning\", \"right\", \"select\",\n \"session_user\", \"similar\", \"some\", \"symmetric\", \"system_user\", \"table\", \"tablesample\", \"then\",\n \"to\", \"trailing\", \"true\", \"union\", \"unique\", \"user\", \"using\", \"variadic\", \"verbose\", \"when\",\n \"where\", \"window\", \"with\"\n]);\n\n/** An identifier Postgres reads back unchanged without quotes. */\nconst BARE_IDENTIFIER = /^[a-z_][a-z0-9_$]*$/;\n\n/**\n * Quote a column reference when Postgres would not read the bare name as that\n * column — and only then.\n *\n * Three ways a bare name goes wrong, in ascending order of how long it takes to\n * notice:\n *\n * - **Case.** `columnName` is used verbatim, and `rebase schema introspect`\n * populates it from a live database, so a legacy `\"createdAt\"` column arrives\n * spelled exactly that way. Unquoted, Postgres folds it to `createdat` and\n * `CREATE POLICY` fails with \"column does not exist\" — the collection keeps\n * RLS enabled with no policy, which denies every row.\n * - **Syntax.** A column named `order` or `default` is a syntax error mid-clause.\n * - **Silent rebinding.** `user`, `current_user`, `session_user`, `current_date`\n * and friends are *valid bare expressions*, so the policy compiles, applies,\n * and is reported as a success — while comparing against the connected role\n * or the wall clock instead of the column. Under RLS every request runs as the\n * same `rebase_user` role, so `USING (user = rebase.uid())` is a constant: it\n * denies everything, and its negation admits everything.\n *\n * Only the names that need it are quoted, so an ordinary snake_case policy body\n * is emitted byte-for-byte as before. That keeps generated artifacts and the\n * policies already stored in shipped databases stable — this fix reaches the\n * clauses that were broken and no others.\n */\nfunction quoteColumnIdentifier(name: string): string {\n if (BARE_IDENTIFIER.test(name) && !RESERVED_SQL_WORDS.has(name)) return name;\n return `\"${name.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\nfunction quoteLiteral(value: string | number | boolean | null): string {\n if (value === null) return \"NULL\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") return String(value);\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/** Sorted, single-quoted `ARRAY['a','b']` — matches the generators' output. */\nfunction rolesArraySql(roles: readonly string[]): string {\n return `ARRAY[${[...roles].sort().map(r => `'${r}'`).join(\",\")}]`;\n}\n","import { ANONYMOUS_USER_ID, isAnonymousUid, Entity, PolicyCompareOperator, PolicyExpression, PolicyOperand } from \"@rebasepro/types\";\n\n/**\n * Result of evaluating a policy client-side. `\"unknown\"` means the expression\n * could not be decided without more information — either a raw-SQL escape-hatch\n * node (which the client deliberately never guesses) or a row-column reference\n * with no entity in hand (e.g. list-level gating). Callers decide how to resolve\n * `\"unknown\"`: fail-closed for an enforcement decision, optimistic for pure\n * visibility gating.\n */\nexport type TriState = boolean | \"unknown\";\n\n/**\n * Context for {@link evaluatePolicy}: the acting user (or none) and the row\n * being evaluated (or none, for collection-level gating).\n */\nexport interface PolicyEvalContext {\n /**\n * The current user's id, or null/undefined when no user is signed in.\n *\n * Null here means *anonymous visitor*, not \"server context\" — a client is\n * never the server context. `authUid` operands therefore resolve to\n * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`\n * the database would see for the same request.\n */\n uid?: string | null;\n /** The current user's application roles. */\n roles?: string[];\n /**\n * Whether this session is a GUEST — anonymous sign-in rather than an\n * account. Optional, and absent means \"not a guest\", so a caller that does\n * not know keeps the behaviour it had.\n */\n isAnonymous?: boolean;\n /** The row being evaluated, or null when no specific row is available. */\n entity: Entity | null;\n}\n\n/**\n * Evaluates a {@link PolicyExpression} against a user + row, using three-valued\n * (Kleene) logic so that `\"unknown\"` sub-results propagate soundly.\n *\n * This is the JavaScript twin of {@link policyToPostgres}: both derive from the\n * same expression, so the admin UI matches database enforcement by construction\n * for every non-raw rule.\n */\nexport function evaluatePolicy(expr: PolicyExpression, ctx: PolicyEvalContext): TriState {\n switch (expr.kind) {\n case \"true\":\n return true;\n case \"false\":\n return false;\n case \"and\":\n return kleeneAnd(expr.operands.map(o => evaluatePolicy(o, ctx)));\n case \"or\":\n return kleeneOr(expr.operands.map(o => evaluatePolicy(o, ctx)));\n case \"not\":\n return kleeneNot(evaluatePolicy(expr.operand, ctx));\n case \"compare\":\n return evaluateCompare(expr.op, expr.left, expr.right, ctx);\n case \"rolesOverlap\": {\n const userRoles = ctx.roles ?? [];\n return expr.roles.some(r => r === \"public\" || userRoles.includes(r));\n }\n case \"rolesContain\": {\n const userRoles = ctx.roles ?? [];\n return expr.roles.every(r => r === \"public\" || userRoles.includes(r));\n }\n case \"authenticated\":\n // Every anonymous spelling, matching what this node compiles to in\n // Postgres — the two evaluators disagreeing about who is signed in\n // is the client optimistically rendering a row the database will\n // refuse, or hiding one it would have allowed.\n return ctx.uid != null && !isAnonymousUid(ctx.uid);\n case \"registered\":\n // The same two halves the Postgres compilation has. A client that\n // disagreed with the database here would optimistically render a\n // row the database refuses, or hide one it would have allowed.\n return ctx.uid != null && !isAnonymousUid(ctx.uid) && ctx.isAnonymous !== true;\n case \"serverContext\":\n // A client is never the server context. Postgres decides this by\n // `rebase.uid() IS NULL`, which a client request can never produce:\n // the driver substitutes ANONYMOUS_USER_ID for a missing id.\n return false;\n case \"existsIn\":\n // A membership subquery cannot be run client-side — server-authoritative.\n return \"unknown\";\n case \"raw\":\n // Arbitrary SQL cannot be evaluated client-side — never guess.\n return \"unknown\";\n }\n}\n\n// ── Three-valued logic ───────────────────────────────────────────────\n\nfunction kleeneAnd(values: TriState[]): TriState {\n if (values.some(v => v === false)) return false;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return true;\n}\n\nfunction kleeneOr(values: TriState[]): TriState {\n if (values.some(v => v === true)) return true;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return false;\n}\n\nfunction kleeneNot(value: TriState): TriState {\n if (value === \"unknown\") return \"unknown\";\n return !value;\n}\n\n// ── Comparison ───────────────────────────────────────────────────────\n\ntype ResolvedOperand = { known: false } | { known: true; value: unknown };\n\nfunction resolveOperand(operand: PolicyOperand, ctx: PolicyEvalContext): ResolvedOperand {\n switch (operand.kind) {\n case \"literal\":\n return { known: true, value: operand.value };\n case \"authUid\":\n // The sentinel, not null: `rebase.uid()` is never NULL for a request\n // that came from a client, so comparing against null here would\n // disagree with the database on exactly the rules that test for it\n // (e.g. `rebase.uid() <> 'anonymous'`).\n return { known: true, value: ctx.uid ?? ANONYMOUS_USER_ID };\n case \"authRoles\":\n return { known: true, value: ctx.roles ?? [] };\n case \"field\":\n // Can't resolve a row column without the row.\n if (!ctx.entity) return { known: false };\n return { known: true, value: ctx.entity.values[operand.name] };\n case \"outerField\":\n // Only meaningful inside an `existsIn` subquery (server-authoritative).\n return { known: false };\n case \"authClaim\":\n // Server-authoritative, like `existsIn` and `raw`, and for a reason\n // worth stating: a claim is TEXT, and Postgres compares it to the\n // column after casting it to the column's type. Reproducing that\n // here means reproducing uuid case-folding, numeric widening and\n // the `NULLIF`, in JavaScript, from a `PolicyEvalContext` that does\n // not know the column's type. A second implementation of a cast\n // that is subtly wrong is worse than no answer: it would render a\n // row the database refuses, or hide one it would have allowed.\n return { known: false };\n }\n}\n\nfunction evaluateCompare(\n op: PolicyCompareOperator,\n left: PolicyOperand,\n right: PolicyOperand,\n ctx: PolicyEvalContext\n): TriState {\n const l = resolveOperand(left, ctx);\n const r = resolveOperand(right, ctx);\n if (!l.known || !r.known) return \"unknown\";\n\n const a = l.value;\n const b = r.value;\n\n if (a === null || b === null) {\n // SQL answers NULL for *every* comparison against NULL, and a policy\n // that answers NULL does not grant the row. So the only question here\n // is which JavaScript answer reproduces that outcome.\n //\n // This used to answer `false` for `eq` and `true` for `neq`, which is\n // JavaScript's two-valued reading of a three-valued question.\n //\n // `neq` was a grant the database does not give:\n // `owner_id != rebase.uid()` on a row whose `owner_id` is NULL read as\n // *permitted* in the admin panel and was refused by Postgres — on every\n // row where the column is null, which for a nullable column is usually\n // most of them.\n //\n // `false` for `eq` looked safe, because false denies and NULL denies.\n // It is not, because it does not survive negation: `not(a = NULL)`\n // became `true` while `NOT NULL` stays NULL, so the same grant reappears\n // one operator up. A local answer that is only right in a positive\n // position is not right — it just moves.\n //\n // \"unknown\" is what SQL actually says, it composes correctly through\n // Kleene negation, and enforcement callers already resolve it\n // fail-closed. Both were found by the exhaustive Postgres differential\n // in `policy-agreement-exhaustive.test.ts`, the second only after the\n // first was fixed.\n return \"unknown\";\n }\n\n if (op === \"eq\") return a === b;\n if (op === \"neq\") return a !== b;\n\n if (typeof a === \"string\" && typeof b === \"string\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n if (typeof a === \"number\" && typeof b === \"number\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n if (typeof a === \"bigint\" && typeof b === \"bigint\") {\n if (op === \"lt\") return a < b;\n if (op === \"lte\") return a <= b;\n if (op === \"gt\") return a > b;\n if (op === \"gte\") return a >= b;\n }\n\n return \"unknown\";\n}\n","import { AuthState, Entity, CollectionConfig, SecurityOperation, SecurityRule, User } from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"./policy/securityRuleToConditions\";\nimport { evaluatePolicy, PolicyEvalContext, TriState } from \"./policy/evaluatePolicy\";\n\n/**\n * Minimal auth context for permission checking.\n * Only requires the user object — avoids forcing callers to construct\n * a full AuthController just to check permissions.\n *\n * An alias, not a second definition: {@link AuthState} in `@rebasepro/types` is\n * the same shape and is what `dynamicProps` and the JSON-Logic condition context\n * now take, so declaring it twice would be the `WhereFilterOp` mistake again —\n * two copies that agree only by luck.\n */\nexport type AuthContext<USER extends User = User> = AuthState<USER>;\n\n/**\n * How to resolve a policy result that cannot be decided client-side (a raw-SQL\n * escape-hatch rule, or a row-column reference with no row in hand).\n *\n * - `\"allow\"` (default): optimistic — used for admin-UI gating, where Postgres\n * remains the authoritative gate and hiding a working action is worse than\n * showing one the server may reject.\n * - `\"deny\"`: fail-closed — used by real enforcement callers (e.g. a driver\n * applying policies in-process), so an undecidable rule never silently allows.\n */\nexport type UnknownResolution = \"allow\" | \"deny\";\n\n/**\n * Which half of a rule to evaluate.\n *\n * Postgres evaluates `USING` against the row as it is *now* and `WITH CHECK`\n * against the row as it *will be*, both inside the transaction. A driver\n * enforcing an update in-process has two different rows in hand and therefore\n * needs to ask the two questions separately — asking one question about one row\n * either checks the new values against the old row's ownership or the reverse.\n *\n * - `\"both\"` (default): what a single-row decision means (`USING ∧ WITH CHECK`).\n * - `\"using\"`: the read/target clause only — ask it about the stored row.\n * - `\"withCheck\"`: the write clause only — ask it about the row being written.\n *\n * Rule *selection* is unaffected: the target operation still decides which rules\n * apply, so `\"using\"` on an `update` evaluates the update rules' USING clause,\n * not the delete rules'.\n */\nexport type PolicyClauses = \"both\" | \"using\" | \"withCheck\";\n\nexport interface CheckOperationOptions {\n onUnknown?: UnknownResolution;\n clauses?: PolicyClauses;\n}\n\n/** Combine clause results with AND under three-valued (Kleene) logic. */\nfunction kleeneAnd(values: TriState[]): TriState {\n if (values.some(v => v === false)) return false;\n if (values.some(v => v === \"unknown\")) return \"unknown\";\n return true;\n}\n\n/** The operations a rule covers, mirroring the Postgres generator's resolution. */\nfunction ruleOperations(rule: SecurityRule): readonly SecurityOperation[] {\n return rule.operations && rule.operations.length > 0\n ? rule.operations\n : [rule.operation ?? \"all\"];\n}\n\nfunction ruleApplies(rule: SecurityRule, targetOperation: SecurityOperation): boolean {\n const ops = ruleOperations(rule);\n return ops.includes(targetOperation) || ops.includes(\"all\");\n}\n\n/**\n * Evaluate a single rule for one operation, returning a tri-state.\n *\n * A `null` clause (the rule contributes no condition for a required clause)\n * denies — matching Postgres, which emits `USING (false)` / `WITH CHECK (false)`\n * in that case. USING applies to SELECT/UPDATE/DELETE; WITH CHECK to\n * INSERT/UPDATE; both must pass for UPDATE.\n */\nfunction evaluateRuleForOperation(\n rule: SecurityRule,\n ctx: PolicyEvalContext,\n targetOperation: SecurityOperation,\n clauses: PolicyClauses = \"both\"\n): TriState {\n const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);\n const clause = (expr: typeof usingExpr): TriState => expr === null ? false : evaluatePolicy(expr, ctx);\n\n const needsUsing = targetOperation !== \"insert\" && clauses !== \"withCheck\";\n const needsWithCheck = (targetOperation === \"insert\" || targetOperation === \"update\") && clauses !== \"using\";\n\n const results: TriState[] = [];\n if (needsUsing) results.push(clause(usingExpr));\n if (needsWithCheck) results.push(clause(withCheckExpr));\n return kleeneAnd(results);\n}\n\nfunction resolveTriState(value: TriState, onUnknown: UnknownResolution): boolean {\n if (value === \"unknown\") return onUnknown === \"allow\";\n return value;\n}\n\n/**\n * Decide whether an operation is permitted for a user on a (possibly null) row,\n * by evaluating the collection's security rules with the shared policy model —\n * the same model compiled to Postgres RLS DDL, so the decision matches database\n * enforcement for every non-raw rule.\n *\n * Engine-independent by design. `securityRules` are a declaration about the\n * data, not about Postgres: the engine decides *who* enforces them (Postgres\n * compiles them to RLS DDL, a document driver applies them in-process), never\n * *whether* they hold. Gating this function on the engine's `supportsRLS`\n * capability is what made every `{ onUnknown: \"deny\" }` call site in the Mongo\n * driver return `true` before it evaluated anything — and it did so only for\n * collections that spelled their engine out, so declaring `engine: \"mongodb\"`\n * was what switched authorization off.\n *\n * @param options.onUnknown how to treat rules that cannot be decided\n * client-side (raw SQL, or row predicates with no row). Defaults to `\"allow\"`\n * for optimistic UI gating; enforcement callers should pass `\"deny\"`.\n * @param options.clauses which half of each rule to evaluate. See\n * {@link PolicyClauses}; defaults to `\"both\"`.\n */\nexport function checkOperation<M extends Record<string, unknown>, USER extends User>(\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n entity: Entity<M> | null,\n targetOperation: SecurityOperation,\n options?: CheckOperationOptions\n): boolean {\n const onUnknown = options?.onUnknown ?? \"allow\";\n const clauses = options?.clauses ?? \"both\";\n const securityRules = collection.securityRules;\n if (!securityRules || securityRules.length === 0) {\n return true;\n }\n\n const applicableRules = securityRules.filter((r: SecurityRule) => ruleApplies(r, targetOperation));\n if (applicableRules.length === 0) return false;\n\n const ctx: PolicyEvalContext = {\n uid: authContext.user?.uid,\n roles: authContext.user?.roles ?? [],\n entity\n };\n\n let grantedByPermissive = false;\n let deniedByRestrictive = false;\n let hasPermissive = false;\n\n for (const rule of applicableRules) {\n const mode = rule.mode || \"permissive\";\n const passed = resolveTriState(evaluateRuleForOperation(rule, ctx, targetOperation, clauses), onUnknown);\n\n if (mode === \"restrictive\") {\n if (!passed) {\n deniedByRestrictive = true;\n break;\n }\n } else {\n hasPermissive = true;\n if (passed) grantedByPermissive = true;\n }\n }\n\n if (deniedByRestrictive) return false;\n return hasPermissive ? grantedByPermissive : false;\n}\n\nexport function canReadCollection<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>\n ): boolean {\n return checkOperation(collection, authContext, null, \"select\");\n}\n\nexport function canEditEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"update\");\n}\n\nexport function canCreateEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"insert\");\n}\n\nexport function canDeleteEntity<M extends Record<string, unknown>, USER extends User>\n (\n collection: CollectionConfig<M>,\n authContext: AuthContext<USER>,\n path: string,\n entity: Entity<M> | null\n ): boolean {\n return checkOperation(collection, authContext, entity, \"delete\");\n}\n","import {\n CollectionConfig,\n FirebaseCollectionConfig,\n FirebaseProperties,\n FirebaseProperty,\n InferEntityType,\n MongoDBCollectionConfig,\n MongoProperties,\n MongoProperty,\n PostgresCollectionConfig,\n PostgresProperties,\n PostgresProperty,\n Properties,\n Property,\n StrictProperties,\n User,\n resolveResourceRefs,\n type ResourceRef\n} from \"@rebasepro/types\";\n\n\n// ── defineCollection ─────────────────────────────────────────────────────\n// A smarter builder that uses `const` type-parameter inference (TS 5.0+)\n// to capture literal property types automatically. This gives you\n// autocomplete on `display.title`, `sort`, `propertiesOrder`, `fixedFilter`,\n// callbacks, etc. — without writing `as const` or passing manual generics.\n\n/** The engines a collection can declare. `postgres` when it says nothing. */\ntype CollectionEngine = \"postgres\" | \"firestore\" | \"mongodb\";\n\n/**\n * The concrete collection type an `engine` selects.\n *\n * This builder used to be three overloads — one per engine — and overload\n * resolution is what made its errors unreadable. When no overload matches,\n * TypeScript emits **one** diagnostic at the call site listing each overload's\n * *first* failure, so a misspelled key on a Postgres collection came back as\n * three paragraphs of `No overload matches this call. Overload 1 of 3 … Overload\n * 3 of 3, '(collection: Omit<MongoDBCollectionConfig<…>>)'` — pointing at\n * `defineCollection(` and blaming a database the project does not use.\n *\n * One signature, with the engine as a type parameter, reports the error at the\n * key instead. Same fix as `@rebasepro/cms-types`, and deliberately the same\n * shape: this is the builder a headless (`--headless`) scaffold, `rebase schema\n * introspect` output and the example app's own collections use, so the two must\n * not diverge.\n */\ntype CollectionConfigForEngine<E, P, USER extends User> =\n E extends \"firestore\" ? FirebaseCollectionConfig<EntityShapeOf<P>, USER>\n : E extends \"mongodb\" ? MongoDBCollectionConfig<EntityShapeOf<P>, USER>\n : PostgresCollectionConfig<EntityShapeOf<P>, USER>;\n\n/**\n * `InferEntityType`, tolerant of a property map that has an error in it.\n *\n * The key set has to survive a bad property, or one mistake hides every other\n * check that reads it. See `KEYS` on the signature below.\n */\ntype EntityShapeOf<P> = InferEntityType<{\n [K in keyof P]: P[K] extends Property ? P[K] : Property;\n}>;\n\n/** The property union an engine admits — the engine gate, as a type. */\ntype PropertyForEngine<E> =\n E extends \"firestore\" ? FirebaseProperty\n : E extends \"mongodb\" ? MongoProperty\n : PostgresProperty;\n\n/** {@link PropertyForEngine} as a property map, for the `P` constraint. */\ntype PropertiesForEngine<E> =\n E extends \"firestore\" ? FirebaseProperties\n : E extends \"mongodb\" ? MongoProperties\n : PostgresProperties;\n\n/**\n * Define a collection with full type inference. Postgres unless `engine` says\n * otherwise.\n *\n * The `const P` generic captures literal property types from your\n * `properties` object, so every key that names a property — a security rule's\n * `ownerField`, a relation's `localKey`, an entity callback's `value` — is\n * checked against the collection's own property names rather than `string`.\n *\n * This is the builder for a project with no admin panel. One with an admin\n * panel wants `defineCollection` from `@rebasepro/cms-types`, which is the same\n * function with the `admin` block type-checked.\n *\n * @example\n * ```ts\n * const products = defineCollection({\n * name: \"Products\",\n * slug: \"products\",\n * table: \"products\",\n * properties: {\n * name: { name: \"Name\", type: \"string\", validation: { required: true } },\n * price: { name: \"Price\", type: \"number\" },\n * },\n * securityRules: [{ operation: \"select\", access: \"public\" }]\n * });\n * ```\n *\n * @group Builder\n */\nexport function defineCollection<\n const E extends CollectionEngine = \"postgres\",\n /**\n * The properties, **constrained**. This is what checks them, and what\n * supplies the contextual type inside them: without a constraint the\n * parameter of an inline `callbacks: { beforeSave: ({ value }) => … }` has\n * nothing to be typed from, and TypeScript reports an implicit `any` on a\n * callback the author wrote correctly.\n */\n const P extends PropertiesForEngine<E> & Properties = PropertiesForEngine<E> & Properties,\n /**\n * The properties again, **unconstrained**, and this is why there are two.\n *\n * A constraint TypeScript cannot satisfy is one it silently falls back\n * from: one property with a bad `defaultValue` made `P` become\n * `PostgresProperties`, the entity shape become `Record<string, unknown>`,\n * and every key that is checked against the property names — `display.title`,\n * `propertiesOrder`, `sort` — widen to `string` and stop being checked.\n *\n * `KEYS` has no constraint to fall back from, so `keyof KEYS` survives a bad\n * property and the rest of the collection is still checked against the real\n * key set.\n */\n const KEYS = Properties,\n USER extends User = User\n>(\n collection: Omit<CollectionConfigForEngine<E, KEYS, USER>, \"properties\" | \"engine\" | \"dataSource\">\n & {\n engine?: E;\n properties: StrictProperties<P, PropertyForEngine<E>> & KEYS;\n dataSource?: ResourceRef;\n }\n): CollectionConfigForEngine<E, KEYS, USER> & { properties: KEYS };\n\n/**\n * At runtime this is a plain identity function: a resource handle written where\n * a key belongs — `dataSource: analytics` — becomes its key, so past this point\n * a collection is plain data. The signature above is the rest of the point.\n * @group Builder\n */\nexport function defineCollection(\n collection: Omit<CollectionConfig, \"dataSource\"> & { dataSource?: ResourceRef }\n): CollectionConfig {\n return resolveResourceRefs(collection) as CollectionConfig;\n}\n\n","import { ArrayProperty, EntityValues, StorageConfig, StorageSource, StorageSourceRegistry, StringProperty, UploadedFileContext } from \"@rebasepro/types\";\nimport { randomString } from \"@rebasepro/utils\";\n\n/**\n * Resolve the {@link StorageSource} to use for a property, given the key\n * referenced by `StorageConfig.storageSource`.\n *\n * Resolution priority:\n * 1. No `sourceKey` → the default source (backward compatible).\n * 2. An explicit {@link StorageSourceRegistry} (e.g. `client.storageRegistry`).\n * 3. A `sources` lookup map (e.g. the `StorageSourcesContext`).\n * 4. Fall back to the default source.\n *\n * Shared by the upload hook, the markdown editor, and the read-only previews\n * so the resolution logic lives in one place.\n *\n * @group Storage\n */\nexport function resolveStorageSource(params: {\n /** Key from `StorageConfig.storageSource`. */\n sourceKey?: string | null;\n /** Built sources keyed by storage-source key (e.g. from context). */\n sources?: Record<string, StorageSource>;\n /** Optional explicit registry — takes precedence over `sources`. */\n registry?: StorageSourceRegistry;\n /** Default source, used when no key is set or the key cannot be resolved. */\n defaultSource: StorageSource;\n}): StorageSource {\n const { sourceKey, sources, registry, defaultSource } = params;\n if (!sourceKey) return defaultSource;\n if (registry) return registry.getOrDefault(sourceKey);\n const fromSources = sources?.[sourceKey];\n if (fromSources) return fromSources;\n return defaultSource;\n}\n\ninterface ResolveFilenameStringParams<M extends Record<string, unknown>> {\n input: string | ((context: UploadedFileContext) => (Promise<string> | string));\n storage: StorageConfig;\n values: EntityValues<M>;\n entityId?: string | number;\n path?: string;\n property: StringProperty | ArrayProperty,\n file: File;\n propertyKey: string;\n}\n\nexport async function resolveStorageFilenameString<M extends Record<string, unknown>>(\n {\n input,\n storage,\n values,\n entityId,\n path,\n property,\n file,\n propertyKey\n }: ResolveFilenameStringParams<M>): Promise<string> {\n let result;\n\n if (typeof input === \"function\") {\n result = await input({\n path,\n entityId,\n values,\n property,\n file,\n storage,\n propertyKey\n });\n if (!result)\n console.warn(\"Storage callback returned empty result. Using default name value\")\n } else {\n result = replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n });\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n\ninterface ResolveStoragePathStringParams<M extends Record<string, unknown>> {\n input: string | ((context: UploadedFileContext) => string);\n storage: StorageConfig;\n values: EntityValues<M>;\n entityId?: string | number;\n path?: string;\n property: StringProperty | ArrayProperty;\n file: File;\n propertyKey: string;\n}\n\nexport function resolveStoragePathString<M extends Record<string, unknown>>(\n {\n input,\n storage,\n values,\n entityId,\n path,\n property,\n file,\n propertyKey\n }: ResolveStoragePathStringParams<M>): string {\n let result;\n if (typeof input === \"function\") {\n result = input({\n path,\n entityId,\n values,\n property,\n file,\n storage,\n propertyKey\n });\n if (!result)\n console.warn(\"Storage callback returned empty result. Using default name value\")\n } else {\n result = replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n });\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n\ninterface Placeholders {\n file: File;\n input: string;\n entityId?: string | number;\n propertyKey: string;\n path?: string;\n}\n\nfunction replacePlaceholders({\n file,\n input,\n entityId,\n propertyKey,\n path\n}: Placeholders) {\n const ext = file.name.split(\".\").pop();\n let result = input\n .replace(\"{propertyKey}\", propertyKey)\n .replace(\"{rand}\", randomString())\n .replace(\"{file}\", file.name)\n .replace(\"{file.type}\", file.type);\n if (entityId) {\n result = result.replace(\"{entityId}\", String(entityId));\n }\n if (path) {\n result = result.replace(\"{path}\", path);\n }\n if (ext) {\n result = result.replace(\"{file.ext}\", ext);\n const name = file.name.replace(`.${ext}`, \"\");\n result = result.replace(\"{file.name}\", name)\n }\n\n if (!result)\n result = randomString() + \"_\" + file.name;\n\n return result;\n}\n","import { CollectionCallbacks, Properties, RebaseCallContext } from \"@rebasepro/types\";\n\n/**\n * Context passed to entity lifecycle callbacks.\n * @group Models\n */\nexport type EntityCallbackContext = RebaseCallContext;\n\n\n/**\n * Helper function to recursively check if there are any callbacks in the properties.\n */\nfunction hasPropertyCallbacks(properties: Properties, callbackName: \"afterRead\" | \"beforeSave\"): boolean {\n if (!properties) return false;\n for (const property of Object.values(properties)) {\n if (property.callbacks?.[callbackName]) return true;\n if (property.type === \"map\" && property.properties) {\n if (hasPropertyCallbacks(property.properties, callbackName)) return true;\n } else if (property.type === \"array\" && property.of) {\n const ofs = Array.isArray(property.of) ? property.of : [property.of];\n for (const of of ofs) {\n if (of.callbacks?.[callbackName]) return true;\n if (of.type === \"map\" && of.properties && hasPropertyCallbacks(of.properties, callbackName)) return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Recursively process properties to apply field-level hooks.\n */\nasync function processProperties(\n properties: Properties,\n values: Record<string, unknown>,\n previousValues: Record<string, unknown>,\n propsContext: unknown,\n callbackName: \"afterRead\" | \"beforeSave\"\n): Promise<Record<string, unknown>> {\n if (!values || typeof values !== \"object\") return values;\n\n const result = { ...values };\n\n for (const [key, property] of Object.entries(properties)) {\n if (result[key] === undefined) continue;\n\n let currentValue = result[key];\n const previousValue = previousValues?.[key];\n\n // 1. Array Property\n if (property.type === \"array\" && Array.isArray(currentValue)) {\n // We only support traversing single-type arrays for hooks currently to avoid complex union matching\n if (property.of && !Array.isArray(property.of)) {\n currentValue = await Promise.all(currentValue.map(async (item, index) => {\n const prevItem = Array.isArray(previousValue) ? previousValue[index] : undefined;\n // Mock a properties object to process a single item\n const singlePropData = { \"_tmp\": property.of } as Properties;\n const res = await processProperties(singlePropData, { \"_tmp\": item }, { \"_tmp\": prevItem }, propsContext, callbackName);\n return res[\"_tmp\"];\n }));\n }\n }\n // 2. Map Property\n else if (property.type === \"map\" && property.properties && typeof currentValue === \"object\") {\n currentValue = await processProperties(property.properties, currentValue as Record<string, unknown>, (previousValue ?? {}) as Record<string, unknown>, propsContext, callbackName);\n }\n\n // 3. Property's own callback\n if (property.callbacks?.[callbackName]) {\n\n const cbRes = await Promise.resolve(property.callbacks[callbackName]({\n ...(propsContext as Record<string, unknown>),\n value: currentValue,\n previousValue\n } as never));\n if (cbRes !== undefined) {\n currentValue = cbRes;\n }\n }\n\n result[key] = currentValue;\n }\n return result;\n}\n\n/**\n * Helper function to extract field-level PropertyCallbacks from a properties schema\n * and wrap them into an CollectionCallbacks object recursively.\n */\nexport const buildPropertyCallbacks = (properties: Properties): CollectionCallbacks | undefined => {\n if (!properties) return undefined;\n\n const propertyCallbacks: CollectionCallbacks = {};\n\n if (hasPropertyCallbacks(properties, \"afterRead\")) {\n propertyCallbacks.afterRead = async (props) => {\n const row = props.row;\n const processedValues = await processProperties(\n properties,\n row,\n row,\n props as unknown,\n \"afterRead\"\n );\n return { ...props.row, ...processedValues };\n };\n }\n\n if (hasPropertyCallbacks(properties, \"beforeSave\")) {\n propertyCallbacks.beforeSave = async (props) => {\n return await processProperties(\n properties,\n props.values as Record<string, unknown>,\n (props.previousValues ?? {}) as Record<string, unknown>,\n props as unknown,\n \"beforeSave\"\n );\n };\n }\n\n return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : undefined;\n};\n","import { RebaseApiError } from \"@rebasepro/types\";\n\n/**\n * The code a write carries when a collection callback rejected it and did not\n * say how. Distinct from `INVALID_INPUT`, which the framework's own validation\n * raises: this one means *your* rule refused, so the message is the author's.\n *\n * `details.stage` names which callback refused — `beforeSave`, `beforeDelete`,\n * `afterSave` or `afterDelete`. An `after*` hook runs inside the write's\n * transaction, so a throw there rolls the row back too; the caller is told the\n * write did not happen and which hook decided that.\n */\nexport const CALLBACK_REJECTED = \"CALLBACK_REJECTED\";\n\n/**\n * Turn whatever a user callback threw into something the API layer can answer\n * with.\n *\n * ### Why a plain `throw` has to mean 400\n *\n * Both `docs/collections/callbacks.md` (\"Throw an error to **block the save**\")\n * and `docs/backend/hooks.md` (\"the operation is rejected with an HTTP 400\n * error response\") promised this, and neither delivered it: an `Error` thrown\n * from `beforeSave` reached the client as\n *\n * 500 {\"error\":{\"message\":\"Internal Server Error\",\"code\":\"INTERNAL_ERROR\"}}\n *\n * with the author's message visible only in the server log, because the error\n * normalizer masks 5xx bodies — correctly, since a 500 is by definition\n * something the caller must not be told about.\n *\n * But a callback is not the server failing. It is the application speaking, in\n * code its author wrote, about a request its author judged invalid. The\n * conservative reading — \"an unrecognised throw might be a real bug, so 500\" —\n * costs every validation rule its message and makes the documented example\n * wrong. A rule that wants a 500 can still raise one explicitly.\n *\n * ### Why `after*` comes through here too\n *\n * `afterSave` and `afterDelete` run inside the write's transaction and are\n * awaited, so a throw in one aborts the transaction: the row is not there when\n * the request ends. Left unconverted, the caller saw a 500 for a write that a\n * rule deliberately undid, and had no way to tell that from a database outage.\n * Converted, it is the same 400 `CALLBACK_REJECTED` a `before*` hook produces,\n * with `stage` naming the hook that refused.\n *\n * ### What passes through untouched\n *\n * Anything that already carries a status: `RebaseApiError` from\n * `@rebasepro/types` (the browser-safe class a `config/collections/*.ts` file\n * can import — the collection file is bundled into the admin SPA, so it may not\n * import the server package), and the server's own `ApiError`, recognised\n * structurally rather than by `instanceof` because a monorepo can resolve two\n * copies of a package and `instanceof` is false across them.\n *\n * @param error What the callback threw.\n * @param stage The callback name, for the log line.\n * @param path The collection path, for the log line.\n */\nexport function toCallbackError(error: unknown, stage: string, path: string): unknown {\n if (error !== null && typeof error === \"object\") {\n const carried = error as { status?: unknown; statusCode?: unknown };\n // Already an answerable HTTP outcome — the author chose the status.\n if (typeof carried.statusCode === \"number\" || typeof carried.status === \"number\") {\n return error;\n }\n }\n\n const message = error instanceof Error\n ? error.message\n : typeof error === \"string\" ? error : `${stage} rejected the write`;\n\n return new RebaseApiError(message, {\n status: 400,\n code: CALLBACK_REJECTED,\n details: { stage, path },\n cause: error\n });\n}\n\n/**\n * The refusal a callback expresses by returning `false` rather than throwing.\n *\n * `beforeDelete` is typed `boolean | void` and documented as \"return false or\n * throw to block deletion\". Returning `false` did stop the delete — and then the\n * route answered `204 No Content`, which says the row is gone. The admin panel\n * removed it from the list, a client that trusted the status dropped it from its\n * cache, and the next reload brought it back. A veto that reports success is\n * worse than no veto.\n *\n * 403, not the 400 a throw produces: a throw carries the author's message and\n * reads as \"this input is wrong\", while `false` is a flat refusal with no\n * explanation — the server understood the request and will not perform it. The\n * code is the same either way, so a client can handle both in one branch.\n *\n * @param stage The callback name, for `details.stage`.\n * @param path The collection path, for `details.path`.\n */\nexport function callbackRefusal(stage: string, path: string): RebaseApiError {\n return new RebaseApiError(`${stage} refused the operation`, {\n status: 403,\n code: CALLBACK_REJECTED,\n details: { stage, path }\n });\n}\n","/**\n * The one reading of `collection.tenant`.\n *\n * Four things have to agree for a tenant-scoped collection to work — the\n * column, the RLS policy, the value stamped on insert and the index — and\n * before this they were four hand-written declarations that nothing compared.\n * The three that are schema become a {@link SecurityRule} and a column effect\n * derived here and in `planSchema`; the fourth, the write path, is\n * {@link resolveTenantWrite}.\n *\n * Everything in this module is pure. The policy it builds is a `SecurityRule`\n * like any other, which is what makes `db push`, the doctor, boot-ensure, the\n * drift detector and the Studio treat the tenancy policy as what it is —\n * generated, named, and recognisable — rather than as somebody's hand-written\n * SQL that a push should offer to drop.\n */\nimport {\n DEFAULT_TENANT_BYPASS_ROLES,\n isTenantClaimSource,\n policy,\n type CollectionConfig,\n type CollectionTenantConfig,\n type EntityStatus,\n type PolicyExpression,\n type SecurityRule\n} from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\n\n/**\n * The collection's tenancy declaration, or nothing.\n *\n * Read through this rather than off the object, so the one shape check —\n * `tenant` is an object carrying a `field` and a `from` — is in one place. A\n * config that is *wrong* is refused by `validateCollectionConfig` with a\n * message; this is only asking whether there is one.\n */\nexport function getTenantConfig(collection: CollectionConfig | undefined): CollectionTenantConfig | undefined {\n const tenant = (collection as { tenant?: unknown } | undefined)?.tenant as CollectionTenantConfig | undefined;\n if (!tenant || typeof tenant !== \"object\") return undefined;\n if (typeof tenant.field !== \"string\" || !tenant.field) return undefined;\n if (!tenant.from || typeof tenant.from !== \"object\") return undefined;\n return tenant;\n}\n\n/** The roles tenancy does not apply to, defaulted. */\nexport function tenantBypassRoles(tenant: CollectionTenantConfig): readonly string[] {\n return tenant.bypassRoles ?? DEFAULT_TENANT_BYPASS_ROLES;\n}\n\n/**\n * The name of the policy a tenant declaration compiles to.\n *\n * Explicit — not a `getPolicyNameHash` of the rule — precisely because the\n * rule's *body* is compiled with more information in some callers than in\n * others (`planSchema` can resolve a relation's target collection and so knows\n * the column's type; the Studio, asking only for names, cannot). A hashed name\n * would then differ between the two, and the same policy would read as drift.\n * A frozen identifier: see `contracts/derived-names.txt`.\n */\nexport function tenantPolicyName(tableName: string): string {\n return `${tableName}_tenant_scope`;\n}\n\n/** The `reason` on the index a tenant column gets. Rendered into `schema.sql`. */\nexport const TENANT_INDEX_REASON = \"tenant scope\";\n\n/**\n * The condition a tenant declaration means, as a policy expression.\n *\n * `serverContext()` first, for the same reason every injected baseline rule\n * carries it: the trusted plane runs migrations, the auth flows and the boot,\n * and a restrictive policy that excluded it would not protect a tenant, it\n * would stop the server from starting.\n *\n * Then the bypass roles, then the tenancy test itself — a claim comparison or a\n * correlated `EXISTS` over the membership table, which are the two ways a\n * deployment answers \"which tenant is this caller in\".\n */\nexport function tenantScopeExpression(tenant: CollectionTenantConfig): PolicyExpression {\n const match: PolicyExpression = isTenantClaimSource(tenant.from)\n ? policy.compare(policy.field(tenant.field), \"eq\", policy.authClaim(tenant.from.claim))\n : policy.existsIn({\n collection: tenant.from.membership.collection,\n where: policy.and(\n policy.compare(\n policy.field(tenant.from.membership.tenantField),\n \"eq\",\n policy.outerField(tenant.field)\n ),\n policy.compare(\n policy.field(tenant.from.membership.userField),\n \"eq\",\n policy.authUid()\n )\n )\n });\n\n const bypass = tenantBypassRoles(tenant);\n return bypass.length > 0\n ? policy.or(policy.serverContext(), policy.rolesOverlap(bypass), match)\n : policy.or(policy.serverContext(), match);\n}\n\n/**\n * The rule a tenant declaration compiles to, or nothing when there is none.\n *\n * **Restrictive**, and that is the whole design. A restrictive policy is ANDed\n * with every other policy on the table, so tenancy narrows what the\n * collection's own `securityRules` allow and can never widen it. A permissive\n * one would OR with them, and a single `access: \"public\"` rule elsewhere in the\n * file would take the entire tenancy boundary off without contradicting\n * anything a reader could see.\n *\n * One rule with `operation: \"all\"` rather than four with `operations: [...]`:\n * `FOR ALL` gives Postgres the USING clause for SELECT/UPDATE/DELETE and the\n * WITH CHECK clause for INSERT/UPDATE, which is exactly the coverage wanted,\n * as one policy with one name instead of four.\n */\nexport function buildTenantSecurityRule(collection: CollectionConfig): SecurityRule | undefined {\n const tenant = getTenantConfig(collection);\n if (!tenant) return undefined;\n const expression = tenantScopeExpression(tenant);\n return {\n name: tenantPolicyName(getTableName(collection)),\n mode: \"restrictive\",\n operation: \"all\",\n condition: expression,\n check: expression\n };\n}\n\n// ── The write path ───────────────────────────────────────────────────────────\n\n/** Why a write was refused by tenancy. */\nexport interface TenantWriteRefusal {\n code: \"TENANT_REQUIRED\" | \"TENANT_MISMATCH\" | \"TENANT_IMMUTABLE\";\n /** The property, for a `violations` entry and for the message. */\n field: string;\n message: string;\n}\n\n/** What {@link resolveTenantWrite} decided. */\nexport type TenantWriteDecision =\n /** The values to write, with the tenant stamped if it was missing. */\n | { values: Record<string, unknown>; refusal?: undefined }\n | { refusal: TenantWriteRefusal; values?: undefined };\n\nexport interface TenantWriteInput {\n tenant: CollectionTenantConfig;\n /** The write's values, after defaults and hooks. */\n values: Record<string, unknown>;\n status: EntityStatus;\n /**\n * Every tenant the caller may write into.\n *\n * One entry for a claim, however many memberships they hold for the\n * membership form, and none for a caller carrying neither.\n */\n callerTenants: readonly unknown[];\n /**\n * Whether `callerTenants` is the whole list.\n *\n * A membership lookup is capped — a caller with more memberships than the\n * cap would otherwise make every write of theirs a large read. When the cap\n * is hit this is `false`, and a value that is not in the list is **let\n * through** rather than refused: the list is no longer evidence of absence,\n * and the policy's `WITH CHECK` is what actually decides. The API check is\n * an earlier, clearer refusal of the same writes, never a second authority.\n */\n callerTenantsComplete?: boolean;\n /**\n * True when tenancy does not apply to this caller — a bypass role, or the\n * trusted server context. The same set the policy lets through, so the API\n * and the database refuse the same writes.\n */\n bypass: boolean;\n /** The row's current values, on an update. */\n previousValues?: Record<string, unknown>;\n /** The collection slug, for the message. */\n slug: string;\n}\n\n/**\n * An id, however it arrived.\n *\n * A tenant field may be a `belongsTo` relation or a `reference`, and those\n * arrive over the wire as `{ id }` envelopes as often as bare ids. Comparing\n * the envelope to a bare id would refuse every correct write with\n * `TENANT_MISMATCH`, which is the most confusing possible failure — the caller\n * sent exactly the tenant they belong to.\n */\nfunction tenantIdOf(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"object\") {\n const id = (value as { id?: unknown }).id;\n return id === undefined ? value : id;\n }\n return value;\n}\n\n/**\n * Compare two tenant ids as the database will.\n *\n * Stringified, because JSON has one number type and Postgres has several: a\n * caller sending `\"42\"` for a `bigint` tenant column is writing the same row as\n * one sending `42`, and Postgres agrees after the cast. Refusing one of them\n * would be an API rule the database does not have.\n */\nfunction sameTenant(a: unknown, b: unknown): boolean {\n if (a === null || a === undefined || b === null || b === undefined) return false;\n return String(tenantIdOf(a)) === String(tenantIdOf(b));\n}\n\n/**\n * Stamp, or refuse, the tenant on a write.\n *\n * Three refusals, and each exists because the alternative lands somewhere\n * worse:\n *\n * - **`TENANT_REQUIRED`** — the caller has no tenant, or belongs to several and\n * named none. Stamping a guess would put the row in the wrong tenant; letting\n * it through would write a NULL into a `NOT NULL` column and surface as a\n * 23502 naming a column the caller never wrote.\n * - **`TENANT_MISMATCH`** — the caller named a tenant that is not theirs. The\n * database refuses this too, through the policy's `WITH CHECK`, but as a\n * 42501 \"new row violates row-level security policy\" with no mention of which\n * field or why. Refused here so the answer names the field.\n * - **`TENANT_IMMUTABLE`** — an update that moves a row to another tenant. RLS\n * would allow it whenever the caller belongs to both, and it is almost never\n * what anybody meant: it takes the row out of one tenant's history and drops\n * it into another's, with no trace on either side. A deliberate move is a\n * `bypassRoles` operation.\n *\n * A bypass caller is exempt from all three: they are trusted across tenants by\n * declaration, and stamping their write would silently confine a support\n * operator's row to whichever tenant they happen to carry.\n */\nexport function resolveTenantWrite(input: TenantWriteInput): TenantWriteDecision {\n const { tenant, values, status, callerTenants, bypass, previousValues, slug } = input;\n const field = tenant.field;\n const complete = input.callerTenantsComplete !== false;\n /** Is `value` one the caller may write? Unknown counts as yes — see `callerTenantsComplete`. */\n const callerHas = (value: unknown): boolean =>\n callerTenants.some(t => sameTenant(t, value)) || !complete;\n\n if (bypass) return { values };\n\n const provided = values[field];\n const creating = status !== \"existing\";\n\n if (!creating) {\n // An update that does not mention the field cannot move the row, and\n // the row's own tenant is already what RLS checked to let the update\n // through. Nothing to do.\n if (provided === undefined) return { values };\n\n const previous = previousValues?.[field];\n if (previous !== undefined && !sameTenant(provided, previous)) {\n return {\n refusal: {\n code: \"TENANT_IMMUTABLE\",\n field,\n message:\n `'${field}' is the tenant '${slug}' rows belong to, and a row cannot change tenant. ` +\n `This update would move it from '${String(tenantIdOf(previous))}' to ` +\n `'${String(tenantIdOf(provided))}'. Create the row in the other tenant and delete ` +\n \"this one, or perform the move with a role listed in `tenant.bypassRoles`.\"\n }\n };\n }\n if (previous === undefined && !callerHas(provided)) {\n return { refusal: mismatch(field, slug, provided, callerTenants) };\n }\n return { values };\n }\n\n if (provided === undefined || provided === null || provided === \"\") {\n if (callerTenants.length === 1) {\n return { values: { ...values, [field]: tenantIdOf(callerTenants[0]) } };\n }\n return {\n refusal: {\n code: \"TENANT_REQUIRED\",\n field,\n message: callerTenants.length === 0\n ? `'${slug}' is scoped to a tenant and this request carries none, so there is nothing ` +\n `to write into '${field}'. ` + sourceHint(tenant)\n : `'${slug}' is scoped to a tenant and this caller belongs to ${callerTenants.length} ` +\n `of them, so '${field}' cannot be inferred. Send it on the write — it must be one ` +\n \"the caller belongs to.\"\n }\n };\n }\n\n if (!callerHas(provided)) {\n return { refusal: mismatch(field, slug, provided, callerTenants) };\n }\n\n return { values };\n}\n\nfunction mismatch(\n field: string,\n slug: string,\n provided: unknown,\n callerTenants: readonly unknown[]\n): TenantWriteRefusal {\n return {\n code: \"TENANT_MISMATCH\",\n field,\n message:\n `'${field}' names tenant '${String(tenantIdOf(provided))}', which this caller does not belong ` +\n `to, so the write to '${slug}' would be refused by the database as well. ` +\n (callerTenants.length === 0\n ? \"This request carries no tenant at all.\"\n : `The caller's ${callerTenants.length === 1 ? \"tenant is\" : \"tenants are\"} ` +\n callerTenants.map(t => `'${String(tenantIdOf(t))}'`).join(\", \") + \".\")\n };\n}\n\n/** Where a caller's tenant was supposed to come from, for the 400. */\nfunction sourceHint(tenant: CollectionTenantConfig): string {\n return isTenantClaimSource(tenant.from)\n ? `The tenant comes from the '${tenant.from.claim}' claim on the caller's token; this one has no ` +\n \"such claim. Sign in, or add the claim in the custom-claims hook.\"\n : `The tenant comes from rows of '${tenant.from.membership.collection}' whose ` +\n `'${tenant.from.membership.userField}' is the caller; this caller has none.`;\n}\n","import { CollectionConfig, SecurityRule, SecurityOperation, AuthCollectionConfig, PolicyExpression, isPostgresCollectionConfig, policy } from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\nimport { buildTenantSecurityRule } from \"./tenant\";\nimport { getPolicyNamesForRules } from \"@rebasepro/utils\";\n\n/**\n * Default RLS policies injected by the schema generator.\n *\n * Rebase's enforcement model is unified: authenticated (user-context) requests\n * run under the restricted `rebase_user` role, so Postgres RLS binds *every*\n * statement — reads and writes. A collection's `securityRules` are the whole\n * authorization model. The server context (auth flows, migrations, raw\n * `rebase.sql`) runs as the owner and bypasses RLS.\n *\n * `rebase.dataAsAdmin` is **not** in that set, despite the name: it is scoped as\n * `{ uid: \"service\", roles: [\"admin\"] }`, so it runs as `rebase_user` like any\n * other caller and clears the baseline below through the *admin* arm, not the\n * server arm. Which is why `disableDefaultPolicies` plus a lone\n * `policy.serverContext()` rule locks it out too.\n *\n * Because RLS default-denies, every collection is **locked by default**: with\n * no rules, only the server context and admins can touch it. The generator\n * injects that safe baseline:\n *\n * **For every collection**\n * 1. A permissive **server-or-admin SELECT** grant.\n * 2. A permissive **server-or-admin write** grant (insert/update/delete).\n *\n * Author `securityRules` are permissive and OR together, so explicit rules only\n * *broaden* access from this locked baseline (e.g. \"users read/write their own\n * rows\").\n *\n * **For auth collections additionally**\n * 3. A permissive **self SELECT** grant (`id = rebase.uid()`), so users can read\n * their own row (profile, session bootstrap) without every app re-declaring\n * it.\n * 4. A **restrictive** admin write gate. Restrictive policies are AND'd with\n * every other policy, so a write is rejected unless the caller is an admin\n * (or the server context) — even if the author also wrote a permissive rule\n * such as \"a user may edit their own row\". Without this, a permissive owner\n * rule would let a user change their own `roles`.\n *\n * The server context is recognised as `rebase.uid() IS NULL` (`policy.serverContext()`)\n * — the built-in flows that run without a user (signup, migrations) set no user\n * GUC — which also lets the owner connection satisfy these policies even under\n * FORCE RLS. A *user* request never reaches that state: an anonymous one carries\n * `ANONYMOUS_USER_ID`, precisely so it cannot pass for the server here.\n *\n * **For a collection declaring `tenant`, additionally**\n * 5. A **restrictive** tenancy gate for every operation. Same kind of thing as\n * the admin write gate and injected for the same reason: it is ANDed with\n * every other policy, so it narrows what the author's permissive rules\n * grant and can never widen them. See `./tenant.ts`.\n *\n * Opt out with `disableDefaultPolicies: true` to take full responsibility for\n * the collection's RLS. The *restrictive* rules are not part of that opt-out:\n * dropping a rule that can only remove access could express nothing but \"let\n * more people in\", which is what the flag already does by removing the grants.\n */\n// Expressed structurally (not as raw SQL) so the admin UI can evaluate it\n// exactly — the framework's most security-critical policies must be reflected\n// precisely, not left as un-evaluable raw clauses. Compiles to\n// `rebase.uid() IS NULL OR (string_to_array(rebase.roles(), ',') && ARRAY['admin'])`.\n//\n// `serverContext()`, emphatically not `not(authenticated())`: the server arm of\n// this grant must match the server context and nothing else. Anonymous visitors\n// are not signed in either, so a negated `authenticated()` would hand them the\n// server-or-admin grant on every collection's default policy.\nconst SERVER_OR_ADMIN_EXPR: PolicyExpression = policy.or(\n policy.serverContext(),\n policy.rolesOverlap([\"admin\"])\n);\n\n/** Write operations that must be admin-gated by default on auth collections. */\nconst DEFAULT_GUARDED_OPS: SecurityOperation[] = [\"insert\", \"update\", \"delete\"];\n\n/** Whether a collection is flagged as an authentication collection. */\nfunction isAuthCollection(collection: CollectionConfig): boolean {\n const auth = collection.auth;\n return auth === true || (typeof auth === \"object\" && (auth as AuthCollectionConfig)?.enabled === true);\n}\n\n/** The property marked as the row id (falls back to `id`). */\nfunction getIdPropertyName(collection: CollectionConfig): string {\n for (const [name, prop] of Object.entries(collection.properties ?? {})) {\n if (prop && typeof prop === \"object\" && \"isId\" in prop && (prop as { isId?: unknown }).isId) {\n return name;\n }\n }\n return \"id\";\n}\n\n/**\n * Returns the security rules that should be applied to a collection: the\n * author's explicit `securityRules` plus the framework defaults described in\n * the module doc (baseline server/admin read for all collections; self-read\n * and the admin write gate for auth collections).\n *\n * Collections that opt out via `disableDefaultPolicies` are returned unchanged.\n */\n/**\n * The restrictive write gate for an auth collection.\n *\n * Restrictive, so it is ANDed with everything else: whatever an author's\n * permissive rules allow, a write to this table still has to satisfy this too.\n * It is the only thing standing between \"users may edit their own row\" and\n * \"users may grant themselves any role\".\n */\nfunction adminWriteGate(tableName: string): SecurityRule {\n return {\n name: `${tableName}_require_admin_write`,\n mode: \"restrictive\",\n operations: [...DEFAULT_GUARDED_OPS],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n };\n}\n\n/**\n * The restrictive tenancy policy, as a list of zero or one.\n *\n * A list so the two call sites can splice it in without a conditional, and a\n * separate function so it is obvious that it is injected on *both* paths —\n * including the `disableDefaultPolicies` one, where it is the only permissive-\n * looking thing that stays. See `./tenant.ts`.\n */\nfunction tenantRule(collection: CollectionConfig): SecurityRule[] {\n const rule = buildTenantSecurityRule(collection);\n return rule ? [rule] : [];\n}\n\nexport function getEffectiveSecurityRules(collection: CollectionConfig): SecurityRule[] {\n const explicit = [...(collection.securityRules ?? [])];\n\n const tableName = getTableName(collection);\n const injected: SecurityRule[] = [];\n\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n // The opt-out drops the *permissive* defaults — the ones that grant.\n // The restrictive admin-write gate on an auth collection is not among\n // them, because it is different in kind: a restrictive policy is ANDed\n // with every other policy and can only ever remove access, so opting\n // out of it cannot express anything except \"let more people write\".\n //\n // Dropping it did exactly that. `{ disableDefaultPolicies: true,\n // securityRules: [{ operation: \"all\", ownerField: \"id\" }] }` — an\n // ordinary \"users may edit their own row\" configuration — let any\n // signed-in user set their own `roles` to `[\"admin\"]`, with no warning\n // from any boot guard, doctor check or validator.\n //\n // An author who needs a different gate can add their own restrictive\n // rule; they cannot end up with none by accident.\n // Tenancy survives the opt-out for exactly the reason the write gate\n // does: it is restrictive, so it can only ever remove access. Dropping\n // it could express nothing except \"let every tenant read every other\n // tenant's rows\", which is not a thing `disableDefaultPolicies` is for\n // — that flag is about taking over the *grants*.\n return [...explicit, ...tenantRule(collection), ...(isAuthCollection(collection)\n ? [adminWriteGate(tableName)]\n : [])];\n }\n\n // Baseline read + write: the server context and admins can always operate.\n // RLS default-denies under the user role, so without these a rule-less\n // collection would be locked to everyone — including the admin studio.\n // Author rules are permissive and broaden access from here.\n injected.push({\n name: `${tableName}_default_admin_read`,\n operations: [\"select\"],\n condition: SERVER_OR_ADMIN_EXPR\n });\n injected.push({\n name: `${tableName}_default_admin_write`,\n operations: [...DEFAULT_GUARDED_OPS],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n });\n\n if (isAuthCollection(collection)) {\n // Self-read: a user can always read their own row.\n injected.push({\n name: `${tableName}_default_self_read`,\n operations: [\"select\"],\n condition: policy.compare(policy.field(getIdPropertyName(collection)), \"eq\", policy.authUid())\n });\n\n // Restrictive gate: AND'd with all other policies, so no permissive rule\n // (e.g. an owner \"edit your own row\" rule) can let a non-admin change\n // privileged columns like `roles`. Survives `disableDefaultPolicies` —\n // see the note above the opt-out.\n injected.push(adminWriteGate(tableName));\n }\n\n // Last, so it reads as what it is: a restriction ANDed over everything\n // above it, author rules included.\n injected.push(...tenantRule(collection));\n\n return [...explicit, ...injected];\n}\n\n/**\n * The framework defaults that {@link getEffectiveSecurityRules} would add to a\n * collection, without the author's own rules.\n *\n * These policies appear in the database under names the author never wrote, and\n * a permissive policy ORs with every other permissive policy — so someone\n * reading their `securityRules` and then the real ACL sees more access than they\n * declared. Dropping them by hand does nothing either: `db push` is declarative,\n * so the next push asserts them again. Callers use this to say, in the generated\n * DDL, which policies are injected and how to take them off.\n */\nexport function getInjectedSecurityRules(collection: CollectionConfig): SecurityRule[] {\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n // Not empty for an auth collection, nor for a tenant-scoped one: both\n // restrictive rules are still injected, and the generated DDL has to\n // say so — a policy in the database that the author never wrote and\n // cannot find in this list is exactly the surprise this function exists\n // to prevent.\n return [...tenantRule(collection), ...(isAuthCollection(collection)\n ? [adminWriteGate(getTableName(collection))]\n : [])];\n }\n\n const explicitCount = (collection.securityRules ?? []).length;\n // getEffectiveSecurityRules appends the defaults after the author's rules,\n // so everything past the author's count is injected.\n return getEffectiveSecurityRules(collection).slice(explicitCount);\n}\n\n/**\n * Every policy name `rebase db push` would write for a collection.\n *\n * This is the answer to \"did the codebase produce this live policy?\", and it is\n * more than `securityRules.map(r => r.name)` for two reasons:\n *\n * - a rule without an explicit `name` compiles to `<table>_<op>_<hash>`, one\n * per operation, so comparing `rule.name` to `policyname` never matches it;\n * - the generator also injects the safe-by-default baseline\n * (`<table>_default_admin_*`), which is in no collection's `securityRules`.\n *\n * Every UI that flags drift has to get both right, and each one that derived it\n * by hand got a different subset — which is how four policies *Rebase itself\n * wrote* came to be badged as hand-written drift on every table in a project,\n * with a button offering to import them back into the codebase that produced\n * them. There is one derivation now, and this is it.\n */\nexport function getGeneratedPolicyNames(collection: CollectionConfig): Set<string> {\n return getPolicyNamesForRules(getEffectiveSecurityRules(collection), getTableName(collection));\n}\n","import {\n CollectionConfig,\n JUNCTION_PIVOT_KEY,\n PolicyExpression,\n PolicyOperand,\n Properties,\n Property,\n Relation,\n SecurityRule,\n isPostgresCollectionConfig,\n policy\n} from \"@rebasepro/types\";\nimport { getPolicyOperations } from \"@rebasepro/utils\";\nimport { getTableName } from \"./relations\";\nimport { resolveCollectionRelations } from \"./relations\";\nimport { isManyToMany } from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"./policy/securityRuleToConditions\";\n\n/**\n * RLS derivation for many-to-many junction tables.\n *\n * A `through` relation makes the generator create a table nobody declared as a\n * collection — `posts_tags`, `user_roles`. Those tables used to be the one kind\n * of generated table with **no** RLS at all: `rebase_user` holds full DML grants,\n * so with the endpoints locked down, any signed-up user could still read or wipe\n * every edge between them. There is also nowhere in the config to write rules\n * for a junction, so the author could not even fix it by hand.\n *\n * The architecture here is that a junction's security is *derived*, never\n * hand-written:\n *\n * 1. **Locked baseline.** The same server-or-admin `default_admin` grants every\n * collection gets, so the invariant holds again: every table the generator\n * creates is default-deny, and rules only broaden.\n *\n * 2. **Reads follow the endpoints.** An edge is visible iff *both* endpoint\n * rows are visible — two correlated `EXISTS` subqueries. The subqueries run\n * under the caller's role, so each endpoint's own RLS filters them: junction\n * visibility delegates to the endpoints' policies, whatever they become,\n * with nothing duplicated. A public blog keeps rendering its tags; a private\n * CRM's edges are exactly as hidden as its rows.\n *\n * 3. **Writes follow the owning side's update rules.** Linking or unlinking an\n * edge *is* an edit of the owning row — tagging a post is editing the post —\n * so edge writes inherit the declaring collection's explicit permissive\n * `update` rules, each wrapped in an `EXISTS` against the owning row. Where\n * a rule cannot be embedded faithfully (see below) it is dropped, so the\n * failure mode is always *too locked*, never open. Explicit **restrictive**\n * update rules are inherited as restrictive junction rules; if one of them\n * cannot be embedded, the whole derived write grant for that side is\n * suppressed — granting without the author's gate would be looser than the\n * parent itself.\n *\n * **Embeddability.** A parent rule is embedded by moving its condition inside\n * `EXISTS (SELECT 1 FROM parent WHERE parent.pk = junction.fk AND <condition>)`.\n * In that scope, `field` operands bind to the parent — which is what the author\n * meant. But `outerField` operands and `{column}` placeholders in `raw` SQL bind\n * to the RLS row, which is now the junction, not the parent the author wrote\n * them against. So: `raw` anywhere disqualifies a rule; a top-level `outerField`\n * (equivalent to `field` outside a subquery) is rewritten to `field`; an\n * `outerField` inside a nested `existsIn` cannot be re-scoped and disqualifies\n * the rule.\n *\n * Injected parent defaults are never inherited — the junction's own baseline\n * already covers the server/admin plane, and an auth collection's restrictive\n * `require_admin_write` gate exists to protect privileged parent *columns*,\n * which an edge write cannot touch. Inheriting it would stop users managing\n * e.g. their own interests through a `users_interests` junction for no gain.\n *\n * Everything flows through the shared naming machinery, so the Studio\n * recognises these policies as generated instead of offering to \"import\" them.\n */\n\n/** One side of a junction: the collection and the FK column pointing at it. */\nexport interface JunctionEndpoint {\n collection: CollectionConfig;\n /** Junction column holding this endpoint's key. */\n junctionColumn: string;\n}\n\n/** A collection that declares the `through` relation (owns the edge semantics). */\nexport interface JunctionDeclaringSide extends JunctionEndpoint {\n relation: Relation;\n}\n\nexport interface JunctionSpec {\n /** Bare table name (schema stripped). */\n table: string;\n /** Schema the junction is created in — mirrors the CREATE TABLE path. */\n schema: string;\n /** The two endpoints, in [source, target] order of the first declaring relation. */\n endpoints: [JunctionEndpoint, JunctionEndpoint];\n /** Every collection that declares a relation through this table. */\n declaringSides: JunctionDeclaringSide[];\n /**\n * The junction's own columns beyond the two keys — `through.properties`,\n * merged across every declaring side. `{}` when there are none.\n *\n * See {@link ManyToManyRelation.through} for what they are; every side that\n * names a key has to describe the same column, which is checked when the\n * specs are resolved rather than left for `CREATE TABLE` to discover.\n */\n properties: Properties;\n}\n\n// Mirrors auth-default-policies: the server context or an admin.\nconst SERVER_OR_ADMIN_EXPR: PolicyExpression = policy.or(\n policy.serverContext(),\n policy.rolesOverlap([\"admin\"])\n);\n\n/**\n * Walk every collection's resolved relations and aggregate the junction tables\n * they declare. Two collections may declare the same junction from opposite\n * sides (posts→tags and tags→posts through `posts_tags`); both become\n * `declaringSides` of one spec, so derived write grants consider both.\n */\nexport function resolveJunctionSpecs(collections: CollectionConfig[]): Map<string, JunctionSpec> {\n const specs = new Map<string, JunctionSpec>();\n\n for (const collection of collections) {\n const resolved = resolveCollectionRelations(collection);\n for (const relation of Object.values(resolved)) {\n // Narrowed rather than probed: only a many-to-many has a junction,\n // and only after narrowing is `through` guaranteed complete.\n if (!isManyToMany(relation)) continue;\n\n const targetCollection: CollectionConfig | undefined = relation.target();\n if (!targetCollection) continue;\n\n const rawName = relation.through.table;\n // The CREATE TABLE path strips a schema prefix from the name but\n // still creates in \"public\"; the policies must target the same\n // table, so mirror that behaviour exactly.\n const table = rawName.includes(\".\") ? rawName.split(\".\").pop()! : rawName;\n const schema = \"public\";\n\n const source: JunctionDeclaringSide = {\n collection,\n junctionColumn: relation.through.sourceColumn,\n relation\n };\n const target: JunctionEndpoint = {\n collection: targetCollection,\n junctionColumn: relation.through.targetColumn\n };\n\n const existing = specs.get(table);\n if (!existing) {\n specs.set(table, {\n table,\n schema,\n endpoints: [source, target],\n declaringSides: [source],\n properties: mergeJunctionPayload({}, relation.through.properties, table, collection)\n });\n } else {\n // Merged whether or not this side is new: the same collection\n // can reach one junction under two relation names, and the\n // columns each of them asks for all have to exist.\n existing.properties = mergeJunctionPayload(\n existing.properties, relation.through.properties, table, collection);\n if (!existing.declaringSides.some(s => s.collection === collection)) {\n existing.declaringSides.push(source);\n }\n }\n }\n }\n\n return specs;\n}\n\n/**\n * Fold one side's `through.properties` into the junction's, refusing a\n * disagreement rather than picking a winner.\n *\n * Both ends of a link may declare it — `posts.tags` and `tags.posts` are one\n * junction — and each end may name the payload. Only one table gets created, so\n * two descriptions of `role` that are not the same description are a question\n * with no correct answer: whichever won, one of the two collections would be\n * writing through a column it does not think it has. Compared structurally, so\n * two sides that spell the same property twice (the normal case, and the one\n * the docs recommend) are fine.\n */\nfunction mergeJunctionPayload(\n into: Properties,\n incoming: Properties | undefined,\n table: string,\n collection: CollectionConfig\n): Properties {\n if (!incoming || Object.keys(incoming).length === 0) return into;\n const merged: Properties = { ...into };\n for (const [key, property] of Object.entries(incoming)) {\n const already = merged[key as keyof Properties] as Property | undefined;\n if (already && JSON.stringify(already) !== JSON.stringify(property)) {\n throw new Error(\n `The junction table \"${table}\" is declared from more than one side, and they disagree ` +\n `about the payload column \"${key}\": \"${collection.slug ?? collection.name}\" describes it ` +\n \"differently than another declaring collection does. One table is created, so both \" +\n \"`through.properties` blocks have to describe the same column — or only one side should \" +\n \"declare it.\"\n );\n }\n (merged as Record<string, Property>)[key] = property as Property;\n }\n return merged;\n}\n\n/**\n * A synthetic CollectionConfig standing in for the junction during policy\n * compilation and naming. Its two FK columns carry explicit `columnName`s so\n * `outerField` operands resolve to the exact columns the CREATE TABLE emitted,\n * whatever their casing.\n *\n * The payload columns are here too, exactly as authored. That is what lets one\n * reading of a `Property` serve the junction as well as a collection: the\n * schema planner plans these columns with the same function it plans a\n * collection's with, and the write path validates a `_pivot` against them with\n * the same validator a row's values go through. A second description of a\n * payload column anywhere is a second description that can disagree.\n */\nexport function getJunctionCollectionConfig(spec: JunctionSpec): CollectionConfig {\n const properties: Record<string, unknown> = {};\n for (const endpoint of spec.endpoints) {\n properties[endpoint.junctionColumn] = {\n type: \"string\",\n columnName: endpoint.junctionColumn\n };\n }\n // After the keys, so a payload property that collides with a key column\n // cannot quietly replace it — `checkJunctionPayload` refuses that config at\n // boot, and this ordering means the key column survives if one gets past.\n for (const [key, property] of Object.entries(spec.properties)) {\n if (key === JUNCTION_PIVOT_KEY || key in properties) continue;\n properties[key] = property;\n }\n return {\n slug: spec.table,\n name: spec.table,\n table: spec.table,\n schema: spec.schema,\n properties\n } as unknown as CollectionConfig;\n}\n\n/** The property marked as the row id (falls back to `id`). */\nfunction getIdPropertyName(collection: CollectionConfig): string {\n for (const [name, prop] of Object.entries(collection.properties ?? {})) {\n if (prop && typeof prop === \"object\" && \"isId\" in prop && (prop as { isId?: unknown }).isId) {\n return name;\n }\n }\n return \"id\";\n}\n\n/** `EXISTS (SELECT 1 FROM endpoint WHERE endpoint.pk = junction.fk [AND extra])`. */\nfunction existsEndpoint(endpoint: JunctionEndpoint, extra?: PolicyExpression): PolicyExpression {\n const correlation = policy.compare(\n policy.field(getIdPropertyName(endpoint.collection)),\n \"eq\",\n policy.outerField(endpoint.junctionColumn)\n );\n return policy.existsIn({\n collection: endpoint.collection.slug,\n where: extra ? policy.and(correlation, extra) : correlation\n });\n}\n\n/**\n * Whether a parent-rule expression keeps its meaning when moved inside the\n * junction's `EXISTS` subquery — and the re-scoped copy if it does.\n *\n * Returns `null` when the rule cannot be embedded faithfully: `raw` SQL\n * anywhere (its `{column}` placeholders would bind to the junction), or an\n * `outerField` inside a nested `existsIn` (it would bind to the junction while\n * the author meant the parent, and no operand can express \"the middle scope\").\n * Top-level `outerField`s are rewritten to `field`, which is what they meant.\n */\nexport function embedParentExpression(expr: PolicyExpression, depth = 0): PolicyExpression | null {\n switch (expr.kind) {\n case \"raw\":\n return null;\n case \"and\":\n case \"or\": {\n const parts: PolicyExpression[] = [];\n for (const child of expr.operands) {\n const embedded = embedParentExpression(child, depth);\n if (!embedded) return null;\n parts.push(embedded);\n }\n return expr.kind === \"and\" ? policy.and(...parts) : policy.or(...parts);\n }\n case \"not\": {\n const embedded = embedParentExpression(expr.operand, depth);\n return embedded ? policy.not(embedded) : null;\n }\n case \"existsIn\": {\n const where = embedParentExpression(expr.where, depth + 1);\n return where ? policy.existsIn({ collection: expr.collection, where }) : null;\n }\n case \"compare\": {\n const left = embedOperand(expr.left, depth);\n const right = embedOperand(expr.right, depth);\n if (!left || !right) return null;\n return { ...expr, left, right };\n }\n default:\n // Leaf expressions with no field references (true, false,\n // serverContext, authenticated, rolesOverlap, rolesContain) are\n // position-independent.\n return expr;\n }\n}\n\n/** Re-scope an operand, or return `null` if its binding cannot be preserved. */\nfunction embedOperand(operand: PolicyOperand, depth: number): PolicyOperand | null {\n if (operand.kind === \"outerField\") {\n // Outside a subquery, outerField ≡ field: the author meant their own\n // row, which after embedding is the EXISTS's joined table → field.\n if (depth === 0) return policy.field(operand.name);\n // Inside the author's own existsIn it meant the parent row; after\n // embedding it would bind to the junction. Not expressible.\n return null;\n }\n return operand;\n}\n\n/** Does the rule cover the `update` operation? */\nfunction coversUpdate(rule: SecurityRule): boolean {\n return getPolicyOperations(rule).some(op => op === \"update\" || op === \"all\");\n}\n\n/**\n * The full derived policy set for a junction table: the locked server/admin\n * baseline, the endpoint-visibility read grant, inherited write grants, and\n * inherited restrictive gates. Returns `[]` when every declaring collection set\n * `disableDefaultPolicies` — the junction is then the author's to police, and\n * stays locked (RLS is still enabled) until they write policies for it.\n */\nexport function getJunctionSecurityRules(spec: JunctionSpec): SecurityRule[] {\n if (spec.declaringSides.every(side => isPostgresCollectionConfig(side.collection) && side.collection.disableDefaultPolicies)) {\n return [];\n }\n\n const rules: SecurityRule[] = [];\n\n // 1. Locked baseline — same shape and naming as every collection's.\n rules.push({\n name: `${spec.table}_default_admin_read`,\n operations: [\"select\"],\n condition: SERVER_OR_ADMIN_EXPR\n });\n rules.push({\n name: `${spec.table}_default_admin_write`,\n operations: [\"insert\", \"update\", \"delete\"],\n condition: SERVER_OR_ADMIN_EXPR,\n check: SERVER_OR_ADMIN_EXPR\n });\n\n // 2. Reads follow the endpoints: the edge is visible iff both rows are.\n // The EXISTS subqueries run under the caller's role, so each endpoint's\n // own RLS applies inside them — visibility is delegated, not copied.\n rules.push({\n name: `${spec.table}_default_edge_read`,\n operations: [\"select\"],\n condition: policy.and(\n existsEndpoint(spec.endpoints[0]),\n existsEndpoint(spec.endpoints[1])\n )\n });\n\n // 3. Writes follow the owning side's explicit update rules.\n const writeGrants: PolicyExpression[] = [];\n for (const side of spec.declaringSides) {\n const explicitRules = (isPostgresCollectionConfig(side.collection)\n ? side.collection.securityRules\n : undefined) ?? [];\n const updateRules = explicitRules.filter(coversUpdate);\n\n const permissive = updateRules.filter(r => r.mode !== \"restrictive\");\n const restrictive = updateRules.filter(r => r.mode === \"restrictive\");\n\n // Embed the restrictive gates first: if any of them cannot be carried\n // over, granting writes from this side would be looser than the parent\n // itself allows — so the whole side's grant is suppressed.\n const embeddedGates: PolicyExpression[] = [];\n let gatesEmbeddable = true;\n for (const gate of restrictive) {\n const using = securityRuleToConditions(gate).usingExpr;\n const embedded = using ? embedParentExpression(using) : null;\n if (!embedded) {\n gatesEmbeddable = false;\n break;\n }\n embeddedGates.push(embedded);\n }\n if (!gatesEmbeddable) continue;\n\n const grants: PolicyExpression[] = [];\n for (const rule of permissive) {\n const using = securityRuleToConditions(rule).usingExpr;\n const embedded = using ? embedParentExpression(using) : null;\n if (embedded) grants.push(embedded);\n }\n if (grants.length === 0) continue;\n\n // \"May update the owning row\": any permissive grant, AND every gate.\n const condition = embeddedGates.length > 0\n ? policy.and(policy.or(...grants), ...embeddedGates)\n : policy.or(...grants);\n\n writeGrants.push(existsEndpoint(side, condition));\n }\n\n if (writeGrants.length > 0) {\n rules.push({\n name: `${spec.table}_default_edge_write`,\n operations: [\"insert\", \"update\", \"delete\"],\n condition: writeGrants.length === 1 ? writeGrants[0] : policy.or(...writeGrants),\n check: writeGrants.length === 1 ? writeGrants[0] : policy.or(...writeGrants)\n });\n }\n\n return rules;\n}\n","import jsonLogic from \"json-logic-js\";\nimport {\n ArrayProperty,\n AuthState,\n ConditionContext,\n ConditionRule,\n EnumValueConfig,\n NumberProperty,\n PropertyConditions,\n Property,\n ReferenceProperty,\n StringProperty\n} from \"@rebasepro/types\";\n\n/**\n * Access a nested property from an object via dot notation.\n */\nfunction getIn(obj: Record<string, unknown> | unknown, path: string): unknown {\n if (!obj || !path) return undefined;\n return path.split(\".\").reduce((acc: unknown, part: string) => acc && (acc as Record<string, unknown>)[part], obj);\n}\n\nlet operationsRegistered = false;\n\n/**\n * Register custom JSON Logic operations for Rebase.\n * Call this once at app initialization.\n */\nexport function registerConditionOperations(): void {\n if (operationsRegistered) return;\n\n // Check if user has a specific role by ID\n jsonLogic.add_operation(\"hasRole\", function (this: ConditionContext, roleId: string) {\n return this?.user?.roles?.includes(roleId) ?? false;\n });\n\n // Check if user has any of the specified roles\n jsonLogic.add_operation(\"hasAnyRole\", function (this: ConditionContext, roleIds: string[]) {\n if (!this?.user?.roles || !Array.isArray(roleIds)) return false;\n return roleIds.some(role => this.user.roles.includes(role));\n });\n\n // Check if a timestamp is today\n jsonLogic.add_operation(\"isToday\", (timestamp: number) => {\n if (!timestamp) return false;\n const date = new Date(timestamp);\n const today = new Date();\n return date.getFullYear() === today.getFullYear() &&\n date.getMonth() === today.getMonth() &&\n date.getDate() === today.getDate();\n });\n\n // Check if a timestamp is in the past\n jsonLogic.add_operation(\"isPast\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp < Date.now();\n });\n\n // Check if a timestamp is in the future\n jsonLogic.add_operation(\"isFuture\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp > Date.now();\n });\n\n operationsRegistered = true;\n}\n\n/**\n * Evaluate a condition against the given context.\n *\n * A condition may be stated as a literal instead of a rule — `hidden: true`\n * rather than `hidden: { \"==\": [1, 1] }` — and a literal is already its own\n * answer, so it is returned rather than handed to the evaluator.\n */\nexport function evaluateCondition(rule: ConditionRule, context: ConditionContext): unknown {\n if (typeof rule === \"boolean\") return rule;\n // Ensure operations are registered\n registerConditionOperations();\n return jsonLogic.apply(rule, context);\n}\n\n/**\n * Convert a value to a format suitable for JSON Logic evaluation.\n * Specifically handles Date objects by converting them to Unix timestamps.\n */\nfunction serializeValueForConditions(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.getTime();\n }\n\n // Handle Firestore Timestamp-like objects (have toDate or toMillis)\n if (typeof (value as { toMillis?: () => number })?.toMillis === \"function\") {\n return (value as { toMillis: () => number }).toMillis();\n }\n if (typeof (value as { toDate?: () => Date })?.toDate === \"function\") {\n return (value as { toDate: () => Date }).toDate().getTime();\n }\n\n // Handle arrays recursively\n if (Array.isArray(value)) {\n return value.map(serializeValueForConditions);\n }\n\n // Handle plain objects recursively\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValueForConditions((value as Record<string, unknown>)[key]);\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Build a ConditionContext from the current property resolution context.\n */\nexport function buildConditionContext(params: {\n propertyKey?: string;\n values?: Record<string, unknown>;\n previousValues?: Record<string, unknown>;\n path: string;\n entityId?: string;\n index?: number;\n authController: AuthState;\n}): ConditionContext {\n const {\n propertyKey,\n values,\n previousValues,\n path,\n entityId,\n index,\n authController\n } = params;\n\n const user = authController.user;\n const serializedValues = serializeValueForConditions(values ?? {});\n const serializedPreviousValues = serializeValueForConditions(previousValues ?? values ?? {});\n\n return {\n values: serializedValues as Record<string, unknown>,\n previousValues: serializedPreviousValues as Record<string, unknown>,\n propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : undefined,\n path,\n entityId,\n isNew: !entityId,\n index,\n user: {\n uid: user?.uid ?? \"\",\n email: user?.email ?? null,\n displayName: user?.displayName ?? null,\n photoURL: user?.photoURL ?? null,\n roles: (user?.roles ?? []).map((r: unknown) => typeof r === \"string\" ? r : (r as { id: string }).id)\n },\n now: Date.now()\n };\n}\n\n/**\n * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.\n */\n","import { rewriteLegacyRlsFunctions } from \"@rebasepro/types\";\nimport type {\n ArrayProperty,\n NumberProperty,\n PostgresProperties,\n Property,\n Relation,\n SecurityOperation,\n SecurityRule,\n StringProperty,\n TableColumnInfo,\n TableMetadata\n} from \"@rebasepro/types\";\nimport { firstFreeKey, prettifyIdentifier, toWireKey } from \"@rebasepro/utils\";\n\n/**\n * A collection as introspection can describe it: the table, its columns, the\n * relations its foreign keys imply, and the RLS policies already on it.\n *\n * Deliberately not `Partial<AdminCollection>`, which is what this returned\n * while it lived in `@rebasepro/studio`. `propertiesOrder` is the only admin\n * key it produces, and naming the admin view model for one field would put\n * `@rebasepro/cms-types` on the dependency path of a package the backend\n * loads.\n */\nexport interface IntrospectedCollection {\n name: string;\n slug: string;\n table: string;\n properties: PostgresProperties;\n propertiesOrder: string[];\n relations?: Relation[];\n securityRules?: SecurityRule[];\n}\n\n/**\n * Maps a PostgreSQL column data type to a Rebase property type.\n */\nfunction pgTypeToRebaseProperty(column: TableColumnInfo): Property | null {\n const {\n column_name,\n data_type,\n udt_name,\n is_nullable,\n column_default,\n character_maximum_length,\n enum_values\n } = column;\n\n const required = is_nullable === \"NO\";\n const prettifiedName = prettifyIdentifier(column_name);\n\n // Detect if this column is a primary key (auto-generated id)\n const isAutoId = column_default != null && (\n column_default.includes(\"nextval\") ||\n column_default.includes(\"gen_random_uuid\") ||\n column_default.includes(\"uuid_generate\") ||\n column_default.includes(\"identity\")\n );\n\n // USER-DEFINED = PostgreSQL enums\n if (data_type === \"USER-DEFINED\" && enum_values && enum_values.length > 0) {\n return {\n type: \"string\",\n name: prettifiedName,\n enum: enum_values.map((v: string) => ({ id: v,\nlabel: prettifyIdentifier(v) })),\n validation: required ? { required: true } : undefined\n } as StringProperty;\n }\n\n const dt = data_type.toLowerCase();\n switch (dt) {\n case \"character varying\":\n case \"varchar\":\n case \"text\":\n case \"char\":\n case \"character\":\n case \"citext\": {\n let colType: \"varchar\" | \"text\" | \"char\" = \"varchar\";\n if (dt === \"text\" || dt === \"citext\") colType = \"text\";\n if (dt === \"char\" || dt === \"character\") colType = \"char\";\n // Carry the declared width across. Dropping it made introspection\n // lossy in the one direction that costs data: a `character\n // varying(500)` column read back as a bare `varchar` regenerates as\n // `VARCHAR(255)`, narrowing a column that already holds longer\n // values. TEXT has no width, and reporting one would invent a limit\n // the database does not have.\n const declaredLength = colType === \"text\" ? null : character_maximum_length;\n const prop: StringProperty = {\n type: \"string\",\n name: prettifiedName,\n columnType: colType,\n validation: required || declaredLength\n ? {\n ...(required ? { required: true } : {}),\n ...(declaredLength ? { max: declaredLength } : {})\n }\n : undefined\n };\n if (isAutoId) {\n prop.isId = \"manual\";\n }\n return prop;\n }\n\n case \"uuid\": {\n const prop: StringProperty = {\n type: \"string\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n if (isAutoId) {\n prop.isId = \"uuid\";\n }\n return prop;\n }\n\n case \"integer\":\n case \"bigint\":\n case \"smallint\": {\n const colType = dt === \"bigint\" ? \"bigint\" : \"integer\";\n const prop: NumberProperty = {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n validation: {\n ...(required ? { required: true } : {}),\n integer: true\n }\n };\n if (isAutoId) {\n prop.isId = \"increment\";\n }\n return prop;\n }\n\n case \"serial\":\n case \"bigserial\":\n case \"smallserial\": {\n const colType = dt === \"bigserial\" ? \"bigserial\" : \"serial\";\n return {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n isId: \"increment\",\n validation: {\n ...(required ? { required: true } : {}),\n integer: true\n }\n } as NumberProperty;\n }\n\n case \"numeric\":\n case \"decimal\":\n case \"real\":\n case \"double precision\": {\n let colType: \"numeric\" | \"real\" | \"double precision\" = \"numeric\";\n if (dt === \"real\") colType = \"real\";\n if (dt === \"double precision\") colType = \"double precision\";\n return {\n type: \"number\",\n name: prettifiedName,\n columnType: colType,\n validation: required ? { required: true } : undefined\n };\n }\n\n case \"boolean\":\n return {\n type: \"boolean\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n\n case \"timestamp with time zone\":\n case \"timestamp without time zone\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"date\":\n case \"time with time zone\":\n case \"time without time zone\":\n case \"time\": {\n let colType: \"timestamp\" | \"date\" | \"time\" = \"timestamp\";\n if (dt.startsWith(\"date\")) colType = \"date\";\n if (dt.startsWith(\"time \") || dt === \"time\") colType = \"time\";\n return {\n type: \"date\",\n name: prettifiedName,\n columnType: colType,\n validation: required ? { required: true } : undefined\n };\n }\n\n case \"jsonb\":\n case \"json\":\n return {\n type: \"map\",\n name: prettifiedName,\n columnType: dt === \"jsonb\" ? \"jsonb\" : \"json\",\n keyValue: true,\n properties: {}\n };\n\n case \"array\":\n case \"ARRAY\": {\n let innerType = \"string\";\n let colType: ArrayProperty[\"columnType\"] = undefined;\n if (udt_name === \"_text\" || udt_name === \"_varchar\") {\n innerType = \"string\";\n colType = \"text[]\";\n } else if (udt_name === \"_int4\" || udt_name === \"_int2\" || udt_name === \"_int8\") {\n innerType = \"number\";\n colType = \"integer[]\";\n } else if (udt_name === \"_bool\") {\n innerType = \"boolean\";\n colType = \"boolean[]\";\n } else if (udt_name === \"_numeric\") {\n innerType = \"number\";\n colType = \"numeric[]\";\n }\n return {\n type: \"array\",\n name: prettifiedName,\n columnType: colType,\n of: { type: innerType }\n } as ArrayProperty;\n }\n\n default:\n // Fallback: treat unknown types as string\n return {\n type: \"string\",\n name: prettifiedName,\n validation: required ? { required: true } : undefined\n };\n }\n}\n\n/**\n * Builds a collection description from PostgreSQL table metadata.\n * This is used when creating a new collection from an existing database table.\n */\nexport function buildCollectionFromTableMetadata(\n tableName: string,\n metadata: TableMetadata\n): IntrospectedCollection {\n const properties: Record<string, Property> = {};\n const propertiesOrder: string[] = [];\n // Introspection can only ever produce two shapes: a foreign key on this\n // table, or a junction between two. Both are named by their kind.\n const relations: Array<{\n id: string;\n relationName: string;\n target: string;\n kind: \"belongsTo\" | \"manyToMany\";\n localKey?: string;\n through?: { table: string; sourceColumn: string; targetColumn: string };\n }> = [];\n const securityRules: SecurityRule[] = [];\n\n // Parse columns\n for (const column of metadata.columns) {\n const property = pgTypeToRebaseProperty(column);\n if (property) {\n const propRecord = property as unknown as Record<string, unknown>;\n Object.keys(propRecord).forEach(key => propRecord[key] === undefined && delete propRecord[key]);\n\n // The key is the wire name; `columnName` carries the column. This\n // used to key by the column and rely on the two being the same\n // string, which is what put `user_id` on the API of an imported\n // collection and `displayName` on the API of an authored one.\n //\n // `columnName` is stamped unconditionally rather than left to the\n // snake_case default, because the default is not the inverse of\n // camel-casing for every name — the mapping has to be recorded, not\n // recomputed.\n //\n // First free candidate: `user_id` and `userId` as two real columns\n // camel-case to one key, and one of them would otherwise overwrite\n // the other and be silently dropped.\n const key = firstFreeKey(\n [toWireKey(column.column_name), column.column_name],\n { has: (candidate: string) => candidate in properties }\n );\n if (key !== column.column_name) propRecord.columnName = column.column_name;\n properties[key] = property;\n propertiesOrder.push(key);\n }\n }\n\n // Parse Outgoing Foreign Keys -> Many-to-One / One-to-One\n if (metadata.foreignKeys) {\n for (const fk of metadata.foreignKeys) {\n const relName = toWireKey(\n fk.column_name.endsWith(\"_id\")\n ? fk.column_name.substring(0, fk.column_name.length - 3)\n : fk.column_name\n );\n relations.push({\n id: fk.column_name,\n relationName: relName,\n target: fk.foreign_table_name, // Will be hydrated later\n kind: \"belongsTo\",\n localKey: fk.column_name\n });\n }\n }\n\n // Parse Incoming Junctions -> Many-to-Many\n if (metadata.junctions) {\n for (const junction of metadata.junctions) {\n const relName = junction.target_table_name; // E.g., 'roles'\n relations.push({\n id: junction.target_table_name + \"_relation\",\n relationName: relName,\n target: junction.target_table_name, // Will be hydrated later\n kind: \"manyToMany\",\n through: {\n table: junction.junction_table_name,\n sourceColumn: junction.source_column_name,\n targetColumn: junction.target_column_name\n }\n });\n }\n }\n\n // Parse RLS Policies\n if (metadata.policies) {\n for (const policy of metadata.policies) {\n // Attempt to map typical cmds to operations.\n // Postgres cmd: SELECT, INSERT, UPDATE, DELETE, ALL\n let operations: SecurityOperation[] = [];\n switch (policy.cmd) {\n case \"ALL\": operations = [\"all\"]; break;\n case \"SELECT\": operations = [\"select\"]; break;\n case \"INSERT\": operations = [\"insert\"]; break;\n case \"UPDATE\": operations = [\"update\"]; break;\n case \"DELETE\": operations = [\"delete\"]; break;\n }\n // Normalised on the way in, the same way `sqlToPolicy` normalises\n // what the admin UI reads back. Without it, importing a table from a\n // database provisioned before 1.0 copies `auth.uid()` straight into\n // the project's config — a call to a function the framework no\n // longer creates, which then boots with a legacy-helper warning\n // forever and holds the `auth` schema open.\n const qual = policy.qual ? rewriteLegacyRlsFunctions(policy.qual) : undefined;\n const withCheck = policy.with_check ? rewriteLegacyRlsFunctions(policy.with_check) : undefined;\n if (qual) {\n securityRules.push({\n name: policy.policy_name,\n operations,\n roles: policy.roles ?? [],\n using: qual,\n ...(withCheck ? { withCheck } : {})\n });\n } else {\n securityRules.push({\n name: policy.policy_name,\n operations,\n roles: policy.roles ?? []\n });\n }\n }\n }\n\n return {\n name: prettifyIdentifier(tableName),\n slug: tableName,\n table: tableName,\n properties: properties as PostgresProperties,\n propertiesOrder,\n // `target` is still a slug here — the caller hydrates it into a thunk.\n ...(relations.length > 0 ? { relations: relations as unknown as Relation[] } : {}),\n ...(securityRules.length > 0 ? { securityRules } : {})\n };\n}\n","import type { StringProperty } from \"@rebasepro/types\";\n\n/**\n * The length a bounded string column is declared with when the property does\n * not say. Historical: it is what the DDL generator hardcoded, kept so that\n * regenerating an existing schema does not silently redefine its columns.\n */\nexport const DEFAULT_STRING_COLUMN_LENGTH = 255;\n\n/**\n * How wide a `varchar`/`char` column should be for a given property.\n *\n * One definition, three call sites, because they used to disagree. For the same\n * `columnType: \"varchar\"` property the DDL generator emitted `VARCHAR(255)`\n * while the Drizzle generator emitted a bare `varchar(\"col\")` — which Postgres\n * reads as *unbounded* — so which of the two you ran decided whether the column\n * had a limit at all. Introspection then dropped the length entirely, so reading\n * an existing `character varying(500)` column back and regenerating it produced\n * a `VARCHAR(255)`: a silent narrowing of a column with data already in it.\n *\n * `validation.max` is the property's own statement about how long the value may\n * be, so it is the only sensible source for the column's width — and it keeps\n * the constraint the database enforces in step with the one the app enforces,\n * rather than inventing a second, different limit underneath it.\n */\nexport function resolveStringColumnLength(prop: Pick<StringProperty, \"validation\">): number {\n const max = prop.validation?.max;\n return typeof max === \"number\" && Number.isInteger(max) && max > 0\n ? max\n : DEFAULT_STRING_COLUMN_LENGTH;\n}\n","/**\n * The tables Rebase creates for its own bookkeeping, and the SQL that keeps the\n * end-user role away from them.\n *\n * ## Why this exists\n *\n * Authenticated requests run as {@link REBASE_USER_ROLE}, and the boot-time role\n * provisioning grants that role `SELECT, INSERT, UPDATE, DELETE` on every table\n * in the schemas a project uses — including `rebase`, because a project's own\n * collections are allowed to live there (the scaffold puts `users` there). It\n * also sets `ALTER DEFAULT PRIVILEGES`, so a table created *later* by the\n * migrating role inherits the same grant.\n *\n * Every framework-internal table is created later: auth's tables come up during\n * `initializeAuth`, `api_keys` during route mounting, `cron_logs` when the first\n * job registers, `idempotency_keys` on the first request that carries a key. So\n * they all inherited full DML for the end-user role — and none of them enables\n * row-level security, because none of them is a collection with\n * `securityRules`. Measured on a freshly provisioned database, `SET ROLE\n * rebase_user` could read `rebase.refresh_tokens` (session token hashes),\n * `rebase.mfa_factors` (`secret_encrypted`), `rebase.recovery_codes`, and\n * `rebase.api_keys` (including its `admin` flag), and insert into\n * `rebase.app_config`.\n *\n * Nothing routes a user-context query at those tables today, so this was not\n * reachable over the API. That is the wrong thing to depend on: the documented\n * model is that RLS is the authorization boundary, and these tables sat outside\n * it. The boundary is now a privilege boundary instead — the role simply cannot\n * address them.\n *\n * ## Why REVOKE rather than ENABLE ROW LEVEL SECURITY\n *\n * RLS with no policy denies every row, which is the same outcome, but it is the\n * *weaker* statement: it leaves the grant in place, so a later policy — or a\n * `FORCE` flag cleared by some future migration — reopens the table. There is no\n * row of `refresh_tokens` any end user should ever reach, so the honest encoding\n * is \"this role has no privilege here at all\". It also keeps the owner\n * connection (which auth actually runs on) completely unaffected.\n *\n * ## Keeping it true\n *\n * `packages/rls-check` scans the `rebase` schema — it used to skip it as a\n * \"platform\" schema — and its `rls-disabled` check fires on exactly the\n * condition this module removes: RLS off *and* a DML grant to a reachable role.\n * So a table added here without a revoke is caught by `pnpm rls:check`, not by\n * someone re-reading this file.\n */\n\n/**\n * The Postgres role authenticated requests run as.\n *\n * Defined here rather than in the Postgres driver because both the driver (which\n * provisions the role) and this module (which revokes on its behalf) need it,\n * and a second spelling of a role name is a silent no-op waiting to happen.\n */\nexport const REBASE_USER_ROLE = \"rebase_user\";\n\n/**\n * Framework-internal table names, unqualified.\n *\n * Deliberately NOT including `users`: the auth user table is also a collection,\n * with `securityRules`, RLS enabled and policies applied. Users read their own\n * row through it — revoking there would break sign-in. `revokeInternalTableSql`\n * now skips any table with RLS enabled, so that exception is enforced rather\n * than merely remembered — and so is the same hazard for every other name here,\n * any of which a project may legitimately use for a collection of its own.\n *\n * `atlas_schema_revisions` is Atlas's migration ledger, which lands in `rebase`\n * because `db migrate apply` passes `--revisions-schema rebase`.\n *\n * Every entry here must also be revoked by whatever creates it, and vice versa:\n * the creation-time revoke fires once, on the boot that first makes the table,\n * so it cannot help a database provisioned before that revoke existed. This\n * list is what the boot-time sweep in `ensureAppRole` iterates, and the sweep\n * is the only thing that can repair an already-granted table. A table revoked\n * at creation but missing here is therefore permanently stranded on any\n * database that predates its revoke.\n *\n * These names are unqualified, and the boot-time sweep applies them to every\n * schema a project uses — so an entry here is a claim on that name in `public`\n * as much as in `rebase`. `jobs` is Rebase's queue at `rebase.jobs` AND a\n * perfectly ordinary collection name, and revoking `public.jobs` from a project\n * that owns it leaves every read failing 42501 with correct policies applied\n * and nothing in the RLS logs to explain it. The `relrowsecurity` guard in\n * `revokeInternalTableSql` is what makes a common noun safe here — it is not a\n * licence to claim more of them.\n */\nexport const REBASE_INTERNAL_TABLES: readonly string[] = [\n // auth\n \"user_identities\",\n \"refresh_tokens\",\n \"password_reset_tokens\",\n \"magic_link_tokens\",\n \"mfa_factors\",\n \"mfa_challenges\",\n \"recovery_codes\",\n \"app_config\",\n \"schema_meta\",\n // platform services\n \"api_keys\",\n \"cron_logs\",\n \"cron_claims\",\n \"jobs\",\n \"rate_limit_hits\",\n \"idempotency_keys\",\n \"entity_history\",\n \"branches\",\n \"metric_samples\",\n // realtime channels — authorization for these lives in the channel rules the\n // server evaluates before it reads or writes, never in a row policy\n \"channel_messages\",\n \"channel_cursors\",\n \"channel_presence\",\n // migration bookkeeping\n \"atlas_schema_revisions\"\n];\n\n/** Postgres identifiers this module is willing to interpolate. */\nconst SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$]*$/;\n\n/**\n * A single statement that takes every privilege on `schema.table` away from the\n * end-user role.\n *\n * Wrapped in a `DO` block guarded on `pg_roles` for two reasons, both of which\n * happen in practice:\n *\n * - the role does not exist when the connection is unprivileged (Rebase then\n * relies on native RLS rather than a role switch), and a bare `REVOKE` on a\n * missing role is an error, not a no-op;\n * - the table may not exist yet — `cron_logs` never appears in a project with\n * no cron jobs — and `to_regclass` returning NULL has to be tolerated too.\n *\n * The third guard is the one that decides whether the *right* table is being\n * revoked. The names in {@link REBASE_INTERNAL_TABLES} are unqualified, and the\n * boot-time sweep in `ensureAppRole` applies all of them to every schema a\n * project uses — including the schema its own collections live in. A project is\n * free to call a collection `jobs`, `branches` or `api_keys`, and when it does,\n * the sweep was revoking `rebase_user`'s DML on the project's table on every\n * single boot. That is not a subtle degradation: the collection's whole API\n * answers 500 `permission denied for table …` from then on, which is what\n * happened to a public job board whose vacancies live in `public.jobs`.\n *\n * `relrowsecurity` separates the two cleanly, and it is the same fact this\n * module already relies on. Framework-internal tables carry no RLS — that is the\n * premise stated at the top of this file, and the reason a revoke is needed at\n * all. Every collection table has it enabled, because that is how Rebase\n * enforces `securityRules`. So \"RLS is off\" is exactly \"this is not somebody's\n * collection\", and the guard also subsumes the hand-carved `users` exception:\n * the auth user table is a collection, has RLS, and would now be skipped on its\n * own merits rather than by being kept off a list.\n *\n * One command, so it is safe on handles that speak the extended query protocol\n * and reject multi-statement strings.\n */\nexport function revokeInternalTableSql(schema: string, table: string): string {\n if (!SAFE_IDENTIFIER.test(schema)) {\n throw new Error(`Refusing to build SQL with an unsafe schema name: ${JSON.stringify(schema)}`);\n }\n if (!SAFE_IDENTIFIER.test(table)) {\n throw new Error(`Refusing to build SQL with an unsafe table name: ${JSON.stringify(table)}`);\n }\n const qualified = `\"${schema}\".\"${table}\"`;\n return `\n DO $rebase_revoke$\n BEGIN\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${REBASE_USER_ROLE}')\n AND to_regclass('${qualified}') IS NOT NULL\n AND NOT (SELECT relrowsecurity FROM pg_class WHERE oid = to_regclass('${qualified}')) THEN\n EXECUTE 'REVOKE ALL ON ${qualified} FROM ${REBASE_USER_ROLE}';\n END IF;\n END\n $rebase_revoke$;\n `.trim();\n}\n\n/**\n * Revoke on every internal table in `schema`, one statement at a time.\n *\n * Best-effort per table: a connection that does not own one of them (a\n * pre-provisioned database, a platform-managed ledger) cannot revoke on it, and\n * that must not take down a boot. The caller decides how loud to be — `onError`\n * exists so the driver can warn without this module importing a logger.\n */\nexport async function revokeInternalTableAccess(\n execute: (sql: string) => Promise<unknown>,\n schema: string,\n options?: { tables?: readonly string[]; onError?: (table: string, error: unknown) => void }\n): Promise<void> {\n for (const table of options?.tables ?? REBASE_INTERNAL_TABLES) {\n try {\n await execute(revokeInternalTableSql(schema, table));\n } catch (error) {\n options?.onError?.(table, error);\n }\n }\n}\n","import {\n DataSourceDefinition,\n ResolvedDataSource,\n DEFAULT_DATA_SOURCE_KEY,\n getDataSourceCapabilities\n} from \"@rebasepro/types\";\n\n/**\n * The subset of a collection needed to resolve its data source. Accepting a\n * structural type (rather than the full `CollectionConfig`) keeps this usable\n * from anywhere — frontend router, backend registry, editor — without coupling\n * to the collection union.\n */\nexport interface DataSourceResolvable {\n /** Preferred routing key. */\n dataSource?: string;\n /** Engine type discriminant (set on variant collection types). */\n engine?: string;\n /** Within-engine instance. */\n databaseId?: string;\n}\n\n/** A lookup of data-source definitions by key. */\nexport type DataSourceRegistry = Record<string, DataSourceDefinition>;\n\n/**\n * Build a keyed registry from a list of {@link DataSourceDefinition}s.\n * Later entries win on key collision.\n */\nexport function createDataSourceRegistry(definitions?: DataSourceDefinition[]): DataSourceRegistry {\n const registry: DataSourceRegistry = {};\n for (const def of definitions ?? []) {\n registry[def.key] = def;\n }\n return registry;\n}\n\n/**\n * Resolve the effective data source for a collection — the single source of\n * truth shared by the frontend router, the backend driver registry, and the\n * editor's capability lookups.\n *\n * Resolution order:\n * 1. The routing **key** is `collection.dataSource`, else\n * {@link DEFAULT_DATA_SOURCE_KEY}.\n * 2. If a definition is registered for that key, it provides `engine`,\n * `transport`, and `databaseId`.\n * 3. Otherwise values are synthesized: `engine` from `collection.engine`\n * (or the key, or `\"postgres\"`), `transport` defaults to `\"server\"`,\n * and `databaseId` from the collection.\n *\n * `capabilities` are always derived from the resolved `engine`, so two\n * data sources sharing an engine share capabilities.\n *\n * @param collection the collection (or any object carrying the routing fields)\n * @param registry optional registry of declared data sources\n */\nexport function resolveDataSource(\n collection: DataSourceResolvable | undefined,\n registry?: DataSourceRegistry\n): ResolvedDataSource {\n const key = collection?.dataSource ?? DEFAULT_DATA_SOURCE_KEY;\n const def = registry?.[key];\n\n const engine = def?.engine\n ?? collection?.engine\n ?? (key !== DEFAULT_DATA_SOURCE_KEY ? key : \"postgres\");\n\n const transport = def?.transport ?? \"server\";\n const databaseId = collection?.databaseId ?? def?.databaseId;\n\n return {\n key,\n engine,\n transport,\n databaseId,\n capabilities: getDataSourceCapabilities(engine)\n };\n}\n\n/**\n * Does a SQL toolchain own this collection's storage?\n *\n * \"Owns the storage\" means: something generates a table for it, pushes that\n * table to a database, plans its RLS policies, and reports it as drifted when\n * the two disagree. That is true of a Postgres collection and false of a\n * Firestore or MongoDB one, whose documents live in a store Rebase never\n * migrates — and the two were never told apart. Every stage of the SQL\n * toolchain took \"the collections\" to mean *all* of them, so a Firestore\n * collection declared next to the Postgres ones got a `pgTable` in the\n * generated schema, a `CREATE TABLE` at boot, RLS policies, and a place in the\n * `db push` include list — where its name shielding a same-named real table\n * from Atlas's exclude list is the one that can lose data.\n *\n * The answer is the resolved engine's {@link DataSourceCapabilities}, not a\n * name check: an engine registered through `registerDataSourceCapabilities`\n * gets the same treatment as the built-in ones.\n *\n * Deliberately answers **true** for an engine nobody has heard of. Build-time\n * tooling (the CLI, the schema generator) has no data-source registry to\n * resolve a `dataSource` key against, so an unknown key resolves to an unknown\n * engine — and the cost of the two mistakes is not symmetric. Wrongly\n * including a collection generates a table nothing writes to; wrongly excluding\n * one silently stops generating a table the app is serving from. Declare\n * `engine` on a collection that is not SQL-backed and this is exact.\n */\nexport function isRelationalCollection(\n collection: DataSourceResolvable | undefined,\n registry?: DataSourceRegistry\n): boolean {\n // The collection's own `engine` wins over a registered definition's. That\n // is the opposite of {@link resolveDataSource}'s precedence, deliberately:\n // there a definition describes where the data *goes*, so it should override;\n // here the question is what the author said this collection is, and a\n // collection declaring `engine: \"firestore\"` with no `dataSource` must not\n // come back as the default source's engine and be handed a table.\n const engine = collection?.engine\n ?? (collection?.dataSource ? resolveDataSource(collection, registry).engine : undefined);\n return getDataSourceCapabilities(engine).supportsRelations;\n}\n\n/**\n * The subset of `collections` a SQL toolchain owns — see\n * {@link isRelationalCollection}.\n *\n * Every stage that generates SQL from collections starts by calling this, so\n * the rule lives in one place rather than being re-decided per generator. It\n * keeps the input order.\n */\nexport function relationalCollections<C extends DataSourceResolvable>(\n collections: readonly C[],\n registry?: DataSourceRegistry\n): C[] {\n return collections.filter(collection => isRelationalCollection(collection, registry));\n}\n","import {\n ArrayProperty,\n CollectionCallbacks,\n EngineProperties,\n CollectionConfig,\n getDataSourceCapabilities,\n getDeclaredSubcollections,\n NumberProperty,\n Properties,\n Property,\n Relation,\n RelationProperty,\n StringProperty\n} from \"@rebasepro/types\";\nimport { deepEqual } from \"fast-equals\";\n\nimport {\n enumToObjectEntries,\n findRelation,\n getSubcollections,\n getTableName,\n resolveCollectionRelations,\n resolveRelation\n} from \"../util\";\nimport { deepClone, mergeDeep, removeFunctions } from \"@rebasepro/utils\";\nimport { DataSourceRegistry, resolveDataSource } from \"../data/resolveDataSource\";\n\nexport class CollectionRegistry {\n\n /**\n * Declared data sources, used during normalization to resolve each\n * collection's engine (so `dataSource`-only collections get the right\n * capabilities). Empty by default.\n */\n private dataSources: DataSourceRegistry = {};\n\n /**\n * Global lifecycle callbacks applied to every collection.\n * Runs on all data paths (REST, WebSocket, `rebase.dataAsAdmin`).\n * Execution order: global → collection → property callbacks.\n */\n private _globalCallbacks?: CollectionCallbacks;\n\n /**\n * Set global lifecycle callbacks that apply to every collection.\n * Typically called once during backend initialization.\n */\n setGlobalCallbacks(callbacks: CollectionCallbacks): void {\n this._globalCallbacks = callbacks;\n }\n\n /**\n * Get the currently registered global callbacks, if any.\n */\n getGlobalCallbacks(): CollectionCallbacks | undefined {\n return this._globalCallbacks;\n }\n\n // Normalized runtime layer (used by Data Grid / UI)\n private collectionsByTableName = new Map<string, CollectionConfig>();\n private collectionsBySlug = new Map<string, CollectionConfig>();\n private rootCollections: CollectionConfig[] = [];\n private cachedCollectionsList: CollectionConfig[] | null = null;\n\n // Raw configuration layer (used by Collection Editor AST generator)\n private rawCollectionsByTableName = new Map<string, CollectionConfig>();\n private rawCollectionsBySlug = new Map<string, CollectionConfig>();\n private rawRootCollections: CollectionConfig[] = [];\n private cachedRawCollectionsList: CollectionConfig[] | null = null;\n\n // Entity of raw input for idempotency check — compared BEFORE normalization\n // to avoid the issue where normalization creates new objects that always fail equality.\n private lastRawInputEntity: ReturnType<typeof removeFunctions>[] | null = null;\n\n constructor(collections?: CollectionConfig[], dataSources?: DataSourceRegistry) {\n if (dataSources) this.dataSources = dataSources;\n if (collections) {\n this.registerMultiple(collections);\n }\n }\n\n /**\n * Provide the declared data sources used to resolve each collection's\n * engine during normalization. Set this before registering collections.\n * Returns true if the registry changed (callers may re-register).\n */\n setDataSources(dataSources: DataSourceRegistry): boolean {\n if (deepEqual(this.dataSources, dataSources)) return false;\n this.dataSources = dataSources ?? {};\n return true;\n }\n\n reset() {\n this.collectionsByTableName.clear();\n this.collectionsBySlug.clear();\n this.rootCollections = [];\n this.cachedCollectionsList = null;\n\n this.rawCollectionsByTableName.clear();\n this.rawCollectionsBySlug.clear();\n this.rawRootCollections = [];\n this.cachedRawCollectionsList = null;\n }\n\n /**\n * Registers a collection and its subcollections recursively.\n * Returns true if the collections have changed, false otherwise.\n *\n * Idempotent: compares the raw input (before normalization) against a stored\n * entity. Only re-normalizes and re-registers when the raw input actually changed.\n * @param collections\n */\n registerMultiple(collections: CollectionConfig[]): boolean {\n // Compare raw input BEFORE normalization to detect actual changes.\n // This avoids the old issue where normalization creates new objects\n // that always fail deep-equal even when the source data is identical.\n const rawEntity = collections.map(c => removeFunctions(c));\n if (this.lastRawInputEntity && deepEqual(this.lastRawInputEntity, rawEntity)) {\n return false;\n }\n\n this.reset();\n // Phase 0: Populate maps with raw collections first for string target resolution\n collections.forEach((c) => {\n if (c.slug) {\n this.collectionsBySlug.set(c.slug, c);\n }\n this.collectionsByTableName.set(getTableName(c), c);\n });\n\n const normalizedCollections = collections.map(c => this.normalizeCollection({ ...c }));\n\n // Phase 1: Register all top-level collections first (without recursion).\n // This ensures that injected entityViews (e.g. History tab) are preserved.\n // Without this, _registerRecursively could register a relation-target collection\n // (e.g. Tags from Posts.relations) using the raw module object (without injected views)\n // before the top-level Tags collection (with injected views) gets its turn.\n normalizedCollections.forEach((c, index) => {\n const raw = deepClone(collections[index]);\n this.rootCollections.push(c);\n this.rawRootCollections.push(raw);\n\n const normalized = this.normalizeCollection(c);\n this.collectionsByTableName.set(getTableName(normalized), normalized);\n this.rawCollectionsByTableName.set(getTableName(raw), raw);\n if (normalized.slug) {\n this.collectionsBySlug.set(normalized.slug, normalized);\n }\n if (raw.slug) {\n this.rawCollectionsBySlug.set(raw.slug, raw);\n }\n });\n\n // Phase 2: Now recurse into subcollections (relations, etc.)\n normalizedCollections.forEach((c) => {\n const subcollections = getSubcollections(c);\n if (subcollections && subcollections.length > 0) {\n subcollections.forEach((subCollection) => {\n if (!subCollection) return;\n // Spread to avoid mutating the original target() return value\n this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));\n });\n }\n });\n\n // Store the entity for future comparisons\n this.lastRawInputEntity = rawEntity;\n\n return true;\n }\n\n register(collection: CollectionConfig, rawCollection?: CollectionConfig) {\n const raw = rawCollection ? deepClone(rawCollection) : deepClone(collection);\n\n this.rootCollections.push(collection);\n this.rawRootCollections.push(raw);\n\n this._registerRecursively(collection, raw);\n }\n\n private _registerRecursively(collection: CollectionConfig, rawCollection: CollectionConfig) {\n if (this.collectionsByTableName.has(getTableName(collection))) {\n return;\n }\n\n const normalizedCollection = this.normalizeCollection(collection);\n this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);\n this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);\n\n if (normalizedCollection.slug) {\n this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);\n }\n if (rawCollection.slug) {\n this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);\n }\n\n // Use the normalized collection for subcollection discovery so that\n // both inline-extracted and explicit relations are considered.\n const subcollections = getSubcollections(normalizedCollection);\n\n if (subcollections && subcollections.length > 0) {\n subcollections.forEach((subCollection) => {\n if (!subCollection) return;\n // Spread to avoid mutating the original target() return value\n this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));\n });\n }\n }\n\n public normalizeCollection(collection: CollectionConfig): CollectionConfig {\n // Work on a shallow copy to avoid mutating the caller's reference.\n // This is critical for idempotency (the raw input must not be changed)\n // and for preventing mutation of module-level collection singletons.\n const result = { ...collection } as CollectionConfig;\n\n // 0. Resolve and stamp `dataSource` and `engine` on the normalized copy.\n // After this block every normalized collection has both fields set,\n // so downstream code can read them directly without calling\n // `resolveDataSource()`. Only the normalized layer is affected —\n // the raw layer used by the collection editor keeps the author's\n // original fields.\n {\n const resolved = resolveDataSource(result, this.dataSources);\n if (!result.dataSource) (result as { dataSource?: string }).dataSource = resolved.key;\n if (!result.engine) (result as { engine?: string }).engine = resolved.engine;\n }\n\n // Relations are left exactly as authored.\n //\n // This used to hoist every inline relation property into\n // `collection.relations`, merge it with the declared ones, and run each\n // through `sanitizeRelation` — a pass that guessed at missing fields and\n // fell back to the raw relation when it threw. `resolveCollectionRelations`\n // now reads both sources itself and defaults deterministically, so there\n // is nothing to hoist, nothing to merge and nothing to guess.\n //\n // The hoisting also had a defect worth not reinstating: it flattened\n // relations declared inside a `map` up to the collection's top level,\n // where they became child-view tabs keyed by the inner property key.\n\n // Stamp each relation property with its resolved relation.\n const properties: Properties = this.normalizeProperties(result.properties, result);\n result.properties = properties as EngineProperties;\n\n // `childCollections` is deliberately NOT populated here.\n //\n // It used to be, from the same many-relations `getEntityChildViews`\n // reads — but stamped with the *target's* slug rather than the relation\n // key, and then cached onto the collection, so the registry's version\n // shadowed the correct one for every consumer downstream. Deriving on\n // read leaves one implementation and keeps `childCollections` meaning\n // what it documents: a custom driver's explicit override.\n return result;\n }\n\n private normalizeProperties(properties: Properties, collection: CollectionConfig): Properties {\n const newProperties: Properties = {};\n for (const key in properties) {\n newProperties[key] = this.normalizeProperty(key, properties[key], collection);\n }\n return newProperties;\n }\n\n private normalizeProperty(key: string, property: Property, collection: CollectionConfig): Property {\n const newProperty = { ...property };\n\n if (newProperty.type === \"map\" && newProperty.properties) {\n newProperty.properties = this.normalizeProperties(newProperty.properties, collection);\n } else if (newProperty.type === \"array\") {\n // Cast to get a properly typed mutable reference\n const arrayProp = newProperty as ArrayProperty;\n if (arrayProp.of) {\n if (Array.isArray(arrayProp.of)) {\n (arrayProp as { of: Property | Property[] }).of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, collection));\n } else {\n arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, collection);\n }\n } else if (arrayProp.oneOf && arrayProp.oneOf.properties) {\n arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, collection);\n }\n } else if ((newProperty.type === \"string\" || newProperty.type === \"number\") && newProperty.enum) {\n const stringOrNumberProperty = newProperty as StringProperty | NumberProperty;\n if (typeof stringOrNumberProperty.enum === \"object\" && !Array.isArray(stringOrNumberProperty.enum)) {\n stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];\n }\n } else if (newProperty.type === \"relation\") {\n const relationProperty = newProperty as RelationProperty;\n\n // A property either declares its link inline, or names one the\n // collection declares. Resolve the first directly; look the second\n // up by name. Either way the property carries the fully-defaulted\n // relation, so no consumer has to re-derive it.\n if (relationProperty.relation) {\n relationProperty.resolvedRelation = resolveRelation(relationProperty.relation, collection, key);\n } else {\n const declared = resolveCollectionRelations(collection)[key];\n if (declared) {\n relationProperty.resolvedRelation = declared;\n } else {\n // The boot validator refuses this shape outright, naming the\n // property and both ways to fix it — see\n // `checkRelationPropertiesResolve` in @rebasepro/server. This\n // stays as the second line, for the registries built outside\n // a validated boot: the panel's, and the collection editor's\n // preview of a config being written.\n //\n // Still `console.warn`. There is no logger below\n // @rebasepro/server, and this package runs in the browser as\n // well as on the server, so acquiring one is a design\n // decision rather than a substitution.\n console.warn(\n `Relation property '${key}' on '${collection.slug}' names no relation: it has no ` +\n \"`relation` block, and the collection's `relations` array has no entry called \" +\n `'${key}'. The field will render no picker, generate no foreign key, and return ` +\n \"nothing from `include()`.\"\n );\n }\n }\n }\n\n return newProperty;\n }\n\n get(path: string): CollectionConfig | undefined {\n // First try slug lookup\n const bySlug = this.collectionsBySlug.get(path);\n if (bySlug) return bySlug;\n\n // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)\n if (path.includes(\"-\")) {\n const normalized = path.replace(/-/g, \"_\");\n const byNormalized = this.collectionsBySlug.get(normalized);\n if (byNormalized) return byNormalized;\n }\n\n // Fallback to table name lookup\n return this.collectionsByTableName.get(path);\n }\n\n /**\n * Gets the pristine, un-normalized collection exactly as it was provided.\n * Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.\n */\n getRaw(path: string): CollectionConfig | undefined {\n const bySlug = this.rawCollectionsBySlug.get(path);\n if (bySlug) return bySlug;\n\n // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)\n if (path.includes(\"-\")) {\n const normalized = path.replace(/-/g, \"_\");\n const byNormalized = this.rawCollectionsBySlug.get(normalized);\n if (byNormalized) return byNormalized;\n }\n\n return this.rawCollectionsByTableName.get(path);\n }\n\n /**\n * Get collection by resolving multi-segment paths through relations\n * e.g., \"authors/70/posts\" resolves to the posts collection\n */\n getCollectionByPath(collectionPath: string): CollectionConfig | undefined {\n // Handle simple single collection path\n if (!collectionPath.includes(\"/\")) {\n return this.get(collectionPath);\n }\n\n // Handle multi-segment paths by resolving through relations\n const pathSegments = collectionPath.split(\"/\").filter(p => p);\n\n if (pathSegments.length < 3 || pathSegments.length % 2 === 0) {\n throw new Error(`Invalid relation path: ${collectionPath}. Expected format: collection/id/relation or collection/id/relation/id/relation`);\n }\n\n // Start with the root collection\n const rootCollectionPath = pathSegments[0];\n let currentCollection = this.get(rootCollectionPath);\n\n if (!currentCollection) {\n throw new Error(`Root collection not found: ${rootCollectionPath}`);\n }\n\n // Navigate through the path using relations\n for (let i = 2; i < pathSegments.length; i += 2) {\n const relationKey = pathSegments[i];\n\n // Get relations for current collection\n if (!getDataSourceCapabilities(currentCollection.engine).supportsRelations) {\n throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses engine '${currentCollection.engine}'`);\n }\n const resolvedRelations = resolveCollectionRelations(currentCollection);\n const relation = findRelation(resolvedRelations, relationKey);\n\n if (!relation) {\n throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);\n }\n\n // Move to the target collection.\n //\n // By the relation's own target, never by a slug lookup on its\n // *name*: `this.get(relation.relationName)` searches the global slug\n // map, so a relation named `people` that targets `notes` resolved to\n // an unrelated root collection called `people` — and a nested write\n // then ran that collection's callbacks against its properties.\n // The registered instance is preferred, matched by table, to pick up\n // whatever normalization and injection it received.\n const target = relation.target();\n currentCollection = this.collectionsByTableName.get(getTableName(target))\n ?? this.normalizeCollection(target);\n\n // If there are more segments, continue navigation\n if (i + 1 < pathSegments.length) {\n // Skip entity ID segment\n }\n }\n\n return currentCollection;\n }\n\n getCollections(): CollectionConfig[] {\n if (!this.cachedCollectionsList) {\n this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());\n }\n return this.cachedCollectionsList;\n }\n\n getRawCollections(): CollectionConfig[] {\n if (!this.cachedRawCollectionsList) {\n this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());\n }\n return this.cachedRawCollectionsList;\n }\n\n /**\n * Resolves a multi-segment path like \"products/123/locales\" and returns\n * information about the collections and entity IDs along the path\n */\n resolvePathToCollections(path: string): {\n collections: CollectionConfig[],\n entityIds: (string | number)[],\n finalCollection: CollectionConfig\n } {\n const pathSegments = path.split(\"/\").filter(p => p);\n\n if (pathSegments.length === 0) {\n throw new Error(`Invalid path: ${path}`);\n }\n\n if (pathSegments.length % 2 !== 1) {\n throw new Error(`Invalid collection path: ${path}. It must have an odd number of segments.`);\n }\n\n const collections: CollectionConfig[] = [];\n const entityIds: (string | number)[] = [];\n\n // Start with the first collection\n let currentCollection = this.get(pathSegments[0]);\n\n if (!currentCollection) {\n throw new Error(`Unknown collection path or slug: ${pathSegments[0]}`);\n }\n\n collections.push(currentCollection);\n\n // Process the rest of the path in pairs (entityId, subcollectionSlug)\n for (let i = 1; i < pathSegments.length; i += 2) {\n const entityId = pathSegments[i];\n entityIds.push(entityId);\n\n if (i + 1 < pathSegments.length) {\n const subcollectionSlug = pathSegments[i + 1];\n const subcollections: CollectionConfig[] | undefined = getSubcollections(currentCollection);\n if (!subcollections || subcollections.length === 0) {\n throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);\n }\n\n const subcollection: CollectionConfig | undefined = subcollections.find(c => c.slug === subcollectionSlug);\n if (!subcollection) {\n throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);\n }\n // The child as resolved, not whatever root collection happens to\n // share its slug. Re-looking it up globally both risked the wrong\n // collection and discarded the relation's `overrides`, which are\n // applied when the child view is built.\n currentCollection = this.normalizeCollection(subcollection);\n collections.push(currentCollection);\n }\n }\n\n return {\n collections,\n entityIds,\n finalCollection: currentCollection\n };\n }\n\n}\n\n","import { defineCollection } from \"../util/builders\";\n\n/**\n * Default users collection.\n *\n * Prepended to the developer's collections array by the admin and server.\n * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers\n * override by defining their own collection with `slug: \"users\"`.\n *\n * Schema only — no `admin` block. This package is on the backend's dependency path,\n * where that field does not exist: `@rebasepro/cms-types` adds it by declaration\n * merging, and a BaaS install never installs that. The scaffolded\n * `config/collections/users.ts` carries the presentation for projects that want this\n * collection in their panel, which is also where it is editable.\n */\nexport const defaultUsersCollection = defineCollection({\n name: \"Users\",\n singularName: \"User\",\n slug: \"users\",\n auth: true,\n table: \"users\",\n schema: \"rebase\",\n securityRules: [\n { operation: \"select\",\nroles: [\"admin\"] },\n { operations: [\"insert\", \"update\", \"delete\"],\nroles: [\"admin\"] }\n ],\n properties: {\n id: {\n name: \"ID\",\n type: \"string\",\n isId: \"uuid\"\n },\n email: {\n name: \"Email\",\n type: \"string\",\n validation: { required: true,\nunique: true }\n },\n displayName: {\n name: \"Name\",\n type: \"string\",\n columnName: \"display_name\",\n validation: { required: true }\n },\n photoURL: {\n name: \"Photo URL\",\n type: \"string\",\n columnName: \"photo_url\"\n },\n roles: {\n name: \"Roles\",\n type: \"array\",\n columnType: \"text[]\",\n of: {\n name: \"Role\",\n type: \"string\",\n enum: {\n admin: \"Admin\",\n editor: \"Editor\",\n viewer: \"Viewer\"\n }\n }\n },\n passwordHash: {\n name: \"Password Hash\",\n type: \"string\",\n columnName: \"password_hash\",\n excludeFromApi: true\n },\n emailVerified: {\n name: \"Email Verified\",\n type: \"boolean\",\n columnName: \"email_verified\",\n defaultValue: false\n },\n emailVerificationToken: {\n name: \"Email Verification Token\",\n type: \"string\",\n columnName: \"email_verification_token\",\n excludeFromApi: true\n },\n emailVerificationSentAt: {\n name: \"Email Verification Sent At\",\n type: \"date\",\n columnName: \"email_verification_sent_at\"\n },\n metadata: {\n name: \"Metadata\",\n type: \"map\",\n keyValue: true,\n properties: {},\n defaultValue: {}\n },\n createdAt: {\n name: \"Created At\",\n type: \"date\",\n columnName: \"created_at\",\n autoValue: \"on_create\"\n },\n updatedAt: {\n name: \"Updated At\",\n type: \"date\",\n columnName: \"updated_at\",\n autoValue: \"on_update\"\n }\n }\n});\n","import type { CollectionConfig, FieldAccess, Property } from \"@rebasepro/types\";\n\n/**\n * Field-level access control: one mechanism, read by every enforcement point.\n *\n * A collection's `securityRules` decide which *rows* a caller reaches;\n * `property.access` decides which *fields* of a reached row they see and may\n * set. The two are independent — a field rule never widens row access, and a\n * row a caller cannot read has no fields to talk about.\n *\n * `excludeFromApi` is sugar for `access: { read: [], write: [] }` and is\n * normalised into it by {@link effectiveAccess}, which is the only place either\n * spelling is read. It used to be its own code path in five files — the read\n * strip, the write refusal, the SDK generator, the OpenAPI schema builder and\n * the filter-parameter builder — and the second rule would have made ten.\n * There is one predicate now, and the flag is a shorthand for it.\n *\n * @module\n */\n\n/**\n * The caller a field rule is judged against: whatever the call context carries\n * as the user's application roles.\n *\n * `undefined` is the trusted server plane — an in-process `rebase.data` call\n * with no request behind it, the auth adapter writing a password hash, a\n * migration. Every API boundary has a viewer: an unauthenticated REST request is\n * scoped as `{ uid: ANONYMOUS_USER_ID, roles: [\"anon\"] }` before it reaches a\n * driver, so \"no viewer\" cannot be reached from outside.\n */\nexport interface FieldViewer {\n roles?: readonly string[];\n}\n\n/**\n * The role that satisfies any non-empty list.\n *\n * The same arm every baseline policy carries: `security_rules` injects\n * `rolesOverlap(['admin'])` into the default read and write policies, and\n * `rebase.dataAsAdmin` is scoped with `{ uid: \"service\", roles: [\"admin\"] }`.\n * Without this an author could declare `access: { read: [\"hr\"] }` and lock the\n * administrator out of a column of their own database — and lock the Studio out\n * of rendering it.\n */\nexport const ADMIN_ROLE = \"admin\";\n\n/**\n * What a property's access rules actually are, with `excludeFromApi` expanded.\n *\n * Returns `undefined` when the property constrains nothing, so callers can skip\n * the whole check for the overwhelmingly common case.\n */\nexport function effectiveAccess(property: Property | undefined): FieldAccess | undefined {\n if (!property) return undefined;\n if (property.excludeFromApi) return EXCLUDED_ACCESS;\n const access = property.access;\n if (!access) return undefined;\n if (access.read === undefined && access.write === undefined) return undefined;\n return access;\n}\n\n/** The rule `excludeFromApi: true` expands to. Frozen: it is shared by every caller. */\nconst EXCLUDED_ACCESS: FieldAccess = Object.freeze({ read: Object.freeze([]), write: Object.freeze([]) });\n\n/**\n * Does a caller holding `roles` satisfy `allowed`?\n *\n * Three cases, and the middle one is the one worth stating out loud:\n *\n * - `allowed` omitted — the field carries no rule of its own, so the row's\n * policies have already answered. True.\n * - `allowed` empty — nobody, at any privilege, through any API. Not the admin,\n * not the service key, not the trusted plane reading on a caller's behalf.\n * This is what `excludeFromApi` has always meant on the read side, and\n * collapsing the two spellings means the empty list has to keep meaning it.\n * - `allowed` non-empty — one of the named roles, or `admin`, or no viewer at\n * all (the trusted server plane, which is not an API caller).\n */\nfunction satisfies(allowed: readonly string[] | undefined, viewer: FieldViewer | undefined): boolean {\n if (allowed === undefined) return true;\n if (allowed.length === 0) return false;\n if (!viewer) return true;\n const roles = viewer.roles;\n if (!roles || roles.length === 0) return false;\n return roles.includes(ADMIN_ROLE) || allowed.some(role => roles.includes(role));\n}\n\n/** May this caller receive this field's value? */\nexport function canReadField(property: Property | undefined, viewer: FieldViewer | undefined): boolean {\n const access = effectiveAccess(property);\n return access ? satisfies(access.read, viewer) : true;\n}\n\n/** May this caller set this field's value? */\nexport function canWriteField(property: Property | undefined, viewer: FieldViewer | undefined): boolean {\n const access = effectiveAccess(property);\n return access ? satisfies(access.write, viewer) : true;\n}\n\n/**\n * The names on this collection a caller may not touch, in the two spellings a\n * caller can write them in.\n *\n * `declared` is the property keys, which is what has to leave a *known-fields*\n * set. `refused` is those plus the physical column names behind them: a caller\n * who knows the table can send `password_hash` as readily as `passwordHash`, and\n * a rule that only knew the wire name would be one rename away from useless.\n *\n * `kind` picks which half of the rule is read; nothing else differs.\n */\nexport function restrictedFieldNames(\n collection: CollectionConfig,\n viewer: FieldViewer | undefined,\n kind: \"read\" | \"write\"\n): { declared: string[]; refused: Set<string> } {\n const declared: string[] = [];\n const refused = new Set<string>();\n const allowed = kind === \"read\" ? canReadField : canWriteField;\n\n for (const [name, property] of Object.entries(collection.properties ?? {})) {\n if (allowed(property as Property, viewer)) continue;\n declared.push(name);\n refused.add(name);\n const columnName = (property as Property).columnName;\n if (columnName) refused.add(columnName);\n }\n return { declared, refused };\n}\n\n/**\n * True when nothing on this collection restricts a field, for either direction.\n *\n * Every read of every row runs through the strip, so the collection that has no\n * rules — which is almost all of them — has to cost one property walk and no\n * allocation.\n */\nexport function hasFieldAccessRules(collection: CollectionConfig): boolean {\n for (const property of Object.values(collection.properties ?? {})) {\n if (effectiveAccess(property as Property)) return true;\n }\n return false;\n}\n","import type { OrderByTuple } from \"@rebasepro/types\";\n\n/**\n * The keyset-cursor wire codec.\n *\n * ## Why this is one module\n *\n * Keyset pagination was implemented three times and reachable once. The driver\n * has a NULL-correct multi-key comparison (`FetchService.buildKeysetComparison`)\n * that only a WebSocket `startAfter` could reach; REST could not seek at all;\n * and the SDK's `iterate({cursor})` re-implemented a *single*-column keyset as a\n * `where` clause, which threw on any multi-key sort and silently dropped rows\n * whose sort value was NULL. Three implementations, three answers to \"what is\n * page two\".\n *\n * There is now one. The driver's comparison is the implementation; this module\n * is the only thing that says how a cursor is written down, and every transport\n * — the REST `?after=`, the WebSocket `startAfter`, the SDK's `iterate()` —\n * carries the string this produces and hands it back unread.\n *\n * ## What a cursor holds\n *\n * The sort keys the query was ordered by, the last served row's value for each\n * of them, and that row's id. The keys travel *with* the values because a\n * cursor that carried only values would be silently reinterpretable: paging a\n * `created_at DESC` listing and then asking for `title ASC` would seek on the\n * dates as though they were titles. Carrying the keys makes that a refusal\n * ({@link CursorMismatchError}) rather than a page of arbitrary rows.\n *\n * ## Opacity\n *\n * The encoding is base64url of JSON, and it is **not** API. It is opaque so it\n * can change — adding a key, changing how a value is tagged — without every\n * client that learned to read it breaking. Nothing outside this file parses it.\n *\n * @module\n */\n\n/** The decoded contents of a cursor. */\nexport interface DecodedCursor {\n /** The sort keys the cursor was produced under, in order of significance. */\n orderBy: OrderByTuple[];\n /** The last served row's value for each sort key, by field name. */\n values: Record<string, unknown>;\n /** The last served row's id, which breaks ties on the last key. */\n id: unknown;\n}\n\n/** A cursor that cannot be read at all — truncated, re-encoded, or invented. */\nexport class CursorError extends Error {\n readonly code = \"INVALID_CURSOR\";\n constructor(detail: string) {\n super(\n `Invalid \\`after\\` cursor: ${detail}. Pass back the \\`meta.nextCursor\\` ` +\n \"from the previous page unchanged — it is opaque and must not be built by hand.\"\n );\n this.name = \"CursorError\";\n Object.setPrototypeOf(this, CursorError.prototype);\n }\n}\n\n/**\n * A cursor that reads fine but describes a different query.\n *\n * Separate from {@link CursorError} because the fix is different: this one is\n * not a corrupt string, it is a correct cursor used against a sort it was not\n * produced under. Seeking anyway would return rows in an order nobody asked\n * for, and — worse — would look like it worked.\n */\nexport class CursorMismatchError extends Error {\n readonly code = \"CURSOR_ORDER_MISMATCH\";\n constructor(cursorKeys: string[], queryKeys: string[]) {\n super(\n `The \\`after\\` cursor was produced by a query ordered by ` +\n `${cursorKeys.map(k => `\"${k}\"`).join(\", \") || \"(nothing)\"}, but this query orders by ` +\n `${queryKeys.map(k => `\"${k}\"`).join(\", \") || \"(nothing)\"}. A cursor only continues the ` +\n \"listing it came from — keep `orderBy` identical across pages, or drop `after` to start over.\"\n );\n this.name = \"CursorMismatchError\";\n Object.setPrototypeOf(this, CursorMismatchError.prototype);\n }\n}\n\n/**\n * Tag for a value whose JSON round-trip would otherwise lose its type.\n *\n * A `timestamp` column comes back from the driver as a `Date`; JSON turns it\n * into a string, and the string would then be compared against the column by\n * whatever cast Postgres chose. Round-tripping it as a `Date` keeps the\n * comparison the one the ORDER BY made.\n */\nconst DATE_TAG = \"$date\";\n\nfunction encodeValue(value: unknown): unknown {\n if (value instanceof Date) return { [DATE_TAG]: value.toISOString() };\n return value;\n}\n\nfunction decodeValue(value: unknown): unknown {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const tagged = (value as Record<string, unknown>)[DATE_TAG];\n if (typeof tagged === \"string\") {\n const date = new Date(tagged);\n return Number.isNaN(date.getTime()) ? tagged : date;\n }\n }\n return value;\n}\n\n/** base64url, without depending on Node's Buffer (this package runs in browsers). */\nfunction toBase64Url(text: string): string {\n const bytes = new TextEncoder().encode(text);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(encoded: string): string {\n const padded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\")\n + \"=\".repeat((4 - (encoded.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode \"everything strictly after this row, in this order\".\n *\n * @param orderBy the sort keys the listing ran under, in order of significance\n * @param row the last row served, which the next page picks up after\n * @param id that row's id — the tiebreaker every keyset comparison ends on\n * @returns the opaque cursor, or `undefined` when no cursor can describe the\n * page. That is not a failure: a listing sorted by relevance has no stored\n * value to compare a later page against (scores are computed per query and\n * are not on the same scale between two of them), so it pages by offset and\n * `meta.nextCursor` is simply absent.\n */\nexport function encodeCursor(\n orderBy: OrderByTuple[] | undefined,\n row: Record<string, unknown>,\n id: unknown\n): string | undefined {\n if (id === undefined || id === null) return undefined;\n const keys = orderBy ?? [];\n // A key whose value is not on the row cannot be seeked past. Rather than\n // emit a cursor that the next request would refuse, emit none — the caller\n // falls back to offset paging, which is what it did before cursors existed.\n const values: Record<string, unknown> = {};\n for (const [field] of keys) {\n if (!(field in row)) return undefined;\n values[field] = encodeValue(row[field]);\n }\n return toBase64Url(JSON.stringify({ k: keys, v: values, i: encodeValue(id) }));\n}\n\n/**\n * Read a cursor produced by {@link encodeCursor}.\n *\n * @throws {CursorError} when the string is not a cursor this codec wrote.\n */\nexport function decodeCursor(raw: string): DecodedCursor {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fromBase64Url(raw.trim()));\n } catch {\n throw new CursorError(\"it is not a cursor this API issued\");\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new CursorError(\"it does not decode to a cursor\");\n }\n const body = parsed as { k?: unknown; v?: unknown; i?: unknown };\n if (!Array.isArray(body.k)) throw new CursorError(\"it carries no sort keys\");\n if (body.i === undefined) throw new CursorError(\"it carries no row id\");\n\n const orderBy: OrderByTuple[] = [];\n for (const entry of body.k) {\n if (!Array.isArray(entry) || typeof entry[0] !== \"string\") {\n throw new CursorError(\"one of its sort keys is malformed\");\n }\n const direction = entry[1] === \"desc\" ? \"desc\" : \"asc\";\n orderBy.push(entry[2] === \"first\" || entry[2] === \"last\"\n ? [entry[0], direction, entry[2]]\n : [entry[0], direction]);\n }\n\n const rawValues = (body.v && typeof body.v === \"object\" && !Array.isArray(body.v))\n ? body.v as Record<string, unknown>\n : {};\n const values: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(rawValues)) values[field] = decodeValue(value);\n\n return { orderBy, values, id: decodeValue(body.i) };\n}\n\n/**\n * The `orderBy` a request should run under, given a cursor and whatever sort\n * the request itself named.\n *\n * A request that names no sort **adopts the cursor's** — that is what makes\n * `find({ after })` work without restating the `orderBy` from the previous\n * call, and it cannot be wrong, since the cursor is the only sort in play.\n * A request that names one must name the *same* one, key for key, direction for\n * direction, nulls for nulls; anything else is {@link CursorMismatchError}.\n *\n * @throws {CursorMismatchError}\n */\nexport function reconcileCursorOrder(\n cursor: DecodedCursor,\n requested: OrderByTuple[] | undefined\n): OrderByTuple[] {\n if (!requested || requested.length === 0) return cursor.orderBy;\n const spell = (keys: OrderByTuple[]) =>\n keys.map(([field, direction, nulls]) => `${field}:${direction}${nulls ? `:${nulls}` : \"\"}`);\n const cursorKeys = spell(cursor.orderBy);\n const queryKeys = spell(requested);\n if (cursorKeys.length !== queryKeys.length\n || cursorKeys.some((key, i) => key !== queryKeys[i])) {\n throw new CursorMismatchError(cursorKeys, queryKeys);\n }\n return requested;\n}\n\n/**\n * The `startAfter` shape the driver contract takes, built from a cursor.\n *\n * The driver has always accepted `{ id, values }`; this is the one place that\n * shape is produced, so the REST route and the WebSocket ingress cannot drift\n * into two spellings of the same seek.\n */\nexport function cursorToStartAfter(cursor: DecodedCursor): Record<string, unknown> {\n return { id: cursor.id, values: cursor.values };\n}\n","import type { NullsPlacement, OrderBySortTuple, OrderBySpec, OrderByTuple } from \"@rebasepro/types\";\nimport { isRelationAggregateSort, sortKeyToString } from \"@rebasepro/types\";\n\n/**\n * Sort-order wire codec.\n *\n * This is the ONLY module that knows about the colon-delimited wire format\n * (`\"field:direction\"`) used in HTTP query parameters, and about the JSON-array\n * form that carries a multi-column sort over the same parameter.\n * Everything else speaks {@link OrderByTuple} exclusively.\n *\n * Mirrors the filter architecture in `filter-dialect.ts`.\n *\n * @module\n */\n\n/**\n * Collapse the one-key and many-key spellings of a sort into the list form.\n *\n * `[\"a\", \"desc\"]` and `[[\"a\", \"desc\"]]` mean the same thing and normalize to\n * the same value; the two are told apart by whether the first element is\n * itself an array, which no field name ever is.\n *\n * This is also where a {@link RelationAggregateSort} object stops being an\n * object. Above this function a sort key may be either spelling; below it,\n * every key is a string — which is what `OrderByTuple`, the REST parameter, the\n * driver contract and the cursor all already were. Doing it here means the one\n * place that already collapses the two *shapes* of a sort also collapses the\n * two *spellings* of a key, rather than every consumer learning about both.\n *\n * @returns The keys in order of significance, or `undefined` for no sort. An\n * empty list also returns `undefined` — \"sort by nothing\" is no sort, and\n * letting `[]` through would have every layer below re-deciding what it meant.\n */\nexport function normalizeOrderBy(orderBy?: OrderBySpec): OrderByTuple[] | undefined {\n if (!orderBy || orderBy.length === 0) return undefined;\n // An aggregate key is an object, so the first element being an array still\n // tells the list form from the single-tuple one — no field name is an\n // array, and neither is an aggregate key.\n const list = Array.isArray(orderBy[0])\n ? orderBy as OrderBySortTuple[]\n : [orderBy as OrderBySortTuple];\n if (list.length === 0) return undefined;\n // Through `toStrictTuple`, not a destructure. `([key, direction]) => …` over\n // whatever it was handed is only safe for a caller the types checked, and\n // this is reached straight from `find({ orderBy })` — where the plausible\n // mistakes are an object (`{ title: \"asc\" }`, which is how every other\n // query API spells a sort) and a bare number. Both used to come back as\n // `TypeError: object is not iterable`, from a package the caller has never\n // heard of, with no `code` and no field name, while the same call's `where`\n // clause answers with a `RebaseClientError` naming the field and the fix.\n return list.map((entry, index) => toStrictTuple(entry, index));\n}\n\n/**\n * The most significant sort key, for a caller that can only express one —\n * a column header's arrow, a URL parameter, a driver that has not been taught\n * the list form.\n */\nexport function primaryOrderBy(orderBy?: OrderBySpec): OrderByTuple | undefined {\n return normalizeOrderBy(orderBy)?.[0];\n}\n\n/**\n * Collapse the driver-level `{orderBy, order}` pair into the list form.\n *\n * The driver contract spells a single-column sort as a field name plus a\n * separate direction, and a multi-column one as a list of tuples that leaves\n * `order` meaningless. Every driver reads both through here so neither\n * spelling has to be handled twice.\n *\n * An absent direction means ascending — the same thing a bare `?orderBy=name`\n * has always meant over HTTP. The Postgres driver used to read the same pair as\n * *descending* while Mongo read it as ascending, so one field name and no\n * direction described two different queries depending on which database was\n * underneath. Neither had a caller: every path in the workspace passes a\n * direction, which is why the disagreement went unnoticed rather than being\n * load-bearing.\n */\nexport function normalizeDriverOrderBy(\n orderBy?: string | OrderByTuple[],\n order?: \"asc\" | \"desc\"\n): OrderByTuple[] | undefined {\n if (!orderBy) return undefined;\n if (typeof orderBy === \"string\") return [[orderBy, order === \"desc\" ? \"desc\" : \"asc\"]];\n return orderBy.length > 0 ? orderBy : undefined;\n}\n\n/** A sort whose *shape* is unusable, as opposed to one naming a field that does not exist. */\nexport class OrderBySpecError extends Error {\n readonly code = \"INVALID_ORDER_BY\";\n constructor(detail: string) {\n super(\n `Invalid \\`orderBy\\`: ${detail}. Expected a field name, or a list of ` +\n \"[field, direction] pairs like [[\\\"roles\\\",\\\"asc\\\"],[\\\"created_at\\\",\\\"desc\\\"]]\"\n );\n this.name = \"OrderBySpecError\";\n }\n}\n\n/**\n * Validate an `orderBy` that arrived from outside this process — a WebSocket\n * subscribe frame, a driver call from untyped JavaScript — and return it in the\n * list form.\n *\n * Strict on purpose, in the same way the REST `parseOrderByParam` is: the\n * failure mode for a shape nobody checks is not a crash but a *silently\n * different query*. A malformed entry read as a field name resolves to no\n * column, and under the lenient unknown-field mode the sort is then dropped and\n * the rows come back in whatever order the database pleased — sorted, as far as\n * the subscriber can tell, by whatever they asked for.\n */\nexport function parseOrderBySpecStrict(raw: unknown, order?: \"asc\" | \"desc\"): OrderByTuple[] | undefined {\n if (raw === undefined || raw === null || raw === \"\") return undefined;\n // The string spelling is the driver contract's, so it takes its direction\n // from the same companion `order` — and defaults the same way it does.\n if (typeof raw === \"string\") return normalizeDriverOrderBy(raw, order);\n if (!Array.isArray(raw) || raw.length === 0) {\n throw new OrderBySpecError(`${typeof raw} is not a field name or a list of sort keys`);\n }\n\n // The single-tuple spelling, `[\"created_at\", \"desc\"]` — or the same shape\n // with an aggregate key in place of the field name.\n if (typeof raw[0] === \"string\" || isRelationAggregateSort(raw[0])) return [toStrictTuple(raw, 0)];\n\n return raw.map(toStrictTuple);\n}\n\n/** `first`/`last`, or a refusal naming the entry — see {@link NullsPlacement}. */\nfunction toStrictNulls(raw: unknown, index: number): NullsPlacement | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (raw !== \"first\" && raw !== \"last\") {\n throw new OrderBySpecError(\n `entry ${index} has nulls '${String(raw)}' — expected \"first\" or \"last\"`\n );\n }\n return raw;\n}\n\nfunction toStrictTuple(raw: unknown, index: number): OrderByTuple {\n if (!Array.isArray(raw)) {\n throw new OrderBySpecError(`entry ${index} has no field name`);\n }\n // The object spelling of an aggregate key, from an untyped caller that did\n // not go through `normalizeOrderBy`. Encoded rather than refused: it is a\n // sort this understands, and rejecting the shape a typed caller writes\n // would be a distinction between the two spellings that nothing else makes.\n const key = isRelationAggregateSort(raw[0]) ? sortKeyToString(raw[0]) : raw[0];\n if (typeof key !== \"string\" || key.trim() === \"\") {\n throw new OrderBySpecError(`entry ${index} has no field name`);\n }\n const direction = raw[1];\n if (direction !== undefined && direction !== \"asc\" && direction !== \"desc\") {\n throw new OrderBySpecError(`entry ${index} has direction '${String(direction)}'`);\n }\n const nulls = toStrictNulls(raw[2], index);\n // Omitted rather than defaulted: absent means \"the direction's convention\",\n // and writing one in here would make an explicit `NULLS LAST` on a\n // descending key indistinguishable from having said nothing — which the\n // keyset comparison and the ORDER BY both have to agree about.\n return nulls ? [key, direction ?? \"asc\", nulls] : [key, direction ?? \"asc\"];\n}\n\n/**\n * Serialize a sort to the wire.\n *\n * A single key keeps the `\"field:direction\"` shorthand it has always used —\n * short, readable in a URL, and what every existing client and test expects.\n * Several keys are emitted as the canonical JSON array the server already\n * accepts, because the shorthand has no separator to spare: a comma-joined\n * `\"a:asc,b:desc\"` parses as one field named `a` with the direction\n * `\"asc,b:desc\"`, which the server refuses.\n *\n * **Runtime tolerance:** if the input is already a well-formed wire string\n * (from an untyped JS caller), it is returned unchanged.\n * This is undocumented tolerance, not public API — don't rely on it.\n *\n * @param orderBy - A canonical tuple or list of tuples, or at runtime\n * possibly a pre-serialized string (undocumented tolerance).\n * @returns The wire-format string, or `undefined` if the input is falsy.\n *\n * @remarks\n * Field names containing `:` are representable in the tuple form but\n * **not** in the single-key wire encoding — this is an inherent limitation of\n * the colon-delimited shorthand and is not resolved here.\n */\nexport function serializeOrderBy(orderBy?: OrderBySpec | string): string | undefined {\n if (!orderBy) return undefined;\n // Runtime tolerance: pass through a pre-serialized wire string unchanged.\n if (typeof orderBy === \"string\") return orderBy;\n // `normalizeOrderBy` has already encoded any aggregate key to its string\n // spelling, which is why the shorthand below can assume a string: neither\n // `min(applications.created_at)` nor `count(applications)` contains a `:`.\n const list = normalizeOrderBy(orderBy);\n if (!list) return undefined;\n // `field:direction:nulls` — the third segment appears only when the key\n // asked for a placement, so every sort written before nulls existed still\n // serializes to exactly the string it always did.\n if (list.length === 1) {\n const [field, direction, nulls] = list[0];\n return nulls ? `${field}:${direction}:${nulls}` : `${field}:${direction}`;\n }\n return JSON.stringify(list.map(([field, direction, nulls]) => (nulls\n ? { field, direction, nulls }\n : { field, direction })));\n}\n\n/**\n * Deserialize a wire-format `\"field:direction\"` string into an {@link OrderByTuple}.\n *\n * Lenient parsing:\n * - Bare field name (no colon): `\"name\"` → `[\"name\", \"asc\"]`\n * - Unknown direction: `\"name:foo\"` → `[\"name\", \"asc\"]`\n * - Empty / falsy input, or a blank field name: → `undefined`\n *\n * The leniency is this end's alone; the *server* refuses the same value. This\n * used to say \"matches existing server behaviour\", and it stopped being true\n * when `parseOrderByParam` grew a strict direction check: `?orderBy=name:foo`\n * now answers `400 INVALID_ORDER_BY` (\"entry 0 has direction 'foo'\"). The split\n * is deliberate — see {@link parseOrderBySpecStrict} — because a value this\n * function is handed was produced by {@link serializeOrderBy} a moment earlier,\n * and one that reaches the server came from a stranger.\n *\n * A blank field is `undefined` rather than `[\" \", \"asc\"]`: whitespace is not a\n * field name, and the tuple it used to produce could not be re-encoded — the\n * only value in this codec that survived a decode and failed the next encode.\n *\n * Reads the single-key shorthand only. For a value that may carry several keys,\n * use {@link deserializeOrderByList} — handed a JSON array this returns the\n * whole array as one nonsensical field name.\n *\n * @param raw - The wire-format string from an HTTP query parameter.\n * @returns The canonical tuple, or `undefined` if the input names no field.\n */\nexport function deserializeOrderBy(raw?: string): OrderByTuple | undefined {\n if (!raw) return undefined;\n const idx = raw.indexOf(\":\");\n if (idx === -1) return raw.trim() === \"\" ? undefined : [raw, \"asc\"];\n const field = raw.slice(0, idx);\n if (field.trim() === \"\") return undefined;\n const rest = raw.slice(idx + 1);\n // `field:direction:nulls`. The nulls segment is optional, and — leniently,\n // as everything else on this end of the codec is — anything that is not\n // \"first\"/\"last\" is read as \"unspecified\" rather than refused. The *server*\n // end (`parseOrderByParam`) refuses it, for the reason in the docblock.\n const nullsIdx = rest.indexOf(\":\");\n const dir = nullsIdx === -1 ? rest : rest.slice(0, nullsIdx);\n const nulls = nullsIdx === -1 ? undefined : rest.slice(nullsIdx + 1);\n const direction = dir === \"desc\" ? \"desc\" : \"asc\";\n return nulls === \"first\" || nulls === \"last\"\n ? [field, direction, nulls]\n : [field, direction];\n}\n\n/**\n * Deserialize either wire spelling — the single-key shorthand or the JSON\n * array — into the list form.\n *\n * Lenient in the same way {@link deserializeOrderBy} is: this is the client end\n * of the codec, where the value was produced by {@link serializeOrderBy} a\n * moment earlier. The *server* end parses the same shapes strictly, in\n * `parseOrderByParam`, because there the value came from a stranger and a\n * direction it cannot read has to be refused rather than quietly turned into\n * `\"asc\"`.\n */\nexport function deserializeOrderByList(raw?: string): OrderByTuple[] | undefined {\n if (!raw) return undefined;\n const trimmed = raw.trim();\n if (trimmed.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n const list = parsed\n .map((entry): OrderByTuple | undefined => {\n if (typeof entry === \"string\") return deserializeOrderBy(entry);\n if (entry && typeof entry === \"object\" && typeof entry.field === \"string\") {\n const direction = entry.direction === \"desc\" ? \"desc\" : \"asc\";\n return entry.nulls === \"first\" || entry.nulls === \"last\"\n ? [entry.field, direction, entry.nulls]\n : [entry.field, direction];\n }\n return undefined;\n })\n .filter((entry): entry is OrderByTuple => entry !== undefined);\n return list.length > 0 ? list : undefined;\n }\n } catch {\n // Not JSON after all — fall through to the shorthand, which is what\n // a field name that merely begins with \"[\" would be.\n }\n }\n const single = deserializeOrderBy(trimmed);\n return single ? [single] : undefined;\n}\n","import { MAX_INCLUDE_DEPTH } from \"@rebasepro/types\";\nimport type { FilterValues, IncludeOptions, IncludeSpec, LogicalCondition, OrderByTuple } from \"@rebasepro/types\";\nimport { deserializeOrderByList, normalizeOrderBy } from \"./sort-dialect\";\n\n/**\n * The `include` codec: one shape, whatever spelling it arrived in.\n *\n * `include` reaches the driver by four routes — the REST `?include=` parameter,\n * a WebSocket subscribe frame, the SDK's `include(...)`, and the admin panel's\n * \"all relations\" — and each used to hand the driver something slightly\n * different. This normalises all four to one tree, so the fetch pipeline has a\n * single thing to read and `find()`, `findById()` and `listen()` cannot disagree\n * about what \"include the author\" means.\n *\n * @module\n */\n\n/**\n * One relation to load, and how.\n *\n * `children` is the nesting: `comments.author` is a `comments` node with an\n * `author` child. Every other field narrows the rows *of this relation* — the\n * same knobs a top-level query has, which is the point.\n */\nexport interface IncludeNode {\n /** Rows to load per parent row. */\n limit?: number;\n /** Filter over the related rows. */\n where?: FilterValues<string>;\n /** An `and`/`or`/`not` group over the related rows. */\n logical?: LogicalCondition;\n /** Sort for the related rows. */\n orderBy?: OrderByTuple[];\n /** Columns of the related row to return. */\n fields?: string[];\n /** Relations of the related row, loaded in turn. */\n children: Record<string, IncludeNode>;\n}\n\n/**\n * A whole `include` request: the tree, plus whether the caller asked for\n * *every* relation.\n *\n * The wildcard is kept as a flag rather than expanded into names here, because\n * expanding it needs the collection — which this package does not have. The\n * driver expands it against the relations it actually resolved.\n */\nexport interface NormalizedInclude {\n /** `include=*` — every relation of the collection, one hop deep. */\n wildcard: boolean;\n /** The named relations. Empty when `wildcard` is set alone. */\n tree: Record<string, IncludeNode>;\n}\n\n/** An `include` that cannot be read, as opposed to one naming a relation that does not exist. */\nexport class IncludeSpecError extends Error {\n readonly code: string;\n constructor(detail: string, code = \"INVALID_INCLUDE\") {\n super(`Invalid \\`include\\`: ${detail}`);\n this.name = \"IncludeSpecError\";\n this.code = code;\n Object.setPrototypeOf(this, IncludeSpecError.prototype);\n }\n}\n\nconst emptyNode = (): IncludeNode => ({ children: {} });\n\nfunction ensureNode(tree: Record<string, IncludeNode>, key: string): IncludeNode {\n return (tree[key] ??= emptyNode());\n}\n\n/**\n * Merge one dotted path (`\"comments.author\"`) into a tree.\n *\n * Merging rather than assigning is what makes `include=comments,comments.author`\n * mean the same thing as `include=comments.author`: the second path deepens the\n * node the first created instead of replacing it and losing its options.\n */\nfunction addPath(tree: Record<string, IncludeNode>, path: string): void {\n const segments = path.split(\".\").map(s => s.trim()).filter(Boolean);\n if (segments.length === 0) return;\n if (segments.length > MAX_INCLUDE_DEPTH) {\n throw new IncludeSpecError(\n `\"${path}\" nests ${segments.length} relations deep; the limit is ${MAX_INCLUDE_DEPTH}. ` +\n \"Each hop is another query, and an unbounded one walks a self-referencing relation forever.\",\n \"INCLUDE_TOO_DEEP\"\n );\n }\n let level = tree;\n for (const segment of segments) {\n level = ensureNode(level, segment).children;\n }\n}\n\nfunction normalizeOptions(key: string, options: IncludeOptions, depth: number): IncludeNode {\n if (depth > MAX_INCLUDE_DEPTH) {\n throw new IncludeSpecError(\n `\"${key}\" nests more than ${MAX_INCLUDE_DEPTH} relations deep.`,\n \"INCLUDE_TOO_DEEP\"\n );\n }\n if (options.limit !== undefined\n && (!Number.isInteger(options.limit) || options.limit < 1)) {\n throw new IncludeSpecError(\n `\"${key}\" has limit ${JSON.stringify(options.limit)} — expected a whole number of 1 or more.`\n );\n }\n const node: IncludeNode = { children: {} };\n if (options.limit !== undefined) node.limit = options.limit;\n if (options.where) node.where = options.where;\n if (options.logical) node.logical = options.logical;\n if (options.fields && options.fields.length > 0) node.fields = [...options.fields];\n // The same two spellings the top-level `?orderBy=` accepts: the\n // `field:direction[:nulls]` shorthand a caller writes into a query string,\n // and the tuple form a typed caller writes in code. Accepting only the\n // tuples made the JSON include form — the one that exists *because* it\n // travels over a query string — unable to express the shorthand beside it.\n const orderBy = typeof options.orderBy === \"string\"\n ? deserializeOrderByList(options.orderBy)\n : normalizeOrderBy(options.orderBy);\n if (orderBy) node.orderBy = orderBy;\n if (options.include) {\n const nested = normalizeIncludeAt(options.include, depth + 1);\n if (nested.wildcard) {\n // `*` inside a nested include has no bound: it would load every\n // relation of every related row, of every related row. The outer\n // wildcard is already the widest thing this API offers.\n throw new IncludeSpecError(\n `\"${key}\" asks for \\`*\\` inside a nested include. Name the relations you need.`\n );\n }\n node.children = nested.tree;\n }\n return node;\n}\n\nfunction normalizeIncludeAt(spec: IncludeSpec, depth: number): NormalizedInclude {\n if (Array.isArray(spec)) {\n const tree: Record<string, IncludeNode> = {};\n let wildcard = false;\n for (const raw of spec) {\n if (typeof raw !== \"string\") {\n throw new IncludeSpecError(`${typeof raw} is not a relation name`);\n }\n const name = raw.trim();\n if (!name) continue;\n if (name === \"*\") { wildcard = true; continue; }\n addPath(tree, name);\n }\n return { wildcard, tree };\n }\n if (typeof spec !== \"object\" || spec === null) {\n throw new IncludeSpecError(`${typeof spec} is not a list of relations or an include tree`);\n }\n\n const tree: Record<string, IncludeNode> = {};\n let wildcard = false;\n for (const [key, value] of Object.entries(spec)) {\n if (key === \"*\") {\n if (value) wildcard = true;\n continue;\n }\n if (value === true) { ensureNode(tree, key); continue; }\n // `false`/`null` are not in `IncludeSpec`, but this reads values that\n // arrived as JSON off a query string, where they are exactly what a\n // caller writes to turn one relation off in a tree they built by\n // spreading another. Skipping is what they mean.\n if ((value as unknown) === false || value === undefined || value === null) continue;\n if (typeof value !== \"object\" || Array.isArray(value)) {\n throw new IncludeSpecError(`\"${key}\" must be \\`true\\` or an options object`);\n }\n tree[key] = normalizeOptions(key, value as IncludeOptions, depth);\n }\n return { wildcard, tree };\n}\n\n/**\n * Collapse any {@link IncludeSpec} spelling into one tree.\n *\n * `[\"author\", \"comments.author\"]` and\n * `{ author: true, comments: { include: { author: true } } }` normalize to the\n * same value — which is the whole point: the REST parameter can only carry the\n * flat spelling, the SDK prefers the tree, and the driver should never learn\n * about either.\n *\n * @throws {IncludeSpecError} for a shape that is not an include at all, or one\n * that nests past {@link MAX_INCLUDE_DEPTH}.\n */\nexport function normalizeInclude(spec?: IncludeSpec): NormalizedInclude | undefined {\n if (spec === undefined || spec === null) return undefined;\n const normalized = normalizeIncludeAt(spec, 1);\n if (!normalized.wildcard && Object.keys(normalized.tree).length === 0) return undefined;\n return normalized;\n}\n\n/**\n * Every relation name a tree names, as dotted paths — `[\"comments\",\n * \"comments.author\"]`.\n *\n * Used to report which names an `include` asked for when one of them is not a\n * relation, and to serialize a tree that carries no per-relation options back\n * to the flat wire spelling.\n */\nexport function includePaths(tree: Record<string, IncludeNode>, prefix = \"\"): string[] {\n const out: string[] = [];\n for (const [key, node] of Object.entries(tree)) {\n const path = prefix ? `${prefix}.${key}` : key;\n out.push(path);\n out.push(...includePaths(node.children, path));\n }\n return out;\n}\n\n/**\n * The relation names an `include` asks for at the top level.\n *\n * `[\"author\", \"comments.author\"]` and `{author: true, comments: {...}}` both\n * answer `[\"author\", \"comments\"]` — a *hop*, not a path, because the only\n * consumer is `?fields=`, which names keys on the row being returned and a\n * nested relation is not one of those.\n *\n * Derived rather than passed: `include` has four spellings and three of them\n * are not a `string[]`, so every consumer that wants the plain names either\n * calls this or reimplements the flattening.\n */\nexport function topLevelIncludeNames(spec?: IncludeSpec): string[] {\n const normalized = normalizeInclude(spec);\n if (!normalized) return [];\n return Object.keys(normalized.tree);\n}\n\n/** Whether any node in the tree carries per-relation options. */\nfunction hasOptions(tree: Record<string, IncludeNode>): boolean {\n return Object.values(tree).some(node =>\n node.limit !== undefined || node.where !== undefined || node.logical !== undefined\n || node.orderBy !== undefined || node.fields !== undefined\n || hasOptions(node.children));\n}\n\n/**\n * Serialize an {@link IncludeSpec} for the REST `?include=` parameter.\n *\n * Two spellings, and which one is used is decided by the request rather than\n * chosen:\n *\n * - **Comma-separated dotted paths** — `include=author,comments.author`. What a\n * plain include is, what a human types, and what every existing client sends.\n * - **JSON**, when any relation carries options — `include={\"comments\":{\"limit\":5,\n * \"include\":{\"author\":true}}}`. The flat spelling has nowhere to put a\n * `limit`, and inventing a punctuation for it (`comments(limit:5)`) would be a\n * third grammar to learn beside the two this API already has.\n *\n * The server accepts both on every list and get route, and tells them apart the\n * same way this does: a value starting with `{` is JSON.\n */\nexport function serializeInclude(spec?: IncludeSpec): string | undefined {\n const normalized = normalizeInclude(spec);\n if (!normalized) return undefined;\n if (normalized.wildcard && Object.keys(normalized.tree).length === 0) return \"*\";\n if (!hasOptions(normalized.tree)) {\n const paths = includePaths(normalized.tree);\n // Only the leaves: `comments.author` already implies `comments`, and\n // sending both is the same request twice.\n const leaves = paths.filter(path => !paths.some(other => other.startsWith(`${path}.`)));\n const all = normalized.wildcard ? [\"*\", ...leaves] : leaves;\n return all.length > 0 ? all.join(\",\") : undefined;\n }\n return JSON.stringify(toWireTree(normalized));\n}\n\n/**\n * A normalized tree, back in the {@link IncludeSpec} spelling a caller writes.\n *\n * The round trip is what lets a builder accumulate `include` calls: normalize\n * each, merge, and hand the result back as a spec the next layer can normalize\n * again. Idempotent, so doing it twice changes nothing.\n */\nexport function denormalizeInclude(normalized: NormalizedInclude): IncludeSpec {\n return toWireTree(normalized) as IncludeSpec;\n}\n\nfunction mergeTrees(\n into: Record<string, IncludeNode>,\n from: Record<string, IncludeNode>\n): Record<string, IncludeNode> {\n for (const [key, node] of Object.entries(from)) {\n const existing = into[key];\n if (!existing) { into[key] = node; continue; }\n // The later call wins on each option it names, and says nothing about\n // the ones it does not — so `.include(\"comments\")` after\n // `.include({comments:{limit:5}})` keeps the limit rather than erasing\n // it, which is the behaviour that makes accumulating calls safe.\n if (node.limit !== undefined) existing.limit = node.limit;\n if (node.where !== undefined) existing.where = node.where;\n if (node.logical !== undefined) existing.logical = node.logical;\n if (node.orderBy !== undefined) existing.orderBy = node.orderBy;\n if (node.fields !== undefined) existing.fields = node.fields;\n existing.children = mergeTrees(existing.children, node.children);\n }\n return into;\n}\n\n/**\n * Combine several `include` requests into one.\n *\n * Repeated `.include(...)` calls on a query builder are additive: each names\n * more of the graph to load, and a later one must not discard what an earlier\n * one asked for. Assigning instead of merging is why `.include(\"author\")\n * .include(\"tags\")` used to load only tags.\n */\nexport function mergeIncludeSpecs(\n existing: IncludeSpec | undefined,\n additions: (string | IncludeSpec)[]\n): IncludeSpec | undefined {\n const merged: NormalizedInclude = { wildcard: false, tree: {} };\n const absorb = (spec?: IncludeSpec) => {\n const normalized = normalizeInclude(spec);\n if (!normalized) return;\n merged.wildcard ||= normalized.wildcard;\n mergeTrees(merged.tree, normalized.tree);\n };\n absorb(existing);\n // A bare string is one relation name; anything else is a spec in its own\n // right. `.include(\"a\", \"b\")` and `.include([\"a\",\"b\"])` are the same call.\n const names = additions.filter((a): a is string => typeof a === \"string\");\n if (names.length > 0) absorb(names);\n for (const addition of additions) {\n if (typeof addition !== \"string\") absorb(addition);\n }\n if (!merged.wildcard && Object.keys(merged.tree).length === 0) return undefined;\n return denormalizeInclude(merged);\n}\n\nfunction toWireTree(normalized: NormalizedInclude): Record<string, unknown> {\n const emit = (tree: Record<string, IncludeNode>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [key, node] of Object.entries(tree)) {\n const options: Record<string, unknown> = {};\n if (node.limit !== undefined) options.limit = node.limit;\n if (node.where) options.where = node.where;\n if (node.logical) options.logical = node.logical;\n if (node.orderBy) options.orderBy = node.orderBy;\n if (node.fields) options.fields = node.fields;\n const children = emit(node.children);\n if (Object.keys(children).length > 0) options.include = children;\n out[key] = Object.keys(options).length > 0 ? options : true;\n }\n return out;\n };\n const tree = emit(normalized.tree);\n if (normalized.wildcard) tree[\"*\"] = true;\n return tree;\n}\n\n/**\n * Read the REST `?include=` parameter, in either spelling.\n *\n * @throws {IncludeSpecError} for malformed JSON or a tree that nests too deep.\n */\nexport function deserializeInclude(raw?: string): IncludeSpec | undefined {\n if (raw === undefined || raw === null) return undefined;\n const text = raw.trim();\n if (!text) return undefined;\n if (text.startsWith(\"{\")) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new IncludeSpecError(\n \"the parametrised form must be a JSON object, e.g. \"\n + \"{\\\"comments\\\":{\\\"limit\\\":5,\\\"include\\\":{\\\"author\\\":true}}}\"\n );\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new IncludeSpecError(\"the parametrised form must be a JSON object\");\n }\n return parsed as IncludeSpec;\n }\n return text.split(\",\").map(s => s.trim()).filter(Boolean);\n}\n","import {\n CollectionAccessor,\n FilterCondition,\n FindParams,\n FindResponse,\n LogicalCondition,\n OrderByTuple,\n QueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n type ComputedSortField\n} from \"@rebasepro/types\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\n\nexport function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"or\",\nconditions };\n}\n\nexport function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"and\",\nconditions };\n}\n\n/**\n * Negate a group: `not(a)` is `NOT a`, and `not(a, b)` is `NOT (a AND b)`.\n *\n * The conjunction, not the disjunction — one rule, stated on\n * {@link LogicalCondition} and applied identically by the wire codec, the REST\n * `?not=` parameter and every driver compiler. Groups nest, so De Morgan's\n * other half is `not(or(a, b))`.\n *\n * It compiles to a real SQL `NOT (...)` rather than to inverted operators,\n * which matters more than it looks: SQL is three-valued, so `NOT (a AND b)` and\n * `(NOT a) OR (NOT b)` stop agreeing the moment a NULL is involved, and only\n * one of them is the query the caller wrote. It also means a negation includes\n * rows whose column is NULL — which is what `NOT` means.\n */\nexport function not(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {\n return { type: \"not\",\nconditions };\n}\n\nexport function cond(column: string, operator: WhereFilterOp, value: unknown): FilterCondition {\n return { column,\noperator,\nvalue };\n}\n\nexport class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {\n // Keyed by plain `string` on purpose: it is written in place by the\n // methods below, whose own parameters are typed against `M`, and a\n // `Partial<Record<FieldPath<M>, …>>` is read-only under a generic `M`\n // (TS2862). The typing users see is on the methods; this is the buffer\n // behind them, cast once at each handoff.\n private params: FindParams = { where: {} };\n\n constructor(private collection: CollectionAccessor<M>) {}\n\n /**\n * Add a filter condition to your query.\n * @example\n * client.collection('users').where('age', '>=', 18).find()\n */\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n // Handle LogicalCondition signature\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK builder\n // in `@rebasepro/client`, which had the same defect: every other\n // `.where()` adds a condition, so the one that silently dropped the\n // previous group was also the one that widened the result set.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n\n if (!this.params.where) {\n this.params.where = {};\n }\n\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n // Convert existing single tuple/value into array of tuples\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n\n return this;\n }\n\n /**\n * Order the results by a specific column.\n *\n * Called again, this adds a tie-breaker rather than replacing the sort:\n * keys apply in the order they were added.\n *\n * @example\n * client.collection('users').orderBy('createdAt', 'desc').find()\n * @example\n * client.collection('users').orderBy('roles').orderBy('createdAt', 'desc').find()\n */\n orderBy(column: (keyof M & string) | ComputedSortField, direction: \"asc\" | \"desc\" = \"asc\"): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n this.params.orderBy = [...existing, [column, direction] as OrderByTuple];\n return this;\n }\n\n /**\n * Limit the number of results returned.\n */\n limit(count: number): this {\n this.params.limit = count;\n return this;\n }\n\n /**\n * Skip the first N results.\n */\n offset(count: number): this {\n this.params.offset = count;\n return this;\n }\n\n /**\n * Set a free-text search string if supported by the backend.\n */\n search(searchString: string, options?: { explain?: boolean }): this {\n this.params.searchString = searchString;\n if (options?.explain !== undefined) this.params.searchExplain = options.explain;\n return this;\n }\n\n /**\n * Order rows by nearest-neighbour distance to `vector`, closest first.\n *\n * Postgres only, over a property declared as `type: \"vector\"`. Rows come\n * back with a `_distance`; `where` filters before the ordering.\n */\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n\n /**\n * Include related entities in the response.\n * Relations will be populated with full entity data instead of just IDs.\n *\n * @param relations - Relation names to include, or \"*\" for all.\n * @example\n * // Include specific relations\n * client.data.posts.include(\"tags\", \"author\").find()\n *\n * // Include all relations\n * client.data.posts.include(\"*\").find()\n */\n include(...relations: string[]): this {\n this.params.include = relations;\n return this;\n }\n\n /**\n * Execute the find query and return the results.\n */\n async find(): Promise<FindResponse<M>> {\n return this.collection.find(this.params as FindParams<M>) as Promise<FindResponse<M>>;\n }\n\n /**\n * Listen to realtime updates matching this query.\n */\n listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.collection.listen) {\n throw new Error(\"Listen is only available when RebaseClient is configured with a websocketUrl.\");\n }\n return this.collection.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n","import {\n DEFAULT_LIST_LIMIT,\n FindAllParams,\n FindParams,\n FindResult,\n IterateParams\n} from \"@rebasepro/types\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\n\n/**\n * The pagination engine behind `iterate()` / `findAll()`.\n *\n * It lives here, above both transports, on purpose: the HTTP client and the\n * in-process accessor implement the same `SDKCollectionClient` contract, and a\n * helper written twice is a helper that drifts. Both call into this file, so\n * \"the SDK paginates like *this*\" has exactly one definition.\n *\n * Everything below is expressed in terms of a single `find(params)` function,\n * which is all either transport has to supply.\n */\n\n/** Rows requested per page when the caller does not say. */\nexport const DEFAULT_PAGE_SIZE = 200;\n\n/** Rows `findAll()` will materialise before it refuses to continue. */\nexport const DEFAULT_FIND_ALL_MAX_ROWS = 10_000;\n\n/**\n * Requests one walk may make before it gives up on the server ever saying\n * `hasMore: false`. At the default page size that is two million rows — far\n * past any legitimate walk, and short of running forever.\n */\nexport const DEFAULT_MAX_PAGES = 10_000;\n\n/** Why a pagination walk refused to continue. */\nexport type PaginationErrorCode =\n /** `findAll()` matched more rows than its ceiling allows. */\n | \"max-rows\"\n /** The walk made its maximum number of requests without the server finishing. */\n | \"max-pages\"\n /**\n * The server said there was another page but issued no cursor to reach it.\n *\n * A query whose ordering has no stored value to seek on — relevance — is the\n * case that produces this. Page it by offset instead.\n */\n | \"cursor-missing\"\n /** Two consecutive pages returned the same cursor, so the walk cannot advance. */\n | \"cursor-stalled\";\n\n/**\n * Thrown when a walk stops for a reason the caller needs to know about.\n *\n * Every one of these is a case where the alternative would be silent: a\n * truncated array that looks complete, or a loop that never returns. Check\n * {@link code} to tell them apart.\n */\nexport class RebasePaginationError extends Error {\n readonly code: PaginationErrorCode;\n\n constructor(code: PaginationErrorCode, message: string) {\n super(message);\n this.name = \"RebasePaginationError\";\n this.code = code;\n // Keeps `instanceof` working when this is compiled down for an older\n // target, where extending a builtin otherwise loses the prototype.\n Object.setPrototypeOf(this, RebasePaginationError.prototype);\n }\n}\n\n/** The one thing a transport has to provide to be paginated. */\nexport type PageFinder<M extends Record<string, unknown> = Record<string, unknown>> =\n (params: FindParams<M>) => Promise<FindResult<M>>;\n\n/**\n * Resolve `limit`/`offset`/`page` into the window a read will actually use.\n *\n * Lives here, next to the walk, for the reason at the top of this file: every\n * transport has to mean the same thing by \"page two\". Four of them did not —\n * the REST layer strode by {@link DEFAULT_LIST_LIMIT}, the local-first\n * evaluator by {@link DEFAULT_PAGE_SIZE}, the in-process accessor by 20, and\n * the published type documented a fourth number. Pages that overlap or skip\n * rows are the mildest of those outcomes.\n *\n * `page` wins over `offset`, as {@link FindParams} documents. `driverOffset`\n * is the value to hand a driver: it stays `undefined` when the caller named no\n * offset, because keyset pagination seeks with a `where` clause and must not\n * look like it is paging by offset.\n */\nexport function resolveFindWindow(\n params?: Pick<FindParams, \"limit\" | \"offset\" | \"page\">\n): { limit: number; offset: number; driverOffset: number | undefined } {\n const limit = params?.limit ?? DEFAULT_LIST_LIMIT;\n const offset = params?.page != null\n ? Math.max(0, (params.page - 1) * limit)\n : (params?.offset ?? 0);\n return {\n limit,\n offset,\n driverOffset: params?.page != null ? offset : params?.offset\n };\n}\n\nfunction normalizePageSize(raw: number | undefined): number {\n if (raw === undefined || !Number.isFinite(raw)) return DEFAULT_PAGE_SIZE;\n return Math.max(1, Math.floor(raw));\n}\n\nfunction normalizeMaxPages(raw: number | undefined): number {\n if (raw === undefined) return DEFAULT_MAX_PAGES;\n if (raw === Number.POSITIVE_INFINITY) return raw;\n if (!Number.isFinite(raw)) return DEFAULT_MAX_PAGES;\n return Math.max(1, Math.floor(raw));\n}\n\nfunction normalizeMaxRows(raw: number | undefined): number {\n if (raw === undefined) return DEFAULT_FIND_ALL_MAX_ROWS;\n if (raw === Number.POSITIVE_INFINITY) return raw;\n if (!Number.isFinite(raw)) return DEFAULT_FIND_ALL_MAX_ROWS;\n return Math.max(0, Math.floor(raw));\n}\n\n/**\n * Walk every row a query matches, yielding one row at a time and fetching the\n * next page only when the consumer asks for it.\n *\n * See {@link SDKCollectionClient.iterate} for the caller-facing contract,\n * including the offset-drift caveat and the `cursor` alternative.\n *\n * @param find the transport's single-page read\n * @param params `find()` parameters minus the window, plus the walk options\n * @param label the collection name, so an error says which walk failed\n */\nexport async function* paginateFind<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n params?: IterateParams<M>,\n label = \"collection\"\n): AsyncGenerator<M, void, undefined> {\n const {\n pageSize,\n cursor,\n maxPages,\n ...rest\n } = (params ?? {}) as IterateParams<M> & Record<string, unknown>;\n\n const findParams = { ...rest } as FindParams<M>;\n const size = normalizePageSize(pageSize as number | undefined);\n const pageCap = normalizeMaxPages(maxPages as number | undefined);\n\n // ── Cursor (keyset) setup ────────────────────────────────────────────────\n //\n // The walk no longer builds a keyset of its own. It used to: a `>`/`<` on\n // one column, expressed as an extra `where`, which threw on any multi-key\n // sort (\"keyset pagination advances along a single column\") and dropped\n // every row whose sort value was NULL, because `> value` answers *unknown*\n // against NULL. The driver has had a NULL-correct multi-key comparison all\n // along and nothing over HTTP could reach it.\n //\n // So this is now a *request* for seeking, not an implementation of it: the\n // server issues `meta.nextCursor` and the walk hands it back as `after`.\n // Multi-key sorts and nullable keys work because the comparison is the\n // driver's, and there is one of it.\n const seekRequested = cursor !== undefined && cursor !== null;\n if (seekRequested) {\n // A named column still means \"sort by this and seek along it\", which is\n // what every existing caller wrote. It is an `orderBy` now rather than\n // a second pagination mode — the seeking itself needs no column named,\n // since the cursor carries whatever keys the sort used.\n const field = typeof cursor === \"string\" ? cursor : cursor.field;\n const requested = (typeof cursor === \"object\" && cursor !== null) ? cursor.direction : undefined;\n const explicit = normalizeOrderBy(findParams.orderBy);\n // An explicit `orderBy` wins and the named column is redundant, not\n // wrong: seeking follows whatever the query is sorted by, so there is\n // no longer a mismatch to refuse.\n if (!explicit) {\n findParams.orderBy = [field, requested ?? \"asc\"] as FindParams<M>[\"orderBy\"];\n }\n }\n\n let offset = 0;\n let pages = 0;\n let after: string | undefined;\n\n for (;;) {\n if (pages >= pageCap) {\n throw new RebasePaginationError(\n \"max-pages\",\n `Iterating \"${label}\" made ${pages} requests without the server reporting the end of ` +\n `the collection. Stopping rather than looping forever — raise \\`maxPages\\` if the walk ` +\n `is genuinely this long, or check that the backend sets \\`meta.hasMore\\`.`\n );\n }\n\n const pageParams: FindParams<M> = { ...findParams, limit: size };\n if (seekRequested) {\n if (after) pageParams.after = after;\n } else {\n pageParams.offset = offset;\n }\n\n const page = await find(pageParams);\n pages += 1;\n\n const rows = page?.data ?? [];\n // A page with nothing on it always ends the walk, whatever the server\n // claims about `hasMore` — there is no cursor to advance and no offset\n // that would ever move past it.\n if (rows.length === 0) return;\n\n for (const row of rows) {\n yield row;\n }\n\n // The server is the only authority on whether more rows exist. Never\n // infer it from `rows.length >= size`: a last page that happens to be\n // exactly full is indistinguishable from a middle one, and guessing\n // there drops every row after it.\n if (page?.meta?.hasMore !== true) return;\n\n if (seekRequested) {\n const next = page.meta.nextCursor;\n if (!next) {\n throw new RebasePaginationError(\n \"cursor-missing\",\n `Cannot seek past the last row of \"${label}\": the server reported another page but ` +\n `issued no cursor for it. An ordering with no stored value to compare against — ` +\n `relevance (\\`_score\\`) — cannot key a cursor. Drop \\`cursor\\` to page by offset.`\n );\n }\n if (next === after) {\n throw new RebasePaginationError(\n \"cursor-stalled\",\n `Iterating \"${label}\" is stuck: two pages in a row ended on the same cursor, so the ` +\n `walk cannot advance. Continuing would loop forever. Page by offset instead, or ` +\n `report this — a cursor that does not move is a server-side bug.`\n );\n }\n after = next;\n } else {\n // Advance by what actually arrived, not by the page size: a server\n // free to return fewer rows than asked for would otherwise leave a\n // hole in the walk.\n offset += rows.length;\n }\n }\n}\n\n/**\n * {@link paginateFind}, collected into an array under a ceiling.\n *\n * See {@link SDKCollectionClient.findAll}.\n */\nexport async function collectAllPages<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n params?: FindAllParams<M>,\n label = \"collection\"\n): Promise<M[]> {\n const { maxRows, ...rest } = (params ?? {}) as FindAllParams<M> & Record<string, unknown>;\n const cap = normalizeMaxRows(maxRows as number | undefined);\n\n const out: M[] = [];\n for await (const row of paginateFind<M>(find, rest as IterateParams<M>, label)) {\n out.push(row);\n if (out.length > cap) {\n throw new RebasePaginationError(\n \"max-rows\",\n `findAll(\"${label}\") matched more than ${cap} rows. Returning the first ${cap} would ` +\n `look like the whole answer and quietly not be one, so this throws instead. Raise ` +\n `\\`maxRows\\` if you meant to load them all, or stream with \\`iterate()\\`.`\n );\n }\n }\n return out;\n}\n\n/**\n * Build the `iterate` / `findAll` pair for one collection from its `find`.\n *\n * Both transports call this, which is what keeps the two implementations from\n * being two implementations.\n */\nexport function createPaginationHelpers<M extends Record<string, unknown> = Record<string, unknown>>(\n find: PageFinder<M>,\n label: string\n): {\n iterate: (params?: IterateParams<M>) => AsyncIterableIterator<M>;\n findAll: (params?: FindAllParams<M>) => Promise<M[]>;\n} {\n return {\n iterate: (params?: IterateParams<M>) => paginateFind<M>(find, params, label),\n findAll: (params?: FindAllParams<M>) => collectAllPages<M>(find, params, label)\n };\n}\n","/**\n * REST wire-format adapter for the unified filter system.\n *\n * This module is the ONLY code in the entire codebase that knows about\n * PostgREST-style dot-syntax strings (`eq.active`, `gt.18`, `in.(a,b)`).\n * Everything else speaks `FilterValues` exclusively.\n *\n * Wire-format values are always strings — the wire format carries no type\n * metadata, so type coercion is the responsibility of the server-side data\n * driver which has access to the collection schema.\n *\n * Structural characters inside a value are backslash-escaped: `,` → `\\,`,\n * `(` → `\\(`, `)` → `\\)`, and a literal backslash as `\\\\`. Decoding is\n * deliberately conservative — only those four sequences are decoded, so a\n * backslash that arrives unescaped from an older client survives intact.\n *\n * @module\n */\n\nimport {\n WhereFilterOp,\n FilterValues,\n ALL_WHERE_FILTER_OPS,\n CANONICAL_TO_REST,\n REST_TO_CANONICAL,\n RestFilterOp,\n toCanonicalOp,\n LogicalCondition,\n FilterCondition,\n LIST_OPS,\n NULL_OPS\n} from \"@rebasepro/types\";\nimport { normalizeToEntityRelation } from \"../util/entities\";\n\n// ---------------------------------------------------------------------------\n// Value stringification\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a JS value to its querystring representation.\n * `null` is serialized as the literal string `\"null\"`.\n * Relation values (`EntityRelation` instances or `{ __type: \"relation\", id, path }`\n * objects) are serialized as their raw id — the wire format only carries the\n * value to compare against the FK column.\n */\nfunction stringifyValue(value: unknown): string {\n if (value === null) return \"null\";\n const relation = normalizeToEntityRelation(value);\n if (relation) return String(relation.id);\n return String(value);\n}\n\n// ---------------------------------------------------------------------------\n// Comma escaping for list values\n// ---------------------------------------------------------------------------\n\n/**\n * Characters that carry structure in the wire format and must therefore be\n * escaped inside a value: the separator, the group delimiters, and the escape\n * character itself.\n *\n * Parentheses are here because `and(...)`/`or(...)` groups are parsed by\n * tracking paren depth. A value containing one is not merely ambiguous, it\n * moves where the parser thinks the group ends.\n */\nconst WIRE_SPECIALS = /[\\\\,()]/g;\n\n/**\n * Escape a value for the wire format: `\\` → `\\\\`, `,` → `\\,`, `(` → `\\(`,\n * `)` → `\\)`.\n */\n/**\n * The wire spelling of an empty list.\n *\n * A lone backslash: unproducible by {@link escapeWireValue}, which doubles\n * every backslash it emits, so it cannot collide with any real item.\n */\nconst EMPTY_LIST_TOKEN = \"\\\\\";\n\nfunction escapeWireValue(value: string): string {\n return value.replace(WIRE_SPECIALS, ch => `\\\\${ch}`);\n}\n\n/**\n * Unescape a wire-format value.\n *\n * **Conservative**, and deliberately so: only the four sequences\n * {@link escapeWireValue} actually produces are decoded. A backslash followed\n * by anything else is left exactly as it is.\n *\n * This used to consume the backslash before *any* character, which is\n * indistinguishable for anything this codec emitted — it only ever emits those\n * four — but not for input arriving from elsewhere. A client on an older\n * release sends a Windows path or a LIKE pattern with a literal `C:\\x`\n * unescaped, and greedy unescaping silently turned it into `C:x`, changing\n * which rows matched. Decoding only what the encoder can produce makes the two\n * directions agree across versions.\n */\nfunction unescapeWireValue(value: string): string {\n let result = \"\";\n for (let i = 0; i < value.length; i++) {\n const next = value[i + 1];\n if (value[i] === \"\\\\\" && (next === \"\\\\\" || next === \",\" || next === \"(\" || next === \")\")) {\n result += next;\n i++;\n continue;\n }\n result += value[i];\n }\n return result;\n}\n\n/**\n * Split a parenthesized list string on unescaped commas.\n * Input is the content between `(` and `)`.\n *\n * @example\n * splitListItems(\"admin,editor\") // [\"admin\", \"editor\"]\n * splitListItems(\"hello\\\\, world,foo\") // [\"hello, world\", \"foo\"]\n */\nfunction splitListItems(inner: string): string[] {\n const items: string[] = [];\n let current = \"\";\n for (let i = 0; i < inner.length; i++) {\n if (inner[i] === \"\\\\\" && i + 1 < inner.length) {\n // Escaped pair — consume both chars so the comma in `\\,` is not\n // read as a separator. Kept verbatim; decoding happens once, below.\n current += inner[i] + inner[i + 1];\n i++;\n } else if (inner[i] === \",\") {\n items.push(unescapeWireValue(current));\n current = \"\";\n } else {\n current += inner[i];\n }\n }\n items.push(unescapeWireValue(current));\n return items;\n}\n\n/**\n * Split a group body on commas at paren depth 0, honouring escapes.\n *\n * The escape-awareness is the point. The splitter used to track only paren\n * depth, so a comma inside a scalar value ended a condition:\n * `or(name.eq.Doe, John,age.gte.18)` parsed as *three* conditions, the middle\n * one a fabricated `\" John\" == true`. On an `or` that widens the result set,\n * and nothing anywhere reports an error — the query simply stops meaning what\n * the caller wrote.\n */\nfunction splitGroupItems(inner: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < inner.length; i++) {\n const ch = inner[i];\n if (ch === \"\\\\\" && i + 1 < inner.length) { i++; continue; }\n if (ch === \"(\") depth++;\n else if (ch === \")\") depth--;\n else if (ch === \",\" && depth === 0) {\n parts.push(inner.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(inner.slice(start));\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// Typed operator map lookups (no `as any`)\n// ---------------------------------------------------------------------------\n\n/**\n * Operator tables as `Map`s, because the key comes off the wire.\n *\n * Indexed as plain objects, every `Object.prototype` member answered: a query\n * string of `?f=valueOf.x` found a truthy \"operator\" — the inherited function —\n * and `deserializeTuple` returned it *as the operator*, so a function object\n * travelled on into the compilers in place of a `WhereFilterOp`. The guard one\n * line below (`if (!canonicalOp)`) reads as though it rejects anything unknown,\n * and does not: `Object.prototype` is not unknown to a plain object.\n *\n * Same shape as the prototype-key defects swept out of `setIn`, `getIn`,\n * `mergeDeep`, `unflattenObject` and `FOREIGN_CONVENTION_UIDS`.\n */\nconst REST_OP_LOOKUP = new Map<string, WhereFilterOp>(\n Object.entries(REST_TO_CANONICAL) as [string, WhereFilterOp][]\n);\nconst CANONICAL_OP_LOOKUP = new Map<string, RestFilterOp>(\n Object.entries(CANONICAL_TO_REST) as [string, RestFilterOp][]\n);\n\n// ---------------------------------------------------------------------------\n// Unknown operators\n// ---------------------------------------------------------------------------\n\n/** The operator spellings a rejection lists back to the caller. */\nconst VALID_OPERATOR_LIST = ALL_WHERE_FILTER_OPS.join(\", \");\n\n/**\n * A filter condition named an operator this dialect does not have.\n *\n * ## Why this throws, rather than returning a typed rejection\n *\n * `deserializeFilter` is the *shared* codec: the REST ingress\n * (`packages/server/src/api/rest/query-parser.ts`), the browser SDK and the\n * admin panel (`buildRebaseData.ts`) all decode through it. Two constraints\n * follow.\n *\n * - It cannot throw the server's `ApiError`. `@rebasepro/common` does not\n * depend on `@rebasepro/server` (the dependency runs the other way), and a\n * browser client has no error handler to render an `ApiError` with. So the\n * rejection is this plain `Error` subclass, whose `message` reads correctly\n * wherever it surfaces — a rejected promise in an app, a 400 body over HTTP.\n * - It cannot be a returned rejection *value*. Every caller assigns the result\n * straight into a query it is about to run; a sentinel that none of them\n * check would be ignored, which is exactly the silently-wrong-filter failure\n * this exists to stop. Throwing is also what this file already does for the\n * sibling cases — `serializeTuple` on an unknown canonical operator,\n * `deserializeLogicalCondition` past the nesting bound — and the REST parser\n * already converts the latter into a 400.\n *\n * `statusCode`, `code` and `details` are carried as fields because the server's\n * Hono error handler duck-types those off any thrown error: a decode path that\n * forgets to convert still answers 400 with the canonical envelope instead of a\n * 500 that says \"An unexpected error occurred\". `query-parser.ts` converts\n * explicitly all the same — that is the path the contract is stated on, and an\n * incidental 400 is not a contract.\n */\nexport class UnknownFilterOperatorError extends Error {\n /** The field the condition was written against. */\n public readonly field: string;\n /** The operator string as it arrived, verbatim. */\n public readonly operator: string;\n /** Every operator this dialect accepts, in canonical spelling. */\n public readonly validOperators: readonly WhereFilterOp[] = ALL_WHERE_FILTER_OPS;\n /** See the class docblock: read by the server's error handler. */\n public readonly statusCode = 400;\n public readonly code = \"UNKNOWN_FILTER_OPERATOR\";\n public readonly details: { field: string; operator: string; validOperators: readonly WhereFilterOp[] };\n\n constructor(field: string, operator: string) {\n super(\n `Unknown filter operator '${operator}' on field '${field}'. `\n + `Valid operators: ${VALID_OPERATOR_LIST}`\n );\n this.name = \"UnknownFilterOperatorError\";\n this.field = field;\n this.operator = operator;\n this.details = { field, operator, validOperators: ALL_WHERE_FILTER_OPS };\n }\n}\n\n/**\n * Two to three characters of ASCII punctuation and nothing else — the shape\n * every symbolic operator has (`==`, `>=`, `<>`, `~~`, `!!`, `>>`, `===`), and\n * one a column value effectively never has.\n *\n * Two characters minimum on purpose. A *single* punctuation character is a\n * perfectly ordinary value — `{ grade: [\"-\", \"+\"] }` is a two-item list, not a\n * condition — and the only single-character operator anyone actually mistypes\n * is `=`, which is named separately below. `<` and `>` need no special case:\n * they are real operators and resolve.\n */\nconst SYMBOLIC_OPERATOR = /^[^\\p{L}\\p{N}\\s]{2,3}$/u;\n\n/** Lowercase, strip everything that is not a letter or digit. */\nfunction normalizeOperatorName(op: string): string {\n return op.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\n/**\n * Every real operator name with its case and separators removed, so a\n * respelling of one — `arrayContains`, `not_in`, `NOT-LIKE`, `isNull` — is\n * recognised as an attempt at an operator rather than read as a value.\n *\n * These are rejected rather than accepted: admitting a second spelling of an\n * operator would leave two wire spellings of one thing, and the rejection\n * message names the one that works.\n */\nconst RESPELLED_OPERATORS: ReadonlySet<string> = new Set(\n [...ALL_WHERE_FILTER_OPS, ...Object.keys(REST_TO_CANONICAL)].map(normalizeOperatorName)\n);\n\n/**\n * Operator names *other* query dialects use, which this one does not have.\n *\n * This list is curated, and deliberately so. For a word-shaped string there is\n * no rule that separates \"an operator the caller guessed\" from \"a value that\n * happens to be a word\": `{ tags: [\"a\", \"b\"] }` has to keep meaning a two-item\n * `in` list, so the codec cannot simply refuse every unrecognised word in\n * position 0. The line is therefore drawn by name, and only around names whose\n * use as an operator is far more likely than their use as one of two sibling\n * values. `contains` is the motivating case — the first thing a developer\n * reaches for, and until now it compiled to `title IN ('contains', 'Hell')`.\n *\n * Genuinely ambiguous single words (`any`, `all`, `exists`, `search`, `not`)\n * are left off: as operators they are rare, and as enum values they are common.\n * Everywhere else the tie goes to *rejecting*, because a 400 naming the\n * supported set costs the caller one round trip, and the alternative — which is\n * what every name on this list used to produce — is a query that runs, returns\n * rows, and is wrong.\n */\nconst NEAR_MISS_OPERATORS: ReadonlySet<string> = new Set([\n \"contains\", \"notcontains\", \"doesnotcontain\", \"doesnotcontains\",\n \"includes\", \"notincludes\",\n \"startswith\", \"notstartswith\", \"beginswith\", \"startingwith\",\n \"endswith\", \"notendswith\",\n \"matches\", \"notmatches\", \"regex\", \"regexp\",\n \"between\", \"notbetween\",\n \"equals\", \"notequals\", \"equalto\", \"isequalto\", \"isnotequalto\",\n \"greaterthan\", \"greaterthanorequal\", \"greaterthanorequalto\",\n \"lessthan\", \"lessthanorequal\", \"lessthanorequalto\",\n \"isempty\", \"isnotempty\",\n \"oneof\", \"noneof\", \"anyof\", \"allof\",\n \"null\", \"isnullorempty\"\n]);\n\n/**\n * Was this string *meant* as an operator?\n *\n * Only consulted after {@link toCanonicalOp} has already failed to resolve it,\n * so a `true` here is always a rejection.\n */\nfunction isOperatorShaped(op: string): boolean {\n if (op === \"=\") return true;\n if (SYMBOLIC_OPERATOR.test(op)) return true;\n const normalized = normalizeOperatorName(op);\n if (!normalized) return false;\n return RESPELLED_OPERATORS.has(normalized) || NEAR_MISS_OPERATORS.has(normalized);\n}\n\n/**\n * Read a `[op, value]` tuple, if that is what this is.\n *\n * Three outcomes, and the middle one is the defect this function exists for:\n *\n * - the operator resolves (canonical *or* REST spelling) → the canonical tuple;\n * - the operator does not resolve but was plainly meant as one → throw;\n * - it does not look like an operator at all → `undefined`, and the caller\n * falls back to reading the array as a list of values.\n *\n * The old test was `toCanonicalOp(raw[0]) === raw[0]`, i.e. canonical spelling\n * only, with *everything else* — including every REST short-code — dropping\n * through to `[\"in\", raw]`. So the operator string itself became a value in a\n * membership test: `[\"!!\", \"Hello\"]` compiled to `title IN ('!!','Hello')`,\n * which matches, and the caller got back rows their filter was written to\n * exclude. `[\"eq\", \"active\"]` had the same shape of failure.\n */\nfunction readTuple(field: string, raw: unknown): [WhereFilterOp, unknown] | undefined {\n if (!Array.isArray(raw) || raw.length !== 2) return undefined;\n const [op, value] = raw;\n if (typeof op !== \"string\") return undefined;\n\n const canonical = toCanonicalOp(op);\n if (canonical) return [canonical, value];\n\n // A dot means this is a *wire* string, not an operator token: two repeated\n // query params arrive as `[\"gte.18\", \"lt.65\"]`, which is a two-element array\n // of strings and therefore tuple-shaped. Deferred with exactly the test the\n // repeated-dot-string branch below uses, so the two cannot disagree.\n //\n // The property test found this: `[\"ilike\", \"\"]` serializes to `\"ilike.\"`,\n // whose normalized form is a real operator name, so a well-formed\n // round-trip was being rejected as a bad operator.\n if (op.includes(\".\")) return undefined;\n\n if (isOperatorShaped(op)) throw new UnknownFilterOperatorError(field, op);\n\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Serialize: FilterValues → REST querystring\n// ---------------------------------------------------------------------------\n\n/**\n * Encode the `<op>.<value>` half of a wire condition.\n *\n * This is the single leaf encoder. Both wire positions that carry a condition\n * — a top-level query parameter (`?status=eq.active`) and a leaf inside an\n * `and(...)`/`or(...)` group (`or(status.eq.active,…)`) — go through it, so a\n * rule expressed here holds in both. The group serializer used to carry its\n * own copy, and the copy had drifted on every rule that matters: `null` went\n * out as the four-character string, the empty list as `()`, and an operator\n * this dialect does not have was silently rewritten to `eq` — a filter that\n * ran, returned rows, and answered a different question than the one asked.\n *\n * `escapeScalar` is the one thing the two positions legitimately disagree\n * about. A scalar in a query parameter owns the whole value and needs no\n * escaping; a scalar inside a group sits between the same commas a list item\n * does, so a comma in it would end the condition early.\n */\nfunction serializeOperatorAndValue(\n op: WhereFilterOp,\n value: unknown,\n { escapeScalar, where }: { escapeScalar: boolean; where: string }\n): string {\n if (typeof op !== \"string\") {\n throw new TypeError(\n `${where}: operator must be a string, got ${typeof op}`\n );\n }\n\n // Canonical spellings only, on purpose: this codec parses liberally and\n // emits strictly. `deserializeFilter` accepts a REST short-code because one\n // arrives off the wire; a *caller* handing one to the serializer has a\n // condition object built by hand, and the spelling it wants is the one the\n // types name.\n //\n // The throw is the fix. `serializeLogicalCondition` used to end this lookup\n // with `?? \"eq\"`, so `{ operator: \"gte\" }` — the spelling the wire uses, and\n // therefore the one most often guessed — was sent as `age.eq.18`: a query\n // that ran, returned rows, and answered a different question.\n const restOp = CANONICAL_OP_LOOKUP.get(op);\n if (!restOp) {\n throw new TypeError(\n `${where}: unknown operator \"${op}\". Valid operators: ${Object.keys(CANONICAL_TO_REST).join(\", \")}`\n );\n }\n\n // `== null` and `!= null` go out as the null-testing operators.\n //\n // They used to serialize as `eq.null`, and `deserializeTuple` had no way to\n // tell that from a search for the four-character string \"null\" — so it\n // returned the string, and `.where(\"deleted_at\", \"==\", null)` compiled to\n // `deleted_at = 'null'` over HTTP. The typed builder allows it, the Postgres\n // compiler implements it as IS NULL, and only the wire trip broke it.\n //\n // These are the same query: SQL `= NULL` is never true, so `== null` can\n // only mean IS NULL. Emitting it as such is unambiguous in both directions\n // and leaves `eq.null` free to mean the literal string, which it now does.\n if (value === null && (op === \"==\" || op === \"!=\")) {\n return op === \"==\" ? \"isnull.null\" : \"notnull.null\";\n }\n\n // A null test has no operand. Whatever was parked in `value` is dropped\n // here rather than on the way back, so the encoding is stable: both\n // deserializers normalize `isnull.<anything>` to `null`, and re-encoding\n // that must land on the same string it came from.\n if (NULL_OPS.has(op)) return `${restOp}.null`;\n\n if (Array.isArray(value)) {\n // The empty list needs a spelling of its own.\n //\n // A comma-joined format has no way to write \"zero items\": `()` is the\n // empty string between the parens, which splits to `[\"\"]`. So\n // `.where(\"id\", \"in\", [])` — which matches nothing — used to arrive as\n // a search for the empty string: a 500 on a uuid column, silently the\n // wrong rows on a text one.\n //\n // `EMPTY_LIST_TOKEN` is a single unescaped backslash, which no real\n // value can produce: `escapeWireValue` doubles every backslash, so a\n // one-item list holding `\\` serializes as `(\\\\)`. That keeps both\n // directions exact — `[]` and `[\"\"]` stay distinct — rather than\n // trading one lossy reading for another.\n if (value.length === 0) return `${restOp}.(${EMPTY_LIST_TOKEN})`;\n const items = value.map(v => escapeWireValue(stringifyValue(v))).join(\",\");\n return `${restOp}.(${items})`;\n }\n\n const scalar = stringifyValue(value);\n return `${restOp}.${escapeScalar ? escapeWireValue(scalar) : scalar}`;\n}\n\n/**\n * Serialize a single canonical condition tuple to a PostgREST dot-string.\n *\n * Throws `TypeError` if the input is not a valid `[WhereFilterOp, unknown]` tuple.\n *\n * @example\n * serializeTuple([\"==\", \"active\"]) // \"eq.active\"\n * serializeTuple([\"in\", [\"admin\",\"editor\"]]) // \"in.(admin,editor)\"\n * serializeTuple([\">=\", 18]) // \"gte.18\"\n */\nfunction serializeTuple(tuple: [WhereFilterOp, unknown]): string {\n if (!Array.isArray(tuple) || tuple.length !== 2) {\n throw new TypeError(\n `serializeTuple: expected a [WhereFilterOp, value] tuple, got ${JSON.stringify(tuple)}`\n );\n }\n\n const [op, value] = tuple;\n return serializeOperatorAndValue(op, value, {\n escapeScalar: false,\n where: \"serializeTuple\"\n });\n}\n\n/**\n * Convert `FilterValues` (or `WireFilterValues`) to a PostgREST-style\n * querystring record.\n *\n * - Canonical `[WhereFilterOp, value]` tuples are serialized strictly.\n * - Pre-serialized PostgREST strings (e.g. `\"eq.published\"`) are passed through.\n * - Single conditions produce a string value.\n * - Multiple conditions on the same field produce a string array (repeated params).\n *\n * @example\n * serializeFilter({ status: [\"==\", \"active\"] })\n * // → { status: \"eq.active\" }\n *\n * serializeFilter({ age: [[\">=\", 18], [\"<\", 65]] })\n * // → { age: [\"gte.18\", \"lt.65\"] }\n *\n * // Pre-serialized strings pass through unchanged:\n * serializeFilter({ status: \"eq.published\" })\n * // → { status: \"eq.published\" }\n */\nexport function serializeFilter(\n filter: FilterValues<string> | Record<string, unknown>\n): Record<string, string | string[]> {\n const result: Record<string, string | string[]> = {};\n\n for (const [field, condition] of Object.entries(filter)) {\n if (condition === undefined) continue;\n\n // Pre-serialized PostgREST string — pass through unchanged.\n // This supports WireFilterValues where values may already be\n // serialized dot-strings like \"eq.active\" or raw strings like \"true\".\n if (typeof condition === \"string\") {\n result[field] = condition;\n continue;\n }\n\n // Multiple conditions on the same field: array of tuples\n // We detect this by checking if the first element is also an array.\n if (Array.isArray(condition) && condition.length > 0 && Array.isArray(condition[0])) {\n result[field] = (condition as [WhereFilterOp, unknown][]).map(serializeTuple);\n } else {\n // Single condition — must be a [WhereFilterOp, value] tuple\n result[field] = serializeTuple(condition as [WhereFilterOp, unknown]);\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize: REST querystring → FilterValues\n// ---------------------------------------------------------------------------\n\n/**\n * The spellings a null-testing operator's operand may take.\n *\n * The serializer writes `isnull.null`; a hand-written `isnull.true` means the\n * same thing and has always been accepted. Anything else after the operator is\n * not an operand it has — `notnull.reason` is a *value* — see\n * {@link deserializeSingle}.\n */\nconst NULL_OPERANDS: ReadonlySet<string> = new Set([\"null\", \"true\", \"false\", \"\"]);\n\n/**\n * Parse a single PostgREST dot-string into a `[WhereFilterOp, unknown]` tuple.\n *\n * All values are returned as strings — the wire format carries no type\n * metadata, so coercion is the data driver's responsibility.\n *\n * ## When a leading segment is an operator, and when it is part of the value\n *\n * `?status=in.progress` and `?status=in.(a,b)` differ by one character and mean\n * entirely different things, and the reading here decides which. The rule, in\n * full:\n *\n * > A dot-string is read as `operator.operand` **only** when its first segment\n * > names a known REST operator **and** what follows is a well-formed operand\n * > *for that operator's arity*. Otherwise the whole string is the value.\n *\n * Arity, per operator family:\n *\n * - **List** operators (`in`, `nin`, `csa` — `LIST_OPS`) take a parenthesised\n * list and nothing else. `in.(draft,review)` is the operator; `in.progress`\n * is the *value* `\"in.progress\"`, because there is no list there and so no\n * `in` filter that could have been written. That case used to compile to\n * `status IN ('progress')` — a filter the caller never wrote, quietly\n * matching the wrong rows and, on a status field, hiding every row they were\n * looking for.\n * - **Null** operators (`isnull`, `notnull` — {@link NULL_OPS}) take no\n * operand: only {@link NULL_OPERANDS}. `notnull.reason` is the value\n * `\"notnull.reason\"`, not \"reason is not null\".\n * - **Everything else** takes one scalar, and any remainder is one — including\n * the empty string, so `eq.` really is \"equals the empty string\".\n *\n * ### The one ambiguity that remains, and how to write past it\n *\n * A scalar operator's operand is unconstrained, so `?status=like.that` is a\n * `LIKE 'that'` and no rule at this layer can tell it from the literal value\n * `\"like.that\"` — both are well-formed encodings, and picking either by guess\n * would break the other. Two spellings say \"value\" unambiguously, and both\n * round-trip:\n *\n * - `?status=eq.like.that` — name the operator. The *first* segment is consumed\n * as the operator and everything after it is the value, dots and all. This is\n * what `serializeFilter` emits, which is why the SDK never meets the\n * ambiguity at all.\n * - `?where={\"status\":[\"==\",\"like.that\"]}` — the JSON dialect's tuple form.\n *\n * Values that merely *contain* dots (`user@host.com`, `1.2.3`) were never\n * ambiguous: their first segment names no operator to begin with.\n */\nfunction deserializeSingle(raw: string): [WhereFilterOp, unknown] {\n const dotIndex = raw.indexOf(\".\");\n if (dotIndex === -1) {\n // No dot → equality on the raw value (kept as string)\n return [\"==\", raw];\n }\n\n const prefix = raw.substring(0, dotIndex);\n const rest = raw.substring(dotIndex + 1);\n\n // Check if the prefix is a known REST operator.\n // This is the key defense against values like \"eq.something\" or \"gt.foo\"\n // being misinterpreted — only known REST short-codes are treated as operators.\n const canonicalOp = REST_OP_LOOKUP.get(prefix);\n if (!canonicalOp) {\n // Not a known operator (e.g., email \"user@host.com\" or version \"1.2.3\")\n // Treat the entire string as an equality value\n return [\"==\", raw];\n }\n\n // Null-testing operators ignore their serialized value — normalize to null\n // so the tuple round-trips stably (`isnull.null` → [\"is-null\", null]).\n if (NULL_OPS.has(canonicalOp)) {\n // ...but only when what follows is an operand this operator has. See\n // the docblock: `notnull.reason` names no null test, so it is a value.\n if (!NULL_OPERANDS.has(rest)) return [\"==\", raw];\n return [canonicalOp, null];\n }\n\n // Parse list values: \"(admin,editor)\" → [\"admin\", \"editor\"]\n if (rest.startsWith(\"(\") && rest.endsWith(\")\")) {\n const inner = rest.slice(1, -1);\n // See EMPTY_LIST_TOKEN: `(\\)` is the empty list. `()` remains a list\n // holding one empty string, which is what splitting it yields anyway.\n const items = inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner);\n return [canonicalOp, items];\n }\n\n // A list operator with no list is not that operator — see the docblock.\n // `?status=in.progress` is the value \"in.progress\"; the `in` filter it used\n // to compile to was never written by anyone.\n if (LIST_OPS.has(canonicalOp)) return [\"==\", raw];\n\n return [canonicalOp, rest];\n}\n\n/**\n * Convert a PostgREST-style querystring record to `FilterValues`.\n *\n * - String values are parsed as single conditions.\n * - String arrays (repeated query params) become multiple conditions on the same field.\n *\n * @example\n * deserializeFilter({ status: \"eq.active\" })\n * // → { status: [\"==\", \"active\"] }\n *\n * deserializeFilter({ age: [\"gte.18\", \"lt.65\"] })\n * // → { age: [[\">=\", \"18\"], [\"<\", \"65\"]] }\n *\n * @throws {UnknownFilterOperatorError} when a condition names an operator this\n * dialect does not have. See that class for why a rejection here is a throw.\n */\nexport function deserializeFilter(\n query: Record<string, unknown>\n): FilterValues<string> {\n const result: FilterValues<string> = {};\n\n for (const [field, raw] of Object.entries(query)) {\n if (raw === undefined) continue;\n\n // A single `[op, value]` condition.\n const tuple = readTuple(field, raw);\n if (tuple) {\n result[field] = tuple;\n continue;\n }\n\n if (Array.isArray(raw)) {\n if (raw.length === 0) continue;\n\n // An array of tuples: several conditions on the same field. Every\n // element is checked, not just the first — the old test read\n // `raw[0]` and cast the whole array, so one bad operator among\n // several travelled on untouched.\n if (Array.isArray(raw[0])) {\n const tuples = raw.map(item => readTuple(field, item));\n if (tuples.every((t): t is [WhereFilterOp, unknown] => t !== undefined)) {\n result[field] = tuples;\n continue;\n }\n }\n\n if (raw.length === 1) {\n result[field] = typeof raw[0] === \"string\" ? deserializeSingle(raw[0]) : [\"==\", raw[0]];\n } else {\n // If the elements are strings, they might be PostgREST dot-strings (repeated params)\n if (typeof raw[0] === \"string\" && raw[0].includes(\".\")) {\n result[field] = raw.map(r => typeof r === \"string\" ? deserializeSingle(r) : ([\"==\", r] as [WhereFilterOp, unknown])) as [WhereFilterOp, unknown][];\n } else {\n // Otherwise assume it's a list of values for an implicit\n // \"in\" — `{ tags: [\"a\",\"b\"] }`, and `?tags=a&tags=b`, which\n // arrives here identically.\n //\n // A two-element array reaches this line only after\n // `readTuple` has decided its first element was not meant\n // as an operator. Everything longer never had the\n // ambiguity: an operator tuple has exactly two slots.\n result[field] = [\"in\", raw];\n }\n }\n } else if (typeof raw === \"string\") {\n result[field] = deserializeSingle(raw);\n } else {\n result[field] = [\"==\", raw];\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Logical conditions: serialize / deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a `LogicalCondition` or `FilterCondition` to its wire-format string.\n *\n * Leaf encoding is {@link serializeOperatorAndValue}, the same function\n * `serializeTuple` uses, so `null`, the empty list and an unknown operator\n * behave identically inside a group and in a query parameter.\n *\n * @throws {TypeError} when a leaf names an operator this dialect does not have.\n * It used to fall back to `eq`, which turned `age >= 18` into `age = 18` with\n * no diagnostic anywhere.\n *\n * @example\n * serializeLogicalCondition({ column: \"status\", operator: \"==\", value: \"active\" })\n * // → \"status.eq.active\"\n *\n * serializeLogicalCondition({ column: \"deleted_at\", operator: \"==\", value: null })\n * // → \"deleted_at.isnull.null\"\n *\n * serializeLogicalCondition({ type: \"or\", conditions: [...] })\n * // → \"or(status.eq.active,status.eq.pending)\"\n */\nexport function serializeLogicalCondition(\n cond: LogicalCondition | FilterCondition\n): string {\n if (\"type\" in cond) {\n // LogicalCondition (and/or)\n const inner = (cond.conditions ?? [])\n .map(serializeLogicalCondition)\n .join(\",\");\n return `${cond.type}(${inner})`;\n }\n\n // FilterCondition. The leaf goes through the shared encoder, so a group\n // condition and a query parameter agree on nulls, empty lists and unknown\n // operators — see `serializeOperatorAndValue`.\n //\n // The column is escaped like a value: it is not one, but it shares the\n // delimiters, and a comma or paren in it would move where the group parser\n // thinks the condition ends. Dots are deliberately *not* escaped — a\n // relation path is `author.name` on the wire, and the parser below finds\n // the operator rather than assuming it is the second segment.\n return `${escapeWireValue(cond.column)}.${serializeOperatorAndValue(cond.operator, cond.value, {\n escapeScalar: true,\n where: \"serializeLogicalCondition\"\n })}`;\n}\n\n/**\n * Split a leaf condition into `column`, operator token and value.\n *\n * The naive reading — column is everything before the first dot, operator is\n * everything up to the second — cannot express a relation path. A filter on\n * `author.name` serializes to `author.name.eq.bob` and came back as the column\n * `author` with the operator `name`, which resolves to nothing, so the\n * fallback made it `author == \"eq.bob\"`: a condition that runs and matches\n * nothing, on a column the caller never named.\n *\n * So the operator is found rather than assumed: it is the first dot-separated\n * segment after the column that resolves to a real operator. Everything before\n * it is the column, everything after is the value. `version.eq.1.2.3` reads as\n * `version == \"1.2.3\"` because the scan stops at the first match — the `eq` at\n * offset 1, not a later segment — and `metadata->>x.eq.5` never had dots in the\n * column to begin with.\n *\n * Returns `undefined` when no segment resolves — `status.active`, an equality\n * written without an operator, which the caller handles.\n */\nfunction splitLeafCondition(str: string): { column: string; operator: WhereFilterOp; value: string } | undefined {\n // Dots inside a list value (`in.(1.5,2.5)`) are not separators. The value\n // always follows the operator, so the search only needs the region before\n // the first unescaped paren.\n let limit = str.length;\n for (let i = 0; i < str.length; i++) {\n if (str[i] === \"\\\\\") { i++; continue; }\n if (str[i] === \"(\") { limit = i; break; }\n }\n\n const dots: number[] = [];\n for (let i = 0; i < limit; i++) {\n if (str[i] === \"\\\\\") { i++; continue; }\n if (str[i] === \".\") dots.push(i);\n }\n\n // Segment 0 is always the column, and an operator needs a value after it,\n // so a candidate is bounded on both sides by a dot.\n for (let i = 1; i < dots.length; i++) {\n const operator = toCanonicalOp(str.substring(dots[i - 1] + 1, dots[i]));\n if (!operator) continue;\n return {\n column: unescapeWireValue(str.substring(0, dots[i - 1])),\n operator,\n value: str.substring(dots[i] + 1)\n };\n }\n\n return undefined;\n}\n\n/**\n * Parse a logical condition wire-format string back into a\n * `LogicalCondition` or `FilterCondition`.\n *\n * @example\n * deserializeLogicalCondition(\"status.eq.active\")\n * // → { column: \"status\", operator: \"==\", value: \"active\" }\n *\n * deserializeLogicalCondition(\"or(status.eq.active,age.gte.18)\")\n * // → { type: \"or\", conditions: [...] }\n */\n/**\n * How deeply `or(...)`/`and(...)` groups may nest.\n *\n * This parser recurses once per level, on a value that arrives in a query\n * string. Unbounded, twenty thousand levels reached `RangeError: Maximum call\n * stack size exceeded`, which a caller sees as a 500 about the call stack\n * rather than a 400 about their filter. Node's 16 KB header cap keeps a GET\n * below that in practice, but \"the HTTP layer happens to stop it\" is not a\n * bound this parser should rely on.\n *\n * Thirty-two is far past anything a real filter expresses; the deepest in this\n * repository's own tests is three.\n */\nexport const MAX_LOGICAL_NESTING_DEPTH = 32;\n\nexport function deserializeLogicalCondition(\n str: string,\n // Not `depth`: the body already uses that name for paren tracking, inside a\n // block that shadows a parameter of the same name — so the recursion\n // counter silently became the paren counter and never grew.\n nesting = 0\n): LogicalCondition | FilterCondition {\n if (nesting > MAX_LOGICAL_NESTING_DEPTH) {\n throw new Error(\n `Filter groups nest more than ${MAX_LOGICAL_NESTING_DEPTH} levels deep. ` +\n \"Flatten the condition — `or(a,or(b,c))` is `or(a,b,c)`.\"\n );\n }\n // Check for logical group: \"and(...)\", \"or(...)\" or \"not(...)\"\n const logicalMatch = str.match(/^(and|or|not)\\((.+)\\)$/);\n if (logicalMatch) {\n const type = logicalMatch[1] as \"and\" | \"or\" | \"not\";\n const innerStr = logicalMatch[2];\n\n const conditions = splitGroupItems(innerStr)\n .map(part => deserializeLogicalCondition(part, nesting + 1));\n\n return { type, conditions };\n }\n\n // FilterCondition: \"column.op.value\"\n const leaf = splitLeafCondition(str);\n if (!leaf) {\n const firstDot = str.indexOf(\".\");\n if (firstDot === -1) {\n return { column: unescapeWireValue(str), operator: \"==\", value: true };\n }\n // \"column.value\" — no segment resolved as an operator, so this is an\n // equality written without one. The value keeps its dots.\n return {\n column: unescapeWireValue(str.substring(0, firstDot)),\n operator: \"==\",\n value: unescapeWireValue(str.substring(firstDot + 1))\n };\n }\n\n const { column, operator, value: valueStr } = leaf;\n\n // A null test has no operand: `isnull.null` is what the serializer writes,\n // but a hand-written `isnull.true` means the same thing. Normalizing here\n // is what makes the tuple stable through a re-encode, and it matches\n // `deserializeSingle`, which has done it for query parameters all along.\n if (NULL_OPS.has(operator)) {\n return { column, operator, value: null };\n }\n\n // Parse list values with escape-aware splitting. The wrapping parens are\n // written by the serializer *after* the items are escaped, so an escaped\n // paren inside an item can never be mistaken for them.\n if (valueStr.startsWith(\"(\") && valueStr.endsWith(\")\")) {\n const inner = valueStr.slice(1, -1);\n // See EMPTY_LIST_TOKEN: `(\\)` is the empty list, which is not the same\n // query as a search for the empty string.\n const items = inner === EMPTY_LIST_TOKEN ? [] : splitListItems(inner);\n return { column, operator, value: items };\n }\n\n return { column, operator, value: unescapeWireValue(valueStr) };\n}\n","import { CollectionAccessor, DataDriver, Entity, EntityValues, FindAllParams, FindParams, FindResponse, FindResult, IterateParams, LogicalCondition, OrderByTuple, PageWalkOptions, RebaseApiError, RebaseData, RebaseSdkData, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, sortKeyToString, type AggregateParams, type AggregateRow, type AggregateSelect, type ComputedSortField, type FieldPath, type IncludeSpec, type NonColumnFieldPath, type NullsPlacement, type SearchMatch, type UpdateValues, type UpsertOptions, WhereFilterOp, WhereValueFor, isUnsupported, unsupportedMethod } from \"@rebasepro/types\";\nimport { toSnakeCase, toWireKey } from \"@rebasepro/utils\";\nimport { cursorToStartAfter, decodeCursor, reconcileCursorOrder } from \"./cursor\";\nimport { mergeIncludeSpecs } from \"./include-spec\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind, resolveFindWindow } from \"./paginate\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\nimport { resolveCollectionRelations } from \"../util/relations\";\nimport { EntityRelation } from \"@rebasepro/types\";\n\n/**\n * What a client says when its data source cannot subscribe.\n *\n * Named rather than inlined so the sentence a caller sees does not depend on\n * which of the two adapters below happened to build the client.\n */\nconst noRealtime = (slug: string): string =>\n `Realtime is not available for \"${slug}\": its data source does not support subscriptions.`;\n\n/** What a client says when its data source cannot count. */\nconst noCount = (slug: string): string =>\n `Counting is not available for \"${slug}\": its data source does not support it.`;\n\n/**\n * Derive the response key an aggregate comes back under.\n *\n * `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because\n * the REST parser derives the same alias from `?select=sum(total)` and the two\n * have to agree — a caller reading `row.sum_total` off an SDK result and off an\n * HTTP response is reading the same key or the SDK is broken.\n */\nexport function aggregateAlias(fn: string, field?: string): string {\n return field ? `${fn}_${field}` : fn;\n}\n\nfunction toDriverAggregate(\n select: AggregateSelect<Record<string, unknown>>\n): { fn: \"count\" | \"sum\" | \"avg\" | \"min\" | \"max\"; field?: string; alias: string } {\n const field = select.field as string | undefined;\n return { fn: select.fn, field, alias: aggregateAlias(select.fn, field) };\n}\n\n/**\n * What a client says when its data source cannot aggregate.\n *\n * A stub rather than a fallback that fetches and reduces in JavaScript: that\n * would be wrong under a `limit` and unaffordable without one, and it would look\n * like it had worked.\n */\nconst noAggregate = (slug: string): string =>\n `Aggregates are not available for \"${slug}\": its data source does not implement them.`;\n\nexport interface EntityDataOptions {\n /**\n * Look up a collection's config by slug, to derive row addresses from its\n * primary keys.\n *\n * Called lazily rather than up front: the data layer is created by `Rebase`,\n * which sits *above* the admin that owns the collections, so a resolver\n * registered on mount would otherwise arrive too late to be seen.\n */\n resolveCollection?: (slug: string) => { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined;\n}\n\nfunction createPrimaryKeyResolver(options?: EntityDataOptions) {\n const cache = new Map<string, PrimaryKeyInfo[]>();\n const warned = new Set<string>();\n\n return function primaryKeysFor(slug: string): PrimaryKeyInfo[] {\n const cached = cache.get(slug);\n if (cached) return cached;\n\n const collection = options?.resolveCollection?.(slug);\n if (!collection) {\n // The registry may not have been registered yet. Don't memoize a\n // miss, or the collection would stay address-less for this session.\n return [];\n }\n\n const keys = resolvePrimaryKeys(collection);\n if (keys.length > 0) {\n // Memoized for the session: a collection's key does not change\n // while the app runs, and this is called once per row. Editing\n // `isId` in the schema editor needs a reload to take effect here.\n cache.set(slug, keys);\n return keys;\n }\n\n if (!warned.has(slug)) {\n warned.add(slug);\n // Silence here surfaces much later as rows that cannot be opened,\n // linked, or saved, with nothing pointing back at the cause.\n console.warn(\n `[rebase] Collection '${slug}' declares no primary key, so its rows have no address: ` +\n `detail links, caching and relations will not work for it. ` +\n `Mark the key property with \\`isId\\` in its collection config — the server logs which ` +\n `column to mark at boot, if its schema knows the key.`\n );\n }\n return keys;\n };\n}\n\n/**\n * Build the admin's view model out of the row the wire serves.\n *\n * The wire has ONE shape, for every consumer: flat columns, typed the way the\n * database typed them, and a relation rendered as the target's own columns (or\n * only its foreign key, when nothing asked for it). That is the REST contract,\n * what `find()` returns, what `listen()` pushes, and what the generated types\n * describe.\n *\n * The admin renders neither of those directly. Its date field requires a real\n * `Date` and rejects a string outright; its relation cells read `.data.values`\n * off a relation ref. Those requirements are the *admin's*, so they are met\n * here — in the browser, from the collection config the panel already has —\n * rather than by asking the server for a second wire shape.\n *\n * That second shape is what this replaces. Until 2026-09-09 the realtime wire\n * carried the view model and every other read carried flat rows, so `find()`\n * and `listen()` answered one query two ways; unifying the wire without doing\n * this conversion is what left every date cell reading \"Invalid date value\"\n * and every relation cell \"Unexpected value\".\n *\n * Values already in view-model form pass through untouched: a driver that\n * still sends `{ __type: \"date\" }` or a relation ref (the client revives both)\n * is served by the same walk.\n */\nfunction toViewModelValues(\n values: Record<string, unknown>,\n properties: Record<string, unknown> | undefined,\n collection: { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined,\n resolveCollection?: EntityDataOptions[\"resolveCollection\"]\n): Record<string, unknown> {\n if (!properties) return values;\n\n const relations = collection\n ? resolveCollectionRelations(collection as never)\n : {};\n let out: Record<string, unknown> | undefined;\n const write = (key: string, value: unknown) => {\n out = out ?? { ...values };\n out[key] = value;\n };\n\n for (const [key, rawProperty] of Object.entries(properties)) {\n const property = rawProperty as { type?: string; of?: { type?: string }; properties?: Record<string, unknown> } | undefined;\n if (!property) continue;\n\n // A relation nobody included is still a relation: the row carries only\n // its foreign key, and an addressable ref with no data attached is what\n // lets the preview fetch the one record it needs. Without this the\n // record form showed an empty chip where the customer goes — the panel\n // reads a form through `listenById`, which takes no `include`.\n if (!(key in values)) {\n const fkRelation = relations[key];\n // `localKey` is the column; the row is keyed the way the wire keys\n // it, which is that column camelCased (`customer_id` → `customerId`).\n const column = fkRelation && \"localKey\" in fkRelation ? fkRelation.localKey : undefined;\n const fk = column !== undefined\n ? values[column] ?? values[toWireKey(column)]\n : undefined;\n const fkTarget = fkRelation?.targetSlug;\n if (fkTarget && (typeof fk === \"string\" || typeof fk === \"number\")) {\n write(key, new EntityRelation(fk, fkTarget));\n }\n continue;\n }\n\n const value = values[key];\n if (value === null || value === undefined) continue;\n\n // A relation, under the property key or the relation name.\n const relation = relations[key];\n if (relation && (property.type === \"relation\" || property.of?.type === \"relation\" || property.type === \"array\")) {\n const target = relation.targetSlug;\n if (!target) continue;\n const targetProperties = resolveCollection?.(target)?.properties;\n const targetCollection = resolveCollection?.(target);\n const toRef = (item: unknown): unknown => {\n if (item instanceof EntityRelation) return item;\n if (typeof item === \"object\" && item !== null && \"__type\" in item) return item;\n // The target's own columns: the id it is addressed by, and the\n // values a relation cell renders without a second fetch.\n if (typeof item === \"object\" && item !== null) {\n const row = item as Record<string, unknown>;\n const keys = targetCollection ? resolvePrimaryKeys(targetCollection as never) : [];\n const id = keys.length > 0 ? buildCompositeId(row, keys) : row.id as string | number;\n if (id === undefined || id === null || id === \"\") return item;\n return new EntityRelation(id, target, {\n id,\n path: target,\n values: toViewModelValues(row, targetProperties, targetCollection, resolveCollection)\n });\n }\n // Only the foreign key came back — nothing asked for the\n // relation. Addressable, with nothing to render but its id.\n if (typeof item === \"string\" || typeof item === \"number\") {\n return new EntityRelation(item, target);\n }\n return item;\n };\n write(key, Array.isArray(value) ? value.map(toRef) : toRef(value));\n continue;\n }\n\n if (property.type === \"date\" && !(value instanceof Date)) {\n if (typeof value === \"string\" || typeof value === \"number\") {\n const date = new Date(value);\n write(key, isNaN(date.getTime()) ? null : date);\n }\n continue;\n }\n\n // A map's children are declared too, and a date two levels down is\n // still a date.\n if (property.type === \"map\" && property.properties && typeof value === \"object\" && !Array.isArray(value)) {\n write(key, toViewModelValues(value as Record<string, unknown>, property.properties, undefined, resolveCollection));\n }\n }\n\n return out ?? values;\n}\n\n/**\n * Give a flat row the Entity view-model the admin renders.\n *\n * The address is *derived here* — it is not a column, and the row it came from\n * does not contain one. Rows carry exactly what the table has, with the types\n * Postgres returned; the id is this layer's invention, and this is the only\n * place it is minted.\n *\n * `primaryKeys` empty falls back to a literal `id` on the row: drivers other\n * than postgres still serve rows with one, and this keeps them working.\n */\nfunction rowToEntity<M extends Record<string, unknown>>(\n row: Record<string, unknown>,\n slug: string,\n primaryKeys: PrimaryKeyInfo[] = [],\n /**\n * Turns the wire's row into the view model — see\n * {@link toViewModelValues}. Absent when the collections cannot be\n * resolved, which is every consumer that is not the admin: the flat SDK\n * derives itself from this layer and must keep the wire's own types.\n */\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): Entity<M> {\n // Query-computed metadata rides in on the row because that is how the wire\n // carries it, but it is not a column: it belongs beside `values`, not in\n // them. Left inside, `_matches` would show up in the record inspector as a\n // field the collection never declared.\n const { _matches, ...values } = row as Record<string, unknown> & { _matches?: SearchMatch[] };\n\n return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: (toViewModel ? toViewModel(values) : values) as EntityValues<M>,\n ...(_matches ? { searchMatches: _matches } : {})\n };\n}\n\n/**\n * The relation envelope `toFlatRow` writes where a relation was:\n * `{ id, path, __type: \"relation\", data: { id, path, values } }`. It is the\n * admin's view-model, and the only pipeline that produces one is postgres'.\n */\nfunction isRelationEnvelope(\n value: unknown\n): value is { __type: \"relation\"; data?: { values?: Record<string, unknown> } } {\n return typeof value === \"object\"\n && value !== null\n && !Array.isArray(value)\n && (value as { __type?: unknown }).__type === \"relation\";\n}\n\n/** The target's own columns, as `toRestRow` would have inlined them. */\nfunction inlineEnvelope(envelope: { data?: { values?: Record<string, unknown> } }): Record<string, unknown> {\n return envelope.data?.values ?? {};\n}\n\n/**\n * Replace every relation envelope on a row with the target's flat columns.\n *\n * The SDK serves one relation shape — the inlined one (see\n * {@link RestFetchService}) — and Postgres now serves it on every read, so\n * against that driver this walk finds nothing to do. It stays for the drivers\n * whose own `fetchCollection` still answers with refs: a developer reading\n * through this accessor gets one shape whichever driver is underneath.\n *\n * Only applied where the REST pipeline is the contract (see `find`); a driver\n * without a `restFetchService` keeps whatever it returns.\n *\n * Note this is NOT how the admin gets its view model — that is built in the\n * browser by {@link toViewModelValues}, from the same flat row.\n */\nfunction inlineRelationRefs(row: Record<string, unknown>): Record<string, unknown> {\n let out: Record<string, unknown> | undefined;\n for (const [key, value] of Object.entries(row)) {\n if (isRelationEnvelope(value)) {\n out = out ?? { ...row };\n out[key] = inlineEnvelope(value);\n } else if (Array.isArray(value) && value.some(isRelationEnvelope)) {\n out = out ?? { ...row };\n out[key] = value.map((item) => isRelationEnvelope(item) ? inlineEnvelope(item) : item);\n }\n }\n return out ?? row;\n}\n\nfunction createDriverAccessor<M extends Record<string, unknown> = Record<string, unknown>>(\n driver: DataDriver,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): CollectionAccessor<M> {\n const accessor: CollectionAccessor<M> = {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n // Ensure filters are in canonical [op, value] format even if passed as PostgREST strings\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n\n // Keyset paging, through the same codec and the same driver\n // comparison the HTTP route uses. The in-process accessor is a\n // transport like any other: a walk that seeked differently here\n // than over the wire would be a difference the types cannot see.\n const cursor = params?.after ? decodeCursor(params.after) : undefined;\n const orderBy = cursor\n ? reconcileCursorOrder(cursor, normalizeOrderBy(params?.orderBy))\n : normalizeOrderBy(params?.orderBy);\n const startAfter = cursor ? cursorToStartAfter(cursor) : undefined;\n\n // One relation shape, whatever the call looks like.\n //\n // This used to fork on `include`: asking for one ran the REST\n // pipeline, which inlines a relation as the target's own columns;\n // not asking ran the driver's own fetch, which eagerly loaded\n // *every* relation and put a `{ __type: \"relation\" }` envelope\n // where the foreign key was. The same method answered in two\n // shapes, the generated types described only one, and a column\n // typed `string` arrived as an object.\n //\n // The REST pipeline is the published contract — the shape the HTTP\n // API serves for this same query, and what `RestFetchService`\n // documents — so every read goes through it when the driver has\n // one. Drivers without one (every browser driver, and so the\n // admin's own path through `buildRebaseData`) are untouched.\n //\n // One row past the page, when seeking.\n //\n // `hasMore` on an offset page is `offset + rows.length < total`, and\n // under a cursor that arithmetic is simply false: every seeked page\n // runs at offset 0, so it compares one page against the whole\n // collection and says \"more\" forever. Asking for `limit + 1` and\n // looking at whether the extra row arrived is the answer keyset\n // paging actually has — and it costs nothing, where the count it\n // replaces was a second query per page.\n const probeLimit = startAfter ? limit + 1 : limit;\n\n const fetchService = driver.restFetchService;\n const fetched = fetchService\n ? await fetchService.fetchCollectionForRest(\n slug,\n {\n filter,\n // Without this the group was dropped and the read ran\n // unfiltered — every row the caller's policies allow,\n // in place of the ones they asked for.\n logical: params?.logical,\n limit: probeLimit,\n // A cursor and an offset describe the same window two\n // incompatible ways; seeking wins and the offset is not\n // sent, or the page would start `offset` rows past\n // where the cursor pointed.\n offset: startAfter ? undefined : driverOffset,\n startAfter,\n orderBy,\n searchString: params?.searchString,\n fields: params?.fields,\n distinct: params?.distinct\n },\n params?.include\n )\n : await driver.fetchCollection<M>({\n path: slug,\n limit: probeLimit,\n offset: startAfter ? undefined : driverOffset,\n startAfter,\n filter,\n logical: params?.logical,\n orderBy,\n searchString: params?.searchString,\n include: params?.include,\n fields: params?.fields,\n distinct: params?.distinct\n });\n\n // The probe row is evidence, not data — it is never served.\n const seeking = startAfter !== undefined;\n const rows = seeking ? fetched.slice(0, limit) : fetched;\n\n // Compute real total when count is available\n let total = rows.length + offset;\n let hasMore = seeking ? fetched.length > limit : rows.length >= limit;\n if (driver.count) {\n // The same narrowing the rows were read with. Counting only by\n // `filter` reported the whole collection beside a narrowed\n // page, and `hasMore` is derived from it — so the list offered\n // a next page that did not exist.\n total = await driver.count({\n path: slug,\n filter,\n logical: params?.logical,\n searchString: params?.searchString\n });\n // ...but only for an *offset* page. `offset` is 0 on every\n // seeked page, so this arithmetic compares one page against the\n // whole collection and says \"more\" forever; the probe row above\n // is what answers it under a cursor.\n if (!seeking) hasMore = offset + rows.length < total;\n }\n\n // The cursor for the *next* page, from the last row served. Issued\n // by the driver, which is the only layer that knows which columns\n // address a row; absent where it cannot describe one, and the\n // caller then pages by offset.\n const last = rows[rows.length - 1] as Record<string, unknown> | undefined;\n const nextCursor = (hasMore && last && driver.restFetchService?.cursorFor)\n ? driver.restFetchService.cursorFor(slug, last, orderBy)\n : undefined;\n\n return {\n data: rows.map((row: Record<string, unknown>) => rowToEntity<M>(row, slug, getPks(), toViewModel)),\n meta: { total, limit, offset, hasMore, ...(nextCursor && { nextCursor }) }\n };\n },\n\n async findById(id: string | number): Promise<Entity<M> | undefined> {\n // Same contract as `find` above: one row read the same way the\n // collection read is, so `find()[0]` and `findById()` agree.\n const fetchService = driver.restFetchService;\n const row = fetchService\n ? await fetchService.fetchOneForRest(slug, id)\n : await driver.fetchOne<M>({ path: slug, id: id });\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n\n // Present only when the driver's fetch service implements it — the SDK\n // wrapper turns an absent one into a stub that names the capability.\n aggregate: driver.restFetchService?.aggregate\n ? async (params: AggregateParams<M>): Promise<AggregateRow[]> =>\n driver.restFetchService!.aggregate!(slug, {\n aggregates: params.select.map(toDriverAggregate),\n groupBy: params.groupBy as string[] | undefined,\n filter: params.where\n ? deserializeFilter(params.where as Record<string, unknown>)\n : undefined,\n logical: params.logical,\n searchString: params.searchString,\n limit: params.limit\n })\n : undefined,\n\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: slug,\n values: data,\n id: id,\n status: \"new\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n createMany: driver.saveMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: slug,\n rows: data,\n upsert: options?.upsert,\n // Dropped here, an `upsert` on a natural key silently\n // became an upsert on the primary key — which for a serial\n // id is a plain insert, so the re-runnable import the\n // option exists for duplicated every row instead.\n onConflict: options?.onConflict\n });\n return rows.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: slug,\n values: data,\n id: id,\n status: \"existing\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n async delete(id: string | number): Promise<void> {\n return driver.delete({\n row: { id,\npath: slug,\nvalues: {} as Record<string, unknown> }\n });\n },\n\n // Present only when the driver is: exposing these unconditionally and\n // looping single writes underneath would give a caller neither the\n // atomicity nor the single round trip they reached for a batch to get,\n // while looking exactly like it had.\n updateMany: driver.updateMany\n ? async (updates: { id: string | number; data: Partial<EntityValues<M>> }[]): Promise<Entity<M>[]> => {\n const rows = await driver.updateMany!<M>({\n path: slug,\n updates: updates.map(u => ({ id: u.id,\nvalues: u.data })),\n });\n return rows.map(row => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n\n deleteMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: slug,\nids });\n }\n : undefined,\n\n count: driver.count\n ? async (params?: FindParams<M>): Promise<number> => {\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n // Every narrowing `find()` applies has to apply here too, or\n // the count describes a different query than the one it is\n // reported against.\n return driver.count!({\n path: slug,\n filter,\n logical: params?.logical,\n searchString: params?.searchString\n });\n }\n : undefined,\n\n listen: driver.listenCollection\n ? (params: FindParams<M> | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void) => {\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n // Belt and braces. Postgres serves one shape on every read now,\n // realtime included, so this flattens nothing there — but a\n // driver whose `listen` still answers with refs is normalized\n // to the shape the rest of this accessor serves rather than\n // handing a developer two.\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenCollection!<M>({\n path: slug,\n limit,\n offset: driverOffset,\n filter: params?.where,\n logical: params?.logical,\n orderBy: normalizeOrderBy(params?.orderBy),\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Forwarded so the SERVER can refuse it. `realtimeService`\n // rejects a subscription carrying `vectorSearch` — a\n // subscription is re-run on every matching write and\n // nothing there computes distances — and the docs promise\n // that refusal. Both producers hand-list their fields and\n // both omitted this one, so the guard could not fire and\n // `.vectorSearch(…).listen()` returned an ordinary\n // `id DESC` listing with no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks(), toViewModel)),\n meta: {\n // No count is issued on this path, so the total\n // is unknown; the lower bound is the rows in\n // hand plus the ones paged past to reach them.\n // Reporting `entities.length` claimed a read at\n // offset 100 had found a collection of two.\n total: offset + entities.length,\n limit,\n offset,\n hasMore: entities.length >= limit\n }\n });\n },\n onError\n });\n } : undefined,\n\n listenById: driver.listenOne\n ? (id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void) => {\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenOne!<M>({\n path: slug,\n id: id,\n onUpdate: (entity) => onUpdate(entity ? rowToEntity<M>(normalize(entity), slug, getPks(), toViewModel) : undefined),\n onError\n });\n } : undefined,\n\n // Fluent Query Builder\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, ascending?: \"asc\" | \"desc\") {\n return new QueryBuilder<M>(accessor).orderBy(column, ascending);\n },\n limit(count: number) {\n return new QueryBuilder<M>(accessor).limit(count);\n },\n offset(count: number) {\n return new QueryBuilder<M>(accessor).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new QueryBuilder<M>(accessor).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new QueryBuilder<M>(accessor).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new QueryBuilder<M>(accessor).include(...relations);\n }\n };\n\n return accessor;\n}\n\n/**\n * Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.\n *\n * This is the key bridge: any property access like `data.products` returns\n * a `CollectionAccessor` backed by the underlying DataDriver, without\n * needing per-collection code generation.\n *\n * @example\n * const data = buildRebaseData(driver);\n * await data.products.create({ name: \"Camera\", price: 299 });\n * const { data: items } = await data.products.find({ where: { status: [\"==\", \"published\"] } });\n */\n/**\n * The view-model converter for one collection, or `undefined` when there is no\n * collection config to build it from.\n *\n * Absent is the honest answer for every consumer that is not the admin: the\n * flat SDK derives itself from this same layer (`buildSdkData`) and must keep\n * the wire's own types, and it registers no collection resolver.\n */\nfunction createViewModelConverter(options?: EntityDataOptions) {\n if (!options?.resolveCollection) return () => undefined;\n return function converterFor(slug: string) {\n return (values: Record<string, unknown>): Record<string, unknown> => {\n // Resolved per call rather than memoized: the resolver is\n // late-bound (see `createPrimaryKeyResolver`) and a collection\n // edited in the schema editor should not need a reload here.\n const collection = options.resolveCollection?.(slug);\n if (!collection) return values;\n return toViewModelValues(values, collection.properties, collection, options.resolveCollection);\n };\n };\n}\n\nexport function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug), viewModelFor(slug));\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = {\n collection: getAccessor\n } as RebaseData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n // Ignore Symbol properties (e.g. Symbol.toPrimitive, Symbol.iterator)\n if (typeof prop === \"symbol\") return undefined;\n // Ignore internal JS properties\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n\n // Convert camelCase property names to snake_case slugs\n const slug = toSnakeCase(prop);\n return getAccessor(slug);\n }\n });\n}\n\n// =============================================================================\n// SDK data — flat rows (symmetric with the frontend SDK client)\n// =============================================================================\n\n/**\n * Unwrap a Entity back into the flat row it was built from. `rowToEntity` keeps\n * the row untouched under `.values` and derives `.id` alongside it, so dropping\n * the wrapper is the whole operation — the address was never part of the row.\n */\nfunction entityToRow<M extends Record<string, unknown>>(entity: Entity<M>): M {\n return entity.values as unknown as M;\n}\n\n/**\n * Fluent query builder for the flat SDK data layer. Mirrors {@link QueryBuilder}\n * but resolves to `FindResult<M>` (flat rows) instead of Entity-wrapped\n * `FindResponse<M>`.\n */\nclass SdkQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n private params: FindParams = { where: {} };\n\n constructor(private client: SDKCollectionClient<M>) {}\n\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n /** A relation path (`author.name`) or a JSON path (`metadata->>tier`). */\n where(column: NonColumnFieldPath, operator: WhereFilterOp, value: unknown): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK\n // builder in `@rebasepro/client`, which had the same defect.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n if (!this.params.where) this.params.where = {};\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n return this;\n }\n\n /** Called again, this adds a tie-breaker rather than replacing the sort. */\n orderBy(\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction: \"asc\" | \"desc\" = \"asc\",\n nulls?: NullsPlacement\n ): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n const key = sortKeyToString(column);\n this.params.orderBy = [...existing, (nulls\n ? [key, direction, nulls]\n : [key, direction]) as OrderByTuple];\n return this;\n }\n\n limit(count: number): this { this.params.limit = count; return this; }\n offset(count: number): this { this.params.offset = count; return this; }\n search(searchString: string, options?: { explain?: boolean }): this { this.params.searchString = searchString; if (options?.explain !== undefined) this.params.searchExplain = options.explain; return this; }\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n /**\n * Load relations. Merges rather than replaces, so `.include(\"author\")` then\n * `.include({ comments: { limit: 5 } })` asks for both — a builder call that\n * silently discarded an earlier one is the same defect `where` had.\n */\n include(...relations: (string | IncludeSpec)[]): this {\n this.params.include = mergeIncludeSpecs(this.params.include, relations);\n return this;\n }\n\n fields(...columns: (FieldPath<M> | string)[]): this {\n this.params.fields = [...(this.params.fields ?? []), ...columns as string[]];\n return this;\n }\n\n distinct(enabled = true): this { this.params.distinct = enabled; return this; }\n\n after(cursor: string): this { this.params.after = cursor; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n /** Aggregate the matching rows. See {@link SDKCollectionClient.aggregate}. */\n async aggregate(\n params: Omit<AggregateParams<M>, \"where\" | \"logical\" | \"searchString\">\n ): Promise<AggregateRow[]> {\n return this.client.aggregate({\n ...params,\n where: this.params.where as AggregateParams<M>[\"where\"],\n logical: this.params.logical,\n searchString: this.params.searchString\n });\n }\n\n /**\n * Page through everything this query matches, one row at a time.\n *\n * `.limit()` on the builder becomes the page size, so the ceiling on a\n * single `find()` is not a ceiling on what the query can read.\n */\n iterate(options?: PageWalkOptions<M>): AsyncIterableIterator<M> {\n return this.client.iterate({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as IterateParams<M>);\n }\n\n /** Collect everything this query matches into one array. */\n findAll(options?: PageWalkOptions<M> & { maxRows?: number }): Promise<M[]> {\n return this.client.findAll({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as FindAllParams<M>);\n }\n\n /**\n * Count the records matching this query.\n *\n * This used to answer `0` when the client had no `count` — a number, from a\n * source that had not counted anything, indistinguishable from an empty\n * collection. It now does what the client does, which on a source that\n * cannot count is throw and say so.\n */\n async count(): Promise<number> {\n return this.client.count(this.params as FindParams<M>);\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n return this.client.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n\n/**\n * Wrap a Entity-shaped {@link CollectionAccessor} into a flat\n * {@link SDKCollectionClient}. Every returned record is unwrapped to a flat row\n * so the backend SDK is byte-for-byte the same shape as the frontend client.\n */\nfunction toSdkCollectionClient<M extends Record<string, unknown>>(\n snap: CollectionAccessor<M>,\n slug = \"collection\"\n): SDKCollectionClient<M> {\n const client: SDKCollectionClient<M> = {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const res = await snap.find(params);\n return { data: res.data.map(entityToRow), meta: res.meta };\n },\n // Pagination is shared with the HTTP client rather than reimplemented:\n // both transports satisfy the same `SDKCollectionClient`, so a walk that\n // behaved differently in-process than over the wire would be a bug the\n // type system could not see.\n iterate(params?: IterateParams<M>) {\n return paginateFind<M>((p) => client.find(p), params, slug);\n },\n findAll(params?: FindAllParams<M>) {\n return collectAllPages<M>((p) => client.find(p), params, slug);\n },\n async findById(id: string | number): Promise<M | undefined> {\n const s = await snap.findById(id);\n return s ? entityToRow(s) : undefined;\n },\n async get(id: string | number): Promise<M> {\n // The same contract server-side as in the browser SDK, deliberately:\n // a callback, a cron and an app all read a row by id, and the shape\n // of \"it is not there\" should not depend on which one is asking.\n const s = await snap.findById(id);\n if (!s) {\n throw new RebaseApiError(\n `No record with id ${JSON.stringify(String(id))} in \"${slug}\".`,\n { status: 404, code: \"NOT_FOUND\" }\n );\n }\n return entityToRow(s);\n },\n async create(data: Partial<M>, id?: string | number): Promise<M> {\n return entityToRow(await snap.create(data as Partial<EntityValues<M>>, id));\n },\n async createMany(data: Partial<M>[], options?: { upsert?: boolean }): Promise<M[]> {\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n if (!snap.createMany) {\n throw new Error(\n \"Bulk writes are not supported by this collection's data source. \" +\n \"Fall back to create() per record.\"\n );\n }\n const rows = await snap.createMany(data as Partial<EntityValues<M>>[], options);\n return rows.map(entityToRow);\n },\n /**\n * One row through the bulk path, because the bulk path is where the\n * conflict target lives.\n *\n * `CollectionAccessor` has no single-row upsert and adding one would\n * mean a second way to say the same thing to the same driver method —\n * `saveMany` already takes `upsert` and `onConflict`, and a batch of\n * one is exactly an upsert of one.\n */\n async upsert(data: Partial<M>, options?: UpsertOptions): Promise<M> {\n if (!snap.createMany) {\n throw new Error(\n \"Upsert is not supported by this collection's data source: it needs a bulk write, \" +\n \"which this driver does not implement. Fall back to create() or update().\"\n );\n }\n const rows = await snap.createMany(\n [data as Partial<EntityValues<M>>],\n { upsert: true, onConflict: options?.onConflict }\n );\n const row = rows[0];\n if (!row) throw new Error(`Upsert into \"${slug}\" returned no row.`);\n return entityToRow(row);\n },\n async update(id: string | number, data: Partial<M> | UpdateValues<Partial<M>>): Promise<M> {\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n async updateMany(updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[]): Promise<M[]> {\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n if (!snap.updateMany) {\n throw new Error(\n \"Bulk updates are not supported by this collection's data source. \" +\n \"Fall back to update() per record.\"\n );\n }\n const rows = await snap.updateMany(\n updates.map(u => ({ id: u.id,\ndata: u.data as Partial<EntityValues<M>> }))\n );\n return rows.map(entityToRow);\n },\n delete(id: string | number): Promise<void> {\n return snap.delete(id);\n },\n async deleteMany(ids: (string | number)[]): Promise<void> {\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n if (!snap.deleteMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to delete() per record.\"\n );\n }\n await snap.deleteMany(ids);\n },\n // The three are non-optional on `SDKCollectionClient`: where the\n // underlying accessor cannot serve one, a stub says so when called\n // rather than being absent. `isUnsupported()` is how an adapter asks\n // the capability question — see `toEntityAccessor` below, which has to.\n count: snap.count\n ? (params?: FindParams<M>) => snap.count!(params)\n : unsupportedMethod(noCount(slug)),\n listen: snap.listen\n ? (params: FindParams<M> | undefined, onUpdate: (r: FindResult<M>) => void, onError?: (e: Error) => void) =>\n snap.listen!(params, (res) => onUpdate({ data: res.data.map(entityToRow), meta: res.meta }), onError)\n : unsupportedMethod(noRealtime(slug)),\n listenById: snap.listenById\n ? (id: string | number, onUpdate: (r: M | undefined) => void, onError?: (e: Error) => void) =>\n snap.listenById!(id, (s) => onUpdate(s ? entityToRow(s) : undefined), onError)\n : unsupportedMethod(noRealtime(slug)),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SdkQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction?: \"asc\" | \"desc\",\n nulls?: NullsPlacement\n ) => new SdkQueryBuilder<M>(client).orderBy(column, direction, nulls),\n limit: (count: number) => new SdkQueryBuilder<M>(client).limit(count),\n offset: (count: number) => new SdkQueryBuilder<M>(client).offset(count),\n search: (searchString: string) => new SdkQueryBuilder<M>(client).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new SdkQueryBuilder<M>(client).vectorSearch(property, vector, options),\n include: (...relations: (string | IncludeSpec)[]) => new SdkQueryBuilder<M>(client).include(...relations),\n fields: (...columns: (FieldPath<M> | string)[]) => new SdkQueryBuilder<M>(client).fields(...columns),\n distinct: (enabled?: boolean) => new SdkQueryBuilder<M>(client).distinct(enabled),\n after: (cursor: string) => new SdkQueryBuilder<M>(client).after(cursor),\n aggregate: snap.aggregate\n ? (params: AggregateParams<M>) => snap.aggregate!(params)\n : unsupportedMethod(noAggregate(slug))\n };\n return client;\n}\n\n/**\n * Wrap a flat {@link SDKCollectionClient} into a Entity-shaped\n * {@link CollectionAccessor}. Every returned row is re-wrapped into the\n * `{ id, path, values }` view-model the admin panel renders.\n */\nfunction toEntityAccessor<M extends Record<string, unknown>>(\n sdk: SDKCollectionClient<M>,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): CollectionAccessor<M> {\n const accessor: CollectionAccessor<M> = {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n const res = await sdk.find(params);\n return { data: res.data.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel)), meta: res.meta };\n },\n async findById(id: string | number): Promise<Entity<M> | undefined> {\n const row = await sdk.findById(id);\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n return rowToEntity<M>(await sdk.create(data as Partial<M>, id), slug, getPks(), toViewModel);\n },\n // Declared on `CollectionAccessor` and, until now, never implemented on\n // this side of the boundary — so the admin's own import wrote one HTTP\n // request per row and could neither be atomic nor upsert. It forwards to\n // the same `/bulk` route the SDK client uses.\n createMany: sdk.createMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await sdk.createMany!(data as Partial<M>[], options);\n return rows.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await sdk.update(id, data as Partial<M>);\n if (!row) throw new Error(`Update returned no data for id ${id}`);\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n delete(id: string | number): Promise<void> {\n return sdk.delete(id);\n },\n // `CollectionAccessor` keeps these optional, and the optionality is\n // load-bearing: the admin panel picks between subscribing and a\n // one-shot `find()` on exactly this property, and a UI that subscribes\n // into a throw is worse than one that polls. The client's method is\n // always present now, so the capability is read off the stub instead.\n count: isUnsupported(sdk.count) ? undefined : (params?: FindParams<M>) => sdk.count(params),\n aggregate: isUnsupported(sdk.aggregate)\n ? undefined\n : (params: AggregateParams<M>) => sdk.aggregate(params),\n listen: isUnsupported(sdk.listen)\n ? undefined\n : (params: FindParams<M> | undefined, onUpdate: (r: FindResponse<M>) => void, onError?: (e: Error) => void) =>\n sdk.listen(params, (res) => onUpdate({ data: res.data.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel)), meta: res.meta }), onError),\n listenById: isUnsupported(sdk.listenById)\n ? undefined\n : (id: string | number, onUpdate: (s: Entity<M> | undefined) => void, onError?: (e: Error) => void) =>\n sdk.listenById(id, (row) => onUpdate(row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined), onError),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new QueryBuilder<M>(accessor).orderBy(column, direction),\n limit: (count: number) => new QueryBuilder<M>(accessor).limit(count),\n offset: (count: number) => new QueryBuilder<M>(accessor).offset(count),\n search: (searchString: string) => new QueryBuilder<M>(accessor).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new QueryBuilder<M>(accessor).vectorSearch(property, vector, options),\n include: (...relations: string[]) => new QueryBuilder<M>(accessor).include(...relations)\n };\n return accessor;\n}\n\n/**\n * Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.\n *\n * This is the **admin boundary**: the SDK client (`client.data`) returns flat\n * rows, but the admin renders the `Entity` view-model (`entity.values.*`).\n * `core/Rebase.tsx` wraps `client.data` through this before handing it to the\n * admin `RebaseDataContext` — without it the admin renders rows with only their\n * `id`.\n */\n/**\n * Only the by-slug accessor is asked for, so only that is required.\n *\n * Taking a whole `RebaseSdkData` meant taking `RebaseSdkData<unknown>`, whose\n * dynamic branch is an index signature — and no `RebaseSdkData<DB>` satisfies\n * it, because its own `collection` method is not a `SDKCollectionClient`. So a\n * caller holding a *typed* client could not pass it to a function that reads\n * one method off it, and that method is identical on every instantiation.\n */\nexport function wrapAsEntityData(sdkData: Pick<RebaseSdkData, \"collection\">, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug), viewModelFor(slug));\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = { collection: getAccessor } as RebaseData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n if (typeof prop === \"symbol\") return undefined;\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n return getAccessor(toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Wrap a Entity-shaped {@link RebaseData} into a flat {@link RebaseSdkData}.\n *\n * Every collection accessor is adapted to return flat rows. Use this to derive\n * the flat SDK data layer (`context.data`) from an existing Entity data layer\n * — e.g. the admin routes its Entity data via `useData()` and exposes the\n * same routing as flat `context.data` for callbacks by wrapping it here.\n */\nexport function wrapAsSdkData(entityData: RebaseData): RebaseSdkData {\n const cache = new Map<string, SDKCollectionClient>();\n\n function getAccessor(slug: string): SDKCollectionClient {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toSdkCollectionClient(entityData.collection(slug), slug);\n cache.set(slug, accessor);\n }\n return accessor;\n }\n\n const target = { collection: getAccessor } as RebaseSdkData;\n\n return new Proxy(target, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n if (typeof prop === \"symbol\") return undefined;\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n return getAccessor(toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Build a flat {@link RebaseSdkData} from a `DataDriver`.\n *\n * This is the developer-facing SDK data layer used by backend framework\n * callbacks & scripts (`context.data` / `rebase.dataAsAdmin`). It returns flat rows —\n * identical in shape to the frontend SDK client, down to how a relation is\n * served: a foreign key stays a foreign key, and a relation named in `include`\n * arrives as the target's own columns. The `{ __type: \"relation\" }` envelope is\n * the admin's view-model and never reaches here.\n *\n * The admin uses {@link buildRebaseData} (Entity) over its own driver.\n */\nexport function buildSdkData(driver: DataDriver): RebaseSdkData {\n return wrapAsSdkData(buildRebaseData(driver));\n}\n","import { RebaseData, RebaseSdkData } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The two data-layer shapes that can be routed: the Entity-shaped admin\n * {@link RebaseData} or the flat SDK {@link RebaseSdkData}. Both expose a\n * `.collection(slug)` accessor, which is all the router needs.\n */\nexport type RoutableData = RebaseData | RebaseSdkData;\n\n/**\n * Parameters for {@link buildRoutedRebaseData}.\n */\nexport interface RoutedRebaseDataParams<T extends RoutableData = RebaseData> {\n /**\n * The default data source. Handles every collection that does not\n * resolve to an entry in `sources` (i.e. server-transport collections,\n * which ride the Rebase client).\n */\n defaultData: T;\n\n /**\n * Per-data-source instances for direct and custom transports, keyed by\n * data-source key (e.g. `\"analytics\"`). Server-mediated sources are not\n * listed here — they fall through to `defaultData`.\n */\n sources: Record<string, T>;\n\n /**\n * Resolve the data-source key for a given collection slug or path.\n * Typically backed by the collection registry + `resolveDataSource`\n * (`resolveDataSource(registry.getCollection(path), defs).key`).\n *\n * Return `undefined` (or a key absent from `sources`) to route to the\n * default data source.\n */\n resolveKey: (slugOrPath: string) => string | undefined;\n}\n\n/**\n * Build a {@link RebaseData} that routes each collection to the right\n * backend based on its resolved data source.\n *\n * `.collection(path)` (and dynamic `data.products`-style access) resolves the\n * collection's data-source key via `resolveKey` and delegates to the matching\n * entry in `sources`, falling back to `defaultData` when there is no match.\n * Because routing keys off the *path being accessed*, a reference widget\n * inside a Firestore form that points at a Postgres collection is still\n * served by Postgres — routing follows the target, not the ancestor.\n *\n * When `sources` is empty this returns `defaultData` untouched, so the\n * single-driver setup keeps identical behaviour and identity (important for\n * effect dependencies that key off the data instance).\n *\n * @example\n * const data = buildRoutedRebaseData({\n * defaultData: client.data,\n * sources: { analytics: buildRebaseData(firestoreDriver) },\n * resolveKey: (path) => resolveDataSource(registry.getCollection(path), defs).key\n * });\n * await data.products.find(); // → default (server / Postgres)\n * await data.events.find(); // → Firestore, if `events.dataSource === \"analytics\"`\n */\nexport function buildRoutedRebaseData<T extends RoutableData = RebaseData>({\n defaultData,\n sources,\n resolveKey\n}: RoutedRebaseDataParams<T>): T {\n\n // Fast path: nothing to route → return the default untouched (preserves\n // referential identity for effect dependencies).\n if (!sources || Object.keys(sources).length === 0) {\n return defaultData;\n }\n\n function resolve(slugOrPath: string): T {\n const key = resolveKey(slugOrPath);\n if (key && sources[key]) return sources[key];\n return defaultData;\n }\n\n function getAccessor(slugOrPath: string) {\n return (resolve(slugOrPath) as RoutableData).collection(slugOrPath);\n }\n\n const target = {\n collection: getAccessor\n } as unknown as T;\n\n return new Proxy(target as object, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") return getAccessor;\n // Ignore Symbol properties (e.g. Symbol.toPrimitive, Symbol.iterator)\n if (typeof prop === \"symbol\") return undefined;\n // Ignore internal JS properties\n if (prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\") return undefined;\n\n // Convert camelCase property names to snake_case slugs, mirroring\n // buildRebaseData so dynamic access routes consistently.\n return getAccessor(toSnakeCase(prop));\n }\n }) as T;\n}\n","/**\n * The `FilterValues` grammar, one level below the wire codec.\n *\n * A field's filter is either one `[op, value]` tuple or an **array** of them —\n * `{ age: [[\">=\", 18], [\"<\", 65]] }` — which is what the fluent builder produces\n * from two `.where()` calls on the same column. Reading that shape is grammar,\n * not a driver detail, so every compiler reads it through here.\n *\n * It lived only inside the Postgres compiler, and the Mongo one destructured\n * `const [op, value] = filterParam` regardless: given the array-of-tuples form\n * `op` bound to `[\">=\", 18]`, no operator matched, and the condition was\n * dropped. Both of them. A read asking for adults under 65 returned every row\n * of the collection with a 200.\n *\n * @module\n */\n\nimport type { WhereFilterOp } from \"@rebasepro/types\";\n\n/** One `[operator, value]` condition. */\nexport type FilterTuple = [WhereFilterOp, unknown];\n\n/**\n * Read one field's filter as the list of conditions it stands for.\n *\n * Accepts both declared shapes and normalises them to a list:\n *\n * ```ts\n * toFilterTuples([\"==\", \"active\"]) // [[\"==\", \"active\"]]\n * toFilterTuples([[\">=\", 18], [\"<\", 65]]) // [[\">=\", 18], [\"<\", 65]]\n * ```\n *\n * A falsy, non-array or empty param has no conditions in it — the empty list,\n * so a caller iterating adds nothing rather than compiling a tuple of\n * `undefined`s and logging about an operator nobody sent.\n */\nexport function toFilterTuples(filterParam: unknown): FilterTuple[] {\n if (!filterParam || !Array.isArray(filterParam) || filterParam.length === 0) return [];\n // The first element discriminates: a condition starts with an operator\n // string, a list of conditions starts with a condition. `[\"in\", [\"a\",\"b\"]]`\n // is one condition whose value happens to be a list.\n if (Array.isArray(filterParam[0])) {\n return filterParam as FilterTuple[];\n }\n return [filterParam as FilterTuple];\n}\n","/**\n * Table Classification\n *\n * Shared constants and pure functions for classifying database tables.\n * Used by both the server-side PostgresBackendDriver and the Studio RLS editor.\n */\n\n/** Possible categories a database table can belong to. */\nexport type TableCategory = \"rebase-internal\" | \"junction\" | \"user\";\n\n/** Schemas that are always considered Rebase-internal. */\nexport const REBASE_INTERNAL_SCHEMAS: readonly string[] = [\"rebase\", \"auth\"];\n\n/** Table-name prefixes that mark a table as Rebase-internal regardless of schema. */\nexport const REBASE_INTERNAL_PREFIXES: readonly string[] = [\n \"_rebase_\",\n \"_auth_\",\n \"drizzle_\",\n];\n\n/**\n * Synchronously classify a table based on naming conventions.\n *\n * @param tableName - The unqualified name of the table.\n * @param schemaName - The schema the table belongs to (e.g. `\"public\"`, `\"rebase\"`).\n * @returns `\"rebase-internal\"` when the table belongs to a reserved schema or\n * carries a reserved prefix; `\"user\"` otherwise.\n *\n * @remarks\n * Junction-table detection requires an async database query and is therefore\n * **not** handled by this function. Use {@link detectJunctionTables} to obtain\n * the set of junction tables, then reclassify as needed.\n */\nexport function classifyTable(\n tableName: string,\n schemaName: string,\n): TableCategory {\n if (\n REBASE_INTERNAL_SCHEMAS.includes(schemaName) ||\n REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))\n ) {\n return \"rebase-internal\";\n }\n\n return \"user\";\n}\n\n/**\n * Convenience predicate that checks whether a table is Rebase-internal.\n *\n * @param tableName - The unqualified name of the table.\n * @param schemaName - The schema the table belongs to.\n * @returns `true` if the table is classified as `\"rebase-internal\"`.\n */\nexport function isRebaseInternalTable(\n tableName: string,\n schemaName: string,\n): boolean {\n return classifyTable(tableName, schemaName) === \"rebase-internal\";\n}\n\n/** SQL query that detects junction tables in the `public` schema. */\nexport const JUNCTION_TABLES_SQL = `\n SELECT t.table_name\n FROM information_schema.tables t\n WHERE t.table_schema = 'public'\n AND t.table_type = 'BASE TABLE'\n AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns c\n WHERE c.table_schema = t.table_schema\n AND c.table_name = t.table_name\n AND c.column_name NOT IN (\n SELECT kcu.column_name\n FROM information_schema.key_column_usage kcu\n JOIN information_schema.table_constraints tc\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY'\n AND kcu.table_schema = t.table_schema\n AND kcu.table_name = t.table_name\n )\n )\n`;\n\n/**\n * Asynchronously detect junction (link) tables in the `public` schema.\n *\n * A junction table is defined as a table where **every** column participates in\n * at least one foreign-key constraint.\n *\n * @param executeSql - A callback that executes a raw SQL string and returns the\n * resulting rows.\n * @returns A `Set` containing the names of all detected junction tables.\n */\nexport async function detectJunctionTables(\n executeSql: (sql: string) => Promise<Record<string, unknown>[]>,\n): Promise<Set<string>> {\n const rows = await executeSql(JUNCTION_TABLES_SQL);\n const junctionTables = new Set<string>();\n\n for (const row of rows) {\n if (typeof row.table_name === \"string\") {\n junctionTables.add(row.table_name);\n }\n }\n\n return junctionTables;\n}\n"],"mappings":"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"}
|