@rocicorp/zero 0.25.7 → 0.26.0-canary.2
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/shared/src/custom-key-map.d.ts +4 -4
- package/out/shared/src/custom-key-map.d.ts.map +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/iterables.d.ts +6 -8
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +13 -7
- package/out/shared/src/iterables.js.map +1 -1
- package/out/zero/package.json.js +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +5 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +12 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/observability/events.d.ts.map +1 -1
- package/out/zero-cache/src/observability/events.js +15 -5
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +10 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +22 -4
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +0 -4
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -10
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +8 -2
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -14
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/replica-schema.js +8 -1
- package/out/zero-cache/src/services/change-source/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +5 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +16 -5
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +4 -4
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +9 -24
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +1 -2
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +51 -12
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +4 -3
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -2
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js +36 -31
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +5 -6
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +5 -23
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +6 -4
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +1 -8
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +2 -11
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +0 -2
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +2 -10
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +1 -2
- 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 +40 -42
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts +0 -1
- package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js +0 -2
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +2 -5
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +1 -4
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-client/src/client/context.js +1 -0
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-protocol/src/push.d.ts +7 -0
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +9 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +1 -0
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +41 -2
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/zql-database.d.ts.map +1 -1
- package/out/zero-server/src/zql-database.js +9 -0
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -0
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.js +1 -0
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/package.json +3 -3
- package/out/zero-cache/src/types/schema-versions.d.ts +0 -12
- package/out/zero-cache/src/types/schema-versions.d.ts.map +0 -1
- package/out/zero-cache/src/types/schema-versions.js +0 -28
- package/out/zero-cache/src/types/schema-versions.js.map +0 -1
|
@@ -37,14 +37,11 @@ async function connect(lc, { file, vacuumIntervalHours }, walMode, mode) {
|
|
|
37
37
|
const t0 = performance.now();
|
|
38
38
|
replica.unsafeMode(true);
|
|
39
39
|
replica.pragma("journal_mode = OFF");
|
|
40
|
-
replica.exec('DELETE FROM "_zero.changeLog"');
|
|
41
|
-
const t1 = performance.now();
|
|
42
|
-
lc.info?.(`Cleared _zero.changeLog (${t1 - t0} ms)`);
|
|
43
40
|
replica.exec("VACUUM");
|
|
44
41
|
recordEvent(replica, "vacuum");
|
|
45
42
|
replica.unsafeMode(false);
|
|
46
|
-
const
|
|
47
|
-
lc.info?.(`VACUUM completed (${
|
|
43
|
+
const t1 = performance.now();
|
|
44
|
+
lc.info?.(`VACUUM completed (${t1 - t0} ms)`);
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
47
|
lc.info?.(`setting ${file} to ${walMode} mode`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.js","sources":["../../../../../zero-cache/src/workers/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {ReplicaOptions} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {upgradeReplica} from '../services/change-source/replica-schema.ts';\nimport {Notifier} from '../services/replicator/notifier.ts';\nimport type {\n ReplicaState,\n ReplicaStateNotifier,\n Replicator,\n} from '../services/replicator/replicator.ts';\nimport {\n getAscendingEvents,\n recordEvent,\n} from '../services/replicator/schema/replication-state.ts';\nimport type {Worker} from '../types/processes.ts';\n\nexport const replicaFileModeSchema = v.literalUnion(\n 'serving',\n 'serving-copy',\n 'backup',\n);\n\nexport type ReplicaFileMode = v.Infer<typeof replicaFileModeSchema>;\n\nexport function replicaFileName(replicaFile: string, mode: ReplicaFileMode) {\n return mode === 'serving-copy' ? `${replicaFile}-serving-copy` : replicaFile;\n}\n\nconst MILLIS_PER_HOUR = 1000 * 60 * 60;\nconst MB = 1024 * 1024;\n\nasync function connect(\n lc: LogContext,\n {file, vacuumIntervalHours}: ReplicaOptions,\n walMode: 'wal' | 'wal2',\n mode: ReplicaFileMode,\n): Promise<Database> {\n const replica = new Database(lc, file);\n\n // Perform any upgrades to the replica in case the backup is an\n // earlier version.\n await upgradeReplica(lc, `${mode}-replica`, file);\n\n // Start by folding any (e.g. restored) WAL(2) files into the main db.\n replica.pragma('journal_mode = delete');\n\n const [{page_size: pageSize}] = replica.pragma<{page_size: number}>(\n 'page_size',\n );\n const [{page_count: pageCount}] = replica.pragma<{page_count: number}>(\n 'page_count',\n );\n const [{freelist_count: freelistCount}] = replica.pragma<{\n freelist_count: number;\n }>('freelist_count');\n\n const dbSize = ((pageCount * pageSize) / MB).toFixed(2);\n const freelistSize = ((freelistCount * pageSize) / MB).toFixed(2);\n\n // TODO: Consider adding a freelist size or ratio based vacuum trigger.\n lc.info?.(`Size of db ${file}: ${dbSize} MB (${freelistSize} MB freeable)`);\n\n // Check for the VACUUM threshold.\n const events = getAscendingEvents(replica);\n lc.debug?.(`Runtime events for db ${file}`, {events});\n if (vacuumIntervalHours !== undefined) {\n const millisSinceLastEvent =\n Date.now() - (events.at(-1)?.timestamp.getTime() ?? 0);\n if (millisSinceLastEvent / MILLIS_PER_HOUR > vacuumIntervalHours) {\n lc.info?.(`Performing maintenance cleanup on ${file}`);\n const t0 = performance.now();\n replica.unsafeMode(true);\n replica.pragma('journal_mode = OFF');\n
|
|
1
|
+
{"version":3,"file":"replicator.js","sources":["../../../../../zero-cache/src/workers/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {ReplicaOptions} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {upgradeReplica} from '../services/change-source/replica-schema.ts';\nimport {Notifier} from '../services/replicator/notifier.ts';\nimport type {\n ReplicaState,\n ReplicaStateNotifier,\n Replicator,\n} from '../services/replicator/replicator.ts';\nimport {\n getAscendingEvents,\n recordEvent,\n} from '../services/replicator/schema/replication-state.ts';\nimport type {Worker} from '../types/processes.ts';\n\nexport const replicaFileModeSchema = v.literalUnion(\n 'serving',\n 'serving-copy',\n 'backup',\n);\n\nexport type ReplicaFileMode = v.Infer<typeof replicaFileModeSchema>;\n\nexport function replicaFileName(replicaFile: string, mode: ReplicaFileMode) {\n return mode === 'serving-copy' ? `${replicaFile}-serving-copy` : replicaFile;\n}\n\nconst MILLIS_PER_HOUR = 1000 * 60 * 60;\nconst MB = 1024 * 1024;\n\nasync function connect(\n lc: LogContext,\n {file, vacuumIntervalHours}: ReplicaOptions,\n walMode: 'wal' | 'wal2',\n mode: ReplicaFileMode,\n): Promise<Database> {\n const replica = new Database(lc, file);\n\n // Perform any upgrades to the replica in case the backup is an\n // earlier version.\n await upgradeReplica(lc, `${mode}-replica`, file);\n\n // Start by folding any (e.g. restored) WAL(2) files into the main db.\n replica.pragma('journal_mode = delete');\n\n const [{page_size: pageSize}] = replica.pragma<{page_size: number}>(\n 'page_size',\n );\n const [{page_count: pageCount}] = replica.pragma<{page_count: number}>(\n 'page_count',\n );\n const [{freelist_count: freelistCount}] = replica.pragma<{\n freelist_count: number;\n }>('freelist_count');\n\n const dbSize = ((pageCount * pageSize) / MB).toFixed(2);\n const freelistSize = ((freelistCount * pageSize) / MB).toFixed(2);\n\n // TODO: Consider adding a freelist size or ratio based vacuum trigger.\n lc.info?.(`Size of db ${file}: ${dbSize} MB (${freelistSize} MB freeable)`);\n\n // Check for the VACUUM threshold.\n const events = getAscendingEvents(replica);\n lc.debug?.(`Runtime events for db ${file}`, {events});\n if (vacuumIntervalHours !== undefined) {\n const millisSinceLastEvent =\n Date.now() - (events.at(-1)?.timestamp.getTime() ?? 0);\n if (millisSinceLastEvent / MILLIS_PER_HOUR > vacuumIntervalHours) {\n lc.info?.(`Performing maintenance cleanup on ${file}`);\n const t0 = performance.now();\n replica.unsafeMode(true);\n replica.pragma('journal_mode = OFF');\n replica.exec('VACUUM');\n recordEvent(replica, 'vacuum');\n replica.unsafeMode(false);\n const t1 = performance.now();\n lc.info?.(`VACUUM completed (${t1 - t0} ms)`);\n }\n }\n\n lc.info?.(`setting ${file} to ${walMode} mode`);\n replica.pragma(`journal_mode = ${walMode}`);\n\n // The duration of the loop in which the replicator attempts\n // to begin a transaction while litestream is performing a\n // checkpoint.\n replica.pragma('busy_timeout = 30000');\n\n replica.pragma('optimize = 0x10002');\n // Cap the running time of `PRAGMA optimize` calls that happen\n // after replicating schema changes. 1000 is the limit recommended\n // in https://sqlite.org/lang_analyze.html#approx\n replica.pragma('analysis_limit = 1000');\n lc.info?.(`optimized ${file}`);\n return replica;\n}\n\nexport async function setupReplica(\n lc: LogContext,\n mode: ReplicaFileMode,\n replicaOptions: ReplicaOptions,\n): Promise<Database> {\n lc.info?.(`setting up ${mode} replica`);\n\n switch (mode) {\n case 'backup': {\n const replica = await connect(lc, replicaOptions, 'wal', mode);\n // https://litestream.io/tips/#disable-autocheckpoints-for-high-write-load-servers\n replica.pragma('wal_autocheckpoint = 0');\n return replica;\n }\n\n case 'serving-copy': {\n // In 'serving-copy' mode, the original file is being used for 'backup'\n // mode, so we make a copy for servicing sync requests.\n const {file} = replicaOptions;\n const copyLocation = replicaFileName(file, mode);\n deleteLiteDB(copyLocation);\n\n const start = Date.now();\n lc.info?.(`copying ${file} to ${copyLocation}`);\n const replica = new Database(lc, file);\n replica.prepare(`VACUUM INTO ?`).run(copyLocation);\n replica.close();\n lc.info?.(`finished copy (${Date.now() - start} ms)`);\n\n return connect(lc, {...replicaOptions, file: copyLocation}, 'wal2', mode);\n }\n\n case 'serving':\n return connect(lc, replicaOptions, 'wal2', mode);\n\n default:\n throw new Error(`Invalid ReplicaMode ${mode}`);\n }\n}\n\nexport function setUpMessageHandlers(\n lc: LogContext,\n replicator: Replicator,\n parent: Worker,\n) {\n handleSubscriptionsFrom(lc, parent, replicator);\n}\n\ntype Notification = ['notify', ReplicaState];\n\nexport function handleSubscriptionsFrom(\n lc: LogContext,\n subscriber: Worker,\n notifier: ReplicaStateNotifier,\n) {\n subscriber.onMessageType('subscribe', async () => {\n const subscription = notifier.subscribe();\n\n subscriber.on('close', () => {\n lc.debug?.(`closing replication subscription from ${subscriber.pid}`);\n subscription.cancel();\n });\n\n for await (const msg of subscription) {\n subscriber.send<Notification>(['notify', msg]);\n }\n });\n}\n\n/**\n * Creates a Notifier to relay notifications the notifier of another Worker.\n * This does not send the initial subscription message. Use {@link subscribeTo}\n * to initiate the subscription.\n */\nexport function createNotifierFrom(_lc: LogContext, source: Worker): Notifier {\n const notifier = new Notifier();\n source.onMessageType<Notification>('notify', msg =>\n notifier.notifySubscribers(msg),\n );\n return notifier;\n}\n\nexport function subscribeTo(_lc: LogContext, source: Worker) {\n source.send(['subscribe', {}]);\n}\n"],"names":["v.literalUnion"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer-ws-message-handler.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kCAAkC,CAAC;AAC/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,gCAAgC,CAAC;AAC5D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,+BAA+B,CAAC;AAC1D,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACX,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAInE,qBAAa,sBAAuB,YAAW,cAAc;;gBAezD,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"syncer-ws-message-handler.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kCAAkC,CAAC;AAC/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,gCAAgC,CAAC;AAC5D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,+BAA+B,CAAC;AAC1D,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACX,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAC,aAAa,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAInE,qBAAa,sBAAuB,YAAW,cAAc;;gBAezD,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,SAAS;IAkCtB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAmJ7D"}
|
|
@@ -27,7 +27,6 @@ class SyncerWsMessageHandler {
|
|
|
27
27
|
wsID,
|
|
28
28
|
baseCookie,
|
|
29
29
|
protocolVersion,
|
|
30
|
-
schemaVersion,
|
|
31
30
|
httpCookie
|
|
32
31
|
} = connectParams;
|
|
33
32
|
this.#viewSyncer = viewSyncer;
|
|
@@ -44,7 +43,6 @@ class SyncerWsMessageHandler {
|
|
|
44
43
|
wsID,
|
|
45
44
|
baseCookie,
|
|
46
45
|
protocolVersion,
|
|
47
|
-
schemaVersion,
|
|
48
46
|
tokenData,
|
|
49
47
|
httpCookie
|
|
50
48
|
};
|
|
@@ -65,7 +63,7 @@ class SyncerWsMessageHandler {
|
|
|
65
63
|
tracer,
|
|
66
64
|
"connection.push",
|
|
67
65
|
async () => {
|
|
68
|
-
const { clientGroupID, mutations
|
|
66
|
+
const { clientGroupID, mutations } = msg[1];
|
|
69
67
|
if (clientGroupID !== this.#clientGroupID) {
|
|
70
68
|
return [
|
|
71
69
|
{
|
|
@@ -105,7 +103,6 @@ class SyncerWsMessageHandler {
|
|
|
105
103
|
const maybeError = await this.#mutagen.processMutation(
|
|
106
104
|
mutation,
|
|
107
105
|
this.#authData,
|
|
108
|
-
schemaVersion,
|
|
109
106
|
this.#pusher !== void 0
|
|
110
107
|
);
|
|
111
108
|
if (maybeError !== void 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer-ws-message-handler.js","sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {JWTPayload} from 'jose';\nimport {startAsyncSpan, startSpan} from '../../../otel/src/span.ts';\nimport {version} from '../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport type {Upstream} from '../../../zero-protocol/src/up.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport type {\n SyncContext,\n TokenData,\n ViewSyncer,\n} from '../services/view-syncer/view-syncer.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport type {HandlerResult, MessageHandler} from './connection.ts';\n\nconst tracer = trace.getTracer('syncer-ws-server', version);\n\nexport class SyncerWsMessageHandler implements MessageHandler {\n readonly #viewSyncer: ViewSyncer;\n readonly #mutagen: Mutagen;\n readonly #mutationLock: Lock;\n readonly #lc: LogContext;\n readonly #authData: JWTPayload | undefined;\n readonly #clientGroupID: string;\n readonly #syncContext: SyncContext;\n readonly #pusher: Pusher | undefined;\n // DEPRECATED: remove #token\n // and forward auth and cookie headers that were\n // sent with the push.\n readonly #token: string | undefined;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n tokenData: TokenData | undefined,\n viewSyncer: ViewSyncer,\n mutagen: Mutagen,\n pusher: Pusher | undefined,\n ) {\n const {\n clientGroupID,\n clientID,\n profileID,\n wsID,\n baseCookie,\n protocolVersion,\n schemaVersion,\n httpCookie,\n } = connectParams;\n this.#viewSyncer = viewSyncer;\n this.#mutagen = mutagen;\n this.#mutationLock = new Lock();\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#authData = tokenData?.decoded;\n this.#token = tokenData?.raw;\n this.#clientGroupID = clientGroupID;\n this.#pusher = pusher;\n this.#syncContext = {\n clientID,\n profileID,\n wsID,\n baseCookie,\n protocolVersion,\n schemaVersion,\n tokenData,\n httpCookie,\n };\n }\n\n async handleMessage(msg: Upstream): Promise<HandlerResult[]> {\n const lc = this.#lc;\n const msgType = msg[0];\n const viewSyncer = this.#viewSyncer;\n switch (msgType) {\n case 'ping':\n lc.error?.('Ping is not supported at this layer by Zero');\n break;\n case 'pull':\n lc.error?.('Pull is not supported by Zero');\n break;\n case 'push': {\n return startAsyncSpan<HandlerResult[]>(\n tracer,\n 'connection.push',\n async () => {\n const {clientGroupID, mutations, schemaVersion} = msg[1];\n if (clientGroupID !== this.#clientGroupID) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n `clientGroupID in mutation \"${clientGroupID}\" does not match ` +\n `clientGroupID of connection \"${this.#clientGroupID}`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n if (mutations.length === 0) {\n return [\n {\n type: 'ok',\n },\n ];\n }\n\n // The client only ever sends 1 mutation per push.\n // #pusher will throw if it sees a CRUD mutation.\n // #mutagen will throw if it see a custom mutation.\n if (mutations[0].type === 'custom') {\n assert(\n this.#pusher,\n 'A ZERO_MUTATE_URL must be set in order to process custom mutations.',\n );\n return [\n this.#pusher.enqueuePush(\n this.#syncContext.clientID,\n msg[1],\n this.#token,\n this.#syncContext.httpCookie,\n ),\n ];\n }\n\n // Hold a connection-level lock while processing mutations so that:\n // 1. Mutations are processed in the order in which they are received and\n // 2. A single view syncer connection cannot hog multiple upstream connections.\n const ret = await this.#mutationLock.withLock(async () => {\n const errors: ErrorBody[] = [];\n for (const mutation of mutations) {\n const maybeError = await this.#mutagen.processMutation(\n mutation,\n this.#authData,\n schemaVersion,\n this.#pusher !== undefined,\n );\n if (maybeError !== undefined) {\n errors.push({\n kind: maybeError[0],\n message: maybeError[1],\n origin: ErrorOrigin.ZeroCache,\n });\n }\n }\n if (errors.length > 0) {\n return {type: 'transient', errors} satisfies HandlerResult;\n }\n return {type: 'ok'} satisfies HandlerResult;\n });\n return [ret];\n },\n );\n }\n case 'changeDesiredQueries':\n await startAsyncSpan(tracer, 'connection.changeDesiredQueries', () =>\n viewSyncer.changeDesiredQueries(this.#syncContext, msg),\n );\n break;\n case 'deleteClients':\n await startAsyncSpan(tracer, 'connection.deleteClients', () =>\n viewSyncer.deleteClients(this.#syncContext, msg),\n );\n break;\n case 'initConnection': {\n const ret: HandlerResult[] = [\n {\n type: 'stream',\n source: 'viewSyncer',\n stream: startSpan(tracer, 'connection.initConnection', () =>\n viewSyncer.initConnection(this.#syncContext, msg),\n ),\n },\n ];\n\n // Given we support both CRUD and Custom mutators,\n // we do not initialize the `pusher` unless the user has opted\n // into custom mutations. We detect that by checking\n // if the pushURL has been set.\n if (this.#pusher) {\n ret.push({\n type: 'stream',\n source: 'pusher',\n stream: this.#pusher.initConnection(\n this.#syncContext.clientID,\n this.#syncContext.wsID,\n msg[1].userPushURL,\n ),\n });\n }\n\n return ret;\n }\n case 'closeConnection':\n // This message is deprecated and no longer used.\n break;\n\n case 'inspect':\n await startAsyncSpan(tracer, 'connection.inspect', () =>\n viewSyncer.inspect(this.#syncContext, msg),\n );\n break;\n\n case 'ackMutationResponses':\n if (this.#pusher) {\n await this.#pusher.ackMutationResponses(msg[1]);\n }\n break;\n\n default:\n unreachable(msgType);\n }\n\n return [{type: 'ok'}];\n }\n}\n"],"names":["ErrorKind.InvalidPush","ErrorOrigin.ZeroCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncer-ws-message-handler.js","sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {JWTPayload} from 'jose';\nimport {startAsyncSpan, startSpan} from '../../../otel/src/span.ts';\nimport {version} from '../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport type {Upstream} from '../../../zero-protocol/src/up.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport type {\n SyncContext,\n TokenData,\n ViewSyncer,\n} from '../services/view-syncer/view-syncer.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport type {HandlerResult, MessageHandler} from './connection.ts';\n\nconst tracer = trace.getTracer('syncer-ws-server', version);\n\nexport class SyncerWsMessageHandler implements MessageHandler {\n readonly #viewSyncer: ViewSyncer;\n readonly #mutagen: Mutagen;\n readonly #mutationLock: Lock;\n readonly #lc: LogContext;\n readonly #authData: JWTPayload | undefined;\n readonly #clientGroupID: string;\n readonly #syncContext: SyncContext;\n readonly #pusher: Pusher | undefined;\n // DEPRECATED: remove #token\n // and forward auth and cookie headers that were\n // sent with the push.\n readonly #token: string | undefined;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n tokenData: TokenData | undefined,\n viewSyncer: ViewSyncer,\n mutagen: Mutagen,\n pusher: Pusher | undefined,\n ) {\n const {\n clientGroupID,\n clientID,\n profileID,\n wsID,\n baseCookie,\n protocolVersion,\n httpCookie,\n } = connectParams;\n this.#viewSyncer = viewSyncer;\n this.#mutagen = mutagen;\n this.#mutationLock = new Lock();\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#authData = tokenData?.decoded;\n this.#token = tokenData?.raw;\n this.#clientGroupID = clientGroupID;\n this.#pusher = pusher;\n this.#syncContext = {\n clientID,\n profileID,\n wsID,\n baseCookie,\n protocolVersion,\n tokenData,\n httpCookie,\n };\n }\n\n async handleMessage(msg: Upstream): Promise<HandlerResult[]> {\n const lc = this.#lc;\n const msgType = msg[0];\n const viewSyncer = this.#viewSyncer;\n switch (msgType) {\n case 'ping':\n lc.error?.('Ping is not supported at this layer by Zero');\n break;\n case 'pull':\n lc.error?.('Pull is not supported by Zero');\n break;\n case 'push': {\n return startAsyncSpan<HandlerResult[]>(\n tracer,\n 'connection.push',\n async () => {\n const {clientGroupID, mutations} = msg[1];\n if (clientGroupID !== this.#clientGroupID) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n `clientGroupID in mutation \"${clientGroupID}\" does not match ` +\n `clientGroupID of connection \"${this.#clientGroupID}`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n if (mutations.length === 0) {\n return [\n {\n type: 'ok',\n },\n ];\n }\n\n // The client only ever sends 1 mutation per push.\n // #pusher will throw if it sees a CRUD mutation.\n // #mutagen will throw if it see a custom mutation.\n if (mutations[0].type === 'custom') {\n assert(\n this.#pusher,\n 'A ZERO_MUTATE_URL must be set in order to process custom mutations.',\n );\n return [\n this.#pusher.enqueuePush(\n this.#syncContext.clientID,\n msg[1],\n this.#token,\n this.#syncContext.httpCookie,\n ),\n ];\n }\n\n // Hold a connection-level lock while processing mutations so that:\n // 1. Mutations are processed in the order in which they are received and\n // 2. A single view syncer connection cannot hog multiple upstream connections.\n const ret = await this.#mutationLock.withLock(async () => {\n const errors: ErrorBody[] = [];\n for (const mutation of mutations) {\n const maybeError = await this.#mutagen.processMutation(\n mutation,\n this.#authData,\n this.#pusher !== undefined,\n );\n if (maybeError !== undefined) {\n errors.push({\n kind: maybeError[0],\n message: maybeError[1],\n origin: ErrorOrigin.ZeroCache,\n });\n }\n }\n if (errors.length > 0) {\n return {type: 'transient', errors} satisfies HandlerResult;\n }\n return {type: 'ok'} satisfies HandlerResult;\n });\n return [ret];\n },\n );\n }\n case 'changeDesiredQueries':\n await startAsyncSpan(tracer, 'connection.changeDesiredQueries', () =>\n viewSyncer.changeDesiredQueries(this.#syncContext, msg),\n );\n break;\n case 'deleteClients':\n await startAsyncSpan(tracer, 'connection.deleteClients', () =>\n viewSyncer.deleteClients(this.#syncContext, msg),\n );\n break;\n case 'initConnection': {\n const ret: HandlerResult[] = [\n {\n type: 'stream',\n source: 'viewSyncer',\n stream: startSpan(tracer, 'connection.initConnection', () =>\n viewSyncer.initConnection(this.#syncContext, msg),\n ),\n },\n ];\n\n // Given we support both CRUD and Custom mutators,\n // we do not initialize the `pusher` unless the user has opted\n // into custom mutations. We detect that by checking\n // if the pushURL has been set.\n if (this.#pusher) {\n ret.push({\n type: 'stream',\n source: 'pusher',\n stream: this.#pusher.initConnection(\n this.#syncContext.clientID,\n this.#syncContext.wsID,\n msg[1].userPushURL,\n ),\n });\n }\n\n return ret;\n }\n case 'closeConnection':\n // This message is deprecated and no longer used.\n break;\n\n case 'inspect':\n await startAsyncSpan(tracer, 'connection.inspect', () =>\n viewSyncer.inspect(this.#syncContext, msg),\n );\n break;\n\n case 'ackMutationResponses':\n if (this.#pusher) {\n await this.#pusher.ackMutationResponses(msg[1]);\n }\n break;\n\n default:\n unreachable(msgType);\n }\n\n return [{type: 'ok'}];\n }\n}\n"],"names":["ErrorKind.InvalidPush","ErrorOrigin.ZeroCache"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import "../../../shared/src/config.js";
|
|
|
6
6
|
import "../../../replicache/src/hash.js";
|
|
7
7
|
import "../../../replicache/src/btree/node.js";
|
|
8
8
|
import "compare-utf8";
|
|
9
|
+
import "../../../shared/src/iterables.js";
|
|
9
10
|
import "../../../shared/src/btree-set.js";
|
|
10
11
|
import "../../../zero-protocol/src/ast.js";
|
|
11
12
|
import "js-xxhash";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sources":["../../../../../zero-client/src/client/context.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NoIndexDiff} from '../../../replicache/src/btree/node.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport type {DebugDelegate} from '../../../zql/src/builder/debug-delegate.ts';\nimport type {Input} from '../../../zql/src/ivm/operator.ts';\nimport type {Source, SourceInput} from '../../../zql/src/ivm/source.ts';\nimport {MeasurePushOperator} from '../../../zql/src/query/measure-push-operator.ts';\nimport type {MetricsDelegate} from '../../../zql/src/query/metrics-delegate.ts';\nimport {QueryDelegateBase} from '../../../zql/src/query/query-delegate-base.ts';\nimport type {CommitListener} from '../../../zql/src/query/query-delegate.ts';\nimport type {RunOptions} from '../../../zql/src/query/query.ts';\nimport {type IVMSourceBranch} from './ivm-branch.ts';\nimport type {QueryManager} from './query-manager.ts';\n\nexport type AddQuery = QueryManager['addLegacy'];\nexport type AddCustomQuery = QueryManager['addCustom'];\n\nexport type UpdateQuery = QueryManager['updateLegacy'];\nexport type UpdateCustomQuery = QueryManager['updateCustom'];\nexport type FlushQueryChanges = QueryManager['flushBatch'];\n\n/**\n * ZeroContext glues together zql and Replicache. It listens to changes in\n * Replicache data and pushes them into IVM and on tells the server about new\n * queries.\n */\nexport class ZeroContext extends QueryDelegateBase {\n readonly #lc: LogContext;\n\n // It is a bummer to have to maintain separate MemorySources here and copy the\n // data in from the Replicache db. But we want the data to be accessible via\n // pipelines *synchronously* and the core Replicache infra is all async. So\n // that needs to be fixed.\n readonly #mainSources: IVMSourceBranch;\n\n readonly addServerQuery: AddQuery;\n readonly addCustomQuery: AddCustomQuery;\n readonly updateServerQuery: UpdateQuery;\n readonly updateCustomQuery: UpdateCustomQuery;\n readonly flushQueryChanges: () => void;\n readonly #batchViewUpdates: (applyViewUpdates: () => void) => void;\n readonly #commitListeners: Set<CommitListener> = new Set();\n\n readonly assertValidRunOptions: (options?: RunOptions) => void;\n\n /**\n * Client-side queries start out as \"unknown\" and are then updated to\n * \"complete\" once the server has sent back the query result.\n */\n readonly defaultQueryComplete = false;\n\n readonly addMetric: MetricsDelegate['addMetric'];\n\n constructor(\n lc: LogContext,\n mainSources: IVMSourceBranch,\n addQuery: AddQuery,\n addCustomQuery: AddCustomQuery,\n updateQuery: UpdateQuery,\n updateCustomQuery: UpdateCustomQuery,\n flushQueryChanges: () => void,\n batchViewUpdates: (applyViewUpdates: () => void) => void,\n addMetric: MetricsDelegate['addMetric'],\n assertValidRunOptions: (options?: RunOptions) => void,\n ) {\n super();\n this.#lc = lc;\n this.#mainSources = mainSources;\n this.addServerQuery = addQuery;\n this.updateServerQuery = updateQuery;\n this.updateCustomQuery = updateCustomQuery;\n this.#batchViewUpdates = batchViewUpdates;\n this.assertValidRunOptions = assertValidRunOptions;\n this.addCustomQuery = addCustomQuery;\n this.flushQueryChanges = flushQueryChanges;\n this.addMetric = addMetric;\n }\n\n applyFiltersAnyway?: boolean | undefined;\n\n debug?: DebugDelegate | undefined;\n\n getSource(name: string): Source | undefined {\n return this.#mainSources.getSource(name);\n }\n\n mapAst(ast: AST): AST {\n return ast;\n }\n\n override decorateSourceInput(input: SourceInput, queryID: string): Input {\n return new MeasurePushOperator(input, queryID, this, 'query-update-client');\n }\n\n onTransactionCommit(cb: CommitListener): () => void {\n this.#commitListeners.add(cb);\n return () => {\n this.#commitListeners.delete(cb);\n };\n }\n\n override batchViewUpdates<T>(applyViewUpdates: () => T) {\n let result: T | undefined;\n let viewChangesPerformed = false;\n this.#batchViewUpdates(() => {\n result = applyViewUpdates();\n viewChangesPerformed = true;\n });\n assert(\n viewChangesPerformed,\n 'batchViewUpdates must call applyViewUpdates synchronously.',\n );\n return result as T;\n }\n\n processChanges(\n expectedHead: Hash | undefined,\n newHead: Hash,\n changes: NoIndexDiff,\n ) {\n this.batchViewUpdates(() => {\n try {\n this.#mainSources.advance(expectedHead, newHead, changes);\n } finally {\n this.#endTransaction();\n }\n });\n }\n\n #endTransaction() {\n for (const listener of this.#commitListeners) {\n try {\n listener();\n } catch (e) {\n // We should not fatal the inner-workings of Zero due to the user's application\n // code throwing an error.\n // Hence we wrap notifications in a try-catch block.\n this.#lc.error?.(\n ErrorKind.Internal,\n 'Failed notifying a commit listener of IVM updates',\n e,\n );\n }\n }\n }\n}\n"],"names":["ErrorKind.Internal"],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.js","sources":["../../../../../zero-client/src/client/context.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NoIndexDiff} from '../../../replicache/src/btree/node.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport type {DebugDelegate} from '../../../zql/src/builder/debug-delegate.ts';\nimport type {Input} from '../../../zql/src/ivm/operator.ts';\nimport type {Source, SourceInput} from '../../../zql/src/ivm/source.ts';\nimport {MeasurePushOperator} from '../../../zql/src/query/measure-push-operator.ts';\nimport type {MetricsDelegate} from '../../../zql/src/query/metrics-delegate.ts';\nimport {QueryDelegateBase} from '../../../zql/src/query/query-delegate-base.ts';\nimport type {CommitListener} from '../../../zql/src/query/query-delegate.ts';\nimport type {RunOptions} from '../../../zql/src/query/query.ts';\nimport {type IVMSourceBranch} from './ivm-branch.ts';\nimport type {QueryManager} from './query-manager.ts';\n\nexport type AddQuery = QueryManager['addLegacy'];\nexport type AddCustomQuery = QueryManager['addCustom'];\n\nexport type UpdateQuery = QueryManager['updateLegacy'];\nexport type UpdateCustomQuery = QueryManager['updateCustom'];\nexport type FlushQueryChanges = QueryManager['flushBatch'];\n\n/**\n * ZeroContext glues together zql and Replicache. It listens to changes in\n * Replicache data and pushes them into IVM and on tells the server about new\n * queries.\n */\nexport class ZeroContext extends QueryDelegateBase {\n readonly #lc: LogContext;\n\n // It is a bummer to have to maintain separate MemorySources here and copy the\n // data in from the Replicache db. But we want the data to be accessible via\n // pipelines *synchronously* and the core Replicache infra is all async. So\n // that needs to be fixed.\n readonly #mainSources: IVMSourceBranch;\n\n readonly addServerQuery: AddQuery;\n readonly addCustomQuery: AddCustomQuery;\n readonly updateServerQuery: UpdateQuery;\n readonly updateCustomQuery: UpdateCustomQuery;\n readonly flushQueryChanges: () => void;\n readonly #batchViewUpdates: (applyViewUpdates: () => void) => void;\n readonly #commitListeners: Set<CommitListener> = new Set();\n\n readonly assertValidRunOptions: (options?: RunOptions) => void;\n\n /**\n * Client-side queries start out as \"unknown\" and are then updated to\n * \"complete\" once the server has sent back the query result.\n */\n readonly defaultQueryComplete = false;\n\n readonly addMetric: MetricsDelegate['addMetric'];\n\n constructor(\n lc: LogContext,\n mainSources: IVMSourceBranch,\n addQuery: AddQuery,\n addCustomQuery: AddCustomQuery,\n updateQuery: UpdateQuery,\n updateCustomQuery: UpdateCustomQuery,\n flushQueryChanges: () => void,\n batchViewUpdates: (applyViewUpdates: () => void) => void,\n addMetric: MetricsDelegate['addMetric'],\n assertValidRunOptions: (options?: RunOptions) => void,\n ) {\n super();\n this.#lc = lc;\n this.#mainSources = mainSources;\n this.addServerQuery = addQuery;\n this.updateServerQuery = updateQuery;\n this.updateCustomQuery = updateCustomQuery;\n this.#batchViewUpdates = batchViewUpdates;\n this.assertValidRunOptions = assertValidRunOptions;\n this.addCustomQuery = addCustomQuery;\n this.flushQueryChanges = flushQueryChanges;\n this.addMetric = addMetric;\n }\n\n applyFiltersAnyway?: boolean | undefined;\n\n debug?: DebugDelegate | undefined;\n\n getSource(name: string): Source | undefined {\n return this.#mainSources.getSource(name);\n }\n\n mapAst(ast: AST): AST {\n return ast;\n }\n\n override decorateSourceInput(input: SourceInput, queryID: string): Input {\n return new MeasurePushOperator(input, queryID, this, 'query-update-client');\n }\n\n onTransactionCommit(cb: CommitListener): () => void {\n this.#commitListeners.add(cb);\n return () => {\n this.#commitListeners.delete(cb);\n };\n }\n\n override batchViewUpdates<T>(applyViewUpdates: () => T) {\n let result: T | undefined;\n let viewChangesPerformed = false;\n this.#batchViewUpdates(() => {\n result = applyViewUpdates();\n viewChangesPerformed = true;\n });\n assert(\n viewChangesPerformed,\n 'batchViewUpdates must call applyViewUpdates synchronously.',\n );\n return result as T;\n }\n\n processChanges(\n expectedHead: Hash | undefined,\n newHead: Hash,\n changes: NoIndexDiff,\n ) {\n this.batchViewUpdates(() => {\n try {\n this.#mainSources.advance(expectedHead, newHead, changes);\n } finally {\n this.#endTransaction();\n }\n });\n }\n\n #endTransaction() {\n for (const listener of this.#commitListeners) {\n try {\n listener();\n } catch (e) {\n // We should not fatal the inner-workings of Zero due to the user's application\n // code throwing an error.\n // Hence we wrap notifications in a try-catch block.\n this.#lc.error?.(\n ErrorKind.Internal,\n 'Failed notifying a commit listener of IVM updates',\n e,\n );\n }\n }\n }\n}\n"],"names":["ErrorKind.Internal"],"mappings":";;;;;;;;;;;;;;AA6BO,MAAM,oBAAoB,kBAAkB;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,uCAA4C,IAAA;AAAA,EAE5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,uBAAuB;AAAA,EAEvB;AAAA,EAET,YACE,IACA,aACA,UACA,gBACA,aACA,mBACA,mBACA,kBACA,WACA,uBACA;AACA,UAAA;AACA,SAAK,MAAM;AACX,SAAK,eAAe;AACpB,SAAK,iBAAiB;AACtB,SAAK,oBAAoB;AACzB,SAAK,oBAAoB;AACzB,SAAK,oBAAoB;AACzB,SAAK,wBAAwB;AAC7B,SAAK,iBAAiB;AACtB,SAAK,oBAAoB;AACzB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA,UAAU,MAAkC;AAC1C,WAAO,KAAK,aAAa,UAAU,IAAI;AAAA,EACzC;AAAA,EAEA,OAAO,KAAe;AACpB,WAAO;AAAA,EACT;AAAA,EAES,oBAAoB,OAAoB,SAAwB;AACvE,WAAO,IAAI,oBAAoB,OAAO,SAAS,MAAM,qBAAqB;AAAA,EAC5E;AAAA,EAEA,oBAAoB,IAAgC;AAClD,SAAK,iBAAiB,IAAI,EAAE;AAC5B,WAAO,MAAM;AACX,WAAK,iBAAiB,OAAO,EAAE;AAAA,IACjC;AAAA,EACF;AAAA,EAES,iBAAoB,kBAA2B;AACtD,QAAI;AACJ,QAAI,uBAAuB;AAC3B,SAAK,kBAAkB,MAAM;AAC3B,eAAS,iBAAA;AACT,6BAAuB;AAAA,IACzB,CAAC;AACD;AAAA,MACE;AAAA,MACA;AAAA,IAAA;AAEF,WAAO;AAAA,EACT;AAAA,EAEA,eACE,cACA,SACA,SACA;AACA,SAAK,iBAAiB,MAAM;AAC1B,UAAI;AACF,aAAK,aAAa,QAAQ,cAAc,SAAS,OAAO;AAAA,MAC1D,UAAA;AACE,aAAK,gBAAA;AAAA,MACP;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,kBAAkB;AAChB,eAAW,YAAY,KAAK,kBAAkB;AAC5C,UAAI;AACF,iBAAA;AAAA,MACF,SAAS,GAAG;AAIV,aAAK,IAAI;AAAA,UACPA;AAAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA,EACF;AACF;"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as v from '../../shared/src/valita.ts';
|
|
2
2
|
import type { NameMapper } from '../../zero-types/src/name-mapper.ts';
|
|
3
3
|
export declare const CRUD_MUTATION_NAME = "_zero_crud";
|
|
4
|
+
export declare const CLEANUP_RESULTS_MUTATION_NAME = "_zero_cleanupResults";
|
|
5
|
+
export declare const cleanupResultsArgSchema: v.ObjectType<{
|
|
6
|
+
clientGroupID: v.Type<string>;
|
|
7
|
+
clientID: v.Type<string>;
|
|
8
|
+
upToMutationID: v.Type<number>;
|
|
9
|
+
}, undefined>;
|
|
4
10
|
/**
|
|
5
11
|
* Inserts if entity with id does not already exist.
|
|
6
12
|
*/
|
|
@@ -581,6 +587,7 @@ export type PushError = v.Infer<typeof pushErrorSchema>;
|
|
|
581
587
|
export type PushOk = v.Infer<typeof pushOkSchema>;
|
|
582
588
|
export type MutationResult = v.Infer<typeof mutationResultSchema>;
|
|
583
589
|
export type AckMutationMessage = v.Infer<typeof ackMutationResponsesMessageSchema>;
|
|
590
|
+
export type CleanupResultsArg = v.Infer<typeof cleanupResultsArgSchema>;
|
|
584
591
|
export type { MutationID } from './mutation-id.ts';
|
|
585
592
|
export declare function mapCRUD(arg: CRUDMutationArg, map: NameMapper): CRUDMutationArg;
|
|
586
593
|
//# sourceMappingURL=push.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/push.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,4BAA4B,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AAUpE,eAAO,MAAM,kBAAkB,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,8 +5,14 @@ import { pushFailedBodySchema } from "./error.js";
|
|
|
5
5
|
import { mutationIDSchema } from "./mutation-id.js";
|
|
6
6
|
import { CRUD, Custom } from "./mutation-type-enum.js";
|
|
7
7
|
import { primaryKeySchema, primaryKeyValueRecordSchema } from "./primary-key.js";
|
|
8
|
-
import { object, string, literal, union, array, tuple
|
|
8
|
+
import { object, number, string, literal, union, array, tuple } from "@badrap/valita";
|
|
9
9
|
const CRUD_MUTATION_NAME = "_zero_crud";
|
|
10
|
+
const CLEANUP_RESULTS_MUTATION_NAME = "_zero_cleanupResults";
|
|
11
|
+
const cleanupResultsArgSchema = object({
|
|
12
|
+
clientGroupID: string(),
|
|
13
|
+
clientID: string(),
|
|
14
|
+
upToMutationID: number()
|
|
15
|
+
});
|
|
10
16
|
const insertOpSchema = object({
|
|
11
17
|
op: literal("insert"),
|
|
12
18
|
tableName: string(),
|
|
@@ -170,8 +176,10 @@ function mapCRUD(arg, map) {
|
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
178
|
export {
|
|
179
|
+
CLEANUP_RESULTS_MUTATION_NAME,
|
|
173
180
|
CRUD_MUTATION_NAME,
|
|
174
181
|
ackMutationResponsesMessageSchema,
|
|
182
|
+
cleanupResultsArgSchema,
|
|
175
183
|
crudMutationSchema,
|
|
176
184
|
customMutationSchema,
|
|
177
185
|
mapCRUD,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sources":["../../../../zero-protocol/src/push.ts"],"sourcesContent":["import {jsonSchema} from '../../shared/src/json-schema.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {NameMapper} from '../../zero-types/src/name-mapper.ts';\nimport {rowSchema} from './data.ts';\nimport {pushFailedBodySchema} from './error.ts';\nimport {mutationIDSchema} from './mutation-id.ts';\nimport * as MutationType from './mutation-type-enum.ts';\nimport {primaryKeySchema, primaryKeyValueRecordSchema} from './primary-key.ts';\n\n// NOTE! If you change this name you must also change the\n// string in `replicache-impl.ts` But CRUD mutators are being\n// deleted soon so this should not happen.\nexport const CRUD_MUTATION_NAME = '_zero_crud';\n\n/**\n * Inserts if entity with id does not already exist.\n */\nconst insertOpSchema = v.object({\n op: v.literal('insert'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n value: rowSchema,\n});\n\n/**\n * Upsert semantics. Inserts if entity with id does not already exist,\n * otherwise updates existing entity with id.\n */\nconst upsertOpSchema = v.object({\n op: v.literal('upsert'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n value: rowSchema,\n});\n\n/**\n * Updates if entity with id exists, otherwise does nothing.\n */\nconst updateOpSchema = v.object({\n op: v.literal('update'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n // Partial value with at least the primary key fields\n value: rowSchema,\n});\n\n/**\n * Deletes entity with id if it exists, otherwise does nothing.\n */\nconst deleteOpSchema = v.object({\n op: v.literal('delete'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n // Partial value representing the primary key\n value: primaryKeyValueRecordSchema,\n});\n\nconst crudOpSchema = v.union(\n insertOpSchema,\n upsertOpSchema,\n updateOpSchema,\n deleteOpSchema,\n);\n\nconst crudArgSchema = v.object({\n ops: v.array(crudOpSchema),\n});\n\nconst crudArgsSchema = v.tuple([crudArgSchema]);\n\nexport const crudMutationSchema = v.object({\n type: v.literal(MutationType.CRUD),\n id: v.number(),\n clientID: v.string(),\n name: v.literal(CRUD_MUTATION_NAME),\n args: crudArgsSchema,\n timestamp: v.number(),\n});\n\nexport const customMutationSchema = v.object({\n type: v.literal(MutationType.Custom),\n id: v.number(),\n clientID: v.string(),\n name: v.string(),\n args: v.array(jsonSchema),\n timestamp: v.number(),\n});\n\nexport const mutationSchema = v.union(crudMutationSchema, customMutationSchema);\n\nexport const pushBodySchema = v.object({\n clientGroupID: v.string(),\n mutations: v.array(mutationSchema),\n pushVersion: v.number(),\n // For legacy (CRUD) mutations, the schema is tied to the client group /\n // sync connection. For custom mutations, schema versioning is delegated\n // to the custom protocol / api-server.\n schemaVersion: v.number().optional(),\n timestamp: v.number(),\n requestID: v.string(),\n});\n\nexport const pushMessageSchema = v.tuple([v.literal('push'), pushBodySchema]);\n\nconst appErrorSchema = v.object({\n error: v.literal('app'),\n // The user can return any additional data here\n message: v.string().optional(),\n details: jsonSchema.optional(),\n});\nconst zeroErrorSchema = v.object({\n error: v.union(\n /** @deprecated push oooMutation errors are now represented as ['error', { ... }] messages */\n v.literal('oooMutation'),\n v.literal('alreadyProcessed'),\n ),\n details: jsonSchema.optional(),\n});\n\nconst mutationOkSchema = v.object({\n // The user can return any additional data here\n data: jsonSchema.optional(),\n});\nconst mutationErrorSchema = v.union(appErrorSchema, zeroErrorSchema);\n\nexport const mutationResultSchema = v.union(\n mutationOkSchema,\n mutationErrorSchema,\n);\n\nexport const mutationResponseSchema = v.object({\n id: mutationIDSchema,\n result: mutationResultSchema,\n});\n\nconst pushOkSchema = v.object({\n mutations: v.array(mutationResponseSchema),\n});\n\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst unsupportedPushVersionSchema = v.object({\n /** @deprecated */\n error: v.literal('unsupportedPushVersion'),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst unsupportedSchemaVersionSchema = v.object({\n /** @deprecated */\n error: v.literal('unsupportedSchemaVersion'),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push http errors are now represented as ['error', { ... }] messages\n */\nconst httpErrorSchema = v.object({\n /** @deprecated */\n error: v.literal('http'),\n /** @deprecated */\n status: v.number(),\n /** @deprecated */\n details: v.string(),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push zero errors are now represented as ['error', { ... }] messages\n */\nconst zeroPusherErrorSchema = v.object({\n /** @deprecated */\n error: v.literal('zeroPusher'),\n /** @deprecated */\n details: v.string(),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst pushErrorSchema = v.union(\n unsupportedPushVersionSchema,\n unsupportedSchemaVersionSchema,\n httpErrorSchema,\n zeroPusherErrorSchema,\n);\n\nexport const pushResponseBodySchema = v.union(pushOkSchema, pushErrorSchema);\n\nexport const pushResponseSchema = v.union(\n pushResponseBodySchema,\n pushFailedBodySchema,\n);\nexport const pushResponseMessageSchema = v.tuple([\n v.literal('pushResponse'),\n pushResponseBodySchema,\n]);\n\nexport const ackMutationResponsesMessageSchema = v.tuple([\n v.literal('ackMutationResponses'),\n mutationIDSchema,\n]);\n\n/**\n * The schema for the querystring parameters of the custom push endpoint.\n */\nexport const pushParamsSchema = v.object({\n schema: v.string(),\n appID: v.string(),\n});\n\nexport type InsertOp = v.Infer<typeof insertOpSchema>;\nexport type UpsertOp = v.Infer<typeof upsertOpSchema>;\nexport type UpdateOp = v.Infer<typeof updateOpSchema>;\nexport type DeleteOp = v.Infer<typeof deleteOpSchema>;\nexport type CRUDOp = v.Infer<typeof crudOpSchema>;\nexport type CRUDOpKind = CRUDOp['op'];\nexport type CRUDMutationArg = v.Infer<typeof crudArgSchema>;\nexport type CRUDMutation = v.Infer<typeof crudMutationSchema>;\nexport type CustomMutation = v.Infer<typeof customMutationSchema>;\nexport type Mutation = v.Infer<typeof mutationSchema>;\nexport type PushBody = v.Infer<typeof pushBodySchema>;\nexport type PushMessage = v.Infer<typeof pushMessageSchema>;\nexport type PushResponseBody = v.Infer<typeof pushResponseBodySchema>;\nexport type PushResponse = v.Infer<typeof pushResponseSchema>;\nexport type PushResponseMessage = v.Infer<typeof pushResponseMessageSchema>;\nexport type MutationResponse = v.Infer<typeof mutationResponseSchema>;\nexport type MutationOk = v.Infer<typeof mutationOkSchema>;\nexport type MutationError = v.Infer<typeof mutationErrorSchema>;\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nexport type PushError = v.Infer<typeof pushErrorSchema>;\nexport type PushOk = v.Infer<typeof pushOkSchema>;\nexport type MutationResult = v.Infer<typeof mutationResultSchema>;\nexport type AckMutationMessage = v.Infer<\n typeof ackMutationResponsesMessageSchema\n>;\nexport type {MutationID} from './mutation-id.ts';\n\nexport function mapCRUD(\n arg: CRUDMutationArg,\n map: NameMapper,\n): CRUDMutationArg {\n return {\n ops: arg.ops.map(\n ({op, tableName, primaryKey, value}) =>\n ({\n op,\n tableName: map.tableName(tableName),\n primaryKey: map.columns(tableName, primaryKey),\n value: map.row(tableName, value),\n // The cast is necessary because ts objects to the `value` field\n // for \"delete\" ops being different.\n }) as unknown as CRUDOp,\n ),\n };\n}\n"],"names":["v.object","v.literal","v.string","v.union","v.array","v.tuple","MutationType.CRUD","v.number","MutationType.Custom"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"push.js","sources":["../../../../zero-protocol/src/push.ts"],"sourcesContent":["import {jsonSchema} from '../../shared/src/json-schema.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {NameMapper} from '../../zero-types/src/name-mapper.ts';\nimport {rowSchema} from './data.ts';\nimport {pushFailedBodySchema} from './error.ts';\nimport {mutationIDSchema} from './mutation-id.ts';\nimport * as MutationType from './mutation-type-enum.ts';\nimport {primaryKeySchema, primaryKeyValueRecordSchema} from './primary-key.ts';\n\n// NOTE! If you change this name you must also change the\n// string in `replicache-impl.ts` But CRUD mutators are being\n// deleted soon so this should not happen.\nexport const CRUD_MUTATION_NAME = '_zero_crud';\n\n// Internal mutation name for cleaning up mutation results.\n// This mutation is sent from zero-cache to zero-server to delete\n// acknowledged mutation results from the upstream database.\nexport const CLEANUP_RESULTS_MUTATION_NAME = '_zero_cleanupResults';\n\nexport const cleanupResultsArgSchema = v.object({\n clientGroupID: v.string(),\n clientID: v.string(),\n upToMutationID: v.number(),\n});\n\n/**\n * Inserts if entity with id does not already exist.\n */\nconst insertOpSchema = v.object({\n op: v.literal('insert'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n value: rowSchema,\n});\n\n/**\n * Upsert semantics. Inserts if entity with id does not already exist,\n * otherwise updates existing entity with id.\n */\nconst upsertOpSchema = v.object({\n op: v.literal('upsert'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n value: rowSchema,\n});\n\n/**\n * Updates if entity with id exists, otherwise does nothing.\n */\nconst updateOpSchema = v.object({\n op: v.literal('update'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n // Partial value with at least the primary key fields\n value: rowSchema,\n});\n\n/**\n * Deletes entity with id if it exists, otherwise does nothing.\n */\nconst deleteOpSchema = v.object({\n op: v.literal('delete'),\n tableName: v.string(),\n primaryKey: primaryKeySchema,\n // Partial value representing the primary key\n value: primaryKeyValueRecordSchema,\n});\n\nconst crudOpSchema = v.union(\n insertOpSchema,\n upsertOpSchema,\n updateOpSchema,\n deleteOpSchema,\n);\n\nconst crudArgSchema = v.object({\n ops: v.array(crudOpSchema),\n});\n\nconst crudArgsSchema = v.tuple([crudArgSchema]);\n\nexport const crudMutationSchema = v.object({\n type: v.literal(MutationType.CRUD),\n id: v.number(),\n clientID: v.string(),\n name: v.literal(CRUD_MUTATION_NAME),\n args: crudArgsSchema,\n timestamp: v.number(),\n});\n\nexport const customMutationSchema = v.object({\n type: v.literal(MutationType.Custom),\n id: v.number(),\n clientID: v.string(),\n name: v.string(),\n args: v.array(jsonSchema),\n timestamp: v.number(),\n});\n\nexport const mutationSchema = v.union(crudMutationSchema, customMutationSchema);\n\nexport const pushBodySchema = v.object({\n clientGroupID: v.string(),\n mutations: v.array(mutationSchema),\n pushVersion: v.number(),\n // For legacy (CRUD) mutations, the schema is tied to the client group /\n // sync connection. For custom mutations, schema versioning is delegated\n // to the custom protocol / api-server.\n schemaVersion: v.number().optional(),\n timestamp: v.number(),\n requestID: v.string(),\n});\n\nexport const pushMessageSchema = v.tuple([v.literal('push'), pushBodySchema]);\n\nconst appErrorSchema = v.object({\n error: v.literal('app'),\n // The user can return any additional data here\n message: v.string().optional(),\n details: jsonSchema.optional(),\n});\nconst zeroErrorSchema = v.object({\n error: v.union(\n /** @deprecated push oooMutation errors are now represented as ['error', { ... }] messages */\n v.literal('oooMutation'),\n v.literal('alreadyProcessed'),\n ),\n details: jsonSchema.optional(),\n});\n\nconst mutationOkSchema = v.object({\n // The user can return any additional data here\n data: jsonSchema.optional(),\n});\nconst mutationErrorSchema = v.union(appErrorSchema, zeroErrorSchema);\n\nexport const mutationResultSchema = v.union(\n mutationOkSchema,\n mutationErrorSchema,\n);\n\nexport const mutationResponseSchema = v.object({\n id: mutationIDSchema,\n result: mutationResultSchema,\n});\n\nconst pushOkSchema = v.object({\n mutations: v.array(mutationResponseSchema),\n});\n\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst unsupportedPushVersionSchema = v.object({\n /** @deprecated */\n error: v.literal('unsupportedPushVersion'),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst unsupportedSchemaVersionSchema = v.object({\n /** @deprecated */\n error: v.literal('unsupportedSchemaVersion'),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push http errors are now represented as ['error', { ... }] messages\n */\nconst httpErrorSchema = v.object({\n /** @deprecated */\n error: v.literal('http'),\n /** @deprecated */\n status: v.number(),\n /** @deprecated */\n details: v.string(),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push zero errors are now represented as ['error', { ... }] messages\n */\nconst zeroPusherErrorSchema = v.object({\n /** @deprecated */\n error: v.literal('zeroPusher'),\n /** @deprecated */\n details: v.string(),\n /** @deprecated */\n mutationIDs: v.array(mutationIDSchema).optional(),\n});\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nconst pushErrorSchema = v.union(\n unsupportedPushVersionSchema,\n unsupportedSchemaVersionSchema,\n httpErrorSchema,\n zeroPusherErrorSchema,\n);\n\nexport const pushResponseBodySchema = v.union(pushOkSchema, pushErrorSchema);\n\nexport const pushResponseSchema = v.union(\n pushResponseBodySchema,\n pushFailedBodySchema,\n);\nexport const pushResponseMessageSchema = v.tuple([\n v.literal('pushResponse'),\n pushResponseBodySchema,\n]);\n\nexport const ackMutationResponsesMessageSchema = v.tuple([\n v.literal('ackMutationResponses'),\n mutationIDSchema,\n]);\n\n/**\n * The schema for the querystring parameters of the custom push endpoint.\n */\nexport const pushParamsSchema = v.object({\n schema: v.string(),\n appID: v.string(),\n});\n\nexport type InsertOp = v.Infer<typeof insertOpSchema>;\nexport type UpsertOp = v.Infer<typeof upsertOpSchema>;\nexport type UpdateOp = v.Infer<typeof updateOpSchema>;\nexport type DeleteOp = v.Infer<typeof deleteOpSchema>;\nexport type CRUDOp = v.Infer<typeof crudOpSchema>;\nexport type CRUDOpKind = CRUDOp['op'];\nexport type CRUDMutationArg = v.Infer<typeof crudArgSchema>;\nexport type CRUDMutation = v.Infer<typeof crudMutationSchema>;\nexport type CustomMutation = v.Infer<typeof customMutationSchema>;\nexport type Mutation = v.Infer<typeof mutationSchema>;\nexport type PushBody = v.Infer<typeof pushBodySchema>;\nexport type PushMessage = v.Infer<typeof pushMessageSchema>;\nexport type PushResponseBody = v.Infer<typeof pushResponseBodySchema>;\nexport type PushResponse = v.Infer<typeof pushResponseSchema>;\nexport type PushResponseMessage = v.Infer<typeof pushResponseMessageSchema>;\nexport type MutationResponse = v.Infer<typeof mutationResponseSchema>;\nexport type MutationOk = v.Infer<typeof mutationOkSchema>;\nexport type MutationError = v.Infer<typeof mutationErrorSchema>;\n/**\n * @deprecated push errors are now represented as ['error', { ... }] messages\n */\nexport type PushError = v.Infer<typeof pushErrorSchema>;\nexport type PushOk = v.Infer<typeof pushOkSchema>;\nexport type MutationResult = v.Infer<typeof mutationResultSchema>;\nexport type AckMutationMessage = v.Infer<\n typeof ackMutationResponsesMessageSchema\n>;\nexport type CleanupResultsArg = v.Infer<typeof cleanupResultsArgSchema>;\nexport type {MutationID} from './mutation-id.ts';\n\nexport function mapCRUD(\n arg: CRUDMutationArg,\n map: NameMapper,\n): CRUDMutationArg {\n return {\n ops: arg.ops.map(\n ({op, tableName, primaryKey, value}) =>\n ({\n op,\n tableName: map.tableName(tableName),\n primaryKey: map.columns(tableName, primaryKey),\n value: map.row(tableName, value),\n // The cast is necessary because ts objects to the `value` field\n // for \"delete\" ops being different.\n }) as unknown as CRUDOp,\n ),\n };\n}\n"],"names":["v.object","v.string","v.number","v.literal","v.union","v.array","v.tuple","MutationType.CRUD","MutationType.Custom"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ export interface TransactionProviderHooks {
|
|
|
10
10
|
lastMutationID: number | bigint;
|
|
11
11
|
}>;
|
|
12
12
|
writeMutationResult: (result: MutationResponse) => Promise<void>;
|
|
13
|
+
deleteMutationResults: (clientGroupID: string, clientID: string, upToMutationID: number) => Promise<void>;
|
|
13
14
|
}
|
|
14
15
|
export interface TransactionProviderInput {
|
|
15
16
|
upstreamSchema: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-mutations.d.ts","sourceRoot":"","sources":["../../../../zero-server/src/process-mutations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAG3D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,CAAC,MAAM,4BAA4B,CAAC;AAWhD,OAAO,
|
|
1
|
+
{"version":3,"file":"process-mutations.d.ts","sourceRoot":"","sources":["../../../../zero-server/src/process-mutations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { assert } from "../../shared/src/asserts.js";
|
|
2
2
|
import { getErrorMessage, getErrorDetails } from "../../shared/src/error.js";
|
|
3
3
|
import { promiseVoid } from "../../shared/src/resolved-promises.js";
|
|
4
|
-
import { parse } from "../../shared/src/valita.js";
|
|
4
|
+
import { parse, test } from "../../shared/src/valita.js";
|
|
5
5
|
import { MutationAlreadyProcessedError } from "../../zero-cache/src/services/mutagen/error.js";
|
|
6
6
|
import { isApplicationError, wrapWithApplicationError } from "../../zero-protocol/src/application-error.js";
|
|
7
7
|
import { PushFailed } from "../../zero-protocol/src/error-kind-enum.js";
|
|
8
8
|
import { Server } from "../../zero-protocol/src/error-origin-enum.js";
|
|
9
9
|
import { Parse, UnsupportedPushVersion, OutOfOrderMutation as OutOfOrderMutation$1, Database, Internal } from "../../zero-protocol/src/error-reason-enum.js";
|
|
10
|
-
import { pushBodySchema, pushParamsSchema } from "../../zero-protocol/src/push.js";
|
|
10
|
+
import { pushBodySchema, pushParamsSchema, CLEANUP_RESULTS_MUTATION_NAME, cleanupResultsArgSchema } from "../../zero-protocol/src/push.js";
|
|
11
11
|
import { isMutator } from "../../zql/src/mutate/mutator.js";
|
|
12
12
|
import { createLogContext } from "./logging.js";
|
|
13
13
|
const applicationErrorWrapper = async (fn) => {
|
|
@@ -107,6 +107,22 @@ async function handleMutateRequest(dbProvider, cb, queryStringOrRequest, bodyOrL
|
|
|
107
107
|
try {
|
|
108
108
|
const transactor = new Transactor(dbProvider, pushBody, queryParams, lc);
|
|
109
109
|
for (const m of pushBody.mutations) {
|
|
110
|
+
if (m.type === "custom" && m.name === CLEANUP_RESULTS_MUTATION_NAME) {
|
|
111
|
+
lc.debug?.(
|
|
112
|
+
`Processing internal mutation '${m.name}' (clientID=${m.clientID})`
|
|
113
|
+
);
|
|
114
|
+
try {
|
|
115
|
+
await processCleanupResultsMutation(dbProvider, m, queryParams, lc);
|
|
116
|
+
processedCount++;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
lc.warn?.(
|
|
119
|
+
`Failed to process cleanup mutation for client ${m.clientID}`,
|
|
120
|
+
error
|
|
121
|
+
);
|
|
122
|
+
processedCount++;
|
|
123
|
+
}
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
110
126
|
assert(m.type === "custom", "Expected custom mutation");
|
|
111
127
|
lc.debug?.(
|
|
112
128
|
`Processing mutation '${m.name}' (id=${m.id}, clientID=${m.clientID})`,
|
|
@@ -338,6 +354,29 @@ function getObjectAtPath(obj, path) {
|
|
|
338
354
|
}
|
|
339
355
|
return current;
|
|
340
356
|
}
|
|
357
|
+
async function processCleanupResultsMutation(dbProvider, mutation, queryParams, lc) {
|
|
358
|
+
const parseResult = test(mutation.args[0], cleanupResultsArgSchema);
|
|
359
|
+
if (!parseResult.ok) {
|
|
360
|
+
lc.warn?.("Cleanup mutation has invalid args", parseResult.error);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const args = parseResult.value;
|
|
364
|
+
await dbProvider.transaction(
|
|
365
|
+
async (_, hooks) => {
|
|
366
|
+
await hooks.deleteMutationResults(
|
|
367
|
+
args.clientGroupID,
|
|
368
|
+
args.clientID,
|
|
369
|
+
args.upToMutationID
|
|
370
|
+
);
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
upstreamSchema: queryParams.schema,
|
|
374
|
+
clientGroupID: args.clientGroupID,
|
|
375
|
+
clientID: args.clientID,
|
|
376
|
+
mutationID: 0
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
}
|
|
341
380
|
class DatabaseTransactionError extends Error {
|
|
342
381
|
constructor(phase, options) {
|
|
343
382
|
super(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-mutations.js","sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import type {LogContext, LogLevel} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {promiseVoid} from '../../shared/src/resolved-promises.ts';\nimport type {MaybePromise} from '../../shared/src/types.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {MutationAlreadyProcessedError} from '../../zero-cache/src/services/mutagen/error.ts';\nimport type {ApplicationError} from '../../zero-protocol/src/application-error.ts';\nimport {\n isApplicationError,\n wrapWithApplicationError,\n} from '../../zero-protocol/src/application-error.ts';\nimport {ErrorKind} from '../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../zero-protocol/src/error-reason.ts';\nimport type {PushFailedBody} from '../../zero-protocol/src/error.ts';\nimport {\n pushBodySchema,\n pushParamsSchema,\n type CustomMutation,\n type Mutation,\n type MutationID,\n type MutationResponse,\n type PushBody,\n type PushResponse,\n} from '../../zero-protocol/src/push.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs, CustomMutatorImpl} from './custom.ts';\nimport {createLogContext} from './logging.ts';\n\nexport interface TransactionProviderHooks {\n updateClientMutationID: () => Promise<{lastMutationID: number | bigint}>;\n writeMutationResult: (result: MutationResponse) => Promise<void>;\n}\n\nexport interface TransactionProviderInput {\n upstreamSchema: string;\n clientGroupID: string;\n clientID: string;\n mutationID: number;\n}\n\n/**\n * Defines the abstract interface for a database that PushProcessor can execute\n * transactions against.\n */\nexport interface Database<T> {\n transaction: <R>(\n callback: (\n tx: T,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ) => Promise<R>;\n}\n\nexport type ExtractTransactionType<D> = D extends Database<infer T> ? T : never;\nexport type Params = v.Infer<typeof pushParamsSchema>;\n\nexport type TransactFn<D extends Database<ExtractTransactionType<D>>> = (\n cb: TransactFnCallback<D>,\n) => Promise<MutationResponse>;\n\nexport type TransactFnCallback<D extends Database<ExtractTransactionType<D>>> =\n (\n tx: ExtractTransactionType<D>,\n mutatorName: string,\n mutatorArgs: ReadonlyJSONValue | undefined,\n ) => Promise<void>;\n\nexport type Parsed<D extends Database<ExtractTransactionType<D>>> = {\n transact: TransactFn<D>;\n mutations: CustomMutation[];\n};\n\ntype MutationPhase = 'preTransaction' | 'transactionPending' | 'postCommit';\n\nconst applicationErrorWrapper = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (\n error instanceof DatabaseTransactionError ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError ||\n isApplicationError(error)\n ) {\n throw error;\n }\n\n throw wrapWithApplicationError(error);\n }\n};\n\n/**\n * @deprecated Use {@linkcode handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n queryString: URLSearchParams | Record<string, string>,\n body: ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<PushResponse>;\n\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n request: Request,\n logLevel?: LogLevel,\n): Promise<PushResponse>;\n\nexport async function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n queryStringOrRequest: Request | URLSearchParams | Record<string, string>,\n bodyOrLogLevel?: ReadonlyJSONValue | LogLevel,\n logLevel?: LogLevel,\n): Promise<PushResponse> {\n // Parse overload arguments\n const isRequestOverload = queryStringOrRequest instanceof Request;\n\n let request: Request | undefined;\n let queryString: URLSearchParams | Record<string, string>;\n let jsonBody: unknown;\n\n let lc: LogContext;\n\n if (isRequestOverload) {\n request = queryStringOrRequest;\n const level = (bodyOrLogLevel as LogLevel | undefined) ?? 'info';\n\n // Create log context early, before extracting JSON from Request\n lc = createLogContext(level).withContext('PushProcessor');\n\n const url = new URL(request.url);\n queryString = url.searchParams;\n\n try {\n jsonBody = await request.json();\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs: [],\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n } else {\n queryString = queryStringOrRequest;\n jsonBody = bodyOrLogLevel;\n const level = logLevel ?? 'info';\n lc = createLogContext(level).withContext('PushProcessor');\n }\n\n let mutationIDs: MutationID[] = [];\n\n let pushBody: PushBody;\n try {\n pushBody = v.parse(jsonBody, pushBodySchema);\n mutationIDs = pushBody.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n let queryParams: Params;\n try {\n const queryStringObj =\n queryString instanceof URLSearchParams\n ? Object.fromEntries(queryString)\n : queryString;\n queryParams = v.parse(queryStringObj, pushParamsSchema, 'passthrough');\n } catch (error) {\n lc.error?.('Failed to parse push query parameters', error);\n const message = `Failed to parse push query parameters: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n if (pushBody.pushVersion !== 1) {\n const response = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version: ${pushBody.pushVersion}`,\n } as const satisfies PushFailedBody;\n return response;\n }\n\n const responses: MutationResponse[] = [];\n let processedCount = 0;\n\n try {\n const transactor = new Transactor(dbProvider, pushBody, queryParams, lc);\n\n // Each mutation goes through three phases:\n // 1. Pre-transaction: user logic that runs before `transact` is called. If\n // this throws we still advance LMID and persist the failure result.\n // 2. Transaction: the callback passed to `transact`, which can be retried\n // if it fails with an ApplicationError.\n // 3. Post-commit: any logic that runs after `transact` resolves. Failures\n // here are logged but the mutation remains committed.\n for (const m of pushBody.mutations) {\n assert(m.type === 'custom', 'Expected custom mutation');\n lc.debug?.(\n `Processing mutation '${m.name}' (id=${m.id}, clientID=${m.clientID})`,\n m.args,\n );\n\n let mutationPhase: MutationPhase = 'preTransaction';\n\n const transactProxy: TransactFn<D> = async innerCb => {\n mutationPhase = 'transactionPending';\n const result = await transactor.transact(m, (tx, name, args) =>\n applicationErrorWrapper(() => innerCb(tx, name, args)),\n );\n mutationPhase = 'postCommit';\n return result;\n };\n\n try {\n const res = await applicationErrorWrapper(() => cb(transactProxy, m));\n responses.push(res);\n lc.debug?.(`Mutation '${m.name}' (id=${m.id}) completed successfully`);\n\n processedCount++;\n } catch (error) {\n if (!isApplicationError(error)) {\n throw error;\n }\n\n if (mutationPhase === 'preTransaction') {\n // Pre-transaction\n await transactor.persistPreTransactionFailure(m, error);\n } else if (mutationPhase === 'postCommit') {\n // Post-commit\n lc.error?.(\n `Post-commit mutation handler failed for mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n }\n\n lc.warn?.(\n `Application error processing mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n responses.push(makeAppErrorResponse(m, error));\n\n processedCount++;\n }\n }\n\n return {\n mutations: responses,\n };\n } catch (error) {\n lc.error?.('Failed to process push request', error);\n // only include mutationIDs for mutations that were not processed\n const unprocessedMutationIDs = mutationIDs.slice(processedCount);\n\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason:\n error instanceof OutOfOrderMutation\n ? ErrorReason.OutOfOrderMutation\n : error instanceof DatabaseTransactionError\n ? ErrorReason.Database\n : ErrorReason.Internal,\n message,\n mutationIDs: unprocessedMutationIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\nclass Transactor<D extends Database<ExtractTransactionType<D>>> {\n readonly #dbProvider: D;\n readonly #req: PushBody;\n readonly #params: Params;\n readonly #lc: LogContext;\n\n constructor(dbProvider: D, req: PushBody, params: Params, lc: LogContext) {\n this.#dbProvider = dbProvider;\n this.#req = req;\n this.#params = params;\n this.#lc = lc;\n }\n\n transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n for (;;) {\n try {\n const ret = await this.#transactImpl(mutation, cb, appError);\n if (appError !== undefined) {\n this.#lc.warn?.(\n `Mutation ${mutation.id} for client ${mutation.clientID} was retried after an error`,\n appError,\n );\n return makeAppErrorResponse(mutation, appError);\n }\n\n return ret;\n } catch (error) {\n if (error instanceof OutOfOrderMutation) {\n this.#lc.error?.(error);\n throw error;\n }\n\n if (error instanceof MutationAlreadyProcessedError) {\n this.#lc.warn?.(error);\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {\n error: 'alreadyProcessed',\n details: error.message,\n },\n };\n }\n\n if (isApplicationError(error)) {\n appError = error;\n this.#lc.warn?.(\n `Application error processing mutation ${mutation.id} for client ${mutation.clientID}`,\n appError,\n );\n continue;\n }\n\n this.#lc.error?.(\n `Unexpected error processing mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n\n throw error;\n }\n }\n };\n\n async persistPreTransactionFailure(\n mutation: CustomMutation,\n appError: ApplicationError<ReadonlyJSONValue | undefined>,\n ): Promise<MutationResponse> {\n // User-land code threw before calling `transact`. We still need to bump the\n // LMID for this mutation and persist the error so that the client knows it failed.\n const ret = await this.#transactImpl(\n mutation,\n // noop callback since there's no transaction to execute\n () => promiseVoid,\n appError,\n );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | undefined,\n ): Promise<MutationResponse> {\n let transactionPhase: DatabaseTransactionPhase = 'open';\n\n try {\n const ret = await this.#dbProvider.transaction(\n async (dbTx, transactionHooks) => {\n // update the transaction phase to 'execute' after the transaction is opened\n transactionPhase = 'execute';\n\n await this.#checkAndIncrementLastMutationID(\n transactionHooks,\n mutation.clientID,\n mutation.id,\n );\n\n if (appError === undefined) {\n this.#lc.debug?.(\n `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n try {\n await cb(dbTx, mutation.name, mutation.args[0]);\n } catch (appError) {\n throw wrapWithApplicationError(appError);\n }\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n }\n\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {},\n };\n },\n this.#getTransactionInput(mutation),\n );\n\n return ret;\n } catch (error) {\n if (\n isApplicationError(error) ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError\n ) {\n throw error;\n }\n\n throw new DatabaseTransactionError(transactionPhase, {cause: error});\n }\n }\n\n #getTransactionInput(mutation: CustomMutation): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\n };\n }\n\n async #checkAndIncrementLastMutationID(\n transactionHooks: TransactionProviderHooks,\n clientID: string,\n receivedMutationID: number,\n ) {\n const {lastMutationID} = await transactionHooks.updateClientMutationID();\n\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new OutOfOrderMutation(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n }\n }\n}\n\nexport class OutOfOrderMutation extends Error {\n constructor(\n clientID: string,\n receivedMutationID: number,\n lastMutationID: number | bigint,\n ) {\n super(\n `Client ${clientID} sent mutation ID ${receivedMutationID} but expected ${lastMutationID}`,\n );\n }\n}\n\nfunction makeAppErrorResponse(\n m: Mutation,\n error: ApplicationError<ReadonlyJSONValue | undefined>,\n): MutationResponse {\n return {\n id: {\n clientID: m.clientID,\n id: m.id,\n },\n result: {\n error: 'app',\n message: error.message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n}\n\n/** @deprecated Use getMutator instead */\nexport function getMutation(\n // oxlint-disable-next-line no-explicit-any\n mutators: AnyMutatorRegistry | CustomMutatorDefs<any>,\n name: string,\n // oxlint-disable-next-line no-explicit-any\n): CustomMutatorImpl<any> {\n const path = name.split(/\\.|\\|/);\n const mutator = getObjectAtPath(mutators, path);\n assert(typeof mutator === 'function', `could not find mutator ${name}`);\n\n if (isMutator(mutator)) {\n // mutator needs to be called with {tx, args, ctx}\n // CustomMutatorImpl is called with (tx, args, ctx)\n return (tx, args, ctx) => mutator.fn({args, ctx, tx});\n }\n\n // oxlint-disable-next-line no-explicit-any\n return mutator as CustomMutatorImpl<any>;\n}\n\nfunction getObjectAtPath(\n obj: Record<string, unknown>,\n path: string[],\n): unknown {\n let current: unknown = obj;\n for (const part of path) {\n if (typeof current !== 'object' || current === null || !(part in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\ntype DatabaseTransactionPhase = 'open' | 'execute';\nclass DatabaseTransactionError extends Error {\n constructor(phase: DatabaseTransactionPhase, options?: ErrorOptions) {\n super(\n phase === 'open'\n ? `Failed to open database transaction: ${getErrorMessage(options?.cause)}`\n : `Database transaction failed after opening: ${getErrorMessage(options?.cause)}`,\n options,\n );\n this.name = 'DatabaseTransactionError';\n }\n}\n"],"names":["ErrorKind.PushFailed","ErrorOrigin.Server","ErrorReason.Parse","v.parse","ErrorReason.UnsupportedPushVersion","ErrorReason.OutOfOrderMutation","ErrorReason.Database","ErrorReason.Internal","appError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"process-mutations.js","sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import type {LogContext, LogLevel} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {promiseVoid} from '../../shared/src/resolved-promises.ts';\nimport type {MaybePromise} from '../../shared/src/types.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {MutationAlreadyProcessedError} from '../../zero-cache/src/services/mutagen/error.ts';\nimport type {ApplicationError} from '../../zero-protocol/src/application-error.ts';\nimport {\n isApplicationError,\n wrapWithApplicationError,\n} from '../../zero-protocol/src/application-error.ts';\nimport {ErrorKind} from '../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../zero-protocol/src/error-reason.ts';\nimport type {PushFailedBody} from '../../zero-protocol/src/error.ts';\nimport {\n CLEANUP_RESULTS_MUTATION_NAME,\n cleanupResultsArgSchema,\n pushBodySchema,\n pushParamsSchema,\n type CleanupResultsArg,\n type CustomMutation,\n type Mutation,\n type MutationID,\n type MutationResponse,\n type PushBody,\n type PushResponse,\n} from '../../zero-protocol/src/push.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs, CustomMutatorImpl} from './custom.ts';\nimport {createLogContext} from './logging.ts';\n\nexport interface TransactionProviderHooks {\n updateClientMutationID: () => Promise<{lastMutationID: number | bigint}>;\n writeMutationResult: (result: MutationResponse) => Promise<void>;\n deleteMutationResults: (\n clientGroupID: string,\n clientID: string,\n upToMutationID: number,\n ) => Promise<void>;\n}\n\nexport interface TransactionProviderInput {\n upstreamSchema: string;\n clientGroupID: string;\n clientID: string;\n mutationID: number;\n}\n\n/**\n * Defines the abstract interface for a database that PushProcessor can execute\n * transactions against.\n */\nexport interface Database<T> {\n transaction: <R>(\n callback: (\n tx: T,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ) => Promise<R>;\n}\n\nexport type ExtractTransactionType<D> = D extends Database<infer T> ? T : never;\nexport type Params = v.Infer<typeof pushParamsSchema>;\n\nexport type TransactFn<D extends Database<ExtractTransactionType<D>>> = (\n cb: TransactFnCallback<D>,\n) => Promise<MutationResponse>;\n\nexport type TransactFnCallback<D extends Database<ExtractTransactionType<D>>> =\n (\n tx: ExtractTransactionType<D>,\n mutatorName: string,\n mutatorArgs: ReadonlyJSONValue | undefined,\n ) => Promise<void>;\n\nexport type Parsed<D extends Database<ExtractTransactionType<D>>> = {\n transact: TransactFn<D>;\n mutations: CustomMutation[];\n};\n\ntype MutationPhase = 'preTransaction' | 'transactionPending' | 'postCommit';\n\nconst applicationErrorWrapper = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (\n error instanceof DatabaseTransactionError ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError ||\n isApplicationError(error)\n ) {\n throw error;\n }\n\n throw wrapWithApplicationError(error);\n }\n};\n\n/**\n * @deprecated Use {@linkcode handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n queryString: URLSearchParams | Record<string, string>,\n body: ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<PushResponse>;\n\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n request: Request,\n logLevel?: LogLevel,\n): Promise<PushResponse>;\n\nexport async function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n cb: (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n ) => Promise<MutationResponse>,\n queryStringOrRequest: Request | URLSearchParams | Record<string, string>,\n bodyOrLogLevel?: ReadonlyJSONValue | LogLevel,\n logLevel?: LogLevel,\n): Promise<PushResponse> {\n // Parse overload arguments\n const isRequestOverload = queryStringOrRequest instanceof Request;\n\n let request: Request | undefined;\n let queryString: URLSearchParams | Record<string, string>;\n let jsonBody: unknown;\n\n let lc: LogContext;\n\n if (isRequestOverload) {\n request = queryStringOrRequest;\n const level = (bodyOrLogLevel as LogLevel | undefined) ?? 'info';\n\n // Create log context early, before extracting JSON from Request\n lc = createLogContext(level).withContext('PushProcessor');\n\n const url = new URL(request.url);\n queryString = url.searchParams;\n\n try {\n jsonBody = await request.json();\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs: [],\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n } else {\n queryString = queryStringOrRequest;\n jsonBody = bodyOrLogLevel;\n const level = logLevel ?? 'info';\n lc = createLogContext(level).withContext('PushProcessor');\n }\n\n let mutationIDs: MutationID[] = [];\n\n let pushBody: PushBody;\n try {\n pushBody = v.parse(jsonBody, pushBodySchema);\n mutationIDs = pushBody.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n let queryParams: Params;\n try {\n const queryStringObj =\n queryString instanceof URLSearchParams\n ? Object.fromEntries(queryString)\n : queryString;\n queryParams = v.parse(queryStringObj, pushParamsSchema, 'passthrough');\n } catch (error) {\n lc.error?.('Failed to parse push query parameters', error);\n const message = `Failed to parse push query parameters: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n if (pushBody.pushVersion !== 1) {\n const response = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version: ${pushBody.pushVersion}`,\n } as const satisfies PushFailedBody;\n return response;\n }\n\n const responses: MutationResponse[] = [];\n let processedCount = 0;\n\n try {\n const transactor = new Transactor(dbProvider, pushBody, queryParams, lc);\n\n // Each mutation goes through three phases:\n // 1. Pre-transaction: user logic that runs before `transact` is called. If\n // this throws we still advance LMID and persist the failure result.\n // 2. Transaction: the callback passed to `transact`, which can be retried\n // if it fails with an ApplicationError.\n // 3. Post-commit: any logic that runs after `transact` resolves. Failures\n // here are logged but the mutation remains committed.\n for (const m of pushBody.mutations) {\n // Handle internal mutations (like cleanup) directly without user dispatch\n if (m.type === 'custom' && m.name === CLEANUP_RESULTS_MUTATION_NAME) {\n lc.debug?.(\n `Processing internal mutation '${m.name}' (clientID=${m.clientID})`,\n );\n try {\n await processCleanupResultsMutation(dbProvider, m, queryParams, lc);\n // No response added - this is fire-and-forget\n processedCount++;\n } catch (error) {\n lc.warn?.(\n `Failed to process cleanup mutation for client ${m.clientID}`,\n error,\n );\n // Don't fail the whole push for cleanup errors\n processedCount++;\n }\n continue;\n }\n\n assert(m.type === 'custom', 'Expected custom mutation');\n lc.debug?.(\n `Processing mutation '${m.name}' (id=${m.id}, clientID=${m.clientID})`,\n m.args,\n );\n\n let mutationPhase: MutationPhase = 'preTransaction';\n\n const transactProxy: TransactFn<D> = async innerCb => {\n mutationPhase = 'transactionPending';\n const result = await transactor.transact(m, (tx, name, args) =>\n applicationErrorWrapper(() => innerCb(tx, name, args)),\n );\n mutationPhase = 'postCommit';\n return result;\n };\n\n try {\n const res = await applicationErrorWrapper(() => cb(transactProxy, m));\n responses.push(res);\n lc.debug?.(`Mutation '${m.name}' (id=${m.id}) completed successfully`);\n\n processedCount++;\n } catch (error) {\n if (!isApplicationError(error)) {\n throw error;\n }\n\n if (mutationPhase === 'preTransaction') {\n // Pre-transaction\n await transactor.persistPreTransactionFailure(m, error);\n } else if (mutationPhase === 'postCommit') {\n // Post-commit\n lc.error?.(\n `Post-commit mutation handler failed for mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n }\n\n lc.warn?.(\n `Application error processing mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n responses.push(makeAppErrorResponse(m, error));\n\n processedCount++;\n }\n }\n\n return {\n mutations: responses,\n };\n } catch (error) {\n lc.error?.('Failed to process push request', error);\n // only include mutationIDs for mutations that were not processed\n const unprocessedMutationIDs = mutationIDs.slice(processedCount);\n\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason:\n error instanceof OutOfOrderMutation\n ? ErrorReason.OutOfOrderMutation\n : error instanceof DatabaseTransactionError\n ? ErrorReason.Database\n : ErrorReason.Internal,\n message,\n mutationIDs: unprocessedMutationIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\nclass Transactor<D extends Database<ExtractTransactionType<D>>> {\n readonly #dbProvider: D;\n readonly #req: PushBody;\n readonly #params: Params;\n readonly #lc: LogContext;\n\n constructor(dbProvider: D, req: PushBody, params: Params, lc: LogContext) {\n this.#dbProvider = dbProvider;\n this.#req = req;\n this.#params = params;\n this.#lc = lc;\n }\n\n transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n for (;;) {\n try {\n const ret = await this.#transactImpl(mutation, cb, appError);\n if (appError !== undefined) {\n this.#lc.warn?.(\n `Mutation ${mutation.id} for client ${mutation.clientID} was retried after an error`,\n appError,\n );\n return makeAppErrorResponse(mutation, appError);\n }\n\n return ret;\n } catch (error) {\n if (error instanceof OutOfOrderMutation) {\n this.#lc.error?.(error);\n throw error;\n }\n\n if (error instanceof MutationAlreadyProcessedError) {\n this.#lc.warn?.(error);\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {\n error: 'alreadyProcessed',\n details: error.message,\n },\n };\n }\n\n if (isApplicationError(error)) {\n appError = error;\n this.#lc.warn?.(\n `Application error processing mutation ${mutation.id} for client ${mutation.clientID}`,\n appError,\n );\n continue;\n }\n\n this.#lc.error?.(\n `Unexpected error processing mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n\n throw error;\n }\n }\n };\n\n async persistPreTransactionFailure(\n mutation: CustomMutation,\n appError: ApplicationError<ReadonlyJSONValue | undefined>,\n ): Promise<MutationResponse> {\n // User-land code threw before calling `transact`. We still need to bump the\n // LMID for this mutation and persist the error so that the client knows it failed.\n const ret = await this.#transactImpl(\n mutation,\n // noop callback since there's no transaction to execute\n () => promiseVoid,\n appError,\n );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | undefined,\n ): Promise<MutationResponse> {\n let transactionPhase: DatabaseTransactionPhase = 'open';\n\n try {\n const ret = await this.#dbProvider.transaction(\n async (dbTx, transactionHooks) => {\n // update the transaction phase to 'execute' after the transaction is opened\n transactionPhase = 'execute';\n\n await this.#checkAndIncrementLastMutationID(\n transactionHooks,\n mutation.clientID,\n mutation.id,\n );\n\n if (appError === undefined) {\n this.#lc.debug?.(\n `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n try {\n await cb(dbTx, mutation.name, mutation.args[0]);\n } catch (appError) {\n throw wrapWithApplicationError(appError);\n }\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n }\n\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {},\n };\n },\n this.#getTransactionInput(mutation),\n );\n\n return ret;\n } catch (error) {\n if (\n isApplicationError(error) ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError\n ) {\n throw error;\n }\n\n throw new DatabaseTransactionError(transactionPhase, {cause: error});\n }\n }\n\n #getTransactionInput(mutation: CustomMutation): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\n };\n }\n\n async #checkAndIncrementLastMutationID(\n transactionHooks: TransactionProviderHooks,\n clientID: string,\n receivedMutationID: number,\n ) {\n const {lastMutationID} = await transactionHooks.updateClientMutationID();\n\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new OutOfOrderMutation(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n }\n }\n}\n\nexport class OutOfOrderMutation extends Error {\n constructor(\n clientID: string,\n receivedMutationID: number,\n lastMutationID: number | bigint,\n ) {\n super(\n `Client ${clientID} sent mutation ID ${receivedMutationID} but expected ${lastMutationID}`,\n );\n }\n}\n\nfunction makeAppErrorResponse(\n m: Mutation,\n error: ApplicationError<ReadonlyJSONValue | undefined>,\n): MutationResponse {\n return {\n id: {\n clientID: m.clientID,\n id: m.id,\n },\n result: {\n error: 'app',\n message: error.message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n}\n\n/** @deprecated Use getMutator instead */\nexport function getMutation(\n // oxlint-disable-next-line no-explicit-any\n mutators: AnyMutatorRegistry | CustomMutatorDefs<any>,\n name: string,\n // oxlint-disable-next-line no-explicit-any\n): CustomMutatorImpl<any> {\n const path = name.split(/\\.|\\|/);\n const mutator = getObjectAtPath(mutators, path);\n assert(typeof mutator === 'function', `could not find mutator ${name}`);\n\n if (isMutator(mutator)) {\n // mutator needs to be called with {tx, args, ctx}\n // CustomMutatorImpl is called with (tx, args, ctx)\n return (tx, args, ctx) => mutator.fn({args, ctx, tx});\n }\n\n // oxlint-disable-next-line no-explicit-any\n return mutator as CustomMutatorImpl<any>;\n}\n\nfunction getObjectAtPath(\n obj: Record<string, unknown>,\n path: string[],\n): unknown {\n let current: unknown = obj;\n for (const part of path) {\n if (typeof current !== 'object' || current === null || !(part in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/**\n * Processes internal cleanup mutation that deletes acknowledged mutation results\n * from the upstream database. This runs without LMID tracking since it's an\n * internal operation.\n */\nasync function processCleanupResultsMutation<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n mutation: CustomMutation,\n queryParams: Params,\n lc: LogContext,\n): Promise<void> {\n const parseResult = v.test(mutation.args[0], cleanupResultsArgSchema);\n if (!parseResult.ok) {\n lc.warn?.('Cleanup mutation has invalid args', parseResult.error);\n return;\n }\n const args: CleanupResultsArg = parseResult.value;\n\n // Run in a transaction, using the hook for DB-specific operation\n await dbProvider.transaction(\n async (_, hooks) => {\n await hooks.deleteMutationResults(\n args.clientGroupID,\n args.clientID,\n args.upToMutationID,\n );\n },\n {\n upstreamSchema: queryParams.schema,\n clientGroupID: args.clientGroupID,\n clientID: args.clientID,\n mutationID: 0,\n },\n );\n}\n\ntype DatabaseTransactionPhase = 'open' | 'execute';\nclass DatabaseTransactionError extends Error {\n constructor(phase: DatabaseTransactionPhase, options?: ErrorOptions) {\n super(\n phase === 'open'\n ? `Failed to open database transaction: ${getErrorMessage(options?.cause)}`\n : `Database transaction failed after opening: ${getErrorMessage(options?.cause)}`,\n options,\n );\n this.name = 'DatabaseTransactionError';\n }\n}\n"],"names":["ErrorKind.PushFailed","ErrorOrigin.Server","ErrorReason.Parse","v.parse","ErrorReason.UnsupportedPushVersion","ErrorReason.OutOfOrderMutation","ErrorReason.Database","ErrorReason.Internal","appError","v.test"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zql-database.d.ts","sourceRoot":"","sources":["../../../../zero-server/src/zql-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAE5D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAgB,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EACV,aAAa,EACb,KAAK,EACL,UAAU,EACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAqB,KAAK,eAAe,EAAC,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EACV,QAAQ,EACR,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;GAMG;AACH,qBAAa,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,mBAAmB,CAClE,YAAW,QAAQ,CAAC,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;;IAElE,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;gBAG3C,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO;IAK1E,WAAW,CAAC,CAAC,EACX,QAAQ,EAAE,CACR,EAAE,EAAE,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,EACjD,gBAAgB,EAAE,wBAAwB,KACvC,YAAY,CAAC,CAAC,CAAC,EACpB,gBAAgB,CAAC,EAAE,wBAAwB,GAC1C,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"zql-database.d.ts","sourceRoot":"","sources":["../../../../zero-server/src/zql-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAE5D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAgB,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EACV,aAAa,EACb,KAAK,EACL,UAAU,EACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAqB,KAAK,eAAe,EAAC,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EACV,QAAQ,EACR,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;GAMG;AACH,qBAAa,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,mBAAmB,CAClE,YAAW,QAAQ,CAAC,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;;IAElE,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;gBAG3C,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO;IAK1E,WAAW,CAAC,CAAC,EACX,QAAQ,EAAE,CACR,EAAE,EAAE,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,EACjD,gBAAgB,EAAE,wBAAwB,KACvC,YAAY,CAAC,CAAC,CAAC,EACpB,gBAAgB,CAAC,EAAE,wBAAwB,GAC1C,OAAO,CAAC,CAAC,CAAC;IAuEb,GAAG,CAAC,MAAM,SAAS,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EAC1D,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EACtC,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;CAGnC"}
|
|
@@ -45,6 +45,15 @@ class ZQLDatabase {
|
|
|
45
45
|
)}::text::json)`
|
|
46
46
|
);
|
|
47
47
|
await dbTx.query(formatted.text, formatted.values);
|
|
48
|
+
},
|
|
49
|
+
async deleteMutationResults(targetClientGroupID, targetClientID, upToMutationID) {
|
|
50
|
+
const formatted = formatPg(
|
|
51
|
+
sql`DELETE FROM ${sql.ident(upstreamSchema)}."mutations"
|
|
52
|
+
WHERE "clientGroupID" = ${targetClientGroupID}
|
|
53
|
+
AND "clientID" = ${targetClientID}
|
|
54
|
+
AND "mutationID" <= ${upToMutationID}`
|
|
55
|
+
);
|
|
56
|
+
await dbTx.query(formatted.text, formatted.values);
|
|
48
57
|
}
|
|
49
58
|
});
|
|
50
59
|
});
|