@rebasepro/common 0.28.2 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/buildRebaseData.d.ts +15 -7
- package/dist/data/buildRebaseData.es.js +86 -33
- package/dist/data/buildRebaseData.es.js.map +1 -1
- package/dist/data/buildRoutedRebaseData.d.ts +4 -4
- package/dist/data/buildRoutedRebaseData.es.js.map +1 -1
- package/dist/data/filter-dialect.d.ts +7 -1
- package/dist/data/filter-dialect.es.js +108 -7
- package/dist/data/filter-dialect.es.js.map +1 -1
- package/dist/index.es.js +2 -2
- package/dist/util/permissions.es.js +9 -0
- package/dist/util/permissions.es.js.map +1 -1
- package/dist/util/relations.d.ts +3 -1
- package/dist/util/relations.es.js.map +1 -1
- package/dist/util/resolutions.es.js +5 -2
- package/dist/util/resolutions.es.js.map +1 -1
- package/dist/util/storage.es.js +3 -3
- package/dist/util/storage.es.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildRebaseData.es.js","names":[],"sources":["../../src/data/buildRebaseData.ts"],"sourcesContent":["import { CollectionAccessor, DataDriver, DEFAULT_VECTOR_LIST_LIMIT, Entity, EntityValues, FindAllParams, FindParams, FindResponse, FindResult, IterateParams, LogicalCondition, OrderByTuple, PageWalkOptions, RebaseApiError, RebaseData, RebaseSdkData, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, sortKeyToString, type AggregateMeta, type AggregateParams, type AggregateResult, type AggregateSelect, type ComputedSortField, type FieldPath, type ByIdReadOptions, type IncludeSpec, type NonColumnFieldPath, type NullsPlacement, type SearchMatch, type UpdateValues, type UpsertOptions, type WriteOptions, type RestoreOptions, type TrashClient, RebaseClientError, WhereFilterOp, WhereValueFor, isUnsupported, unsupportedMethod } from \"@rebasepro/types\";\nimport { toSnakeCase, toWireKey } from \"@rebasepro/utils\";\nimport { cursorToStartAfter, decodeCursor, reconcileCursorOrder } from \"./cursor\";\nimport { mergeIncludeSpecs } from \"./include-spec\";\nimport { etagOf } from \"./row-etag\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind, resolveFindWindow } from \"./paginate\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\nimport { resolveCollectionRelations } from \"../util/relations\";\nimport { EntityRelation } from \"@rebasepro/types\";\n\n/**\n * What a client says when its data source cannot subscribe.\n *\n * Named rather than inlined so the sentence a caller sees does not depend on\n * which of the two adapters below happened to build the client.\n */\nconst noRealtime = (slug: string): string =>\n `Realtime is not available for \"${slug}\": its data source does not support subscriptions.`;\n\n/** What a client says when its data source cannot count. */\nconst noCount = (slug: string): string =>\n `Counting is not available for \"${slug}\": its data source does not support it.`;\n\n/**\n * Derive the response key an aggregate comes back under.\n *\n * `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because\n * the REST parser derives the same alias from `?select=sum(total)` and the two\n * have to agree — a caller reading `row.sum_total` off an SDK result and off an\n * HTTP response is reading the same key or the SDK is broken.\n */\nexport function aggregateAlias(fn: string, field?: string): string {\n return field ? `${fn}_${field}` : fn;\n}\n\nfunction toDriverAggregate(\n select: AggregateSelect<Record<string, unknown>>\n): { fn: \"count\" | \"sum\" | \"avg\" | \"min\" | \"max\"; field?: string; alias: string } {\n const field = select.field as string | undefined;\n return { fn: select.fn, field, alias: aggregateAlias(select.fn, field) };\n}\n\n/**\n * What a client says when its data source cannot aggregate.\n *\n * A stub rather than a fallback that fetches and reduces in JavaScript: that\n * would be wrong under a `limit` and unaffordable without one, and it would look\n * like it had worked.\n */\n/** What a client says when its data source cannot read a collection's trash. */\nconst noTrash = (slug: string): string =>\n `The trash is not available for \"${slug}\": its data source cannot read soft-deleted rows.`;\n\n/** What `softDelete: true` names the field a delete stamps; a restore clears it. */\nconst DEFAULT_SOFT_DELETE_FIELD = \"deletedAt\";\n\nconst noAggregate = (slug: string): string =>\n `Aggregates are not available for \"${slug}\": its data source does not implement them.`;\n\nexport interface EntityDataOptions {\n /**\n * Look up a collection's config by slug, to derive row addresses from its\n * primary keys.\n *\n * Called lazily rather than up front: the data layer is created by `Rebase`,\n * which sits *above* the admin that owns the collections, so a resolver\n * registered on mount would otherwise arrive too late to be seen.\n */\n resolveCollection?: (slug: string) => { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined;\n\n /**\n * Translate the path a collection is addressed by into the path its driver\n * stores it under.\n *\n * The admin addresses every collection by its slug, and a Firestore or\n * MongoDB collection may declare a `path` of its own: `fs_diagnosis` stored\n * at `diagnosis`, and `fs_diagnosis/abc/locales` at `diagnosis/abc/locales`.\n * Only the driver is handed the stored path. Rows keep the address they\n * were asked for by, because that is what the admin resolves their\n * collection, routes and deletes by.\n *\n * Late-bound, like `resolveCollection`. Absent, or answering `undefined`,\n * the address is the stored path.\n */\n resolveDataPath?: (path: string) => string | undefined;\n\n /**\n * The slug a property of the proxy names (`data.myNotes`). Absent, a\n * property is its snake_case (`my_notes`). The server passes\n * `createAccessorSlugResolver` over its collection registry, so a\n * collection is reachable under the accessor its generated type has.\n */\n resolveAccessor?: (property: string) => string;\n}\n\nfunction createPrimaryKeyResolver(options?: EntityDataOptions) {\n const cache = new Map<string, PrimaryKeyInfo[]>();\n const warned = new Set<string>();\n\n return function primaryKeysFor(slug: string): PrimaryKeyInfo[] {\n const cached = cache.get(slug);\n if (cached) return cached;\n\n const collection = options?.resolveCollection?.(slug);\n if (!collection) {\n // The registry may not have been registered yet. Don't memoize a\n // miss, or the collection would stay address-less for this session.\n return [];\n }\n\n const keys = resolvePrimaryKeys(collection);\n if (keys.length > 0) {\n // Memoized for the session: a collection's key does not change\n // while the app runs, and this is called once per row. Editing\n // `isId` in the schema editor needs a reload to take effect here.\n cache.set(slug, keys);\n return keys;\n }\n\n if (!warned.has(slug)) {\n warned.add(slug);\n // Silence here surfaces much later as rows that cannot be opened,\n // linked, or saved, with nothing pointing back at the cause.\n console.warn(\n `[rebase] Collection '${slug}' declares no primary key, so its rows have no address: ` +\n `detail links, caching and relations will not work for it. ` +\n `Mark the key property with \\`isId\\` in its collection config — the server logs which ` +\n `column to mark at boot, if its schema knows the key.`\n );\n }\n return keys;\n };\n}\n\n/**\n * Build the admin's view model out of the row the wire serves.\n *\n * The wire has ONE shape, for every consumer: flat columns, typed the way the\n * database typed them, and a relation rendered as the target's own columns (or\n * only its foreign key, when nothing asked for it). That is the REST contract,\n * what `find()` returns, what `listen()` pushes, and what the generated types\n * describe.\n *\n * The admin renders neither of those directly. Its date field requires a real\n * `Date` and rejects a string outright; its relation cells read `.data.values`\n * off a relation ref. Those requirements are the *admin's*, so they are met\n * here — in the browser, from the collection config the panel already has —\n * rather than by asking the server for a second wire shape.\n *\n * That second shape is what this replaces. Until 2026-09-09 the realtime wire\n * carried the view model and every other read carried flat rows, so `find()`\n * and `listen()` answered one query two ways; unifying the wire without doing\n * this conversion is what left every date cell reading \"Invalid date value\"\n * and every relation cell \"Unexpected value\".\n *\n * Values already in view-model form pass through untouched: a driver that\n * still sends `{ __type: \"date\" }` or a relation ref (the client revives both)\n * is served by the same walk.\n */\nfunction toViewModelValues(\n values: Record<string, unknown>,\n properties: Record<string, unknown> | undefined,\n collection: { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined,\n resolveCollection?: EntityDataOptions[\"resolveCollection\"]\n): Record<string, unknown> {\n if (!properties) return values;\n\n const relations = collection\n ? resolveCollectionRelations(collection as never)\n : {};\n let out: Record<string, unknown> | undefined;\n const write = (key: string, value: unknown) => {\n out = out ?? { ...values };\n out[key] = value;\n };\n\n for (const [key, rawProperty] of Object.entries(properties)) {\n const property = rawProperty as { type?: string; of?: { type?: string }; properties?: Record<string, unknown> } | undefined;\n if (!property) continue;\n\n // A relation nobody included is still a relation: the row carries only\n // its foreign key, and an addressable ref with no data attached is what\n // lets the preview fetch the one record it needs. Without this the\n // record form showed an empty chip where the customer goes, on a read\n // that did not name the relation in its `include`.\n if (!(key in values)) {\n const fkRelation = relations[key];\n // `localKey` is the column; the row is keyed the way the wire keys\n // it, which is that column camelCased (`customer_id` → `customerId`).\n const column = fkRelation && \"localKey\" in fkRelation ? fkRelation.localKey : undefined;\n const fk = column !== undefined\n ? values[column] ?? values[toWireKey(column)]\n : undefined;\n const fkTarget = fkRelation?.targetSlug;\n if (fkTarget && (typeof fk === \"string\" || typeof fk === \"number\")) {\n write(key, new EntityRelation(fk, fkTarget));\n }\n continue;\n }\n\n const value = values[key];\n if (value === null || value === undefined) continue;\n\n // A relation, under the property key or the relation name.\n const relation = relations[key];\n if (relation && (property.type === \"relation\" || property.of?.type === \"relation\" || property.type === \"array\")) {\n const target = relation.targetSlug;\n if (!target) continue;\n const targetProperties = resolveCollection?.(target)?.properties;\n const targetCollection = resolveCollection?.(target);\n const toRef = (item: unknown): unknown => {\n if (item instanceof EntityRelation) return item;\n if (typeof item === \"object\" && item !== null && \"__type\" in item) return item;\n // The target's own columns: the id it is addressed by, and the\n // values a relation cell renders without a second fetch.\n if (typeof item === \"object\" && item !== null) {\n const row = item as Record<string, unknown>;\n const keys = targetCollection ? resolvePrimaryKeys(targetCollection as never) : [];\n const id = keys.length > 0 ? buildCompositeId(row, keys) : row.id as string | number;\n if (id === undefined || id === null || id === \"\") return item;\n return new EntityRelation(id, target, {\n id,\n path: target,\n values: toViewModelValues(row, targetProperties, targetCollection, resolveCollection)\n });\n }\n // Only the foreign key came back — nothing asked for the\n // relation. Addressable, with nothing to render but its id.\n if (typeof item === \"string\" || typeof item === \"number\") {\n return new EntityRelation(item, target);\n }\n return item;\n };\n write(key, Array.isArray(value) ? value.map(toRef) : toRef(value));\n continue;\n }\n\n if (property.type === \"date\" && !(value instanceof Date)) {\n if (typeof value === \"string\" || typeof value === \"number\") {\n const date = new Date(value);\n write(key, isNaN(date.getTime()) ? null : date);\n }\n continue;\n }\n\n // A map's children are declared too, and a date two levels down is\n // still a date.\n if (property.type === \"map\" && property.properties && typeof value === \"object\" && !Array.isArray(value)) {\n write(key, toViewModelValues(value as Record<string, unknown>, property.properties, undefined, resolveCollection));\n }\n }\n\n return out ?? values;\n}\n\n/**\n * Give a flat row the Entity view-model the admin renders.\n *\n * The address is *derived here* — it is not a column, and the row it came from\n * does not contain one. Rows carry exactly what the table has, with the types\n * Postgres returned; the id is this layer's invention, and this is the only\n * place it is minted.\n *\n * `primaryKeys` empty falls back to a literal `id` on the row: drivers other\n * than postgres still serve rows with one, and this keeps them working.\n */\nfunction rowToEntity<M extends Record<string, unknown>>(\n row: Record<string, unknown>,\n slug: string,\n primaryKeys: PrimaryKeyInfo[] = [],\n /**\n * Turns the wire's row into the view model — see\n * {@link toViewModelValues}. Absent when the collections cannot be\n * resolved, which is every consumer that is not the admin: the flat SDK\n * derives itself from this layer and must keep the wire's own types.\n */\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): Entity<M> {\n // Query-computed metadata rides in on the row because that is how the wire\n // carries it, but it is not a column: it belongs beside `values`, not in\n // them. Left inside, `_matches` would show up in the record inspector as a\n // field the collection never declared.\n const { _matches, ...values } = row as Record<string, unknown> & { _matches?: SearchMatch[] };\n // The version the read reported rides on the row out of sight (see\n // `withETag`); the entity carries it in the open, beside `values`.\n const etag = etagOf(row);\n\n return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: (toViewModel ? toViewModel(values) : values) as EntityValues<M>,\n ...(_matches ? { searchMatches: _matches } : {}),\n ...(etag ? { etag } : {})\n };\n}\n\n/**\n * The relation envelope `toFlatRow` writes where a relation was:\n * `{ id, path, __type: \"relation\", data: { id, path, values } }`. It is the\n * admin's view-model, and the only pipeline that produces one is postgres'.\n */\nfunction isRelationEnvelope(\n value: unknown\n): value is { __type: \"relation\"; data?: { values?: Record<string, unknown> } } {\n return typeof value === \"object\"\n && value !== null\n && !Array.isArray(value)\n && (value as { __type?: unknown }).__type === \"relation\";\n}\n\n/** The target's own columns, as `toRestRow` would have inlined them. */\nfunction inlineEnvelope(envelope: { data?: { values?: Record<string, unknown> } }): Record<string, unknown> {\n return envelope.data?.values ?? {};\n}\n\n/**\n * Replace every relation envelope on a row with the target's flat columns.\n *\n * The SDK serves one relation shape — the inlined one (see\n * {@link RestFetchService}) — and Postgres now serves it on every read, so\n * against that driver this walk finds nothing to do. It stays for the drivers\n * whose own `fetchCollection` still answers with refs: a developer reading\n * through this accessor gets one shape whichever driver is underneath.\n *\n * Only applied where the REST pipeline is the contract (see `find`); a driver\n * without a `restFetchService` keeps whatever it returns.\n *\n * Note this is NOT how the admin gets its view model — that is built in the\n * browser by {@link toViewModelValues}, from the same flat row.\n */\nfunction inlineRelationRefs(row: Record<string, unknown>): Record<string, unknown> {\n let out: Record<string, unknown> | undefined;\n for (const [key, value] of Object.entries(row)) {\n if (isRelationEnvelope(value)) {\n out = out ?? { ...row };\n out[key] = inlineEnvelope(value);\n } else if (Array.isArray(value) && value.some(isRelationEnvelope)) {\n out = out ?? { ...row };\n out[key] = value.map((item) => isRelationEnvelope(item) ? inlineEnvelope(item) : item);\n }\n }\n return out ?? row;\n}\n\nfunction createDriverAccessor<M extends Record<string, unknown> = Record<string, unknown>>(\n driver: DataDriver,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>,\n /**\n * Where the driver stores `slug` — see `EntityDataOptions.resolveDataPath`.\n * Asked on every call, not once: the collections it is derived from\n * register after this accessor is built, and can change while it lives.\n */\n storedPath: () => string = () => slug\n): CollectionAccessor<M> {\n /**\n * One page of the collection, and its count. `withDeleted: \"only\"` reads\n * the trash (see `trash` below) through the same pipeline, so a trashed\n * page is paged, counted and shaped exactly as a live one.\n */\n async function findRows(params?: FindParams<M>, withDeleted?: \"only\"): 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 window = resolveFindWindow(params);\n const { offset, driverOffset } = window;\n // A vector search nobody bounded reads the nearest\n // DEFAULT_VECTOR_LIST_LIMIT rows, as it does over HTTP — not 50\n // rows ordered by distance. The `page` stride stays the plain one,\n // as the HTTP parser's does.\n const limit = params?.limit === undefined && params?.vectorSearch\n ? DEFAULT_VECTOR_LIST_LIMIT\n : window.limit;\n\n // Keyset paging, through the same codec and the same driver\n // comparison the HTTP route uses. The in-process accessor is a\n // transport like any other: a walk that seeked differently here\n // than over the wire would be a difference the types cannot see.\n const cursor = params?.after ? decodeCursor(params.after) : undefined;\n const sort = cursor\n ? reconcileCursorOrder(cursor, normalizeOrderBy(params?.orderBy))\n : normalizeOrderBy(params?.orderBy);\n // A cursor issued for an unsorted read carries an empty sort; the\n // HTTP route hands the driver none at all, and so does this.\n const orderBy = sort && sort.length > 0 ? sort : undefined;\n const startAfter = cursor ? cursorToStartAfter(cursor) : undefined;\n\n // One relation shape, whatever the call looks like.\n //\n // This used to fork on `include`: asking for one ran the REST\n // pipeline, which inlines a relation as the target's own columns;\n // not asking ran the driver's own fetch, which eagerly loaded\n // *every* relation and put a `{ __type: \"relation\" }` envelope\n // where the foreign key was. The same method answered in two\n // shapes, the generated types described only one, and a column\n // typed `string` arrived as an object.\n //\n // The REST pipeline is the published contract — the shape the HTTP\n // API serves for this same query, and what `RestFetchService`\n // documents — so every read goes through it when the driver has\n // one. Drivers without one (every browser driver, and so the\n // admin's own path through `buildRebaseData`) are untouched.\n //\n // One row past the page, when seeking — and when not counting.\n //\n // `hasMore` on an offset page is `offset + rows.length < total`, and\n // under a cursor that arithmetic is simply false: every seeked page\n // runs at offset 0, so it compares one page against the whole\n // collection and says \"more\" forever. Without a count there is\n // nothing to compare against at all. Asking for `limit + 1` and\n // looking at whether the extra row arrived is the answer both have —\n // and it costs nothing, where the count it replaces is a second\n // query over every matching row.\n //\n // Counted only where the HTTP listing counts (`RestApiGenerator.readPage`).\n // A distinct read collapses rows the count would count one by one,\n // so its total described a different set from the one served, and\n // `hasMore` — derived from it — offered pages that did not exist.\n // A vector search with a threshold is a distance computed for every\n // row of the table, beside a page an index serves in a millisecond.\n const seeking = startAfter !== undefined;\n const counting = driver.count !== undefined\n && params?.count !== \"none\"\n && params?.distinct !== true\n && params?.vectorSearch?.threshold === undefined;\n const probing = seeking || !counting;\n const probeLimit = probing ? limit + 1 : limit;\n\n // The page and its count in one transaction where the driver can\n // run them so — see `DataDriver.readTogether`.\n const read = async (reader: DataDriver) => {\n const fetchService = reader.restFetchService;\n const fetched = fetchService\n ? await fetchService.fetchCollectionForRest(\n storedPath(),\n {\n filter,\n // Without this the group was dropped and the read ran\n // unfiltered — every row the caller's policies allow,\n // in place of the ones they asked for.\n logical: params?.logical,\n limit: probeLimit,\n // A cursor and an offset describe the same window two\n // incompatible ways; seeking wins and the offset is not\n // sent, or the page would start `offset` rows past\n // where the cursor pointed.\n offset: seeking ? undefined : driverOffset,\n startAfter,\n orderBy,\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Left out, `.vectorSearch(…).find()` in a function\n // or a cron read the newest rows in id order, with\n // no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n fields: params?.fields,\n distinct: params?.distinct,\n ...(withDeleted ? { withDeleted } : {})\n },\n params?.include\n )\n : await reader.fetchCollection<M>({\n path: storedPath(),\n limit: probeLimit,\n offset: seeking ? undefined : driverOffset,\n startAfter,\n filter,\n logical: params?.logical,\n orderBy,\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n vectorSearch: params?.vectorSearch,\n include: params?.include,\n fields: params?.fields,\n distinct: params?.distinct,\n ...(withDeleted ? { withDeleted } : {})\n });\n // The same narrowing the rows were read with. Counting only by\n // `filter` reported the whole collection beside a narrowed\n // page, and `hasMore` was derived from it — so the list offered\n // a next page that did not exist.\n const counted = counting && reader.count\n ? await reader.count({\n path: storedPath(),\n filter,\n logical: params?.logical,\n searchString: params?.searchString,\n vectorSearch: params?.vectorSearch,\n ...(withDeleted ? { withDeleted } : {})\n })\n : undefined;\n return { fetched, counted };\n };\n const { fetched, counted } = driver.readTogether\n ? await driver.readTogether(read)\n : await read(driver);\n\n // The probe row is evidence, not data — it is never served.\n const rows = probing ? fetched.slice(0, limit) : fetched;\n\n // Without a count, the rows up to the end of this page: all a\n // driver that cannot count has ever been able to report.\n const total = counted ?? rows.length + offset;\n const hasMore = probing ? fetched.length > limit : offset + rows.length < total;\n\n // The cursor for the *next* page, from the last row served. Issued\n // by the driver, which is the only layer that knows which columns\n // address a row; absent where it cannot describe one, and the\n // caller then pages by offset.\n const last = rows[rows.length - 1] as Record<string, unknown> | undefined;\n const nextCursor = (hasMore && last && driver.restFetchService?.cursorFor)\n ? driver.restFetchService.cursorFor(storedPath(), last, orderBy)\n : undefined;\n\n return {\n data: rows.map((row: Record<string, unknown>) => rowToEntity<M>(row, slug, getPks(), toViewModel)),\n meta: { total, limit, offset, hasMore, ...(nextCursor && { nextCursor }) }\n };\n }\n\n async function countRows(params?: FindParams<M>, withDeleted?: \"only\"): Promise<number> {\n if (!driver.count) throw new Error(noCount(slug));\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n // Every narrowing `find()` applies has to apply here too, or\n // the count describes a different query than the one it is\n // reported against.\n return driver.count({\n path: storedPath(),\n filter,\n logical: params?.logical,\n searchString: params?.searchString,\n // A threshold drops rows, so it narrows the count as it\n // narrows the read — as `GET /count` does.\n vectorSearch: params?.vectorSearch,\n ...(withDeleted ? { withDeleted } : {})\n });\n }\n\n const accessor: CollectionAccessor<M> = {\n find(params?: FindParams<M>): Promise<FindResponse<M>> {\n return findRows(params);\n },\n\n async findById(id: string | number, options?: ByIdReadOptions): 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(storedPath(), id, options?.include)\n : await driver.fetchOne<M>({ path: storedPath(), id: id });\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n\n // Present only when the driver's fetch service implements it — the SDK\n // wrapper turns an absent one into a stub that names the capability.\n aggregate: driver.restFetchService?.aggregate\n ? async (params: AggregateParams<M>): Promise<AggregateResult> => {\n // Paged the way the REST route pages it: one group past the\n // `limit`, so `meta.hasMore` is known rather than guessed from a\n // page that happened to come back full. No default limit here —\n // an in-process caller that names none gets every group, as it\n // always did — so `meta` is only for a call that named one.\n const grouped = (params.groupBy?.length ?? 0) > 0;\n const limit = grouped ? params.limit : undefined;\n const rows: AggregateResult = await driver.restFetchService!.aggregate!(storedPath(), {\n aggregates: params.select.map(toDriverAggregate),\n groupBy: params.groupBy as string[] | undefined,\n filter: params.where\n ? deserializeFilter(params.where as Record<string, unknown>)\n : undefined,\n logical: params.logical,\n searchString: params.searchString,\n limit: limit !== undefined ? limit + 1 : params.limit,\n offset: params.offset,\n // The sort the HTTP route forwards. Dropped here, an\n // in-process `aggregate({ orderBy: [\"sum_total\", \"desc\"] })`\n // came back in group-key order instead.\n orderBy: normalizeOrderBy(params.orderBy)\n });\n if (limit === undefined) return rows;\n const page: AggregateResult = rows.slice(0, limit);\n const meta: AggregateMeta = { limit, offset: params.offset ?? 0, hasMore: rows.length > limit };\n Object.defineProperty(page, \"meta\", { value: meta, enumerable: false });\n return page;\n }\n : undefined,\n\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: storedPath(),\n values: data,\n id: id,\n status: \"new\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n createMany: driver.saveMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: storedPath(),\n rows: data,\n upsert: options?.upsert,\n // Dropped here, an `upsert` on a natural key silently\n // became an upsert on the primary key — which for a serial\n // id is a plain insert, so the re-runnable import the\n // option exists for duplicated every row instead.\n onConflict: options?.onConflict\n });\n return rows.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: storedPath(),\n values: data,\n id: id,\n status: \"existing\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n async delete(id: string | number): Promise<void> {\n // The address only. A server driver reads the row it deletes; the\n // `values: {}` this used to send was recorded by history as the\n // deleted row, and was all a `beforeDelete` had to judge.\n return driver.delete({ row: { id, path: storedPath() } });\n },\n\n // Present only when the driver is: exposing these unconditionally and\n // looping single writes underneath would give a caller neither the\n // atomicity nor the single round trip they reached for a batch to get,\n // while looking exactly like it had.\n updateMany: driver.updateMany\n ? async (updates: { id: string | number; data: Partial<EntityValues<M>> }[]): Promise<Entity<M>[]> => {\n const rows = await driver.updateMany!<M>({\n path: storedPath(),\n updates: updates.map(u => ({ id: u.id,\nvalues: u.data })),\n });\n return rows.map(row => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n\n deleteMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: storedPath(),\nids });\n }\n : undefined,\n\n count: driver.count ? (params?: FindParams<M>) => countRows(params) : undefined,\n\n // `?deleted=only` and `?hard=true`, in-process. A restore is an\n // ordinary `update` (the soft-delete field back to `null`), which the\n // driver already lets reach a trashed row, so it is not here.\n trash: {\n find: (params?: FindParams<M>) => findRows(params, \"only\"),\n count: (params?: FindParams<M>) => countRows(params, \"only\"),\n purge: (id: string | number) => driver.delete({ row: { id, path: storedPath() }, hard: true }),\n purgeMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: storedPath(), ids, hard: true });\n }\n : undefined\n },\n\n listen: driver.listenCollection\n ? (params: FindParams<M> | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void) => {\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n // Belt and braces. Postgres serves one shape on every read now,\n // realtime included, so this flattens nothing there — but a\n // driver whose `listen` still answers with refs is normalized\n // to the shape the rest of this accessor serves rather than\n // handing a developer two.\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenCollection!<M>({\n path: storedPath(),\n limit,\n offset: driverOffset,\n filter: params?.where,\n logical: params?.logical,\n orderBy: normalizeOrderBy(params?.orderBy),\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Forwarded so the SERVER can refuse it. `realtimeService`\n // rejects a subscription carrying `vectorSearch` — a\n // subscription is re-run on every matching write and\n // nothing there computes distances — and the docs promise\n // that refusal. Both producers hand-list their fields and\n // both omitted this one, so the guard could not fire and\n // `.vectorSearch(…).listen()` returned an ordinary\n // `id DESC` listing with no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks(), toViewModel)),\n meta: {\n // No count is issued on this path, so the total\n // is unknown; the lower bound is the rows in\n // hand plus the ones paged past to reach them.\n // Reporting `entities.length` claimed a read at\n // offset 100 had found a collection of two.\n total: offset + entities.length,\n limit,\n offset,\n hasMore: entities.length >= limit\n }\n });\n },\n onError\n });\n } : undefined,\n\n listenById: driver.listenOne\n ? (id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void, options?: ByIdReadOptions) => {\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenOne!<M>({\n path: storedPath(),\n id: id,\n include: options?.include,\n onUpdate: (entity) => onUpdate(entity ? rowToEntity<M>(normalize(entity), slug, getPks(), toViewModel) : undefined),\n onError\n });\n } : undefined,\n\n // Fluent Query Builder\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, ascending?: \"asc\" | \"desc\") {\n return new QueryBuilder<M>(accessor).orderBy(column, ascending);\n },\n limit(count: number) {\n return new QueryBuilder<M>(accessor).limit(count);\n },\n offset(count: number) {\n return new QueryBuilder<M>(accessor).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new QueryBuilder<M>(accessor).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new QueryBuilder<M>(accessor).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new QueryBuilder<M>(accessor).include(...relations);\n }\n };\n\n return accessor;\n}\n\n/**\n * Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.\n *\n * This is the key bridge: any property access like `data.products` returns\n * a `CollectionAccessor` backed by the underlying DataDriver, without\n * needing per-collection code generation.\n *\n * @example\n * const data = buildRebaseData(driver);\n * await data.products.create({ name: \"Camera\", price: 299 });\n * const { data: items } = await data.products.find({ where: { status: [\"==\", \"published\"] } });\n */\n/**\n * The view-model converter for one collection, or `undefined` when there is no\n * collection config to build it from.\n *\n * Absent is the honest answer for every consumer that is not the admin: the\n * flat SDK derives itself from this same layer (`buildSdkData`) and must keep\n * the wire's own types, and it registers no collection resolver.\n */\nfunction createViewModelConverter(options?: EntityDataOptions) {\n if (!options?.resolveCollection) return () => undefined;\n return function converterFor(slug: string) {\n return (values: Record<string, unknown>): Record<string, unknown> => {\n // Resolved per call rather than memoized: the resolver is\n // late-bound (see `createPrimaryKeyResolver`) and a collection\n // edited in the schema editor should not need a reload here.\n const collection = options.resolveCollection?.(slug);\n if (!collection) return values;\n return toViewModelValues(values, collection.properties, collection, options.resolveCollection);\n };\n };\n}\n\nexport function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = createDriverAccessor(\n driver,\n slug,\n () => primaryKeysFor(slug),\n viewModelFor(slug),\n () => options?.resolveDataPath?.(slug) ?? slug\n );\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 // A collection's own accessor name, else camelCase → snake_case.\n return getAccessor(options?.resolveAccessor?.(prop) ?? toSnakeCase(prop));\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 M;\n}\n\n/**\n * Fluent query builder for the flat SDK data layer. Mirrors {@link QueryBuilder}\n * but resolves to `FindResult<M>` (flat rows) instead of Entity-wrapped\n * `FindResponse<M>`.\n */\nclass SdkQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n private params: FindParams = { where: {} };\n\n constructor(private client: SDKCollectionClient<M>) {}\n\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n /** A relation path (`author.name`) or a JSON path (`metadata->>tier`). */\n where(column: NonColumnFieldPath, operator: WhereFilterOp, value: unknown): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK\n // builder in `@rebasepro/client`, which had the same defect.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n if (!this.params.where) this.params.where = {};\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n return this;\n }\n\n /** Called again, this adds a tie-breaker rather than replacing the sort. */\n orderBy(\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction: \"asc\" | \"desc\" = \"asc\",\n nulls?: NullsPlacement\n ): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n const key = sortKeyToString(column);\n this.params.orderBy = [...existing, (nulls\n ? [key, direction, nulls]\n : [key, direction]) as OrderByTuple];\n return this;\n }\n\n limit(count: number): this { this.params.limit = count; return this; }\n offset(count: number): this { this.params.offset = count; return this; }\n search(searchString: string, options?: { explain?: boolean }): this { this.params.searchString = searchString; if (options?.explain !== undefined) this.params.searchExplain = options.explain; return this; }\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n /**\n * Load relations. Merges rather than replaces, so `.include(\"author\")` then\n * `.include({ comments: { limit: 5 } })` asks for both — a builder call that\n * silently discarded an earlier one is the same defect `where` had.\n */\n include(...relations: (string | IncludeSpec)[]): this {\n this.params.include = mergeIncludeSpecs(this.params.include, relations);\n return this;\n }\n\n fields(...columns: (FieldPath<M> | string)[]): this {\n this.params.fields = [...(this.params.fields ?? []), ...columns as string[]];\n return this;\n }\n\n distinct(enabled = true): this { this.params.distinct = enabled; return this; }\n\n after(cursor: string): this { this.params.after = cursor; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n /** Aggregate the matching rows. See {@link SDKCollectionClient.aggregate}. */\n async aggregate(\n params: Omit<AggregateParams<M>, \"where\" | \"logical\" | \"searchString\">\n ): Promise<AggregateResult> {\n return this.client.aggregate({\n ...params,\n where: this.params.where as AggregateParams<M>[\"where\"],\n logical: this.params.logical as AggregateParams<M>[\"logical\"],\n searchString: this.params.searchString\n });\n }\n\n /**\n * Page through everything this query matches, one row at a time.\n *\n * `.limit()` on the builder becomes the page size, so the ceiling on a\n * single `find()` is not a ceiling on what the query can read.\n */\n iterate(options?: PageWalkOptions<M>): AsyncIterableIterator<M> {\n return this.client.iterate({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as IterateParams<M>);\n }\n\n /** Collect everything this query matches into one array. */\n findAll(options?: PageWalkOptions<M> & { maxRows?: number }): Promise<M[]> {\n return this.client.findAll({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as FindAllParams<M>);\n }\n\n /**\n * Count the records matching this query.\n *\n * This used to answer `0` when the client had no `count` — a number, from a\n * source that had not counted anything, indistinguishable from an empty\n * collection. It now does what the client does, which on a source that\n * cannot count is throw and say so.\n */\n async count(): Promise<number> {\n return this.client.count(this.params as FindParams<M>);\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n return this.client.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n\n/**\n * Refuse a write option this door cannot honour, by name, before anything is\n * written.\n *\n * The in-process accessor implements the same `SDKCollectionClient` as the HTTP\n * client, whose `ifMatch` is \"Honoured on `update` and `delete`\" — and its\n * writes took no options at all, so `rebase.data.posts.update(id, data, {\n * ifMatch: staleEtag })` overwrote the row and resolved. A precondition the\n * caller wrote, dropped without a word, is the worst thing a door can do with\n * it. The driver's `save` has no precondition and no idempotency store to hand\n * these to, so they are refused rather than ignored. An `undefined` value is no\n * option, as it is over HTTP: `etagOf(row)` is `undefined` for a row that\n * carries no version, and that means \"write unconditionally\".\n */\nfunction refuseUnhonouredWriteOptions(method: string, slug: string, options?: WriteOptions): void {\n const unsupported = ([\"ifMatch\", \"idempotencyKey\"] as const).filter((key) => options?.[key]);\n if (unsupported.length === 0) return;\n throw new RebaseClientError(\n `${method}() on \"${slug}\" in-process (context.data, rebase.data) cannot honour ` +\n `${unsupported.join(\" or \")}: this door has no ` +\n `${unsupported.includes(\"ifMatch\") ? \"precondition check\" : \"idempotency store\"}, and ignoring ` +\n \"the option would write as though it had been checked. Drop it here, or send the write through \" +\n \"the HTTP client, which honours it.\",\n { code: \"UNSUPPORTED_OPTION\", details: { method, options: unsupported } }\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, options?: ByIdReadOptions): Promise<M | undefined> {\n const s = await snap.findById(id, options);\n return s ? entityToRow(s) : undefined;\n },\n async get(id: string | number): Promise<M> {\n // The same contract server-side as in the browser SDK, deliberately:\n // a callback, a cron and an app all read a row by id, and the shape\n // of \"it is not there\" should not depend on which one is asking.\n const s = await snap.findById(id);\n if (!s) {\n throw new RebaseApiError(\n `No record with id ${JSON.stringify(String(id))} in \"${slug}\".`,\n { status: 404, code: \"NOT_FOUND\" }\n );\n }\n return entityToRow(s);\n },\n async create(data: Partial<M>, id?: string | number, options?: WriteOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"create\", slug, options);\n return entityToRow(await snap.create(data as Partial<EntityValues<M>>, id));\n },\n async createMany(\n data: Partial<M>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] } & WriteOptions\n ): Promise<M[]> {\n refuseUnhonouredWriteOptions(\"createMany\", slug, options);\n if (!Array.isArray(data)) {\n throw new RebaseClientError(\"createMany expects an array of records.\", { code: \"INVALID_BULK_BODY\" });\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>>[], {\n upsert: options?.upsert,\n onConflict: options?.onConflict\n });\n return rows.map(entityToRow);\n },\n /**\n * One row through the bulk path, because the bulk path is where the\n * conflict target lives.\n *\n * `CollectionAccessor` has no single-row upsert and adding one would\n * mean a second way to say the same thing to the same driver method —\n * `saveMany` already takes `upsert` and `onConflict`, and a batch of\n * one is exactly an upsert of one.\n */\n async upsert(data: Partial<M>, options?: UpsertOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"upsert\", slug, options);\n if (!snap.createMany) {\n throw new Error(\n \"Upsert is not supported by this collection's data source: it needs a bulk write, \" +\n \"which this driver does not implement. Fall back to create() or update().\"\n );\n }\n const rows = await snap.createMany(\n [data as Partial<EntityValues<M>>],\n { upsert: true, onConflict: options?.onConflict }\n );\n const row = rows[0];\n if (!row) throw new Error(`Upsert into \"${slug}\" returned no row.`);\n return entityToRow(row);\n },\n async update(id: string | number, data: Partial<M> | UpdateValues<Partial<M>>, options?: WriteOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"update\", slug, options);\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n async updateMany(\n updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[],\n options?: WriteOptions\n ): Promise<M[]> {\n refuseUnhonouredWriteOptions(\"updateMany\", slug, options);\n if (!Array.isArray(updates)) {\n throw new RebaseClientError(\"updateMany expects an array of { id, data } entries.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (updates.length === 0) return [];\n if (!snap.updateMany) {\n throw new Error(\n \"Bulk updates are not supported by this collection's data source. \" +\n \"Fall back to update() per record.\"\n );\n }\n const rows = await snap.updateMany(\n updates.map(u => ({ id: u.id,\ndata: u.data as Partial<EntityValues<M>> }))\n );\n return rows.map(entityToRow);\n },\n async delete(id: string | number, options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"delete\", slug, options);\n return snap.delete(id);\n },\n async deleteMany(ids: (string | number)[], options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"deleteMany\", slug, options);\n if (!Array.isArray(ids)) {\n throw new RebaseClientError(\"deleteMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (ids.length === 0) return;\n if (!snap.deleteMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to delete() per record.\"\n );\n }\n await snap.deleteMany(ids);\n },\n // The three are non-optional on `SDKCollectionClient`: where the\n // underlying accessor cannot serve one, a stub says so when called\n // rather than being absent. `isUnsupported()` is how an adapter asks\n // the capability question — see `toEntityAccessor` below, which has to.\n count: snap.count\n ? (params?: FindParams<M>) => snap.count!(params)\n : unsupportedMethod(noCount(slug)),\n listen: snap.listen\n ? (params: FindParams<M> | undefined, onUpdate: (r: FindResult<M>) => void, onError?: (e: Error) => void) =>\n snap.listen!(params, (res) => onUpdate({ data: res.data.map(entityToRow), meta: res.meta }), onError)\n : unsupportedMethod(noRealtime(slug)),\n listenById: snap.listenById\n ? (id: string | number, onUpdate: (r: M | undefined) => void, onError?: (e: Error) => void, options?: ByIdReadOptions) =>\n snap.listenById!(id, (s) => onUpdate(s ? entityToRow(s) : undefined), onError, options)\n : unsupportedMethod(noRealtime(slug)),\n listenCount: snap.listenCount\n ? (params: FindParams<M> | undefined, onCount: (total: number) => void, onError?: (e: Error) => void) =>\n snap.listenCount!(params, onCount, onError)\n : unsupportedMethod(noRealtime(slug)),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SdkQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction?: \"asc\" | \"desc\",\n nulls?: NullsPlacement\n ) => new SdkQueryBuilder<M>(client).orderBy(column, direction, nulls),\n limit: (count: number) => new SdkQueryBuilder<M>(client).limit(count),\n offset: (count: number) => new SdkQueryBuilder<M>(client).offset(count),\n search: (searchString: string) => new SdkQueryBuilder<M>(client).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new SdkQueryBuilder<M>(client).vectorSearch(property, vector, options),\n include: (...relations: (string | IncludeSpec)[]) => new SdkQueryBuilder<M>(client).include(...relations),\n fields: (...columns: (FieldPath<M> | string)[]) => new SdkQueryBuilder<M>(client).fields(...columns),\n distinct: (enabled?: boolean) => new SdkQueryBuilder<M>(client).distinct(enabled),\n after: (cursor: string) => new SdkQueryBuilder<M>(client).after(cursor),\n aggregate: snap.aggregate\n ? (params: AggregateParams<M>) => snap.aggregate!(params)\n : unsupportedMethod(noAggregate(slug)),\n trash: toSdkTrashClient(snap, slug, () => client)\n };\n return client;\n}\n\n/**\n * {@link SDKCollectionClient.trash} over an accessor's\n * {@link CollectionAccessor.trash}: the same API the browser client has. A\n * restore is the client's own `update`/`updateMany` setting the soft-delete\n * field to `null`, so it keeps their refusals and their answer.\n */\nfunction toSdkTrashClient<M extends Record<string, unknown>>(\n snap: CollectionAccessor<M>,\n slug: string,\n client: () => SDKCollectionClient<M>\n): TrashClient<M> {\n const trash = snap.trash;\n if (!trash) {\n return {\n find: unsupportedMethod(noTrash(slug)),\n count: unsupportedMethod(noTrash(slug)),\n restore: unsupportedMethod(noTrash(slug)),\n restoreMany: unsupportedMethod(noTrash(slug)),\n purge: unsupportedMethod(noTrash(slug)),\n purgeMany: unsupportedMethod(noTrash(slug))\n };\n }\n const field = (options?: RestoreOptions<M>): string => options?.field ?? DEFAULT_SOFT_DELETE_FIELD;\n return {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const res = await trash.find(params);\n return { data: res.data.map(entityToRow), meta: res.meta };\n },\n count: (params?: FindParams<M>) => trash.count(params),\n restore: (id: string | number, options?: RestoreOptions<M>) =>\n client().update(id, { [field(options)]: null } as Partial<M>, writeOptionsOf(options)),\n restoreMany(ids: (string | number)[], options?: RestoreOptions<M>) {\n if (!Array.isArray(ids)) {\n return Promise.reject(new RebaseClientError(\n \"trash.restoreMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" }\n ));\n }\n const data = { [field(options)]: null } as Partial<M>;\n return client().updateMany(ids.map((id) => ({ id, data })), writeOptionsOf(options));\n },\n async purge(id: string | number, options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"trash.purge\", slug, options);\n await trash.purge(id);\n },\n async purgeMany(ids: (string | number)[], options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"trash.purgeMany\", slug, options);\n if (!Array.isArray(ids)) {\n throw new RebaseClientError(\"trash.purgeMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (ids.length === 0) return;\n if (!trash.purgeMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to trash.purge() per record.\"\n );\n }\n await trash.purgeMany(ids);\n }\n };\n}\n\n/** A {@link RestoreOptions} without its `field`: the write options it carries. */\nfunction writeOptionsOf<M extends Record<string, unknown>>(options?: RestoreOptions<M>): WriteOptions | undefined {\n if (!options) return undefined;\n const { field: _field, ...rest } = options;\n return rest;\n}\n\n/**\n * Wrap a flat {@link SDKCollectionClient} into a Entity-shaped\n * {@link CollectionAccessor}. Every returned row is re-wrapped into the\n * `{ id, path, values }` view-model the admin panel renders.\n */\nfunction toEntityAccessor<M extends Record<string, unknown>>(\n sdk: SDKCollectionClient<M>,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>,\n /**\n * What `toViewModel` converts with — the collection as resolved now. A\n * live list keeps the entities it built for as long as this and the keys\n * stay the same object (see `listen`).\n */\n viewModelSource: () => unknown = () => undefined\n): CollectionAccessor<M> {\n const accessor: CollectionAccessor<M> = {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n const res = await sdk.find(params);\n return { data: res.data.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel)), meta: res.meta };\n },\n async findById(id: string | number, options?: ByIdReadOptions): Promise<Entity<M> | undefined> {\n const row = await sdk.findById(id, options);\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n return rowToEntity<M>(await sdk.create(data as Partial<M>, id), slug, getPks(), toViewModel);\n },\n // Declared on `CollectionAccessor` and, until now, never implemented on\n // this side of the boundary — so the admin's own import wrote one HTTP\n // request per row and could neither be atomic nor upsert. It forwards to\n // the same `/bulk` route the SDK client uses.\n createMany: sdk.createMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await sdk.createMany!(data as Partial<M>[], options);\n return rows.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel));\n }\n : undefined,\n async update(id: string | number, data: Partial<EntityValues<M>>, options?: { ifMatch?: string }): Promise<Entity<M>> {\n const row = options?.ifMatch\n ? await sdk.update(id, data as Partial<M>, { ifMatch: options.ifMatch })\n : await sdk.update(id, data as Partial<M>);\n if (!row) throw new Error(`Update returned no data for id ${id}`);\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n delete(id: string | number): Promise<void> {\n return sdk.delete(id);\n },\n // `CollectionAccessor` keeps these optional, and the optionality is\n // load-bearing: the admin panel picks between subscribing and a\n // one-shot `find()` on exactly this property, and a UI that subscribes\n // into a throw is worse than one that polls. The client's method is\n // always present now, so the capability is read off the stub instead.\n count: isUnsupported(sdk.count) ? undefined : (params?: FindParams<M>) => sdk.count(params),\n aggregate: isUnsupported(sdk.aggregate)\n ? undefined\n : (params: AggregateParams<M>) => sdk.aggregate(params),\n listen: isUnsupported(sdk.listen)\n ? undefined\n : (params: FindParams<M> | undefined, onUpdate: (r: FindResponse<M>) => void, onError?: (e: Error) => void) => {\n // One entity per row object, for as long as the subscription\n // lives. The socket hands back the very row it cached when a\n // refetch of it is equal (`mergeRows`), so a push that changed\n // one row of fifty arrives with forty-nine rows it has seen;\n // building fifty new entities threw that away, and every view\n // downstream compared fifty rows by value again. Keyed on the\n // row object, so only a row the socket kept is reused, and\n // dropped whenever the keys or the collection the values are\n // converted with change (a key learned late, a schema edit).\n let built = new WeakMap<object, Entity<M>>();\n let builtWith: [PrimaryKeyInfo[], unknown] | undefined;\n return sdk.listen(params, (res) => {\n const pks = getPks();\n const source = viewModelSource();\n if (!builtWith || builtWith[0] !== pks || builtWith[1] !== source) {\n built = new WeakMap();\n builtWith = [pks, source];\n }\n onUpdate({\n data: res.data.map((row) => {\n let entity = built.get(row);\n if (!entity) {\n entity = rowToEntity<M>(row, slug, pks, toViewModel);\n built.set(row, entity);\n }\n return entity;\n }),\n meta: res.meta\n });\n }, onError);\n },\n listenById: isUnsupported(sdk.listenById)\n ? undefined\n : (id: string | number, onUpdate: (s: Entity<M> | undefined) => void, onError?: (e: Error) => void, options?: ByIdReadOptions) =>\n sdk.listenById(id, (row) => onUpdate(row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined), onError, options),\n listenCount: isUnsupported(sdk.listenCount)\n ? undefined\n : (params: FindParams<M> | undefined, onCount: (total: number) => void, onError?: (e: Error) => void) =>\n sdk.listenCount(params, onCount, onError),\n trash: !sdk.trash || isUnsupported(sdk.trash.find)\n ? undefined\n : {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n const res = await sdk.trash.find(params);\n return { data: res.data.map((row) => rowToEntity<M>(row, slug, getPks(), toViewModel)), meta: res.meta };\n },\n count: (params?: FindParams<M>) => sdk.trash.count(params),\n purge: (id: string | number) => sdk.trash.purge(id),\n purgeMany: (ids: (string | number)[]) => sdk.trash.purgeMany(ids)\n },\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new QueryBuilder<M>(accessor).orderBy(column, direction),\n limit: (count: number) => new QueryBuilder<M>(accessor).limit(count),\n offset: (count: number) => new QueryBuilder<M>(accessor).offset(count),\n search: (searchString: string) => new QueryBuilder<M>(accessor).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new QueryBuilder<M>(accessor).vectorSearch(property, vector, options),\n include: (...relations: string[]) => new QueryBuilder<M>(accessor).include(...relations)\n };\n return accessor;\n}\n\n/**\n * Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.\n *\n * This is the **admin boundary**: the SDK client (`client.data`) returns flat\n * rows, but the admin renders the `Entity` view-model (`entity.values.*`).\n * `core/Rebase.tsx` wraps `client.data` through this before handing it to the\n * admin `RebaseDataContext` — without it the admin renders rows with only their\n * `id`.\n */\n/**\n * Only the by-slug accessor is asked for, so only that is required.\n *\n * Taking a whole `RebaseSdkData` meant taking `RebaseSdkData<unknown>`, whose\n * dynamic branch is an index signature — and no `RebaseSdkData<DB>` satisfies\n * it, because its own `collection` method is not a `SDKCollectionClient`. So a\n * caller holding a *typed* client could not pass it to a function that reads\n * one method off it, and that method is identical on every instantiation.\n */\nexport function wrapAsEntityData(sdkData: Pick<RebaseSdkData, \"collection\">, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug), viewModelFor(slug),\n () => options?.resolveCollection?.(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 *\n * `DB` is the generated `Database` the result is typed from. One object serves\n * every `Database` — each accessor is resolved by name when it is read — so it\n * describes the result and changes nothing it does. Only an explicit type\n * argument sets it (`NoInfer`): inferred from where the result is assigned, it\n * would be whatever that place declares — an untyped `Promise<RebaseSdkData>`\n * made it `RebaseSdkData | PromiseLike<RebaseSdkData>`. Untyped by default.\n */\nexport function wrapAsSdkData<DB = unknown>(\n entityData: RebaseData,\n options?: Pick<EntityDataOptions, \"resolveAccessor\">\n): RebaseSdkData<NoInfer<DB>> {\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<DB>;\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(options?.resolveAccessor?.(prop) ?? toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Build a flat {@link RebaseSdkData} from a `DataDriver`.\n *\n * This is the developer-facing SDK data layer used by backend framework\n * callbacks & scripts (`context.data` / `rebase.dataAsAdmin`). It returns flat rows —\n * identical in shape to the frontend SDK client, down to how a relation is\n * served: a foreign key stays a foreign key, and a relation named in `include`\n * arrives as the target's own columns. The `{ __type: \"relation\" }` envelope is\n * the admin's view-model and never reaches here.\n *\n * The admin uses {@link buildRebaseData} (Entity) over its own driver.\n *\n * `options.resolveAccessor` names the slug a property is (see\n * `createAccessorSlugResolver`); absent, `data.myNotes` is `my_notes`.\n *\n * `DB` types the result from a generated `Database` — `buildSdkData<Database>(driver)`\n * is the plane `context.data` is in a project that registered it. It describes\n * the object and changes nothing it does, and only an explicit type argument\n * sets it (see {@link wrapAsSdkData}); untyped by default.\n */\nexport function buildSdkData<DB = unknown>(\n driver: DataDriver,\n options?: Pick<EntityDataOptions, \"resolveAccessor\">\n): RebaseSdkData<NoInfer<DB>> {\n return wrapAsSdkData<DB>(buildRebaseData(driver), options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"buildRebaseData.es.js","names":[],"sources":["../../src/data/buildRebaseData.ts"],"sourcesContent":["import { toDriverAggregate, toDriverAggregateGroup } from \"@rebasepro/types\";\nimport { CollectionAccessor, DataDriver, DEFAULT_VECTOR_LIST_LIMIT, Entity, EntityValues, FindAllParams, FindParams, FindResponse, FindResult, IterateParams, LogicalCondition, OrderByTuple, PageWalkOptions, RebaseApiError, RebaseData, RebaseSdkData, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, sortKeyToString, type AggregateMeta, type AggregateParams, type AggregateResult, type AggregateGroup, type RefreshOptions, type ComputedSortField, type FieldPath, type ByIdReadOptions, type IncludeSpec, type NonColumnFieldPath, type NullsPlacement, type SearchMatch, type UpdateValues, type UpsertOptions, type WriteOptions, type RestoreOptions, type TrashClient, RebaseClientError, WhereFilterOp, WhereValueFor, isUnsupported, unsupportedMethod } from \"@rebasepro/types\";\nimport { toSnakeCase, toWireKey } from \"@rebasepro/utils\";\nimport { cursorToStartAfter, decodeCursor, reconcileCursorOrder } from \"./cursor\";\nimport { mergeIncludeSpecs } from \"./include-spec\";\nimport { etagOf } from \"./row-etag\";\nimport { QueryBuilder } from \"./query_builder\";\nimport { collectAllPages, paginateFind, resolveFindWindow } from \"./paginate\";\nimport { normalizeOrderBy } from \"./sort-dialect\";\nimport { deserializeFilter } from \"./filter-dialect\";\nimport { buildCompositeId, resolvePrimaryKeys, PrimaryKeyInfo } from \"../util/identity\";\nimport { fieldKeyForColumn, resolveCollectionRelations } from \"../util/relations\";\nimport { EntityReference, EntityRelation } from \"@rebasepro/types\";\n\n/**\n * What a client says when its data source cannot subscribe.\n *\n * Named rather than inlined so the sentence a caller sees does not depend on\n * which of the two adapters below happened to build the client.\n */\nconst noRealtime = (slug: string): string =>\n `Realtime is not available for \"${slug}\": its data source does not support subscriptions.`;\n\n/** What a client says when its data source cannot count. */\nconst noCount = (slug: string): string =>\n `Counting is not available for \"${slug}\": its data source does not support it.`;\n\n/**\n * The key an aggregate comes back under — `@rebasepro/types`' derivation,\n * which the REST parser and the HTTP client use too.\n */\nexport { aggregateAlias } from \"@rebasepro/types\";\n\n/**\n * What a client says when its data source cannot aggregate.\n *\n * A stub rather than a fallback that fetches and reduces in JavaScript: that\n * would be wrong under a `limit` and unaffordable without one, and it would look\n * like it had worked.\n */\n/** What a client says when its data source cannot read a collection's trash. */\nconst noTrash = (slug: string): string =>\n `The trash is not available for \"${slug}\": its data source cannot read soft-deleted rows.`;\n\n/** What `softDelete: true` names the field a delete stamps; a restore clears it. */\nconst DEFAULT_SOFT_DELETE_FIELD = \"deletedAt\";\n\nconst noAggregate = (slug: string): string =>\n `Aggregates are not available for \"${slug}\": its data source does not implement them.`;\n\n/** What a client says when its data source has no materialized views to refresh. */\nconst noRefresh = (slug: string): string =>\n `\"${slug}\" cannot be refreshed: its data source has no materialized views.`;\n\nexport interface EntityDataOptions {\n /**\n * Look up a collection's config by slug, to derive row addresses from its\n * primary keys.\n *\n * Called lazily rather than up front: the data layer is created by `Rebase`,\n * which sits *above* the admin that owns the collections, so a resolver\n * registered on mount would otherwise arrive too late to be seen.\n */\n resolveCollection?: (slug: string) => { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined;\n\n /**\n * Translate the path a collection is addressed by into the path its driver\n * stores it under.\n *\n * The admin addresses every collection by its slug, and a Firestore or\n * MongoDB collection may declare a `path` of its own: `fs_diagnosis` stored\n * at `diagnosis`, and `fs_diagnosis/abc/locales` at `diagnosis/abc/locales`.\n * Only the driver is handed the stored path. Rows keep the address they\n * were asked for by, because that is what the admin resolves their\n * collection, routes and deletes by.\n *\n * Late-bound, like `resolveCollection`. Absent, or answering `undefined`,\n * the address is the stored path.\n */\n resolveDataPath?: (path: string) => string | undefined;\n\n /**\n * The slug a property of the proxy names (`data.myNotes`). Absent, a\n * property is its snake_case (`my_notes`). The server passes\n * `createAccessorSlugResolver` over its collection registry, so a\n * collection is reachable under the accessor its generated type has.\n */\n resolveAccessor?: (property: string) => string;\n}\n\nfunction createPrimaryKeyResolver(options?: EntityDataOptions) {\n const cache = new Map<string, PrimaryKeyInfo[]>();\n const warned = new Set<string>();\n\n return function primaryKeysFor(slug: string): PrimaryKeyInfo[] {\n const cached = cache.get(slug);\n if (cached) return cached;\n\n const collection = options?.resolveCollection?.(slug);\n if (!collection) {\n // The registry may not have been registered yet. Don't memoize a\n // miss, or the collection would stay address-less for this session.\n return [];\n }\n\n const keys = resolvePrimaryKeys(collection);\n if (keys.length > 0) {\n // Memoized for the session: a collection's key does not change\n // while the app runs, and this is called once per row. Editing\n // `isId` in the schema editor needs a reload to take effect here.\n cache.set(slug, keys);\n return keys;\n }\n\n if (!warned.has(slug)) {\n warned.add(slug);\n // Silence here surfaces much later as rows that cannot be opened,\n // linked, or saved, with nothing pointing back at the cause.\n console.warn(\n `[rebase] Collection '${slug}' declares no primary key, so its rows have no address: ` +\n `detail links, caching and relations will not work for it. ` +\n `Mark the key property with \\`isId\\` in its collection config — the server logs which ` +\n `column to mark at boot, if its schema knows the key.`\n );\n }\n return keys;\n };\n}\n\n/**\n * Build the admin's view model out of the row the wire serves.\n *\n * The wire has ONE shape, for every consumer: flat columns, typed the way the\n * database typed them, and a relation rendered as the target's own columns (or\n * only its foreign key, when nothing asked for it). That is the REST contract,\n * what `find()` returns, what `listen()` pushes, and what the generated types\n * describe.\n *\n * The admin renders neither of those directly. Its date field requires a real\n * `Date` and rejects a string outright; its relation cells read `.data.values`\n * off a relation ref. Those requirements are the *admin's*, so they are met\n * here — in the browser, from the collection config the panel already has —\n * rather than by asking the server for a second wire shape.\n *\n * That second shape is what this replaces. Until 2026-09-09 the realtime wire\n * carried the view model and every other read carried flat rows, so `find()`\n * and `listen()` answered one query two ways; unifying the wire without doing\n * this conversion is what left every date cell reading \"Invalid date value\"\n * and every relation cell \"Unexpected value\".\n *\n * Values already in view-model form pass through untouched: a driver that\n * still sends `{ __type: \"date\" }` or a relation ref (the client revives both)\n * is served by the same walk.\n */\nfunction toViewModelValues(\n values: Record<string, unknown>,\n properties: Record<string, unknown> | undefined,\n collection: { properties?: Record<string, unknown>; relations?: unknown[]; slug?: string } | undefined,\n resolveCollection?: EntityDataOptions[\"resolveCollection\"]\n): Record<string, unknown> {\n if (!properties) return values;\n\n const relations = collection\n ? resolveCollectionRelations(collection as never)\n : {};\n let out: Record<string, unknown> | undefined;\n const write = (key: string, value: unknown) => {\n out = out ?? { ...values };\n out[key] = value;\n };\n\n for (const [key, rawProperty] of Object.entries(properties)) {\n const property = rawProperty as { type?: string; of?: { type?: string }; properties?: Record<string, unknown> } | undefined;\n if (!property) continue;\n\n // A relation nobody included is still a relation: the row carries only\n // its foreign key, and an addressable ref with no data attached is what\n // lets the preview fetch the one record it needs. Without this the\n // record form showed an empty chip where the customer goes, on a read\n // that did not name the relation in its `include`.\n if (!(key in values)) {\n const fkRelation = relations[key];\n // `localKey` is the column; the row is keyed the way the wire keys\n // it: under a property that names the column, else that column\n // camelCased (`customer_id` → `customerId`).\n const column = fkRelation && \"localKey\" in fkRelation ? fkRelation.localKey : undefined;\n const fk = column !== undefined\n ? values[fieldKeyForColumn(collection, column)] ?? values[column] ?? values[toWireKey(column)]\n : undefined;\n const fkTarget = fkRelation?.targetSlug;\n if (fkTarget && (typeof fk === \"string\" || typeof fk === \"number\")) {\n write(key, new EntityRelation(fk, fkTarget));\n }\n continue;\n }\n\n const value = values[key];\n if (value === null || value === undefined) continue;\n\n // A relation, under the property key or the relation name.\n const relation = relations[key];\n if (relation && (property.type === \"relation\" || property.of?.type === \"relation\" || property.type === \"array\")) {\n const target = relation.targetSlug;\n if (!target) continue;\n const targetProperties = resolveCollection?.(target)?.properties;\n const targetCollection = resolveCollection?.(target);\n const toRef = (item: unknown): unknown => {\n if (item instanceof EntityRelation) return item;\n if (typeof item === \"object\" && item !== null && \"__type\" in item) return item;\n // The target's own columns: the id it is addressed by, and the\n // values a relation cell renders without a second fetch.\n if (typeof item === \"object\" && item !== null) {\n const row = item as Record<string, unknown>;\n const keys = targetCollection ? resolvePrimaryKeys(targetCollection as never) : [];\n const id = keys.length > 0 ? buildCompositeId(row, keys) : row.id as string | number;\n if (id === undefined || id === null || id === \"\") return item;\n return new EntityRelation(id, target, {\n id,\n path: target,\n values: toViewModelValues(row, targetProperties, targetCollection, resolveCollection)\n });\n }\n // Only the foreign key came back — nothing asked for the\n // relation. Addressable, with nothing to render but its id.\n if (typeof item === \"string\" || typeof item === \"number\") {\n return new EntityRelation(item, target);\n }\n return item;\n };\n write(key, Array.isArray(value) ? value.map(toRef) : toRef(value));\n continue;\n }\n\n if (property.type === \"date\" && !(value instanceof Date)) {\n if (typeof value === \"string\" || typeof value === \"number\") {\n const date = new Date(value);\n write(key, isNaN(date.getTime()) ? null : date);\n }\n continue;\n }\n\n // A map's children are declared too, and a date two levels down is\n // still a date.\n if (property.type === \"map\" && property.properties && typeof value === \"object\" && !Array.isArray(value)) {\n write(key, toViewModelValues(value as Record<string, unknown>, property.properties, undefined, resolveCollection));\n }\n }\n\n return out ?? values;\n}\n\n/**\n * Give a flat row the Entity view-model the admin renders.\n *\n * The address is *derived here* — it is not a column, and the row it came from\n * does not contain one. Rows carry exactly what the table has, with the types\n * Postgres returned; the id is this layer's invention, and this is the only\n * place it is minted.\n *\n * `primaryKeys` empty falls back to a literal `id` on the row: drivers other\n * than postgres still serve rows with one, and this keeps them working.\n */\nfunction rowToEntity<M extends Record<string, unknown>>(\n row: Record<string, unknown>,\n slug: string,\n primaryKeys: PrimaryKeyInfo[] = [],\n /**\n * Turns the wire's row into the view model — see\n * {@link toViewModelValues}. Absent when the collections cannot be\n * resolved, which is every consumer that is not the admin: the flat SDK\n * derives itself from this layer and must keep the wire's own types.\n */\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): Entity<M> {\n // Query-computed metadata rides in on the row because that is how the wire\n // carries it, but it is not a column: it belongs beside `values`, not in\n // them. Left inside, `_matches` would show up in the record inspector as a\n // field the collection never declared.\n const { _matches, ...values } = row as Record<string, unknown> & { _matches?: SearchMatch[] };\n // The version the read reported rides on the row out of sight (see\n // `withETag`); the entity carries it in the open, beside `values`.\n const etag = etagOf(row);\n\n return {\n id: primaryKeys.length > 0\n ? buildCompositeId(row, primaryKeys)\n : row.id as string | number,\n path: slug,\n values: (toViewModel ? toViewModel(values) : values) as EntityValues<M>,\n ...(_matches ? { searchMatches: _matches } : {}),\n ...(etag ? { etag } : {})\n };\n}\n\n/** {@link rowToEntity} over a page of rows, the primary keys looked up once for all of them. */\nfunction rowsToEntities<M extends Record<string, unknown>>(\n rows: Record<string, unknown>[],\n slug: string,\n primaryKeys: PrimaryKeyInfo[],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>\n): Entity<M>[] {\n return rows.map(row => rowToEntity<M>(row, slug, primaryKeys, toViewModel));\n}\n\n/**\n * The relation envelope `toFlatRow` writes where a relation was:\n * `{ id, path, __type: \"relation\", data: { id, path, values } }`. It is the\n * admin's view-model, and the only pipeline that produces one is postgres'.\n */\nfunction isRelationEnvelope(\n value: unknown\n): value is { __type: \"relation\"; data?: { values?: Record<string, unknown> } } {\n return typeof value === \"object\"\n && value !== null\n && !Array.isArray(value)\n && (value as { __type?: unknown }).__type === \"relation\";\n}\n\n/** The target's own columns, as `toRestRow` would have inlined them. */\nfunction inlineEnvelope(envelope: { data?: { values?: Record<string, unknown> } }): Record<string, unknown> {\n return envelope.data?.values ?? {};\n}\n\n/**\n * Replace every relation envelope on a row with the target's flat columns.\n *\n * The SDK serves one relation shape — the inlined one (see\n * {@link RestFetchService}) — and Postgres now serves it on every read, so\n * against that driver this walk finds nothing to do. It stays for the drivers\n * whose own `fetchCollection` still answers with refs: a developer reading\n * through this accessor gets one shape whichever driver is underneath.\n *\n * Only applied where the REST pipeline is the contract (see `find`); a driver\n * without a `restFetchService` keeps whatever it returns.\n *\n * Note this is NOT how the admin gets its view model — that is built in the\n * browser by {@link toViewModelValues}, from the same flat row.\n */\nfunction inlineRelationRefs(row: Record<string, unknown>): Record<string, unknown> {\n let out: Record<string, unknown> | undefined;\n for (const [key, value] of Object.entries(row)) {\n if (isRelationEnvelope(value)) {\n out = out ?? { ...row };\n out[key] = inlineEnvelope(value);\n } else if (Array.isArray(value) && value.some(isRelationEnvelope)) {\n out = out ?? { ...row };\n out[key] = value.map((item) => isRelationEnvelope(item) ? inlineEnvelope(item) : item);\n }\n }\n return out ?? row;\n}\n\nfunction createDriverAccessor<M extends Record<string, unknown> = Record<string, unknown>>(\n driver: DataDriver,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>,\n /**\n * Where the driver stores `slug` — see `EntityDataOptions.resolveDataPath`.\n * Asked on every call, not once: the collections it is derived from\n * register after this accessor is built, and can change while it lives.\n */\n storedPath: () => string = () => slug\n): CollectionAccessor<M> {\n /**\n * One page of the collection, and its count. `withDeleted: \"only\"` reads\n * the trash (see `trash` below) through the same pipeline, so a trashed\n * page is paged, counted and shaped exactly as a live one.\n */\n async function findRows(params?: FindParams<M>, withDeleted?: \"only\"): 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 window = resolveFindWindow(params);\n const { offset, driverOffset } = window;\n // A vector search nobody bounded reads the nearest\n // DEFAULT_VECTOR_LIST_LIMIT rows, as it does over HTTP — not 50\n // rows ordered by distance. The `page` stride stays the plain one,\n // as the HTTP parser's does.\n const limit = params?.limit === undefined && params?.vectorSearch\n ? DEFAULT_VECTOR_LIST_LIMIT\n : window.limit;\n\n // Keyset paging, through the same codec and the same driver\n // comparison the HTTP route uses. The in-process accessor is a\n // transport like any other: a walk that seeked differently here\n // than over the wire would be a difference the types cannot see.\n const cursor = params?.after ? decodeCursor(params.after) : undefined;\n const sort = cursor\n ? reconcileCursorOrder(cursor, normalizeOrderBy(params?.orderBy))\n : normalizeOrderBy(params?.orderBy);\n // A cursor issued for an unsorted read carries an empty sort; the\n // HTTP route hands the driver none at all, and so does this.\n const orderBy = sort && sort.length > 0 ? sort : undefined;\n const startAfter = cursor ? cursorToStartAfter(cursor) : undefined;\n\n // One relation shape, whatever the call looks like.\n //\n // This used to fork on `include`: asking for one ran the REST\n // pipeline, which inlines a relation as the target's own columns;\n // not asking ran the driver's own fetch, which eagerly loaded\n // *every* relation and put a `{ __type: \"relation\" }` envelope\n // where the foreign key was. The same method answered in two\n // shapes, the generated types described only one, and a column\n // typed `string` arrived as an object.\n //\n // The REST pipeline is the published contract — the shape the HTTP\n // API serves for this same query, and what `RestFetchService`\n // documents — so every read goes through it when the driver has\n // one. Drivers without one (every browser driver, and so the\n // admin's own path through `buildRebaseData`) are untouched.\n //\n // One row past the page, when seeking — and when not counting.\n //\n // `hasMore` on an offset page is `offset + rows.length < total`, and\n // under a cursor that arithmetic is simply false: every seeked page\n // runs at offset 0, so it compares one page against the whole\n // collection and says \"more\" forever. Without a count there is\n // nothing to compare against at all. Asking for `limit + 1` and\n // looking at whether the extra row arrived is the answer both have —\n // and it costs nothing, where the count it replaces is a second\n // query over every matching row.\n //\n // Counted only where the HTTP listing counts (`RestApiGenerator.readPage`).\n // A distinct read collapses rows the count would count one by one,\n // so its total described a different set from the one served, and\n // `hasMore` — derived from it — offered pages that did not exist.\n // A vector search with a threshold is a distance computed for every\n // row of the table, beside a page an index serves in a millisecond.\n const seeking = startAfter !== undefined;\n const counting = driver.count !== undefined\n && params?.count !== \"none\"\n && params?.distinct !== true\n && params?.vectorSearch?.threshold === undefined;\n const probing = seeking || !counting;\n const probeLimit = probing ? limit + 1 : limit;\n\n // The page and its count in one transaction where the driver can\n // run them so — see `DataDriver.readTogether`.\n const read = async (reader: DataDriver) => {\n const fetchService = reader.restFetchService;\n const fetched = fetchService\n ? await fetchService.fetchCollectionForRest(\n storedPath(),\n {\n filter,\n // Without this the group was dropped and the read ran\n // unfiltered — every row the caller's policies allow,\n // in place of the ones they asked for.\n logical: params?.logical,\n limit: probeLimit,\n // A cursor and an offset describe the same window two\n // incompatible ways; seeking wins and the offset is not\n // sent, or the page would start `offset` rows past\n // where the cursor pointed.\n offset: seeking ? undefined : driverOffset,\n startAfter,\n orderBy,\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Left out, `.vectorSearch(…).find()` in a function\n // or a cron read the newest rows in id order, with\n // no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n fields: params?.fields,\n distinct: params?.distinct,\n ...(withDeleted ? { withDeleted } : {})\n },\n params?.include\n )\n : await reader.fetchCollection<M>({\n path: storedPath(),\n limit: probeLimit,\n offset: seeking ? undefined : driverOffset,\n startAfter,\n filter,\n logical: params?.logical,\n orderBy,\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n vectorSearch: params?.vectorSearch,\n include: params?.include,\n fields: params?.fields,\n distinct: params?.distinct,\n ...(withDeleted ? { withDeleted } : {})\n });\n // The same narrowing the rows were read with. Counting only by\n // `filter` reported the whole collection beside a narrowed\n // page, and `hasMore` was derived from it — so the list offered\n // a next page that did not exist.\n const counted = counting && reader.count\n ? await reader.count({\n path: storedPath(),\n filter,\n logical: params?.logical,\n searchString: params?.searchString,\n vectorSearch: params?.vectorSearch,\n ...(withDeleted ? { withDeleted } : {})\n })\n : undefined;\n return { fetched, counted };\n };\n // One read without a count: nothing to keep on one snapshot with it.\n const { fetched, counted } = counting && driver.readTogether\n ? await driver.readTogether(read)\n : await read(driver);\n\n // The probe row is evidence, not data — it is never served.\n const rows = probing ? fetched.slice(0, limit) : fetched;\n\n // Without a count, the rows up to the end of this page: all a\n // driver that cannot count has ever been able to report.\n const total = counted ?? rows.length + offset;\n const hasMore = probing ? fetched.length > limit : offset + rows.length < total;\n\n // The cursor for the *next* page, from the last row served. Issued\n // by the driver, which is the only layer that knows which columns\n // address a row; absent where it cannot describe one, and the\n // caller then pages by offset.\n const last = rows[rows.length - 1] as Record<string, unknown> | undefined;\n const nextCursor = (hasMore && last && driver.restFetchService?.cursorFor)\n ? driver.restFetchService.cursorFor(storedPath(), last, orderBy)\n : undefined;\n\n return {\n data: rowsToEntities<M>(rows, slug, getPks(), toViewModel),\n meta: { total, limit, offset, hasMore, ...(nextCursor && { nextCursor }) }\n };\n }\n\n async function countRows(params?: FindParams<M>, withDeleted?: \"only\"): Promise<number> {\n if (!driver.count) throw new Error(noCount(slug));\n const filter = params?.where ? deserializeFilter(params.where as Record<string, unknown>) : undefined;\n // Every narrowing `find()` applies has to apply here too, or\n // the count describes a different query than the one it is\n // reported against.\n return driver.count({\n path: storedPath(),\n filter,\n logical: params?.logical,\n searchString: params?.searchString,\n // A threshold drops rows, so it narrows the count as it\n // narrows the read — as `GET /count` does.\n vectorSearch: params?.vectorSearch,\n ...(withDeleted ? { withDeleted } : {})\n });\n }\n\n const accessor: CollectionAccessor<M> = {\n find(params?: FindParams<M>): Promise<FindResponse<M>> {\n return findRows(params);\n },\n\n async findById(id: string | number, options?: ByIdReadOptions): 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(storedPath(), id, options?.include)\n : await driver.fetchOne<M>({ path: storedPath(), id: id });\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n\n // Present only when the driver's fetch service implements it — the SDK\n // wrapper turns an absent one into a stub that names the capability.\n aggregate: driver.restFetchService?.aggregate\n ? async (params: AggregateParams<M>): Promise<AggregateResult> => {\n // Paged the way the REST route pages it: one group past the\n // `limit`, so `meta.hasMore` is known rather than guessed from a\n // page that happened to come back full. No default limit here —\n // an in-process caller that names none gets every group, as it\n // always did — so `meta` is only for a call that named one.\n const grouped = (params.groupBy?.length ?? 0) > 0;\n const limit = grouped ? params.limit : undefined;\n const rows: AggregateResult = await driver.restFetchService!.aggregate!(storedPath(), {\n aggregates: params.select.map(toDriverAggregate),\n groupBy: params.groupBy?.map(group => toDriverAggregateGroup(group as string | AggregateGroup)),\n filter: params.where\n ? deserializeFilter(params.where as Record<string, unknown>)\n : undefined,\n logical: params.logical,\n searchString: params.searchString,\n limit: limit !== undefined ? limit + 1 : params.limit,\n offset: params.offset,\n // The sort the HTTP route forwards. Dropped here, an\n // in-process `aggregate({ orderBy: [\"sum_total\", \"desc\"] })`\n // came back in group-key order instead.\n orderBy: normalizeOrderBy(params.orderBy)\n });\n if (limit === undefined) return rows;\n const page: AggregateResult = rows.slice(0, limit);\n const meta: AggregateMeta = { limit, offset: params.offset ?? 0, hasMore: rows.length > limit };\n Object.defineProperty(page, \"meta\", { value: meta, enumerable: false });\n return page;\n }\n : undefined,\n\n refresh: driver.refreshView\n ? (options?: RefreshOptions) => driver.refreshView!(storedPath(), options)\n : undefined,\n\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: storedPath(),\n values: data,\n id: id,\n status: \"new\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n createMany: driver.saveMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await driver.saveMany!<M>({\n path: storedPath(),\n rows: data,\n upsert: options?.upsert,\n // Dropped here, an `upsert` on a natural key silently\n // became an upsert on the primary key — which for a serial\n // id is a plain insert, so the re-runnable import the\n // option exists for duplicated every row instead.\n onConflict: options?.onConflict\n });\n return rowsToEntities<M>(rows, slug, getPks(), toViewModel);\n }\n : undefined,\n\n async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {\n const row = await driver.save<M>({\n path: storedPath(),\n values: data,\n id: id,\n status: \"existing\"\n });\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n\n async delete(id: string | number): Promise<void> {\n // The address only. A server driver reads the row it deletes; the\n // `values: {}` this used to send was recorded by history as the\n // deleted row, and was all a `beforeDelete` had to judge.\n return driver.delete({ row: { id, path: storedPath() } });\n },\n\n // Present only when the driver is: exposing these unconditionally and\n // looping single writes underneath would give a caller neither the\n // atomicity nor the single round trip they reached for a batch to get,\n // while looking exactly like it had.\n updateMany: driver.updateMany\n ? async (updates: { id: string | number; data: Partial<EntityValues<M>> }[]): Promise<Entity<M>[]> => {\n const rows = await driver.updateMany!<M>({\n path: storedPath(),\n updates: updates.map(u => ({ id: u.id,\nvalues: u.data })),\n });\n return rowsToEntities<M>(rows, slug, getPks(), toViewModel);\n }\n : undefined,\n\n deleteMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: storedPath(),\nids });\n }\n : undefined,\n\n count: driver.count ? (params?: FindParams<M>) => countRows(params) : undefined,\n\n // `?deleted=only` and `?hard=true`, in-process. A restore is an\n // ordinary `update` (the soft-delete field back to `null`), which the\n // driver already lets reach a trashed row, so it is not here.\n trash: {\n find: (params?: FindParams<M>) => findRows(params, \"only\"),\n count: (params?: FindParams<M>) => countRows(params, \"only\"),\n purge: (id: string | number) => driver.delete({ row: { id, path: storedPath() }, hard: true }),\n purgeMany: driver.deleteMany\n ? async (ids: (string | number)[]): Promise<void> => {\n await driver.deleteMany!<M>({ path: storedPath(), ids, hard: true });\n }\n : undefined\n },\n\n listen: driver.listenCollection\n ? (params: FindParams<M> | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void) => {\n const { limit, offset, driverOffset } = resolveFindWindow(params);\n // Belt and braces. Postgres serves one shape on every read now,\n // realtime included, so this flattens nothing there — but a\n // driver whose `listen` still answers with refs is normalized\n // to the shape the rest of this accessor serves rather than\n // handing a developer two.\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenCollection!<M>({\n path: storedPath(),\n limit,\n offset: driverOffset,\n filter: params?.where,\n logical: params?.logical,\n orderBy: normalizeOrderBy(params?.orderBy),\n searchString: params?.searchString,\n searchExplain: params?.searchExplain,\n // Forwarded so the SERVER can refuse it. `realtimeService`\n // rejects a subscription carrying `vectorSearch` — a\n // subscription is re-run on every matching write and\n // nothing there computes distances — and the docs promise\n // that refusal. Both producers hand-list their fields and\n // both omitted this one, so the guard could not fire and\n // `.vectorSearch(…).listen()` returned an ordinary\n // `id DESC` listing with no `_distance` and no error.\n vectorSearch: params?.vectorSearch,\n onUpdate: (entities) => {\n onUpdate({\n data: entities.map((row: Record<string, unknown>) => rowToEntity<M>(normalize(row), slug, getPks(), toViewModel)),\n meta: {\n // No count is issued on this path, so the total\n // is unknown; the lower bound is the rows in\n // hand plus the ones paged past to reach them.\n // Reporting `entities.length` claimed a read at\n // offset 100 had found a collection of two.\n total: offset + entities.length,\n limit,\n offset,\n hasMore: entities.length >= limit\n }\n });\n },\n onError\n });\n } : undefined,\n\n listenById: driver.listenOne\n ? (id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void, options?: ByIdReadOptions) => {\n const normalize = driver.restFetchService ? inlineRelationRefs : (row: Record<string, unknown>) => row;\n return driver.listenOne!<M>({\n path: storedPath(),\n id: id,\n include: options?.include,\n onUpdate: (entity) => onUpdate(entity ? rowToEntity<M>(normalize(entity), slug, getPks(), toViewModel) : undefined),\n onError\n });\n } : undefined,\n\n // Fluent Query Builder\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, ascending?: \"asc\" | \"desc\") {\n return new QueryBuilder<M>(accessor).orderBy(column, ascending);\n },\n limit(count: number) {\n return new QueryBuilder<M>(accessor).limit(count);\n },\n offset(count: number) {\n return new QueryBuilder<M>(accessor).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new QueryBuilder<M>(accessor).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new QueryBuilder<M>(accessor).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new QueryBuilder<M>(accessor).include(...relations);\n }\n };\n\n return accessor;\n}\n\n/**\n * Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.\n *\n * This is the key bridge: any property access like `data.products` returns\n * a `CollectionAccessor` backed by the underlying DataDriver, without\n * needing per-collection code generation.\n *\n * @example\n * const data = buildRebaseData(driver);\n * await data.products.create({ name: \"Camera\", price: 299 });\n * const { data: items } = await data.products.find({ where: { status: [\"==\", \"published\"] } });\n */\n/**\n * The view-model converter for one collection, or `undefined` when there is no\n * collection config to build it from.\n *\n * Absent is the honest answer for every consumer that is not the admin: the\n * flat SDK derives itself from this same layer (`buildSdkData`) and must keep\n * the wire's own types, and it registers no collection resolver.\n */\nfunction createViewModelConverter(options?: EntityDataOptions) {\n if (!options?.resolveCollection) return () => undefined;\n return function converterFor(slug: string) {\n return (values: Record<string, unknown>): Record<string, unknown> => {\n // Resolved per call rather than memoized: the resolver is\n // late-bound (see `createPrimaryKeyResolver`) and a collection\n // edited in the schema editor should not need a reload here.\n const collection = options.resolveCollection?.(slug);\n if (!collection) return values;\n return toViewModelValues(values, collection.properties, collection, options.resolveCollection);\n };\n };\n}\n\n/**\n * A row as the server sends it, in the admin's view model of its collection:\n * the `values` `buildRebaseData` and `wrapAsEntityData` give an `Entity` of\n * that collection — a date as a `Date`, a relation as an `EntityRelation`,\n * also under its property's key where the row carries only the foreign key —\n * and a `reference` property's id as an `EntityReference`. A collection the\n * resolver does not know leaves the row as it is.\n *\n * For code that holds a flat row and hands it to what expects the admin's\n * values, such as a collection's `browserCallbacks` or a preview.\n */\nexport function rowToViewModel(\n row: Record<string, unknown>,\n slug: string,\n resolveCollection: NonNullable<EntityDataOptions[\"resolveCollection\"]>\n): Record<string, unknown> {\n const collection = resolveCollection(slug);\n if (!collection) return row;\n return withReferences(toViewModelValues(row, collection.properties, collection, resolveCollection), collection.properties);\n}\n\n/**\n * Each `reference` property's value — the referenced id, or `{ id, path }` —\n * as the `EntityReference` the admin's reference field and preview read; an\n * array of references item by item.\n */\nfunction withReferences(values: Record<string, unknown>, properties: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!properties) return values;\n let out: Record<string, unknown> | undefined;\n for (const [key, property] of Object.entries(properties)) {\n if (!(key in values)) continue;\n const value = values[key];\n const path = referencePath(property);\n const arrayPath = isPlainRecord(property) && property.type === \"array\" ? referencePath(property.of) : undefined;\n if (path !== undefined) {\n out = out ?? { ...values };\n out[key] = toReference(value, path);\n } else if (arrayPath !== undefined && Array.isArray(value)) {\n out = out ?? { ...values };\n out[key] = value.map(item => toReference(item, arrayPath));\n }\n }\n return out ?? values;\n}\n\n/** The target path of a `reference` property; `undefined` for any other. */\nfunction referencePath(property: unknown): string | undefined {\n if (!isPlainRecord(property) || property.type !== \"reference\") return undefined;\n return typeof property.path === \"string\" ? property.path : \"\";\n}\n\nfunction toReference(value: unknown, path: string): unknown {\n if (value === null || value === undefined || value === \"\") return null;\n if (value instanceof EntityReference) return value;\n if ((typeof value === \"string\" || typeof value === \"number\") && path) {\n return new EntityReference({ id: String(value), path });\n }\n if (isPlainRecord(value) && (typeof value.id === \"string\" || typeof value.id === \"number\")) {\n const target = typeof value.path === \"string\" ? value.path : path;\n if (target) return new EntityReference({ id: String(value.id), path: target });\n }\n return value;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = createDriverAccessor(\n driver,\n slug,\n () => primaryKeysFor(slug),\n viewModelFor(slug),\n () => options?.resolveDataPath?.(slug) ?? slug\n );\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 // A collection's own accessor name, else camelCase → snake_case.\n return getAccessor(options?.resolveAccessor?.(prop) ?? toSnakeCase(prop));\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 M;\n}\n\n/**\n * Fluent query builder for the flat SDK data layer. Mirrors {@link QueryBuilder}\n * but resolves to `FindResult<M>` (flat rows) instead of Entity-wrapped\n * `FindResponse<M>`.\n */\nclass SdkQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n private params: FindParams = { where: {} };\n\n constructor(private client: SDKCollectionClient<M>) {}\n\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n /** A relation path (`author.name`) or a JSON path (`metadata->>tier`). */\n where(column: NonColumnFieldPath, operator: WhereFilterOp, value: unknown): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n // A second group narrows rather than replaces — see the SDK\n // builder in `@rebasepro/client`, which had the same defect.\n const next = columnOrCondition as LogicalCondition;\n this.params.logical = this.params.logical\n ? { type: \"and\", conditions: [this.params.logical, next] }\n : next;\n return this;\n }\n if (!this.params.where) this.params.where = {};\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n return this;\n }\n\n /** Called again, this adds a tie-breaker rather than replacing the sort. */\n orderBy(\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction: \"asc\" | \"desc\" = \"asc\",\n nulls?: NullsPlacement\n ): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n const key = sortKeyToString(column);\n this.params.orderBy = [...existing, (nulls\n ? [key, direction, nulls]\n : [key, direction]) as OrderByTuple];\n return this;\n }\n\n limit(count: number): this { this.params.limit = count; return this; }\n offset(count: number): this { this.params.offset = count; return this; }\n search(searchString: string, options?: { explain?: boolean }): this { this.params.searchString = searchString; if (options?.explain !== undefined) this.params.searchExplain = options.explain; return this; }\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n /**\n * Load relations. Merges rather than replaces, so `.include(\"author\")` then\n * `.include({ comments: { limit: 5 } })` asks for both — a builder call that\n * silently discarded an earlier one is the same defect `where` had.\n */\n include(...relations: (string | IncludeSpec)[]): this {\n this.params.include = mergeIncludeSpecs(this.params.include, relations);\n return this;\n }\n\n fields(...columns: (FieldPath<M> | string)[]): this {\n this.params.fields = [...(this.params.fields ?? []), ...columns as string[]];\n return this;\n }\n\n distinct(enabled = true): this { this.params.distinct = enabled; return this; }\n\n after(cursor: string): this { this.params.after = cursor; return this; }\n\n async find(): Promise<FindResult<M>> {\n return this.client.find(this.params as FindParams<M>);\n }\n\n /** Aggregate the matching rows. See {@link SDKCollectionClient.aggregate}. */\n async aggregate(\n params: Omit<AggregateParams<M>, \"where\" | \"logical\" | \"searchString\">\n ): Promise<AggregateResult> {\n return this.client.aggregate({\n ...params,\n where: this.params.where as AggregateParams<M>[\"where\"],\n logical: this.params.logical as AggregateParams<M>[\"logical\"],\n searchString: this.params.searchString\n });\n }\n\n /**\n * Page through everything this query matches, one row at a time.\n *\n * `.limit()` on the builder becomes the page size, so the ceiling on a\n * single `find()` is not a ceiling on what the query can read.\n */\n iterate(options?: PageWalkOptions<M>): AsyncIterableIterator<M> {\n return this.client.iterate({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as IterateParams<M>);\n }\n\n /** Collect everything this query matches into one array. */\n findAll(options?: PageWalkOptions<M> & { maxRows?: number }): Promise<M[]> {\n return this.client.findAll({\n ...(this.params as FindParams<M>),\n ...(this.params.limit !== undefined && { pageSize: this.params.limit }),\n ...options\n } as FindAllParams<M>);\n }\n\n /**\n * Count the records matching this query.\n *\n * This used to answer `0` when the client had no `count` — a number, from a\n * source that had not counted anything, indistinguishable from an empty\n * collection. It now does what the client does, which on a source that\n * cannot count is throw and say so.\n */\n async count(): Promise<number> {\n return this.client.count(this.params as FindParams<M>);\n }\n\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n return this.client.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n\n/**\n * Refuse a write option this door cannot honour, by name, before anything is\n * written.\n *\n * The in-process accessor implements the same `SDKCollectionClient` as the HTTP\n * client, whose `ifMatch` is \"Honoured on `update` and `delete`\" — and its\n * writes took no options at all, so `rebase.data.posts.update(id, data, {\n * ifMatch: staleEtag })` overwrote the row and resolved. A precondition the\n * caller wrote, dropped without a word, is the worst thing a door can do with\n * it. The driver's `save` has no precondition and no idempotency store to hand\n * these to, so they are refused rather than ignored. An `undefined` value is no\n * option, as it is over HTTP: `etagOf(row)` is `undefined` for a row that\n * carries no version, and that means \"write unconditionally\".\n */\nfunction refuseUnhonouredWriteOptions(method: string, slug: string, options?: WriteOptions): void {\n const unsupported = ([\"ifMatch\", \"idempotencyKey\"] as const).filter((key) => options?.[key]);\n if (unsupported.length === 0) return;\n throw new RebaseClientError(\n `${method}() on \"${slug}\" in-process (context.data, rebase.data) cannot honour ` +\n `${unsupported.join(\" or \")}: this door has no ` +\n `${unsupported.includes(\"ifMatch\") ? \"precondition check\" : \"idempotency store\"}, and ignoring ` +\n \"the option would write as though it had been checked. Drop it here, or send the write through \" +\n \"the HTTP client, which honours it.\",\n { code: \"UNSUPPORTED_OPTION\", details: { method, options: unsupported } }\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, options?: ByIdReadOptions): Promise<M | undefined> {\n const s = await snap.findById(id, options);\n return s ? entityToRow(s) : undefined;\n },\n async get(id: string | number): Promise<M> {\n // The same contract server-side as in the browser SDK, deliberately:\n // a callback, a cron and an app all read a row by id, and the shape\n // of \"it is not there\" should not depend on which one is asking.\n const s = await snap.findById(id);\n if (!s) {\n throw new RebaseApiError(\n `No record with id ${JSON.stringify(String(id))} in \"${slug}\".`,\n { status: 404, code: \"NOT_FOUND\" }\n );\n }\n return entityToRow(s);\n },\n async create(data: Partial<M>, id?: string | number, options?: WriteOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"create\", slug, options);\n return entityToRow(await snap.create(data as Partial<EntityValues<M>>, id));\n },\n async createMany(\n data: Partial<M>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] } & WriteOptions\n ): Promise<M[]> {\n refuseUnhonouredWriteOptions(\"createMany\", slug, options);\n if (!Array.isArray(data)) {\n throw new RebaseClientError(\"createMany expects an array of records.\", { code: \"INVALID_BULK_BODY\" });\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>>[], {\n upsert: options?.upsert,\n onConflict: options?.onConflict\n });\n return rows.map(entityToRow);\n },\n /**\n * One row through the bulk path, because the bulk path is where the\n * conflict target lives.\n *\n * `CollectionAccessor` has no single-row upsert and adding one would\n * mean a second way to say the same thing to the same driver method —\n * `saveMany` already takes `upsert` and `onConflict`, and a batch of\n * one is exactly an upsert of one.\n */\n async upsert(data: Partial<M>, options?: UpsertOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"upsert\", slug, options);\n if (!snap.createMany) {\n throw new Error(\n \"Upsert is not supported by this collection's data source: it needs a bulk write, \" +\n \"which this driver does not implement. Fall back to create() or update().\"\n );\n }\n const rows = await snap.createMany(\n [data as Partial<EntityValues<M>>],\n { upsert: true, onConflict: options?.onConflict }\n );\n const row = rows[0];\n if (!row) throw new Error(`Upsert into \"${slug}\" returned no row.`);\n return entityToRow(row);\n },\n async update(id: string | number, data: Partial<M> | UpdateValues<Partial<M>>, options?: WriteOptions): Promise<M> {\n refuseUnhonouredWriteOptions(\"update\", slug, options);\n return entityToRow(await snap.update(id, data as Partial<EntityValues<M>>));\n },\n async updateMany(\n updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[],\n options?: WriteOptions\n ): Promise<M[]> {\n refuseUnhonouredWriteOptions(\"updateMany\", slug, options);\n if (!Array.isArray(updates)) {\n throw new RebaseClientError(\"updateMany expects an array of { id, data } entries.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (updates.length === 0) return [];\n if (!snap.updateMany) {\n throw new Error(\n \"Bulk updates are not supported by this collection's data source. \" +\n \"Fall back to update() per record.\"\n );\n }\n const rows = await snap.updateMany(\n updates.map(u => ({ id: u.id,\ndata: u.data as Partial<EntityValues<M>> }))\n );\n return rows.map(entityToRow);\n },\n async delete(id: string | number, options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"delete\", slug, options);\n return snap.delete(id);\n },\n async deleteMany(ids: (string | number)[], options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"deleteMany\", slug, options);\n if (!Array.isArray(ids)) {\n throw new RebaseClientError(\"deleteMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (ids.length === 0) return;\n if (!snap.deleteMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to delete() per record.\"\n );\n }\n await snap.deleteMany(ids);\n },\n // The three are non-optional on `SDKCollectionClient`: where the\n // underlying accessor cannot serve one, a stub says so when called\n // rather than being absent. `isUnsupported()` is how an adapter asks\n // the capability question — see `toEntityAccessor` below, which has to.\n count: snap.count\n ? (params?: FindParams<M>) => snap.count!(params)\n : unsupportedMethod(noCount(slug)),\n listen: snap.listen\n ? (params: FindParams<M> | undefined, onUpdate: (r: FindResult<M>) => void, onError?: (e: Error) => void) =>\n snap.listen!(params, (res) => onUpdate({ data: res.data.map(entityToRow), meta: res.meta }), onError)\n : unsupportedMethod(noRealtime(slug)),\n listenById: snap.listenById\n ? (id: string | number, onUpdate: (r: M | undefined) => void, onError?: (e: Error) => void, options?: ByIdReadOptions) =>\n snap.listenById!(id, (s) => onUpdate(s ? entityToRow(s) : undefined), onError, options)\n : unsupportedMethod(noRealtime(slug)),\n listenCount: snap.listenCount\n ? (params: FindParams<M> | undefined, onCount: (total: number) => void, onError?: (e: Error) => void) =>\n snap.listenCount!(params, onCount, onError)\n : unsupportedMethod(noRealtime(slug)),\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SdkQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (\n column: FieldPath<M> | ComputedSortField | RelationAggregateSort,\n direction?: \"asc\" | \"desc\",\n nulls?: NullsPlacement\n ) => new SdkQueryBuilder<M>(client).orderBy(column, direction, nulls),\n limit: (count: number) => new SdkQueryBuilder<M>(client).limit(count),\n offset: (count: number) => new SdkQueryBuilder<M>(client).offset(count),\n search: (searchString: string) => new SdkQueryBuilder<M>(client).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new SdkQueryBuilder<M>(client).vectorSearch(property, vector, options),\n include: (...relations: (string | IncludeSpec)[]) => new SdkQueryBuilder<M>(client).include(...relations),\n fields: (...columns: (FieldPath<M> | string)[]) => new SdkQueryBuilder<M>(client).fields(...columns),\n distinct: (enabled?: boolean) => new SdkQueryBuilder<M>(client).distinct(enabled),\n after: (cursor: string) => new SdkQueryBuilder<M>(client).after(cursor),\n aggregate: snap.aggregate\n ? (params: AggregateParams<M>) => snap.aggregate!(params)\n : unsupportedMethod(noAggregate(slug)),\n refresh: snap.refresh\n ? (options?: RefreshOptions) => snap.refresh!(options)\n : unsupportedMethod(noRefresh(slug)),\n trash: toSdkTrashClient(snap, slug, () => client)\n };\n return client;\n}\n\n/**\n * {@link SDKCollectionClient.trash} over an accessor's\n * {@link CollectionAccessor.trash}: the same API the browser client has. A\n * restore is the client's own `update`/`updateMany` setting the soft-delete\n * field to `null`, so it keeps their refusals and their answer.\n */\nfunction toSdkTrashClient<M extends Record<string, unknown>>(\n snap: CollectionAccessor<M>,\n slug: string,\n client: () => SDKCollectionClient<M>\n): TrashClient<M> {\n const trash = snap.trash;\n if (!trash) {\n return {\n find: unsupportedMethod(noTrash(slug)),\n count: unsupportedMethod(noTrash(slug)),\n restore: unsupportedMethod(noTrash(slug)),\n restoreMany: unsupportedMethod(noTrash(slug)),\n purge: unsupportedMethod(noTrash(slug)),\n purgeMany: unsupportedMethod(noTrash(slug))\n };\n }\n const field = (options?: RestoreOptions<M>): string => options?.field ?? DEFAULT_SOFT_DELETE_FIELD;\n return {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const res = await trash.find(params);\n return { data: res.data.map(entityToRow), meta: res.meta };\n },\n count: (params?: FindParams<M>) => trash.count(params),\n restore: (id: string | number, options?: RestoreOptions<M>) =>\n client().update(id, { [field(options)]: null } as Partial<M>, writeOptionsOf(options)),\n restoreMany(ids: (string | number)[], options?: RestoreOptions<M>) {\n if (!Array.isArray(ids)) {\n return Promise.reject(new RebaseClientError(\n \"trash.restoreMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" }\n ));\n }\n const data = { [field(options)]: null } as Partial<M>;\n return client().updateMany(ids.map((id) => ({ id, data })), writeOptionsOf(options));\n },\n async purge(id: string | number, options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"trash.purge\", slug, options);\n await trash.purge(id);\n },\n async purgeMany(ids: (string | number)[], options?: WriteOptions): Promise<void> {\n refuseUnhonouredWriteOptions(\"trash.purgeMany\", slug, options);\n if (!Array.isArray(ids)) {\n throw new RebaseClientError(\"trash.purgeMany expects an array of ids.\", { code: \"INVALID_BULK_BODY\" });\n }\n if (ids.length === 0) return;\n if (!trash.purgeMany) {\n throw new Error(\n \"Bulk deletes are not supported by this collection's data source. \" +\n \"Fall back to trash.purge() per record.\"\n );\n }\n await trash.purgeMany(ids);\n }\n };\n}\n\n/** A {@link RestoreOptions} without its `field`: the write options it carries. */\nfunction writeOptionsOf<M extends Record<string, unknown>>(options?: RestoreOptions<M>): WriteOptions | undefined {\n if (!options) return undefined;\n const { field: _field, ...rest } = options;\n return rest;\n}\n\n/**\n * Wrap a flat {@link SDKCollectionClient} into a Entity-shaped\n * {@link CollectionAccessor}. Every returned row is re-wrapped into the\n * `{ id, path, values }` view-model the admin panel renders.\n */\nfunction toEntityAccessor<M extends Record<string, unknown>>(\n sdk: SDKCollectionClient<M>,\n slug: string,\n getPks: () => PrimaryKeyInfo[] = () => [],\n toViewModel?: (values: Record<string, unknown>) => Record<string, unknown>,\n /**\n * What `toViewModel` converts with — the collection as resolved now. A\n * live list keeps the entities it built for as long as this and the keys\n * stay the same object (see `listen`).\n */\n viewModelSource: () => unknown = () => undefined\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: rowsToEntities<M>(res.data, slug, getPks(), toViewModel), meta: res.meta };\n },\n async findById(id: string | number, options?: ByIdReadOptions): Promise<Entity<M> | undefined> {\n const row = await sdk.findById(id, options);\n return row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined;\n },\n async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {\n return rowToEntity<M>(await sdk.create(data as Partial<M>, id), slug, getPks(), toViewModel);\n },\n // Declared on `CollectionAccessor` and, until now, never implemented on\n // this side of the boundary — so the admin's own import wrote one HTTP\n // request per row and could neither be atomic nor upsert. It forwards to\n // the same `/bulk` route the SDK client uses.\n createMany: sdk.createMany\n ? async (\n data: Partial<EntityValues<M>>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] }\n ): Promise<Entity<M>[]> => {\n const rows = await sdk.createMany!(data as Partial<M>[], options);\n return rowsToEntities<M>(rows, slug, getPks(), toViewModel);\n }\n : undefined,\n async update(id: string | number, data: Partial<EntityValues<M>>, options?: { ifMatch?: string }): Promise<Entity<M>> {\n const row = options?.ifMatch\n ? await sdk.update(id, data as Partial<M>, { ifMatch: options.ifMatch })\n : await sdk.update(id, data as Partial<M>);\n if (!row) throw new Error(`Update returned no data for id ${id}`);\n return rowToEntity<M>(row, slug, getPks(), toViewModel);\n },\n delete(id: string | number): Promise<void> {\n return sdk.delete(id);\n },\n // `CollectionAccessor` keeps these optional, and the optionality is\n // load-bearing: the admin panel picks between subscribing and a\n // one-shot `find()` on exactly this property, and a UI that subscribes\n // into a throw is worse than one that polls. The client's method is\n // always present now, so the capability is read off the stub instead.\n count: isUnsupported(sdk.count) ? undefined : (params?: FindParams<M>) => sdk.count(params),\n aggregate: isUnsupported(sdk.aggregate)\n ? undefined\n : (params: AggregateParams<M>) => sdk.aggregate(params),\n refresh: isUnsupported(sdk.refresh)\n ? undefined\n : (options?: RefreshOptions) => sdk.refresh(options),\n listen: isUnsupported(sdk.listen)\n ? undefined\n : (params: FindParams<M> | undefined, onUpdate: (r: FindResponse<M>) => void, onError?: (e: Error) => void) => {\n // One entity per row object, for as long as the subscription\n // lives. The socket hands back the very row it cached when a\n // refetch of it is equal (`mergeRows`), so a push that changed\n // one row of fifty arrives with forty-nine rows it has seen;\n // building fifty new entities threw that away, and every view\n // downstream compared fifty rows by value again. Keyed on the\n // row object, so only a row the socket kept is reused, and\n // dropped whenever the keys or the collection the values are\n // converted with change (a key learned late, a schema edit).\n let built = new WeakMap<object, Entity<M>>();\n let builtWith: [PrimaryKeyInfo[], unknown] | undefined;\n return sdk.listen(params, (res) => {\n const pks = getPks();\n const source = viewModelSource();\n if (!builtWith || builtWith[0] !== pks || builtWith[1] !== source) {\n built = new WeakMap();\n builtWith = [pks, source];\n }\n onUpdate({\n data: res.data.map((row) => {\n let entity = built.get(row);\n if (!entity) {\n entity = rowToEntity<M>(row, slug, pks, toViewModel);\n built.set(row, entity);\n }\n return entity;\n }),\n meta: res.meta\n });\n }, onError);\n },\n listenById: isUnsupported(sdk.listenById)\n ? undefined\n : (id: string | number, onUpdate: (s: Entity<M> | undefined) => void, onError?: (e: Error) => void, options?: ByIdReadOptions) =>\n sdk.listenById(id, (row) => onUpdate(row ? rowToEntity<M>(row, slug, getPks(), toViewModel) : undefined), onError, options),\n listenCount: isUnsupported(sdk.listenCount)\n ? undefined\n : (params: FindParams<M> | undefined, onCount: (total: number) => void, onError?: (e: Error) => void) =>\n sdk.listenCount(params, onCount, onError),\n trash: !sdk.trash || isUnsupported(sdk.trash.find)\n ? undefined\n : {\n async find(params?: FindParams<M>): Promise<FindResponse<M>> {\n const res = await sdk.trash.find(params);\n return { data: rowsToEntities<M>(res.data, slug, getPks(), toViewModel), meta: res.meta };\n },\n count: (params?: FindParams<M>) => sdk.trash.count(params),\n purge: (id: string | number) => sdk.trash.purge(id),\n purgeMany: (ids: (string | number)[]) => sdk.trash.purgeMany(ids)\n },\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new QueryBuilder<M>(accessor);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy: (column: keyof M & string, direction?: \"asc\" | \"desc\") => new QueryBuilder<M>(accessor).orderBy(column, direction),\n limit: (count: number) => new QueryBuilder<M>(accessor).limit(count),\n offset: (count: number) => new QueryBuilder<M>(accessor).offset(count),\n search: (searchString: string) => new QueryBuilder<M>(accessor).search(searchString),\n vectorSearch: (\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) => new QueryBuilder<M>(accessor).vectorSearch(property, vector, options),\n include: (...relations: string[]) => new QueryBuilder<M>(accessor).include(...relations)\n };\n return accessor;\n}\n\n/**\n * Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.\n *\n * This is the **admin boundary**: the SDK client (`client.data`) returns flat\n * rows, but the admin renders the `Entity` view-model (`entity.values.*`).\n * `core/Rebase.tsx` wraps `client.data` through this before handing it to the\n * admin `RebaseDataContext` — without it the admin renders rows with only their\n * `id`.\n */\n/**\n * Only the by-slug accessor is asked for, so only that is required.\n *\n * Taking a whole `RebaseSdkData` meant taking `RebaseSdkData<unknown>`, whose\n * dynamic branch is an index signature — and no `RebaseSdkData<DB>` satisfies\n * it, because its own `collection` method is not a `SDKCollectionClient`. So a\n * caller holding a *typed* client could not pass it to a function that reads\n * one method off it, and that method is identical on every instantiation.\n */\nexport function wrapAsEntityData(sdkData: Pick<RebaseSdkData, \"collection\">, options?: EntityDataOptions): RebaseData {\n const cache = new Map<string, CollectionAccessor>();\n const primaryKeysFor = createPrimaryKeyResolver(options);\n const viewModelFor = createViewModelConverter(options);\n\n function getAccessor(slug: string): CollectionAccessor {\n let accessor = cache.get(slug);\n if (!accessor) {\n accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug), viewModelFor(slug),\n () => options?.resolveCollection?.(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 *\n * `DB` is the generated `Database` the result is typed from. One object serves\n * every `Database` — each accessor is resolved by name when it is read — so it\n * describes the result and changes nothing it does. Only an explicit type\n * argument sets it (`NoInfer`): inferred from where the result is assigned, it\n * would be whatever that place declares — an untyped `Promise<RebaseSdkData>`\n * made it `RebaseSdkData | PromiseLike<RebaseSdkData>`. Untyped by default.\n */\nexport function wrapAsSdkData<DB = unknown>(\n entityData: RebaseData,\n options?: Pick<EntityDataOptions, \"resolveAccessor\">\n): RebaseSdkData<NoInfer<DB>> {\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<DB>;\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(options?.resolveAccessor?.(prop) ?? toSnakeCase(prop));\n }\n });\n}\n\n/**\n * Build a flat {@link RebaseSdkData} from a `DataDriver`.\n *\n * This is the developer-facing SDK data layer used by backend framework\n * callbacks & scripts (`context.data` / `rebase.dataAsAdmin`). It returns flat rows —\n * identical in shape to the frontend SDK client, down to how a relation is\n * served: a foreign key stays a foreign key, and a relation named in `include`\n * arrives as the target's own columns. The `{ __type: \"relation\" }` envelope is\n * the admin's view-model and never reaches here.\n *\n * The admin uses {@link buildRebaseData} (Entity) over its own driver.\n *\n * `options.resolveAccessor` names the slug a property is (see\n * `createAccessorSlugResolver`); absent, `data.myNotes` is `my_notes`.\n *\n * `DB` types the result from a generated `Database` — `buildSdkData<Database>(driver)`\n * is the plane `context.data` is in a project that registered it. It describes\n * the object and changes nothing it does, and only an explicit type argument\n * sets it (see {@link wrapAsSdkData}); untyped by default.\n */\nexport function buildSdkData<DB = unknown>(\n driver: DataDriver,\n options?: Pick<EntityDataOptions, \"resolveAccessor\">\n): RebaseSdkData<NoInfer<DB>> {\n return wrapAsSdkData<DB>(buildRebaseData(driver), options);\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { RebaseData, RebaseSdkData } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
4
|
-
* {@link RebaseData}
|
|
5
|
-
* `.collection(slug)` accessor, which is all the router needs.
|
|
3
|
+
* The data-layer shapes that can be routed: the Entity-shaped admin
|
|
4
|
+
* {@link RebaseData}, the flat SDK {@link RebaseSdkData}, or just the flat
|
|
5
|
+
* SDK's `.collection(slug)` accessor, which is all the router needs.
|
|
6
6
|
*/
|
|
7
|
-
export type RoutableData = RebaseData | RebaseSdkData
|
|
7
|
+
export type RoutableData = RebaseData | RebaseSdkData | Pick<RebaseSdkData, "collection">;
|
|
8
8
|
/**
|
|
9
9
|
* Parameters for {@link buildRoutedRebaseData}.
|
|
10
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildRoutedRebaseData.es.js","names":[],"sources":["../../src/data/buildRoutedRebaseData.ts"],"sourcesContent":["import { RebaseData, RebaseSdkData } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The
|
|
1
|
+
{"version":3,"file":"buildRoutedRebaseData.es.js","names":[],"sources":["../../src/data/buildRoutedRebaseData.ts"],"sourcesContent":["import { RebaseData, RebaseSdkData } from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The data-layer shapes that can be routed: the Entity-shaped admin\n * {@link RebaseData}, the flat SDK {@link RebaseSdkData}, or just the flat\n * SDK's `.collection(slug)` accessor, which is all the router needs.\n */\nexport type RoutableData = RebaseData | RebaseSdkData | Pick<RebaseSdkData, \"collection\">;\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 * The slug a property names (`data.myNotes`). Absent, a property is its\n * snake_case (`my_notes`). See `createAccessorSlugResolver`.\n */\n resolveAccessor?: (property: string) => string;\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 resolveAccessor\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 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 // A collection's own accessor name, else camelCase → snake_case,\n // mirroring buildRebaseData so dynamic access routes consistently.\n return getAccessor(resolveAccessor?.(prop) ?? toSnakeCase(prop));\n }\n }) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAgB,sBAA2D,EACvE,aACA,SACA,YACA,mBAC6B;CAI7B,IAAI,CAAC,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAC5C,OAAO;CAGX,SAAS,QAAQ,YAAuB;EACpC,MAAM,MAAM,WAAW,UAAU;EACjC,IAAI,OAAO,QAAQ,MAAM,OAAO,QAAQ;EACxC,OAAO;CACX;CAEA,SAAS,YAAY,YAAoB;EACrC,OAAQ,QAAQ,UAAU,CAAC,CAAkB,WAAW,UAAU;CACtE;CAMA,OAAO,IAAI,MAAM,EAHb,YAAY,YAGC,GAAkB,EAC/B,IAAI,SAAS,MAAuB;EAChC,IAAI,SAAS,cAAc,OAAO;EAElC,IAAI,OAAO,SAAS,UAAU,OAAO,KAAA;EAErC,IAAI,SAAS,UAAU,SAAS,YAAY,SAAS,YAAY,OAAO,KAAA;EAIxE,OAAO,YAAY,kBAAkB,IAAI,KAAK,YAAY,IAAI,CAAC;CACnE,EACJ,CAAC;AACL"}
|
|
@@ -62,7 +62,13 @@ export declare class UnknownFilterOperatorError extends Error {
|
|
|
62
62
|
operator: string;
|
|
63
63
|
validOperators: readonly WhereFilterOp[];
|
|
64
64
|
};
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
|
+
* @param hint - One sentence naming the spelling that works, when the
|
|
67
|
+
* rejected one is recognisably another dialect's. It leads the message:
|
|
68
|
+
* the operator list is in canonical spelling, which is not what a wire
|
|
69
|
+
* string takes, so on its own it would send the caller to `is-null.null`.
|
|
70
|
+
*/
|
|
71
|
+
constructor(field: string, operator: string, hint?: string);
|
|
66
72
|
}
|
|
67
73
|
/**
|
|
68
74
|
* The query-parameter names a REST listing reads as something other than a
|