@rocicorp/zero 0.25.10-canary.6 → 0.25.10-canary.9
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/out/zero/package.json.js +1 -1
- package/out/zero/src/adapters/prisma.d.ts +2 -0
- package/out/zero/src/adapters/prisma.d.ts.map +1 -0
- package/out/zero/src/adapters/prisma.js +6 -0
- package/out/zero/src/adapters/prisma.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +2 -0
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-react/src/zero-provider.js +1 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-server/src/adapters/drizzle.d.ts +18 -13
- package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/pg.d.ts +19 -13
- package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.d.ts +19 -13
- package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.d.ts +66 -0
- package/out/zero-server/src/adapters/prisma.d.ts.map +1 -0
- package/out/zero-server/src/adapters/prisma.js +63 -0
- package/out/zero-server/src/adapters/prisma.js.map +1 -0
- package/out/zero-solid/src/use-zero.js +1 -1
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/package.json +5 -1
package/out/zero/package.json.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../../../src/adapters/prisma.ts"],"names":[],"mappings":"AAAA,cAAc,6CAA6C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-syncer.js","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {JWTPayload} from 'jose';\nimport type {Row} from 'postgres';\nimport {\n manualSpan,\n startAsyncSpan,\n startSpan,\n} from '../../../../otel/src/span.ts';\nimport {version} from '../../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {randInt} from '../../../../shared/src/rand.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {ChangeDesiredQueriesMessage} from '../../../../zero-protocol/src/change-desired-queries.ts';\nimport type {\n InitConnectionBody,\n InitConnectionMessage,\n} from '../../../../zero-protocol/src/connect.ts';\nimport type {ErroredQuery} from '../../../../zero-protocol/src/custom-queries.ts';\nimport type {DeleteClientsMessage} from '../../../../zero-protocol/src/delete-clients.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {\n ProtocolError,\n type TransformFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport type {\n InspectUpBody,\n InspectUpMessage,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport {clampTTL, MAX_TTL_MS} from '../../../../zql/src/query/ttl.ts';\nimport {\n transformAndHashQuery,\n type TransformedAndHashed,\n} from '../../auth/read-authorizer.ts';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport type {ZeroConfig} from '../../config/zero-config.ts';\nimport type {CustomQueryTransformer} from '../../custom-queries/transform-query.ts';\nimport type {HeaderOptions} from '../../custom/fetch.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n getOrCreateUpDownCounter,\n} from '../../observability/metrics.ts';\nimport type {InspectorDelegate} from '../../server/inspector-delegate.ts';\nimport {\n getLogLevel,\n ProtocolErrorWithLevel,\n} from '../../types/error-with-level.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport {rowIDString, type RowKey} from '../../types/row-key.ts';\nimport type {ShardID} from '../../types/shards.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {ReplicaState} from '../replicator/replicator.ts';\nimport {ZERO_VERSION_COLUMN_NAME} from '../replicator/schema/replication-state.ts';\nimport type {ActivityBasedService} from '../service.ts';\nimport {\n ClientHandler,\n startPoke,\n type PatchToVersion,\n type PokeHandler,\n type RowPatch,\n} from './client-handler.ts';\nimport {ClientNotFoundError, CVRStore} from './cvr-store.ts';\nimport type {CVRUpdater} from './cvr.ts';\nimport {\n CVRConfigDrivenUpdater,\n CVRQueryDrivenUpdater,\n nextEvictionTime,\n type CVRSnapshot,\n type RowUpdate,\n} from './cvr.ts';\nimport type {DrainCoordinator} from './drain-coordinator.ts';\nimport {handleInspect} from './inspect-handler.ts';\nimport type {PipelineDriver} from './pipeline-driver.ts';\nimport {type RowChange} from './pipeline-driver.ts';\nimport {\n cmpVersions,\n EMPTY_CVR_VERSION,\n versionFromString,\n versionString,\n versionToCookie,\n type ClientQueryRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type NullableCVRVersion,\n type QueryRecord,\n type RowID,\n} from './schema/types.ts';\nimport {ResetPipelinesSignal} from './snapshotter.ts';\nimport {\n ttlClockAsNumber,\n ttlClockFromNumber,\n type TTLClock,\n} from './ttl-clock.ts';\n\nexport type TokenData = {\n readonly raw: string;\n /** @deprecated */\n readonly decoded: JWTPayload;\n};\n\nexport type SyncContext = {\n readonly clientID: string;\n readonly wsID: string;\n readonly profileID: string | null;\n readonly baseCookie: string | null;\n readonly protocolVersion: number;\n readonly schemaVersion: number | null;\n readonly tokenData: TokenData | undefined;\n readonly httpCookie: string | undefined;\n};\n\nconst tracer = trace.getTracer('view-syncer', version);\n\nconst PROTOCOL_VERSION_ATTR = 'protocol.version';\n\nexport interface ViewSyncer {\n initConnection(\n ctx: SyncContext,\n msg: InitConnectionMessage,\n ): Source<Downstream>;\n\n changeDesiredQueries(\n ctx: SyncContext,\n msg: ChangeDesiredQueriesMessage,\n ): Promise<void>;\n\n deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<void>;\n inspect(context: SyncContext, msg: InspectUpMessage): Promise<void>;\n}\n\nconst DEFAULT_KEEPALIVE_MS = 5_000;\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\ntype SetTimeout = (\n fn: (...args: unknown[]) => void,\n delay?: number,\n) => ReturnType<typeof setTimeout>;\n\n/**\n * We update the ttlClock in flush that writes to the CVR but\n * some flushes do not write to the CVR and in those cases we\n * use a timer to update the ttlClock every minute.\n */\nexport const TTL_CLOCK_INTERVAL = 60_000;\n\n/**\n * This is some extra time we delay the TTL timer to allow for some\n * slack in the timing of the timer. This is to allow multiple evictions\n * to happen in a short period of time without having to wait for the\n * next tick of the timer.\n */\nexport const TTL_TIMER_HYSTERESIS = 50; // ms\n\nexport class ViewSyncerService implements ViewSyncer, ActivityBasedService {\n readonly id: string;\n readonly #shard: ShardID;\n readonly #lc: LogContext;\n readonly #pipelines: PipelineDriver;\n readonly #stateChanges: Subscription<ReplicaState>;\n readonly #drainCoordinator: DrainCoordinator;\n readonly #keepaliveMs: number;\n readonly #slowHydrateThreshold: number;\n readonly #queryConfig: ZeroConfig['query'];\n\n userQueryURL?: string | undefined;\n userQueryHeaders?: Record<string, string> | undefined;\n\n // The ViewSyncerService is only started in response to a connection,\n // so #lastConnectTime is always initialized to now(). This is necessary\n // to handle race conditions in which, e.g. the replica is ready and the\n // CVR is accessed before the first connection sends a request.\n //\n // Note: It is fine to update this variable outside of the lock.\n #lastConnectTime = Date.now();\n\n /**\n * The TTL clock is used to determine the time at which queries are considered\n * expired.\n */\n #ttlClock: TTLClock | undefined;\n\n /**\n * The base time for the TTL clock. This is used to compute the current TTL\n * clock value. The first time a connection is made, this is set to the\n * current time. On subsequent connections, the TTL clock is computed as the\n * difference between the current time and this base time.\n *\n * Every time we write the ttlClock this is update to the current time. That\n * way we can compute how much time has passed since the last time we set the\n * ttlClock. When we set the ttlClock we just increment it by the amount of\n * time that has passed since the last time we set it.\n */\n #ttlClockBase = Date.now();\n\n /**\n * We update the ttlClock every minute to ensure that it is not too much\n * out of sync with the current time.\n */\n #ttlClockInterval: ReturnType<SetTimeout> | 0 = 0;\n\n // Note: It is okay to add/remove clients without acquiring the lock.\n readonly #clients = new Map<string, ClientHandler>();\n\n // Serialize on this lock for:\n // (1) storage or database-dependent operations\n // (2) updating member variables.\n readonly #lock = new Lock();\n readonly #cvrStore: CVRStore;\n readonly #stopped = resolver();\n readonly #initialized = resolver<'initialized'>();\n\n #cvr: CVRSnapshot | undefined;\n #pipelinesSynced = false;\n // DEPRECATED: remove `authData` in favor of forwarding\n // auth and cookie headers directly\n #authData: TokenData | undefined;\n\n #httpCookie: string | undefined;\n\n #expiredQueriesTimer: ReturnType<SetTimeout> | 0 = 0;\n readonly #setTimeout: SetTimeout;\n readonly #customQueryTransformer: CustomQueryTransformer | undefined;\n\n // Track query replacements for thrashing detection\n readonly #queryReplacements = new Map<\n string,\n {count: number; windowStart: number}\n >();\n\n readonly #activeClients = getOrCreateUpDownCounter(\n 'sync',\n 'active-clients',\n 'Number of active sync clients',\n );\n readonly #hydrations = getOrCreateCounter(\n 'sync',\n 'hydration',\n 'Number of query hydrations',\n );\n readonly #hydrationTime = getOrCreateHistogram('sync', 'hydration-time', {\n description: 'Time to hydrate a query.',\n unit: 's',\n });\n readonly #transactionAdvanceTime = getOrCreateHistogram(\n 'sync',\n 'advance-time',\n {\n description:\n 'Time to advance all queries for a given client group after applying a new transaction to the replica.',\n unit: 's',\n },\n );\n readonly #queryTransformations = getOrCreateCounter(\n 'sync',\n 'query.transformations',\n 'Number of query transformations performed',\n );\n readonly #queryTransformationTime = getOrCreateHistogram(\n 'sync',\n 'query.transformation-time',\n {\n description: 'Time to transform custom queries via API server',\n unit: 's',\n },\n );\n readonly #queryTransformationHashChanges = getOrCreateCounter(\n 'sync',\n 'query.transformation-hash-changes',\n 'Number of times query transformation hash changed',\n );\n readonly #queryTransformationNoOps = getOrCreateCounter(\n 'sync',\n 'query.transformation-no-ops',\n 'Number of times query transformation resulted in no-op (hash unchanged)',\n );\n\n readonly #inspectorDelegate: InspectorDelegate;\n\n readonly #config: NormalizedZeroConfig;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n clientGroupID: string,\n cvrDb: PostgresDB,\n upstreamDb: PostgresDB | undefined,\n pipelineDriver: PipelineDriver,\n versionChanges: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n slowHydrateThreshold: number,\n inspectorDelegate: InspectorDelegate,\n customQueryTransformer: CustomQueryTransformer | undefined,\n keepaliveMs = DEFAULT_KEEPALIVE_MS,\n setTimeoutFn: SetTimeout = setTimeout.bind(globalThis),\n ) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n this.#config = config;\n this.id = clientGroupID;\n this.#shard = shard;\n this.#queryConfig = queryConfig;\n this.#lc = lc;\n this.#pipelines = pipelineDriver;\n this.#stateChanges = versionChanges;\n this.#drainCoordinator = drainCoordinator;\n this.#keepaliveMs = keepaliveMs;\n this.#slowHydrateThreshold = slowHydrateThreshold;\n this.#inspectorDelegate = inspectorDelegate;\n this.#customQueryTransformer = customQueryTransformer;\n this.#cvrStore = new CVRStore(\n lc,\n cvrDb,\n upstreamDb,\n shard,\n taskID,\n clientGroupID,\n // On failure, cancel the #stateChanges subscription. The run()\n // loop will then await #cvrStore.flushed() which rejects if necessary.\n () => this.#stateChanges.cancel(),\n );\n this.#setTimeout = setTimeoutFn;\n\n // Wait for the first connection to init.\n this.keepalive();\n }\n\n #getHeaderOptions(forwardCookie: boolean): HeaderOptions {\n return {\n apiKey: this.#queryConfig.apiKey,\n customHeaders: this.userQueryHeaders,\n token: this.#authData?.raw,\n cookie: forwardCookie ? this.#httpCookie : undefined,\n };\n }\n\n #runInLockWithCVR(\n fn: (lc: LogContext, cvr: CVRSnapshot) => Promise<void> | void,\n ): Promise<void> {\n const rid = randomID();\n this.#lc.debug?.('about to acquire lock for cvr ', rid);\n return this.#lock.withLock(async () => {\n this.#lc.debug?.('acquired lock in #runInLockWithCVR ', rid);\n const lc = this.#lc.withContext('lock', rid);\n if (!this.#stateChanges.active) {\n // view-syncer has been shutdown. this can be a backlog of tasks\n // queued on the lock, or it can be a race condition in which a\n // client connects before the ViewSyncer has been deleted from the\n // ServiceRunner.\n this.#lc.debug?.('state changes are inactive');\n clearTimeout(this.#expiredQueriesTimer);\n throw new ProtocolErrorWithLevel({\n kind: ErrorKind.Rehome,\n message: 'Reconnect required',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n // If all clients have disconnected, cancel all pending work.\n if (await this.#checkForShutdownConditionsInLock()) {\n this.#lc.info?.(`closing clientGroupID=${this.id}`);\n this.#stateChanges.cancel(); // Note: #stateChanges.active becomes false.\n return;\n }\n if (!this.#cvr) {\n this.#lc.debug?.('loading CVR');\n this.#cvr = await this.#cvrStore.load(lc, this.#lastConnectTime);\n this.#ttlClock = this.#cvr.ttlClock;\n this.#ttlClockBase = Date.now();\n } else {\n // Make sure the CVR ttlClock is up to date.\n const now = Date.now();\n this.#cvr = {\n ...this.#cvr,\n ttlClock: this.#getTTLClock(now),\n };\n }\n\n try {\n await fn(lc, this.#cvr);\n } catch (e) {\n // Clear cached state if an error is encountered.\n this.#cvr = undefined;\n throw e;\n }\n });\n }\n\n readyState(): Promise<'initialized' | 'draining'> {\n return Promise.race([\n this.#initialized.promise,\n this.#drainCoordinator.draining,\n ]);\n }\n\n async run(): Promise<void> {\n try {\n // Wait for initialization if we need to process queries.\n // This ensures authData and cvr.clientSchema are available before\n // transforming custom queries (dependency on authData) and building\n // pipelines (dependency on cvr.clientSchema).\n if ((await this.readyState()) === 'draining') {\n this.#lc.debug?.(`draining view-syncer ${this.id} before running`);\n void this.stop();\n }\n for await (const {state} of this.#stateChanges) {\n if (this.#drainCoordinator.shouldDrain()) {\n this.#lc.debug?.(`draining view-syncer ${this.id} (elective)`);\n break;\n }\n assert(state === 'version-ready', 'state should be version-ready'); // This is the only state change used.\n\n await this.#runInLockWithCVR(async (lc, cvr) => {\n const clientSchema = must(\n cvr.clientSchema,\n 'cvr.clientSchema missing after initialization',\n );\n if (!this.#pipelines.initialized()) {\n // On the first version-ready signal, connect to the replica.\n this.#pipelines.init(clientSchema);\n }\n if (\n cvr.replicaVersion !== null &&\n cvr.version.stateVersion !== '00' &&\n this.#pipelines.replicaVersion < cvr.replicaVersion\n ) {\n const message = `Cannot sync from older replica: CVR=${\n cvr.replicaVersion\n }, DB=${this.#pipelines.replicaVersion}`;\n lc.info?.(`resetting CVR: ${message}`);\n throw new ClientNotFoundError(message);\n }\n\n if (this.#pipelinesSynced) {\n const result = await this.#advancePipelines(lc, cvr);\n if (result === 'success') {\n return;\n }\n lc.info?.(`resetting pipelines: ${result.message}`);\n this.#pipelines.reset(clientSchema);\n }\n\n // Advance the snapshot to the current version.\n const version = this.#pipelines.advanceWithoutDiff();\n const cvrVer = versionString(cvr.version);\n\n if (version < cvr.version.stateVersion) {\n lc.debug?.(`replica@${version} is behind cvr@${cvrVer}`);\n return; // Wait for the next advancement.\n }\n\n // stateVersion is at or beyond CVR version for the first time.\n lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);\n\n await this.#hydrateUnchangedQueries(lc, cvr);\n await this.#syncQueryPipelineSet(lc, cvr);\n this.#pipelinesSynced = true;\n });\n }\n\n // If this view-syncer exited due to an elective or forced drain,\n // set the next drain timeout.\n if (this.#drainCoordinator.shouldDrain()) {\n this.#drainCoordinator.drainNextIn(this.#totalHydrationTimeMs());\n }\n this.#cleanup();\n } catch (e) {\n this.#lc[getLogLevel(e)]?.(\n `stopping view-syncer ${this.id}: ${String(e)}`,\n e,\n );\n this.#cleanup(e);\n } finally {\n // Always wait for the cvrStore to flush, regardless of how the service\n // was stopped.\n await this.#cvrStore\n .flushed(this.#lc)\n .catch(e => this.#lc[getLogLevel(e)]?.(e));\n this.#lc.info?.(`view-syncer ${this.id} finished`);\n this.#stopped.resolve();\n }\n }\n\n // must be called from within #lock\n #removeExpiredQueries = async (\n lc: LogContext,\n cvr: CVRSnapshot,\n ): Promise<void> => {\n if (hasExpiredQueries(cvr)) {\n lc = lc.withContext('method', '#removeExpiredQueries');\n lc.debug?.('Queries have expired');\n // #syncQueryPipelineSet() will remove the expired queries.\n await this.#syncQueryPipelineSet(lc, cvr);\n this.#pipelinesSynced = true;\n }\n\n // Even if we have expired queries, we still need to schedule next eviction\n // since there might be inactivated queries that need to be expired queries\n // in the future.\n this.#scheduleExpireEviction(lc, cvr);\n };\n\n #totalHydrationTimeMs(): number {\n return this.#pipelines.totalHydrationTimeMs();\n }\n\n #keepAliveUntil: number = 0;\n\n /**\n * Guarantees that the ViewSyncer will remain running for at least\n * its configured `keepaliveMs`. This is called when establishing a\n * new connection to ensure that its associated ViewSyncer isn't\n * shutdown before it receives the connection.\n *\n * @return `true` if the ViewSyncer will stay alive, `false` if the\n * ViewSyncer is shutting down.\n */\n keepalive(): boolean {\n if (!this.#stateChanges.active) {\n return false;\n }\n this.#keepAliveUntil = Date.now() + this.#keepaliveMs;\n return true;\n }\n\n // oxlint-disable-next-line no-unused-private-class-members -- False positive, used in #scheduleShutdown\n #shutdownTimer: NodeJS.Timeout | null = null;\n\n #scheduleShutdown(delayMs = 0) {\n this.#shutdownTimer ??= this.#setTimeout(() => {\n this.#shutdownTimer = null;\n\n // All lock tasks check for shutdown so that queued work is immediately\n // canceled when clients disconnect. Queue an empty task to ensure that\n // this check happens.\n void this.#runInLockWithCVR(() => {}).catch(e =>\n // If an error occurs (e.g. ownership change), propagate the error\n // to the main run() loop via the #stateChanges Subscription.\n this.#stateChanges.fail(e),\n );\n }, delayMs);\n }\n\n async #checkForShutdownConditionsInLock(): Promise<boolean> {\n if (this.#clients.size > 0) {\n return false; // common case.\n }\n\n // Keep the view-syncer alive if there are pending rows being flushed.\n // It's better to do this before shutting down since it may take a\n // while, during which new connections may come in.\n await this.#cvrStore.flushed(this.#lc);\n\n if (Date.now() <= this.#keepAliveUntil) {\n this.#scheduleShutdown(this.#keepaliveMs); // check again later\n return false;\n }\n\n // If no clients have connected while waiting for the row flush, shutdown.\n return this.#clients.size === 0;\n }\n\n #deleteClientDueToDisconnect(clientID: string, client: ClientHandler) {\n // Note: It is okay to delete / cleanup clients without acquiring the lock.\n // In fact, it is important to do so in order to guarantee that idle cleanup\n // is performed in a timely manner, regardless of the amount of work\n // queued on the lock.\n const c = this.#clients.get(clientID);\n if (c === client) {\n this.#clients.delete(clientID);\n\n if (this.#clients.size === 0) {\n // It is possible to delete a client before we read the ttl clock from\n // the CVR.\n if (this.#ttlClock !== undefined) {\n this.#updateTTLClockInCVRWithoutLock(this.#lc);\n }\n this.#stopExpireTimer();\n this.#scheduleShutdown();\n }\n }\n }\n\n #stopExpireTimer() {\n this.#lc.debug?.('Stopping expired queries timer');\n clearTimeout(this.#expiredQueriesTimer);\n this.#expiredQueriesTimer = 0;\n }\n\n initConnection(\n ctx: SyncContext,\n initConnectionMessage: InitConnectionMessage,\n ): Source<Downstream> {\n this.#lc.debug?.('viewSyncer.initConnection');\n return startSpan(tracer, 'vs.initConnection', () => {\n const {\n clientID,\n profileID,\n wsID,\n baseCookie,\n schemaVersion,\n tokenData,\n httpCookie,\n protocolVersion,\n } = ctx;\n this.#authData = pickToken(this.#lc, this.#authData, tokenData);\n this.#lc.debug?.(\n `Picked auth token: ${JSON.stringify(this.#authData?.decoded)}`,\n );\n this.#httpCookie = httpCookie;\n\n // Handle custom query URL and headers\n const [, {userQueryURL, userQueryHeaders}] = initConnectionMessage;\n if (this.userQueryURL === undefined) {\n // First client in the group - store its parameters\n this.userQueryURL = userQueryURL;\n this.userQueryHeaders = userQueryHeaders;\n } else {\n // Validate that subsequent clients have compatible parameters\n if (this.userQueryURL !== userQueryURL) {\n this.#lc.warn?.(\n 'Client provided different query parameters than client group',\n {\n clientID,\n clientURL: userQueryURL,\n clientGroupURL: this.userQueryURL,\n },\n );\n }\n }\n\n const lc = this.#lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID);\n\n // Setup the downstream connection.\n const downstream = Subscription.create<Downstream>({\n cleanup: (_, err) => {\n err\n ? lc[getLogLevel(err)]?.(`client closed with error`, err)\n : lc.info?.('client closed');\n this.#deleteClientDueToDisconnect(clientID, newClient);\n this.#activeClients.add(-1, {\n [PROTOCOL_VERSION_ATTR]: protocolVersion,\n });\n },\n });\n this.#activeClients.add(1, {\n [PROTOCOL_VERSION_ATTR]: protocolVersion,\n });\n\n if (this.#clients.size === 0) {\n // First connection to this ViewSyncerService.\n\n // initConnection must be synchronous so that the downstream\n // subscription is returned immediately.\n const now = Date.now();\n this.#ttlClockBase = now;\n }\n\n const newClient = new ClientHandler(\n lc,\n this.id,\n clientID,\n wsID,\n this.#shard,\n baseCookie,\n schemaVersion,\n downstream,\n );\n this.#clients.get(clientID)?.close(`replaced by wsID: ${wsID}`);\n this.#clients.set(clientID, newClient);\n\n // Note: initConnection() must be synchronous so that `downstream` is\n // immediately returned to the caller (connection.ts). This ensures\n // that if the connection is subsequently closed, the `downstream`\n // subscription can be properly canceled even if #runInLockForClient()\n // has not had a chance to run.\n void this.#runInLockForClient(\n ctx,\n initConnectionMessage,\n async (lc, clientID, msg: InitConnectionBody, cvr) => {\n if (cvr.clientSchema === null && !msg.clientSchema) {\n throw new ProtocolErrorWithLevel({\n kind: ErrorKind.InvalidConnectionRequest,\n message:\n 'The initConnection message for a new client group must include client schema.',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n await this.#handleConfigUpdate(\n lc,\n clientID,\n msg,\n cvr,\n // Until the profileID is required in the URL, default it to\n // `cg${clientGroupID}`, as is done in the schema migration.\n // As clients update to the zero version with the profileID logic,\n // the value will be correspondingly in the CVR db.\n profileID ?? `cg${this.id}`,\n );\n // this.#authData and cvr (in particular cvr.clientSchema) have been\n // initialized, signal the run loop to run.\n this.#initialized.resolve('initialized');\n },\n newClient,\n ).catch(e => newClient.fail(e));\n\n return downstream;\n });\n }\n\n async changeDesiredQueries(\n ctx: SyncContext,\n msg: ChangeDesiredQueriesMessage,\n ): Promise<void> {\n await this.#runInLockForClient(ctx, msg, this.#handleConfigUpdate);\n }\n\n async deleteClients(\n ctx: SyncContext,\n msg: DeleteClientsMessage,\n ): Promise<void> {\n await this.#runInLockForClient(\n ctx,\n [msg[0], {deleted: msg[1]}],\n this.#handleConfigUpdate,\n );\n }\n\n #getTTLClock(now: number): TTLClock {\n // We will update ttlClock with delta from the ttlClockBase to the current time.\n const delta = now - this.#ttlClockBase;\n assert(this.#ttlClock !== undefined, 'ttlClock should be defined');\n const ttlClock = ttlClockFromNumber(\n ttlClockAsNumber(this.#ttlClock) + delta,\n );\n assert(\n ttlClockAsNumber(ttlClock) <= now,\n 'ttlClock should be less than or equal to now',\n );\n this.#ttlClock = ttlClock;\n this.#ttlClockBase = now;\n return ttlClock as TTLClock;\n }\n\n async #flushUpdater(\n lc: LogContext,\n updater: CVRUpdater,\n ): Promise<CVRSnapshot> {\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n const {cvr, flushed} = await updater.flush(\n lc,\n this.#lastConnectTime,\n now,\n ttlClock,\n );\n\n if (flushed) {\n // If the CVR was flushed, we restart the ttlClock interval.\n this.#startTTLClockInterval(lc);\n }\n\n return cvr;\n }\n\n #startTTLClockInterval(lc: LogContext): void {\n this.#stopTTLClockInterval();\n this.#ttlClockInterval = this.#setTimeout(() => {\n this.#updateTTLClockInCVRWithoutLock(lc);\n this.#startTTLClockInterval(lc);\n }, TTL_CLOCK_INTERVAL);\n }\n\n #stopTTLClockInterval(): void {\n clearTimeout(this.#ttlClockInterval);\n this.#ttlClockInterval = 0;\n }\n\n #updateTTLClockInCVRWithoutLock(lc: LogContext): void {\n lc.debug?.('Syncing ttlClock');\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n this.#cvrStore.updateTTLClock(ttlClock, now).catch(e => {\n lc.error?.('failed to update TTL clock', e);\n });\n }\n\n async #updateCVRConfig(\n lc: LogContext,\n cvr: CVRSnapshot,\n clientID: string,\n fn: (updater: CVRConfigDrivenUpdater) => PatchToVersion[],\n ): Promise<CVRSnapshot> {\n const updater = new CVRConfigDrivenUpdater(\n this.#cvrStore,\n cvr,\n this.#shard,\n );\n updater.ensureClient(clientID);\n const patches = fn(updater);\n\n this.#cvr = await this.#flushUpdater(lc, updater);\n\n if (cmpVersions(cvr.version, this.#cvr.version) < 0) {\n // Send pokes to catch up clients that are up to date.\n // (Clients that are behind the cvr.version need to be caught up in\n // #syncQueryPipelineSet(), as row data may be needed for catchup)\n const newCVR = this.#cvr;\n const pokers = startPoke(this.#getClients(cvr.version), newCVR.version);\n for (const patch of patches) {\n await pokers.addPatch(patch);\n }\n await pokers.end(newCVR.version);\n }\n\n if (this.#pipelinesSynced) {\n await this.#syncQueryPipelineSet(lc, this.#cvr);\n }\n\n return this.#cvr;\n }\n\n /**\n * Runs the given `fn` to process the `msg` from within the `#lock`,\n * optionally adding the `newClient` if supplied.\n */\n #runInLockForClient<B, M extends [cmd: string, B] = [string, B]>(\n ctx: SyncContext,\n msg: M,\n fn: (\n lc: LogContext,\n clientID: string,\n body: B,\n cvr: CVRSnapshot,\n ) => Promise<void>,\n newClient?: ClientHandler,\n ): Promise<void> {\n this.#lc.debug?.('viewSyncer.#runInLockForClient');\n const {clientID, wsID} = ctx;\n const [cmd, body] = msg;\n\n if (newClient || !this.#clients.has(clientID)) {\n this.#lastConnectTime = Date.now();\n }\n\n return startAsyncSpan(\n tracer,\n `vs.#runInLockForClient(${cmd})`,\n async () => {\n let client: ClientHandler | undefined;\n try {\n await this.#runInLockWithCVR((lc, cvr) => {\n lc = lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID)\n .withContext('cmd', cmd);\n lc.debug?.('acquired lock for cvr');\n\n client = this.#clients.get(clientID);\n if (client?.wsID !== wsID) {\n lc.debug?.('mismatched wsID', client?.wsID, wsID);\n // Only respond to messages of the currently connected client.\n // Connections may have been drained or dropped due to an error.\n return;\n }\n\n if (newClient) {\n assert(\n newClient === client,\n 'newClient must match existing client',\n );\n checkClientAndCVRVersions(client.version(), cvr.version);\n } else if (!this.#clients.has(clientID)) {\n lc.warn?.(`Processing ${cmd} before initConnection was received`);\n }\n\n lc.debug?.(cmd, body);\n return fn(lc, clientID, body, cvr);\n });\n } catch (e) {\n const lc = this.#lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID)\n .withContext('cmd', cmd);\n lc[getLogLevel(e)]?.(`closing connection with error`, e);\n if (client) {\n // Ideally, propagate the exception to the client's downstream subscription ...\n client.fail(e);\n } else {\n // unless the exception happened before the client could be looked up.\n throw e;\n }\n }\n },\n );\n }\n\n #getClients(atVersion?: CVRVersion): ClientHandler[] {\n const clients = [...this.#clients.values()];\n return atVersion\n ? clients.filter(\n c => cmpVersions(c.version() ?? EMPTY_CVR_VERSION, atVersion) === 0,\n )\n : clients;\n }\n\n // Must be called from within #lock.\n readonly #handleConfigUpdate = (\n lc: LogContext,\n clientID: string,\n\n {\n clientSchema,\n deleted,\n desiredQueriesPatch,\n activeClients,\n }: Partial<InitConnectionBody>,\n cvr: CVRSnapshot,\n profileID?: string,\n ) =>\n startAsyncSpan(tracer, 'vs.#patchQueries', async () => {\n const deletedClientIDs: string[] = [];\n const deletedClientGroupIDs: string[] = [];\n\n cvr = await this.#updateCVRConfig(lc, cvr, clientID, updater => {\n const {ttlClock} = cvr;\n const patches: PatchToVersion[] = [];\n\n if (clientSchema) {\n updater.setClientSchema(lc, clientSchema);\n }\n if (profileID) {\n updater.setProfileID(lc, profileID);\n }\n\n // Apply requested patches.\n lc.debug?.(`applying ${desiredQueriesPatch?.length} query patches`);\n if (desiredQueriesPatch?.length) {\n for (const patch of desiredQueriesPatch) {\n switch (patch.op) {\n case 'put':\n patches.push(...updater.putDesiredQueries(clientID, [patch]));\n break;\n case 'del':\n patches.push(\n ...updater.markDesiredQueriesAsInactive(\n clientID,\n [patch.hash],\n ttlClock,\n ),\n );\n break;\n case 'clear':\n patches.push(...updater.clearDesiredQueries(clientID));\n break;\n }\n }\n }\n\n const clientIDsToDelete: Set<string> = new Set();\n\n if (activeClients) {\n // We find all the clients in this client group that are not active.\n const allClientIDs = Object.keys(cvr.clients);\n const activeClientsSet = new Set(activeClients);\n for (const id of allClientIDs) {\n if (!activeClientsSet.has(id)) {\n clientIDsToDelete.add(id);\n }\n }\n }\n\n if (deleted?.clientIDs?.length) {\n for (const cid of deleted.clientIDs) {\n assert(cid !== clientID, 'cannot delete self');\n clientIDsToDelete.add(cid);\n }\n }\n\n for (const cid of clientIDsToDelete) {\n const patchesDueToClient = updater.deleteClient(cid, ttlClock);\n patches.push(...patchesDueToClient);\n deletedClientIDs.push(cid);\n }\n\n if (deleted?.clientGroupIDs?.length) {\n lc.debug?.(\n `ignoring ${deleted.clientGroupIDs.length} deprecated client group deletes`,\n );\n }\n\n return patches;\n });\n\n // Send 'deleteClients' ack to the clients.\n if (\n (deletedClientIDs.length && deleted?.clientIDs?.length) ||\n deletedClientGroupIDs.length\n ) {\n const clients = this.#getClients();\n await Promise.allSettled(\n clients.map(client =>\n client.sendDeleteClients(\n lc,\n deletedClientIDs,\n deletedClientGroupIDs,\n ),\n ),\n );\n }\n\n this.#scheduleExpireEviction(lc, cvr);\n });\n\n #scheduleExpireEviction(lc: LogContext, cvr: CVRSnapshot): void {\n const {ttlClock} = cvr;\n this.#stopExpireTimer();\n\n // first see if there is any inactive query with a ttl.\n const next = nextEvictionTime(cvr);\n\n if (next === undefined) {\n lc.debug?.('no inactive queries with ttl');\n // no inactive queries with a ttl. Cancel existing timeout if any.\n return;\n }\n\n // It is common for many queries to be evicted close to the same time, so\n // we add a small delay so we can collapse multiple evictions into a\n // single timer. However, don't add the delay if we're already at the\n // maximum timer limit, as that's not about collapsing.\n const delay = Math.max(\n TTL_TIMER_HYSTERESIS,\n Math.min(\n ttlClockAsNumber(next) -\n ttlClockAsNumber(ttlClock) +\n TTL_TIMER_HYSTERESIS,\n MAX_TTL_MS,\n ),\n );\n\n lc.debug?.('Scheduling eviction timer to run in ', delay, 'ms');\n this.#expiredQueriesTimer = this.#setTimeout(() => {\n this.#expiredQueriesTimer = 0;\n this.#runInLockWithCVR((lc, cvr) =>\n this.#removeExpiredQueries(lc, cvr),\n ).catch(e =>\n // If an error occurs (e.g. ownership change), propagate the error\n // to the main run() loop via the #stateChanges Subscription.\n this.#stateChanges.fail(e),\n );\n }, delay);\n }\n\n /**\n * Adds and hydrates pipelines for queries whose results are already\n * recorded in the CVR. Namely:\n *\n * 1. The CVR state version and database version are the same.\n * 2. The transformation hash of the queries equal those in the CVR.\n *\n * Note that by definition, only \"got\" queries can satisfy condition (2),\n * as desired queries do not have a transformation hash.\n *\n * This is an initialization step that sets up pipeline state without\n * the expensive of loading and diffing CVR row state.\n *\n * This must be called from within the #lock.\n */\n async #hydrateUnchangedQueries(lc: LogContext, cvr: CVRSnapshot) {\n assert(this.#pipelines.initialized(), 'pipelines must be initialized');\n\n const dbVersion = this.#pipelines.currentVersion();\n const cvrVersion = cvr.version;\n\n if (cvrVersion.stateVersion !== dbVersion) {\n lc.info?.(\n `CVR (${versionToCookie(cvrVersion)}) is behind db ${dbVersion}`,\n );\n return; // hydration needs to be run with the CVR updater.\n }\n\n const gotQueries = Object.entries(cvr.queries).filter(\n ([_, state]) => state.transformationHash !== undefined,\n );\n\n const customQueries: Map<string, CustomQueryRecord> = new Map();\n const otherQueries: (ClientQueryRecord | InternalQueryRecord)[] = [];\n\n for (const [, query] of gotQueries) {\n if (\n query.type !== 'internal' &&\n Object.values(query.clientState).every(\n ({inactivatedAt}) => inactivatedAt !== undefined,\n )\n ) {\n continue; // No longer desired.\n }\n\n if (query.type === 'custom') {\n customQueries.set(query.id, query);\n } else {\n otherQueries.push(query);\n }\n }\n\n const transformedQueries: TransformedAndHashed[] = [];\n if (customQueries.size > 0 && !this.#customQueryTransformer) {\n lc.warn?.(\n 'Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.',\n );\n }\n if (this.#customQueryTransformer && customQueries.size > 0) {\n // Always transform custom queries, even during initialization,\n // to ensure authorization validation with current auth context.\n const transformedCustomQueries =\n await this.#customQueryTransformer.transform(\n this.#getHeaderOptions(this.#queryConfig.forwardCookies),\n customQueries.values(),\n this.userQueryURL,\n );\n\n this.#processTransformedCustomQueries(\n lc,\n transformedCustomQueries,\n (q: TransformedAndHashed) => transformedQueries.push(q),\n customQueries,\n );\n }\n\n for (const q of otherQueries) {\n const transformed = transformAndHashQuery(\n lc,\n q.id,\n q.ast,\n must(this.#pipelines.currentPermissions()).permissions ?? {\n tables: {},\n },\n this.#authData?.decoded,\n q.type === 'internal',\n );\n if (transformed.transformationHash === q.transformationHash) {\n // only processing unchanged queries here\n transformedQueries.push(transformed);\n }\n }\n\n for (const {\n id: queryID,\n transformationHash,\n transformedAst,\n } of transformedQueries) {\n const timer = new TimeSliceTimer();\n let count = 0;\n await startAsyncSpan(\n tracer,\n 'vs.#hydrateUnchangedQueries.addQuery',\n async span => {\n span.setAttribute('queryHash', queryID);\n span.setAttribute('transformationHash', transformationHash);\n span.setAttribute('table', transformedAst.table);\n for (const change of this.#pipelines.addQuery(\n transformationHash,\n queryID,\n transformedAst,\n await timer.start(),\n )) {\n if (change === 'yield') {\n await timer.yieldProcess('yield in hydrateUnchangedQueries');\n } else {\n count++;\n }\n }\n },\n );\n\n const elapsed = timer.totalElapsed();\n this.#hydrations.add(1);\n this.#hydrationTime.record(elapsed / 1000);\n this.#addQueryMaterializationServerMetric(transformationHash, elapsed);\n lc.debug?.(`hydrated ${count} rows for ${queryID} (${elapsed} ms)`);\n }\n }\n\n #processTransformedCustomQueries(\n lc: LogContext,\n transformedCustomQueries:\n | (TransformedAndHashed | ErroredQuery)[]\n | TransformFailedBody,\n cb: (q: TransformedAndHashed) => void,\n customQueryMap: Map<string, CustomQueryRecord>,\n ): string[] {\n if ('kind' in transformedCustomQueries) {\n this.#sendQueryTransformErrorToClients(\n customQueryMap,\n transformedCustomQueries,\n );\n return transformedCustomQueries.queryIDs;\n }\n\n const appQueryErrors: ErroredQuery[] = [];\n\n for (const q of transformedCustomQueries) {\n if ('error' in q) {\n const errorMessage = `Error transforming custom query ${q.name}: ${q.error}${q.details ? ` ${JSON.stringify(q.details)}` : ''}`;\n lc.warn?.(errorMessage, q);\n appQueryErrors.push(q);\n continue;\n }\n cb(q);\n }\n\n this.#sendQueryTransformErrorToClients(customQueryMap, appQueryErrors);\n return appQueryErrors.map(q => q.id);\n }\n\n #sendQueryTransformErrorToClients(\n customQueryMap: Map<string, CustomQueryRecord>,\n errorOrErrors: ErroredQuery[] | TransformFailedBody,\n ) {\n const getAffectedClientIDs = (queryIDs: string[]): Set<string> => {\n const clientIds = new Set<string>();\n for (const queryID of queryIDs) {\n const q = customQueryMap.get(queryID);\n assert(\n q,\n `got an error for query ${queryID} that does not map back to a custom query`,\n );\n Object.keys(q.clientState).forEach(id => clientIds.add(id));\n }\n return clientIds;\n };\n\n // send the transform failed error to each affected client\n if ('queryIDs' in errorOrErrors) {\n for (const clientId of getAffectedClientIDs(errorOrErrors.queryIDs)) {\n this.#clients\n .get(clientId)\n ?.sendQueryTransformFailedError(errorOrErrors);\n }\n\n return;\n }\n\n // Group and send application errors to each affected client\n const appErrorGroups = new Map<string, ErroredQuery[]>();\n\n for (const err of errorOrErrors) {\n // Application errors need to be grouped by client\n for (const clientId of getAffectedClientIDs([err.id])) {\n const group = appErrorGroups.get(clientId) ?? [];\n group.push(err);\n appErrorGroups.set(clientId, group);\n }\n }\n\n for (const [clientId, errors] of appErrorGroups) {\n this.#clients.get(clientId)?.sendQueryTransformApplicationErrors(errors);\n }\n }\n\n #addQueryMaterializationServerMetric(\n transformationHash: string,\n elapsed: number,\n ) {\n this.#inspectorDelegate.addMetric(\n 'query-materialization-server',\n elapsed,\n transformationHash,\n );\n }\n\n /**\n * Adds and/or removes queries to/from the PipelineDriver to bring it\n * in sync with the set of queries in the CVR (both got and desired).\n * If queries are added, removed, or queried due to a new state version,\n * a new CVR version is created and pokes sent to connected clients.\n *\n * This must be called from within the #lock.\n */\n #syncQueryPipelineSet(lc: LogContext, cvr: CVRSnapshot) {\n return startAsyncSpan(tracer, 'vs.#syncQueryPipelineSet', async () => {\n assert(\n this.#pipelines.initialized(),\n 'pipelines must be initialized (syncQueryPipelineSet)',\n );\n\n const [hydratedQueries, byOriginalHash] = this.#pipelines.addedQueries();\n\n // Convert queries to their transformed ast's and hashes\n const hashToIDs = new Map<string, string[]>();\n\n if (this.#ttlClock === undefined) {\n // Get it from the CVR or initialize it to now.\n this.#ttlClock = cvr.ttlClock;\n }\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n\n // group cvr queries into:\n // 1. custom queries\n // 2. everything else\n // Handle transformation appropriately\n // Then hydrate as `serverQueries`\n const cvrQueryEntires = Object.entries(cvr.queries);\n const customQueries: Map<string, CustomQueryRecord> = new Map();\n const otherQueries: {\n id: string;\n query: ClientQueryRecord | InternalQueryRecord;\n }[] = [];\n const transformedQueries: {\n id: string;\n origQuery: QueryRecord;\n transformed: TransformedAndHashed;\n }[] = [];\n for (const [id, query] of cvrQueryEntires) {\n if (query.type === 'custom') {\n // This should always match, no?\n assert(id === query.id, 'custom query id mismatch');\n customQueries.set(id, query);\n } else {\n otherQueries.push({id, query});\n }\n }\n\n for (const {id, query: origQuery} of otherQueries) {\n // This should always match, no?\n assert(id === origQuery.id, 'query id mismatch');\n const transformed = transformAndHashQuery(\n lc,\n origQuery.id,\n origQuery.ast,\n must(this.#pipelines.currentPermissions()).permissions ?? {\n tables: {},\n },\n this.#authData?.decoded,\n origQuery.type === 'internal',\n );\n transformedQueries.push({\n id,\n origQuery,\n transformed,\n });\n }\n\n if (customQueries.size > 0 && !this.#customQueryTransformer) {\n lc.warn?.(\n 'Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.',\n );\n }\n\n let erroredQueryIDs: string[] | undefined;\n if (this.#customQueryTransformer && customQueries.size > 0) {\n // Always re-transform custom queries on client connection for security.\n // This ensures the user's API server validates authorization with the\n // current auth context.\n const transformStart = performance.now();\n let transformedCustomQueries;\n try {\n transformedCustomQueries =\n await this.#customQueryTransformer.transform(\n this.#getHeaderOptions(true),\n customQueries.values(),\n this.userQueryURL,\n );\n this.#queryTransformations.add(1, {result: 'success'});\n } catch (e) {\n this.#queryTransformations.add(1, {result: 'error'});\n throw e;\n } finally {\n const transformDuration = (performance.now() - transformStart) / 1000;\n this.#queryTransformationTime.record(transformDuration);\n }\n\n // Check if transform failed entirely (HTTP error or server-side failure).\n // This should disconnect the client and keep existing pipelines intact.\n if (\n !Array.isArray(transformedCustomQueries) &&\n transformedCustomQueries.kind === ErrorKind.TransformFailed\n ) {\n // TransformFailedBody indicates an HTTP or infrastructure error.\n // Throw to disconnect the client without modifying pipelines.\n throw new ProtocolErrorWithLevel(\n transformedCustomQueries,\n getLogLevel(transformedCustomQueries.kind),\n );\n }\n\n // Process the transformed queries and track which ones succeeded.\n const successfullyTransformed = new Map<string, TransformedAndHashed>();\n erroredQueryIDs = this.#processTransformedCustomQueries(\n lc,\n transformedCustomQueries,\n (q: TransformedAndHashed) => {\n successfullyTransformed.set(q.id, q);\n transformedQueries.push({\n id: q.id,\n origQuery: must(customQueries.get(q.id)),\n transformed: q,\n });\n },\n customQueries,\n );\n\n // Check for queries whose transformation hash changed and log for debugging.\n // The old pipelines will be automatically removed via unhydrateQueries,\n // and new pipelines will be added via addQueries.\n for (const [queryID, newTransform] of successfullyTransformed) {\n const existingTransforms = byOriginalHash.get(queryID);\n if (existingTransforms && existingTransforms.length > 0) {\n const oldHash = existingTransforms[0].transformationHash;\n const newHash = newTransform.transformationHash;\n\n if (oldHash !== newHash) {\n // Transformation changed - log and check for thrashing.\n // The unhydrateQueries mechanism below will remove the old pipeline,\n // and addQueries will add the new one.\n lc.info?.(\n `Query ${queryID} transformation changed: ${oldHash} -> ${newHash}`,\n );\n this.#checkForThrashing(queryID);\n this.#queryTransformationHashChanges.add(1);\n } else {\n // hash is the same, addQuery will no-op (no re-hydration needed)\n this.#queryTransformationNoOps.add(1);\n }\n }\n // else: new query, will be added normally\n }\n }\n\n const serverQueries = transformedQueries.map(\n ({id, origQuery, transformed}) => {\n const ids = hashToIDs.get(transformed.transformationHash);\n if (ids) {\n ids.push(id);\n } else {\n hashToIDs.set(transformed.transformationHash, [id]);\n }\n return {\n id,\n ast: transformed.transformedAst,\n transformationHash: transformed.transformationHash,\n remove: expired(ttlClock, origQuery),\n };\n },\n );\n\n const addQueries = serverQueries.filter(\n q => !q.remove && !hydratedQueries.has(q.transformationHash),\n );\n const removeQueries: {\n id: string;\n transformationHash: string | undefined;\n }[] = serverQueries.filter(q => q.remove);\n const desiredQueries = new Set(\n serverQueries.filter(q => !q.remove).map(q => q.transformationHash),\n );\n const unhydrateQueries = [...hydratedQueries].filter(\n transformationHash => !desiredQueries.has(transformationHash),\n );\n\n for (const q of addQueries) {\n const orig = cvr.queries[q.id];\n lc.debug?.(\n 'ViewSyncer adding query',\n q.ast,\n 'transformed from',\n orig.type === 'custom' ? orig.name : orig.ast,\n );\n }\n\n // These are queries we need to remove from `desired`, not `got`, because they never transformed.\n if (erroredQueryIDs) {\n // Build a set of transformation hashes that succeeded\n const successfulHashes = new Set(\n transformedQueries.map(\n ({transformed}) => transformed.transformationHash,\n ),\n );\n\n for (const queryID of erroredQueryIDs) {\n // Try to get the last known transformation hash for this query\n let lastKnownHash: string | undefined;\n\n // Check if the query exists in the CVR with a transformation hash\n const cvrQuery = cvr.queries[queryID];\n if (cvrQuery?.transformationHash) {\n lastKnownHash = cvrQuery.transformationHash;\n }\n\n // If a successfully transformed query has the same hash, we can't remove it\n // because that would remove the pipeline for the successful query\n const transformationHash =\n lastKnownHash && successfulHashes.has(lastKnownHash)\n ? undefined\n : lastKnownHash;\n\n removeQueries.push({\n id: queryID,\n transformationHash,\n });\n }\n }\n\n if (\n addQueries.length > 0 ||\n removeQueries.length > 0 ||\n unhydrateQueries.length > 0\n ) {\n await this.#addAndRemoveQueries(\n lc,\n cvr,\n addQueries,\n removeQueries,\n unhydrateQueries,\n hashToIDs,\n );\n } else {\n await this.#catchupClients(lc, cvr);\n }\n });\n }\n\n /**\n * Check if a query is being replaced too frequently (thrashing).\n * Logs a warning if the query has been replaced more than 3 times in 60 seconds.\n */\n #checkForThrashing(queryID: string) {\n const THRASH_WINDOW_MS = 60_000; // 60 seconds\n const THRASH_THRESHOLD = 3;\n const now = Date.now();\n\n let record = this.#queryReplacements.get(queryID);\n if (!record) {\n record = {count: 1, windowStart: now};\n this.#queryReplacements.set(queryID, record);\n return;\n }\n\n // If outside the time window, delete the old entry and create a new one\n if (now - record.windowStart > THRASH_WINDOW_MS) {\n this.#queryReplacements.delete(queryID);\n this.#queryReplacements.set(queryID, {count: 1, windowStart: now});\n return;\n }\n\n // Increment count within the window\n record.count++;\n\n if (record.count >= THRASH_THRESHOLD) {\n this.#lc.warn?.(\n `Query thrashing detected for query ${queryID}. ${record.count} replacements in 60s. This may indicate clients with different auth contexts connecting to the same client group.`,\n );\n }\n }\n\n // This must be called from within the #lock.\n #addAndRemoveQueries(\n lc: LogContext,\n cvr: CVRSnapshot,\n addQueries: {id: string; ast: AST; transformationHash: string}[],\n removeQueries: {id: string; transformationHash: string | undefined}[],\n unhydrateQueries: string[],\n hashToIDs: Map<string, string[]>,\n ): Promise<void> {\n return startAsyncSpan(tracer, 'vs.#addAndRemoveQueries', async () => {\n assert(\n addQueries.length > 0 ||\n removeQueries.length > 0 ||\n unhydrateQueries.length > 0,\n 'Must have queries to add or remove',\n );\n const start = performance.now();\n\n const stateVersion = this.#pipelines.currentVersion();\n lc = lc.withContext('stateVersion', stateVersion);\n lc.info?.(`hydrating ${addQueries.length} queries`);\n\n const updater = new CVRQueryDrivenUpdater(\n this.#cvrStore,\n cvr,\n stateVersion,\n this.#pipelines.replicaVersion,\n );\n\n // Note: This kicks off background PG queries for CVR data associated with the\n // executed and removed queries.\n const {newVersion, queryPatches} = updater.trackQueries(\n lc,\n addQueries,\n removeQueries,\n );\n const clients = this.#getClients();\n const pokers = startPoke(\n clients,\n newVersion,\n this.#pipelines.currentSchemaVersions(),\n );\n for (const patch of queryPatches) {\n await pokers.addPatch(patch);\n }\n\n // Removing queries is easy. The pipelines are dropped, and the CVR\n // updater handles the updates and pokes.\n for (const q of removeQueries) {\n if (q.transformationHash) {\n this.#pipelines.removeQuery(q.transformationHash);\n }\n\n // Remove per-query server metrics when query is deleted\n this.#inspectorDelegate.removeQuery(q.id);\n\n // Clean up thrashing detection for removed queries\n this.#queryReplacements.delete(q.id);\n }\n for (const hash of unhydrateQueries) {\n this.#pipelines.removeQuery(hash);\n // Remove per-query server metrics for unhydrated queries\n const ids = hashToIDs.get(hash);\n if (ids) {\n for (const id of ids) {\n this.#inspectorDelegate.removeQuery(id);\n // Clean up thrashing detection for unhydrated queries\n this.#queryReplacements.delete(id);\n }\n }\n }\n\n let totalProcessTime = 0;\n const timer = new TimeSliceTimer();\n const pipelines = this.#pipelines;\n const hydrations = this.#hydrations;\n const hydrationTime = this.#hydrationTime;\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n\n // yield at the very beginning so that the first time slice\n // is properly processed by the time-slice queue.\n await yieldProcess();\n\n function* generateRowChanges(slowHydrateThreshold: number) {\n for (const q of addQueries) {\n lc = lc\n .withContext('hash', q.id)\n .withContext('transformationHash', q.transformationHash);\n lc.debug?.(`adding pipeline for query`, q.ast);\n\n yield* pipelines.addQuery(\n q.transformationHash,\n q.id,\n q.ast,\n timer.startWithoutYielding(),\n );\n const elapsed = timer.stop();\n totalProcessTime += elapsed;\n\n self.#addQueryMaterializationServerMetric(\n q.transformationHash,\n elapsed,\n );\n\n if (elapsed > slowHydrateThreshold) {\n lc.warn?.('Slow query materialization', elapsed, q.ast);\n }\n manualSpan(tracer, 'vs.addAndConsumeQuery', elapsed, {\n hash: q.id,\n transformationHash: q.transformationHash,\n });\n }\n hydrations.add(1);\n hydrationTime.record(totalProcessTime / 1000);\n }\n // #processChanges does batched de-duping of rows. Wrap all pipelines in\n // a single generator in order to maximize de-duping.\n await this.#processChanges(\n lc,\n timer,\n generateRowChanges(this.#slowHydrateThreshold),\n updater,\n pokers,\n hashToIDs,\n );\n\n for (const patch of await updater.deleteUnreferencedRows(lc)) {\n await pokers.addPatch(patch);\n }\n\n // Commit the changes and update the CVR snapshot.\n this.#cvr = await this.#flushUpdater(lc, updater);\n\n const finalVersion = this.#cvr.version;\n\n // Before ending the poke, catch up clients that were behind the old CVR.\n await this.#catchupClients(\n lc,\n cvr,\n finalVersion,\n addQueries.map(q => q.id),\n pokers,\n );\n\n // Signal clients to commit.\n await pokers.end(finalVersion);\n\n const wallTime = performance.now() - start;\n lc.info?.(\n `finished processing queries (process: ${totalProcessTime} ms, wall: ${wallTime} ms)`,\n );\n });\n }\n\n /**\n * @param cvr The CVR to which clients should be caught up to. This does\n * not necessarily need to be the current CVR.\n * @param current The expected current CVR version. Before performing\n * catchup, the snapshot read will verify that the CVR has not been\n * concurrently modified. Note that this only needs to be done for\n * catchup because it is the only time data from the CVR DB is\n * \"exported\" without being gated by a CVR flush (which provides\n * concurrency protection in all other cases).\n *\n * If unspecified, the version of the `cvr` is used.\n * @param excludeQueryHashes Exclude patches from rows associated with\n * the specified queries.\n * @param usePokers If specified, sends pokes on existing PokeHandlers,\n * in which case the caller is responsible for sending the `pokeEnd`\n * messages. If unspecified, the pokes will be started and ended\n * using the version from the supplied `cvr`.\n */\n // Must be called within #lock\n #catchupClients(\n lc: LogContext,\n cvr: CVRSnapshot,\n current?: CVRVersion,\n excludeQueryHashes: string[] = [],\n usePokers?: PokeHandler,\n ) {\n return startAsyncSpan(tracer, 'vs.#catchupClients', async span => {\n current ??= cvr.version;\n const clients = this.#getClients();\n const pokers =\n usePokers ??\n startPoke(\n clients,\n cvr.version,\n this.#pipelines.currentSchemaVersions(),\n );\n span.setAttribute('numClients', clients.length);\n\n const catchupFrom = clients\n .map(c => c.version())\n .reduce((a, b) => (cmpVersions(a, b) < 0 ? a : b), cvr.version);\n\n // This is an AsyncGenerator which won't execute until awaited.\n const rowPatches = this.#cvrStore.catchupRowPatches(\n lc,\n catchupFrom,\n cvr,\n current,\n excludeQueryHashes,\n );\n\n // This is a plain async function that kicks off immediately.\n const configPatches = this.#cvrStore.catchupConfigPatches(\n lc,\n catchupFrom,\n cvr,\n current,\n );\n\n // await the rowPatches first so that the AsyncGenerator kicks off.\n let rowPatchCount = 0;\n for await (const rows of rowPatches) {\n for (const row of rows) {\n const {schema, table} = row;\n const rowKey = row.rowKey as RowKey;\n const toVersion = versionFromString(row.patchVersion);\n\n const id: RowID = {schema, table, rowKey};\n let patch: RowPatch;\n if (!row.refCounts) {\n patch = {type: 'row', op: 'del', id};\n } else {\n const row = must(\n this.#pipelines.getRow(table, rowKey),\n `Missing row ${table}:${stringify(rowKey)}`,\n );\n const {contents} = contentsAndVersion(row);\n patch = {type: 'row', op: 'put', id, contents};\n }\n const patchToVersion = {patch, toVersion};\n await pokers.addPatch(patchToVersion);\n rowPatchCount++;\n }\n }\n span.setAttribute('rowPatchCount', rowPatchCount);\n if (rowPatchCount) {\n lc.debug?.(`sent ${rowPatchCount} row patches`);\n }\n\n // Then await the config patches which were fetched in parallel.\n for (const patch of await configPatches) {\n await pokers.addPatch(patch);\n }\n\n if (!usePokers) {\n await pokers.end(cvr.version);\n }\n });\n }\n\n #processChanges(\n lc: LogContext,\n timer: TimeSliceTimer,\n changes: Iterable<RowChange | 'yield'>,\n updater: CVRQueryDrivenUpdater,\n pokers: PokeHandler,\n hashToIDs: Map<string, string[]>,\n ) {\n return startAsyncSpan(tracer, 'vs.#processChanges', async () => {\n const start = performance.now();\n\n const rows = new CustomKeyMap<RowID, RowUpdate>(rowIDString);\n let total = 0;\n\n const processBatch = () =>\n startAsyncSpan(tracer, 'processBatch', async () => {\n const wallElapsed = performance.now() - start;\n total += rows.size;\n lc.debug?.(\n `processing ${rows.size} (of ${total}) rows (${wallElapsed} ms)`,\n );\n const patches = await updater.received(lc, rows);\n\n for (const patch of patches) {\n await pokers.addPatch(patch);\n }\n rows.clear();\n });\n\n await startAsyncSpan(tracer, 'loopingChanges', async span => {\n for (const change of changes) {\n if (change === 'yield') {\n await timer.yieldProcess('yield in processChanges');\n continue;\n }\n const {\n type,\n queryHash: transformationHash,\n table,\n rowKey,\n row,\n } = change;\n const queryIDs = must(\n hashToIDs.get(transformationHash),\n 'could not find the original hash for the transformation hash',\n );\n const rowID: RowID = {schema: '', table, rowKey: rowKey as RowKey};\n\n let parsedRow = rows.get(rowID);\n if (!parsedRow) {\n parsedRow = {refCounts: {}};\n rows.set(rowID, parsedRow);\n }\n queryIDs.forEach(hash => (parsedRow.refCounts[hash] ??= 0));\n\n const updateVersion = (row: Row) => {\n // IVM can output multiple versions of a row as it goes through its\n // intermediate stages. Always update the version and contents;\n // the last version will reflect the final state.\n const {version, contents} = contentsAndVersion(row);\n parsedRow.version = version;\n parsedRow.contents = contents;\n };\n switch (type) {\n case 'add':\n updateVersion(row);\n queryIDs.forEach(hash => parsedRow.refCounts[hash]++);\n break;\n case 'edit':\n updateVersion(row);\n // No update to refCounts.\n break;\n case 'remove':\n queryIDs.forEach(hash => parsedRow.refCounts[hash]--);\n break;\n default:\n unreachable(type);\n }\n\n if (rows.size % CURSOR_PAGE_SIZE === 0) {\n await processBatch();\n }\n }\n if (rows.size) {\n await processBatch();\n }\n span.setAttribute('totalRows', total);\n });\n });\n }\n\n /**\n * Advance to the current snapshot of the replica and apply / send\n * changes.\n *\n * Must be called from within the #lock.\n *\n * Returns false if the advancement failed due to a schema change.\n */\n #advancePipelines(\n lc: LogContext,\n cvr: CVRSnapshot,\n ): Promise<'success' | ResetPipelinesSignal> {\n return startAsyncSpan(tracer, 'vs.#advancePipelines', async () => {\n assert(\n this.#pipelines.initialized(),\n 'pipelines must be initialized (advancePipelines',\n );\n const start = performance.now();\n\n const timer = new TimeSliceTimer();\n const {version, numChanges, changes} = this.#pipelines.advance(timer);\n lc = lc.withContext('newVersion', version);\n\n // Probably need a new updater type. CVRAdvancementUpdater?\n const updater = new CVRQueryDrivenUpdater(\n this.#cvrStore,\n cvr,\n version,\n this.#pipelines.replicaVersion,\n );\n // Only poke clients that are at the cvr.version. New clients that\n // are behind need to first be caught up when their initConnection\n // message is processed (and #syncQueryPipelines is called).\n const pokers = startPoke(\n this.#getClients(cvr.version),\n updater.updatedVersion(),\n this.#pipelines.currentSchemaVersions(),\n );\n lc.debug?.(`applying ${numChanges} to advance to ${version}`);\n const hashToIDs = createHashToIDs(cvr);\n\n try {\n await this.#processChanges(\n lc,\n await timer.start(),\n changes,\n updater,\n pokers,\n hashToIDs,\n );\n } catch (e) {\n if (e instanceof ResetPipelinesSignal) {\n await pokers.cancel();\n return e;\n }\n throw e;\n }\n\n // Commit the changes and update the CVR snapshot.\n this.#cvr = await this.#flushUpdater(lc, updater);\n const finalVersion = this.#cvr.version;\n\n // Signal clients to commit.\n await pokers.end(finalVersion);\n\n const elapsed = performance.now() - start;\n lc.info?.(\n `finished processing advancement of ${numChanges} changes (${elapsed} ms)`,\n );\n this.#transactionAdvanceTime.record(elapsed / 1000);\n return 'success';\n });\n }\n\n inspect(context: SyncContext, msg: InspectUpMessage): Promise<void> {\n return this.#runInLockForClient(context, msg, this.#handleInspect);\n }\n\n // oxlint-disable-next-line require-await\n #handleInspect = async (\n lc: LogContext,\n clientID: string,\n body: InspectUpBody,\n cvr: CVRSnapshot,\n ): Promise<void> => {\n const client = must(this.#clients.get(clientID));\n return handleInspect(\n lc,\n body,\n cvr,\n client,\n this.#inspectorDelegate,\n this.id,\n this.#cvrStore,\n this.#config,\n this.#getHeaderOptions(this.#queryConfig.forwardCookies ?? false),\n this.userQueryURL,\n this.#authData,\n );\n };\n\n stop(): Promise<void> {\n this.#lc.info?.('stopping view syncer');\n this.#initialized.reject('shut down before initialization completed');\n this.#stateChanges.cancel();\n return this.#stopped.promise;\n }\n\n #cleanup(err?: unknown) {\n this.#stopTTLClockInterval();\n this.#stopExpireTimer();\n\n this.#pipelines.destroy();\n for (const client of this.#clients.values()) {\n if (err) {\n client.fail(err);\n } else {\n client.close(`closed clientGroupID=${this.id}`);\n }\n }\n }\n\n /**\n * Test helper: Manually mark initialization as complete.\n * This should only be used in tests that don't call initConnection().\n */\n markInitialized() {\n this.#initialized.resolve('initialized');\n }\n}\n\n// Update CVR after every 10000 rows.\nconst CURSOR_PAGE_SIZE = 10000;\n\nfunction createHashToIDs(cvr: CVRSnapshot) {\n const hashToIDs = new Map<string, string[]>();\n for (const {id, transformationHash} of Object.values(cvr.queries)) {\n if (!transformationHash) {\n continue;\n }\n if (hashToIDs.has(transformationHash)) {\n must(hashToIDs.get(transformationHash)).push(id);\n } else {\n hashToIDs.set(transformationHash, [id]);\n }\n }\n return hashToIDs;\n}\n\n// A global Lock acts as a queue to run a single IVM time slice per iteration\n// of the node event loop, thus bounding I/O delay to the duration of a single\n// time slice.\n//\n// Refresher:\n// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#phases-overview\n//\n// Note that recursive use of setImmediate() (i.e. calling setImmediate() from\n// within a setImmediate() callback), results in enqueuing the latter\n// callback in the *next* event loop iteration, as documented in:\n// https://nodejs.org/api/timers.html#setimmediatecallback-args\n//\n// This effectively achieves the desired one-per-event-loop-iteration behavior.\nconst timeSliceQueue = new Lock();\n\nfunction yieldProcess() {\n return timeSliceQueue.withLock(() => new Promise(setImmediate));\n}\n\nfunction contentsAndVersion(row: Row) {\n const {[ZERO_VERSION_COLUMN_NAME]: version, ...contents} = row;\n if (typeof version !== 'string' || version.length === 0) {\n throw new Error(`Invalid _0_version in ${stringify(row)}`);\n }\n return {contents, version};\n}\n\nconst NEW_CVR_VERSION = {stateVersion: '00'};\n\nfunction checkClientAndCVRVersions(\n client: NullableCVRVersion,\n cvr: CVRVersion,\n) {\n if (\n cmpVersions(cvr, NEW_CVR_VERSION) === 0 &&\n cmpVersions(client, NEW_CVR_VERSION) > 0\n ) {\n // CVR is empty but client is not.\n throw new ClientNotFoundError('Client not found');\n }\n\n if (cmpVersions(client, cvr) > 0) {\n // Client is ahead of a non-empty CVR.\n throw new ProtocolError({\n kind: ErrorKind.InvalidConnectionRequestBaseCookie,\n message: `CVR is at version ${versionString(cvr)}`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n}\n\nexport function pickToken(\n lc: LogContext,\n previousToken: TokenData | undefined,\n newToken: TokenData | undefined,\n) {\n if (previousToken === undefined) {\n lc.debug?.(`No previous token, using new token`);\n return newToken;\n }\n\n if (newToken) {\n if (previousToken.decoded.sub !== newToken.decoded.sub) {\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'The user id in the new token does not match the previous token. Client groups are pinned to a single user.',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n if (previousToken.decoded.iat === undefined) {\n lc.debug?.(`No issued at time for the existing token, using new token`);\n // No issued at time for the existing token? We take the most recently received token.\n return newToken;\n }\n\n if (newToken.decoded.iat === undefined) {\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'The new token does not have an issued at time but the prior token does. Tokens for a client group must either all have issued at times or all not have issued at times',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n // The new token is newer, so we take it.\n if (previousToken.decoded.iat < newToken.decoded.iat) {\n lc.debug?.(`New token is newer, using it`);\n return newToken;\n }\n\n // if the new token is older or the same, we keep the existing token.\n lc.debug?.(`New token is older or the same, using existing token`);\n return previousToken;\n }\n\n // previousToken !== undefined but newToken is undefined\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'No token provided. An unauthenticated client cannot connect to an authenticated client group.',\n origin: ErrorOrigin.ZeroCache,\n });\n}\n\n/**\n * A query must be expired for all clients in order to be considered\n * expired.\n */\nfunction expired(\n ttlClock: TTLClock,\n q: InternalQueryRecord | ClientQueryRecord | CustomQueryRecord,\n): boolean {\n if (q.type === 'internal') {\n return false;\n }\n\n for (const clientState of Object.values(q.clientState)) {\n const {ttl, inactivatedAt} = clientState;\n if (inactivatedAt === undefined) {\n return false;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (\n ttlClockAsNumber(inactivatedAt) + clampedTTL >\n ttlClockAsNumber(ttlClock)\n ) {\n return false;\n }\n }\n return true;\n}\n\nfunction hasExpiredQueries(cvr: CVRSnapshot): boolean {\n const {ttlClock} = cvr;\n for (const q of Object.values(cvr.queries)) {\n if (expired(ttlClock, q)) {\n return true;\n }\n }\n return false;\n}\n\nexport class TimeSliceTimer {\n #total = 0;\n #start = 0;\n\n async start() {\n // yield at the very beginning so that the first time slice\n // is properly processed by the time-slice queue.\n await yieldProcess();\n return this.startWithoutYielding();\n }\n\n startWithoutYielding() {\n this.#total = 0;\n this.#startLap();\n return this;\n }\n\n async yieldProcess(_msgForTesting?: string) {\n this.#stopLap();\n await yieldProcess();\n this.#startLap();\n }\n\n #startLap() {\n assert(this.#start === 0, 'already running');\n this.#start = performance.now();\n }\n\n elapsedLap() {\n assert(this.#start !== 0, 'not running');\n return performance.now() - this.#start;\n }\n\n #stopLap() {\n assert(this.#start !== 0, 'not running');\n this.#total += performance.now() - this.#start;\n this.#start = 0;\n }\n\n /** @returns the total elapsed time */\n stop(): number {\n this.#stopLap();\n return this.#total;\n }\n\n /**\n * @returns the elapsed time. This can be called while the Timer is running\n * or after it has been stopped.\n */\n totalElapsed(): number {\n return this.#start === 0\n ? this.#total\n : this.#total + performance.now() - this.#start;\n }\n}\n"],"names":["ErrorKind.Rehome","ErrorOrigin.ZeroCache","version","lc","clientID","ErrorKind.InvalidConnectionRequest","cvr","ErrorKind.TransformFailed","row","ErrorKind.InvalidConnectionRequestBaseCookie","ErrorKind.Unauthorized"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"view-syncer.js","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {JWTPayload} from 'jose';\nimport type {Row} from 'postgres';\nimport {\n manualSpan,\n startAsyncSpan,\n startSpan,\n} from '../../../../otel/src/span.ts';\nimport {version} from '../../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {randInt} from '../../../../shared/src/rand.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {ChangeDesiredQueriesMessage} from '../../../../zero-protocol/src/change-desired-queries.ts';\nimport type {\n InitConnectionBody,\n InitConnectionMessage,\n} from '../../../../zero-protocol/src/connect.ts';\nimport type {ErroredQuery} from '../../../../zero-protocol/src/custom-queries.ts';\nimport type {DeleteClientsMessage} from '../../../../zero-protocol/src/delete-clients.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {\n ProtocolError,\n type TransformFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport type {\n InspectUpBody,\n InspectUpMessage,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport {clampTTL, MAX_TTL_MS} from '../../../../zql/src/query/ttl.ts';\nimport {\n transformAndHashQuery,\n type TransformedAndHashed,\n} from '../../auth/read-authorizer.ts';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport type {ZeroConfig} from '../../config/zero-config.ts';\nimport type {CustomQueryTransformer} from '../../custom-queries/transform-query.ts';\nimport type {HeaderOptions} from '../../custom/fetch.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n getOrCreateUpDownCounter,\n} from '../../observability/metrics.ts';\nimport type {InspectorDelegate} from '../../server/inspector-delegate.ts';\nimport {\n getLogLevel,\n ProtocolErrorWithLevel,\n} from '../../types/error-with-level.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport {rowIDString, type RowKey} from '../../types/row-key.ts';\nimport type {ShardID} from '../../types/shards.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {ReplicaState} from '../replicator/replicator.ts';\nimport {ZERO_VERSION_COLUMN_NAME} from '../replicator/schema/replication-state.ts';\nimport type {ActivityBasedService} from '../service.ts';\nimport {\n ClientHandler,\n startPoke,\n type PatchToVersion,\n type PokeHandler,\n type RowPatch,\n} from './client-handler.ts';\nimport {ClientNotFoundError, CVRStore} from './cvr-store.ts';\nimport type {CVRUpdater} from './cvr.ts';\nimport {\n CVRConfigDrivenUpdater,\n CVRQueryDrivenUpdater,\n nextEvictionTime,\n type CVRSnapshot,\n type RowUpdate,\n} from './cvr.ts';\nimport type {DrainCoordinator} from './drain-coordinator.ts';\nimport {handleInspect} from './inspect-handler.ts';\nimport type {PipelineDriver} from './pipeline-driver.ts';\nimport {type RowChange} from './pipeline-driver.ts';\nimport {\n cmpVersions,\n EMPTY_CVR_VERSION,\n versionFromString,\n versionString,\n versionToCookie,\n type ClientQueryRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type NullableCVRVersion,\n type QueryRecord,\n type RowID,\n} from './schema/types.ts';\nimport {ResetPipelinesSignal} from './snapshotter.ts';\nimport {\n ttlClockAsNumber,\n ttlClockFromNumber,\n type TTLClock,\n} from './ttl-clock.ts';\n\nexport type TokenData = {\n readonly raw: string;\n /** @deprecated */\n readonly decoded: JWTPayload;\n};\n\nexport type SyncContext = {\n readonly clientID: string;\n readonly wsID: string;\n readonly profileID: string | null;\n readonly baseCookie: string | null;\n readonly protocolVersion: number;\n readonly schemaVersion: number | null;\n readonly tokenData: TokenData | undefined;\n readonly httpCookie: string | undefined;\n};\n\nconst tracer = trace.getTracer('view-syncer', version);\n\nconst PROTOCOL_VERSION_ATTR = 'protocol.version';\n\nexport interface ViewSyncer {\n initConnection(\n ctx: SyncContext,\n msg: InitConnectionMessage,\n ): Source<Downstream>;\n\n changeDesiredQueries(\n ctx: SyncContext,\n msg: ChangeDesiredQueriesMessage,\n ): Promise<void>;\n\n deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<void>;\n inspect(context: SyncContext, msg: InspectUpMessage): Promise<void>;\n}\n\nconst DEFAULT_KEEPALIVE_MS = 5_000;\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\ntype SetTimeout = (\n fn: (...args: unknown[]) => void,\n delay?: number,\n) => ReturnType<typeof setTimeout>;\n\n/**\n * We update the ttlClock in flush that writes to the CVR but\n * some flushes do not write to the CVR and in those cases we\n * use a timer to update the ttlClock every minute.\n */\nexport const TTL_CLOCK_INTERVAL = 60_000;\n\n/**\n * This is some extra time we delay the TTL timer to allow for some\n * slack in the timing of the timer. This is to allow multiple evictions\n * to happen in a short period of time without having to wait for the\n * next tick of the timer.\n */\nexport const TTL_TIMER_HYSTERESIS = 50; // ms\n\nexport class ViewSyncerService implements ViewSyncer, ActivityBasedService {\n readonly id: string;\n readonly #shard: ShardID;\n readonly #lc: LogContext;\n readonly #pipelines: PipelineDriver;\n readonly #stateChanges: Subscription<ReplicaState>;\n readonly #drainCoordinator: DrainCoordinator;\n readonly #keepaliveMs: number;\n readonly #slowHydrateThreshold: number;\n readonly #queryConfig: ZeroConfig['query'];\n\n userQueryURL?: string | undefined;\n userQueryHeaders?: Record<string, string> | undefined;\n\n // The ViewSyncerService is only started in response to a connection,\n // so #lastConnectTime is always initialized to now(). This is necessary\n // to handle race conditions in which, e.g. the replica is ready and the\n // CVR is accessed before the first connection sends a request.\n //\n // Note: It is fine to update this variable outside of the lock.\n #lastConnectTime = Date.now();\n\n /**\n * The TTL clock is used to determine the time at which queries are considered\n * expired.\n */\n #ttlClock: TTLClock | undefined;\n\n /**\n * The base time for the TTL clock. This is used to compute the current TTL\n * clock value. The first time a connection is made, this is set to the\n * current time. On subsequent connections, the TTL clock is computed as the\n * difference between the current time and this base time.\n *\n * Every time we write the ttlClock this is update to the current time. That\n * way we can compute how much time has passed since the last time we set the\n * ttlClock. When we set the ttlClock we just increment it by the amount of\n * time that has passed since the last time we set it.\n */\n #ttlClockBase = Date.now();\n\n /**\n * We update the ttlClock every minute to ensure that it is not too much\n * out of sync with the current time.\n */\n #ttlClockInterval: ReturnType<SetTimeout> | 0 = 0;\n\n // Note: It is okay to add/remove clients without acquiring the lock.\n readonly #clients = new Map<string, ClientHandler>();\n\n // Serialize on this lock for:\n // (1) storage or database-dependent operations\n // (2) updating member variables.\n readonly #lock = new Lock();\n readonly #cvrStore: CVRStore;\n readonly #stopped = resolver();\n readonly #initialized = resolver<'initialized'>();\n\n #cvr: CVRSnapshot | undefined;\n #pipelinesSynced = false;\n // DEPRECATED: remove `authData` in favor of forwarding\n // auth and cookie headers directly\n #authData: TokenData | undefined;\n\n #httpCookie: string | undefined;\n\n #expiredQueriesTimer: ReturnType<SetTimeout> | 0 = 0;\n readonly #setTimeout: SetTimeout;\n readonly #customQueryTransformer: CustomQueryTransformer | undefined;\n\n // Track query replacements for thrashing detection\n readonly #queryReplacements = new Map<\n string,\n {count: number; windowStart: number}\n >();\n\n readonly #activeClients = getOrCreateUpDownCounter(\n 'sync',\n 'active-clients',\n 'Number of active sync clients',\n );\n readonly #hydrations = getOrCreateCounter(\n 'sync',\n 'hydration',\n 'Number of query hydrations',\n );\n readonly #hydrationTime = getOrCreateHistogram('sync', 'hydration-time', {\n description: 'Time to hydrate a query.',\n unit: 's',\n });\n readonly #transactionAdvanceTime = getOrCreateHistogram(\n 'sync',\n 'advance-time',\n {\n description:\n 'Time to advance all queries for a given client group after applying a new transaction to the replica.',\n unit: 's',\n },\n );\n readonly #queryTransformations = getOrCreateCounter(\n 'sync',\n 'query.transformations',\n 'Number of query transformations performed',\n );\n readonly #queryTransformationTime = getOrCreateHistogram(\n 'sync',\n 'query.transformation-time',\n {\n description: 'Time to transform custom queries via API server',\n unit: 's',\n },\n );\n readonly #queryTransformationHashChanges = getOrCreateCounter(\n 'sync',\n 'query.transformation-hash-changes',\n 'Number of times query transformation hash changed',\n );\n readonly #queryTransformationNoOps = getOrCreateCounter(\n 'sync',\n 'query.transformation-no-ops',\n 'Number of times query transformation resulted in no-op (hash unchanged)',\n );\n\n readonly #inspectorDelegate: InspectorDelegate;\n\n readonly #config: NormalizedZeroConfig;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n clientGroupID: string,\n cvrDb: PostgresDB,\n upstreamDb: PostgresDB | undefined,\n pipelineDriver: PipelineDriver,\n versionChanges: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n slowHydrateThreshold: number,\n inspectorDelegate: InspectorDelegate,\n customQueryTransformer: CustomQueryTransformer | undefined,\n keepaliveMs = DEFAULT_KEEPALIVE_MS,\n setTimeoutFn: SetTimeout = setTimeout.bind(globalThis),\n ) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n this.#config = config;\n this.id = clientGroupID;\n this.#shard = shard;\n this.#queryConfig = queryConfig;\n this.#lc = lc;\n this.#pipelines = pipelineDriver;\n this.#stateChanges = versionChanges;\n this.#drainCoordinator = drainCoordinator;\n this.#keepaliveMs = keepaliveMs;\n this.#slowHydrateThreshold = slowHydrateThreshold;\n this.#inspectorDelegate = inspectorDelegate;\n this.#customQueryTransformer = customQueryTransformer;\n this.#cvrStore = new CVRStore(\n lc,\n cvrDb,\n upstreamDb,\n shard,\n taskID,\n clientGroupID,\n // On failure, cancel the #stateChanges subscription. The run()\n // loop will then await #cvrStore.flushed() which rejects if necessary.\n () => this.#stateChanges.cancel(),\n );\n this.#setTimeout = setTimeoutFn;\n\n // Wait for the first connection to init.\n this.keepalive();\n }\n\n #getHeaderOptions(forwardCookie: boolean): HeaderOptions {\n return {\n apiKey: this.#queryConfig.apiKey,\n customHeaders: this.userQueryHeaders,\n token: this.#authData?.raw,\n cookie: forwardCookie ? this.#httpCookie : undefined,\n };\n }\n\n #runInLockWithCVR(\n fn: (lc: LogContext, cvr: CVRSnapshot) => Promise<void> | void,\n ): Promise<void> {\n const rid = randomID();\n this.#lc.debug?.('about to acquire lock for cvr ', rid);\n return this.#lock.withLock(async () => {\n this.#lc.debug?.('acquired lock in #runInLockWithCVR ', rid);\n const lc = this.#lc.withContext('lock', rid);\n if (!this.#stateChanges.active) {\n // view-syncer has been shutdown. this can be a backlog of tasks\n // queued on the lock, or it can be a race condition in which a\n // client connects before the ViewSyncer has been deleted from the\n // ServiceRunner.\n this.#lc.debug?.('state changes are inactive');\n clearTimeout(this.#expiredQueriesTimer);\n throw new ProtocolErrorWithLevel({\n kind: ErrorKind.Rehome,\n message: 'Reconnect required',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n // If all clients have disconnected, cancel all pending work.\n if (await this.#checkForShutdownConditionsInLock()) {\n this.#lc.info?.(`closing clientGroupID=${this.id}`);\n this.#stateChanges.cancel(); // Note: #stateChanges.active becomes false.\n return;\n }\n if (!this.#cvr) {\n this.#lc.debug?.('loading CVR');\n this.#cvr = await this.#cvrStore.load(lc, this.#lastConnectTime);\n this.#ttlClock = this.#cvr.ttlClock;\n this.#ttlClockBase = Date.now();\n } else {\n // Make sure the CVR ttlClock is up to date.\n const now = Date.now();\n this.#cvr = {\n ...this.#cvr,\n ttlClock: this.#getTTLClock(now),\n };\n }\n\n try {\n await fn(lc, this.#cvr);\n } catch (e) {\n // Clear cached state if an error is encountered.\n this.#cvr = undefined;\n throw e;\n }\n });\n }\n\n readyState(): Promise<'initialized' | 'draining'> {\n return Promise.race([\n this.#initialized.promise,\n this.#drainCoordinator.draining,\n ]);\n }\n\n async run(): Promise<void> {\n try {\n // Wait for initialization if we need to process queries.\n // This ensures authData and cvr.clientSchema are available before\n // transforming custom queries (dependency on authData) and building\n // pipelines (dependency on cvr.clientSchema).\n if ((await this.readyState()) === 'draining') {\n this.#lc.debug?.(`draining view-syncer ${this.id} before running`);\n void this.stop();\n }\n for await (const {state} of this.#stateChanges) {\n if (this.#drainCoordinator.shouldDrain()) {\n this.#lc.debug?.(`draining view-syncer ${this.id} (elective)`);\n break;\n }\n assert(state === 'version-ready', 'state should be version-ready'); // This is the only state change used.\n\n await this.#runInLockWithCVR(async (lc, cvr) => {\n const clientSchema = must(\n cvr.clientSchema,\n 'cvr.clientSchema missing after initialization',\n );\n if (!this.#pipelines.initialized()) {\n // On the first version-ready signal, connect to the replica.\n this.#pipelines.init(clientSchema);\n }\n if (\n cvr.replicaVersion !== null &&\n cvr.version.stateVersion !== '00' &&\n this.#pipelines.replicaVersion < cvr.replicaVersion\n ) {\n const message = `Cannot sync from older replica: CVR=${\n cvr.replicaVersion\n }, DB=${this.#pipelines.replicaVersion}`;\n lc.info?.(`resetting CVR: ${message}`);\n throw new ClientNotFoundError(message);\n }\n\n if (this.#pipelinesSynced) {\n const result = await this.#advancePipelines(lc, cvr);\n if (result === 'success') {\n return;\n }\n lc.info?.(`resetting pipelines: ${result.message}`);\n this.#pipelines.reset(clientSchema);\n }\n\n // Advance the snapshot to the current version.\n const version = this.#pipelines.advanceWithoutDiff();\n const cvrVer = versionString(cvr.version);\n\n if (version < cvr.version.stateVersion) {\n lc.debug?.(`replica@${version} is behind cvr@${cvrVer}`);\n return; // Wait for the next advancement.\n }\n\n // stateVersion is at or beyond CVR version for the first time.\n lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);\n\n await this.#hydrateUnchangedQueries(lc, cvr);\n await this.#syncQueryPipelineSet(lc, cvr);\n this.#pipelinesSynced = true;\n });\n }\n\n // If this view-syncer exited due to an elective or forced drain,\n // set the next drain timeout.\n if (this.#drainCoordinator.shouldDrain()) {\n this.#drainCoordinator.drainNextIn(this.#totalHydrationTimeMs());\n }\n this.#cleanup();\n } catch (e) {\n this.#lc[getLogLevel(e)]?.(\n `stopping view-syncer ${this.id}: ${String(e)}`,\n e,\n );\n this.#cleanup(e);\n } finally {\n // Always wait for the cvrStore to flush, regardless of how the service\n // was stopped.\n await this.#cvrStore\n .flushed(this.#lc)\n .catch(e => this.#lc[getLogLevel(e)]?.(e));\n this.#lc.info?.(`view-syncer ${this.id} finished`);\n this.#stopped.resolve();\n }\n }\n\n // must be called from within #lock\n #removeExpiredQueries = async (\n lc: LogContext,\n cvr: CVRSnapshot,\n ): Promise<void> => {\n if (hasExpiredQueries(cvr)) {\n lc = lc.withContext('method', '#removeExpiredQueries');\n lc.debug?.('Queries have expired');\n // #syncQueryPipelineSet() will remove the expired queries.\n await this.#syncQueryPipelineSet(lc, cvr);\n this.#pipelinesSynced = true;\n }\n\n // Even if we have expired queries, we still need to schedule next eviction\n // since there might be inactivated queries that need to be expired queries\n // in the future.\n this.#scheduleExpireEviction(lc, cvr);\n };\n\n #totalHydrationTimeMs(): number {\n return this.#pipelines.totalHydrationTimeMs();\n }\n\n #keepAliveUntil: number = 0;\n\n /**\n * Guarantees that the ViewSyncer will remain running for at least\n * its configured `keepaliveMs`. This is called when establishing a\n * new connection to ensure that its associated ViewSyncer isn't\n * shutdown before it receives the connection.\n *\n * @return `true` if the ViewSyncer will stay alive, `false` if the\n * ViewSyncer is shutting down.\n */\n keepalive(): boolean {\n if (!this.#stateChanges.active) {\n return false;\n }\n this.#keepAliveUntil = Date.now() + this.#keepaliveMs;\n return true;\n }\n\n // oxlint-disable-next-line no-unused-private-class-members -- False positive, used in #scheduleShutdown\n #shutdownTimer: NodeJS.Timeout | null = null;\n\n #scheduleShutdown(delayMs = 0) {\n this.#shutdownTimer ??= this.#setTimeout(() => {\n this.#shutdownTimer = null;\n\n // All lock tasks check for shutdown so that queued work is immediately\n // canceled when clients disconnect. Queue an empty task to ensure that\n // this check happens.\n void this.#runInLockWithCVR(() => {}).catch(e =>\n // If an error occurs (e.g. ownership change), propagate the error\n // to the main run() loop via the #stateChanges Subscription.\n this.#stateChanges.fail(e),\n );\n }, delayMs);\n }\n\n async #checkForShutdownConditionsInLock(): Promise<boolean> {\n if (this.#clients.size > 0) {\n return false; // common case.\n }\n\n // Keep the view-syncer alive if there are pending rows being flushed.\n // It's better to do this before shutting down since it may take a\n // while, during which new connections may come in.\n await this.#cvrStore.flushed(this.#lc);\n\n if (Date.now() <= this.#keepAliveUntil) {\n this.#scheduleShutdown(this.#keepaliveMs); // check again later\n return false;\n }\n\n // If no clients have connected while waiting for the row flush, shutdown.\n return this.#clients.size === 0;\n }\n\n #deleteClientDueToDisconnect(clientID: string, client: ClientHandler) {\n // Note: It is okay to delete / cleanup clients without acquiring the lock.\n // In fact, it is important to do so in order to guarantee that idle cleanup\n // is performed in a timely manner, regardless of the amount of work\n // queued on the lock.\n const c = this.#clients.get(clientID);\n if (c === client) {\n this.#clients.delete(clientID);\n\n if (this.#clients.size === 0) {\n // It is possible to delete a client before we read the ttl clock from\n // the CVR.\n if (this.#ttlClock !== undefined) {\n this.#updateTTLClockInCVRWithoutLock(this.#lc);\n }\n this.#stopExpireTimer();\n this.#scheduleShutdown();\n }\n }\n }\n\n #stopExpireTimer() {\n this.#lc.debug?.('Stopping expired queries timer');\n clearTimeout(this.#expiredQueriesTimer);\n this.#expiredQueriesTimer = 0;\n }\n\n initConnection(\n ctx: SyncContext,\n initConnectionMessage: InitConnectionMessage,\n ): Source<Downstream> {\n this.#lc.debug?.('viewSyncer.initConnection');\n return startSpan(tracer, 'vs.initConnection', () => {\n const {\n clientID,\n profileID,\n wsID,\n baseCookie,\n schemaVersion,\n tokenData,\n httpCookie,\n protocolVersion,\n } = ctx;\n this.#authData = pickToken(this.#lc, this.#authData, tokenData);\n this.#lc.debug?.(\n `Picked auth token: ${JSON.stringify(this.#authData?.decoded)}`,\n );\n this.#httpCookie = httpCookie;\n\n // Handle custom query URL and headers\n const [, {userQueryURL, userQueryHeaders}] = initConnectionMessage;\n if (this.userQueryURL === undefined) {\n // First client in the group - store its parameters\n this.userQueryURL = userQueryURL;\n this.userQueryHeaders = userQueryHeaders;\n } else {\n // Validate that subsequent clients have compatible parameters\n if (this.userQueryURL !== userQueryURL) {\n this.#lc.warn?.(\n 'Client provided different query parameters than client group',\n {\n clientID,\n clientURL: userQueryURL,\n clientGroupURL: this.userQueryURL,\n },\n );\n }\n }\n\n const lc = this.#lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID);\n\n // Setup the downstream connection.\n const downstream = Subscription.create<Downstream>({\n cleanup: (_, err) => {\n err\n ? lc[getLogLevel(err)]?.(`client closed with error`, err)\n : lc.info?.('client closed');\n this.#deleteClientDueToDisconnect(clientID, newClient);\n this.#activeClients.add(-1, {\n [PROTOCOL_VERSION_ATTR]: protocolVersion,\n });\n },\n });\n this.#activeClients.add(1, {\n [PROTOCOL_VERSION_ATTR]: protocolVersion,\n });\n\n if (this.#clients.size === 0) {\n // First connection to this ViewSyncerService.\n\n // initConnection must be synchronous so that the downstream\n // subscription is returned immediately.\n const now = Date.now();\n this.#ttlClockBase = now;\n }\n\n const newClient = new ClientHandler(\n lc,\n this.id,\n clientID,\n wsID,\n this.#shard,\n baseCookie,\n schemaVersion,\n downstream,\n );\n this.#clients.get(clientID)?.close(`replaced by wsID: ${wsID}`);\n this.#clients.set(clientID, newClient);\n\n // Note: initConnection() must be synchronous so that `downstream` is\n // immediately returned to the caller (connection.ts). This ensures\n // that if the connection is subsequently closed, the `downstream`\n // subscription can be properly canceled even if #runInLockForClient()\n // has not had a chance to run.\n void this.#runInLockForClient(\n ctx,\n initConnectionMessage,\n async (lc, clientID, msg: InitConnectionBody, cvr) => {\n if (cvr.clientSchema === null && !msg.clientSchema) {\n throw new ProtocolErrorWithLevel({\n kind: ErrorKind.InvalidConnectionRequest,\n message:\n 'The initConnection message for a new client group must include client schema.',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n await this.#handleConfigUpdate(\n lc,\n clientID,\n msg,\n cvr,\n // Until the profileID is required in the URL, default it to\n // `cg${clientGroupID}`, as is done in the schema migration.\n // As clients update to the zero version with the profileID logic,\n // the value will be correspondingly in the CVR db.\n profileID ?? `cg${this.id}`,\n );\n // this.#authData and cvr (in particular cvr.clientSchema) have been\n // initialized, signal the run loop to run.\n this.#initialized.resolve('initialized');\n },\n newClient,\n ).catch(e => newClient.fail(e));\n\n return downstream;\n });\n }\n\n async changeDesiredQueries(\n ctx: SyncContext,\n msg: ChangeDesiredQueriesMessage,\n ): Promise<void> {\n await this.#runInLockForClient(ctx, msg, this.#handleConfigUpdate);\n }\n\n async deleteClients(\n ctx: SyncContext,\n msg: DeleteClientsMessage,\n ): Promise<void> {\n await this.#runInLockForClient(\n ctx,\n [msg[0], {deleted: msg[1]}],\n this.#handleConfigUpdate,\n );\n }\n\n #getTTLClock(now: number): TTLClock {\n // We will update ttlClock with delta from the ttlClockBase to the current time.\n const delta = now - this.#ttlClockBase;\n assert(this.#ttlClock !== undefined, 'ttlClock should be defined');\n const ttlClock = ttlClockFromNumber(\n ttlClockAsNumber(this.#ttlClock) + delta,\n );\n assert(\n ttlClockAsNumber(ttlClock) <= now,\n 'ttlClock should be less than or equal to now',\n );\n this.#ttlClock = ttlClock;\n this.#ttlClockBase = now;\n return ttlClock as TTLClock;\n }\n\n async #flushUpdater(\n lc: LogContext,\n updater: CVRUpdater,\n ): Promise<CVRSnapshot> {\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n const {cvr, flushed} = await updater.flush(\n lc,\n this.#lastConnectTime,\n now,\n ttlClock,\n );\n\n if (flushed) {\n // If the CVR was flushed, we restart the ttlClock interval.\n this.#startTTLClockInterval(lc);\n }\n\n return cvr;\n }\n\n #startTTLClockInterval(lc: LogContext): void {\n this.#stopTTLClockInterval();\n this.#ttlClockInterval = this.#setTimeout(() => {\n this.#updateTTLClockInCVRWithoutLock(lc);\n this.#startTTLClockInterval(lc);\n }, TTL_CLOCK_INTERVAL);\n }\n\n #stopTTLClockInterval(): void {\n clearTimeout(this.#ttlClockInterval);\n this.#ttlClockInterval = 0;\n }\n\n #updateTTLClockInCVRWithoutLock(lc: LogContext): void {\n lc.debug?.('Syncing ttlClock');\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n this.#cvrStore.updateTTLClock(ttlClock, now).catch(e => {\n lc.error?.('failed to update TTL clock', e);\n });\n }\n\n async #updateCVRConfig(\n lc: LogContext,\n cvr: CVRSnapshot,\n clientID: string,\n fn: (updater: CVRConfigDrivenUpdater) => PatchToVersion[],\n ): Promise<CVRSnapshot> {\n const updater = new CVRConfigDrivenUpdater(\n this.#cvrStore,\n cvr,\n this.#shard,\n );\n updater.ensureClient(clientID);\n const patches = fn(updater);\n\n this.#cvr = await this.#flushUpdater(lc, updater);\n\n if (cmpVersions(cvr.version, this.#cvr.version) < 0) {\n // Send pokes to catch up clients that are up to date.\n // (Clients that are behind the cvr.version need to be caught up in\n // #syncQueryPipelineSet(), as row data may be needed for catchup)\n const newCVR = this.#cvr;\n const pokers = startPoke(this.#getClients(cvr.version), newCVR.version);\n for (const patch of patches) {\n await pokers.addPatch(patch);\n }\n await pokers.end(newCVR.version);\n }\n\n if (this.#pipelinesSynced) {\n await this.#syncQueryPipelineSet(lc, this.#cvr);\n }\n\n return this.#cvr;\n }\n\n /**\n * Runs the given `fn` to process the `msg` from within the `#lock`,\n * optionally adding the `newClient` if supplied.\n */\n #runInLockForClient<B, M extends [cmd: string, B] = [string, B]>(\n ctx: SyncContext,\n msg: M,\n fn: (\n lc: LogContext,\n clientID: string,\n body: B,\n cvr: CVRSnapshot,\n ) => Promise<void>,\n newClient?: ClientHandler,\n ): Promise<void> {\n this.#lc.debug?.('viewSyncer.#runInLockForClient');\n const {clientID, wsID} = ctx;\n const [cmd, body] = msg;\n\n if (newClient || !this.#clients.has(clientID)) {\n this.#lastConnectTime = Date.now();\n }\n\n return startAsyncSpan(\n tracer,\n `vs.#runInLockForClient(${cmd})`,\n async () => {\n let client: ClientHandler | undefined;\n try {\n await this.#runInLockWithCVR((lc, cvr) => {\n lc = lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID)\n .withContext('cmd', cmd);\n lc.debug?.('acquired lock for cvr');\n\n client = this.#clients.get(clientID);\n if (client?.wsID !== wsID) {\n lc.debug?.('mismatched wsID', client?.wsID, wsID);\n // Only respond to messages of the currently connected client.\n // Connections may have been drained or dropped due to an error.\n return;\n }\n\n if (newClient) {\n assert(\n newClient === client,\n 'newClient must match existing client',\n );\n checkClientAndCVRVersions(client.version(), cvr.version);\n } else if (!this.#clients.has(clientID)) {\n lc.warn?.(`Processing ${cmd} before initConnection was received`);\n }\n\n lc.debug?.(cmd, body);\n return fn(lc, clientID, body, cvr);\n });\n } catch (e) {\n const lc = this.#lc\n .withContext('clientID', clientID)\n .withContext('wsID', wsID)\n .withContext('cmd', cmd);\n lc[getLogLevel(e)]?.(`closing connection with error`, e);\n if (client) {\n // Ideally, propagate the exception to the client's downstream subscription ...\n client.fail(e);\n } else {\n // unless the exception happened before the client could be looked up.\n throw e;\n }\n }\n },\n );\n }\n\n #getClients(atVersion?: CVRVersion): ClientHandler[] {\n const clients = [...this.#clients.values()];\n return atVersion\n ? clients.filter(\n c => cmpVersions(c.version() ?? EMPTY_CVR_VERSION, atVersion) === 0,\n )\n : clients;\n }\n\n // Must be called from within #lock.\n readonly #handleConfigUpdate = (\n lc: LogContext,\n clientID: string,\n\n {\n clientSchema,\n deleted,\n desiredQueriesPatch,\n activeClients,\n }: Partial<InitConnectionBody>,\n cvr: CVRSnapshot,\n profileID?: string,\n ) =>\n startAsyncSpan(tracer, 'vs.#patchQueries', async () => {\n const deletedClientIDs: string[] = [];\n const deletedClientGroupIDs: string[] = [];\n\n cvr = await this.#updateCVRConfig(lc, cvr, clientID, updater => {\n const {ttlClock} = cvr;\n const patches: PatchToVersion[] = [];\n\n if (clientSchema) {\n updater.setClientSchema(lc, clientSchema);\n }\n if (profileID) {\n updater.setProfileID(lc, profileID);\n }\n\n // Apply requested patches.\n lc.debug?.(`applying ${desiredQueriesPatch?.length} query patches`);\n if (desiredQueriesPatch?.length) {\n for (const patch of desiredQueriesPatch) {\n switch (patch.op) {\n case 'put':\n patches.push(...updater.putDesiredQueries(clientID, [patch]));\n break;\n case 'del':\n patches.push(\n ...updater.markDesiredQueriesAsInactive(\n clientID,\n [patch.hash],\n ttlClock,\n ),\n );\n break;\n case 'clear':\n patches.push(...updater.clearDesiredQueries(clientID));\n break;\n }\n }\n }\n\n const clientIDsToDelete: Set<string> = new Set();\n\n if (activeClients) {\n // We find all the clients in this client group that are not active.\n const allClientIDs = Object.keys(cvr.clients);\n const activeClientsSet = new Set(activeClients);\n for (const id of allClientIDs) {\n if (!activeClientsSet.has(id)) {\n clientIDsToDelete.add(id);\n }\n }\n }\n\n if (deleted?.clientIDs?.length) {\n for (const cid of deleted.clientIDs) {\n assert(cid !== clientID, 'cannot delete self');\n clientIDsToDelete.add(cid);\n }\n }\n\n for (const cid of clientIDsToDelete) {\n const patchesDueToClient = updater.deleteClient(cid, ttlClock);\n patches.push(...patchesDueToClient);\n deletedClientIDs.push(cid);\n }\n\n if (deleted?.clientGroupIDs?.length) {\n lc.debug?.(\n `ignoring ${deleted.clientGroupIDs.length} deprecated client group deletes`,\n );\n }\n\n return patches;\n });\n\n // Send 'deleteClients' ack to the clients.\n if (\n (deletedClientIDs.length && deleted?.clientIDs?.length) ||\n deletedClientGroupIDs.length\n ) {\n const clients = this.#getClients();\n await Promise.allSettled(\n clients.map(client =>\n client.sendDeleteClients(\n lc,\n deletedClientIDs,\n deletedClientGroupIDs,\n ),\n ),\n );\n }\n\n this.#scheduleExpireEviction(lc, cvr);\n });\n\n #scheduleExpireEviction(lc: LogContext, cvr: CVRSnapshot): void {\n const {ttlClock} = cvr;\n this.#stopExpireTimer();\n\n // first see if there is any inactive query with a ttl.\n const next = nextEvictionTime(cvr);\n\n if (next === undefined) {\n lc.debug?.('no inactive queries with ttl');\n // no inactive queries with a ttl. Cancel existing timeout if any.\n return;\n }\n\n // It is common for many queries to be evicted close to the same time, so\n // we add a small delay so we can collapse multiple evictions into a\n // single timer. However, don't add the delay if we're already at the\n // maximum timer limit, as that's not about collapsing.\n const delay = Math.max(\n TTL_TIMER_HYSTERESIS,\n Math.min(\n ttlClockAsNumber(next) -\n ttlClockAsNumber(ttlClock) +\n TTL_TIMER_HYSTERESIS,\n MAX_TTL_MS,\n ),\n );\n\n lc.debug?.('Scheduling eviction timer to run in ', delay, 'ms');\n this.#expiredQueriesTimer = this.#setTimeout(() => {\n this.#expiredQueriesTimer = 0;\n this.#runInLockWithCVR((lc, cvr) =>\n this.#removeExpiredQueries(lc, cvr),\n ).catch(e =>\n // If an error occurs (e.g. ownership change), propagate the error\n // to the main run() loop via the #stateChanges Subscription.\n this.#stateChanges.fail(e),\n );\n }, delay);\n }\n\n /**\n * Adds and hydrates pipelines for queries whose results are already\n * recorded in the CVR. Namely:\n *\n * 1. The CVR state version and database version are the same.\n * 2. The transformation hash of the queries equal those in the CVR.\n *\n * Note that by definition, only \"got\" queries can satisfy condition (2),\n * as desired queries do not have a transformation hash.\n *\n * This is an initialization step that sets up pipeline state without\n * the expensive of loading and diffing CVR row state.\n *\n * This must be called from within the #lock.\n */\n async #hydrateUnchangedQueries(lc: LogContext, cvr: CVRSnapshot) {\n assert(this.#pipelines.initialized(), 'pipelines must be initialized');\n\n const dbVersion = this.#pipelines.currentVersion();\n const cvrVersion = cvr.version;\n\n if (cvrVersion.stateVersion !== dbVersion) {\n lc.info?.(\n `CVR (${versionToCookie(cvrVersion)}) is behind db ${dbVersion}`,\n );\n return; // hydration needs to be run with the CVR updater.\n }\n\n const gotQueries = Object.entries(cvr.queries).filter(\n ([_, state]) => state.transformationHash !== undefined,\n );\n\n const customQueries: Map<string, CustomQueryRecord> = new Map();\n const otherQueries: (ClientQueryRecord | InternalQueryRecord)[] = [];\n\n for (const [, query] of gotQueries) {\n if (\n query.type !== 'internal' &&\n Object.values(query.clientState).every(\n ({inactivatedAt}) => inactivatedAt !== undefined,\n )\n ) {\n continue; // No longer desired.\n }\n\n if (query.type === 'custom') {\n customQueries.set(query.id, query);\n } else {\n otherQueries.push(query);\n }\n }\n\n const transformedQueries: TransformedAndHashed[] = [];\n if (customQueries.size > 0 && !this.#customQueryTransformer) {\n lc.warn?.(\n 'Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.',\n );\n }\n if (this.#customQueryTransformer && customQueries.size > 0) {\n // Always transform custom queries, even during initialization,\n // to ensure authorization validation with current auth context.\n const transformedCustomQueries =\n await this.#customQueryTransformer.transform(\n this.#getHeaderOptions(this.#queryConfig.forwardCookies),\n customQueries.values(),\n this.userQueryURL,\n );\n\n this.#processTransformedCustomQueries(\n lc,\n transformedCustomQueries,\n (q: TransformedAndHashed) => transformedQueries.push(q),\n customQueries,\n );\n }\n\n for (const q of otherQueries) {\n const transformed = transformAndHashQuery(\n lc,\n q.id,\n q.ast,\n must(this.#pipelines.currentPermissions()).permissions ?? {\n tables: {},\n },\n this.#authData?.decoded,\n q.type === 'internal',\n );\n if (transformed.transformationHash === q.transformationHash) {\n // only processing unchanged queries here\n transformedQueries.push(transformed);\n }\n }\n\n for (const {\n id: queryID,\n transformationHash,\n transformedAst,\n } of transformedQueries) {\n const timer = new TimeSliceTimer();\n let count = 0;\n await startAsyncSpan(\n tracer,\n 'vs.#hydrateUnchangedQueries.addQuery',\n async span => {\n span.setAttribute('queryHash', queryID);\n span.setAttribute('transformationHash', transformationHash);\n span.setAttribute('table', transformedAst.table);\n for (const change of this.#pipelines.addQuery(\n transformationHash,\n queryID,\n transformedAst,\n await timer.start(),\n )) {\n if (change === 'yield') {\n await timer.yieldProcess('yield in hydrateUnchangedQueries');\n } else {\n count++;\n }\n }\n },\n );\n\n const elapsed = timer.totalElapsed();\n this.#hydrations.add(1);\n this.#hydrationTime.record(elapsed / 1000);\n this.#addQueryMaterializationServerMetric(transformationHash, elapsed);\n lc.debug?.(`hydrated ${count} rows for ${queryID} (${elapsed} ms)`);\n }\n }\n\n #processTransformedCustomQueries(\n lc: LogContext,\n transformedCustomQueries:\n | (TransformedAndHashed | ErroredQuery)[]\n | TransformFailedBody,\n cb: (q: TransformedAndHashed) => void,\n customQueryMap: Map<string, CustomQueryRecord>,\n ): string[] {\n if ('kind' in transformedCustomQueries) {\n this.#sendQueryTransformErrorToClients(\n customQueryMap,\n transformedCustomQueries,\n );\n return transformedCustomQueries.queryIDs;\n }\n\n const appQueryErrors: ErroredQuery[] = [];\n\n for (const q of transformedCustomQueries) {\n if ('error' in q) {\n const errorMessage = `Error transforming custom query ${q.name}: ${q.error}${q.details ? ` ${JSON.stringify(q.details)}` : ''}`;\n lc.warn?.(errorMessage, q);\n appQueryErrors.push(q);\n continue;\n }\n cb(q);\n }\n\n this.#sendQueryTransformErrorToClients(customQueryMap, appQueryErrors);\n return appQueryErrors.map(q => q.id);\n }\n\n #sendQueryTransformErrorToClients(\n customQueryMap: Map<string, CustomQueryRecord>,\n errorOrErrors: ErroredQuery[] | TransformFailedBody,\n ) {\n const getAffectedClientIDs = (queryIDs: string[]): Set<string> => {\n const clientIds = new Set<string>();\n for (const queryID of queryIDs) {\n const q = customQueryMap.get(queryID);\n assert(\n q,\n `got an error for query ${queryID} that does not map back to a custom query`,\n );\n Object.keys(q.clientState).forEach(id => clientIds.add(id));\n }\n return clientIds;\n };\n\n // send the transform failed error to each affected client\n if ('queryIDs' in errorOrErrors) {\n for (const clientId of getAffectedClientIDs(errorOrErrors.queryIDs)) {\n this.#clients\n .get(clientId)\n ?.sendQueryTransformFailedError(errorOrErrors);\n }\n\n return;\n }\n\n // Group and send application errors to each affected client\n const appErrorGroups = new Map<string, ErroredQuery[]>();\n\n for (const err of errorOrErrors) {\n // Application errors need to be grouped by client\n for (const clientId of getAffectedClientIDs([err.id])) {\n const group = appErrorGroups.get(clientId) ?? [];\n group.push(err);\n appErrorGroups.set(clientId, group);\n }\n }\n\n for (const [clientId, errors] of appErrorGroups) {\n this.#clients.get(clientId)?.sendQueryTransformApplicationErrors(errors);\n }\n }\n\n #addQueryMaterializationServerMetric(\n transformationHash: string,\n elapsed: number,\n ) {\n this.#inspectorDelegate.addMetric(\n 'query-materialization-server',\n elapsed,\n transformationHash,\n );\n }\n\n /**\n * Adds and/or removes queries to/from the PipelineDriver to bring it\n * in sync with the set of queries in the CVR (both got and desired).\n * If queries are added, removed, or queried due to a new state version,\n * a new CVR version is created and pokes sent to connected clients.\n *\n * This must be called from within the #lock.\n */\n #syncQueryPipelineSet(lc: LogContext, cvr: CVRSnapshot) {\n return startAsyncSpan(tracer, 'vs.#syncQueryPipelineSet', async () => {\n assert(\n this.#pipelines.initialized(),\n 'pipelines must be initialized (syncQueryPipelineSet)',\n );\n\n const [hydratedQueries, byOriginalHash] = this.#pipelines.addedQueries();\n\n // Convert queries to their transformed ast's and hashes\n const hashToIDs = new Map<string, string[]>();\n\n if (this.#ttlClock === undefined) {\n // Get it from the CVR or initialize it to now.\n this.#ttlClock = cvr.ttlClock;\n }\n const now = Date.now();\n const ttlClock = this.#getTTLClock(now);\n\n // group cvr queries into:\n // 1. custom queries\n // 2. everything else\n // Handle transformation appropriately\n // Then hydrate as `serverQueries`\n const cvrQueryEntires = Object.entries(cvr.queries);\n const customQueries: Map<string, CustomQueryRecord> = new Map();\n const otherQueries: {\n id: string;\n query: ClientQueryRecord | InternalQueryRecord;\n }[] = [];\n const transformedQueries: {\n id: string;\n origQuery: QueryRecord;\n transformed: TransformedAndHashed;\n }[] = [];\n for (const [id, query] of cvrQueryEntires) {\n if (query.type === 'custom') {\n // This should always match, no?\n assert(id === query.id, 'custom query id mismatch');\n customQueries.set(id, query);\n } else {\n otherQueries.push({id, query});\n }\n }\n\n for (const {id, query: origQuery} of otherQueries) {\n // This should always match, no?\n assert(id === origQuery.id, 'query id mismatch');\n const transformed = transformAndHashQuery(\n lc,\n origQuery.id,\n origQuery.ast,\n must(this.#pipelines.currentPermissions()).permissions ?? {\n tables: {},\n },\n this.#authData?.decoded,\n origQuery.type === 'internal',\n );\n transformedQueries.push({\n id,\n origQuery,\n transformed,\n });\n }\n\n if (customQueries.size > 0 && !this.#customQueryTransformer) {\n lc.warn?.(\n 'Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.',\n );\n }\n\n let erroredQueryIDs: string[] | undefined;\n if (this.#customQueryTransformer && customQueries.size > 0) {\n // Always re-transform custom queries on client connection for security.\n // This ensures the user's API server validates authorization with the\n // current auth context.\n const transformStart = performance.now();\n let transformedCustomQueries;\n try {\n transformedCustomQueries =\n await this.#customQueryTransformer.transform(\n this.#getHeaderOptions(true),\n customQueries.values(),\n this.userQueryURL,\n );\n this.#queryTransformations.add(1, {result: 'success'});\n } catch (e) {\n this.#queryTransformations.add(1, {result: 'error'});\n throw e;\n } finally {\n const transformDuration = (performance.now() - transformStart) / 1000;\n this.#queryTransformationTime.record(transformDuration);\n }\n\n // Check if transform failed entirely (HTTP error or server-side failure).\n // This should disconnect the client and keep existing pipelines intact.\n if (\n !Array.isArray(transformedCustomQueries) &&\n transformedCustomQueries.kind === ErrorKind.TransformFailed\n ) {\n // TransformFailedBody indicates an HTTP or infrastructure error.\n // Throw to disconnect the client without modifying pipelines.\n throw new ProtocolErrorWithLevel(\n transformedCustomQueries,\n getLogLevel(transformedCustomQueries.kind),\n );\n }\n\n // Process the transformed queries and track which ones succeeded.\n const successfullyTransformed = new Map<string, TransformedAndHashed>();\n erroredQueryIDs = this.#processTransformedCustomQueries(\n lc,\n transformedCustomQueries,\n (q: TransformedAndHashed) => {\n successfullyTransformed.set(q.id, q);\n transformedQueries.push({\n id: q.id,\n origQuery: must(customQueries.get(q.id)),\n transformed: q,\n });\n },\n customQueries,\n );\n\n // Check for queries whose transformation hash changed and log for debugging.\n // The old pipelines will be automatically removed via unhydrateQueries,\n // and new pipelines will be added via addQueries.\n for (const [queryID, newTransform] of successfullyTransformed) {\n const existingTransforms = byOriginalHash.get(queryID);\n if (existingTransforms && existingTransforms.length > 0) {\n const oldHash = existingTransforms[0].transformationHash;\n const newHash = newTransform.transformationHash;\n\n if (oldHash !== newHash) {\n // Transformation changed - log and check for thrashing.\n // The unhydrateQueries mechanism below will remove the old pipeline,\n // and addQueries will add the new one.\n lc.info?.(\n `Query ${queryID} transformation changed: ${oldHash} -> ${newHash}`,\n );\n this.#checkForThrashing(queryID);\n this.#queryTransformationHashChanges.add(1);\n } else {\n // hash is the same, addQuery will no-op (no re-hydration needed)\n this.#queryTransformationNoOps.add(1);\n }\n }\n // else: new query, will be added normally\n }\n }\n\n const serverQueries = transformedQueries.map(\n ({id, origQuery, transformed}) => {\n const ids = hashToIDs.get(transformed.transformationHash);\n if (ids) {\n ids.push(id);\n } else {\n hashToIDs.set(transformed.transformationHash, [id]);\n }\n return {\n id,\n ast: transformed.transformedAst,\n transformationHash: transformed.transformationHash,\n remove: expired(ttlClock, origQuery),\n };\n },\n );\n\n const addQueries = serverQueries.filter(\n q => !q.remove && !hydratedQueries.has(q.transformationHash),\n );\n const removeQueries: {\n id: string;\n transformationHash: string | undefined;\n }[] = serverQueries.filter(q => q.remove);\n const desiredQueries = new Set(\n serverQueries.filter(q => !q.remove).map(q => q.transformationHash),\n );\n const unhydrateQueries = [...hydratedQueries].filter(\n transformationHash => !desiredQueries.has(transformationHash),\n );\n\n for (const q of addQueries) {\n const orig = cvr.queries[q.id];\n lc.debug?.(\n 'ViewSyncer adding query',\n q.ast,\n 'transformed from',\n orig.type === 'custom' ? orig.name : orig.ast,\n );\n }\n\n // These are queries we need to remove from `desired`, not `got`, because they never transformed.\n if (erroredQueryIDs) {\n // Build a set of transformation hashes that succeeded\n const successfulHashes = new Set(\n transformedQueries.map(\n ({transformed}) => transformed.transformationHash,\n ),\n );\n\n for (const queryID of erroredQueryIDs) {\n // Try to get the last known transformation hash for this query\n let lastKnownHash: string | undefined;\n\n // Check if the query exists in the CVR with a transformation hash\n const cvrQuery = cvr.queries[queryID];\n if (cvrQuery?.transformationHash) {\n lastKnownHash = cvrQuery.transformationHash;\n }\n\n // If a successfully transformed query has the same hash, we can't remove it\n // because that would remove the pipeline for the successful query\n const transformationHash =\n lastKnownHash && successfulHashes.has(lastKnownHash)\n ? undefined\n : lastKnownHash;\n\n removeQueries.push({\n id: queryID,\n transformationHash,\n });\n }\n }\n\n if (\n addQueries.length > 0 ||\n removeQueries.length > 0 ||\n unhydrateQueries.length > 0\n ) {\n await this.#addAndRemoveQueries(\n lc,\n cvr,\n addQueries,\n removeQueries,\n unhydrateQueries,\n hashToIDs,\n );\n } else {\n await this.#catchupClients(lc, cvr);\n }\n });\n }\n\n /**\n * Check if a query is being replaced too frequently (thrashing).\n * Logs a warning if the query has been replaced more than 3 times in 60 seconds.\n */\n #checkForThrashing(queryID: string) {\n const THRASH_WINDOW_MS = 60_000; // 60 seconds\n const THRASH_THRESHOLD = 3;\n const now = Date.now();\n\n let record = this.#queryReplacements.get(queryID);\n if (!record) {\n record = {count: 1, windowStart: now};\n this.#queryReplacements.set(queryID, record);\n return;\n }\n\n // If outside the time window, delete the old entry and create a new one\n if (now - record.windowStart > THRASH_WINDOW_MS) {\n this.#queryReplacements.delete(queryID);\n this.#queryReplacements.set(queryID, {count: 1, windowStart: now});\n return;\n }\n\n // Increment count within the window\n record.count++;\n\n if (record.count >= THRASH_THRESHOLD) {\n this.#lc.warn?.(\n `Query thrashing detected for query ${queryID}. ${record.count} replacements in 60s. This may indicate clients with different auth contexts connecting to the same client group.`,\n );\n }\n }\n\n // This must be called from within the #lock.\n #addAndRemoveQueries(\n lc: LogContext,\n cvr: CVRSnapshot,\n addQueries: {id: string; ast: AST; transformationHash: string}[],\n removeQueries: {id: string; transformationHash: string | undefined}[],\n unhydrateQueries: string[],\n hashToIDs: Map<string, string[]>,\n ): Promise<void> {\n return startAsyncSpan(tracer, 'vs.#addAndRemoveQueries', async () => {\n assert(\n addQueries.length > 0 ||\n removeQueries.length > 0 ||\n unhydrateQueries.length > 0,\n 'Must have queries to add or remove',\n );\n const start = performance.now();\n\n const stateVersion = this.#pipelines.currentVersion();\n lc = lc.withContext('stateVersion', stateVersion);\n lc.info?.(`hydrating ${addQueries.length} queries`);\n\n const updater = new CVRQueryDrivenUpdater(\n this.#cvrStore,\n cvr,\n stateVersion,\n this.#pipelines.replicaVersion,\n );\n\n // Note: This kicks off background PG queries for CVR data associated with the\n // executed and removed queries.\n const {newVersion, queryPatches} = updater.trackQueries(\n lc,\n addQueries,\n removeQueries,\n );\n const clients = this.#getClients();\n const pokers = startPoke(\n clients,\n newVersion,\n this.#pipelines.currentSchemaVersions(),\n );\n for (const patch of queryPatches) {\n await pokers.addPatch(patch);\n }\n\n // Removing queries is easy. The pipelines are dropped, and the CVR\n // updater handles the updates and pokes.\n for (const q of removeQueries) {\n if (q.transformationHash) {\n this.#pipelines.removeQuery(q.transformationHash);\n }\n\n // Remove per-query server metrics when query is deleted\n this.#inspectorDelegate.removeQuery(q.id);\n\n // Clean up thrashing detection for removed queries\n this.#queryReplacements.delete(q.id);\n }\n for (const hash of unhydrateQueries) {\n this.#pipelines.removeQuery(hash);\n // Remove per-query server metrics for unhydrated queries\n const ids = hashToIDs.get(hash);\n if (ids) {\n for (const id of ids) {\n this.#inspectorDelegate.removeQuery(id);\n // Clean up thrashing detection for unhydrated queries\n this.#queryReplacements.delete(id);\n }\n }\n }\n\n let totalProcessTime = 0;\n const timer = new TimeSliceTimer();\n const pipelines = this.#pipelines;\n const hydrations = this.#hydrations;\n const hydrationTime = this.#hydrationTime;\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n\n // yield at the very beginning so that the first time slice\n // is properly processed by the time-slice queue.\n await yieldProcess();\n\n function* generateRowChanges(slowHydrateThreshold: number) {\n for (const q of addQueries) {\n lc = lc\n .withContext('hash', q.id)\n .withContext('transformationHash', q.transformationHash);\n lc.debug?.(`adding pipeline for query`, q.ast);\n\n yield* pipelines.addQuery(\n q.transformationHash,\n q.id,\n q.ast,\n timer.startWithoutYielding(),\n );\n const elapsed = timer.stop();\n totalProcessTime += elapsed;\n\n self.#addQueryMaterializationServerMetric(\n q.transformationHash,\n elapsed,\n );\n\n if (elapsed > slowHydrateThreshold) {\n lc.warn?.('Slow query materialization', elapsed, q.ast);\n }\n manualSpan(tracer, 'vs.addAndConsumeQuery', elapsed, {\n hash: q.id,\n transformationHash: q.transformationHash,\n });\n }\n hydrations.add(1);\n hydrationTime.record(totalProcessTime / 1000);\n }\n // #processChanges does batched de-duping of rows. Wrap all pipelines in\n // a single generator in order to maximize de-duping.\n await this.#processChanges(\n lc,\n timer,\n generateRowChanges(this.#slowHydrateThreshold),\n updater,\n pokers,\n hashToIDs,\n );\n\n for (const patch of await updater.deleteUnreferencedRows(lc)) {\n await pokers.addPatch(patch);\n }\n\n // Commit the changes and update the CVR snapshot.\n this.#cvr = await this.#flushUpdater(lc, updater);\n\n const finalVersion = this.#cvr.version;\n\n // Before ending the poke, catch up clients that were behind the old CVR.\n await this.#catchupClients(\n lc,\n cvr,\n finalVersion,\n addQueries.map(q => q.id),\n pokers,\n );\n\n // Signal clients to commit.\n await pokers.end(finalVersion);\n\n const wallTime = performance.now() - start;\n lc.info?.(\n `finished processing queries (process: ${totalProcessTime} ms, wall: ${wallTime} ms)`,\n );\n });\n }\n\n /**\n * @param cvr The CVR to which clients should be caught up to. This does\n * not necessarily need to be the current CVR.\n * @param current The expected current CVR version. Before performing\n * catchup, the snapshot read will verify that the CVR has not been\n * concurrently modified. Note that this only needs to be done for\n * catchup because it is the only time data from the CVR DB is\n * \"exported\" without being gated by a CVR flush (which provides\n * concurrency protection in all other cases).\n *\n * If unspecified, the version of the `cvr` is used.\n * @param excludeQueryHashes Exclude patches from rows associated with\n * the specified queries.\n * @param usePokers If specified, sends pokes on existing PokeHandlers,\n * in which case the caller is responsible for sending the `pokeEnd`\n * messages. If unspecified, the pokes will be started and ended\n * using the version from the supplied `cvr`.\n */\n // Must be called within #lock\n #catchupClients(\n lc: LogContext,\n cvr: CVRSnapshot,\n current?: CVRVersion,\n excludeQueryHashes: string[] = [],\n usePokers?: PokeHandler,\n ) {\n return startAsyncSpan(tracer, 'vs.#catchupClients', async span => {\n current ??= cvr.version;\n const clients = this.#getClients();\n const pokers =\n usePokers ??\n startPoke(\n clients,\n cvr.version,\n this.#pipelines.currentSchemaVersions(),\n );\n span.setAttribute('numClients', clients.length);\n\n const catchupFrom = clients\n .map(c => c.version())\n .reduce((a, b) => (cmpVersions(a, b) < 0 ? a : b), cvr.version);\n\n // This is an AsyncGenerator which won't execute until awaited.\n const rowPatches = this.#cvrStore.catchupRowPatches(\n lc,\n catchupFrom,\n cvr,\n current,\n excludeQueryHashes,\n );\n\n // This is a plain async function that kicks off immediately.\n const configPatches = this.#cvrStore.catchupConfigPatches(\n lc,\n catchupFrom,\n cvr,\n current,\n );\n\n // The configPatches Promise will be awaited, and exceptions propagated,\n // after the rowPatches are processed. However, a catch handler must be\n // installed on the Promise in the meantime in order to avoid Node\n // crashing with an unhandled rejection error.\n configPatches.catch(() => {});\n\n // await the rowPatches first so that the AsyncGenerator kicks off.\n let rowPatchCount = 0;\n for await (const rows of rowPatches) {\n for (const row of rows) {\n const {schema, table} = row;\n const rowKey = row.rowKey as RowKey;\n const toVersion = versionFromString(row.patchVersion);\n\n const id: RowID = {schema, table, rowKey};\n let patch: RowPatch;\n if (!row.refCounts) {\n patch = {type: 'row', op: 'del', id};\n } else {\n const row = must(\n this.#pipelines.getRow(table, rowKey),\n `Missing row ${table}:${stringify(rowKey)}`,\n );\n const {contents} = contentsAndVersion(row);\n patch = {type: 'row', op: 'put', id, contents};\n }\n const patchToVersion = {patch, toVersion};\n await pokers.addPatch(patchToVersion);\n rowPatchCount++;\n }\n }\n span.setAttribute('rowPatchCount', rowPatchCount);\n if (rowPatchCount) {\n lc.debug?.(`sent ${rowPatchCount} row patches`);\n }\n\n // Then await the config patches which were fetched in parallel.\n for (const patch of await configPatches) {\n await pokers.addPatch(patch);\n }\n\n if (!usePokers) {\n await pokers.end(cvr.version);\n }\n });\n }\n\n #processChanges(\n lc: LogContext,\n timer: TimeSliceTimer,\n changes: Iterable<RowChange | 'yield'>,\n updater: CVRQueryDrivenUpdater,\n pokers: PokeHandler,\n hashToIDs: Map<string, string[]>,\n ) {\n return startAsyncSpan(tracer, 'vs.#processChanges', async () => {\n const start = performance.now();\n\n const rows = new CustomKeyMap<RowID, RowUpdate>(rowIDString);\n let total = 0;\n\n const processBatch = () =>\n startAsyncSpan(tracer, 'processBatch', async () => {\n const wallElapsed = performance.now() - start;\n total += rows.size;\n lc.debug?.(\n `processing ${rows.size} (of ${total}) rows (${wallElapsed} ms)`,\n );\n const patches = await updater.received(lc, rows);\n\n for (const patch of patches) {\n await pokers.addPatch(patch);\n }\n rows.clear();\n });\n\n await startAsyncSpan(tracer, 'loopingChanges', async span => {\n for (const change of changes) {\n if (change === 'yield') {\n await timer.yieldProcess('yield in processChanges');\n continue;\n }\n const {\n type,\n queryHash: transformationHash,\n table,\n rowKey,\n row,\n } = change;\n const queryIDs = must(\n hashToIDs.get(transformationHash),\n 'could not find the original hash for the transformation hash',\n );\n const rowID: RowID = {schema: '', table, rowKey: rowKey as RowKey};\n\n let parsedRow = rows.get(rowID);\n if (!parsedRow) {\n parsedRow = {refCounts: {}};\n rows.set(rowID, parsedRow);\n }\n queryIDs.forEach(hash => (parsedRow.refCounts[hash] ??= 0));\n\n const updateVersion = (row: Row) => {\n // IVM can output multiple versions of a row as it goes through its\n // intermediate stages. Always update the version and contents;\n // the last version will reflect the final state.\n const {version, contents} = contentsAndVersion(row);\n parsedRow.version = version;\n parsedRow.contents = contents;\n };\n switch (type) {\n case 'add':\n updateVersion(row);\n queryIDs.forEach(hash => parsedRow.refCounts[hash]++);\n break;\n case 'edit':\n updateVersion(row);\n // No update to refCounts.\n break;\n case 'remove':\n queryIDs.forEach(hash => parsedRow.refCounts[hash]--);\n break;\n default:\n unreachable(type);\n }\n\n if (rows.size % CURSOR_PAGE_SIZE === 0) {\n await processBatch();\n }\n }\n if (rows.size) {\n await processBatch();\n }\n span.setAttribute('totalRows', total);\n });\n });\n }\n\n /**\n * Advance to the current snapshot of the replica and apply / send\n * changes.\n *\n * Must be called from within the #lock.\n *\n * Returns false if the advancement failed due to a schema change.\n */\n #advancePipelines(\n lc: LogContext,\n cvr: CVRSnapshot,\n ): Promise<'success' | ResetPipelinesSignal> {\n return startAsyncSpan(tracer, 'vs.#advancePipelines', async () => {\n assert(\n this.#pipelines.initialized(),\n 'pipelines must be initialized (advancePipelines',\n );\n const start = performance.now();\n\n const timer = new TimeSliceTimer();\n const {version, numChanges, changes} = this.#pipelines.advance(timer);\n lc = lc.withContext('newVersion', version);\n\n // Probably need a new updater type. CVRAdvancementUpdater?\n const updater = new CVRQueryDrivenUpdater(\n this.#cvrStore,\n cvr,\n version,\n this.#pipelines.replicaVersion,\n );\n // Only poke clients that are at the cvr.version. New clients that\n // are behind need to first be caught up when their initConnection\n // message is processed (and #syncQueryPipelines is called).\n const pokers = startPoke(\n this.#getClients(cvr.version),\n updater.updatedVersion(),\n this.#pipelines.currentSchemaVersions(),\n );\n lc.debug?.(`applying ${numChanges} to advance to ${version}`);\n const hashToIDs = createHashToIDs(cvr);\n\n try {\n await this.#processChanges(\n lc,\n await timer.start(),\n changes,\n updater,\n pokers,\n hashToIDs,\n );\n } catch (e) {\n if (e instanceof ResetPipelinesSignal) {\n await pokers.cancel();\n return e;\n }\n throw e;\n }\n\n // Commit the changes and update the CVR snapshot.\n this.#cvr = await this.#flushUpdater(lc, updater);\n const finalVersion = this.#cvr.version;\n\n // Signal clients to commit.\n await pokers.end(finalVersion);\n\n const elapsed = performance.now() - start;\n lc.info?.(\n `finished processing advancement of ${numChanges} changes (${elapsed} ms)`,\n );\n this.#transactionAdvanceTime.record(elapsed / 1000);\n return 'success';\n });\n }\n\n inspect(context: SyncContext, msg: InspectUpMessage): Promise<void> {\n return this.#runInLockForClient(context, msg, this.#handleInspect);\n }\n\n // oxlint-disable-next-line require-await\n #handleInspect = async (\n lc: LogContext,\n clientID: string,\n body: InspectUpBody,\n cvr: CVRSnapshot,\n ): Promise<void> => {\n const client = must(this.#clients.get(clientID));\n return handleInspect(\n lc,\n body,\n cvr,\n client,\n this.#inspectorDelegate,\n this.id,\n this.#cvrStore,\n this.#config,\n this.#getHeaderOptions(this.#queryConfig.forwardCookies ?? false),\n this.userQueryURL,\n this.#authData,\n );\n };\n\n stop(): Promise<void> {\n this.#lc.info?.('stopping view syncer');\n this.#initialized.reject('shut down before initialization completed');\n this.#stateChanges.cancel();\n return this.#stopped.promise;\n }\n\n #cleanup(err?: unknown) {\n this.#stopTTLClockInterval();\n this.#stopExpireTimer();\n\n this.#pipelines.destroy();\n for (const client of this.#clients.values()) {\n if (err) {\n client.fail(err);\n } else {\n client.close(`closed clientGroupID=${this.id}`);\n }\n }\n }\n\n /**\n * Test helper: Manually mark initialization as complete.\n * This should only be used in tests that don't call initConnection().\n */\n markInitialized() {\n this.#initialized.resolve('initialized');\n }\n}\n\n// Update CVR after every 10000 rows.\nconst CURSOR_PAGE_SIZE = 10000;\n\nfunction createHashToIDs(cvr: CVRSnapshot) {\n const hashToIDs = new Map<string, string[]>();\n for (const {id, transformationHash} of Object.values(cvr.queries)) {\n if (!transformationHash) {\n continue;\n }\n if (hashToIDs.has(transformationHash)) {\n must(hashToIDs.get(transformationHash)).push(id);\n } else {\n hashToIDs.set(transformationHash, [id]);\n }\n }\n return hashToIDs;\n}\n\n// A global Lock acts as a queue to run a single IVM time slice per iteration\n// of the node event loop, thus bounding I/O delay to the duration of a single\n// time slice.\n//\n// Refresher:\n// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#phases-overview\n//\n// Note that recursive use of setImmediate() (i.e. calling setImmediate() from\n// within a setImmediate() callback), results in enqueuing the latter\n// callback in the *next* event loop iteration, as documented in:\n// https://nodejs.org/api/timers.html#setimmediatecallback-args\n//\n// This effectively achieves the desired one-per-event-loop-iteration behavior.\nconst timeSliceQueue = new Lock();\n\nfunction yieldProcess() {\n return timeSliceQueue.withLock(() => new Promise(setImmediate));\n}\n\nfunction contentsAndVersion(row: Row) {\n const {[ZERO_VERSION_COLUMN_NAME]: version, ...contents} = row;\n if (typeof version !== 'string' || version.length === 0) {\n throw new Error(`Invalid _0_version in ${stringify(row)}`);\n }\n return {contents, version};\n}\n\nconst NEW_CVR_VERSION = {stateVersion: '00'};\n\nfunction checkClientAndCVRVersions(\n client: NullableCVRVersion,\n cvr: CVRVersion,\n) {\n if (\n cmpVersions(cvr, NEW_CVR_VERSION) === 0 &&\n cmpVersions(client, NEW_CVR_VERSION) > 0\n ) {\n // CVR is empty but client is not.\n throw new ClientNotFoundError('Client not found');\n }\n\n if (cmpVersions(client, cvr) > 0) {\n // Client is ahead of a non-empty CVR.\n throw new ProtocolError({\n kind: ErrorKind.InvalidConnectionRequestBaseCookie,\n message: `CVR is at version ${versionString(cvr)}`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n}\n\nexport function pickToken(\n lc: LogContext,\n previousToken: TokenData | undefined,\n newToken: TokenData | undefined,\n) {\n if (previousToken === undefined) {\n lc.debug?.(`No previous token, using new token`);\n return newToken;\n }\n\n if (newToken) {\n if (previousToken.decoded.sub !== newToken.decoded.sub) {\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'The user id in the new token does not match the previous token. Client groups are pinned to a single user.',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n if (previousToken.decoded.iat === undefined) {\n lc.debug?.(`No issued at time for the existing token, using new token`);\n // No issued at time for the existing token? We take the most recently received token.\n return newToken;\n }\n\n if (newToken.decoded.iat === undefined) {\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'The new token does not have an issued at time but the prior token does. Tokens for a client group must either all have issued at times or all not have issued at times',\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n // The new token is newer, so we take it.\n if (previousToken.decoded.iat < newToken.decoded.iat) {\n lc.debug?.(`New token is newer, using it`);\n return newToken;\n }\n\n // if the new token is older or the same, we keep the existing token.\n lc.debug?.(`New token is older or the same, using existing token`);\n return previousToken;\n }\n\n // previousToken !== undefined but newToken is undefined\n throw new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'No token provided. An unauthenticated client cannot connect to an authenticated client group.',\n origin: ErrorOrigin.ZeroCache,\n });\n}\n\n/**\n * A query must be expired for all clients in order to be considered\n * expired.\n */\nfunction expired(\n ttlClock: TTLClock,\n q: InternalQueryRecord | ClientQueryRecord | CustomQueryRecord,\n): boolean {\n if (q.type === 'internal') {\n return false;\n }\n\n for (const clientState of Object.values(q.clientState)) {\n const {ttl, inactivatedAt} = clientState;\n if (inactivatedAt === undefined) {\n return false;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (\n ttlClockAsNumber(inactivatedAt) + clampedTTL >\n ttlClockAsNumber(ttlClock)\n ) {\n return false;\n }\n }\n return true;\n}\n\nfunction hasExpiredQueries(cvr: CVRSnapshot): boolean {\n const {ttlClock} = cvr;\n for (const q of Object.values(cvr.queries)) {\n if (expired(ttlClock, q)) {\n return true;\n }\n }\n return false;\n}\n\nexport class TimeSliceTimer {\n #total = 0;\n #start = 0;\n\n async start() {\n // yield at the very beginning so that the first time slice\n // is properly processed by the time-slice queue.\n await yieldProcess();\n return this.startWithoutYielding();\n }\n\n startWithoutYielding() {\n this.#total = 0;\n this.#startLap();\n return this;\n }\n\n async yieldProcess(_msgForTesting?: string) {\n this.#stopLap();\n await yieldProcess();\n this.#startLap();\n }\n\n #startLap() {\n assert(this.#start === 0, 'already running');\n this.#start = performance.now();\n }\n\n elapsedLap() {\n assert(this.#start !== 0, 'not running');\n return performance.now() - this.#start;\n }\n\n #stopLap() {\n assert(this.#start !== 0, 'not running');\n this.#total += performance.now() - this.#start;\n this.#start = 0;\n }\n\n /** @returns the total elapsed time */\n stop(): number {\n this.#stopLap();\n return this.#total;\n }\n\n /**\n * @returns the elapsed time. This can be called while the Timer is running\n * or after it has been stopped.\n */\n totalElapsed(): number {\n return this.#start === 0\n ? this.#total\n : this.#total + performance.now() - this.#start;\n }\n}\n"],"names":["ErrorKind.Rehome","ErrorOrigin.ZeroCache","version","lc","clientID","ErrorKind.InvalidConnectionRequest","cvr","ErrorKind.TransformFailed","row","ErrorKind.InvalidConnectionRequestBaseCookie","ErrorKind.Unauthorized"],"mappings":"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"}
|
|
@@ -57,7 +57,7 @@ function ZeroProvider({ children, init, ...props }) {
|
|
|
57
57
|
};
|
|
58
58
|
}, [init, ...keysWithoutAuth]);
|
|
59
59
|
useEffect(() => {
|
|
60
|
-
if (!zero) return;
|
|
60
|
+
if (!zero || isExternalZero) return;
|
|
61
61
|
const authChanged = auth !== prevAuthRef.current;
|
|
62
62
|
if (authChanged) {
|
|
63
63
|
prevAuthRef.current = auth;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-provider.js","sources":["../../../../zero-react/src/zero-provider.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport {stringCompare} from '../../shared/src/string-compare.ts';\nimport {\n Zero,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type Schema,\n type ZeroOptions,\n} from './zero.ts';\n\n// oxlint-disable-next-line no-explicit-any\nexport const ZeroContext = createContext<Zero<any, any, any> | undefined>(\n undefined,\n);\n\nexport function useZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(): Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero as Zero<S, MD, Context>;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nexport type ZeroProviderProps<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n> = (ZeroOptions<S, MD, Context> | {zero: Zero<S, MD, Context>}) & {\n init?: (zero: Zero<S, MD, Context>) => void;\n children: ReactNode;\n};\n\nconst NO_AUTH_SET = Symbol();\n\nexport function ZeroProvider<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>({children, init, ...props}: ZeroProviderProps<S, MD, Context>) {\n const isExternalZero = 'zero' in props;\n\n const [zero, setZero] = useState<Zero<S, MD, Context> | undefined>(\n isExternalZero ? props.zero : undefined,\n );\n\n const auth = 'auth' in props ? props.auth : NO_AUTH_SET;\n const prevAuthRef = useRef<typeof auth>(auth);\n\n const keysWithoutAuth = useMemo(\n () =>\n Object.entries(props)\n .filter(([key]) => key !== 'auth')\n .sort(([a], [b]) => stringCompare(a, b))\n .map(([_, value]) => value),\n [props],\n );\n\n // If Zero is not passed in, we construct it, but only client-side.\n // Zero doesn't really work SSR today so this is usually the right thing.\n // When we support Zero SSR this will either become a breaking change or\n // more likely server support will be opt-in with a new prop on this\n // component.\n useEffect(() => {\n if (isExternalZero) {\n setZero(props.zero);\n return;\n }\n\n const z = new Zero(props);\n init?.(z);\n setZero(z);\n\n return () => {\n void z.close();\n setZero(undefined);\n };\n // we intentionally don't include auth in the dependency array\n // to avoid closing zero when auth changes\n }, [init, ...keysWithoutAuth]);\n\n useEffect(() => {\n if (!zero) return;\n\n const authChanged = auth !== prevAuthRef.current;\n\n if (authChanged) {\n prevAuthRef.current = auth;\n void zero.connection.connect({\n auth: auth === NO_AUTH_SET ? undefined : auth,\n });\n }\n }, [auth, zero]);\n\n return (\n zero && <ZeroContext.Provider value={zero}>{children}</ZeroContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBO,MAAM,cAAc;AAAA,EACzB;AACF;AAEO,SAAS,UAIU;AACxB,QAAM,OAAO,WAAW,WAAW;AACnC,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,SAAO;AACT;AAaO,SAAS,gBAIZ;AACF,SAAO,MAAM,QAAA;AACf;AAWA,MAAM,cAAc,OAAA;AAEb,SAAS,aAId,EAAC,UAAU,MAAM,GAAG,SAA2C;AAC/D,QAAM,iBAAiB,UAAU;AAEjC,QAAM,CAAC,MAAM,OAAO,IAAI;AAAA,IACtB,iBAAiB,MAAM,OAAO;AAAA,EAAA;AAGhC,QAAM,OAAO,UAAU,QAAQ,MAAM,OAAO;AAC5C,QAAM,cAAc,OAAoB,IAAI;AAE5C,QAAM,kBAAkB;AAAA,IACtB,MACE,OAAO,QAAQ,KAAK,EACjB,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,MAAM,EAChC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,EACtC,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,KAAK;AAAA,IAC9B,CAAC,KAAK;AAAA,EAAA;AAQR,YAAU,MAAM;AACd,QAAI,gBAAgB;AAClB,cAAQ,MAAM,IAAI;AAClB;AAAA,IACF;AAEA,UAAM,IAAI,IAAI,KAAK,KAAK;AACxB,WAAO,CAAC;AACR,YAAQ,CAAC;AAET,WAAO,MAAM;AACX,WAAK,EAAE,MAAA;AACP,cAAQ,MAAS;AAAA,IACnB;AAAA,EAGF,GAAG,CAAC,MAAM,GAAG,eAAe,CAAC;AAE7B,YAAU,MAAM;AACd,QAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"zero-provider.js","sources":["../../../../zero-react/src/zero-provider.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport {stringCompare} from '../../shared/src/string-compare.ts';\nimport {\n Zero,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type Schema,\n type ZeroOptions,\n} from './zero.ts';\n\n// oxlint-disable-next-line no-explicit-any\nexport const ZeroContext = createContext<Zero<any, any, any> | undefined>(\n undefined,\n);\n\nexport function useZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(): Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero as Zero<S, MD, Context>;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nexport type ZeroProviderProps<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n> = (ZeroOptions<S, MD, Context> | {zero: Zero<S, MD, Context>}) & {\n init?: (zero: Zero<S, MD, Context>) => void;\n children: ReactNode;\n};\n\nconst NO_AUTH_SET = Symbol();\n\nexport function ZeroProvider<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>({children, init, ...props}: ZeroProviderProps<S, MD, Context>) {\n const isExternalZero = 'zero' in props;\n\n const [zero, setZero] = useState<Zero<S, MD, Context> | undefined>(\n isExternalZero ? props.zero : undefined,\n );\n\n const auth = 'auth' in props ? props.auth : NO_AUTH_SET;\n const prevAuthRef = useRef<typeof auth>(auth);\n\n const keysWithoutAuth = useMemo(\n () =>\n Object.entries(props)\n .filter(([key]) => key !== 'auth')\n .sort(([a], [b]) => stringCompare(a, b))\n .map(([_, value]) => value),\n [props],\n );\n\n // If Zero is not passed in, we construct it, but only client-side.\n // Zero doesn't really work SSR today so this is usually the right thing.\n // When we support Zero SSR this will either become a breaking change or\n // more likely server support will be opt-in with a new prop on this\n // component.\n useEffect(() => {\n if (isExternalZero) {\n setZero(props.zero);\n return;\n }\n\n const z = new Zero(props);\n init?.(z);\n setZero(z);\n\n return () => {\n void z.close();\n setZero(undefined);\n };\n // we intentionally don't include auth in the dependency array\n // to avoid closing zero when auth changes\n }, [init, ...keysWithoutAuth]);\n\n useEffect(() => {\n if (!zero || isExternalZero) return;\n\n const authChanged = auth !== prevAuthRef.current;\n\n if (authChanged) {\n prevAuthRef.current = auth;\n void zero.connection.connect({\n auth: auth === NO_AUTH_SET ? undefined : auth,\n });\n }\n }, [auth, zero]);\n\n return (\n zero && <ZeroContext.Provider value={zero}>{children}</ZeroContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBO,MAAM,cAAc;AAAA,EACzB;AACF;AAEO,SAAS,UAIU;AACxB,QAAM,OAAO,WAAW,WAAW;AACnC,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,SAAO;AACT;AAaO,SAAS,gBAIZ;AACF,SAAO,MAAM,QAAA;AACf;AAWA,MAAM,cAAc,OAAA;AAEb,SAAS,aAId,EAAC,UAAU,MAAM,GAAG,SAA2C;AAC/D,QAAM,iBAAiB,UAAU;AAEjC,QAAM,CAAC,MAAM,OAAO,IAAI;AAAA,IACtB,iBAAiB,MAAM,OAAO;AAAA,EAAA;AAGhC,QAAM,OAAO,UAAU,QAAQ,MAAM,OAAO;AAC5C,QAAM,cAAc,OAAoB,IAAI;AAE5C,QAAM,kBAAkB;AAAA,IACtB,MACE,OAAO,QAAQ,KAAK,EACjB,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,MAAM,EAChC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,EACtC,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,KAAK;AAAA,IAC9B,CAAC,KAAK;AAAA,EAAA;AAQR,YAAU,MAAM;AACd,QAAI,gBAAgB;AAClB,cAAQ,MAAM,IAAI;AAClB;AAAA,IACF;AAEA,UAAM,IAAI,IAAI,KAAK,KAAK;AACxB,WAAO,CAAC;AACR,YAAQ,CAAC;AAET,WAAO,MAAM;AACX,WAAK,EAAE,MAAA;AACP,cAAQ,MAAS;AAAA,IACnB;AAAA,EAGF,GAAG,CAAC,MAAM,GAAG,eAAe,CAAC;AAE7B,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,eAAgB;AAE7B,UAAM,cAAc,SAAS,YAAY;AAEzC,QAAI,aAAa;AACf,kBAAY,UAAU;AACtB,WAAK,KAAK,WAAW,QAAQ;AAAA,QAC3B,MAAM,SAAS,cAAc,SAAY;AAAA,MAAA,CAC1C;AAAA,IACH;AAAA,EACF,GAAG,CAAC,MAAM,IAAI,CAAC;AAEf,SACE,QAAQ,oBAAC,YAAY,UAAZ,EAAqB,OAAO,MAAO,UAAS;AAEzD;"}
|
|
@@ -35,24 +35,29 @@ export declare function toIterableRows(result: unknown): Iterable<Row>;
|
|
|
35
35
|
* ```ts
|
|
36
36
|
* import {Pool} from 'pg';
|
|
37
37
|
* import {drizzle} from 'drizzle-orm/node-postgres';
|
|
38
|
-
* import
|
|
38
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
39
|
+
* import {zeroDrizzle} from '@rocicorp/zero/server/adapters/drizzle';
|
|
40
|
+
* import {z} from 'zod/mini';
|
|
39
41
|
*
|
|
40
42
|
* const pool = new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!});
|
|
41
43
|
* const drizzleDb = drizzle(pool, {schema: drizzleSchema});
|
|
42
|
-
*
|
|
43
44
|
* const zql = zeroDrizzle(schema, drizzleDb);
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* )
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* }
|
|
46
|
+
* export const serverMutators = defineMutators({
|
|
47
|
+
* user: {
|
|
48
|
+
* create: defineMutator(
|
|
49
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
50
|
+
* async ({tx, args}) => {
|
|
51
|
+
* if (tx.location !== 'server') {
|
|
52
|
+
* throw new Error('Server-only mutator');
|
|
53
|
+
* }
|
|
54
|
+
* await tx.dbTransaction.wrappedTransaction
|
|
55
|
+
* .insert(drizzleSchema.user)
|
|
56
|
+
* .values({id: args.id, name: args.name, status: 'active'});
|
|
57
|
+
* },
|
|
58
|
+
* ),
|
|
59
|
+
* },
|
|
60
|
+
* });
|
|
56
61
|
* ```
|
|
57
62
|
*/
|
|
58
63
|
export declare function zeroDrizzle<TSchema extends Schema, TDrizzle extends DrizzleDatabase>(schema: TSchema, client: TDrizzle): ZQLDatabase<TSchema, DrizzleTransaction<TDrizzle>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,GAAG,EAAC,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,uBAAuB,CAAC;AAGtE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAE9D,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B,MAAM,MAAM,eAAe,CACzB,YAAY,SAAS,gBAAgB,GAAG,gBAAgB,EACxD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAEtC;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAC5B,WAAW,SAAS,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,SAAS,UAAU,CACtE,gBAAgB,EAChB,MAAM,eAAe,CACtB,GACG,eAAe,GACf,WAAW,IACb,aAAa,CACf,gBAAgB,EAChB,OAAO,EACP,0BAA0B,CAAC,OAAO,CAAC,CACpC,CAAC;AAEF,qBAAa,iBAAiB,CAC5B,QAAQ,SAAS,eAAe,EAChC,YAAY,SACV,kBAAkB,CAAC,QAAQ,CAAC,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAC7D,YAAW,YAAY,CAAC,YAAY,CAAC;;gBAIzB,OAAO,EAAE,QAAQ;IAI7B,WAAW,CAAC,CAAC,EACX,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC,CAAC,CAAC;CASd;AAkCD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAiBrE;AAUD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAuB7D;AAED
|
|
1
|
+
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,GAAG,EAAC,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,uBAAuB,CAAC;AAGtE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAE9D,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B,MAAM,MAAM,eAAe,CACzB,YAAY,SAAS,gBAAgB,GAAG,gBAAgB,EACxD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAEtC;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAC5B,WAAW,SAAS,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,SAAS,UAAU,CACtE,gBAAgB,EAChB,MAAM,eAAe,CACtB,GACG,eAAe,GACf,WAAW,IACb,aAAa,CACf,gBAAgB,EAChB,OAAO,EACP,0BAA0B,CAAC,OAAO,CAAC,CACpC,CAAC;AAEF,qBAAa,iBAAiB,CAC5B,QAAQ,SAAS,eAAe,EAChC,YAAY,SACV,kBAAkB,CAAC,QAAQ,CAAC,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAC7D,YAAW,YAAY,CAAC,YAAY,CAAC;;gBAIzB,OAAO,EAAE,QAAQ;IAI7B,WAAW,CAAC,CAAC,EACX,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC,CAAC,CAAC;CASd;AAkCD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAiBrE;AAUD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAuB7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,WAAW,CACzB,OAAO,SAAS,MAAM,EACtB,QAAQ,SAAS,eAAe,EAEhC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,GACf,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAKpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.js","sources":["../../../../../zero-server/src/adapters/drizzle.ts"],"sourcesContent":["import {sql, type SQL} from 'drizzle-orm';\nimport type {\n PgDatabase,\n PgQueryResultHKT,\n PgTransaction,\n} from 'drizzle-orm/pg-core';\nimport type {ExtractTablesWithRelations} from 'drizzle-orm/relations';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\nexport type DrizzleDatabase<\n TQueryResult extends PgQueryResultHKT = PgQueryResultHKT,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n> = PgDatabase<TQueryResult, TSchema>;\n\n/**\n * Helper type for the wrapped transaction used by drizzle-orm.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, DrizzleTransaction<typeof drizzleDb>>`.\n */\nexport type DrizzleTransaction<\n TDbOrSchema extends DrizzleDatabase | Record<string, unknown>,\n TSchema extends Record<string, unknown> = TDbOrSchema extends PgDatabase<\n PgQueryResultHKT,\n infer TInferredSchema\n >\n ? TInferredSchema\n : TDbOrSchema,\n> = PgTransaction<\n PgQueryResultHKT,\n TSchema,\n ExtractTablesWithRelations<TSchema>\n>;\n\nexport class DrizzleConnection<\n TDrizzle extends DrizzleDatabase,\n TTransaction extends\n DrizzleTransaction<TDrizzle> = DrizzleTransaction<TDrizzle>,\n> implements DBConnection<TTransaction>\n{\n readonly #drizzle: TDrizzle;\n\n constructor(drizzle: TDrizzle) {\n this.#drizzle = drizzle;\n }\n\n transaction<T>(\n fn: (tx: DBTransaction<TTransaction>) => Promise<T>,\n ): Promise<T> {\n return this.#drizzle.transaction(drizzleTx =>\n fn(\n new DrizzleInternalTransaction(\n drizzleTx,\n ) as DBTransaction<TTransaction>,\n ),\n );\n }\n}\n\nclass DrizzleInternalTransaction<\n TTransaction extends DrizzleTransaction<DrizzleDatabase>,\n> implements DBTransaction<TTransaction>\n{\n readonly wrappedTransaction: TTransaction;\n\n constructor(drizzleTx: TTransaction) {\n this.wrappedTransaction = drizzleTx;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Iterable<Row>> {\n const stmt = fromDollarParams(sql, params);\n const result = await this.wrappedTransaction.execute(stmt);\n return toIterableRows(result);\n }\n}\n\n/**\n * Turn `$1, $2...` placeholders into a Drizzle SQL object with bound params.\n */\nexport function fromDollarParams(text: string, params: unknown[]): SQL {\n const re = /\\$(\\d+)/g;\n const s = sql.empty();\n let last = 0;\n let m: RegExpExecArray | null;\n\n while ((m = re.exec(text)) !== null) {\n const idx = Number(m[1]) - 1;\n if (idx < 0 || idx >= params.length) {\n throw new Error(`Missing param for $${m[1]}`);\n }\n if (m.index > last) s.append(sql.raw(text.slice(last, m.index)));\n s.append(sql`${params[idx]}`); // parameterized value\n last = m.index + m[0].length;\n }\n if (last < text.length) s.append(sql.raw(text.slice(last)));\n return s;\n}\n\nfunction isIterable(value: unknown): value is Iterable<unknown> {\n return (\n // oxlint-disable-next-line eqeqeq\n value != null &&\n typeof (value as Iterable<unknown>)[Symbol.iterator] === 'function'\n );\n}\n\nexport function toIterableRows(result: unknown): Iterable<Row> {\n if (result === null || result === undefined) {\n return [] as Row[];\n }\n if (Array.isArray(result)) {\n return result as Row[];\n }\n if (isIterable(result)) {\n return result as Iterable<Row>;\n }\n if (typeof result === 'object') {\n const rows = (result as {rows?: unknown}).rows;\n if (rows === null || rows === undefined) {\n return [] as Row[];\n }\n if (Array.isArray(rows)) {\n return rows as Row[];\n }\n if (isIterable(rows)) {\n return rows as Iterable<Row>;\n }\n }\n throw new TypeError('Drizzle query result is not iterable');\n}\n\n/**\n * Wrap a `drizzle-orm` database for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying drizzle transaction.\n * Use {@link DrizzleTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param client - Drizzle database.\n *\n * @example\n * ```ts\n * import {Pool} from 'pg';\n * import {drizzle} from 'drizzle-orm/node-postgres';\n * import
|
|
1
|
+
{"version":3,"file":"drizzle.js","sources":["../../../../../zero-server/src/adapters/drizzle.ts"],"sourcesContent":["import {sql, type SQL} from 'drizzle-orm';\nimport type {\n PgDatabase,\n PgQueryResultHKT,\n PgTransaction,\n} from 'drizzle-orm/pg-core';\nimport type {ExtractTablesWithRelations} from 'drizzle-orm/relations';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\nexport type DrizzleDatabase<\n TQueryResult extends PgQueryResultHKT = PgQueryResultHKT,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n> = PgDatabase<TQueryResult, TSchema>;\n\n/**\n * Helper type for the wrapped transaction used by drizzle-orm.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, DrizzleTransaction<typeof drizzleDb>>`.\n */\nexport type DrizzleTransaction<\n TDbOrSchema extends DrizzleDatabase | Record<string, unknown>,\n TSchema extends Record<string, unknown> = TDbOrSchema extends PgDatabase<\n PgQueryResultHKT,\n infer TInferredSchema\n >\n ? TInferredSchema\n : TDbOrSchema,\n> = PgTransaction<\n PgQueryResultHKT,\n TSchema,\n ExtractTablesWithRelations<TSchema>\n>;\n\nexport class DrizzleConnection<\n TDrizzle extends DrizzleDatabase,\n TTransaction extends\n DrizzleTransaction<TDrizzle> = DrizzleTransaction<TDrizzle>,\n> implements DBConnection<TTransaction>\n{\n readonly #drizzle: TDrizzle;\n\n constructor(drizzle: TDrizzle) {\n this.#drizzle = drizzle;\n }\n\n transaction<T>(\n fn: (tx: DBTransaction<TTransaction>) => Promise<T>,\n ): Promise<T> {\n return this.#drizzle.transaction(drizzleTx =>\n fn(\n new DrizzleInternalTransaction(\n drizzleTx,\n ) as DBTransaction<TTransaction>,\n ),\n );\n }\n}\n\nclass DrizzleInternalTransaction<\n TTransaction extends DrizzleTransaction<DrizzleDatabase>,\n> implements DBTransaction<TTransaction>\n{\n readonly wrappedTransaction: TTransaction;\n\n constructor(drizzleTx: TTransaction) {\n this.wrappedTransaction = drizzleTx;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Iterable<Row>> {\n const stmt = fromDollarParams(sql, params);\n const result = await this.wrappedTransaction.execute(stmt);\n return toIterableRows(result);\n }\n}\n\n/**\n * Turn `$1, $2...` placeholders into a Drizzle SQL object with bound params.\n */\nexport function fromDollarParams(text: string, params: unknown[]): SQL {\n const re = /\\$(\\d+)/g;\n const s = sql.empty();\n let last = 0;\n let m: RegExpExecArray | null;\n\n while ((m = re.exec(text)) !== null) {\n const idx = Number(m[1]) - 1;\n if (idx < 0 || idx >= params.length) {\n throw new Error(`Missing param for $${m[1]}`);\n }\n if (m.index > last) s.append(sql.raw(text.slice(last, m.index)));\n s.append(sql`${params[idx]}`); // parameterized value\n last = m.index + m[0].length;\n }\n if (last < text.length) s.append(sql.raw(text.slice(last)));\n return s;\n}\n\nfunction isIterable(value: unknown): value is Iterable<unknown> {\n return (\n // oxlint-disable-next-line eqeqeq\n value != null &&\n typeof (value as Iterable<unknown>)[Symbol.iterator] === 'function'\n );\n}\n\nexport function toIterableRows(result: unknown): Iterable<Row> {\n if (result === null || result === undefined) {\n return [] as Row[];\n }\n if (Array.isArray(result)) {\n return result as Row[];\n }\n if (isIterable(result)) {\n return result as Iterable<Row>;\n }\n if (typeof result === 'object') {\n const rows = (result as {rows?: unknown}).rows;\n if (rows === null || rows === undefined) {\n return [] as Row[];\n }\n if (Array.isArray(rows)) {\n return rows as Row[];\n }\n if (isIterable(rows)) {\n return rows as Iterable<Row>;\n }\n }\n throw new TypeError('Drizzle query result is not iterable');\n}\n\n/**\n * Wrap a `drizzle-orm` database for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying drizzle transaction.\n * Use {@link DrizzleTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param client - Drizzle database.\n *\n * @example\n * ```ts\n * import {Pool} from 'pg';\n * import {drizzle} from 'drizzle-orm/node-postgres';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroDrizzle} from '@rocicorp/zero/server/adapters/drizzle';\n * import {z} from 'zod/mini';\n *\n * const pool = new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!});\n * const drizzleDb = drizzle(pool, {schema: drizzleSchema});\n * const zql = zeroDrizzle(schema, drizzleDb);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction\n * .insert(drizzleSchema.user)\n * .values({id: args.id, name: args.name, status: 'active'});\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroDrizzle<\n TSchema extends Schema,\n TDrizzle extends DrizzleDatabase,\n>(\n schema: TSchema,\n client: TDrizzle,\n): ZQLDatabase<TSchema, DrizzleTransaction<TDrizzle>> {\n return new ZQLDatabase(\n new DrizzleConnection<TDrizzle, DrizzleTransaction<TDrizzle>>(client),\n schema,\n );\n}\n"],"names":["sql"],"mappings":";;;AA8CO,MAAM,kBAKb;AAAA,EACW;AAAA,EAET,YAAY,SAAmB;AAC7B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,YACE,IACY;AACZ,WAAO,KAAK,SAAS;AAAA,MAAY,CAAA,cAC/B;AAAA,QACE,IAAI;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,MAAM,2BAGN;AAAA,EACW;AAAA,EAET,YAAY,WAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,SACE,KACA,QACA,QACA,cACiC;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,MAAM,MAAMA,MAAa,QAA2C;AAClE,UAAM,OAAO,iBAAiBA,MAAK,MAAM;AACzC,UAAM,SAAS,MAAM,KAAK,mBAAmB,QAAQ,IAAI;AACzD,WAAO,eAAe,MAAM;AAAA,EAC9B;AACF;AAKO,SAAS,iBAAiB,MAAc,QAAwB;AACrE,QAAM,KAAK;AACX,QAAM,IAAI,IAAI,MAAA;AACd,MAAI,OAAO;AACX,MAAI;AAEJ,UAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,UAAM,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI;AAC3B,QAAI,MAAM,KAAK,OAAO,OAAO,QAAQ;AACnC,YAAM,IAAI,MAAM,sBAAsB,EAAE,CAAC,CAAC,EAAE;AAAA,IAC9C;AACA,QAAI,EAAE,QAAQ,KAAM,GAAE,OAAO,IAAI,IAAI,KAAK,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/D,MAAE,OAAO,MAAM,OAAO,GAAG,CAAC,EAAE;AAC5B,WAAO,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,EACxB;AACA,MAAI,OAAO,KAAK,OAAQ,GAAE,OAAO,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC;AAC1D,SAAO;AACT;AAEA,SAAS,WAAW,OAA4C;AAC9D;AAAA;AAAA,IAEE,SAAS,QACT,OAAQ,MAA4B,OAAO,QAAQ,MAAM;AAAA;AAE7D;AAEO,SAAS,eAAe,QAAgC;AAC7D,MAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,WAAO,CAAA;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,OAAQ,OAA4B;AAC1C,QAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,aAAO,CAAA;AAAA,IACT;AACA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,aAAO;AAAA,IACT;AACA,QAAI,WAAW,IAAI,GAAG;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,IAAI,UAAU,sCAAsC;AAC5D;AAwCO,SAAS,YAId,QACA,QACoD;AACpD,SAAO,IAAI;AAAA,IACT,IAAI,kBAA0D,MAAM;AAAA,IACpE;AAAA,EAAA;AAEJ;"}
|
|
@@ -37,23 +37,29 @@ export declare class NodePgTransactionInternal implements DBTransaction<NodePgTr
|
|
|
37
37
|
* @example
|
|
38
38
|
* ```ts
|
|
39
39
|
* import {Pool} from 'pg';
|
|
40
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
41
|
+
* import {zeroNodePg} from '@rocicorp/zero/server/adapters/pg';
|
|
42
|
+
* import {z} from 'zod/mini';
|
|
40
43
|
*
|
|
41
44
|
* const pool = new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!});
|
|
42
45
|
* const zql = zeroNodePg(schema, pool);
|
|
43
46
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* )
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
47
|
+
* export const serverMutators = defineMutators({
|
|
48
|
+
* user: {
|
|
49
|
+
* create: defineMutator(
|
|
50
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
51
|
+
* async ({tx, args}) => {
|
|
52
|
+
* if (tx.location !== 'server') {
|
|
53
|
+
* throw new Error('Server-only mutator');
|
|
54
|
+
* }
|
|
55
|
+
* await tx.dbTransaction.wrappedTransaction.query(
|
|
56
|
+
* 'INSERT INTO "user" (id, name, status) VALUES ($1, $2, $3)',
|
|
57
|
+
* [args.id, args.name, 'active'],
|
|
58
|
+
* );
|
|
59
|
+
* },
|
|
60
|
+
* ),
|
|
61
|
+
* },
|
|
62
|
+
* });
|
|
57
63
|
* ```
|
|
58
64
|
*/
|
|
59
65
|
export declare function zeroNodePg<S extends Schema>(schema: S, pg: NodePgTransaction | string): ZQLDatabase<S, NodePgTransaction>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAC,IAAI,EAAE,KAAK,UAAU,EAAC,MAAM,IAAI,CAAC;AACzC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM,CAAC;AAE3D,qBAAa,gBAAiB,YAAW,YAAY,CAAC,iBAAiB,CAAC;;gBAG1D,IAAI,EAAE,iBAAiB;IAI7B,WAAW,CAAC,IAAI,EACpB,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,iBAAiB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1D,OAAO,CAAC,IAAI,CAAC;CAqBjB;AAED,qBAAa,yBACX,YAAW,aAAa,CAAC,iBAAiB,CAAC;IAE3C,QAAQ,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;gBAEnC,MAAM,EAAE,iBAAiB;IAIrC,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAU5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAI5D;AAED
|
|
1
|
+
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAC,IAAI,EAAE,KAAK,UAAU,EAAC,MAAM,IAAI,CAAC;AACzC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM,CAAC;AAE3D,qBAAa,gBAAiB,YAAW,YAAY,CAAC,iBAAiB,CAAC;;gBAG1D,IAAI,EAAE,iBAAiB;IAI7B,WAAW,CAAC,IAAI,EACpB,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,iBAAiB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1D,OAAO,CAAC,IAAI,CAAC;CAqBjB;AAED,qBAAa,yBACX,YAAW,aAAa,CAAC,iBAAiB,CAAC;IAE3C,QAAQ,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;gBAEnC,MAAM,EAAE,iBAAiB;IAIrC,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAU5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAI5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,MAAM,EAAE,CAAC,EACT,EAAE,EAAE,iBAAiB,GAAG,MAAM,qCAM/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","sources":["../../../../../zero-server/src/adapters/pg.ts"],"sourcesContent":["import type {Client} from 'pg';\nimport {Pool, type PoolClient} from 'pg';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by node-postgres.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, NodePgTransaction>`.\n */\nexport type NodePgTransaction = Pool | PoolClient | Client;\n\nexport class NodePgConnection implements DBConnection<NodePgTransaction> {\n readonly #pool: NodePgTransaction;\n\n constructor(pool: NodePgTransaction) {\n this.#pool = pool;\n }\n\n async transaction<TRet>(\n fn: (tx: DBTransaction<NodePgTransaction>) => Promise<TRet>,\n ): Promise<TRet> {\n const client =\n this.#pool instanceof Pool ? await this.#pool.connect() : this.#pool;\n try {\n await client.query('BEGIN');\n const result = await fn(new NodePgTransactionInternal(client));\n await client.query('COMMIT');\n return result;\n } catch (error) {\n try {\n await client.query('ROLLBACK');\n } catch {\n // ignore rollback error; original error will be thrown\n }\n throw error;\n } finally {\n if (this.#pool instanceof Pool && 'release' in client) {\n client.release();\n }\n }\n }\n}\n\nexport class NodePgTransactionInternal\n implements DBTransaction<NodePgTransaction>\n{\n readonly wrappedTransaction: NodePgTransaction;\n\n constructor(client: NodePgTransaction) {\n this.wrappedTransaction = client;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Row[]> {\n const res = await this.wrappedTransaction.query(sql, params as unknown[]);\n return res.rows as Row[];\n }\n}\n\n/**\n * Wrap a `pg` Pool for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying node-postgres client.\n * Use {@link NodePgTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `pg` Pool or connection string.\n *\n * @example\n * ```ts\n * import {Pool} from 'pg';\n *\n * const pool = new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!});\n * const zql = zeroNodePg(schema, pool);\n *\n *
|
|
1
|
+
{"version":3,"file":"pg.js","sources":["../../../../../zero-server/src/adapters/pg.ts"],"sourcesContent":["import type {Client} from 'pg';\nimport {Pool, type PoolClient} from 'pg';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by node-postgres.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, NodePgTransaction>`.\n */\nexport type NodePgTransaction = Pool | PoolClient | Client;\n\nexport class NodePgConnection implements DBConnection<NodePgTransaction> {\n readonly #pool: NodePgTransaction;\n\n constructor(pool: NodePgTransaction) {\n this.#pool = pool;\n }\n\n async transaction<TRet>(\n fn: (tx: DBTransaction<NodePgTransaction>) => Promise<TRet>,\n ): Promise<TRet> {\n const client =\n this.#pool instanceof Pool ? await this.#pool.connect() : this.#pool;\n try {\n await client.query('BEGIN');\n const result = await fn(new NodePgTransactionInternal(client));\n await client.query('COMMIT');\n return result;\n } catch (error) {\n try {\n await client.query('ROLLBACK');\n } catch {\n // ignore rollback error; original error will be thrown\n }\n throw error;\n } finally {\n if (this.#pool instanceof Pool && 'release' in client) {\n client.release();\n }\n }\n }\n}\n\nexport class NodePgTransactionInternal\n implements DBTransaction<NodePgTransaction>\n{\n readonly wrappedTransaction: NodePgTransaction;\n\n constructor(client: NodePgTransaction) {\n this.wrappedTransaction = client;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Row[]> {\n const res = await this.wrappedTransaction.query(sql, params as unknown[]);\n return res.rows as Row[];\n }\n}\n\n/**\n * Wrap a `pg` Pool for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying node-postgres client.\n * Use {@link NodePgTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `pg` Pool or connection string.\n *\n * @example\n * ```ts\n * import {Pool} from 'pg';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroNodePg} from '@rocicorp/zero/server/adapters/pg';\n * import {z} from 'zod/mini';\n *\n * const pool = new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!});\n * const zql = zeroNodePg(schema, pool);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction.query(\n * 'INSERT INTO \"user\" (id, name, status) VALUES ($1, $2, $3)',\n * [args.id, args.name, 'active'],\n * );\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroNodePg<S extends Schema>(\n schema: S,\n pg: NodePgTransaction | string,\n) {\n if (typeof pg === 'string') {\n pg = new Pool({connectionString: pg});\n }\n return new ZQLDatabase(new NodePgConnection(pg), schema);\n}\n"],"names":[],"mappings":";;;AAwBO,MAAM,iBAA4D;AAAA,EAC9D;AAAA,EAET,YAAY,MAAyB;AACnC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,YACJ,IACe;AACf,UAAM,SACJ,KAAK,iBAAiB,OAAO,MAAM,KAAK,MAAM,YAAY,KAAK;AACjE,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAC1B,YAAM,SAAS,MAAM,GAAG,IAAI,0BAA0B,MAAM,CAAC;AAC7D,YAAM,OAAO,MAAM,QAAQ;AAC3B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI;AACF,cAAM,OAAO,MAAM,UAAU;AAAA,MAC/B,QAAQ;AAAA,MAER;AACA,YAAM;AAAA,IACR,UAAA;AACE,UAAI,KAAK,iBAAiB,QAAQ,aAAa,QAAQ;AACrD,eAAO,QAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,0BAEb;AAAA,EACW;AAAA,EAET,YAAY,QAA2B;AACrC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,SACE,KACA,QACA,QACA,cACiC;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,MAAM,MAAM,KAAa,QAAmC;AAC1D,UAAM,MAAM,MAAM,KAAK,mBAAmB,MAAM,KAAK,MAAmB;AACxE,WAAO,IAAI;AAAA,EACb;AACF;AAuCO,SAAS,WACd,QACA,IACA;AACA,MAAI,OAAO,OAAO,UAAU;AAC1B,SAAK,IAAI,KAAK,EAAC,kBAAkB,IAAG;AAAA,EACtC;AACA,SAAO,IAAI,YAAY,IAAI,iBAAiB,EAAE,GAAG,MAAM;AACzD;"}
|
|
@@ -37,23 +37,29 @@ export declare class PostgresJsTransactionInternal<T extends Record<string, unkn
|
|
|
37
37
|
* @example
|
|
38
38
|
* ```ts
|
|
39
39
|
* import postgres from 'postgres';
|
|
40
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
41
|
+
* import {zeroPostgresJS} from '@rocicorp/zero/server/adapters/postgresjs';
|
|
42
|
+
* import {z} from 'zod/mini';
|
|
40
43
|
*
|
|
41
44
|
* const sql = postgres(process.env.ZERO_UPSTREAM_DB!);
|
|
42
45
|
* const zql = zeroPostgresJS(schema, sql);
|
|
43
46
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* )
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
47
|
+
* export const serverMutators = defineMutators({
|
|
48
|
+
* user: {
|
|
49
|
+
* create: defineMutator(
|
|
50
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
51
|
+
* async ({tx, args}) => {
|
|
52
|
+
* if (tx.location !== 'server') {
|
|
53
|
+
* throw new Error('Server-only mutator');
|
|
54
|
+
* }
|
|
55
|
+
* await tx.dbTransaction.wrappedTransaction`
|
|
56
|
+
* INSERT INTO "user" (id, name, status)
|
|
57
|
+
* VALUES (${args.id}, ${args.name}, ${'active'})
|
|
58
|
+
* `;
|
|
59
|
+
* },
|
|
60
|
+
* ),
|
|
61
|
+
* },
|
|
62
|
+
* });
|
|
57
63
|
* ```
|
|
58
64
|
*/
|
|
59
65
|
export declare function zeroPostgresJS<S extends Schema, T extends Record<string, unknown> = Record<string, unknown>>(schema: S, pg: postgres.Sql<T> | string): ZQLDatabase<S, PostgresJsTransaction<T>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresjs.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAE/B,qBAAa,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjE,YAAW,YAAY,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;;gBAGrC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAI/B,WAAW,CAAC,IAAI,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC;CAKjB;AAED,qBAAa,6BAA6B,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAC1E,YAAW,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAElD,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAI1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIrD,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;CASnC;AAED
|
|
1
|
+
{"version":3,"file":"postgresjs.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAE/B,qBAAa,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjE,YAAW,YAAY,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;;gBAGrC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAI/B,WAAW,CAAC,IAAI,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC;CAKjB;AAED,qBAAa,6BAA6B,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAC1E,YAAW,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAElD,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAI1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIrD,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;CASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,4CAKxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresjs.js","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by postgres.js.\n *\n * @typeParam T - The row-shape context bound to the postgres.js client.\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PostgresJsTransaction>`.\n */\nexport type PostgresJsTransaction<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = postgres.TransactionSql<T>;\n\nexport class PostgresJSConnection<T extends Record<string, unknown>>\n implements DBConnection<PostgresJsTransaction<T>>\n{\n readonly #pg: postgres.Sql<T>;\n constructor(pg: postgres.Sql<T>) {\n this.#pg = pg;\n }\n\n transaction<TRet>(\n fn: (tx: DBTransaction<PostgresJsTransaction<T>>) => Promise<TRet>,\n ): Promise<TRet> {\n return this.#pg.begin(pgTx =>\n fn(new PostgresJsTransactionInternal(pgTx)),\n ) as Promise<TRet>;\n }\n}\n\nexport class PostgresJsTransactionInternal<T extends Record<string, unknown>>\n implements DBTransaction<PostgresJsTransaction<T>>\n{\n readonly wrappedTransaction: PostgresJsTransaction<T>;\n constructor(pgTx: PostgresJsTransaction<T>) {\n this.wrappedTransaction = pgTx;\n }\n\n query(sql: string, params: unknown[]): Promise<Row[]> {\n return this.wrappedTransaction.unsafe(sql, params as JSONValue[]);\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n}\n\n/**\n * Wrap a `postgres` client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying postgres.js transaction.\n * Use {@link PostgresJsTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `postgres` client or connection string.\n *\n * @example\n * ```ts\n * import postgres from 'postgres';\n *\n * const sql = postgres(process.env.ZERO_UPSTREAM_DB!);\n * const zql = zeroPostgresJS(schema, sql);\n *\n *
|
|
1
|
+
{"version":3,"file":"postgresjs.js","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by postgres.js.\n *\n * @typeParam T - The row-shape context bound to the postgres.js client.\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PostgresJsTransaction>`.\n */\nexport type PostgresJsTransaction<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = postgres.TransactionSql<T>;\n\nexport class PostgresJSConnection<T extends Record<string, unknown>>\n implements DBConnection<PostgresJsTransaction<T>>\n{\n readonly #pg: postgres.Sql<T>;\n constructor(pg: postgres.Sql<T>) {\n this.#pg = pg;\n }\n\n transaction<TRet>(\n fn: (tx: DBTransaction<PostgresJsTransaction<T>>) => Promise<TRet>,\n ): Promise<TRet> {\n return this.#pg.begin(pgTx =>\n fn(new PostgresJsTransactionInternal(pgTx)),\n ) as Promise<TRet>;\n }\n}\n\nexport class PostgresJsTransactionInternal<T extends Record<string, unknown>>\n implements DBTransaction<PostgresJsTransaction<T>>\n{\n readonly wrappedTransaction: PostgresJsTransaction<T>;\n constructor(pgTx: PostgresJsTransaction<T>) {\n this.wrappedTransaction = pgTx;\n }\n\n query(sql: string, params: unknown[]): Promise<Row[]> {\n return this.wrappedTransaction.unsafe(sql, params as JSONValue[]);\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n}\n\n/**\n * Wrap a `postgres` client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying postgres.js transaction.\n * Use {@link PostgresJsTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `postgres` client or connection string.\n *\n * @example\n * ```ts\n * import postgres from 'postgres';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroPostgresJS} from '@rocicorp/zero/server/adapters/postgresjs';\n * import {z} from 'zod/mini';\n *\n * const sql = postgres(process.env.ZERO_UPSTREAM_DB!);\n * const zql = zeroPostgresJS(schema, sql);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction`\n * INSERT INTO \"user\" (id, name, status)\n * VALUES (${args.id}, ${args.name}, ${'active'})\n * `;\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroPostgresJS<\n S extends Schema,\n T extends Record<string, unknown> = Record<string, unknown>,\n>(schema: S, pg: postgres.Sql<T> | string) {\n if (typeof pg === 'string') {\n pg = postgres(pg) as postgres.Sql<T>;\n }\n return new ZQLDatabase(new PostgresJSConnection(pg), schema);\n}\n"],"names":[],"mappings":";;;AA2BO,MAAM,qBAEb;AAAA,EACW;AAAA,EACT,YAAY,IAAqB;AAC/B,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,YACE,IACe;AACf,WAAO,KAAK,IAAI;AAAA,MAAM,CAAA,SACpB,GAAG,IAAI,8BAA8B,IAAI,CAAC;AAAA,IAAA;AAAA,EAE9C;AACF;AAEO,MAAM,8BAEb;AAAA,EACW;AAAA,EACT,YAAY,MAAgC;AAC1C,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,MAAM,KAAa,QAAmC;AACpD,WAAO,KAAK,mBAAmB,OAAO,KAAK,MAAqB;AAAA,EAClE;AAAA,EAEA,SACE,KACA,QACA,QACA,cACiC;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AACF;AAuCO,SAAS,eAGd,QAAW,IAA8B;AACzC,MAAI,OAAO,OAAO,UAAU;AAC1B,SAAK,SAAS,EAAE;AAAA,EAClB;AACA,SAAO,IAAI,YAAY,IAAI,qBAAqB,EAAE,GAAG,MAAM;AAC7D;"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Schema } from '../../../zero-types/src/schema.ts';
|
|
2
|
+
import type { DBConnection, DBTransaction } from '../../../zql/src/mutate/custom.ts';
|
|
3
|
+
import { ZQLDatabase } from '../zql-database.ts';
|
|
4
|
+
export type { ZQLDatabase };
|
|
5
|
+
export type PrismaTransactionLike = {
|
|
6
|
+
$queryRawUnsafe: (query: string, ...params: unknown[]) => Promise<unknown>;
|
|
7
|
+
};
|
|
8
|
+
export type PrismaClientLike<TTransaction extends PrismaTransactionLike = PrismaTransactionLike> = {
|
|
9
|
+
$transaction: <T>(fn: (tx: TTransaction) => Promise<T>) => Promise<T>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Helper type for the wrapped transaction used by Prisma.
|
|
13
|
+
*
|
|
14
|
+
* @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PrismaTransaction<typeof prisma>>`.
|
|
15
|
+
*/
|
|
16
|
+
export type PrismaTransaction<TClient extends PrismaClientLike = PrismaClientLike> = TClient extends PrismaClientLike<infer TTransaction> ? TTransaction : PrismaTransactionLike;
|
|
17
|
+
export declare class PrismaConnection<TClient extends PrismaClientLike> implements DBConnection<PrismaTransaction<TClient>> {
|
|
18
|
+
#private;
|
|
19
|
+
constructor(client: TClient);
|
|
20
|
+
transaction<T>(fn: (tx: DBTransaction<PrismaTransaction<TClient>>) => Promise<T>): Promise<T>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wrap a Prisma client for Zero ZQL.
|
|
24
|
+
*
|
|
25
|
+
* Provides ZQL querying plus access to the underlying Prisma transaction.
|
|
26
|
+
* Use {@link PrismaTransaction} to type your server mutator transaction.
|
|
27
|
+
*
|
|
28
|
+
* @param schema - Zero schema.
|
|
29
|
+
* @param client - Prisma client.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import {PrismaPg} from '@prisma/adapter-pg';
|
|
34
|
+
* import {PrismaClient} from '@prisma/client';
|
|
35
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
36
|
+
* import {zeroPrisma} from '@rocicorp/zero/server/adapters/prisma';
|
|
37
|
+
* import {z} from 'zod/mini';
|
|
38
|
+
*
|
|
39
|
+
* const prisma = new PrismaClient({
|
|
40
|
+
* adapter: new PrismaPg({connectionString: process.env.ZERO_UPSTREAM_DB!}),
|
|
41
|
+
* });
|
|
42
|
+
* const zql = zeroPrisma(schema, prisma);
|
|
43
|
+
*
|
|
44
|
+
* export const serverMutators = defineMutators({
|
|
45
|
+
* user: {
|
|
46
|
+
* create: defineMutator(
|
|
47
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
48
|
+
* async ({tx, args}) => {
|
|
49
|
+
* if (tx.location !== 'server') {
|
|
50
|
+
* throw new Error('Server-only mutator');
|
|
51
|
+
* }
|
|
52
|
+
* await tx.dbTransaction.wrappedTransaction.user.create({
|
|
53
|
+
* data: {
|
|
54
|
+
* id: args.id,
|
|
55
|
+
* name: args.name,
|
|
56
|
+
* status: 'active',
|
|
57
|
+
* },
|
|
58
|
+
* });
|
|
59
|
+
* },
|
|
60
|
+
* ),
|
|
61
|
+
* },
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function zeroPrisma<TSchema extends Schema, TClient extends PrismaClientLike>(schema: TSchema, client: TClient): ZQLDatabase<TSchema, PrismaTransaction<TClient>>;
|
|
66
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/prisma.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAE9D,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EAEd,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B,MAAM,MAAM,qBAAqB,GAAG;IAClC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAC1B,YAAY,SAAS,qBAAqB,GAAG,qBAAqB,IAChE;IACF,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACvE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAC3B,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,IAEnD,OAAO,SAAS,gBAAgB,CAAC,MAAM,YAAY,CAAC,GAChD,YAAY,GACZ,qBAAqB,CAAC;AAE5B,qBAAa,gBAAgB,CAAC,OAAO,SAAS,gBAAgB,CAC5D,YAAW,YAAY,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;;gBAIvC,MAAM,EAAE,OAAO;IAI3B,WAAW,CAAC,CAAC,EACX,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAChE,OAAO,CAAC,CAAC,CAAC;CASd;AAwDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,UAAU,CACxB,OAAO,SAAS,MAAM,EACtB,OAAO,SAAS,gBAAgB,EAEhC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,OAAO,GACd,WAAW,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAElD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { executePostgresQuery } from "../pg-query-executor.js";
|
|
2
|
+
import { ZQLDatabase } from "../zql-database.js";
|
|
3
|
+
class PrismaConnection {
|
|
4
|
+
#client;
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.#client = client;
|
|
7
|
+
}
|
|
8
|
+
transaction(fn) {
|
|
9
|
+
return this.#client.$transaction(
|
|
10
|
+
(prismaTx) => fn(
|
|
11
|
+
new PrismaInternalTransaction(prismaTx)
|
|
12
|
+
)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class PrismaInternalTransaction {
|
|
17
|
+
wrappedTransaction;
|
|
18
|
+
constructor(prismaTx) {
|
|
19
|
+
this.wrappedTransaction = prismaTx;
|
|
20
|
+
}
|
|
21
|
+
runQuery(ast, format, schema, serverSchema) {
|
|
22
|
+
return executePostgresQuery(
|
|
23
|
+
this,
|
|
24
|
+
ast,
|
|
25
|
+
format,
|
|
26
|
+
schema,
|
|
27
|
+
serverSchema
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
async query(sql, params) {
|
|
31
|
+
const result = await this.wrappedTransaction.$queryRawUnsafe(
|
|
32
|
+
sql,
|
|
33
|
+
...params
|
|
34
|
+
);
|
|
35
|
+
return toIterableRows(result);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isIterable(value) {
|
|
39
|
+
return (
|
|
40
|
+
// oxlint-disable-next-line eqeqeq
|
|
41
|
+
value != null && typeof value[Symbol.iterator] === "function"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function toIterableRows(result) {
|
|
45
|
+
if (result === null || result === void 0) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(result)) {
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
if (isIterable(result)) {
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
function zeroPrisma(schema, client) {
|
|
57
|
+
return new ZQLDatabase(new PrismaConnection(client), schema);
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
PrismaConnection,
|
|
61
|
+
zeroPrisma
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sources":["../../../../../zero-server/src/adapters/prisma.ts"],"sourcesContent":["import type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\nexport type PrismaTransactionLike = {\n $queryRawUnsafe: (query: string, ...params: unknown[]) => Promise<unknown>;\n};\n\nexport type PrismaClientLike<\n TTransaction extends PrismaTransactionLike = PrismaTransactionLike,\n> = {\n $transaction: <T>(fn: (tx: TTransaction) => Promise<T>) => Promise<T>;\n};\n\n/**\n * Helper type for the wrapped transaction used by Prisma.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PrismaTransaction<typeof prisma>>`.\n */\nexport type PrismaTransaction<\n TClient extends PrismaClientLike = PrismaClientLike,\n> =\n TClient extends PrismaClientLike<infer TTransaction>\n ? TTransaction\n : PrismaTransactionLike;\n\nexport class PrismaConnection<TClient extends PrismaClientLike>\n implements DBConnection<PrismaTransaction<TClient>>\n{\n readonly #client: TClient;\n\n constructor(client: TClient) {\n this.#client = client;\n }\n\n transaction<T>(\n fn: (tx: DBTransaction<PrismaTransaction<TClient>>) => Promise<T>,\n ): Promise<T> {\n return this.#client.$transaction(prismaTx =>\n fn(\n new PrismaInternalTransaction(prismaTx) as DBTransaction<\n PrismaTransaction<TClient>\n >,\n ),\n );\n }\n}\n\nclass PrismaInternalTransaction<TTransaction extends PrismaTransactionLike>\n implements DBTransaction<TTransaction>\n{\n readonly wrappedTransaction: TTransaction;\n\n constructor(prismaTx: TTransaction) {\n this.wrappedTransaction = prismaTx;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Iterable<Row>> {\n const result = await this.wrappedTransaction.$queryRawUnsafe(\n sql,\n ...params,\n );\n return toIterableRows(result);\n }\n}\n\nfunction isIterable(value: unknown): value is Iterable<unknown> {\n return (\n // oxlint-disable-next-line eqeqeq\n value != null &&\n typeof (value as Iterable<unknown>)[Symbol.iterator] === 'function'\n );\n}\n\nfunction toIterableRows(result: unknown): Iterable<Row> {\n if (result === null || result === undefined) {\n return [] as Row[];\n }\n if (Array.isArray(result)) {\n return result as Row[];\n }\n if (isIterable(result)) {\n return result as Iterable<Row>;\n }\n return [] as Row[];\n}\n\n/**\n * Wrap a Prisma client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying Prisma transaction.\n * Use {@link PrismaTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param client - Prisma client.\n *\n * @example\n * ```ts\n * import {PrismaPg} from '@prisma/adapter-pg';\n * import {PrismaClient} from '@prisma/client';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroPrisma} from '@rocicorp/zero/server/adapters/prisma';\n * import {z} from 'zod/mini';\n *\n * const prisma = new PrismaClient({\n * adapter: new PrismaPg({connectionString: process.env.ZERO_UPSTREAM_DB!}),\n * });\n * const zql = zeroPrisma(schema, prisma);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction.user.create({\n * data: {\n * id: args.id,\n * name: args.name,\n * status: 'active',\n * },\n * });\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroPrisma<\n TSchema extends Schema,\n TClient extends PrismaClientLike,\n>(\n schema: TSchema,\n client: TClient,\n): ZQLDatabase<TSchema, PrismaTransaction<TClient>> {\n return new ZQLDatabase(new PrismaConnection(client), schema);\n}\n"],"names":[],"mappings":";;AAqCO,MAAM,iBAEb;AAAA,EACW;AAAA,EAET,YAAY,QAAiB;AAC3B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,YACE,IACY;AACZ,WAAO,KAAK,QAAQ;AAAA,MAAa,CAAA,aAC/B;AAAA,QACE,IAAI,0BAA0B,QAAQ;AAAA,MAAA;AAAA,IAGxC;AAAA,EAEJ;AACF;AAEA,MAAM,0BAEN;AAAA,EACW;AAAA,EAET,YAAY,UAAwB;AAClC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,SACE,KACA,QACA,QACA,cACiC;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,MAAM,MAAM,KAAa,QAA2C;AAClE,UAAM,SAAS,MAAM,KAAK,mBAAmB;AAAA,MAC3C;AAAA,MACA,GAAG;AAAA,IAAA;AAEL,WAAO,eAAe,MAAM;AAAA,EAC9B;AACF;AAEA,SAAS,WAAW,OAA4C;AAC9D;AAAA;AAAA,IAEE,SAAS,QACT,OAAQ,MAA4B,OAAO,QAAQ,MAAM;AAAA;AAE7D;AAEA,SAAS,eAAe,QAAgC;AACtD,MAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,WAAO,CAAA;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO;AAAA,EACT;AACA,SAAO,CAAA;AACT;AA6CO,SAAS,WAId,QACA,QACkD;AAClD,SAAO,IAAI,YAAY,IAAI,iBAAiB,MAAM,GAAG,MAAM;AAC7D;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-zero.js","sources":["../../../../zero-solid/src/use-zero.ts"],"sourcesContent":["import {\n batch,\n createContext,\n createEffect,\n createMemo,\n onCleanup,\n splitProps,\n untrack,\n useContext,\n type Accessor,\n type JSX,\n} from 'solid-js';\nimport {\n Zero,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type Schema,\n type ZeroOptions,\n} from './zero.ts';\n\nconst ZeroContext = createContext<\n // oxlint-disable-next-line no-explicit-any\n Accessor<Zero<any, any, any>> | undefined\n>(undefined);\n\n/**\n * @deprecated Use {@linkcode ZeroProvider} instead of managing your own Zero instance.\n */\nexport function createZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(options: ZeroOptions<S, MD, Context>): Zero<S, MD, Context> {\n const opts = {\n ...options,\n batchViewUpdates: batch,\n };\n return new Zero(opts);\n}\n\nexport function useZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(): () => Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nconst NO_AUTH_SET = Symbol();\n\nexport function ZeroProvider<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(\n props: {\n children: JSX.Element;\n init?: (zero: Zero<S, MD, Context>) => void;\n } & (\n | {\n zero: Zero<S, MD, Context>;\n }\n | ZeroOptions<S, MD, Context>\n ),\n) {\n const zero = createMemo(() => {\n if ('zero' in props) {\n return props.zero;\n }\n\n const [, options] = splitProps(props, ['children', 'auth']);\n\n const authValue = untrack(() => props.auth);\n const createdZero = new Zero({\n ...options,\n ...(authValue !== undefined ? {auth: authValue} : {}),\n batchViewUpdates: batch,\n });\n options.init?.(createdZero);\n onCleanup(() => createdZero.close());\n return createdZero;\n });\n\n const auth = createMemo<\n typeof NO_AUTH_SET | ZeroOptions<S, MD, Context>['auth']\n >(() => ('auth' in props ? props.auth : NO_AUTH_SET));\n\n let prevAuth: typeof NO_AUTH_SET | ZeroOptions<S, MD, Context>['auth'] =\n NO_AUTH_SET;\n\n createEffect(() => {\n const currentZero = zero();\n if (!currentZero) {\n return;\n }\n\n const currentAuth = auth();\n\n if (prevAuth === NO_AUTH_SET) {\n prevAuth = currentAuth;\n return;\n }\n\n if (currentAuth !== prevAuth) {\n prevAuth = currentAuth;\n void currentZero.connection.connect({\n auth: currentAuth === NO_AUTH_SET ? undefined : currentAuth,\n });\n }\n });\n\n return ZeroContext.Provider({\n value: zero,\n get children() {\n return props.children;\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAM,cAAc,cAGlB,MAAS;AAKJ,SAAS,WAId,SAA4D;AAC5D,QAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,kBAAkB;AAAA,EAAA;AAEpB,SAAO,IAAI,KAAK,IAAI;AACtB;AAEO,SAAS,UAIgB;AAC9B,QAAM,OAAO,WAAW,WAAW;AAEnC,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,SAAO;AACT;AAaO,SAAS,gBAIZ;AACF,SAAO,MAAM,QAAA;AACf;AAEA,MAAM,cAAc,OAAA;AAEb,SAAS,aAKd,OASA;AACA,QAAM,OAAO,WAAW,MAAM;AAC5B,QAAI,UAAU,OAAO;AACnB,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,CAAA,EAAG,OAAO,IAAI,WAAW,OAAO,CAAC,YAAY,MAAM,CAAC;AAE1D,UAAM,YAAY,QAAQ,MAAM,MAAM,IAAI;AAC1C,UAAM,cAAc,IAAI,KAAK;AAAA,MAC3B,GAAG;AAAA,MACH,GAAI,cAAc,SAAY,EAAC,MAAM,UAAA,IAAa,CAAA;AAAA,MAClD,kBAAkB;AAAA,IAAA,CACnB;AACD,YAAQ,OAAO,WAAW;AAC1B,cAAU,MAAM,YAAY,OAAO;AACnC,WAAO;AAAA,EACT,CAAC;AAED,QAAM,OAAO,WAEX,MAAO,UAAU,QAAQ,MAAM,OAAO,WAAY;AAEpD,MAAI,WACF;AAEF,eAAa,MAAM;AACjB,UAAM,cAAc,KAAA;AACpB,QAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"use-zero.js","sources":["../../../../zero-solid/src/use-zero.ts"],"sourcesContent":["import {\n batch,\n createContext,\n createEffect,\n createMemo,\n onCleanup,\n splitProps,\n untrack,\n useContext,\n type Accessor,\n type JSX,\n} from 'solid-js';\nimport {\n Zero,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type Schema,\n type ZeroOptions,\n} from './zero.ts';\n\nconst ZeroContext = createContext<\n // oxlint-disable-next-line no-explicit-any\n Accessor<Zero<any, any, any>> | undefined\n>(undefined);\n\n/**\n * @deprecated Use {@linkcode ZeroProvider} instead of managing your own Zero instance.\n */\nexport function createZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(options: ZeroOptions<S, MD, Context>): Zero<S, MD, Context> {\n const opts = {\n ...options,\n batchViewUpdates: batch,\n };\n return new Zero(opts);\n}\n\nexport function useZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(): () => Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nconst NO_AUTH_SET = Symbol();\n\nexport function ZeroProvider<\n S extends Schema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context = DefaultContext,\n>(\n props: {\n children: JSX.Element;\n init?: (zero: Zero<S, MD, Context>) => void;\n } & (\n | {\n zero: Zero<S, MD, Context>;\n }\n | ZeroOptions<S, MD, Context>\n ),\n) {\n const zero = createMemo(() => {\n if ('zero' in props) {\n return props.zero;\n }\n\n const [, options] = splitProps(props, ['children', 'auth']);\n\n const authValue = untrack(() => props.auth);\n const createdZero = new Zero({\n ...options,\n ...(authValue !== undefined ? {auth: authValue} : {}),\n batchViewUpdates: batch,\n });\n options.init?.(createdZero);\n onCleanup(() => createdZero.close());\n return createdZero;\n });\n\n const auth = createMemo<\n typeof NO_AUTH_SET | ZeroOptions<S, MD, Context>['auth']\n >(() => ('auth' in props ? props.auth : NO_AUTH_SET));\n\n let prevAuth: typeof NO_AUTH_SET | ZeroOptions<S, MD, Context>['auth'] =\n NO_AUTH_SET;\n\n createEffect(() => {\n const currentZero = zero();\n if (!currentZero || 'zero' in props) {\n return;\n }\n\n const currentAuth = auth();\n\n if (prevAuth === NO_AUTH_SET) {\n prevAuth = currentAuth;\n return;\n }\n\n if (currentAuth !== prevAuth) {\n prevAuth = currentAuth;\n void currentZero.connection.connect({\n auth: currentAuth === NO_AUTH_SET ? undefined : currentAuth,\n });\n }\n });\n\n return ZeroContext.Provider({\n value: zero,\n get children() {\n return props.children;\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAM,cAAc,cAGlB,MAAS;AAKJ,SAAS,WAId,SAA4D;AAC5D,QAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,kBAAkB;AAAA,EAAA;AAEpB,SAAO,IAAI,KAAK,IAAI;AACtB;AAEO,SAAS,UAIgB;AAC9B,QAAM,OAAO,WAAW,WAAW;AAEnC,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,SAAO;AACT;AAaO,SAAS,gBAIZ;AACF,SAAO,MAAM,QAAA;AACf;AAEA,MAAM,cAAc,OAAA;AAEb,SAAS,aAKd,OASA;AACA,QAAM,OAAO,WAAW,MAAM;AAC5B,QAAI,UAAU,OAAO;AACnB,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,CAAA,EAAG,OAAO,IAAI,WAAW,OAAO,CAAC,YAAY,MAAM,CAAC;AAE1D,UAAM,YAAY,QAAQ,MAAM,MAAM,IAAI;AAC1C,UAAM,cAAc,IAAI,KAAK;AAAA,MAC3B,GAAG;AAAA,MACH,GAAI,cAAc,SAAY,EAAC,MAAM,UAAA,IAAa,CAAA;AAAA,MAClD,kBAAkB;AAAA,IAAA,CACnB;AACD,YAAQ,OAAO,WAAW;AAC1B,cAAU,MAAM,YAAY,OAAO;AACnC,WAAO;AAAA,EACT,CAAC;AAED,QAAM,OAAO,WAEX,MAAO,UAAU,QAAQ,MAAM,OAAO,WAAY;AAEpD,MAAI,WACF;AAEF,eAAa,MAAM;AACjB,UAAM,cAAc,KAAA;AACpB,QAAI,CAAC,eAAe,UAAU,OAAO;AACnC;AAAA,IACF;AAEA,UAAM,cAAc,KAAA;AAEpB,QAAI,aAAa,aAAa;AAC5B,iBAAW;AACX;AAAA,IACF;AAEA,QAAI,gBAAgB,UAAU;AAC5B,iBAAW;AACX,WAAK,YAAY,WAAW,QAAQ;AAAA,QAClC,MAAM,gBAAgB,cAAc,SAAY;AAAA,MAAA,CACjD;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO,YAAY,SAAS;AAAA,IAC1B,OAAO;AAAA,IACP,IAAI,WAAW;AACb,aAAO,MAAM;AAAA,IACf;AAAA,EAAA,CACD;AACH;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rocicorp/zero",
|
|
3
|
-
"version": "0.25.10-canary.
|
|
3
|
+
"version": "0.25.10-canary.9",
|
|
4
4
|
"description": "Zero is a web framework for serverless web development.",
|
|
5
5
|
"author": "Rocicorp, Inc.",
|
|
6
6
|
"repository": {
|
|
@@ -155,6 +155,10 @@
|
|
|
155
155
|
"types": "./out/zero/src/adapters/drizzle.d.ts",
|
|
156
156
|
"default": "./out/zero/src/adapters/drizzle.js"
|
|
157
157
|
},
|
|
158
|
+
"./server/adapters/prisma": {
|
|
159
|
+
"types": "./out/zero/src/adapters/prisma.d.ts",
|
|
160
|
+
"default": "./out/zero/src/adapters/prisma.js"
|
|
161
|
+
},
|
|
158
162
|
"./server/adapters/pg": {
|
|
159
163
|
"types": "./out/zero/src/adapters/pg.d.ts",
|
|
160
164
|
"default": "./out/zero/src/adapters/pg.js"
|