import { combineZeroClients } from './combineZeroClients' import { createUseQueryDirect } from './createUseQueryDirect' import { createZeroClientInternal, type DirectQueryAdapter, type CreateZeroClientOptions, } from './createZeroClient' import { releaseClientInstances } from './instanceRegistry' import type { GenericModels } from './types' import type { Schema as ZeroSchema } from '@rocicorp/zero' import type { AggregateSet } from 'orez-lite/aggregate' export * from './combineZeroClients' export function createZeroClientWithDirectQueries< Schema extends ZeroSchema, Models extends GenericModels, >( options: CreateZeroClientOptions ): ReturnType> { const createDirectUseQuery: DirectQueryAdapter = createUseQueryDirect return createZeroClientInternal({ ...options, createDirectUseQuery, }) } export type ZeroInstanceManifestEntry< Schema extends ZeroSchema = ZeroSchema, Models extends GenericModels = GenericModels, > = { schema: Schema queries: CreateZeroClientOptions['groupedQueries'] models: Models aggregates?: AggregateSet tables: readonly string[] syncTables: readonly string[] supportTables: readonly string[] scope: string | null defaultVisibility: ((value: string) => { column: string; value: string }) | null } type ClientFor = Entry extends ZeroInstanceManifestEntry ? ReturnType> : never type ClientMap> = { [Name in keyof Instances]: ClientFor } type ClientUnion> = ClientMap[keyof Instances] export function createZeroClients< const Instances extends Record, >(instances: Instances) { const names = Object.keys(instances) if (names.length === 0) { throw new Error('[on-zero] createZeroClients requires at least one instance') } if (names.includes('default')) { names.splice(names.indexOf('default'), 1) names.unshift('default') } const innerName = names[names.length - 1]! const clients: Record> = {} // release the complete old partition atomically before any new instance // claims namespaces that may have moved during hot reload. releaseClientInstances(names) for (const name of [...names].reverse()) { const instance = instances[name]! const options = { schema: instance.schema, models: instance.models, groupedQueries: instance.queries, instanceName: name, aggregates: instance.aggregates, } clients[name] = name === innerName ? createZeroClientInternal(options) : createZeroClientWithDirectQueries(options) } const typedClients = clients as ClientMap // names is checked non-empty above. retain the union of the concrete client // types so combineZeroClients can intersect every instance surface. const orderedClients = names.map( (name) => typedClients[name as keyof Instances] ) as unknown as [ClientUnion, ...ClientUnion[]] const providers = Object.fromEntries( names.map((name) => [name, clients[name]!.ProvideZero]) ) as { [Name in keyof Instances]: ClientMap[Name]['ProvideZero'] } return { clients: typedClients, providers, combined: combineZeroClients(...orderedClients), } }