@rebasepro/common 0.13.0 → 0.13.1-canary.gcd6689e

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.
@@ -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/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/data/resolveDataSource.ts","../src/collections/CollectionRegistry.ts","../src/collections/default-collections.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/sort-dialect.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 if (property.defaultValue || property.defaultValue === null) {\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 * 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 * Returns null if the value cannot be coerced.\n */\nexport function normalizeToEntityRelation(value: unknown, propertyType?: string): EntityRelation | null {\n if (value instanceof EntityRelation) return value;\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\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\" | \"validation\"> = {\n relationName,\n target,\n targetSlug: targetCollection.slug,\n onUpdate: relation.onUpdate,\n onDelete: relation.onDelete,\n overrides: relation.overrides,\n validation: relation.validation\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 }\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 * Call the `target` thunk, and translate the two ways an import cycle breaks it\n * into an error that names the cause.\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. What it cannot\n * defer is a cycle that leaves the binding permanently unusable, and there are\n * two shapes of that:\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.** The half-initialised module object has no `default` yet,\n * the import resolves to `undefined`, and the thunk returns it without\n * 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 */\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 = 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 : \"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 } 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\nexport function getTableName(collection: CollectionConfig): string {\n if (isRelationalCollectionConfig(collection)) {\n return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);\n }\n return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);\n}\n\nexport function getTableVarName(tableName: string): string {\n return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());\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 * 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 const {\n values,\n previousValues,\n ...rest\n } = 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 * 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 } from \"@rebasepro/types\";\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 = auth.uid())`\n * split the expression, and re-emitting the halves produced\n * `(EXISTS (...) AND m.user_id = auth.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 const trimmed = stripOuterParens(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(auth.roles(), ',') && ARRAY['admin', 'editor']\n const overlapMatch = trimmed.match(/^string_to_array\\s*\\(\\s*auth\\.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(auth.roles(), ',') @> ARRAY['admin']\n const containMatch = trimmed.match(/^string_to_array\\s*\\(\\s*auth\\.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\n return policy.raw(sql);\n}\n\n/**\n * Literals from other BaaS platforms that people compare `auth.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 */\nconst FOREIGN_CONVENTION_UIDS: Record<string, string> = {\n anon: \"Supabase\",\n authenticated: \"Supabase\",\n service_role: \"Supabase\"\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/** `auth.uid() IS NOT NULL` in raw SQL, the clause that is always true. */\nconst UID_NOT_NULL = /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 `auth.uid()` really\n * 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 * - `auth.uid() IS NOT NULL` is a tautology on the user path, and\n * - `auth.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: \"`auth.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 return;\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[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 auth.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 if (/current_setting\\s*\\(\\s*'app\\.(uid|user_id)'\\s*\\)/i.test(str) || /auth\\.uid\\(\\)/i.test(str)) {\n return policy.authUid();\n }\n\n // Literal string: 'value'\n const stringMatch = str.match(/^'(.+)'$/);\n if (stringMatch) {\n return policy.literal(stringMatch[1]);\n }\n\n // Bare field name\n if (/^\\w+$/.test(str)) {\n return policy.field(str);\n }\n\n return null;\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 } 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 // `auth.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 const leftSql = castForAuthUid(expr.left, operandToSql(expr.left, scope), expr.right);\n const rightSql = 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(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;\n case \"rolesContain\":\n return `string_to_array(auth.roles(), ',') @> ${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 `auth.uid() IS NOT NULL AND auth.uid() NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`;\n case \"serverContext\":\n // Only the built-in server flows leave `app.uid` unset.\n return \"auth.uid() IS NULL\";\n case \"existsIn\":\n return compileExistsIn(expr, scope);\n case \"raw\":\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 expr.sql.replace(/\\{(\\w+)\\}/g, (_, col) =>\n `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);\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 \"auth.uid()\";\n case \"authRoles\":\n return \"string_to_array(auth.roles(), ',')\";\n }\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 (prop as { columnName: string }).columnName;\n }\n return toSnakeCase(propName);\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 `auth.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 /** 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 \"serverContext\":\n // A client is never the server context. Postgres decides this by\n // `auth.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: `auth.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. `auth.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 }\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 if (op === \"eq\") return false;\n if (op === \"neq\") return true;\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, getDataSourceCapabilities, 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\nexport interface CheckOperationOptions {\n onUnknown?: UnknownResolution;\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(rule: SecurityRule, ctx: PolicyEvalContext, targetOperation: SecurityOperation): 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\";\n const needsWithCheck = targetOperation === \"insert\" || targetOperation === \"update\";\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 * @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 */\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 securityRules = getDataSourceCapabilities(collection.engine).supportsRLS ? collection.securityRules : undefined;\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), 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 InferEntityType,\n MongoDBCollectionConfig,\n MongoProperties,\n PostgresCollectionConfig,\n PostgresProperties,\n User\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 `titleProperty`, `sort`, `propertiesOrder`, `fixedFilter`,\n// callbacks, etc. — without writing `as const` or passing manual generics.\n\n/**\n * Define a PostgreSQL-backed collection with full type inference.\n *\n * The `const P` generic captures literal property types from your\n * `properties` object, which enables autocomplete on `titleProperty`,\n * `sort`, `propertiesOrder`, `fixedFilter`, and entity callbacks.\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 * titleProperty: \"name\", // ✅ autocomplete: \"name\" | \"price\"\n * sort: [\"price\", \"asc\"], // ✅ autocomplete on first element\n * });\n * ```\n *\n * @group Builder\n */\nexport function defineCollection<\n const P extends PostgresProperties,\n USER extends User = User\n>(\n collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): PostgresCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Define a Firestore-backed collection with full type inference.\n * @group Builder\n */\nexport function defineCollection<\n const P extends FirebaseProperties,\n USER extends User = User\n>(\n collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): FirebaseCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Define a MongoDB-backed collection with full type inference.\n * @group Builder\n */\nexport function defineCollection<\n const P extends MongoProperties,\n USER extends User = User\n>(\n collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): MongoDBCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Implementation — delegates to the correct overload at the type level.\n * At runtime this is a plain identity function.\n */\nexport function defineCollection(\n collection: CollectionConfig\n): CollectionConfig {\n return collection;\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 { CollectionConfig, SecurityRule, SecurityOperation, AuthCollectionConfig, PolicyExpression, isPostgresCollectionConfig, policy } from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\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,\n * `dataAsAdmin`) runs as the owner and bypasses RLS.\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 = auth.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 `auth.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 * Opt out with `disableDefaultPolicies: true` to take full responsibility for\n * the collection's RLS.\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// `auth.uid() IS NULL OR (string_to_array(auth.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 */\nexport function getEffectiveSecurityRules(collection: CollectionConfig): SecurityRule[] {\n const explicit = [...(collection.securityRules ?? [])];\n\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n return explicit;\n }\n\n const tableName = getTableName(collection);\n const injected: SecurityRule[] = [];\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`.\n injected.push({\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 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) return [];\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 PolicyExpression,\n PolicyOperand,\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\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 });\n } else if (!existing.declaringSides.some(s => s.collection === collection)) {\n existing.declaringSides.push(source);\n }\n }\n }\n\n return specs;\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 */\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 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 EnumValueConfig,\n JsonLogicRule,\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 JSON Logic rule against the given context.\n */\nexport function evaluateCondition(rule: JsonLogicRule, context: ConditionContext): unknown {\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 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 { prettifyIdentifier } 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/admin-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 properties[column.column_name] = property;\n propertiesOrder.push(column.column_name);\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 = fk.column_name.endsWith(\"_id\") ? fk.column_name.substring(0, fk.column_name.length - 3) : fk.column_name;\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 const qual = policy.qual ?? undefined;\n const withCheck = 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","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","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.data`).\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 console.warn(\n `Relation property '${key}' on '${collection.slug}' declares no \\`relation\\`, and the ` +\n \"collection has no relation of that name.\"\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/admin-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 {\n CollectionAccessor,\n FilterCondition,\n FindParams,\n FindResponse,\n LogicalCondition,\n QueryBuilderInterface,\n WhereFilterOp,\n WhereValue\n} from \"@rebasepro/types\";\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\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>(column: K, operator: WhereFilterOp, value: WhereValue<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 this.params.logical = columnOrCondition as LogicalCondition;\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 * @example\n * client.collection('users').orderBy('createdAt', 'desc').find()\n */\n orderBy(column: keyof M & string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.params.orderBy = [column, direction];\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): this {\n this.params.searchString = searchString;\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 FilterValues,\n FieldPath,\n FindAllParams,\n FindParams,\n FindResult,\n IterateParams,\n WhereFilterOp\n} from \"@rebasepro/types\";\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 /** A cursor row carried no value for the cursor column. */\n | \"cursor-missing\"\n /** Two consecutive pages ended on the same cursor value, so the walk cannot advance. */\n | \"cursor-stalled\"\n /** A `cursor` was asked for on one column while `orderBy` sorted by another. */\n | \"cursor-order-mismatch\";\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\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 * Add one condition to a `where` map without disturbing what is already there.\n *\n * The caller's own filter on the cursor column has to survive — dropping it\n * would widen the query, which is the silent-filter-loss failure mode — so a\n * second condition on the same column becomes the array-of-tuples form that\n * `FindParams.where` already accepts, and both are AND-ed.\n */\nfunction appendCondition<M extends Record<string, unknown>>(\n where: FilterValues<FieldPath<M>> | undefined,\n column: string,\n condition: [WhereFilterOp, unknown]\n): FilterValues<FieldPath<M>> {\n const next = { ...(where ?? {}) } as Record<string, unknown>;\n const existing = next[column];\n if (existing === undefined) {\n next[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n next[column] = [...(existing as [WhereFilterOp, unknown][]), condition];\n } else {\n next[column] = [existing, condition];\n }\n return next as FilterValues<FieldPath<M>>;\n}\n\nfunction cursorEquals(a: unknown, b: unknown): boolean {\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n return Object.is(a, b);\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 const cursorField = typeof cursor === \"string\" ? cursor : cursor?.field;\n const requestedDirection = (typeof cursor === \"object\" && cursor !== null)\n ? cursor.direction\n : undefined;\n\n let direction: \"asc\" | \"desc\" = \"asc\";\n if (cursorField) {\n const orderBy = findParams.orderBy;\n if (orderBy && orderBy[0] !== cursorField) {\n throw new RebasePaginationError(\n \"cursor-order-mismatch\",\n `Cannot seek on \"${cursorField}\" while ordering \"${label}\" by \"${orderBy[0]}\": ` +\n `keyset pagination only advances along the column the query is sorted by. ` +\n `Order by \"${cursorField}\", or drop the cursor and page by offset.`\n );\n }\n direction = requestedDirection ?? orderBy?.[1] ?? \"asc\";\n findParams.orderBy = [cursorField, direction] as FindParams<M>[\"orderBy\"];\n }\n const seekOp: WhereFilterOp = direction === \"desc\" ? \"<\" : \">\";\n const baseWhere = findParams.where;\n\n let offset = 0;\n let pages = 0;\n let cursorValue: unknown;\n let seeking = false;\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 (cursorField) {\n if (seeking) {\n pageParams.where = appendCondition<M>(baseWhere, cursorField, [seekOp, cursorValue]);\n }\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 (cursorField) {\n const last = rows[rows.length - 1] as Record<string, unknown>;\n const nextValue = last?.[cursorField];\n if (nextValue === undefined || nextValue === null) {\n throw new RebasePaginationError(\n \"cursor-missing\",\n `Cannot seek past the last row of \"${label}\": it has no value for the cursor ` +\n `column \"${cursorField}\". Pick a column that is present and non-null on every row.`\n );\n }\n if (seeking && cursorEquals(nextValue, cursorValue)) {\n throw new RebasePaginationError(\n \"cursor-stalled\",\n `Iterating \"${label}\" is stuck: two pages in a row ended at ` +\n `${cursorField}=${String(nextValue)}. The cursor column has to be unique — a ` +\n `repeated value cannot be seeked past, and continuing would either loop forever ` +\n `or skip the duplicates. Use the primary key, or page by offset.`\n );\n }\n cursorValue = nextValue;\n seeking = true;\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 * Commas inside list values are backslash-escaped (`\\,`), and literal\n * backslashes are escaped as `\\\\`.\n *\n * @module\n */\n\nimport {\n WhereFilterOp,\n FilterValues,\n CANONICAL_TO_REST,\n REST_TO_CANONICAL,\n RestFilterOp,\n toCanonicalOp,\n LogicalCondition,\n FilterCondition,\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 * Escape a single list item for the wire format.\n * `\\` → `\\\\`, `,` → `\\,`\n */\nfunction escapeListItem(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/,/g, \"\\\\,\");\n}\n\n/**\n * Unescape a single list item from the wire format.\n * `\\\\` → `\\`, `\\,` → `,`\n */\nfunction unescapeListItem(value: string): string {\n let result = \"\";\n for (let i = 0; i < value.length; i++) {\n if (value[i] === \"\\\\\" && i + 1 < value.length) {\n result += value[i + 1];\n i++; // skip next char\n } else {\n result += value[i];\n }\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 character — consume both chars\n current += inner[i] + inner[i + 1];\n i++;\n } else if (inner[i] === \",\") {\n items.push(unescapeListItem(current));\n current = \"\";\n } else {\n current += inner[i];\n }\n }\n items.push(unescapeListItem(current));\n return items;\n}\n\n// ---------------------------------------------------------------------------\n// Typed operator map lookups (no `as any`)\n// ---------------------------------------------------------------------------\n\nconst REST_OP_LOOKUP = REST_TO_CANONICAL as Readonly<Record<string, WhereFilterOp | undefined>>;\nconst CANONICAL_OP_LOOKUP = CANONICAL_TO_REST as Readonly<Record<string, RestFilterOp | undefined>>;\n\n// ---------------------------------------------------------------------------\n// Serialize: FilterValues → REST querystring\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\n if (typeof op !== \"string\") {\n throw new TypeError(\n `serializeTuple: operator must be a string, got ${typeof op}`\n );\n }\n\n const restOp = CANONICAL_OP_LOOKUP[op];\n if (!restOp) {\n throw new TypeError(\n `serializeTuple: unknown operator \"${op}\". Valid operators: ${Object.keys(CANONICAL_TO_REST).join(\", \")}`\n );\n }\n\n if (Array.isArray(value)) {\n const items = value.map(v => escapeListItem(stringifyValue(v))).join(\",\");\n return `${restOp}.(${items})`;\n }\n\n return `${restOp}.${stringifyValue(value)}`;\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 * 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 * If the string doesn't match a known operator prefix, it falls back to\n * `[\"==\", originalString]` (treating the whole string as an equality value).\n * This intentional defense handles values like `\"user@host.com\"` or\n * `\"1.2.3\"` that happen to contain dots.\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[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 return [canonicalOp, null];\n }\n\n // Parse list values: \"(admin,editor)\" → [\"admin\", \"editor\"]\n if (rest.startsWith(\"(\") && rest.endsWith(\")\")) {\n const items = splitListItems(rest.slice(1, -1));\n return [canonicalOp, items];\n }\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 */\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 // If it's already a canonical tuple [op, value], keep it as is\n if (Array.isArray(raw) && raw.length === 2 && typeof raw[0] === \"string\" && toCanonicalOp(raw[0]) === raw[0]) {\n result[field] = raw as [WhereFilterOp, unknown];\n continue;\n }\n\n if (Array.isArray(raw)) {\n if (raw.length === 0) continue;\n \n // Check if it's an array of canonical tuples\n if (Array.isArray(raw[0]) && raw[0].length === 2 && typeof raw[0][0] === \"string\" && toCanonicalOp(raw[0][0]) === raw[0][0]) {\n result[field] = raw as [WhereFilterOp, unknown][];\n continue;\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 \"in\" or just multiple conditions\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 * @example\n * serializeLogicalCondition({ column: \"status\", operator: \"==\", value: \"active\" })\n * // → \"status.eq.active\"\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\n const restOp = CANONICAL_OP_LOOKUP[cond.operator] ?? \"eq\";\n if (Array.isArray(cond.value)) {\n const items = cond.value.map(v => escapeListItem(stringifyValue(v))).join(\",\");\n return `${cond.column}.${restOp}.(${items})`;\n }\n return `${cond.column}.${restOp}.${stringifyValue(cond.value)}`;\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 */\nexport function deserializeLogicalCondition(\n str: string\n): LogicalCondition | FilterCondition {\n // Check for logical group: \"and(...)\" or \"or(...)\"\n const logicalMatch = str.match(/^(and|or)\\((.+)\\)$/);\n if (logicalMatch) {\n const type = logicalMatch[1] as \"and\" | \"or\";\n const innerStr = logicalMatch[2];\n\n // Split on commas that are not inside parentheses\n const conditions: (LogicalCondition | FilterCondition)[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < innerStr.length; i++) {\n if (innerStr[i] === \"(\") depth++;\n else if (innerStr[i] === \")\") depth--;\n else if (innerStr[i] === \",\" && depth === 0) {\n conditions.push(deserializeLogicalCondition(innerStr.slice(start, i)));\n start = i + 1;\n }\n }\n conditions.push(deserializeLogicalCondition(innerStr.slice(start)));\n\n return { type, conditions };\n }\n\n // FilterCondition: \"column.op.value\"\n const firstDot = str.indexOf(\".\");\n if (firstDot === -1) {\n return { column: str, operator: \"==\", value: true };\n }\n\n const column = str.substring(0, firstDot);\n const rest = str.substring(firstDot + 1);\n\n const secondDot = rest.indexOf(\".\");\n if (secondDot === -1) {\n // \"column.value\" — treat as equality (value kept as string)\n return { column, operator: \"==\", value: rest };\n }\n\n const opStr = rest.substring(0, secondDot);\n const valueStr = rest.substring(secondDot + 1);\n const operator = toCanonicalOp(opStr) ?? \"==\";\n\n // Parse list values with escape-aware splitting\n if (valueStr.startsWith(\"(\") && valueStr.endsWith(\")\")) {\n const items = splitListItems(valueStr.slice(1, -1));\n return { column, operator, value: items };\n }\n\n return { column, operator, value: valueStr };\n}\n","import {\n CollectionAccessor,\n DataDriver,\n Entity,\n EntityValues,\n FindAllParams,\n FindParams,\n FindResponse,\n FindResult,\n IterateParams,\n LogicalCondition,\n RebaseData,\n RebaseSdkData,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValue\n} from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind } from \"./paginate\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\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 return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: row as EntityValues<M>\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 = params?.limit ?? 20;\n const offset = params?.offset ?? 0;\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 const fetchService = driver.restFetchService;\n const rows = fetchService\n ? await fetchService.fetchCollectionForRest(\n slug,\n {\n filter,\n limit: params?.limit,\n offset: params?.offset,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString\n },\n params?.include\n )\n : await driver.fetchCollection<M>({\n path: slug,\n limit: params?.limit,\n offset: params?.offset,\n filter,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString\n });\n\n // Compute real total when count is available\n let total = rows.length + offset;\n let hasMore = rows.length >= limit;\n if (driver.count) {\n total = await driver.count({ path: slug, filter });\n hasMore = offset + rows.length < total;\n }\n\n return {\n data: rows.map((row: Record<string, unknown>) => rowToEntity<M>(row, slug, getPks())),\n meta: { total, limit, offset, hasMore }\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 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 (data: Partial<EntityValues<M>>[], options?: { upsert?: boolean }): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: slug,\n rows: data,\n upsert: options?.upsert\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 count: driver.count\n ? async (params?: FindParams<M>): Promise<number> => {\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n return driver.count!({\n path: slug,\n filter\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 = params?.limit ?? 20;\n const offset = params?.offset ?? 0;\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: params?.limit,\n offset: params?.offset,\n filter: params?.where,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks())),\n meta: {\n total: 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 WhereValue<M[keyof M & string]>);\n },\n orderBy(column: keyof M & string, 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) {\n return new QueryBuilder<M>(accessor).search(searchString);\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>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): 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 this.params.logical = columnOrCondition as LogicalCondition;\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 orderBy(column: keyof M & string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.params.orderBy = [column, direction];\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): this { this.params.searchString = searchString; return this; }\n include(...relations: string[]): this { this.params.include = relations; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n async count(): Promise<number> {\n return this.client.count ? this.client.count(this.params as FindParams<M>) : 0;\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.client.listen) {\n throw new Error(\"Listen is only available when the driver supports realtime.\");\n }\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 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 async update(id: string | number, data: Partial<M>): Promise<M> {\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n delete(id: string | number): Promise<void> {\n return snap.delete(id);\n },\n count: snap.count ? (params?: FindParams<M>) => snap.count!(params) : undefined,\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 : undefined,\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 : undefined,\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 WhereValue<M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new SdkQueryBuilder<M>(client).orderBy(column, direction),\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 include: (...relations: string[]) => new SdkQueryBuilder<M>(client).include(...relations)\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 admin 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 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 count: sdk.count ? (params?: FindParams<M>) => sdk.count!(params) : undefined,\n listen: sdk.listen\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 : undefined,\n listenById: sdk.listenById\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 : undefined,\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 WhereValue<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 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 */\nexport function wrapAsEntityData(sdkData: RebaseSdkData, 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.data`). 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","import type { OrderByTuple } 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.\n * Everything else speaks {@link OrderByTuple} exclusively.\n *\n * Mirrors the filter architecture in `filter-dialect.ts`.\n *\n * @module\n */\n\n/**\n * Serialize an {@link OrderByTuple} to the wire format `\"field:direction\"`.\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 `[field, direction]` tuple, 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** on the wire — this is an inherent limitation of the colon-delimited\n * encoding and is not resolved here.\n */\nexport function serializeOrderBy(orderBy?: OrderByTuple | 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 return `${orderBy[0]}:${orderBy[1]}`;\n}\n\n/**\n * Deserialize a wire-format `\"field:direction\"` string into an {@link OrderByTuple}.\n *\n * Lenient parsing (matches existing server behaviour):\n * - Bare field name (no colon): `\"name\"` → `[\"name\", \"asc\"]`\n * - Unknown direction: `\"name:foo\"` → `[\"name\", \"asc\"]`\n * - Empty / falsy input: → `undefined`\n *\n * @param raw - The wire-format string from an HTTP query parameter.\n * @returns The canonical tuple, or `undefined` if the input is empty/falsy.\n */\nexport function deserializeOrderBy(raw?: string): OrderByTuple | undefined {\n if (!raw) return undefined;\n const idx = raw.indexOf(\":\");\n if (idx === -1) return [raw, \"asc\"];\n const field = raw.slice(0, idx);\n const dir = raw.slice(idx + 1);\n return [field, dir === \"desc\" ? \"desc\" : \"asc\"];\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/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/data/resolveDataSource.ts","../src/collections/CollectionRegistry.ts","../src/collections/default-collections.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/sort-dialect.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 if (property.defaultValue || property.defaultValue === null) {\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 * 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 * Returns null if the value cannot be coerced.\n */\nexport function normalizeToEntityRelation(value: unknown, propertyType?: string): EntityRelation | null {\n if (value instanceof EntityRelation) return value;\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\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\" | \"validation\"> = {\n relationName,\n target,\n targetSlug: targetCollection.slug,\n onUpdate: relation.onUpdate,\n onDelete: relation.onDelete,\n overrides: relation.overrides,\n validation: relation.validation\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 }\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 * Call the `target` thunk, and translate the two ways an import cycle breaks it\n * into an error that names the cause.\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. What it cannot\n * defer is a cycle that leaves the binding permanently unusable, and there are\n * two shapes of that:\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.** The half-initialised module object has no `default` yet,\n * the import resolves to `undefined`, and the thunk returns it without\n * 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 */\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 = 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 : \"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 } 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\nexport function getTableName(collection: CollectionConfig): string {\n if (isRelationalCollectionConfig(collection)) {\n return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);\n }\n return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);\n}\n\nexport function getTableVarName(tableName: string): string {\n return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());\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 * 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 const {\n values,\n previousValues,\n ...rest\n } = 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 * 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 } from \"@rebasepro/types\";\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 = auth.uid())`\n * split the expression, and re-emitting the halves produced\n * `(EXISTS (...) AND m.user_id = auth.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 const trimmed = stripOuterParens(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(auth.roles(), ',') && ARRAY['admin', 'editor']\n const overlapMatch = trimmed.match(/^string_to_array\\s*\\(\\s*auth\\.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(auth.roles(), ',') @> ARRAY['admin']\n const containMatch = trimmed.match(/^string_to_array\\s*\\(\\s*auth\\.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\n return policy.raw(sql);\n}\n\n/**\n * Literals from other BaaS platforms that people compare `auth.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 */\nconst FOREIGN_CONVENTION_UIDS: Record<string, string> = {\n anon: \"Supabase\",\n authenticated: \"Supabase\",\n service_role: \"Supabase\"\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/** `auth.uid() IS NOT NULL` in raw SQL, the clause that is always true. */\nconst UID_NOT_NULL = /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 `auth.uid()` really\n * 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 * - `auth.uid() IS NOT NULL` is a tautology on the user path, and\n * - `auth.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: \"`auth.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 return;\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[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 auth.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 if (/current_setting\\s*\\(\\s*'app\\.(uid|user_id)'\\s*\\)/i.test(str) || /auth\\.uid\\(\\)/i.test(str)) {\n return policy.authUid();\n }\n\n // Literal string: 'value'\n const stringMatch = str.match(/^'(.+)'$/);\n if (stringMatch) {\n return policy.literal(stringMatch[1]);\n }\n\n // Bare field name\n if (/^\\w+$/.test(str)) {\n return policy.field(str);\n }\n\n return null;\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 } 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 // `auth.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 const leftSql = castForAuthUid(expr.left, operandToSql(expr.left, scope), expr.right);\n const rightSql = 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(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;\n case \"rolesContain\":\n return `string_to_array(auth.roles(), ',') @> ${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 `auth.uid() IS NOT NULL AND auth.uid() NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(\", \")})`;\n case \"serverContext\":\n // Only the built-in server flows leave `app.uid` unset.\n return \"auth.uid() IS NULL\";\n case \"existsIn\":\n return compileExistsIn(expr, scope);\n case \"raw\":\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 expr.sql.replace(/\\{(\\w+)\\}/g, (_, col) =>\n `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);\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 \"auth.uid()\";\n case \"authRoles\":\n return \"string_to_array(auth.roles(), ',')\";\n }\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 (prop as { columnName: string }).columnName;\n }\n return toSnakeCase(propName);\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 `auth.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 /** 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 \"serverContext\":\n // A client is never the server context. Postgres decides this by\n // `auth.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: `auth.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. `auth.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 }\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 if (op === \"eq\") return false;\n if (op === \"neq\") return true;\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, getDataSourceCapabilities, 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\nexport interface CheckOperationOptions {\n onUnknown?: UnknownResolution;\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(rule: SecurityRule, ctx: PolicyEvalContext, targetOperation: SecurityOperation): 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\";\n const needsWithCheck = targetOperation === \"insert\" || targetOperation === \"update\";\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 * @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 */\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 securityRules = getDataSourceCapabilities(collection.engine).supportsRLS ? collection.securityRules : undefined;\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), 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 InferEntityType,\n MongoDBCollectionConfig,\n MongoProperties,\n PostgresCollectionConfig,\n PostgresProperties,\n User\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/**\n * Define a PostgreSQL-backed collection with full type inference.\n *\n * The `const P` generic captures literal property types from your\n * `properties` object, which enables autocomplete on `display.title`,\n * `sort`, `propertiesOrder`, `fixedFilter`, and entity callbacks.\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 * display: { title: \"name\" }, // ✅ autocomplete: \"name\" | \"price\"\n * sort: [\"price\", \"asc\"], // ✅ autocomplete on first element\n * });\n * ```\n *\n * @group Builder\n */\nexport function defineCollection<\n const P extends PostgresProperties,\n USER extends User = User\n>(\n collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): PostgresCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Define a Firestore-backed collection with full type inference.\n * @group Builder\n */\nexport function defineCollection<\n const P extends FirebaseProperties,\n USER extends User = User\n>(\n collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): FirebaseCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Define a MongoDB-backed collection with full type inference.\n * @group Builder\n */\nexport function defineCollection<\n const P extends MongoProperties,\n USER extends User = User\n>(\n collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, \"properties\"> & { properties: P }\n): MongoDBCollectionConfig<InferEntityType<P>, USER> & { properties: P };\n\n/**\n * Implementation — delegates to the correct overload at the type level.\n * At runtime this is a plain identity function.\n */\nexport function defineCollection(\n collection: CollectionConfig\n): CollectionConfig {\n return collection;\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 { CollectionConfig, SecurityRule, SecurityOperation, AuthCollectionConfig, PolicyExpression, isPostgresCollectionConfig, policy } from \"@rebasepro/types\";\nimport { getTableName } from \"./relations\";\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,\n * `dataAsAdmin`) runs as the owner and bypasses RLS.\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 = auth.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 `auth.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 * Opt out with `disableDefaultPolicies: true` to take full responsibility for\n * the collection's RLS.\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// `auth.uid() IS NULL OR (string_to_array(auth.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 */\nexport function getEffectiveSecurityRules(collection: CollectionConfig): SecurityRule[] {\n const explicit = [...(collection.securityRules ?? [])];\n\n if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) {\n return explicit;\n }\n\n const tableName = getTableName(collection);\n const injected: SecurityRule[] = [];\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`.\n injected.push({\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 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) return [];\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 PolicyExpression,\n PolicyOperand,\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\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 });\n } else if (!existing.declaringSides.some(s => s.collection === collection)) {\n existing.declaringSides.push(source);\n }\n }\n }\n\n return specs;\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 */\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 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 EnumValueConfig,\n JsonLogicRule,\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 JSON Logic rule against the given context.\n */\nexport function evaluateCondition(rule: JsonLogicRule, context: ConditionContext): unknown {\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 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 { prettifyIdentifier } 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/admin-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 properties[column.column_name] = property;\n propertiesOrder.push(column.column_name);\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 = fk.column_name.endsWith(\"_id\") ? fk.column_name.substring(0, fk.column_name.length - 3) : fk.column_name;\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 const qual = policy.qual ?? undefined;\n const withCheck = 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","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","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.data`).\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 console.warn(\n `Relation property '${key}' on '${collection.slug}' declares no \\`relation\\`, and the ` +\n \"collection has no relation of that name.\"\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/admin-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 {\n CollectionAccessor,\n FilterCondition,\n FindParams,\n FindResponse,\n LogicalCondition,\n QueryBuilderInterface,\n WhereFilterOp,\n WhereValue\n} from \"@rebasepro/types\";\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\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>(column: K, operator: WhereFilterOp, value: WhereValue<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 this.params.logical = columnOrCondition as LogicalCondition;\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 * @example\n * client.collection('users').orderBy('createdAt', 'desc').find()\n */\n orderBy(column: keyof M & string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.params.orderBy = [column, direction];\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): this {\n this.params.searchString = searchString;\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 FilterValues,\n FieldPath,\n FindAllParams,\n FindParams,\n FindResult,\n IterateParams,\n WhereFilterOp\n} from \"@rebasepro/types\";\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 /** A cursor row carried no value for the cursor column. */\n | \"cursor-missing\"\n /** Two consecutive pages ended on the same cursor value, so the walk cannot advance. */\n | \"cursor-stalled\"\n /** A `cursor` was asked for on one column while `orderBy` sorted by another. */\n | \"cursor-order-mismatch\";\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\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 * Add one condition to a `where` map without disturbing what is already there.\n *\n * The caller's own filter on the cursor column has to survive — dropping it\n * would widen the query, which is the silent-filter-loss failure mode — so a\n * second condition on the same column becomes the array-of-tuples form that\n * `FindParams.where` already accepts, and both are AND-ed.\n */\nfunction appendCondition<M extends Record<string, unknown>>(\n where: FilterValues<FieldPath<M>> | undefined,\n column: string,\n condition: [WhereFilterOp, unknown]\n): FilterValues<FieldPath<M>> {\n const next = { ...(where ?? {}) } as Record<string, unknown>;\n const existing = next[column];\n if (existing === undefined) {\n next[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n next[column] = [...(existing as [WhereFilterOp, unknown][]), condition];\n } else {\n next[column] = [existing, condition];\n }\n return next as FilterValues<FieldPath<M>>;\n}\n\nfunction cursorEquals(a: unknown, b: unknown): boolean {\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n return Object.is(a, b);\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 const cursorField = typeof cursor === \"string\" ? cursor : cursor?.field;\n const requestedDirection = (typeof cursor === \"object\" && cursor !== null)\n ? cursor.direction\n : undefined;\n\n let direction: \"asc\" | \"desc\" = \"asc\";\n if (cursorField) {\n const orderBy = findParams.orderBy;\n if (orderBy && orderBy[0] !== cursorField) {\n throw new RebasePaginationError(\n \"cursor-order-mismatch\",\n `Cannot seek on \"${cursorField}\" while ordering \"${label}\" by \"${orderBy[0]}\": ` +\n `keyset pagination only advances along the column the query is sorted by. ` +\n `Order by \"${cursorField}\", or drop the cursor and page by offset.`\n );\n }\n direction = requestedDirection ?? orderBy?.[1] ?? \"asc\";\n findParams.orderBy = [cursorField, direction] as FindParams<M>[\"orderBy\"];\n }\n const seekOp: WhereFilterOp = direction === \"desc\" ? \"<\" : \">\";\n const baseWhere = findParams.where;\n\n let offset = 0;\n let pages = 0;\n let cursorValue: unknown;\n let seeking = false;\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 (cursorField) {\n if (seeking) {\n pageParams.where = appendCondition<M>(baseWhere, cursorField, [seekOp, cursorValue]);\n }\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 (cursorField) {\n const last = rows[rows.length - 1] as Record<string, unknown>;\n const nextValue = last?.[cursorField];\n if (nextValue === undefined || nextValue === null) {\n throw new RebasePaginationError(\n \"cursor-missing\",\n `Cannot seek past the last row of \"${label}\": it has no value for the cursor ` +\n `column \"${cursorField}\". Pick a column that is present and non-null on every row.`\n );\n }\n if (seeking && cursorEquals(nextValue, cursorValue)) {\n throw new RebasePaginationError(\n \"cursor-stalled\",\n `Iterating \"${label}\" is stuck: two pages in a row ended at ` +\n `${cursorField}=${String(nextValue)}. The cursor column has to be unique — a ` +\n `repeated value cannot be seeked past, and continuing would either loop forever ` +\n `or skip the duplicates. Use the primary key, or page by offset.`\n );\n }\n cursorValue = nextValue;\n seeking = true;\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 * Commas inside list values are backslash-escaped (`\\,`), and literal\n * backslashes are escaped as `\\\\`.\n *\n * @module\n */\n\nimport {\n WhereFilterOp,\n FilterValues,\n CANONICAL_TO_REST,\n REST_TO_CANONICAL,\n RestFilterOp,\n toCanonicalOp,\n LogicalCondition,\n FilterCondition,\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 * Escape a single list item for the wire format.\n * `\\` → `\\\\`, `,` → `\\,`\n */\nfunction escapeListItem(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/,/g, \"\\\\,\");\n}\n\n/**\n * Unescape a single list item from the wire format.\n * `\\\\` → `\\`, `\\,` → `,`\n */\nfunction unescapeListItem(value: string): string {\n let result = \"\";\n for (let i = 0; i < value.length; i++) {\n if (value[i] === \"\\\\\" && i + 1 < value.length) {\n result += value[i + 1];\n i++; // skip next char\n } else {\n result += value[i];\n }\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 character — consume both chars\n current += inner[i] + inner[i + 1];\n i++;\n } else if (inner[i] === \",\") {\n items.push(unescapeListItem(current));\n current = \"\";\n } else {\n current += inner[i];\n }\n }\n items.push(unescapeListItem(current));\n return items;\n}\n\n// ---------------------------------------------------------------------------\n// Typed operator map lookups (no `as any`)\n// ---------------------------------------------------------------------------\n\nconst REST_OP_LOOKUP = REST_TO_CANONICAL as Readonly<Record<string, WhereFilterOp | undefined>>;\nconst CANONICAL_OP_LOOKUP = CANONICAL_TO_REST as Readonly<Record<string, RestFilterOp | undefined>>;\n\n// ---------------------------------------------------------------------------\n// Serialize: FilterValues → REST querystring\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\n if (typeof op !== \"string\") {\n throw new TypeError(\n `serializeTuple: operator must be a string, got ${typeof op}`\n );\n }\n\n const restOp = CANONICAL_OP_LOOKUP[op];\n if (!restOp) {\n throw new TypeError(\n `serializeTuple: unknown operator \"${op}\". Valid operators: ${Object.keys(CANONICAL_TO_REST).join(\", \")}`\n );\n }\n\n if (Array.isArray(value)) {\n const items = value.map(v => escapeListItem(stringifyValue(v))).join(\",\");\n return `${restOp}.(${items})`;\n }\n\n return `${restOp}.${stringifyValue(value)}`;\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 * 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 * If the string doesn't match a known operator prefix, it falls back to\n * `[\"==\", originalString]` (treating the whole string as an equality value).\n * This intentional defense handles values like `\"user@host.com\"` or\n * `\"1.2.3\"` that happen to contain dots.\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[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 return [canonicalOp, null];\n }\n\n // Parse list values: \"(admin,editor)\" → [\"admin\", \"editor\"]\n if (rest.startsWith(\"(\") && rest.endsWith(\")\")) {\n const items = splitListItems(rest.slice(1, -1));\n return [canonicalOp, items];\n }\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 */\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 // If it's already a canonical tuple [op, value], keep it as is\n if (Array.isArray(raw) && raw.length === 2 && typeof raw[0] === \"string\" && toCanonicalOp(raw[0]) === raw[0]) {\n result[field] = raw as [WhereFilterOp, unknown];\n continue;\n }\n\n if (Array.isArray(raw)) {\n if (raw.length === 0) continue;\n \n // Check if it's an array of canonical tuples\n if (Array.isArray(raw[0]) && raw[0].length === 2 && typeof raw[0][0] === \"string\" && toCanonicalOp(raw[0][0]) === raw[0][0]) {\n result[field] = raw as [WhereFilterOp, unknown][];\n continue;\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 \"in\" or just multiple conditions\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 * @example\n * serializeLogicalCondition({ column: \"status\", operator: \"==\", value: \"active\" })\n * // → \"status.eq.active\"\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\n const restOp = CANONICAL_OP_LOOKUP[cond.operator] ?? \"eq\";\n if (Array.isArray(cond.value)) {\n const items = cond.value.map(v => escapeListItem(stringifyValue(v))).join(\",\");\n return `${cond.column}.${restOp}.(${items})`;\n }\n return `${cond.column}.${restOp}.${stringifyValue(cond.value)}`;\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 */\nexport function deserializeLogicalCondition(\n str: string\n): LogicalCondition | FilterCondition {\n // Check for logical group: \"and(...)\" or \"or(...)\"\n const logicalMatch = str.match(/^(and|or)\\((.+)\\)$/);\n if (logicalMatch) {\n const type = logicalMatch[1] as \"and\" | \"or\";\n const innerStr = logicalMatch[2];\n\n // Split on commas that are not inside parentheses\n const conditions: (LogicalCondition | FilterCondition)[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < innerStr.length; i++) {\n if (innerStr[i] === \"(\") depth++;\n else if (innerStr[i] === \")\") depth--;\n else if (innerStr[i] === \",\" && depth === 0) {\n conditions.push(deserializeLogicalCondition(innerStr.slice(start, i)));\n start = i + 1;\n }\n }\n conditions.push(deserializeLogicalCondition(innerStr.slice(start)));\n\n return { type, conditions };\n }\n\n // FilterCondition: \"column.op.value\"\n const firstDot = str.indexOf(\".\");\n if (firstDot === -1) {\n return { column: str, operator: \"==\", value: true };\n }\n\n const column = str.substring(0, firstDot);\n const rest = str.substring(firstDot + 1);\n\n const secondDot = rest.indexOf(\".\");\n if (secondDot === -1) {\n // \"column.value\" — treat as equality (value kept as string)\n return { column, operator: \"==\", value: rest };\n }\n\n const opStr = rest.substring(0, secondDot);\n const valueStr = rest.substring(secondDot + 1);\n const operator = toCanonicalOp(opStr) ?? \"==\";\n\n // Parse list values with escape-aware splitting\n if (valueStr.startsWith(\"(\") && valueStr.endsWith(\")\")) {\n const items = splitListItems(valueStr.slice(1, -1));\n return { column, operator, value: items };\n }\n\n return { column, operator, value: valueStr };\n}\n","import {\n CollectionAccessor,\n DataDriver,\n Entity,\n EntityValues,\n FindAllParams,\n FindParams,\n FindResponse,\n FindResult,\n IterateParams,\n LogicalCondition,\n RebaseData,\n RebaseSdkData,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValue\n} from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind } from \"./paginate\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\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 return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: row as EntityValues<M>\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 = params?.limit ?? 20;\n const offset = params?.offset ?? 0;\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 const fetchService = driver.restFetchService;\n const rows = fetchService\n ? await fetchService.fetchCollectionForRest(\n slug,\n {\n filter,\n limit: params?.limit,\n offset: params?.offset,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString\n },\n params?.include\n )\n : await driver.fetchCollection<M>({\n path: slug,\n limit: params?.limit,\n offset: params?.offset,\n filter,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString\n });\n\n // Compute real total when count is available\n let total = rows.length + offset;\n let hasMore = rows.length >= limit;\n if (driver.count) {\n total = await driver.count({ path: slug, filter });\n hasMore = offset + rows.length < total;\n }\n\n return {\n data: rows.map((row: Record<string, unknown>) => rowToEntity<M>(row, slug, getPks())),\n meta: { total, limit, offset, hasMore }\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 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 (data: Partial<EntityValues<M>>[], options?: { upsert?: boolean }): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: slug,\n rows: data,\n upsert: options?.upsert\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 count: driver.count\n ? async (params?: FindParams<M>): Promise<number> => {\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n return driver.count!({\n path: slug,\n filter\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 = params?.limit ?? 20;\n const offset = params?.offset ?? 0;\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: params?.limit,\n offset: params?.offset,\n filter: params?.where,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks())),\n meta: {\n total: 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 WhereValue<M[keyof M & string]>);\n },\n orderBy(column: keyof M & string, 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) {\n return new QueryBuilder<M>(accessor).search(searchString);\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>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): 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 this.params.logical = columnOrCondition as LogicalCondition;\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 orderBy(column: keyof M & string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.params.orderBy = [column, direction];\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): this { this.params.searchString = searchString; return this; }\n include(...relations: string[]): this { this.params.include = relations; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n async count(): Promise<number> {\n return this.client.count ? this.client.count(this.params as FindParams<M>) : 0;\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.client.listen) {\n throw new Error(\"Listen is only available when the driver supports realtime.\");\n }\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 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 async update(id: string | number, data: Partial<M>): Promise<M> {\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n delete(id: string | number): Promise<void> {\n return snap.delete(id);\n },\n count: snap.count ? (params?: FindParams<M>) => snap.count!(params) : undefined,\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 : undefined,\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 : undefined,\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 WhereValue<M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new SdkQueryBuilder<M>(client).orderBy(column, direction),\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 include: (...relations: string[]) => new SdkQueryBuilder<M>(client).include(...relations)\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 admin 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 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 count: sdk.count ? (params?: FindParams<M>) => sdk.count!(params) : undefined,\n listen: sdk.listen\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 : undefined,\n listenById: sdk.listenById\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 : undefined,\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 WhereValue<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 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 */\nexport function wrapAsEntityData(sdkData: RebaseSdkData, 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.data`). 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","import type { OrderByTuple } 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.\n * Everything else speaks {@link OrderByTuple} exclusively.\n *\n * Mirrors the filter architecture in `filter-dialect.ts`.\n *\n * @module\n */\n\n/**\n * Serialize an {@link OrderByTuple} to the wire format `\"field:direction\"`.\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 `[field, direction]` tuple, 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** on the wire — this is an inherent limitation of the colon-delimited\n * encoding and is not resolved here.\n */\nexport function serializeOrderBy(orderBy?: OrderByTuple | 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 return `${orderBy[0]}:${orderBy[1]}`;\n}\n\n/**\n * Deserialize a wire-format `\"field:direction\"` string into an {@link OrderByTuple}.\n *\n * Lenient parsing (matches existing server behaviour):\n * - Bare field name (no colon): `\"name\"` → `[\"name\", \"asc\"]`\n * - Unknown direction: `\"name:foo\"` → `[\"name\", \"asc\"]`\n * - Empty / falsy input: → `undefined`\n *\n * @param raw - The wire-format string from an HTTP query parameter.\n * @returns The canonical tuple, or `undefined` if the input is empty/falsy.\n */\nexport function deserializeOrderBy(raw?: string): OrderByTuple | undefined {\n if (!raw) return undefined;\n const idx = raw.indexOf(\":\");\n if (idx === -1) return [raw, \"asc\"];\n const field = raw.slice(0, idx);\n const dir = raw.slice(idx + 1);\n return [field, dir === \"desc\" ? \"desc\" : \"asc\"];\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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"}