@rocicorp/zero 1.12.0-canary.5 → 1.12.0-canary.6

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.
Files changed (86) hide show
  1. package/out/replicache/src/btree/node.js +1 -1
  2. package/out/replicache/src/deleted-clients.d.ts.map +1 -1
  3. package/out/replicache/src/deleted-clients.js +2 -1
  4. package/out/replicache/src/deleted-clients.js.map +1 -1
  5. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  6. package/out/replicache/src/kv/sqlite-store.js +7 -3
  7. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  8. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  9. package/out/replicache/src/replicache-impl.js +17 -14
  10. package/out/replicache/src/replicache-impl.js.map +1 -1
  11. package/out/z2s/src/compiler.js +3 -3
  12. package/out/z2s/src/compiler.js.map +1 -1
  13. package/out/zero/package.js +2 -2
  14. package/out/zero/package.js.map +1 -1
  15. package/out/zero-cache/src/config/zero-config.js +2 -2
  16. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  17. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  18. package/out/zero-cache/src/server/change-streamer.js +32 -9
  19. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  20. package/out/zero-cache/src/services/change-source/change-source.d.ts +7 -0
  21. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  22. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +25 -0
  23. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -1
  24. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  25. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +8 -1
  26. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -1
  27. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +33 -16
  28. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  29. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +12 -0
  30. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  31. package/out/zero-cache/src/services/change-source/pg/change-source.js +64 -7
  32. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  33. package/out/zero-cache/src/services/change-source/pg/fork-request.d.ts +57 -0
  34. package/out/zero-cache/src/services/change-source/pg/fork-request.d.ts.map +1 -0
  35. package/out/zero-cache/src/services/change-source/pg/fork-request.js +54 -0
  36. package/out/zero-cache/src/services/change-source/pg/fork-request.js.map +1 -0
  37. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  38. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +6 -0
  39. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  40. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +1 -0
  41. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  42. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +3 -0
  43. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
  44. package/out/zero-cache/src/services/change-source/protocol/current/status.js +2 -1
  45. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  46. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
  47. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  48. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +1 -3
  49. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  50. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +6 -6
  51. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  52. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +41 -2
  53. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  54. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +55 -20
  55. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  56. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +9 -0
  57. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  58. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +27 -8
  59. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  60. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +1 -12
  61. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  62. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  63. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +29 -2
  64. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +1 -1
  65. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +80 -11
  66. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  67. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts +2 -2
  68. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -1
  69. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  70. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +2 -2
  71. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -1
  72. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  73. package/out/zero-cache/src/services/change-streamer/server-start-metrics.d.ts +33 -0
  74. package/out/zero-cache/src/services/change-streamer/server-start-metrics.d.ts.map +1 -0
  75. package/out/zero-cache/src/services/change-streamer/server-start-metrics.js +42 -0
  76. package/out/zero-cache/src/services/change-streamer/server-start-metrics.js.map +1 -0
  77. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +17 -6
  78. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  79. package/out/zero-cache/src/services/change-streamer/subscriber.js +5 -4
  80. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  81. package/out/zero-client/src/client/version.js +1 -1
  82. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  83. package/out/zero-protocol/src/ast.js +2 -1
  84. package/out/zero-protocol/src/ast.js.map +1 -1
  85. package/out/zero-protocol/src/down.d.ts +27 -27
  86. package/package.json +2 -2
@@ -0,0 +1,42 @@
1
+ import { getOrCreateGauge } from "../../observability/metrics.js";
2
+ //#region ../zero-cache/src/services/change-streamer/server-start-metrics.ts
3
+ /**
4
+ * Records where a replication-manager's start goes, reported once it is
5
+ * ready by the `zero.replication.server_start` gauge, in seconds, labeled
6
+ * by `phase` and `kind` (see {@link InitializeKind}). This is for diagnosis,
7
+ * e.g. of why a start is slow, as the process only starts once.
8
+ */
9
+ var ServerStartMetrics = class {
10
+ #phasesMs = /* @__PURE__ */ new Map();
11
+ #kind;
12
+ #ready = false;
13
+ setKind(kind) {
14
+ this.#kind = kind;
15
+ }
16
+ record(phase, ms) {
17
+ if (ms !== void 0 && !this.#ready) this.#phasesMs.set(phase, ms);
18
+ }
19
+ /**
20
+ * Records the `total` (by default, the time since the process started) and
21
+ * starts reporting the gauge. Phases recorded afterwards are ignored.
22
+ */
23
+ ready(totalMs = performance.now()) {
24
+ if (this.#ready) return;
25
+ this.record("total", totalMs);
26
+ this.#ready = true;
27
+ const kind = this.#kind ?? "unknown";
28
+ getOrCreateGauge("replication", "server_start", {
29
+ description: "The duration of each phase of the replication-manager start (fork_wait, restore, catch_up, backup_confirm, and the total from process start to readiness), by the kind of initialization (fork, resume, rmv1, initial_sync).",
30
+ unit: "s"
31
+ }).addCallback((o) => {
32
+ for (const [phase, ms] of this.#phasesMs) o.observe(ms / 1e3, {
33
+ phase,
34
+ kind
35
+ });
36
+ });
37
+ }
38
+ };
39
+ //#endregion
40
+ export { ServerStartMetrics };
41
+
42
+ //# sourceMappingURL=server-start-metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-start-metrics.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/server-start-metrics.ts"],"sourcesContent":["import {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {InitializeKind} from '../change-source/common/replica-restore.ts';\n\n/**\n * The phases of a replication-manager's start:\n * - `fork_wait`: selecting the replica to restore from (and creating or\n * claiming its slot), including the wait for a forked replica to pass the\n * new slot's position\n * - `restore`: restoring the replica from its backup\n * - `catch_up`: from initialization to the backup-replicator being caught up\n * (i.e. its replication lag readiness)\n * - `backup_confirm`: waiting for the first backup to cover the replica\n * - `total`: from the start of the (change-streamer) process to readiness\n *\n * The phases are not exhaustive (e.g. connecting to and preparing the\n * upstream), and `catch_up` and `backup_confirm` overlap.\n */\nexport type ServerStartPhase =\n | 'fork_wait'\n | 'restore'\n | 'catch_up'\n | 'backup_confirm'\n | 'total';\n\n/**\n * Records where a replication-manager's start goes, reported once it is\n * ready by the `zero.replication.server_start` gauge, in seconds, labeled\n * by `phase` and `kind` (see {@link InitializeKind}). This is for diagnosis,\n * e.g. of why a start is slow, as the process only starts once.\n */\nexport class ServerStartMetrics {\n readonly #phasesMs = new Map<ServerStartPhase, number>();\n #kind: InitializeKind | undefined;\n #ready = false;\n\n setKind(kind: InitializeKind | undefined) {\n this.#kind = kind;\n }\n\n record(phase: ServerStartPhase, ms: number | undefined) {\n if (ms !== undefined && !this.#ready) {\n this.#phasesMs.set(phase, ms);\n }\n }\n\n /**\n * Records the `total` (by default, the time since the process started) and\n * starts reporting the gauge. Phases recorded afterwards are ignored.\n */\n ready(totalMs = performance.now()) {\n if (this.#ready) {\n return;\n }\n this.record('total', totalMs);\n this.#ready = true;\n const kind = this.#kind ?? 'unknown';\n getOrCreateGauge('replication', 'server_start', {\n description:\n 'The duration of each phase of the replication-manager start ' +\n '(fork_wait, restore, catch_up, backup_confirm, and the total ' +\n 'from process start to readiness), by the kind of initialization ' +\n '(fork, resume, rmv1, initial_sync).',\n unit: 's',\n }).addCallback(o => {\n for (const [phase, ms] of this.#phasesMs) {\n o.observe(ms / 1000, {phase, kind});\n }\n });\n }\n}\n"],"mappings":";;;;;;;;AA8BA,IAAa,qBAAb,MAAgC;CAC9B,4BAAqB,IAAI,IAA8B;CACvD;CACA,SAAS;CAET,QAAQ,MAAkC;EACxC,KAAK,QAAQ;CACf;CAEA,OAAO,OAAyB,IAAwB;EACtD,IAAI,OAAO,KAAA,KAAa,CAAC,KAAK,QAC5B,KAAK,UAAU,IAAI,OAAO,EAAE;CAEhC;;;;;CAMA,MAAM,UAAU,YAAY,IAAI,GAAG;EACjC,IAAI,KAAK,QACP;EAEF,KAAK,OAAO,SAAS,OAAO;EAC5B,KAAK,SAAS;EACd,MAAM,OAAO,KAAK,SAAS;EAC3B,iBAAiB,eAAe,gBAAgB;GAC9C,aACE;GAIF,MAAM;EACR,CAAC,CAAC,CAAC,aAAY,MAAK;GAClB,KAAK,MAAM,CAAC,OAAO,OAAO,KAAK,WAC7B,EAAE,QAAQ,KAAK,KAAM;IAAC;IAAO;GAAI,CAAC;EAEtC,CAAC;CACH;AACF"}
@@ -35,13 +35,16 @@ export type SubscriberOptions = {
35
35
  */
36
36
  onBackfillIgnored?: ((columns: string[]) => void) | undefined;
37
37
  /**
38
- * Resolves to whether lag reporting is disabled upstream. If so, a
39
- * {@link LAG_REPORTING_DISABLED} status is sent once the subscriber is
40
- * aligned, as a stand-in for the lag reports that a subscriber would
41
- * otherwise use to determine that it is caught up (e.g. to advertise
42
- * readiness to serve traffic).
38
+ * Resolves, once it is known whether lag reporting is enabled upstream, to
39
+ * the means of requesting a lag report, or to `undefined` if it is disabled.
40
+ *
41
+ * Subscribers use lag reports to determine that they are caught up (e.g. to
42
+ * advertise readiness to serve traffic). Once the subscriber is aligned, a
43
+ * report is therefore requested, so that it is received without waiting out
44
+ * the report interval. If lag reporting is disabled, a
45
+ * {@link LAG_REPORTING_DISABLED} status is sent as a stand-in instead.
43
46
  */
44
- lagReportingDisabled?: Promise<boolean> | undefined;
47
+ lagReports?: Promise<LagReports | undefined> | undefined;
45
48
  /**
46
49
  * Whether the subscriber switches over to another replication-manager
47
50
  * when this one drains (see `DrainTracker`).
@@ -183,6 +186,14 @@ export declare class Subscriber {
183
186
  replicaVersion?: string | undefined;
184
187
  }): void;
185
188
  }
189
+ /** Requests lag reports from upstream (see {@link SubscriberOptions.lagReports}). */
190
+ export type LagReports = {
191
+ /**
192
+ * Requests that a lag report be sent now rather than at the next report
193
+ * interval. Requests are coalesced upstream, e.g. with a report in flight.
194
+ */
195
+ request(): void;
196
+ };
186
197
  /**
187
198
  * Sent to an aligned subscriber when lag reporting is disabled upstream,
188
199
  * signaling that the subscriber is caught up. This is a lag report with a
@@ -1 +1 @@
1
- {"version":3,"file":"subscriber.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/subscriber.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subscriber.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/subscriber.ts"],"names":[],"mappings":"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"}
@@ -40,7 +40,7 @@ var Subscriber = class {
40
40
  #backfills;
41
41
  #onAligned;
42
42
  #onBackfillIgnored;
43
- #lagReportingDisabled;
43
+ #lagReports;
44
44
  #aligned = false;
45
45
  #caughtUpWatermark;
46
46
  #caughtUp = resolver();
@@ -59,7 +59,7 @@ var Subscriber = class {
59
59
  this.#backfills = options.backfills;
60
60
  this.#onAligned = options.onAligned;
61
61
  this.#onBackfillIgnored = options.onBackfillIgnored;
62
- this.#lagReportingDisabled = options.lagReportingDisabled;
62
+ this.#lagReports = options.lagReports;
63
63
  }
64
64
  /**
65
65
  * The subscriber's tracked pending backfills, or `undefined` if the
@@ -426,8 +426,9 @@ var Subscriber = class {
426
426
  this.#onAligned?.(this);
427
427
  this.#caughtUpWatermark = this.#watermark;
428
428
  this.#checkCaughtUp();
429
- this.#lagReportingDisabled?.then((disabled) => {
430
- if (disabled) this.sendStatus(LAG_REPORTING_DISABLED);
429
+ this.#lagReports?.then((lagReports) => {
430
+ if (lagReports) lagReports.request();
431
+ else this.sendStatus(LAG_REPORTING_DISABLED);
431
432
  });
432
433
  }
433
434
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"subscriber.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/subscriber.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {BigIntJSON} from '../../../../shared/src/bigint-json.ts';\nimport type {Enum} from '../../../../shared/src/enum.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {RingBuffer} from '../../../../shared/src/ring-buffer.ts';\nimport {max} from '../../types/lexi-version.ts';\nimport type {Subscription} from '../../types/subscription.ts';\nimport type {ReplicatorMode} from '../replicator/replicator.ts';\nimport type {BackfillState} from './backfill-state.ts';\nimport type {PreSerializedBatch} from './broadcast.ts';\nimport type {\n ChangeTag,\n Downstream,\n Status,\n WatermarkedChange,\n} from './change-streamer.ts';\nimport type * as ErrorType from './error-type-enum.ts';\n\ntype ErrorType = Enum<typeof ErrorType>;\n\nconst DEFAULT_BACKLOG_HIGH_WATER_BYTES = 16 * 1024 * 1024;\nconst DEFAULT_BACKLOG_LOW_WATER_RATIO = 0.8;\n\nexport type SubscriberOptions = {\n backlogHighWaterBytes?: number | undefined;\n backlogLowWaterRatio?: number | undefined;\n wsBatched?: boolean | undefined;\n\n /**\n * Called whenever the subscriber's acked watermark advances, i.e. when it\n * has confirmed that a commit was durably applied to its replica. The ACK\n * therefore lags the subscriber's replica rather than leading it.\n */\n onAck?: ((watermark: string) => void) | undefined;\n\n /**\n * The subscriber's pending backfills (protocol v8+), which are tracked\n * over the changes sent to the subscriber.\n */\n backfills?: BackfillState | undefined;\n\n /**\n * Called once the subscriber has been sent all changes up to the head of\n * the stream, i.e. its catchup and backlog have been flushed. From this\n * point on, the subscriber's tracked {@link backfills} reflect the same\n * position in the stream as that of the change-streamer.\n */\n onAligned?: ((subscriber: Subscriber) => void) | undefined;\n\n /**\n * Called when a `backfill` or `backfill-completed` message is ignored for\n * some of the subscriber's pending backfill columns, after the subscriber\n * is aligned.\n */\n onBackfillIgnored?: ((columns: string[]) => void) | undefined;\n\n /**\n * Resolves to whether lag reporting is disabled upstream. If so, a\n * {@link LAG_REPORTING_DISABLED} status is sent once the subscriber is\n * aligned, as a stand-in for the lag reports that a subscriber would\n * otherwise use to determine that it is caught up (e.g. to advertise\n * readiness to serve traffic).\n */\n lagReportingDisabled?: Promise<boolean> | undefined;\n\n /**\n * Whether the subscriber switches over to another replication-manager\n * when this one drains (see `DrainTracker`).\n */\n drainable?: boolean | undefined;\n};\n\n/**\n * A status message queued in the backlog, so that it is sent downstream in\n * the order in which it was received relative to (backlogged) changes.\n */\ntype QueuedStatus = {readonly status: string};\n\ntype BacklogEntry = WatermarkedChange | QueuedStatus;\n\nfunction isQueuedStatus(entry: BacklogEntry): entry is QueuedStatus {\n return !Array.isArray(entry);\n}\n\nfunction entryBytes(entry: BacklogEntry): number {\n return isQueuedStatus(entry) ? entry.status.length : entry[2].length;\n}\n\nexport type BacklogFullWait = {\n readonly promise: Promise<void>;\n cancel(): void;\n};\n\nexport type SubscriberStats = {\n processRate: number;\n pending: number;\n backlog: number;\n backlogBytes: number;\n totalBufferedBytes: number;\n missedLastTimeout: boolean;\n};\n\n/**\n * Encapsulates a subscriber to changes. All subscribers start in a\n * \"catchup\" phase in which changes are buffered in a backlog while the\n * storer is queried to send any changes that were committed since the\n * subscriber's watermark. Once the catchup is complete, calls to\n * {@link send()} result in immediately sending the change.\n */\nexport class Subscriber {\n readonly #protocolVersion: number;\n readonly id: string;\n readonly mode: ReplicatorMode;\n readonly drainable: boolean;\n readonly #downstream: Subscription<string | PreSerializedBatch>;\n readonly #wsBatched: boolean;\n #watermark: string;\n #acked: string;\n #backlog: RingBuffer<BacklogEntry> | null;\n // While catchup is running, live changes are buffered here instead of being\n // pushed downstream. RingBuffer lets drainBacklog consume that backlog without\n // shifting an array, which matters when a subscriber is far behind.\n #backlogBytes = 0;\n #backlogInFlightBytes = 0;\n #backlogDrain: Promise<void> | null = null;\n readonly #backlogBackpressure: ByteBackpressureGate;\n readonly #backlogFullWaiters = new Set<Resolver<void>>();\n readonly #onAck: ((watermark: string) => void) | undefined;\n readonly #backfills: BackfillState | undefined;\n readonly #onAligned: ((subscriber: Subscriber) => void) | undefined;\n readonly #onBackfillIgnored: ((columns: string[]) => void) | undefined;\n readonly #lagReportingDisabled: Promise<boolean> | undefined;\n #aligned = false;\n // The watermark to be acked for the subscriber to be caught up, set when\n // it is aligned.\n #caughtUpWatermark: string | undefined;\n readonly #caughtUp = resolver<void>();\n\n constructor(\n protocolVersion: number,\n id: string,\n mode: ReplicatorMode,\n watermark: string,\n downstream: Subscription<string | PreSerializedBatch>,\n options: SubscriberOptions = {},\n ) {\n this.#protocolVersion = protocolVersion;\n this.id = id;\n this.mode = mode;\n this.drainable = options.drainable ?? false;\n this.#downstream = downstream;\n this.#wsBatched = options.wsBatched ?? false;\n this.#watermark = watermark;\n this.#acked = watermark;\n this.#backlog = new RingBuffer();\n this.#backlogBackpressure = new ByteBackpressureGate(\n options.backlogHighWaterBytes ?? DEFAULT_BACKLOG_HIGH_WATER_BYTES,\n options.backlogLowWaterRatio ?? DEFAULT_BACKLOG_LOW_WATER_RATIO,\n );\n this.#onAck = options.onAck;\n this.#backfills = options.backfills;\n this.#onAligned = options.onAligned;\n this.#onBackfillIgnored = options.onBackfillIgnored;\n this.#lagReportingDisabled = options.lagReportingDisabled;\n }\n\n /**\n * The subscriber's tracked pending backfills, or `undefined` if the\n * subscriber does not report them (i.e. protocol < v8).\n */\n get backfills(): BackfillState | undefined {\n return this.#backfills;\n }\n\n /**\n * Whether the subscriber has been sent all changes up to the head of the\n * stream (see {@link SubscriberOptions.onAligned}).\n */\n get aligned(): boolean {\n return this.#aligned;\n }\n\n /**\n * Resolves once the subscriber has caught up, i.e. once it has acked\n * (durably applied) all changes up to the head of the stream at the time\n * it was {@link aligned}. Never resolves if the subscriber is closed first.\n */\n get caughtUp(): Promise<void> {\n return this.#caughtUp.promise;\n }\n\n #trackBackfills(change: WatermarkedChange) {\n if (this.#backfills) {\n const ignored = this.#backfills.applySerialized(change);\n if (ignored.length && this.#aligned) {\n this.#onBackfillIgnored?.(ignored);\n }\n }\n }\n\n get watermark() {\n return this.#watermark;\n }\n\n get acked() {\n return this.#acked;\n }\n\n /**\n * Whether the backlog of live changes buffered during catchup has reached the\n * point at which {@link send()} stops resolving. Past it the subscriber is no\n * longer free: it holds up every subsequent flush, and with no other\n * subscriber to form a majority it stalls replication outright.\n */\n get backlogFull() {\n return (\n this.#bufferedBacklogBytes >= this.#backlogBackpressure.highWaterBytes\n );\n }\n\n /**\n * @returns whether the subscriber is currently sending backlogged messages,\n * vs caught up and sending the \"head\" of the replication stream.\n */\n isBacklogged() {\n return this.#backlog !== null || this.#bufferedBacklogBytes > 0;\n }\n\n /**\n * Resolves the first time {@link backlogFull} becomes true, so that a caller\n * holding the subscriber in catchup can give up at the moment the subscriber\n * starts costing replication rather than on a timer. Call cancel() when the\n * wait is no longer needed so the subscriber does not retain it.\n */\n whenBacklogFull(): BacklogFullWait {\n if (this.backlogFull) {\n return {promise: promiseVoid, cancel() {}};\n }\n const r = resolver<void>();\n this.#backlogFullWaiters.add(r);\n return {\n promise: r.promise,\n cancel: () => {\n this.#backlogFullWaiters.delete(r);\n },\n };\n }\n\n send(change: WatermarkedChange): Promise<void> {\n const [watermark] = change;\n if (watermark > this.#watermark) {\n if (this.#backlog) {\n // During catchup, buffer live changes behind the durable catchup stream.\n // The returned promise applies backpressure if the buffered bytes cross\n // the high water mark.\n this.#pushBacklog(change);\n return this.#maybeWaitForBacklogSpace();\n }\n return this.#sendChange(change);\n }\n return promiseVoid;\n }\n\n sendBatch(\n changes: readonly WatermarkedChange[],\n preSerialized?: PreSerializedBatch | undefined,\n ): Promise<void> {\n if (changes.length === 0) {\n return promiseVoid;\n }\n\n if (\n this.#wsBatched &&\n preSerialized &&\n !this.#backlog &&\n this.#initialized &&\n changes[0][0] > this.#watermark &&\n (this.#protocolVersion >= 5 ||\n changes.every(c => this.supportsMessage(c[1])))\n ) {\n let commitWatermark: string | undefined;\n for (let i = changes.length - 1; i >= 0; i--) {\n if (changes[i][1] === 'commit') {\n commitWatermark = changes[i][0];\n break;\n }\n }\n if (commitWatermark) {\n this.#watermark = commitWatermark;\n }\n for (const change of changes) {\n this.#trackBackfills(change);\n }\n return this.#sendPreSerializedDownstream(preSerialized, commitWatermark);\n }\n\n const promises: Promise<void>[] = [];\n for (const change of changes) {\n const p = this.send(change);\n if (p !== promiseVoid) {\n promises.push(p);\n }\n }\n if (promises.length === 0) {\n return promiseVoid;\n }\n if (promises.length === 1) {\n return promises[0];\n }\n return Promise.all(promises).then(() => {});\n }\n\n #initialized = false;\n\n /**\n * Called once the subscriber's watermark has been validated in the initial\n * catchup process.\n */\n #initialize() {\n if (!this.#initialized) {\n this.#initialized = true;\n // The initial status precedes the catchup, so it bypasses the backlog.\n void this.#sendDownstream(['status', {tag: 'status'}]);\n }\n }\n\n /**\n * Sends a status message (e.g. a lag report) downstream. While the\n * subscriber is catching up, the status is queued in the backlog behind\n * the changes that preceded it in the replication stream, so that the\n * subscriber processes it in stream order. This is necessary for lag\n * reports to measure the lag of the subscriber, including its catchup.\n *\n * Status messages received before the subscriber is initialized are\n * dropped.\n */\n sendStatus(status: Status) {\n if (!this.#initialized) {\n return;\n }\n const downstream: Downstream = ['status', status];\n if (this.#backlog) {\n this.#pushBacklog({status: BigIntJSON.stringify(downstream)});\n return;\n }\n void this.#sendDownstream(downstream);\n }\n\n /**\n * Sends a control status (e.g. `draining` or `restore`) downstream\n * immediately, regardless of whether the subscriber is initialized or\n * catching up. Unlike {@link sendStatus}, such statuses are not tied to a\n * position in the replication stream.\n */\n sendControlStatus(status: Status) {\n void this.#sendDownstream(['status', status]);\n }\n\n /** catchup() is called on ChangeEntries loaded from the store. */\n async catchup(change: WatermarkedChange) {\n this.#initialize();\n await this.#sendChange(change);\n }\n\n /**\n * Marks the Subscribe as \"caught up\" and flushes any backlog of\n * entries that were received during the catchup.\n */\n setCaughtUp(): Promise<void> {\n this.#initialize();\n if (!this.#backlog) {\n return this.#backlogDrain ?? promiseVoid;\n }\n if (!this.#backlogDrain) {\n // Keep #backlog non-null while queued entries are being handed to\n // downstream. That preserves ordering for sends that race with\n // setCaughtUp(): they append to the same backlog instead of bypassing\n // older buffered changes.\n this.#backlogDrain = this.#drainBacklog();\n void this.#backlogDrain.catch(e => this.fail(e));\n }\n return this.#backlogDrain;\n }\n\n async #sendChange(change: WatermarkedChange) {\n const [watermark, tag, json] = change;\n if (watermark <= this.watermark) {\n return;\n }\n if (!this.supportsMessage(tag)) {\n return;\n }\n if (tag === 'commit') {\n this.#watermark = watermark;\n }\n this.#trackBackfills(change);\n const result = await this.#sendStringifiedDownstream(json);\n if (tag === 'commit' && result === 'consumed') {\n this.#advanceAck(watermark);\n }\n }\n\n #advanceAck(watermark: string) {\n // Sends can complete out of order (e.g. the bounded window in\n // #drainBacklog), so the ack only advances monotonically, and listeners\n // are only notified when it does.\n const acked = max(this.#acked, watermark);\n if (acked !== this.#acked) {\n this.#acked = acked;\n this.#onAck?.(acked);\n this.#checkCaughtUp();\n }\n }\n\n #checkCaughtUp() {\n if (\n this.#caughtUpWatermark !== undefined &&\n this.#acked >= this.#caughtUpWatermark\n ) {\n this.#caughtUpWatermark = undefined;\n this.#caughtUp.resolve();\n }\n }\n\n #sendDownstream(downstream: Downstream) {\n return this.#sendStringifiedDownstream(BigIntJSON.stringify(downstream));\n }\n\n async #sendStringifiedDownstream(json: string) {\n const size = json.length;\n this.#pending++;\n this.#pendingBytes += size;\n const {result} = this.#downstream.push(json);\n try {\n return await result;\n } finally {\n this.#pending--;\n this.#pendingBytes -= size;\n this.#processed++;\n }\n }\n\n async #sendPreSerializedDownstream(\n batch: PreSerializedBatch,\n commitWatermark?: string | undefined,\n ): Promise<void> {\n const size = batch.byteLength;\n this.#pending += batch.changes.length;\n this.#pendingBytes += size;\n const {result} = this.#downstream.push(batch);\n try {\n const outcome = await result;\n if (commitWatermark && outcome === 'consumed') {\n this.#advanceAck(commitWatermark);\n }\n } finally {\n this.#pending -= batch.changes.length;\n this.#pendingBytes -= size;\n this.#processed += batch.changes.length;\n }\n }\n\n // `pending` and `processed` stats are tracked by periodically sampling\n // the running totals (by the progress tracker in the Forwarder).\n // This information was originally collected for use in flow control\n // decisions. The final flow control algorithm ended up being simpler\n // than expected and does not actually use this information. However, the\n // stats are still tracked and logged during flow control decisions for\n // debugging, forensics, and potential improvements to the algorithm.\n\n #pending = 0;\n #pendingBytes = 0;\n #processed = 0;\n #samples: {processed: number; timestamp: number}[] = [\n {processed: 0, timestamp: performance.now()},\n ];\n\n /**\n * The number of downstream messages that have yet to be acked.\n */\n get numPending() {\n return this.#pending + this.#backlogCount;\n }\n\n /**\n * The total number of downstream messages that the subscriber has\n * processed (i.e. acked).\n */\n get numProcessed() {\n return this.#processed;\n }\n\n /**\n * Records a new history entry for the number of messages processed,\n * keeping the number of samples bounded to `maxSamples`.\n */\n sampleProcessRate(now: number, maxSamples = 10): this {\n while (this.#samples.length >= maxSamples) {\n this.#samples.shift();\n }\n this.#samples.push({processed: this.#processed, timestamp: now});\n return this;\n }\n\n getStats(): SubscriberStats {\n const pending = this.numPending;\n if (this.#samples.length < 2) {\n return {\n processRate: 0,\n pending,\n backlog: this.#backlogCount,\n backlogBytes: this.#bufferedBacklogBytes,\n totalBufferedBytes: this.#totalBufferedBytes,\n missedLastTimeout: this.#missedLastTimeout,\n };\n }\n const from = this.#samples[0];\n const to = must(this.#samples.at(-1));\n const processed = to.processed - from.processed;\n const seconds = (to.timestamp - from.timestamp) / 1000;\n const processRate = seconds === 0 ? 0 : processed / seconds;\n return {\n processRate,\n pending,\n backlog: this.#backlogCount,\n backlogBytes: this.#bufferedBacklogBytes,\n totalBufferedBytes: this.#totalBufferedBytes,\n missedLastTimeout: this.#missedLastTimeout,\n };\n }\n\n #missedLastTimeout = false;\n #laggingSinceMs: number | undefined;\n\n trackResponseResult(result: 'on-time' | 'timed-out') {\n this.#missedLastTimeout = result === 'timed-out';\n if (result === 'on-time') {\n this.#laggingSinceMs = undefined;\n }\n }\n\n /**\n * Reports the change rate of a slow subscriber (i.e. that missed the last\n * timeout) compared to the change rate of the (slowest) subscriber that\n * responded on time.\n *\n * * `lagging` indicates that this subscriber is slower\n * * `catching-up` indicates that it is faster\n *\n * Returns the total duration in which subscriber has been continuously\n * reported as `lagging`.\n */\n reportChangeRate(now: number, status: 'lagging' | 'catching-up') {\n assert(\n this.#missedLastTimeout,\n `reportChangeRate should only be called for slow subscribers`,\n );\n if (status === 'catching-up') {\n this.#laggingSinceMs = undefined;\n return 0;\n }\n this.#laggingSinceMs ??= now;\n return now - this.#laggingSinceMs;\n }\n\n supportsMessage(tag: ChangeTag) {\n switch (tag) {\n case 'update-table-metadata':\n // update-table-row-key is only understood by subscribers >= protocol v5\n return this.#protocolVersion >= 5;\n }\n return true;\n }\n\n /**\n * Ends the subscription without sending a downstream `['error', ...]`.\n *\n * This is deliberate, and not the same as reporting the failure to the\n * subscriber: `IncrementalSyncer` treats any `['error', ...]` as terminal and\n * shuts down to restore a fresh replica from litestream, whereas a clean end\n * backs off and re-subscribes. The failures routed here -- storer catchup\n * errors, backlog drain errors, backup-monitor errors -- are transient, so a\n * reconnect is the proportionate response and a fleet-wide restore is not.\n *\n * Callers are responsible for logging `err`; it is not carried downstream.\n */\n fail(_err?: unknown) {\n this.close();\n }\n\n close(\n error?: ErrorType,\n message?: string,\n details?: {replicaVersion?: string | undefined},\n ) {\n // Closing the subscriber must also release producers that are blocked on\n // backlog capacity; there is no future drain that could wake them.\n this.#backlog = null;\n this.#backlogBytes = 0;\n this.#backlogBackpressure.releaseAll();\n // The backlog can no longer grow, so nothing would ever resolve these.\n // Waiters re-check backlogFull, which is now false, and see the close.\n this.#resolveBacklogFullWaiters();\n\n if (error !== undefined) {\n // Wait for the ACK of the error message before closing the connection.\n void this.#sendDownstream([\n 'error',\n {type: error, message, ...details},\n ]).finally(() => this.#downstream.cancel());\n } else {\n this.#downstream.cancel();\n }\n }\n\n get #backlogCount() {\n return this.#backlog?.size ?? 0;\n }\n\n get #bufferedBacklogBytes() {\n // Include entries already handed to downstream but not yet consumed. Without\n // this, setCaughtUp() could move bytes out of #backlog faster than the\n // downstream Subscription can process them and release producers too early.\n return this.#backlogBytes + this.#backlogInFlightBytes;\n }\n\n get #totalBufferedBytes() {\n return this.#bufferedBacklogBytes + this.#pendingBytes;\n }\n\n #pushBacklog(entry: BacklogEntry) {\n assert(this.#backlog, 'cannot push to backlog after catchup completed');\n this.#backlog.push(entry);\n this.#backlogBytes += entryBytes(entry);\n if (this.backlogFull) {\n this.#resolveBacklogFullWaiters();\n }\n }\n\n #resolveBacklogFullWaiters() {\n const waiters = [...this.#backlogFullWaiters];\n this.#backlogFullWaiters.clear();\n for (const waiter of waiters) {\n waiter.resolve();\n }\n }\n\n #maybeWaitForBacklogSpace(): Promise<void> {\n return this.#backlogBackpressure.waitForSpace(this.#bufferedBacklogBytes);\n }\n\n async #drainBacklog() {\n const inFlight: {promise: Promise<void>; bytes: number}[] = [];\n let inFlightBytes = 0;\n\n try {\n for (;;) {\n const entry = this.#backlog?.shift();\n if (!entry) {\n const closed = this.#backlog === null;\n this.#backlog = null;\n this.#backlogBytes = 0;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n if (!closed && !this.#aligned) {\n // All changes up to the head have been handed to #sendChange()\n // (and thus tracked), and subsequent changes are sent directly.\n this.#aligned = true;\n this.#onAligned?.(this);\n this.#caughtUpWatermark = this.#watermark;\n this.#checkCaughtUp();\n void this.#lagReportingDisabled?.then(disabled => {\n if (disabled) {\n this.sendStatus(LAG_REPORTING_DISABLED);\n }\n });\n }\n break;\n }\n\n const bytes = entryBytes(entry);\n this.#backlogBytes -= bytes;\n this.#backlogInFlightBytes += bytes;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n\n // Send backlog entries in order, but keep only a bounded byte window in\n // flight. This avoids replacing one unbounded buffer with another inside\n // the downstream Subscription during catchup completion.\n const send = isQueuedStatus(entry)\n ? this.#sendStringifiedDownstream(entry.status).then(() => {})\n : this.#sendChange(entry);\n const promise = send.finally(() => {\n this.#backlogInFlightBytes -= bytes;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n });\n inFlight.push({promise, bytes});\n inFlightBytes += bytes;\n\n while (inFlightBytes >= this.#backlogBackpressure.highWaterBytes) {\n const next = must(inFlight.shift());\n await next.promise;\n inFlightBytes -= next.bytes;\n }\n }\n\n for (const {promise} of inFlight) {\n await promise;\n }\n } finally {\n this.#backlogDrain = null;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n }\n }\n}\n\nclass ByteBackpressureGate {\n readonly highWaterBytes: number;\n readonly #lowWaterBytes: number;\n readonly #waiters: Resolver<void>[] = [];\n\n constructor(highWaterBytes: number, lowWaterRatio: number) {\n this.highWaterBytes = Math.max(1, highWaterBytes);\n this.#lowWaterBytes =\n this.highWaterBytes * Math.min(1, Math.max(0, lowWaterRatio));\n }\n\n waitForSpace(bufferedBytes: number): Promise<void> {\n if (bufferedBytes < this.highWaterBytes) {\n return promiseVoid;\n }\n\n // One waiter represents one send() call that has already appended its\n // change. The producer is released when the backlog falls back below the low\n // water mark or the subscriber closes.\n const r = resolver<void>();\n this.#waiters.push(r);\n return r.promise;\n }\n\n releaseIfUnderLowWater(bufferedBytes: number) {\n if (this.#waiters.length === 0 || bufferedBytes > this.#lowWaterBytes) {\n return;\n }\n\n // Use a low water mark so waiting producers are released in batches instead\n // of waking one at a time around the high water boundary.\n this.releaseAll();\n }\n\n releaseAll() {\n const waiters = this.#waiters.splice(0);\n for (const waiter of waiters) {\n waiter.resolve();\n }\n }\n}\n\n/**\n * Sent to an aligned subscriber when lag reporting is disabled upstream,\n * signaling that the subscriber is caught up. This is a lag report with a\n * `nextSendTimeMs` of 0 and no `lastTimings`, so that no lag is measured\n * (see `isLagReportingDisabledSignal()` in the replicator's recorder).\n */\nexport const LAG_REPORTING_DISABLED: Status = {\n tag: 'status',\n lagReport: {nextSendTimeMs: 0},\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscriber.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/subscriber.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {BigIntJSON} from '../../../../shared/src/bigint-json.ts';\nimport type {Enum} from '../../../../shared/src/enum.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {RingBuffer} from '../../../../shared/src/ring-buffer.ts';\nimport {max} from '../../types/lexi-version.ts';\nimport type {Subscription} from '../../types/subscription.ts';\nimport type {ReplicatorMode} from '../replicator/replicator.ts';\nimport type {BackfillState} from './backfill-state.ts';\nimport type {PreSerializedBatch} from './broadcast.ts';\nimport type {\n ChangeTag,\n Downstream,\n Status,\n WatermarkedChange,\n} from './change-streamer.ts';\nimport type * as ErrorType from './error-type-enum.ts';\n\ntype ErrorType = Enum<typeof ErrorType>;\n\nconst DEFAULT_BACKLOG_HIGH_WATER_BYTES = 16 * 1024 * 1024;\nconst DEFAULT_BACKLOG_LOW_WATER_RATIO = 0.8;\n\nexport type SubscriberOptions = {\n backlogHighWaterBytes?: number | undefined;\n backlogLowWaterRatio?: number | undefined;\n wsBatched?: boolean | undefined;\n\n /**\n * Called whenever the subscriber's acked watermark advances, i.e. when it\n * has confirmed that a commit was durably applied to its replica. The ACK\n * therefore lags the subscriber's replica rather than leading it.\n */\n onAck?: ((watermark: string) => void) | undefined;\n\n /**\n * The subscriber's pending backfills (protocol v8+), which are tracked\n * over the changes sent to the subscriber.\n */\n backfills?: BackfillState | undefined;\n\n /**\n * Called once the subscriber has been sent all changes up to the head of\n * the stream, i.e. its catchup and backlog have been flushed. From this\n * point on, the subscriber's tracked {@link backfills} reflect the same\n * position in the stream as that of the change-streamer.\n */\n onAligned?: ((subscriber: Subscriber) => void) | undefined;\n\n /**\n * Called when a `backfill` or `backfill-completed` message is ignored for\n * some of the subscriber's pending backfill columns, after the subscriber\n * is aligned.\n */\n onBackfillIgnored?: ((columns: string[]) => void) | undefined;\n\n /**\n * Resolves, once it is known whether lag reporting is enabled upstream, to\n * the means of requesting a lag report, or to `undefined` if it is disabled.\n *\n * Subscribers use lag reports to determine that they are caught up (e.g. to\n * advertise readiness to serve traffic). Once the subscriber is aligned, a\n * report is therefore requested, so that it is received without waiting out\n * the report interval. If lag reporting is disabled, a\n * {@link LAG_REPORTING_DISABLED} status is sent as a stand-in instead.\n */\n lagReports?: Promise<LagReports | undefined> | undefined;\n\n /**\n * Whether the subscriber switches over to another replication-manager\n * when this one drains (see `DrainTracker`).\n */\n drainable?: boolean | undefined;\n};\n\n/**\n * A status message queued in the backlog, so that it is sent downstream in\n * the order in which it was received relative to (backlogged) changes.\n */\ntype QueuedStatus = {readonly status: string};\n\ntype BacklogEntry = WatermarkedChange | QueuedStatus;\n\nfunction isQueuedStatus(entry: BacklogEntry): entry is QueuedStatus {\n return !Array.isArray(entry);\n}\n\nfunction entryBytes(entry: BacklogEntry): number {\n return isQueuedStatus(entry) ? entry.status.length : entry[2].length;\n}\n\nexport type BacklogFullWait = {\n readonly promise: Promise<void>;\n cancel(): void;\n};\n\nexport type SubscriberStats = {\n processRate: number;\n pending: number;\n backlog: number;\n backlogBytes: number;\n totalBufferedBytes: number;\n missedLastTimeout: boolean;\n};\n\n/**\n * Encapsulates a subscriber to changes. All subscribers start in a\n * \"catchup\" phase in which changes are buffered in a backlog while the\n * storer is queried to send any changes that were committed since the\n * subscriber's watermark. Once the catchup is complete, calls to\n * {@link send()} result in immediately sending the change.\n */\nexport class Subscriber {\n readonly #protocolVersion: number;\n readonly id: string;\n readonly mode: ReplicatorMode;\n readonly drainable: boolean;\n readonly #downstream: Subscription<string | PreSerializedBatch>;\n readonly #wsBatched: boolean;\n #watermark: string;\n #acked: string;\n #backlog: RingBuffer<BacklogEntry> | null;\n // While catchup is running, live changes are buffered here instead of being\n // pushed downstream. RingBuffer lets drainBacklog consume that backlog without\n // shifting an array, which matters when a subscriber is far behind.\n #backlogBytes = 0;\n #backlogInFlightBytes = 0;\n #backlogDrain: Promise<void> | null = null;\n readonly #backlogBackpressure: ByteBackpressureGate;\n readonly #backlogFullWaiters = new Set<Resolver<void>>();\n readonly #onAck: ((watermark: string) => void) | undefined;\n readonly #backfills: BackfillState | undefined;\n readonly #onAligned: ((subscriber: Subscriber) => void) | undefined;\n readonly #onBackfillIgnored: ((columns: string[]) => void) | undefined;\n readonly #lagReports: Promise<LagReports | undefined> | undefined;\n #aligned = false;\n // The watermark to be acked for the subscriber to be caught up, set when\n // it is aligned.\n #caughtUpWatermark: string | undefined;\n readonly #caughtUp = resolver<void>();\n\n constructor(\n protocolVersion: number,\n id: string,\n mode: ReplicatorMode,\n watermark: string,\n downstream: Subscription<string | PreSerializedBatch>,\n options: SubscriberOptions = {},\n ) {\n this.#protocolVersion = protocolVersion;\n this.id = id;\n this.mode = mode;\n this.drainable = options.drainable ?? false;\n this.#downstream = downstream;\n this.#wsBatched = options.wsBatched ?? false;\n this.#watermark = watermark;\n this.#acked = watermark;\n this.#backlog = new RingBuffer();\n this.#backlogBackpressure = new ByteBackpressureGate(\n options.backlogHighWaterBytes ?? DEFAULT_BACKLOG_HIGH_WATER_BYTES,\n options.backlogLowWaterRatio ?? DEFAULT_BACKLOG_LOW_WATER_RATIO,\n );\n this.#onAck = options.onAck;\n this.#backfills = options.backfills;\n this.#onAligned = options.onAligned;\n this.#onBackfillIgnored = options.onBackfillIgnored;\n this.#lagReports = options.lagReports;\n }\n\n /**\n * The subscriber's tracked pending backfills, or `undefined` if the\n * subscriber does not report them (i.e. protocol < v8).\n */\n get backfills(): BackfillState | undefined {\n return this.#backfills;\n }\n\n /**\n * Whether the subscriber has been sent all changes up to the head of the\n * stream (see {@link SubscriberOptions.onAligned}).\n */\n get aligned(): boolean {\n return this.#aligned;\n }\n\n /**\n * Resolves once the subscriber has caught up, i.e. once it has acked\n * (durably applied) all changes up to the head of the stream at the time\n * it was {@link aligned}. Never resolves if the subscriber is closed first.\n */\n get caughtUp(): Promise<void> {\n return this.#caughtUp.promise;\n }\n\n #trackBackfills(change: WatermarkedChange) {\n if (this.#backfills) {\n const ignored = this.#backfills.applySerialized(change);\n if (ignored.length && this.#aligned) {\n this.#onBackfillIgnored?.(ignored);\n }\n }\n }\n\n get watermark() {\n return this.#watermark;\n }\n\n get acked() {\n return this.#acked;\n }\n\n /**\n * Whether the backlog of live changes buffered during catchup has reached the\n * point at which {@link send()} stops resolving. Past it the subscriber is no\n * longer free: it holds up every subsequent flush, and with no other\n * subscriber to form a majority it stalls replication outright.\n */\n get backlogFull() {\n return (\n this.#bufferedBacklogBytes >= this.#backlogBackpressure.highWaterBytes\n );\n }\n\n /**\n * @returns whether the subscriber is currently sending backlogged messages,\n * vs caught up and sending the \"head\" of the replication stream.\n */\n isBacklogged() {\n return this.#backlog !== null || this.#bufferedBacklogBytes > 0;\n }\n\n /**\n * Resolves the first time {@link backlogFull} becomes true, so that a caller\n * holding the subscriber in catchup can give up at the moment the subscriber\n * starts costing replication rather than on a timer. Call cancel() when the\n * wait is no longer needed so the subscriber does not retain it.\n */\n whenBacklogFull(): BacklogFullWait {\n if (this.backlogFull) {\n return {promise: promiseVoid, cancel() {}};\n }\n const r = resolver<void>();\n this.#backlogFullWaiters.add(r);\n return {\n promise: r.promise,\n cancel: () => {\n this.#backlogFullWaiters.delete(r);\n },\n };\n }\n\n send(change: WatermarkedChange): Promise<void> {\n const [watermark] = change;\n if (watermark > this.#watermark) {\n if (this.#backlog) {\n // During catchup, buffer live changes behind the durable catchup stream.\n // The returned promise applies backpressure if the buffered bytes cross\n // the high water mark.\n this.#pushBacklog(change);\n return this.#maybeWaitForBacklogSpace();\n }\n return this.#sendChange(change);\n }\n return promiseVoid;\n }\n\n sendBatch(\n changes: readonly WatermarkedChange[],\n preSerialized?: PreSerializedBatch | undefined,\n ): Promise<void> {\n if (changes.length === 0) {\n return promiseVoid;\n }\n\n if (\n this.#wsBatched &&\n preSerialized &&\n !this.#backlog &&\n this.#initialized &&\n changes[0][0] > this.#watermark &&\n (this.#protocolVersion >= 5 ||\n changes.every(c => this.supportsMessage(c[1])))\n ) {\n let commitWatermark: string | undefined;\n for (let i = changes.length - 1; i >= 0; i--) {\n if (changes[i][1] === 'commit') {\n commitWatermark = changes[i][0];\n break;\n }\n }\n if (commitWatermark) {\n this.#watermark = commitWatermark;\n }\n for (const change of changes) {\n this.#trackBackfills(change);\n }\n return this.#sendPreSerializedDownstream(preSerialized, commitWatermark);\n }\n\n const promises: Promise<void>[] = [];\n for (const change of changes) {\n const p = this.send(change);\n if (p !== promiseVoid) {\n promises.push(p);\n }\n }\n if (promises.length === 0) {\n return promiseVoid;\n }\n if (promises.length === 1) {\n return promises[0];\n }\n return Promise.all(promises).then(() => {});\n }\n\n #initialized = false;\n\n /**\n * Called once the subscriber's watermark has been validated in the initial\n * catchup process.\n */\n #initialize() {\n if (!this.#initialized) {\n this.#initialized = true;\n // The initial status precedes the catchup, so it bypasses the backlog.\n void this.#sendDownstream(['status', {tag: 'status'}]);\n }\n }\n\n /**\n * Sends a status message (e.g. a lag report) downstream. While the\n * subscriber is catching up, the status is queued in the backlog behind\n * the changes that preceded it in the replication stream, so that the\n * subscriber processes it in stream order. This is necessary for lag\n * reports to measure the lag of the subscriber, including its catchup.\n *\n * Status messages received before the subscriber is initialized are\n * dropped.\n */\n sendStatus(status: Status) {\n if (!this.#initialized) {\n return;\n }\n const downstream: Downstream = ['status', status];\n if (this.#backlog) {\n this.#pushBacklog({status: BigIntJSON.stringify(downstream)});\n return;\n }\n void this.#sendDownstream(downstream);\n }\n\n /**\n * Sends a control status (e.g. `draining` or `restore`) downstream\n * immediately, regardless of whether the subscriber is initialized or\n * catching up. Unlike {@link sendStatus}, such statuses are not tied to a\n * position in the replication stream.\n */\n sendControlStatus(status: Status) {\n void this.#sendDownstream(['status', status]);\n }\n\n /** catchup() is called on ChangeEntries loaded from the store. */\n async catchup(change: WatermarkedChange) {\n this.#initialize();\n await this.#sendChange(change);\n }\n\n /**\n * Marks the Subscribe as \"caught up\" and flushes any backlog of\n * entries that were received during the catchup.\n */\n setCaughtUp(): Promise<void> {\n this.#initialize();\n if (!this.#backlog) {\n return this.#backlogDrain ?? promiseVoid;\n }\n if (!this.#backlogDrain) {\n // Keep #backlog non-null while queued entries are being handed to\n // downstream. That preserves ordering for sends that race with\n // setCaughtUp(): they append to the same backlog instead of bypassing\n // older buffered changes.\n this.#backlogDrain = this.#drainBacklog();\n void this.#backlogDrain.catch(e => this.fail(e));\n }\n return this.#backlogDrain;\n }\n\n async #sendChange(change: WatermarkedChange) {\n const [watermark, tag, json] = change;\n if (watermark <= this.watermark) {\n return;\n }\n if (!this.supportsMessage(tag)) {\n return;\n }\n if (tag === 'commit') {\n this.#watermark = watermark;\n }\n this.#trackBackfills(change);\n const result = await this.#sendStringifiedDownstream(json);\n if (tag === 'commit' && result === 'consumed') {\n this.#advanceAck(watermark);\n }\n }\n\n #advanceAck(watermark: string) {\n // Sends can complete out of order (e.g. the bounded window in\n // #drainBacklog), so the ack only advances monotonically, and listeners\n // are only notified when it does.\n const acked = max(this.#acked, watermark);\n if (acked !== this.#acked) {\n this.#acked = acked;\n this.#onAck?.(acked);\n this.#checkCaughtUp();\n }\n }\n\n #checkCaughtUp() {\n if (\n this.#caughtUpWatermark !== undefined &&\n this.#acked >= this.#caughtUpWatermark\n ) {\n this.#caughtUpWatermark = undefined;\n this.#caughtUp.resolve();\n }\n }\n\n #sendDownstream(downstream: Downstream) {\n return this.#sendStringifiedDownstream(BigIntJSON.stringify(downstream));\n }\n\n async #sendStringifiedDownstream(json: string) {\n const size = json.length;\n this.#pending++;\n this.#pendingBytes += size;\n const {result} = this.#downstream.push(json);\n try {\n return await result;\n } finally {\n this.#pending--;\n this.#pendingBytes -= size;\n this.#processed++;\n }\n }\n\n async #sendPreSerializedDownstream(\n batch: PreSerializedBatch,\n commitWatermark?: string | undefined,\n ): Promise<void> {\n const size = batch.byteLength;\n this.#pending += batch.changes.length;\n this.#pendingBytes += size;\n const {result} = this.#downstream.push(batch);\n try {\n const outcome = await result;\n if (commitWatermark && outcome === 'consumed') {\n this.#advanceAck(commitWatermark);\n }\n } finally {\n this.#pending -= batch.changes.length;\n this.#pendingBytes -= size;\n this.#processed += batch.changes.length;\n }\n }\n\n // `pending` and `processed` stats are tracked by periodically sampling\n // the running totals (by the progress tracker in the Forwarder).\n // This information was originally collected for use in flow control\n // decisions. The final flow control algorithm ended up being simpler\n // than expected and does not actually use this information. However, the\n // stats are still tracked and logged during flow control decisions for\n // debugging, forensics, and potential improvements to the algorithm.\n\n #pending = 0;\n #pendingBytes = 0;\n #processed = 0;\n #samples: {processed: number; timestamp: number}[] = [\n {processed: 0, timestamp: performance.now()},\n ];\n\n /**\n * The number of downstream messages that have yet to be acked.\n */\n get numPending() {\n return this.#pending + this.#backlogCount;\n }\n\n /**\n * The total number of downstream messages that the subscriber has\n * processed (i.e. acked).\n */\n get numProcessed() {\n return this.#processed;\n }\n\n /**\n * Records a new history entry for the number of messages processed,\n * keeping the number of samples bounded to `maxSamples`.\n */\n sampleProcessRate(now: number, maxSamples = 10): this {\n while (this.#samples.length >= maxSamples) {\n this.#samples.shift();\n }\n this.#samples.push({processed: this.#processed, timestamp: now});\n return this;\n }\n\n getStats(): SubscriberStats {\n const pending = this.numPending;\n if (this.#samples.length < 2) {\n return {\n processRate: 0,\n pending,\n backlog: this.#backlogCount,\n backlogBytes: this.#bufferedBacklogBytes,\n totalBufferedBytes: this.#totalBufferedBytes,\n missedLastTimeout: this.#missedLastTimeout,\n };\n }\n const from = this.#samples[0];\n const to = must(this.#samples.at(-1));\n const processed = to.processed - from.processed;\n const seconds = (to.timestamp - from.timestamp) / 1000;\n const processRate = seconds === 0 ? 0 : processed / seconds;\n return {\n processRate,\n pending,\n backlog: this.#backlogCount,\n backlogBytes: this.#bufferedBacklogBytes,\n totalBufferedBytes: this.#totalBufferedBytes,\n missedLastTimeout: this.#missedLastTimeout,\n };\n }\n\n #missedLastTimeout = false;\n #laggingSinceMs: number | undefined;\n\n trackResponseResult(result: 'on-time' | 'timed-out') {\n this.#missedLastTimeout = result === 'timed-out';\n if (result === 'on-time') {\n this.#laggingSinceMs = undefined;\n }\n }\n\n /**\n * Reports the change rate of a slow subscriber (i.e. that missed the last\n * timeout) compared to the change rate of the (slowest) subscriber that\n * responded on time.\n *\n * * `lagging` indicates that this subscriber is slower\n * * `catching-up` indicates that it is faster\n *\n * Returns the total duration in which subscriber has been continuously\n * reported as `lagging`.\n */\n reportChangeRate(now: number, status: 'lagging' | 'catching-up') {\n assert(\n this.#missedLastTimeout,\n `reportChangeRate should only be called for slow subscribers`,\n );\n if (status === 'catching-up') {\n this.#laggingSinceMs = undefined;\n return 0;\n }\n this.#laggingSinceMs ??= now;\n return now - this.#laggingSinceMs;\n }\n\n supportsMessage(tag: ChangeTag) {\n switch (tag) {\n case 'update-table-metadata':\n // update-table-row-key is only understood by subscribers >= protocol v5\n return this.#protocolVersion >= 5;\n }\n return true;\n }\n\n /**\n * Ends the subscription without sending a downstream `['error', ...]`.\n *\n * This is deliberate, and not the same as reporting the failure to the\n * subscriber: `IncrementalSyncer` treats any `['error', ...]` as terminal and\n * shuts down to restore a fresh replica from litestream, whereas a clean end\n * backs off and re-subscribes. The failures routed here -- storer catchup\n * errors, backlog drain errors, backup-monitor errors -- are transient, so a\n * reconnect is the proportionate response and a fleet-wide restore is not.\n *\n * Callers are responsible for logging `err`; it is not carried downstream.\n */\n fail(_err?: unknown) {\n this.close();\n }\n\n close(\n error?: ErrorType,\n message?: string,\n details?: {replicaVersion?: string | undefined},\n ) {\n // Closing the subscriber must also release producers that are blocked on\n // backlog capacity; there is no future drain that could wake them.\n this.#backlog = null;\n this.#backlogBytes = 0;\n this.#backlogBackpressure.releaseAll();\n // The backlog can no longer grow, so nothing would ever resolve these.\n // Waiters re-check backlogFull, which is now false, and see the close.\n this.#resolveBacklogFullWaiters();\n\n if (error !== undefined) {\n // Wait for the ACK of the error message before closing the connection.\n void this.#sendDownstream([\n 'error',\n {type: error, message, ...details},\n ]).finally(() => this.#downstream.cancel());\n } else {\n this.#downstream.cancel();\n }\n }\n\n get #backlogCount() {\n return this.#backlog?.size ?? 0;\n }\n\n get #bufferedBacklogBytes() {\n // Include entries already handed to downstream but not yet consumed. Without\n // this, setCaughtUp() could move bytes out of #backlog faster than the\n // downstream Subscription can process them and release producers too early.\n return this.#backlogBytes + this.#backlogInFlightBytes;\n }\n\n get #totalBufferedBytes() {\n return this.#bufferedBacklogBytes + this.#pendingBytes;\n }\n\n #pushBacklog(entry: BacklogEntry) {\n assert(this.#backlog, 'cannot push to backlog after catchup completed');\n this.#backlog.push(entry);\n this.#backlogBytes += entryBytes(entry);\n if (this.backlogFull) {\n this.#resolveBacklogFullWaiters();\n }\n }\n\n #resolveBacklogFullWaiters() {\n const waiters = [...this.#backlogFullWaiters];\n this.#backlogFullWaiters.clear();\n for (const waiter of waiters) {\n waiter.resolve();\n }\n }\n\n #maybeWaitForBacklogSpace(): Promise<void> {\n return this.#backlogBackpressure.waitForSpace(this.#bufferedBacklogBytes);\n }\n\n async #drainBacklog() {\n const inFlight: {promise: Promise<void>; bytes: number}[] = [];\n let inFlightBytes = 0;\n\n try {\n for (;;) {\n const entry = this.#backlog?.shift();\n if (!entry) {\n const closed = this.#backlog === null;\n this.#backlog = null;\n this.#backlogBytes = 0;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n if (!closed && !this.#aligned) {\n // All changes up to the head have been handed to #sendChange()\n // (and thus tracked), and subsequent changes are sent directly.\n this.#aligned = true;\n this.#onAligned?.(this);\n this.#caughtUpWatermark = this.#watermark;\n this.#checkCaughtUp();\n void this.#lagReports?.then(lagReports => {\n if (lagReports) {\n lagReports.request();\n } else {\n this.sendStatus(LAG_REPORTING_DISABLED);\n }\n });\n }\n break;\n }\n\n const bytes = entryBytes(entry);\n this.#backlogBytes -= bytes;\n this.#backlogInFlightBytes += bytes;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n\n // Send backlog entries in order, but keep only a bounded byte window in\n // flight. This avoids replacing one unbounded buffer with another inside\n // the downstream Subscription during catchup completion.\n const send = isQueuedStatus(entry)\n ? this.#sendStringifiedDownstream(entry.status).then(() => {})\n : this.#sendChange(entry);\n const promise = send.finally(() => {\n this.#backlogInFlightBytes -= bytes;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n });\n inFlight.push({promise, bytes});\n inFlightBytes += bytes;\n\n while (inFlightBytes >= this.#backlogBackpressure.highWaterBytes) {\n const next = must(inFlight.shift());\n await next.promise;\n inFlightBytes -= next.bytes;\n }\n }\n\n for (const {promise} of inFlight) {\n await promise;\n }\n } finally {\n this.#backlogDrain = null;\n this.#backlogBackpressure.releaseIfUnderLowWater(\n this.#bufferedBacklogBytes,\n );\n }\n }\n}\n\nclass ByteBackpressureGate {\n readonly highWaterBytes: number;\n readonly #lowWaterBytes: number;\n readonly #waiters: Resolver<void>[] = [];\n\n constructor(highWaterBytes: number, lowWaterRatio: number) {\n this.highWaterBytes = Math.max(1, highWaterBytes);\n this.#lowWaterBytes =\n this.highWaterBytes * Math.min(1, Math.max(0, lowWaterRatio));\n }\n\n waitForSpace(bufferedBytes: number): Promise<void> {\n if (bufferedBytes < this.highWaterBytes) {\n return promiseVoid;\n }\n\n // One waiter represents one send() call that has already appended its\n // change. The producer is released when the backlog falls back below the low\n // water mark or the subscriber closes.\n const r = resolver<void>();\n this.#waiters.push(r);\n return r.promise;\n }\n\n releaseIfUnderLowWater(bufferedBytes: number) {\n if (this.#waiters.length === 0 || bufferedBytes > this.#lowWaterBytes) {\n return;\n }\n\n // Use a low water mark so waiting producers are released in batches instead\n // of waking one at a time around the high water boundary.\n this.releaseAll();\n }\n\n releaseAll() {\n const waiters = this.#waiters.splice(0);\n for (const waiter of waiters) {\n waiter.resolve();\n }\n }\n}\n\n/** Requests lag reports from upstream (see {@link SubscriberOptions.lagReports}). */\nexport type LagReports = {\n /**\n * Requests that a lag report be sent now rather than at the next report\n * interval. Requests are coalesced upstream, e.g. with a report in flight.\n */\n request(): void;\n};\n\n/**\n * Sent to an aligned subscriber when lag reporting is disabled upstream,\n * signaling that the subscriber is caught up. This is a lag report with a\n * `nextSendTimeMs` of 0 and no `lastTimings`, so that no lag is measured\n * (see `isLagReportingDisabledSignal()` in the replicator's recorder).\n */\nexport const LAG_REPORTING_DISABLED: Status = {\n tag: 'status',\n lagReport: {nextSendTimeMs: 0},\n};\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * The current version of Zero.
4
4
  */
5
- var version = "1.12.0-canary.5";
5
+ var version = "1.12.0-canary.6";
6
6
  //#endregion
7
7
  export { version };
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/ast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/ast.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { assert } from "../../shared/src/asserts.js";
2
2
  import { literalUnion, readonly, readonlyArray, readonlyObject, strict, valita_exports } from "../../shared/src/valita.js";
3
3
  import { getOrInsertComputed } from "../../shared/src/map.js";
4
+ import { toSorted } from "../../shared/src/iterables.js";
4
5
  import { isPlainObject } from "../../shared/src/deep-merge.js";
5
6
  import { must } from "../../shared/src/must.js";
6
7
  import { rowSchema } from "./data.js";
@@ -290,7 +291,7 @@ function insertRelated(related, subquery) {
290
291
  }
291
292
  var normalizedConditions = /* @__PURE__ */ new WeakSet();
292
293
  function normalizeRelated(related) {
293
- for (let i = 1; i < related.length; i++) if (cmpRelated(related[i - 1], related[i]) > 0) return related.toSorted(cmpRelated);
294
+ for (let i = 1; i < related.length; i++) if (cmpRelated(related[i - 1], related[i]) > 0) return toSorted(related, cmpRelated);
294
295
  return related;
295
296
  }
296
297
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ast.js","names":["v","v.readonly","v.literalUnion","v.readonlyArray","v.readonlyObject","v.strict"],"sources":["../../../../zero-protocol/src/ast.ts"],"sourcesContent":["/**\n * Wire-format representation of the zql AST interface.\n *\n * `v.Type<...>` types are explicitly declared to facilitate Typescript verification\n * that the schemas satisfy the zql type definitions. (Incidentally, explicit types\n * are also required for recursive schema definitions.)\n */\n\nimport {compareUTF8} from 'compare-utf8';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {isPlainObject} from '../../shared/src/deep-merge.ts';\nimport {getOrInsertComputed} from '../../shared/src/map.ts';\nimport {must} from '../../shared/src/must.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {NameMapper} from '../../zero-types/src/name-mapper.ts';\nimport {rowSchema, type Row} from './data.ts';\n\nexport const SUBQ_PREFIX = 'zsubq_';\n\nexport const selectorSchema = v.string();\nexport const toStaticParam = Symbol();\nexport const planIdSymbol = Symbol('planId');\n\nconst orderingElementSchema = v.readonly(\n v.tuple([selectorSchema, v.literalUnion('asc', 'desc')]),\n);\n\nexport const orderingSchema = v.readonlyArray(orderingElementSchema);\nexport type System = 'permissions' | 'client' | 'test';\n\nexport const primitiveSchema = v.union(\n v.string(),\n v.number(),\n v.boolean(),\n v.null(),\n);\n\nexport const equalityOpsSchema = v.literalUnion('=', '!=', 'IS', 'IS NOT');\n\nexport const orderOpsSchema = v.literalUnion('<', '>', '<=', '>=');\n\nexport const likeOpsSchema = v.literalUnion(\n 'LIKE',\n 'NOT LIKE',\n 'ILIKE',\n 'NOT ILIKE',\n);\n\nexport const inOpsSchema = v.literalUnion('IN', 'NOT IN');\n\nexport const simpleOperatorSchema = v.union(\n equalityOpsSchema,\n orderOpsSchema,\n likeOpsSchema,\n inOpsSchema,\n);\n\nconst literalReferenceSchema: v.Type<LiteralReference> = v.readonlyObject({\n type: v.literal('literal'),\n value: v.union(\n v.string(),\n v.number(),\n v.boolean(),\n v.null(),\n v.readonlyArray(v.union(v.string(), v.number(), v.boolean())),\n ),\n});\nconst columnReferenceSchema: v.Type<ColumnReference> = v.readonlyObject({\n type: v.literal('column'),\n name: v.string(),\n});\n\n/**\n * A parameter is a value that is not known at the time the query is written\n * and is resolved at runtime.\n *\n * Static parameters refer to something provided by the caller.\n * Static parameters are injected when the query pipeline is built from the AST\n * and do not change for the life of that pipeline.\n *\n * An example static parameter is the current authentication data.\n * When a user is authenticated, queries on the server have access\n * to the user's authentication data in order to evaluate authorization rules.\n * Authentication data doesn't change over the life of a query as a change\n * in auth data would represent a log-in / log-out of the user.\n *\n * AncestorParameters refer to rows encountered while running the query.\n * They are used by subqueries to refer to rows emitted by parent queries.\n */\nconst parameterReferenceSchema = v.readonlyObject({\n type: v.literal('static'),\n // The \"namespace\" of the injected parameter.\n // Write authorization will send the value of a row\n // prior to the mutation being run (preMutationRow).\n // Read and write authorization will both send the\n // current authentication data (authData).\n anchor: v.literalUnion('authData', 'preMutationRow'),\n field: v.union(v.string(), v.array(v.string())),\n});\n\nconst conditionValueSchema = v.union(\n literalReferenceSchema,\n columnReferenceSchema,\n parameterReferenceSchema,\n);\n\nexport type Parameter = v.Infer<typeof parameterReferenceSchema>;\n\nexport const simpleConditionSchema: v.Type<SimpleCondition> = v.readonlyObject({\n type: v.literal('simple'),\n op: simpleOperatorSchema,\n left: conditionValueSchema,\n right: v.union(parameterReferenceSchema, literalReferenceSchema),\n});\n\ntype ConditionValue = v.Infer<typeof conditionValueSchema>;\n\nexport const correlatedSubqueryConditionOperatorSchema: v.Type<CorrelatedSubqueryConditionOperator> =\n v.literalUnion('EXISTS', 'NOT EXISTS');\n\nexport const correlatedSubqueryConditionSchema: v.Type<CorrelatedSubqueryCondition> =\n v.readonlyObject({\n type: v.literal('correlatedSubquery'),\n related: v.lazy(() => correlatedSubquerySchema),\n op: correlatedSubqueryConditionOperatorSchema,\n flip: v.boolean().optional(),\n scalar: v.boolean().optional(),\n });\n\nexport const conditionSchema: v.Type<Condition> = v.union(\n simpleConditionSchema,\n v.lazy(() => conjunctionSchema),\n v.lazy(() => disjunctionSchema),\n correlatedSubqueryConditionSchema,\n);\n\nconst conjunctionSchema: v.Type<Conjunction> = v.readonlyObject({\n type: v.literal('and'),\n conditions: v.readonlyArray(conditionSchema),\n});\n\nconst disjunctionSchema: v.Type<Disjunction> = v.readonlyObject({\n type: v.literal('or'),\n conditions: v.readonlyArray(conditionSchema),\n});\n\nexport type CompoundKey = readonly [string, ...string[]];\n\nfunction mustCompoundKey(field: readonly string[]): CompoundKey {\n assert(\n Array.isArray(field) && field.length >= 1,\n 'Expected non-empty array for compound key',\n );\n return field as unknown as CompoundKey;\n}\n\nexport const compoundKeySchema: v.Type<CompoundKey> = v.readonly(\n // oxlint-disable-next-line e18e/prefer-spread-syntax\n v.tuple([v.string()]).concat(v.array(v.string())),\n);\n\nconst correlationSchema = v.readonlyObject({\n parentField: compoundKeySchema,\n childField: compoundKeySchema,\n});\n\n// Split out so that its inferred type can be checked against\n// Omit<CorrelatedSubquery, 'correlation'> in ast-type-test.ts.\n// The mutually-recursive reference of the 'other' field to astSchema\n// is the only thing added in v.lazy. The v.lazy is necessary due to the\n// mutually-recursive types, but v.lazy prevents inference of the resulting\n// type.\nexport const correlatedSubquerySchemaOmitSubquery = v.readonlyObject({\n correlation: correlationSchema,\n hidden: v.boolean().optional(),\n system: v.literalUnion('permissions', 'client', 'test').optional(),\n});\n\nexport const correlatedSubquerySchema: v.Type<CorrelatedSubquery> =\n correlatedSubquerySchemaOmitSubquery.extend({\n subquery: v.lazy(() => astSchema),\n });\n\nexport const astSchema: v.Type<AST> = v.readonlyObject({\n schema: v.string().optional(),\n table: v.string(),\n alias: v.string().optional(),\n where: conditionSchema.optional(),\n related: v.readonlyArray(correlatedSubquerySchema).optional(),\n limit: v.number().optional(),\n orderBy: orderingSchema.optional(),\n start: v\n .object({\n row: rowSchema,\n exclusive: v.boolean(),\n })\n .optional(),\n});\n\n/**\n * The maximum nesting depth of an AST sent by a client, counting and/or\n * conditions, correlated subqueries and related subqueries. Parsing and the\n * AST visitors in zero-cache are recursive, so a single deeply nested AST can\n * otherwise overflow the stack or burn seconds of CPU. Realistic queries,\n * permission rules included, are less than 10 levels deep.\n */\nexport const MAX_AST_DEPTH = 50;\n\n/**\n * Returns whether the unparsed `ast` is nested deeper than `maxDepth`.\n */\nexport function exceedsASTDepth(\n ast: unknown,\n maxDepth = MAX_AST_DEPTH,\n): boolean {\n return astExceedsDepth(ast, 0, maxDepth);\n}\n\n// Every step down increases the depth, except the one from an AST into its\n// where clause, so the recursion stops within 2 * maxDepth steps and cannot\n// overflow the stack.\nfunction astExceedsDepth(\n ast: unknown,\n depth: number,\n maxDepth: number,\n): boolean {\n if (depth > maxDepth) {\n return true;\n }\n if (!isPlainObject(ast)) {\n return false;\n }\n const {where, related} = ast;\n return (\n conditionExceedsDepth(where, depth, maxDepth) ||\n (Array.isArray(related) &&\n related.some(\n r =>\n isPlainObject(r) && astExceedsDepth(r.subquery, depth + 1, maxDepth),\n ))\n );\n}\n\nfunction conditionExceedsDepth(\n cond: unknown,\n depth: number,\n maxDepth: number,\n): boolean {\n if (depth > maxDepth) {\n return true;\n }\n if (!isPlainObject(cond)) {\n return false;\n }\n const {conditions, related} = cond;\n return (\n (Array.isArray(conditions) &&\n conditions.some(c => conditionExceedsDepth(c, depth + 1, maxDepth))) ||\n (isPlainObject(related) &&\n astExceedsDepth(related.subquery, depth + 1, maxDepth))\n );\n}\n\n/**\n * {@link astSchema} for ASTs sent by clients, which rejects ASTs nested deeper\n * than {@link MAX_AST_DEPTH} before parsing them. ASTs the server stored itself\n * (e.g. in the CVR) are parsed with {@link astSchema} so that ones stored\n * before the limit existed can still be read.\n */\nexport const depthBoundedAstSchema: v.Type<AST> = Object.assign(\n v\n .unknown()\n .chain((value, options) =>\n exceedsASTDepth(value)\n ? v.err(`AST exceeds the maximum depth of ${MAX_AST_DEPTH}`)\n : astSchema.try(value, options),\n ),\n // A transform serializes as just its name. Show astSchema too, so a change\n // to it still changes the hash in protocol-version.test.ts.\n {depthBounded: astSchema},\n);\n\n/**\n * {@link depthBoundedAstSchema} parsed strictly even inside a lenient parse. A\n * server that ignored an AST field it doesn't know would run a different query.\n */\nexport const strictASTSchema: v.Type<AST> = v.strict(depthBoundedAstSchema);\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * As in SQL you can have multiple orderings. We don't currently\n * support ordering on anything other than the root query.\n */\nexport type OrderPart = readonly [field: string, direction: 'asc' | 'desc'];\nexport type Ordering = readonly OrderPart[];\n\nexport type SimpleOperator = EqualityOps | OrderOps | LikeOps | InOps;\nexport type EqualityOps = '=' | '!=' | 'IS' | 'IS NOT';\nexport type OrderOps = '<' | '>' | '<=' | '>=';\nexport type LikeOps = 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE';\nexport type InOps = 'IN' | 'NOT IN';\n\nexport type AST = {\n readonly schema?: string | undefined;\n readonly table: string;\n\n // A query would be aliased if the AST is a subquery.\n // e.g., when two subqueries select from the same table\n // they need an alias to differentiate them.\n // `SELECT\n // [SELECT * FROM issue WHERE issue.id = outer.parentId] AS parent\n // [SELECT * FROM issue WHERE issue.parentId = outer.id] AS children\n // FROM issue as outer`\n readonly alias?: string | undefined;\n\n // `select` is missing given we return all columns for now.\n\n // The PipelineBuilder will pick what to use to correlate\n // a subquery with a parent query. It can choose something from the\n // where conditions or choose the _first_ `related` entry.\n // Choosing the first `related` entry is almost always the best choice if\n // one exists.\n readonly where?: Condition | undefined;\n\n readonly related?: readonly CorrelatedSubquery[] | undefined;\n readonly start?: Bound | undefined;\n readonly limit?: number | undefined;\n readonly orderBy?: Ordering | undefined;\n};\n\nexport type Correlation = {\n readonly parentField: CompoundKey;\n readonly childField: CompoundKey;\n};\n\nexport type CorrelatedSubquery = {\n /**\n * Only equality correlation are supported for now.\n * E.g., direct foreign key relationships.\n */\n readonly correlation: Correlation;\n readonly subquery: AST;\n readonly system?: System | undefined;\n // If a hop in the subquery chain should be hidden from the output view.\n // A common example is junction edges. The query API provides the illusion\n // that they don't exist: `issue.related('labels')` instead of `issue.related('issue_labels').related('labels')`.\n // To maintain this illusion, the junction edge should be hidden.\n // When `hidden` is set to true, this hop will not be included in the output view\n // but its children will be.\n readonly hidden?: boolean | undefined;\n};\n\nexport type ValuePosition = LiteralReference | Parameter | ColumnReference;\n\nexport type ColumnReference = {\n readonly type: 'column';\n /**\n * Not a path yet as we're currently not allowing\n * comparisons across tables. This will need to\n * be a path through the tree in the near future.\n */\n readonly name: string;\n};\n\nexport type LiteralReference = {\n readonly type: 'literal';\n readonly value: LiteralValue;\n};\n\nexport type LiteralValue =\n | string\n | number\n | boolean\n | null\n | ReadonlyArray<string | number | boolean>;\n\n/**\n * Starting only with SimpleCondition for now.\n * ivm1 supports Conjunctions and Disjunctions.\n * We'll support them in the future.\n */\nexport type Condition =\n | SimpleCondition\n | Conjunction\n | Disjunction\n | CorrelatedSubqueryCondition;\n\nexport type SimpleCondition = {\n readonly type: 'simple';\n readonly op: SimpleOperator;\n readonly left: ValuePosition;\n\n /**\n * `null` is absent since we do not have an `IS` or `IS NOT`\n * operator defined and `null != null` in SQL.\n */\n readonly right: Exclude<ValuePosition, ColumnReference>;\n};\n\nexport type Conjunction = {\n type: 'and';\n conditions: readonly Condition[];\n};\n\nexport type Disjunction = {\n type: 'or';\n conditions: readonly Condition[];\n};\n\nexport type CorrelatedSubqueryCondition = {\n type: 'correlatedSubquery';\n related: CorrelatedSubquery;\n op: CorrelatedSubqueryConditionOperator;\n flip?: boolean | undefined;\n scalar?: boolean | undefined;\n [planIdSymbol]?: number | undefined;\n};\n\nexport type CorrelatedSubqueryConditionOperator = 'EXISTS' | 'NOT EXISTS';\n\ninterface ASTTransform {\n tableName(orig: string): string;\n columnName(origTable: string, origColumn: string): string;\n}\n\nfunction transformAST(ast: AST, transform: ASTTransform): Required<AST> {\n // Name mapping functions (e.g. to server names)\n const {tableName, columnName} = transform;\n const colName = (c: string) => columnName(ast.table, c);\n const key = (table: string, k: CompoundKey) => {\n const serverKey = k.map(col => columnName(table, col));\n return mustCompoundKey(serverKey);\n };\n\n const transformed = {\n schema: ast.schema,\n table: tableName(ast.table),\n alias: ast.alias,\n where: ast.where\n ? transformWhere(ast.where, ast.table, transform)\n : undefined,\n related: ast.related?.map(\n r =>\n ({\n correlation: {\n parentField: key(ast.table, r.correlation.parentField),\n childField: key(r.subquery.table, r.correlation.childField),\n },\n hidden: r.hidden,\n subquery: transformAST(r.subquery, transform),\n system: r.system,\n }) satisfies Required<CorrelatedSubquery>,\n ),\n start: ast.start\n ? {\n ...ast.start,\n row: Object.fromEntries(\n Object.entries(ast.start.row).map(([col, val]) => [\n colName(col),\n val,\n ]),\n ),\n }\n : undefined,\n limit: ast.limit,\n orderBy: ast.orderBy?.map(([col, dir]) => [colName(col), dir] as const),\n };\n\n return transformed;\n}\n\nfunction transformWhere(\n where: Condition,\n table: string,\n transform: ASTTransform,\n): Condition {\n // Name mapping functions (e.g. to server names)\n const {columnName} = transform;\n const condValue = (c: ConditionValue) =>\n c.type !== 'column' ? c : {...c, name: columnName(table, c.name)};\n const key = (table: string, k: CompoundKey) => {\n const serverKey = k.map(col => columnName(table, col));\n return mustCompoundKey(serverKey);\n };\n\n if (where.type === 'simple') {\n return {...where, left: condValue(where.left)};\n } else if (where.type === 'correlatedSubquery') {\n const {correlation, subquery} = where.related;\n return {\n ...where,\n related: {\n ...where.related,\n correlation: {\n parentField: key(table, correlation.parentField),\n childField: key(subquery.table, correlation.childField),\n },\n subquery: transformAST(subquery, transform),\n },\n };\n }\n\n return {\n type: where.type,\n conditions: where.conditions.map(c => transformWhere(c, table, transform)),\n };\n}\n\ndeclare const normalizedTag: unique symbol;\n\n/**\n * An {@link AST} in its normalized form: `related` is sorted, `where` is\n * flattened and sorted, and every field is present, in a fixed order. Two ASTs\n * that describe the same query have the same JSON encoding, and thus the same\n * hash, once normalized.\n *\n * The tag only exists in the type, so the only way to get one is from\n * {@link normalizeAST}, {@link normalizedAST} or {@link tableAST}. Spreading\n * one keeps the tag, which is what lets a query builder derive a normalized\n * AST from another one by replacing a field it knows to be safe.\n */\nexport type NormalizedAST = Required<AST> & {readonly [normalizedTag]: true};\n\nfunction asNormalized(ast: Required<AST>): NormalizedAST {\n return ast as NormalizedAST;\n}\n\nconst normalizeCache = new WeakMap<AST, NormalizedAST>();\n\nexport function normalizeAST(ast: AST): NormalizedAST {\n return getOrInsertComputed(normalizeCache, ast, normalizeASTUncached);\n}\n\nfunction normalizeASTUncached(ast: AST): NormalizedAST {\n // normalizedAST() normalizes a single level, so normalize the subqueries\n // first.\n const {where, related} = ast;\n return normalizedAST({\n ...ast,\n where: where && normalizeSubqueries(where),\n related: related?.map(r =>\n normalizedRelated({...r, subquery: normalizeAST(r.subquery)}),\n ),\n });\n}\n\n/**\n * Normalizes the ASTs of the correlated subqueries of `cond`. The condition\n * itself is normalized by {@link normalizeCondition}.\n */\nfunction normalizeSubqueries(cond: Condition): Condition {\n switch (cond.type) {\n case 'simple':\n return cond;\n case 'correlatedSubquery':\n return {\n ...cond,\n related: {\n ...cond.related,\n subquery: normalizeAST(cond.related.subquery),\n },\n };\n default:\n return {\n type: cond.type,\n conditions: cond.conditions.map(normalizeSubqueries),\n };\n }\n}\n\n/**\n * Normalizes a single level of an AST, assuming that the ASTs of its\n * subqueries (i.e. those in `related` and in correlated subquery conditions)\n * are already normalized and that the `related` entries have the shape that\n * {@link normalizedRelated} gives them.\n *\n * This lets a query builder keep its AST normalized as it builds it up\n * instead of normalizing the whole tree afterwards. A builder that knows\n * which of the rules a step can break at all is better off applying just\n * those (see {@link tableAST}, {@link insertRelated} and\n * {@link normalizeCondition}).\n */\nexport function normalizedAST(ast: AST): NormalizedAST {\n const {schema, table, alias, where, related, start, limit, orderBy} = ast;\n // Every field is written, in the same order, so that all normalized ASTs\n // have the same shape (one hidden class) and the same JSON encoding (and\n // thus the same hash) no matter how they were put together. The fields that\n // are undefined are dropped by JSON.stringify() anyway.\n return asNormalized({\n schema,\n table,\n alias,\n where: where === undefined ? undefined : normalizeCondition(where),\n related: related === undefined ? undefined : normalizeRelated(related),\n start,\n limit,\n orderBy,\n });\n}\n\n/**\n * The normalized AST that selects a whole table. Spreading it (or any other\n * normalized AST) and replacing a field keeps the field order, since a\n * normalized AST has every field.\n */\nexport function tableAST(\n table: string,\n alias?: string | undefined,\n): NormalizedAST {\n return asNormalized({\n schema: undefined,\n table,\n alias,\n where: undefined,\n related: undefined,\n start: undefined,\n limit: undefined,\n orderBy: undefined,\n });\n}\n\n/**\n * Adds `subquery` to the `related` of a normalized AST, at the position that\n * keeps it sorted.\n */\nexport function insertRelated(\n related: readonly CorrelatedSubquery[] | undefined,\n subquery: CorrelatedSubquery,\n): readonly CorrelatedSubquery[] {\n const normalized = normalizedRelated(subquery);\n if (related === undefined) {\n return [normalized];\n }\n // Sorting is stable, so an entry goes after the ones it compares equal to.\n let i = related.length;\n while (i > 0 && cmpRelated(related[i - 1], normalized) > 0) {\n i--;\n }\n return related.toSpliced(i, 0, normalized);\n}\n\n// Conjunctions and disjunctions that are known to be normalized. The query\n// builder rebuilds its AST for every step, so this keeps the conditions that\n// did not change from being normalized over and over again.\nconst normalizedConditions = new WeakSet<Condition>();\n\nfunction normalizeRelated(\n related: readonly CorrelatedSubquery[],\n): readonly CorrelatedSubquery[] {\n for (let i = 1; i < related.length; i++) {\n if (cmpRelated(related[i - 1], related[i]) > 0) {\n return related.toSorted(cmpRelated);\n }\n }\n return related;\n}\n\n/**\n * Gives a correlated subquery the shape that a normalized AST has: every\n * field, in a fixed order (see {@link normalizedAST}). Its `subquery` must\n * already be normalized.\n */\nexport function normalizedRelated(\n related: CorrelatedSubquery,\n): Required<CorrelatedSubquery> {\n const {correlation, hidden, subquery, system} = related;\n return {\n correlation: {\n parentField: correlation.parentField,\n childField: correlation.childField,\n },\n hidden,\n subquery,\n system,\n };\n}\n\nfunction normalizeValuePosition<T extends ValuePosition>(v: T): T {\n switch (v.type) {\n case 'literal':\n return {type: 'literal', value: v.value} as T;\n case 'column':\n return {type: 'column', name: v.name} as T;\n default:\n return {type: 'static', anchor: v.anchor, field: v.field} as T;\n }\n}\n\n/**\n * Normalizes a condition, assuming that the ASTs of any correlated subqueries\n * it contains are already normalized.\n *\n * Like {@link normalizedAST}, this rebuilds every node with its fields in a\n * fixed order, so that structurally equal conditions have the same JSON\n * encoding (and thus the same hash) no matter how they were put together.\n * That includes a round trip through Postgres, whose jsonb type reorders\n * object keys: without the rebuild, every query record reloaded from the CVR\n * re-hashed to a different transformationHash than was stored, making it look\n * changed and re-execute on every view-syncer restart.\n */\nexport function normalizeCondition(cond: Condition): Condition {\n if (normalizedConditions.has(cond)) {\n return cond;\n }\n if (cond.type === 'simple') {\n const normalized: Condition = {\n type: 'simple',\n op: cond.op,\n left: normalizeValuePosition(cond.left),\n right: normalizeValuePosition(cond.right),\n };\n normalizedConditions.add(normalized);\n return normalized;\n }\n if (cond.type === 'correlatedSubquery') {\n const normalized: Condition = {\n type: 'correlatedSubquery',\n related: normalizedRelated(cond.related),\n op: cond.op,\n flip: cond.flip,\n scalar: cond.scalar,\n };\n normalizedConditions.add(normalized);\n return normalized;\n }\n\n // Flatten the conditions of nested conjunctions of the same type and sort\n // them. The nested conditions are normalized (and thus flattened and sorted)\n // first.\n const conditions: Condition[] = [];\n for (const c of cond.conditions) {\n const n = normalizeCondition(c);\n if (n.type === cond.type) {\n conditions.push(...n.conditions);\n } else {\n conditions.push(n);\n }\n }\n\n // A singleton conjunction is the same as the condition itself. Empty\n // conjunctions, on the other hand, are meaningful: an empty 'and' is always\n // true and an empty 'or' is always false.\n if (conditions.length === 1) {\n return conditions[0];\n }\n conditions.sort(cmpCondition);\n const normalized: Condition = {type: cond.type, conditions};\n normalizedConditions.add(normalized);\n return normalized;\n}\n\n/**\n * Maps the table and column names of `ast`. The result is not a\n * {@link NormalizedAST} even if `ast` was normalized: the names it sorts by\n * are the ones that changed.\n */\nexport function mapAST(ast: AST, mapper: NameMapper) {\n return transformAST(ast, {\n tableName: table => mapper.tableName(table),\n columnName: (table, col) => mapper.columnName(table, col),\n });\n}\n\nexport function mapCondition(\n cond: Condition,\n table: string,\n mapper: NameMapper,\n) {\n return transformWhere(cond, table, {\n tableName: table => mapper.tableName(table),\n columnName: (table, col) => mapper.columnName(table, col),\n });\n}\n\nfunction cmpCondition(a: Condition, b: Condition): number {\n if (a.type === 'simple') {\n if (b.type !== 'simple') {\n return -1; // Order SimpleConditions first\n }\n\n return (\n compareValuePosition(a.left, b.left) ||\n compareUTF8MaybeNull(a.op, b.op) ||\n compareValuePosition(a.right, b.right)\n );\n }\n\n if (b.type === 'simple') {\n return 1; // Order SimpleConditions first\n }\n\n if (a.type === 'correlatedSubquery') {\n if (b.type !== 'correlatedSubquery') {\n return -1; // Order subquery before conjuctions/disjuctions\n }\n return (\n cmpRelated(a.related, b.related) ||\n compareUTF8MaybeNull(a.op, b.op) ||\n cmpOptionalBool(a.flip, b.flip) ||\n cmpOptionalBool(a.scalar, b.scalar)\n );\n }\n if (b.type === 'correlatedSubquery') {\n return -1; // Order correlatedSubquery before conjuctions/disjuctions\n }\n\n const val = compareUTF8MaybeNull(a.type, b.type);\n if (val !== 0) {\n return val;\n }\n for (\n let l = 0, r = 0;\n l < a.conditions.length && r < b.conditions.length;\n l++, r++\n ) {\n const val = cmpCondition(a.conditions[l], b.conditions[r]);\n if (val !== 0) {\n return val;\n }\n }\n // prefixes first\n return a.conditions.length - b.conditions.length;\n}\n\nfunction compareValuePosition(a: ValuePosition, b: ValuePosition): number {\n if (a.type !== b.type) {\n return compareUTF8(a.type, b.type);\n }\n switch (a.type) {\n case 'literal':\n assert(b.type === 'literal', 'Expected literal type for comparison');\n return cmpLiteralValue(a.value, b.value);\n case 'column':\n assert(b.type === 'column', 'Expected column type for comparison');\n return compareUTF8(a.name, b.name);\n case 'static':\n assert(b.type === 'static', 'Expected static type for comparison');\n // A field is a string or a path of strings, which cmpLiteralValue()\n // tells apart.\n return (\n compareUTF8(a.anchor, b.anchor) || cmpLiteralValue(a.field, b.field)\n );\n }\n}\n\n// null < boolean < number < string < array. Ordering values of different\n// types by their string form would make distinct values compare equal (['a',\n// 'b'] and 'a,b', or 1 and '1'), which would leave their order up to the\n// order they were written in, and with it the hash of the query.\nconst LITERAL_VALUE_TYPES = ['object', 'boolean', 'number', 'string'];\n\nfunction literalValueType(value: LiteralValue): number {\n return Array.isArray(value)\n ? LITERAL_VALUE_TYPES.length\n : LITERAL_VALUE_TYPES.indexOf(typeof value);\n}\n\nfunction cmpLiteralValue(a: LiteralValue, b: LiteralValue): number {\n const type = literalValueType(a) - literalValueType(b);\n if (type !== 0) {\n return type;\n }\n\n if (Array.isArray(a)) {\n assert(Array.isArray(b), 'Expected array for comparison');\n for (let i = 0; i < a.length && i < b.length; i++) {\n const val = cmpLiteralValue(a[i], b[i]);\n if (val !== 0) {\n return val;\n }\n }\n // prefixes first\n return a.length - b.length;\n }\n\n switch (typeof a) {\n case 'string':\n assert(typeof b === 'string', 'Expected string for comparison');\n return compareUTF8(a, b);\n case 'number':\n assert(typeof b === 'number', 'Expected number for comparison');\n return a < b ? -1 : a > b ? 1 : 0;\n case 'boolean':\n assert(typeof b === 'boolean', 'Expected boolean for comparison');\n return Number(a) - Number(b);\n default:\n return 0; // null\n }\n}\n\nfunction cmpRelated(a: CorrelatedSubquery, b: CorrelatedSubquery): number {\n return compareUTF8(must(a.subquery.alias), must(b.subquery.alias));\n}\n\nfunction compareUTF8MaybeNull(a: string | null, b: string | null): number {\n if (a !== null && b !== null) {\n return compareUTF8(a, b);\n }\n if (b !== null) {\n return -1;\n }\n if (a !== null) {\n return 1;\n }\n return 0;\n}\n\nfunction cmpOptionalBool(\n a: boolean | undefined,\n b: boolean | undefined,\n): number {\n // undefined < false < true\n const toNum = (v: boolean | undefined) => (v === undefined ? 0 : v ? 2 : 1);\n return toNum(a) - toNum(b);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ast.js","names":["v","v.readonly","v.literalUnion","v.readonlyArray","v.readonlyObject","v.strict"],"sources":["../../../../zero-protocol/src/ast.ts"],"sourcesContent":["/**\n * Wire-format representation of the zql AST interface.\n *\n * `v.Type<...>` types are explicitly declared to facilitate Typescript verification\n * that the schemas satisfy the zql type definitions. (Incidentally, explicit types\n * are also required for recursive schema definitions.)\n */\n\nimport {compareUTF8} from 'compare-utf8';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {isPlainObject} from '../../shared/src/deep-merge.ts';\nimport {toSorted} from '../../shared/src/iterables.ts';\nimport {getOrInsertComputed} from '../../shared/src/map.ts';\nimport {must} from '../../shared/src/must.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {NameMapper} from '../../zero-types/src/name-mapper.ts';\nimport {rowSchema, type Row} from './data.ts';\n\nexport const SUBQ_PREFIX = 'zsubq_';\n\nexport const selectorSchema = v.string();\nexport const toStaticParam = Symbol();\nexport const planIdSymbol = Symbol('planId');\n\nconst orderingElementSchema = v.readonly(\n v.tuple([selectorSchema, v.literalUnion('asc', 'desc')]),\n);\n\nexport const orderingSchema = v.readonlyArray(orderingElementSchema);\nexport type System = 'permissions' | 'client' | 'test';\n\nexport const primitiveSchema = v.union(\n v.string(),\n v.number(),\n v.boolean(),\n v.null(),\n);\n\nexport const equalityOpsSchema = v.literalUnion('=', '!=', 'IS', 'IS NOT');\n\nexport const orderOpsSchema = v.literalUnion('<', '>', '<=', '>=');\n\nexport const likeOpsSchema = v.literalUnion(\n 'LIKE',\n 'NOT LIKE',\n 'ILIKE',\n 'NOT ILIKE',\n);\n\nexport const inOpsSchema = v.literalUnion('IN', 'NOT IN');\n\nexport const simpleOperatorSchema = v.union(\n equalityOpsSchema,\n orderOpsSchema,\n likeOpsSchema,\n inOpsSchema,\n);\n\nconst literalReferenceSchema: v.Type<LiteralReference> = v.readonlyObject({\n type: v.literal('literal'),\n value: v.union(\n v.string(),\n v.number(),\n v.boolean(),\n v.null(),\n v.readonlyArray(v.union(v.string(), v.number(), v.boolean())),\n ),\n});\nconst columnReferenceSchema: v.Type<ColumnReference> = v.readonlyObject({\n type: v.literal('column'),\n name: v.string(),\n});\n\n/**\n * A parameter is a value that is not known at the time the query is written\n * and is resolved at runtime.\n *\n * Static parameters refer to something provided by the caller.\n * Static parameters are injected when the query pipeline is built from the AST\n * and do not change for the life of that pipeline.\n *\n * An example static parameter is the current authentication data.\n * When a user is authenticated, queries on the server have access\n * to the user's authentication data in order to evaluate authorization rules.\n * Authentication data doesn't change over the life of a query as a change\n * in auth data would represent a log-in / log-out of the user.\n *\n * AncestorParameters refer to rows encountered while running the query.\n * They are used by subqueries to refer to rows emitted by parent queries.\n */\nconst parameterReferenceSchema = v.readonlyObject({\n type: v.literal('static'),\n // The \"namespace\" of the injected parameter.\n // Write authorization will send the value of a row\n // prior to the mutation being run (preMutationRow).\n // Read and write authorization will both send the\n // current authentication data (authData).\n anchor: v.literalUnion('authData', 'preMutationRow'),\n field: v.union(v.string(), v.array(v.string())),\n});\n\nconst conditionValueSchema = v.union(\n literalReferenceSchema,\n columnReferenceSchema,\n parameterReferenceSchema,\n);\n\nexport type Parameter = v.Infer<typeof parameterReferenceSchema>;\n\nexport const simpleConditionSchema: v.Type<SimpleCondition> = v.readonlyObject({\n type: v.literal('simple'),\n op: simpleOperatorSchema,\n left: conditionValueSchema,\n right: v.union(parameterReferenceSchema, literalReferenceSchema),\n});\n\ntype ConditionValue = v.Infer<typeof conditionValueSchema>;\n\nexport const correlatedSubqueryConditionOperatorSchema: v.Type<CorrelatedSubqueryConditionOperator> =\n v.literalUnion('EXISTS', 'NOT EXISTS');\n\nexport const correlatedSubqueryConditionSchema: v.Type<CorrelatedSubqueryCondition> =\n v.readonlyObject({\n type: v.literal('correlatedSubquery'),\n related: v.lazy(() => correlatedSubquerySchema),\n op: correlatedSubqueryConditionOperatorSchema,\n flip: v.boolean().optional(),\n scalar: v.boolean().optional(),\n });\n\nexport const conditionSchema: v.Type<Condition> = v.union(\n simpleConditionSchema,\n v.lazy(() => conjunctionSchema),\n v.lazy(() => disjunctionSchema),\n correlatedSubqueryConditionSchema,\n);\n\nconst conjunctionSchema: v.Type<Conjunction> = v.readonlyObject({\n type: v.literal('and'),\n conditions: v.readonlyArray(conditionSchema),\n});\n\nconst disjunctionSchema: v.Type<Disjunction> = v.readonlyObject({\n type: v.literal('or'),\n conditions: v.readonlyArray(conditionSchema),\n});\n\nexport type CompoundKey = readonly [string, ...string[]];\n\nfunction mustCompoundKey(field: readonly string[]): CompoundKey {\n assert(\n Array.isArray(field) && field.length >= 1,\n 'Expected non-empty array for compound key',\n );\n return field as unknown as CompoundKey;\n}\n\nexport const compoundKeySchema: v.Type<CompoundKey> = v.readonly(\n // oxlint-disable-next-line e18e/prefer-spread-syntax\n v.tuple([v.string()]).concat(v.array(v.string())),\n);\n\nconst correlationSchema = v.readonlyObject({\n parentField: compoundKeySchema,\n childField: compoundKeySchema,\n});\n\n// Split out so that its inferred type can be checked against\n// Omit<CorrelatedSubquery, 'correlation'> in ast-type-test.ts.\n// The mutually-recursive reference of the 'other' field to astSchema\n// is the only thing added in v.lazy. The v.lazy is necessary due to the\n// mutually-recursive types, but v.lazy prevents inference of the resulting\n// type.\nexport const correlatedSubquerySchemaOmitSubquery = v.readonlyObject({\n correlation: correlationSchema,\n hidden: v.boolean().optional(),\n system: v.literalUnion('permissions', 'client', 'test').optional(),\n});\n\nexport const correlatedSubquerySchema: v.Type<CorrelatedSubquery> =\n correlatedSubquerySchemaOmitSubquery.extend({\n subquery: v.lazy(() => astSchema),\n });\n\nexport const astSchema: v.Type<AST> = v.readonlyObject({\n schema: v.string().optional(),\n table: v.string(),\n alias: v.string().optional(),\n where: conditionSchema.optional(),\n related: v.readonlyArray(correlatedSubquerySchema).optional(),\n limit: v.number().optional(),\n orderBy: orderingSchema.optional(),\n start: v\n .object({\n row: rowSchema,\n exclusive: v.boolean(),\n })\n .optional(),\n});\n\n/**\n * The maximum nesting depth of an AST sent by a client, counting and/or\n * conditions, correlated subqueries and related subqueries. Parsing and the\n * AST visitors in zero-cache are recursive, so a single deeply nested AST can\n * otherwise overflow the stack or burn seconds of CPU. Realistic queries,\n * permission rules included, are less than 10 levels deep.\n */\nexport const MAX_AST_DEPTH = 50;\n\n/**\n * Returns whether the unparsed `ast` is nested deeper than `maxDepth`.\n */\nexport function exceedsASTDepth(\n ast: unknown,\n maxDepth = MAX_AST_DEPTH,\n): boolean {\n return astExceedsDepth(ast, 0, maxDepth);\n}\n\n// Every step down increases the depth, except the one from an AST into its\n// where clause, so the recursion stops within 2 * maxDepth steps and cannot\n// overflow the stack.\nfunction astExceedsDepth(\n ast: unknown,\n depth: number,\n maxDepth: number,\n): boolean {\n if (depth > maxDepth) {\n return true;\n }\n if (!isPlainObject(ast)) {\n return false;\n }\n const {where, related} = ast;\n return (\n conditionExceedsDepth(where, depth, maxDepth) ||\n (Array.isArray(related) &&\n related.some(\n r =>\n isPlainObject(r) && astExceedsDepth(r.subquery, depth + 1, maxDepth),\n ))\n );\n}\n\nfunction conditionExceedsDepth(\n cond: unknown,\n depth: number,\n maxDepth: number,\n): boolean {\n if (depth > maxDepth) {\n return true;\n }\n if (!isPlainObject(cond)) {\n return false;\n }\n const {conditions, related} = cond;\n return (\n (Array.isArray(conditions) &&\n conditions.some(c => conditionExceedsDepth(c, depth + 1, maxDepth))) ||\n (isPlainObject(related) &&\n astExceedsDepth(related.subquery, depth + 1, maxDepth))\n );\n}\n\n/**\n * {@link astSchema} for ASTs sent by clients, which rejects ASTs nested deeper\n * than {@link MAX_AST_DEPTH} before parsing them. ASTs the server stored itself\n * (e.g. in the CVR) are parsed with {@link astSchema} so that ones stored\n * before the limit existed can still be read.\n */\nexport const depthBoundedAstSchema: v.Type<AST> = Object.assign(\n v\n .unknown()\n .chain((value, options) =>\n exceedsASTDepth(value)\n ? v.err(`AST exceeds the maximum depth of ${MAX_AST_DEPTH}`)\n : astSchema.try(value, options),\n ),\n // A transform serializes as just its name. Show astSchema too, so a change\n // to it still changes the hash in protocol-version.test.ts.\n {depthBounded: astSchema},\n);\n\n/**\n * {@link depthBoundedAstSchema} parsed strictly even inside a lenient parse. A\n * server that ignored an AST field it doesn't know would run a different query.\n */\nexport const strictASTSchema: v.Type<AST> = v.strict(depthBoundedAstSchema);\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * As in SQL you can have multiple orderings. We don't currently\n * support ordering on anything other than the root query.\n */\nexport type OrderPart = readonly [field: string, direction: 'asc' | 'desc'];\nexport type Ordering = readonly OrderPart[];\n\nexport type SimpleOperator = EqualityOps | OrderOps | LikeOps | InOps;\nexport type EqualityOps = '=' | '!=' | 'IS' | 'IS NOT';\nexport type OrderOps = '<' | '>' | '<=' | '>=';\nexport type LikeOps = 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE';\nexport type InOps = 'IN' | 'NOT IN';\n\nexport type AST = {\n readonly schema?: string | undefined;\n readonly table: string;\n\n // A query would be aliased if the AST is a subquery.\n // e.g., when two subqueries select from the same table\n // they need an alias to differentiate them.\n // `SELECT\n // [SELECT * FROM issue WHERE issue.id = outer.parentId] AS parent\n // [SELECT * FROM issue WHERE issue.parentId = outer.id] AS children\n // FROM issue as outer`\n readonly alias?: string | undefined;\n\n // `select` is missing given we return all columns for now.\n\n // The PipelineBuilder will pick what to use to correlate\n // a subquery with a parent query. It can choose something from the\n // where conditions or choose the _first_ `related` entry.\n // Choosing the first `related` entry is almost always the best choice if\n // one exists.\n readonly where?: Condition | undefined;\n\n readonly related?: readonly CorrelatedSubquery[] | undefined;\n readonly start?: Bound | undefined;\n readonly limit?: number | undefined;\n readonly orderBy?: Ordering | undefined;\n};\n\nexport type Correlation = {\n readonly parentField: CompoundKey;\n readonly childField: CompoundKey;\n};\n\nexport type CorrelatedSubquery = {\n /**\n * Only equality correlation are supported for now.\n * E.g., direct foreign key relationships.\n */\n readonly correlation: Correlation;\n readonly subquery: AST;\n readonly system?: System | undefined;\n // If a hop in the subquery chain should be hidden from the output view.\n // A common example is junction edges. The query API provides the illusion\n // that they don't exist: `issue.related('labels')` instead of `issue.related('issue_labels').related('labels')`.\n // To maintain this illusion, the junction edge should be hidden.\n // When `hidden` is set to true, this hop will not be included in the output view\n // but its children will be.\n readonly hidden?: boolean | undefined;\n};\n\nexport type ValuePosition = LiteralReference | Parameter | ColumnReference;\n\nexport type ColumnReference = {\n readonly type: 'column';\n /**\n * Not a path yet as we're currently not allowing\n * comparisons across tables. This will need to\n * be a path through the tree in the near future.\n */\n readonly name: string;\n};\n\nexport type LiteralReference = {\n readonly type: 'literal';\n readonly value: LiteralValue;\n};\n\nexport type LiteralValue =\n | string\n | number\n | boolean\n | null\n | ReadonlyArray<string | number | boolean>;\n\n/**\n * Starting only with SimpleCondition for now.\n * ivm1 supports Conjunctions and Disjunctions.\n * We'll support them in the future.\n */\nexport type Condition =\n | SimpleCondition\n | Conjunction\n | Disjunction\n | CorrelatedSubqueryCondition;\n\nexport type SimpleCondition = {\n readonly type: 'simple';\n readonly op: SimpleOperator;\n readonly left: ValuePosition;\n\n /**\n * `null` is absent since we do not have an `IS` or `IS NOT`\n * operator defined and `null != null` in SQL.\n */\n readonly right: Exclude<ValuePosition, ColumnReference>;\n};\n\nexport type Conjunction = {\n type: 'and';\n conditions: readonly Condition[];\n};\n\nexport type Disjunction = {\n type: 'or';\n conditions: readonly Condition[];\n};\n\nexport type CorrelatedSubqueryCondition = {\n type: 'correlatedSubquery';\n related: CorrelatedSubquery;\n op: CorrelatedSubqueryConditionOperator;\n flip?: boolean | undefined;\n scalar?: boolean | undefined;\n [planIdSymbol]?: number | undefined;\n};\n\nexport type CorrelatedSubqueryConditionOperator = 'EXISTS' | 'NOT EXISTS';\n\ninterface ASTTransform {\n tableName(orig: string): string;\n columnName(origTable: string, origColumn: string): string;\n}\n\nfunction transformAST(ast: AST, transform: ASTTransform): Required<AST> {\n // Name mapping functions (e.g. to server names)\n const {tableName, columnName} = transform;\n const colName = (c: string) => columnName(ast.table, c);\n const key = (table: string, k: CompoundKey) => {\n const serverKey = k.map(col => columnName(table, col));\n return mustCompoundKey(serverKey);\n };\n\n const transformed = {\n schema: ast.schema,\n table: tableName(ast.table),\n alias: ast.alias,\n where: ast.where\n ? transformWhere(ast.where, ast.table, transform)\n : undefined,\n related: ast.related?.map(\n r =>\n ({\n correlation: {\n parentField: key(ast.table, r.correlation.parentField),\n childField: key(r.subquery.table, r.correlation.childField),\n },\n hidden: r.hidden,\n subquery: transformAST(r.subquery, transform),\n system: r.system,\n }) satisfies Required<CorrelatedSubquery>,\n ),\n start: ast.start\n ? {\n ...ast.start,\n row: Object.fromEntries(\n Object.entries(ast.start.row).map(([col, val]) => [\n colName(col),\n val,\n ]),\n ),\n }\n : undefined,\n limit: ast.limit,\n orderBy: ast.orderBy?.map(([col, dir]) => [colName(col), dir] as const),\n };\n\n return transformed;\n}\n\nfunction transformWhere(\n where: Condition,\n table: string,\n transform: ASTTransform,\n): Condition {\n // Name mapping functions (e.g. to server names)\n const {columnName} = transform;\n const condValue = (c: ConditionValue) =>\n c.type !== 'column' ? c : {...c, name: columnName(table, c.name)};\n const key = (table: string, k: CompoundKey) => {\n const serverKey = k.map(col => columnName(table, col));\n return mustCompoundKey(serverKey);\n };\n\n if (where.type === 'simple') {\n return {...where, left: condValue(where.left)};\n } else if (where.type === 'correlatedSubquery') {\n const {correlation, subquery} = where.related;\n return {\n ...where,\n related: {\n ...where.related,\n correlation: {\n parentField: key(table, correlation.parentField),\n childField: key(subquery.table, correlation.childField),\n },\n subquery: transformAST(subquery, transform),\n },\n };\n }\n\n return {\n type: where.type,\n conditions: where.conditions.map(c => transformWhere(c, table, transform)),\n };\n}\n\ndeclare const normalizedTag: unique symbol;\n\n/**\n * An {@link AST} in its normalized form: `related` is sorted, `where` is\n * flattened and sorted, and every field is present, in a fixed order. Two ASTs\n * that describe the same query have the same JSON encoding, and thus the same\n * hash, once normalized.\n *\n * The tag only exists in the type, so the only way to get one is from\n * {@link normalizeAST}, {@link normalizedAST} or {@link tableAST}. Spreading\n * one keeps the tag, which is what lets a query builder derive a normalized\n * AST from another one by replacing a field it knows to be safe.\n */\nexport type NormalizedAST = Required<AST> & {readonly [normalizedTag]: true};\n\nfunction asNormalized(ast: Required<AST>): NormalizedAST {\n return ast as NormalizedAST;\n}\n\nconst normalizeCache = new WeakMap<AST, NormalizedAST>();\n\nexport function normalizeAST(ast: AST): NormalizedAST {\n return getOrInsertComputed(normalizeCache, ast, normalizeASTUncached);\n}\n\nfunction normalizeASTUncached(ast: AST): NormalizedAST {\n // normalizedAST() normalizes a single level, so normalize the subqueries\n // first.\n const {where, related} = ast;\n return normalizedAST({\n ...ast,\n where: where && normalizeSubqueries(where),\n related: related?.map(r =>\n normalizedRelated({...r, subquery: normalizeAST(r.subquery)}),\n ),\n });\n}\n\n/**\n * Normalizes the ASTs of the correlated subqueries of `cond`. The condition\n * itself is normalized by {@link normalizeCondition}.\n */\nfunction normalizeSubqueries(cond: Condition): Condition {\n switch (cond.type) {\n case 'simple':\n return cond;\n case 'correlatedSubquery':\n return {\n ...cond,\n related: {\n ...cond.related,\n subquery: normalizeAST(cond.related.subquery),\n },\n };\n default:\n return {\n type: cond.type,\n conditions: cond.conditions.map(normalizeSubqueries),\n };\n }\n}\n\n/**\n * Normalizes a single level of an AST, assuming that the ASTs of its\n * subqueries (i.e. those in `related` and in correlated subquery conditions)\n * are already normalized and that the `related` entries have the shape that\n * {@link normalizedRelated} gives them.\n *\n * This lets a query builder keep its AST normalized as it builds it up\n * instead of normalizing the whole tree afterwards. A builder that knows\n * which of the rules a step can break at all is better off applying just\n * those (see {@link tableAST}, {@link insertRelated} and\n * {@link normalizeCondition}).\n */\nexport function normalizedAST(ast: AST): NormalizedAST {\n const {schema, table, alias, where, related, start, limit, orderBy} = ast;\n // Every field is written, in the same order, so that all normalized ASTs\n // have the same shape (one hidden class) and the same JSON encoding (and\n // thus the same hash) no matter how they were put together. The fields that\n // are undefined are dropped by JSON.stringify() anyway.\n return asNormalized({\n schema,\n table,\n alias,\n where: where === undefined ? undefined : normalizeCondition(where),\n related: related === undefined ? undefined : normalizeRelated(related),\n start,\n limit,\n orderBy,\n });\n}\n\n/**\n * The normalized AST that selects a whole table. Spreading it (or any other\n * normalized AST) and replacing a field keeps the field order, since a\n * normalized AST has every field.\n */\nexport function tableAST(\n table: string,\n alias?: string | undefined,\n): NormalizedAST {\n return asNormalized({\n schema: undefined,\n table,\n alias,\n where: undefined,\n related: undefined,\n start: undefined,\n limit: undefined,\n orderBy: undefined,\n });\n}\n\n/**\n * Adds `subquery` to the `related` of a normalized AST, at the position that\n * keeps it sorted.\n */\nexport function insertRelated(\n related: readonly CorrelatedSubquery[] | undefined,\n subquery: CorrelatedSubquery,\n): readonly CorrelatedSubquery[] {\n const normalized = normalizedRelated(subquery);\n if (related === undefined) {\n return [normalized];\n }\n // Sorting is stable, so an entry goes after the ones it compares equal to.\n let i = related.length;\n while (i > 0 && cmpRelated(related[i - 1], normalized) > 0) {\n i--;\n }\n return related.toSpliced(i, 0, normalized);\n}\n\n// Conjunctions and disjunctions that are known to be normalized. The query\n// builder rebuilds its AST for every step, so this keeps the conditions that\n// did not change from being normalized over and over again.\nconst normalizedConditions = new WeakSet<Condition>();\n\nfunction normalizeRelated(\n related: readonly CorrelatedSubquery[],\n): readonly CorrelatedSubquery[] {\n for (let i = 1; i < related.length; i++) {\n if (cmpRelated(related[i - 1], related[i]) > 0) {\n // Not Array.prototype.toSorted: Hermes (React Native) does not\n // implement it.\n return toSorted(related, cmpRelated);\n }\n }\n return related;\n}\n\n/**\n * Gives a correlated subquery the shape that a normalized AST has: every\n * field, in a fixed order (see {@link normalizedAST}). Its `subquery` must\n * already be normalized.\n */\nexport function normalizedRelated(\n related: CorrelatedSubquery,\n): Required<CorrelatedSubquery> {\n const {correlation, hidden, subquery, system} = related;\n return {\n correlation: {\n parentField: correlation.parentField,\n childField: correlation.childField,\n },\n hidden,\n subquery,\n system,\n };\n}\n\nfunction normalizeValuePosition<T extends ValuePosition>(v: T): T {\n switch (v.type) {\n case 'literal':\n return {type: 'literal', value: v.value} as T;\n case 'column':\n return {type: 'column', name: v.name} as T;\n default:\n return {type: 'static', anchor: v.anchor, field: v.field} as T;\n }\n}\n\n/**\n * Normalizes a condition, assuming that the ASTs of any correlated subqueries\n * it contains are already normalized.\n *\n * Like {@link normalizedAST}, this rebuilds every node with its fields in a\n * fixed order, so that structurally equal conditions have the same JSON\n * encoding (and thus the same hash) no matter how they were put together.\n * That includes a round trip through Postgres, whose jsonb type reorders\n * object keys: without the rebuild, every query record reloaded from the CVR\n * re-hashed to a different transformationHash than was stored, making it look\n * changed and re-execute on every view-syncer restart.\n */\nexport function normalizeCondition(cond: Condition): Condition {\n if (normalizedConditions.has(cond)) {\n return cond;\n }\n if (cond.type === 'simple') {\n const normalized: Condition = {\n type: 'simple',\n op: cond.op,\n left: normalizeValuePosition(cond.left),\n right: normalizeValuePosition(cond.right),\n };\n normalizedConditions.add(normalized);\n return normalized;\n }\n if (cond.type === 'correlatedSubquery') {\n const normalized: Condition = {\n type: 'correlatedSubquery',\n related: normalizedRelated(cond.related),\n op: cond.op,\n flip: cond.flip,\n scalar: cond.scalar,\n };\n normalizedConditions.add(normalized);\n return normalized;\n }\n\n // Flatten the conditions of nested conjunctions of the same type and sort\n // them. The nested conditions are normalized (and thus flattened and sorted)\n // first.\n const conditions: Condition[] = [];\n for (const c of cond.conditions) {\n const n = normalizeCondition(c);\n if (n.type === cond.type) {\n conditions.push(...n.conditions);\n } else {\n conditions.push(n);\n }\n }\n\n // A singleton conjunction is the same as the condition itself. Empty\n // conjunctions, on the other hand, are meaningful: an empty 'and' is always\n // true and an empty 'or' is always false.\n if (conditions.length === 1) {\n return conditions[0];\n }\n conditions.sort(cmpCondition);\n const normalized: Condition = {type: cond.type, conditions};\n normalizedConditions.add(normalized);\n return normalized;\n}\n\n/**\n * Maps the table and column names of `ast`. The result is not a\n * {@link NormalizedAST} even if `ast` was normalized: the names it sorts by\n * are the ones that changed.\n */\nexport function mapAST(ast: AST, mapper: NameMapper) {\n return transformAST(ast, {\n tableName: table => mapper.tableName(table),\n columnName: (table, col) => mapper.columnName(table, col),\n });\n}\n\nexport function mapCondition(\n cond: Condition,\n table: string,\n mapper: NameMapper,\n) {\n return transformWhere(cond, table, {\n tableName: table => mapper.tableName(table),\n columnName: (table, col) => mapper.columnName(table, col),\n });\n}\n\nfunction cmpCondition(a: Condition, b: Condition): number {\n if (a.type === 'simple') {\n if (b.type !== 'simple') {\n return -1; // Order SimpleConditions first\n }\n\n return (\n compareValuePosition(a.left, b.left) ||\n compareUTF8MaybeNull(a.op, b.op) ||\n compareValuePosition(a.right, b.right)\n );\n }\n\n if (b.type === 'simple') {\n return 1; // Order SimpleConditions first\n }\n\n if (a.type === 'correlatedSubquery') {\n if (b.type !== 'correlatedSubquery') {\n return -1; // Order subquery before conjuctions/disjuctions\n }\n return (\n cmpRelated(a.related, b.related) ||\n compareUTF8MaybeNull(a.op, b.op) ||\n cmpOptionalBool(a.flip, b.flip) ||\n cmpOptionalBool(a.scalar, b.scalar)\n );\n }\n if (b.type === 'correlatedSubquery') {\n return -1; // Order correlatedSubquery before conjuctions/disjuctions\n }\n\n const val = compareUTF8MaybeNull(a.type, b.type);\n if (val !== 0) {\n return val;\n }\n for (\n let l = 0, r = 0;\n l < a.conditions.length && r < b.conditions.length;\n l++, r++\n ) {\n const val = cmpCondition(a.conditions[l], b.conditions[r]);\n if (val !== 0) {\n return val;\n }\n }\n // prefixes first\n return a.conditions.length - b.conditions.length;\n}\n\nfunction compareValuePosition(a: ValuePosition, b: ValuePosition): number {\n if (a.type !== b.type) {\n return compareUTF8(a.type, b.type);\n }\n switch (a.type) {\n case 'literal':\n assert(b.type === 'literal', 'Expected literal type for comparison');\n return cmpLiteralValue(a.value, b.value);\n case 'column':\n assert(b.type === 'column', 'Expected column type for comparison');\n return compareUTF8(a.name, b.name);\n case 'static':\n assert(b.type === 'static', 'Expected static type for comparison');\n // A field is a string or a path of strings, which cmpLiteralValue()\n // tells apart.\n return (\n compareUTF8(a.anchor, b.anchor) || cmpLiteralValue(a.field, b.field)\n );\n }\n}\n\n// null < boolean < number < string < array. Ordering values of different\n// types by their string form would make distinct values compare equal (['a',\n// 'b'] and 'a,b', or 1 and '1'), which would leave their order up to the\n// order they were written in, and with it the hash of the query.\nconst LITERAL_VALUE_TYPES = ['object', 'boolean', 'number', 'string'];\n\nfunction literalValueType(value: LiteralValue): number {\n return Array.isArray(value)\n ? LITERAL_VALUE_TYPES.length\n : LITERAL_VALUE_TYPES.indexOf(typeof value);\n}\n\nfunction cmpLiteralValue(a: LiteralValue, b: LiteralValue): number {\n const type = literalValueType(a) - literalValueType(b);\n if (type !== 0) {\n return type;\n }\n\n if (Array.isArray(a)) {\n assert(Array.isArray(b), 'Expected array for comparison');\n for (let i = 0; i < a.length && i < b.length; i++) {\n const val = cmpLiteralValue(a[i], b[i]);\n if (val !== 0) {\n return val;\n }\n }\n // prefixes first\n return a.length - b.length;\n }\n\n switch (typeof a) {\n case 'string':\n assert(typeof b === 'string', 'Expected string for comparison');\n return compareUTF8(a, b);\n case 'number':\n assert(typeof b === 'number', 'Expected number for comparison');\n return a < b ? -1 : a > b ? 1 : 0;\n case 'boolean':\n assert(typeof b === 'boolean', 'Expected boolean for comparison');\n return Number(a) - Number(b);\n default:\n return 0; // null\n }\n}\n\nfunction cmpRelated(a: CorrelatedSubquery, b: CorrelatedSubquery): number {\n return compareUTF8(must(a.subquery.alias), must(b.subquery.alias));\n}\n\nfunction compareUTF8MaybeNull(a: string | null, b: string | null): number {\n if (a !== null && b !== null) {\n return compareUTF8(a, b);\n }\n if (b !== null) {\n return -1;\n }\n if (a !== null) {\n return 1;\n }\n return 0;\n}\n\nfunction cmpOptionalBool(\n a: boolean | undefined,\n b: boolean | undefined,\n): number {\n // undefined < false < true\n const toNum = (v: boolean | undefined) => (v === undefined ? 0 : v ? 2 : 1);\n return toNum(a) - toNum(b);\n}\n"],"mappings":"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"}