@rocicorp/zero 1.10.0-canary.11 → 1.10.0-canary.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +1 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -0
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +12 -2
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -3
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +3 -2
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +24 -12
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +4 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -2
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +8 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +10 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +2 -5
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -6
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +2 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +15 -2
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +7 -4
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +17 -10
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-protocol/src/custom-queries.d.ts +1 -1
- package/out/zero-protocol/src/error.d.ts +4 -4
- package/out/zero-protocol/src/inspect-down.js +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +2 -2
- package/out/zero-protocol/src/push.d.ts +1 -1
- package/out/zero-protocol/src/query-server.d.ts +2 -2
- package/package.json +1 -1
|
@@ -97,11 +97,24 @@ type SocketFactoryOptions = {
|
|
|
97
97
|
*
|
|
98
98
|
* Exported for testing.
|
|
99
99
|
*/
|
|
100
|
-
export declare function inactivityTimeoutSocket(lc: LogContext, timeoutMs?: number): (options: SocketFactoryOptions) => Socket;
|
|
100
|
+
export declare function inactivityTimeoutSocket(lc: LogContext, timeoutMs?: number, keepAliveMs?: number): (options: SocketFactoryOptions) => Socket;
|
|
101
101
|
export declare function pgClient(lc: LogContext, connectionURI: string, applicationName: string, options?: postgres.Options<{
|
|
102
102
|
bigint: PostgresType<bigint>;
|
|
103
103
|
json: PostgresType<JSONValue>;
|
|
104
|
-
}
|
|
104
|
+
}> & {
|
|
105
|
+
/**
|
|
106
|
+
* How to detect a dead ("half-open") connection:
|
|
107
|
+
* - `'inactivity-timeout'` (default): reset the socket after
|
|
108
|
+
* {@link SOCKET_INACTIVITY_TIMEOUT_MS} of no read/write activity. Catches
|
|
109
|
+
* proxy-level half-opens, but false-positives on connections that are
|
|
110
|
+
* legitimately idle for long stretches.
|
|
111
|
+
* - `'keepalive'`: disable the inactivity watchdog and rely on OS TCP
|
|
112
|
+
* keepalive probes instead. Use for connections that are expected to sit
|
|
113
|
+
* idle under back-pressure (e.g. the backfill COPY stream), where the
|
|
114
|
+
* watchdog would reset a perfectly healthy connection.
|
|
115
|
+
*/
|
|
116
|
+
liveness?: 'inactivity-timeout' | 'keepalive';
|
|
117
|
+
}, opts?: TypeOptions): PostgresDB;
|
|
105
118
|
export declare function connectPgClient(...args: Parameters<typeof pgClient>): Promise<PostgresDB>;
|
|
106
119
|
/**
|
|
107
120
|
* Disables any statement_timeout for the current transaction. By default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAehC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,QAAQ,EAAE,EAAc,KAAK,YAAY,EAAC,MAAM,UAAU,CAAC;AAClE,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAmB9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAmD7D;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAuBhD;AAcD,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAoBvE;AAOD,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA8ErE;AAED,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,uBAAoB,WAAgB;;;;;;;;;;;;;;;;;;2BAwBlD,OAAO;;;;;;2BAOP,OAAO;;;;;;2BAQP,MAAM,GAAG,IAAI;;;;;;2BAUb,MAAM;uBACV,MAAM,GAAG,MAAM;;;CAG9B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,cAAc,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAkBH,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,UAAU,EACd,SAAS,SAA+B,
|
|
1
|
+
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAehC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,QAAQ,EAAE,EAAc,KAAK,YAAY,EAAC,MAAM,UAAU,CAAC;AAClE,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAmB9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAmD7D;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAuBhD;AAcD,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAoBvE;AAOD,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA8ErE;AAED,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,uBAAoB,WAAgB;;;;;;;;;;;;;;;;;;2BAwBlD,OAAO;;;;;;2BAOP,OAAO;;;;;;2BAQP,MAAM,GAAG,IAAI;;;;;;2BAUb,MAAM;uBACV,MAAM,GAAG,MAAM;;;CAG9B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,cAAc,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAkBH,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,UAAU,EACd,SAAS,SAA+B,EAWxC,WAAW,SAAI,IAEP,SAAS,oBAAoB,KAAG,MAAM,CAqD/C;AAED,wBAAgB,QAAQ,CACtB,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,MAAM,EAGrB,eAAe,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;CAC/B,CAAC,GAAG;IACH;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,oBAAoB,GAAG,WAAW,CAAC;CAC/C,EACD,IAAI,CAAC,EAAE,WAAW,GACjB,UAAU,CAmFZ;AAED,wBAAsB,eAAe,CACnC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,GACnC,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,QAE/D;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,WAE1E;AA8BD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,WAiB/C"}
|
|
@@ -157,9 +157,10 @@ var SOCKET_INACTIVITY_TIMEOUT_MS = parseInt(process.env.ZERO_PG_SOCKET_INACTIVIT
|
|
|
157
157
|
*
|
|
158
158
|
* Exported for testing.
|
|
159
159
|
*/
|
|
160
|
-
function inactivityTimeoutSocket(lc, timeoutMs = SOCKET_INACTIVITY_TIMEOUT_MS) {
|
|
160
|
+
function inactivityTimeoutSocket(lc, timeoutMs = SOCKET_INACTIVITY_TIMEOUT_MS, keepAliveMs = 0) {
|
|
161
161
|
return (options) => {
|
|
162
162
|
const socket = new Socket();
|
|
163
|
+
if (keepAliveMs > 0) socket.setKeepAlive(true, keepAliveMs);
|
|
163
164
|
const target = options.path ? options.path : `${options.host[0]}:${options.port[0]}`;
|
|
164
165
|
if (timeoutMs > 0) {
|
|
165
166
|
let lastActivity = -1;
|
|
@@ -225,8 +226,10 @@ function pgClient(lc, connectionURI, applicationName, options, opts) {
|
|
|
225
226
|
else if (sslFlag === "no-verify") ssl = { rejectUnauthorized: false };
|
|
226
227
|
else ssl = sslFlag;
|
|
227
228
|
const maxLifetimeSeconds = randInt(300, 600);
|
|
228
|
-
const
|
|
229
|
-
const
|
|
229
|
+
const { liveness = "inactivity-timeout", ...pgOptions } = options ?? {};
|
|
230
|
+
const providedConnection = pgOptions.connection ?? {};
|
|
231
|
+
const socketLc = lc.withContext("appName", applicationName);
|
|
232
|
+
const socketFactory = { socket: liveness === "keepalive" ? inactivityTimeoutSocket(socketLc, 0, 6e4) : inactivityTimeoutSocket(socketLc) };
|
|
230
233
|
return postgres(connectionURI, {
|
|
231
234
|
...postgresTypeConfig(opts),
|
|
232
235
|
onnotice,
|
|
@@ -234,7 +237,7 @@ function pgClient(lc, connectionURI, applicationName, options, opts) {
|
|
|
234
237
|
["idle_timeout"]: 60,
|
|
235
238
|
ssl,
|
|
236
239
|
...socketFactory,
|
|
237
|
-
...
|
|
240
|
+
...pgOptions,
|
|
238
241
|
connection: {
|
|
239
242
|
...providedConnection,
|
|
240
243
|
["application_name"]: applicationName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {Socket} from 'node:net';\nimport {\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_EXCEPTION,\n PG_CONNECTION_FAILURE,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_CATALOG_NAME,\n PG_INVALID_PASSWORD,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_TOO_MANY_CONNECTIONS,\n} from '@drdgvhbh/postgres-error-codes';\nimport {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {ConfigurationError} from './configuration-error.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// Matches: YEAR-MM-DD HH:MM:SS[.fraction][+-TZ[:MM]][ BC]\nconst pgTimestampRe =\n /^(\\d+)-(\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)([+-]\\d{1,2}(?::\\d{2})?)?(?: BC)?$/;\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n if (timestamp === 'infinity') return Infinity;\n if (timestamp === '-infinity') return -Infinity;\n const match = timestamp.match(pgTimestampRe);\n if (!match) {\n throw new Error(`Error parsing ${timestamp}`);\n }\n\n const [, yearStr, monthDay, time, tz] = match;\n const bc = timestamp.endsWith(' BC');\n\n let year = Number(yearStr);\n if (bc) {\n // Postgres: 1 BC = JS year 0, 2 BC = JS year -1, N BC = -(N-1)\n year = -(year - 1);\n }\n\n // Format year as ISO 8601 expanded year if needed.\n // https://tc39.es/ecma262/#sec-expanded-years\n let isoYear: string;\n if (year >= 0 && year <= 9999) {\n isoYear = String(year).padStart(4, '0');\n } else if (year >= 0) {\n isoYear = '+' + String(year).padStart(6, '0');\n } else {\n isoYear = '-' + String(Math.abs(year)).padStart(6, '0');\n }\n\n // Build a UTC ISO string so PreciseDate returns microsecond precision.\n const utcString = `${isoYear}-${monthDay}T${time}Z`;\n\n try {\n const fullTime = new PreciseDate(utcString).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // Add back in the timezone offset. We passed local time as UTC,\n // so a positive offset means we need to subtract, and vice versa.\n if (tz) {\n const positiveOffset = tz.startsWith('+');\n const [hours, minutes] = tz.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\n// Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n// Supports optional timezone offset\nconst timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n if (date === 'infinity') return Infinity;\n if (date === '-infinity') return -Infinity;\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\n// Guards against half-open TCP connections, which otherwise hang the pool\n// indefinitely: a proxy can keep the client-facing socket alive (ACKing TCP\n// keepalives) after its backend is gone, so no 'close' or 'error' event ever\n// fires and statements like BEGIN / COMMIT await forever.\n// See https://github.com/porsager/postgres/issues/1089.\n//\n// If no bytes are read or written for this long, the socket is reset, which\n// rejects all in-flight queries and lets the pool recover. Wire activity\n// resets the timer, so streaming operations (e.g. COPY) are safe, but\n// statements that legitimately compute silently for longer (e.g. index\n// builds in migrations) must raise this. Settable as an emergency measure\n// (0 disables); deliberately not exposed as a server option.\nconst SOCKET_INACTIVITY_TIMEOUT_MS = parseInt(\n process.env.ZERO_PG_SOCKET_INACTIVITY_TIMEOUT ?? '120000',\n);\n\ntype SocketFactoryOptions = {\n host: string[];\n port: number[];\n path?: string | false;\n};\n\n/**\n * Creates the custom socket factory passed to postgres.js via its (untyped)\n * `socket` option. postgres.js does not call `connect()` on custom sockets,\n * and its TLS upgrade reads `socket.host` for the SNI servername, so the\n * factory must handle both itself.\n *\n * Note: unlike postgres.js's internal socket, reconnects do not rotate\n * through multiple hosts; multi-host URIs are not used by the zero-cache.\n *\n * Exported for testing.\n */\nexport function inactivityTimeoutSocket(\n lc: LogContext,\n timeoutMs = SOCKET_INACTIVITY_TIMEOUT_MS,\n) {\n return (options: SocketFactoryOptions): Socket => {\n const socket = new Socket();\n const target = options.path\n ? options.path\n : `${options.host[0]}:${options.port[0]}`;\n if (timeoutMs > 0) {\n // socket.setTimeout() cannot be used here: it registers its callback\n // as a 'timeout' event listener, and postgres.js removes all listeners\n // from the raw socket when upgrading it to TLS (removeAllListeners()\n // in secure()), which would silently disarm the watchdog on every TLS\n // connection. An interval is not a socket listener, so it survives the\n // upgrade, and encrypted traffic still flows through this raw socket,\n // advancing its byte counters.\n //\n // The counters are sampled once per timeout period, so a half-open\n // socket is reset after one to two timeout periods of inactivity.\n let lastActivity = -1;\n const watchdog = setInterval(() => {\n if (socket.destroyed) {\n clearInterval(watchdog);\n return;\n }\n const activity = socket.bytesRead + socket.bytesWritten;\n if (activity === lastActivity) {\n lc.warn?.(\n `resetting connection to ${target} after ${timeoutMs} ms of inactivity ` +\n `(likely half-open); in-flight queries will be rejected`,\n );\n clearInterval(watchdog);\n socket.resetAndDestroy();\n } else {\n lastActivity = activity;\n }\n }, timeoutMs);\n watchdog.unref();\n // Best-effort cleanup; removed on TLS upgrade, in which case the\n // destroyed check above clears the (unref'ed) interval instead.\n socket.once('close', () => clearInterval(watchdog));\n }\n if (options.path) {\n socket.connect(options.path);\n } else {\n const [host] = options.host;\n const [port] = options.port;\n socket.connect(port, host);\n // Read by postgres.js for the TLS SNI servername.\n Object.assign(socket, {host, port});\n }\n return socket;\n };\n}\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n // A descriptive name for the code that is making the connection, so that we\n // can debug things like deadlocks.\n applicationName: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\n applicationName = `zero-${applicationName}`;\n\n // Postgres carries row values in the `detail`, `hint`, `where` and\n // `internal_query` fields -- e.g. a unique violation reports\n // `Key (email)=(someone@example.com) already exists` in `detail`. Forward\n // only the severity, SQLSTATE and schema location; never the Notice itself.\n const safeNotice = (n: Notice) => ({\n severity: n.severity,\n code: n.code,\n message: n.message,\n schema: n.schema_name,\n table: n.table_name,\n column: n.column_name,\n constraint: n.constraint_name,\n routine: n.routine,\n });\n\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.('pg notice', safeNotice(n));\n return;\n case 'WARNING':\n lc.warn?.('pg notice', safeNotice(n));\n return;\n case 'EXCEPTION':\n lc.error?.('pg notice', safeNotice(n));\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.('pg notice', safeNotice(n));\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n const providedConnection = options?.connection ?? {};\n\n // postgres.js supports a custom socket factory via the `socket` option,\n // but it is missing from its type declarations, hence the untyped spread.\n const socketFactory = {\n socket: inactivityTimeoutSocket(lc.withContext('appName', applicationName)),\n };\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n // Close idle connections cleanly before the socket inactivity timer\n // (which cannot distinguish an idle pool connection from a hung query)\n // resets them.\n ['idle_timeout']: 60,\n ssl,\n ...socketFactory,\n ...options,\n connection: {\n ...providedConnection,\n ['application_name']: applicationName,\n },\n });\n}\n\nexport async function connectPgClient(\n ...args: Parameters<typeof pgClient>\n): Promise<PostgresDB> {\n const db = pgClient(...args);\n try {\n await db`SELECT 1`.simple().execute();\n return db;\n } catch (e) {\n if (isPostgresConfigError(e)) {\n throw new ConfigurationError(\n 'Unable to connect to Postgres. Check your database configuration.',\n {cause: e},\n );\n }\n throw e;\n }\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * the Postgres documentation).\n *\n * Zero logic in particular often does not fit into the category of general\n * application logic; for potentially long-running operations like migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n\nexport function isPostgresError(e: unknown, ...codes: [string, ...string[]]) {\n return e instanceof postgres.PostgresError && codes.includes(e.code);\n}\n\nfunction isPostgresStartupConnectionError(e: unknown) {\n if (!(e instanceof Error) || !('code' in e)) {\n return false;\n }\n const {code} = e;\n // postgres.js throws plain Error instances for connection failures that\n // happen before Postgres can return a SQLSTATE. These are still startup\n // configuration problems for Zero: wrong host/port, unreachable DB, DNS,\n // TLS certificate mismatch, etc.\n return (\n typeof code === 'string' &&\n [\n 'CONNECT_TIMEOUT',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ETIMEDOUT',\n 'ERR_TLS_CERT_ALTNAME_INVALID',\n 'SELF_SIGNED_CERT_IN_CHAIN',\n 'DEPTH_ZERO_SELF_SIGNED_CERT',\n 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n ].includes(code)\n );\n}\n\nexport function isPostgresConfigError(e: unknown) {\n return (\n isPostgresStartupConnectionError(e) ||\n isPostgresError(\n e,\n PG_CONNECTION_EXCEPTION,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_FAILURE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_PASSWORD,\n PG_INVALID_CATALOG_NAME,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_TOO_MANY_CONNECTIONS,\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n )\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {Socket} from 'node:net';\nimport {\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_EXCEPTION,\n PG_CONNECTION_FAILURE,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_CATALOG_NAME,\n PG_INVALID_PASSWORD,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_TOO_MANY_CONNECTIONS,\n} from '@drdgvhbh/postgres-error-codes';\nimport {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {ConfigurationError} from './configuration-error.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// Matches: YEAR-MM-DD HH:MM:SS[.fraction][+-TZ[:MM]][ BC]\nconst pgTimestampRe =\n /^(\\d+)-(\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)([+-]\\d{1,2}(?::\\d{2})?)?(?: BC)?$/;\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n if (timestamp === 'infinity') return Infinity;\n if (timestamp === '-infinity') return -Infinity;\n const match = timestamp.match(pgTimestampRe);\n if (!match) {\n throw new Error(`Error parsing ${timestamp}`);\n }\n\n const [, yearStr, monthDay, time, tz] = match;\n const bc = timestamp.endsWith(' BC');\n\n let year = Number(yearStr);\n if (bc) {\n // Postgres: 1 BC = JS year 0, 2 BC = JS year -1, N BC = -(N-1)\n year = -(year - 1);\n }\n\n // Format year as ISO 8601 expanded year if needed.\n // https://tc39.es/ecma262/#sec-expanded-years\n let isoYear: string;\n if (year >= 0 && year <= 9999) {\n isoYear = String(year).padStart(4, '0');\n } else if (year >= 0) {\n isoYear = '+' + String(year).padStart(6, '0');\n } else {\n isoYear = '-' + String(Math.abs(year)).padStart(6, '0');\n }\n\n // Build a UTC ISO string so PreciseDate returns microsecond precision.\n const utcString = `${isoYear}-${monthDay}T${time}Z`;\n\n try {\n const fullTime = new PreciseDate(utcString).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // Add back in the timezone offset. We passed local time as UTC,\n // so a positive offset means we need to subtract, and vice versa.\n if (tz) {\n const positiveOffset = tz.startsWith('+');\n const [hours, minutes] = tz.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\n// Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n// Supports optional timezone offset\nconst timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n if (date === 'infinity') return Infinity;\n if (date === '-infinity') return -Infinity;\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\n// Guards against half-open TCP connections, which otherwise hang the pool\n// indefinitely: a proxy can keep the client-facing socket alive (ACKing TCP\n// keepalives) after its backend is gone, so no 'close' or 'error' event ever\n// fires and statements like BEGIN / COMMIT await forever.\n// See https://github.com/porsager/postgres/issues/1089.\n//\n// If no bytes are read or written for this long, the socket is reset, which\n// rejects all in-flight queries and lets the pool recover. Wire activity\n// resets the timer, so streaming operations (e.g. COPY) are safe, but\n// statements that legitimately compute silently for longer (e.g. index\n// builds in migrations) must raise this. Settable as an emergency measure\n// (0 disables); deliberately not exposed as a server option.\nconst SOCKET_INACTIVITY_TIMEOUT_MS = parseInt(\n process.env.ZERO_PG_SOCKET_INACTIVITY_TIMEOUT ?? '120000',\n);\n\ntype SocketFactoryOptions = {\n host: string[];\n port: number[];\n path?: string | false;\n};\n\n/**\n * Creates the custom socket factory passed to postgres.js via its (untyped)\n * `socket` option. postgres.js does not call `connect()` on custom sockets,\n * and its TLS upgrade reads `socket.host` for the SNI servername, so the\n * factory must handle both itself.\n *\n * Note: unlike postgres.js's internal socket, reconnects do not rotate\n * through multiple hosts; multi-host URIs are not used by the zero-cache.\n *\n * Exported for testing.\n */\nexport function inactivityTimeoutSocket(\n lc: LogContext,\n timeoutMs = SOCKET_INACTIVITY_TIMEOUT_MS,\n // When > 0, enables OS-level TCP keepalive probes with this initial delay.\n // Unlike the inactivity watchdog, keepalive distinguishes an idle-but-alive\n // connection from a dead one (the OS tears down the socket only when probes\n // go unanswered), so it does not false-positive on connections that are\n // legitimately idle for long stretches -- e.g. a backfill COPY stream that\n // this process deliberately back-pressures. It does NOT catch a proxy that\n // ACKs keepalives after its backend is gone (the case the watchdog guards);\n // callers that disable the watchdog (timeoutMs = 0) in favor of keepalive\n // accept that residual risk. Keepalive is a socket option, not a listener,\n // so it survives postgres.js's removeAllListeners() on TLS upgrade.\n keepAliveMs = 0,\n) {\n return (options: SocketFactoryOptions): Socket => {\n const socket = new Socket();\n if (keepAliveMs > 0) {\n socket.setKeepAlive(true, keepAliveMs);\n }\n const target = options.path\n ? options.path\n : `${options.host[0]}:${options.port[0]}`;\n if (timeoutMs > 0) {\n // socket.setTimeout() cannot be used here: it registers its callback\n // as a 'timeout' event listener, and postgres.js removes all listeners\n // from the raw socket when upgrading it to TLS (removeAllListeners()\n // in secure()), which would silently disarm the watchdog on every TLS\n // connection. An interval is not a socket listener, so it survives the\n // upgrade, and encrypted traffic still flows through this raw socket,\n // advancing its byte counters.\n //\n // The counters are sampled once per timeout period, so a half-open\n // socket is reset after one to two timeout periods of inactivity.\n let lastActivity = -1;\n const watchdog = setInterval(() => {\n if (socket.destroyed) {\n clearInterval(watchdog);\n return;\n }\n const activity = socket.bytesRead + socket.bytesWritten;\n if (activity === lastActivity) {\n lc.warn?.(\n `resetting connection to ${target} after ${timeoutMs} ms of inactivity ` +\n `(likely half-open); in-flight queries will be rejected`,\n );\n clearInterval(watchdog);\n socket.resetAndDestroy();\n } else {\n lastActivity = activity;\n }\n }, timeoutMs);\n watchdog.unref();\n // Best-effort cleanup; removed on TLS upgrade, in which case the\n // destroyed check above clears the (unref'ed) interval instead.\n socket.once('close', () => clearInterval(watchdog));\n }\n if (options.path) {\n socket.connect(options.path);\n } else {\n const [host] = options.host;\n const [port] = options.port;\n socket.connect(port, host);\n // Read by postgres.js for the TLS SNI servername.\n Object.assign(socket, {host, port});\n }\n return socket;\n };\n}\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n // A descriptive name for the code that is making the connection, so that we\n // can debug things like deadlocks.\n applicationName: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }> & {\n /**\n * How to detect a dead (\"half-open\") connection:\n * - `'inactivity-timeout'` (default): reset the socket after\n * {@link SOCKET_INACTIVITY_TIMEOUT_MS} of no read/write activity. Catches\n * proxy-level half-opens, but false-positives on connections that are\n * legitimately idle for long stretches.\n * - `'keepalive'`: disable the inactivity watchdog and rely on OS TCP\n * keepalive probes instead. Use for connections that are expected to sit\n * idle under back-pressure (e.g. the backfill COPY stream), where the\n * watchdog would reset a perfectly healthy connection.\n */\n liveness?: 'inactivity-timeout' | 'keepalive';\n },\n opts?: TypeOptions,\n): PostgresDB {\n applicationName = `zero-${applicationName}`;\n\n // Postgres carries row values in the `detail`, `hint`, `where` and\n // `internal_query` fields -- e.g. a unique violation reports\n // `Key (email)=(someone@example.com) already exists` in `detail`. Forward\n // only the severity, SQLSTATE and schema location; never the Notice itself.\n const safeNotice = (n: Notice) => ({\n severity: n.severity,\n code: n.code,\n message: n.message,\n schema: n.schema_name,\n table: n.table_name,\n column: n.column_name,\n constraint: n.constraint_name,\n routine: n.routine,\n });\n\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.('pg notice', safeNotice(n));\n return;\n case 'WARNING':\n lc.warn?.('pg notice', safeNotice(n));\n return;\n case 'EXCEPTION':\n lc.error?.('pg notice', safeNotice(n));\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.('pg notice', safeNotice(n));\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n const {liveness = 'inactivity-timeout', ...pgOptions} = options ?? {};\n const providedConnection = pgOptions.connection ?? {};\n\n // postgres.js supports a custom socket factory via the `socket` option,\n // but it is missing from its type declarations, hence the untyped spread.\n const socketLc = lc.withContext('appName', applicationName);\n const socketFactory = {\n socket:\n liveness === 'keepalive'\n ? // Disable the inactivity watchdog and use TCP keepalive instead.\n inactivityTimeoutSocket(socketLc, 0, 60_000)\n : inactivityTimeoutSocket(socketLc),\n };\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n // Close idle connections cleanly before the socket inactivity timer\n // (which cannot distinguish an idle pool connection from a hung query)\n // resets them.\n ['idle_timeout']: 60,\n ssl,\n ...socketFactory,\n ...pgOptions,\n connection: {\n ...providedConnection,\n ['application_name']: applicationName,\n },\n });\n}\n\nexport async function connectPgClient(\n ...args: Parameters<typeof pgClient>\n): Promise<PostgresDB> {\n const db = pgClient(...args);\n try {\n await db`SELECT 1`.simple().execute();\n return db;\n } catch (e) {\n if (isPostgresConfigError(e)) {\n throw new ConfigurationError(\n 'Unable to connect to Postgres. Check your database configuration.',\n {cause: e},\n );\n }\n throw e;\n }\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * the Postgres documentation).\n *\n * Zero logic in particular often does not fit into the category of general\n * application logic; for potentially long-running operations like migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n\nexport function isPostgresError(e: unknown, ...codes: [string, ...string[]]) {\n return e instanceof postgres.PostgresError && codes.includes(e.code);\n}\n\nfunction isPostgresStartupConnectionError(e: unknown) {\n if (!(e instanceof Error) || !('code' in e)) {\n return false;\n }\n const {code} = e;\n // postgres.js throws plain Error instances for connection failures that\n // happen before Postgres can return a SQLSTATE. These are still startup\n // configuration problems for Zero: wrong host/port, unreachable DB, DNS,\n // TLS certificate mismatch, etc.\n return (\n typeof code === 'string' &&\n [\n 'CONNECT_TIMEOUT',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ETIMEDOUT',\n 'ERR_TLS_CERT_ALTNAME_INVALID',\n 'SELF_SIGNED_CERT_IN_CHAIN',\n 'DEPTH_ZERO_SELF_SIGNED_CERT',\n 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n ].includes(code)\n );\n}\n\nexport function isPostgresConfigError(e: unknown) {\n return (\n isPostgresStartupConnectionError(e) ||\n isPostgresError(\n e,\n PG_CONNECTION_EXCEPTION,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_FAILURE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_PASSWORD,\n PG_INVALID_CATALOG_NAME,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_TOO_MANY_CONNECTIONS,\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n )\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-inspector.d.ts","sourceRoot":"","sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,+CAA+C,CAAC;AAGlF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAU,KAAK,eAAe,EAAC,MAAM,mCAAmC,CAAC;AAChF,OAAO,KAAK,MAAM,MAAM,kCAAkC,CAAC;AAC3D,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uDAAuD,CAAC;AAC9F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,sCAAsC,CAAC;AAC9D,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,uCAAuC,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"lazy-inspector.d.ts","sourceRoot":"","sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { withRead } from "../../../../replicache/src/with-transactions.js";
|
|
|
5
5
|
import { getClientGroup } from "../../../../replicache/src/persist/client-groups.js";
|
|
6
6
|
import { getClient, getClients } from "../../../../replicache/src/persist/clients.js";
|
|
7
7
|
import { asQueryInternals } from "../../../../zql/src/query/query-internals.js";
|
|
8
|
-
import { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema } from "../../../../zero-protocol/src/inspect-down.js";
|
|
8
|
+
import { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectErrorDownSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema } from "../../../../zero-protocol/src/inspect-down.js";
|
|
9
9
|
import { nanoid } from "../../util/nanoid.js";
|
|
10
10
|
import "../keys.js";
|
|
11
11
|
import { Client } from "./client.js";
|
|
@@ -35,23 +35,30 @@ function rpcNoAuthTry(socket, arg, downSchema) {
|
|
|
35
35
|
return new Promise((resolve, reject) => {
|
|
36
36
|
const id = nanoid();
|
|
37
37
|
const f = (ev) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (typeof ev.data !== "string") return;
|
|
39
|
+
let msg;
|
|
40
|
+
try {
|
|
41
|
+
msg = JSON.parse(ev.data);
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!Array.isArray(msg) || msg[0] !== "inspect") return;
|
|
46
|
+
const body = msg[1];
|
|
47
|
+
if (body.id !== id) return;
|
|
48
|
+
const errorRes = test(body, inspectErrorDownSchema);
|
|
49
|
+
if (errorRes.ok) reject(new Error(errorRes.value.value));
|
|
50
|
+
else {
|
|
42
51
|
const res = test(body, downSchema);
|
|
43
|
-
if (res.ok)
|
|
44
|
-
else resolve(res.value.value);
|
|
52
|
+
if (res.ok) resolve(res.value.value);
|
|
45
53
|
else {
|
|
46
54
|
const authRes = test(body, inspectAuthenticatedDownSchema);
|
|
47
55
|
if (authRes.ok) {
|
|
48
56
|
assert(authRes.value.value === false, "Expected unauthenticated response");
|
|
49
57
|
reject(new UnauthenticatedError());
|
|
50
|
-
}
|
|
51
|
-
reject(res.error);
|
|
58
|
+
} else reject(res.error);
|
|
52
59
|
}
|
|
53
|
-
socket.removeEventListener("message", f);
|
|
54
60
|
}
|
|
61
|
+
socket.removeEventListener("message", f);
|
|
55
62
|
};
|
|
56
63
|
socket.addEventListener("message", f);
|
|
57
64
|
socket.send(JSON.stringify(["inspect", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-inspector.js","names":[],"sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"sourcesContent":["import type {BTreeRead} from '../../../../replicache/src/btree/read.ts';\nimport type {Read} from '../../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../../replicache/src/format-version-enum.ts';\nimport {getClientGroup} from '../../../../replicache/src/persist/client-groups.ts';\nimport {\n getClient,\n getClients,\n type ClientMap,\n} from '../../../../replicache/src/persist/clients.ts';\nimport type {ReplicacheImpl} from '../../../../replicache/src/replicache-impl.ts';\nimport {withRead} from '../../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../../shared/src/json.ts';\nimport {TDigest, type ReadonlyTDigest} from '../../../../shared/src/tdigest.ts';\nimport * as valita from '../../../../shared/src/valita.ts';\nimport type {AnalyzeQueryResult} from '../../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport {\n inspectAnalyzeQueryDownSchema,\n inspectAuthenticatedDownSchema,\n inspectMetricsDownSchema,\n inspectQueriesDownSchema,\n inspectVersionDownSchema,\n type InspectDownBody,\n type InspectQueryRow,\n type QueryServerMetrics as QueryServerMetricsJSON,\n} from '../../../../zero-protocol/src/inspect-down.ts';\nimport type {\n AnalyzeQueryOptions,\n InspectUpBody,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport type {\n ClientMetricMap,\n ServerMetricMap,\n} from '../../../../zql/src/query/metrics-delegate.ts';\nimport type {QueryDelegate} from '../../../../zql/src/query/query-delegate.ts';\nimport {asQueryInternals} from '../../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../../zql/src/query/query.ts';\nimport {nanoid} from '../../util/nanoid.ts';\nimport {ENTITIES_KEY_PREFIX} from '../keys.ts';\nimport type {MutatorDefs} from '../replicache-types.ts';\nimport {Client} from './client.ts';\nimport {createHTMLPasswordPrompt} from './html-dialog-prompt.ts';\nimport {type Lazy} from './inspector.ts';\nimport {Query} from './query.ts';\n\nexport type GetWebSocket = () => Promise<WebSocket>;\n\nexport type Metrics = {\n readonly [K in keyof (ClientMetricMap & ServerMetricMap)]: ReadonlyTDigest;\n};\n\ntype DistributiveOmit<T, K extends string> = T extends object\n ? Omit<T, K>\n : never;\n\nexport async function rpc<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n try {\n return await rpcNoAuthTry(socket, arg, downSchema);\n } catch (e) {\n if (e instanceof UnauthenticatedError) {\n const password = await createHTMLPasswordPrompt('Enter password:');\n if (password) {\n // Do authenticate rpc\n const authRes = await rpcNoAuthTry(\n socket,\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n if (authRes) {\n // If authentication is successful, retry the original RPC\n return rpcNoAuthTry(socket, arg, downSchema);\n }\n }\n throw new Error('Authentication failed');\n }\n throw e;\n }\n}\n\nfunction rpcNoAuthTry<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n return new Promise((resolve, reject) => {\n const id = nanoid();\n const f = (ev: MessageEvent) => {\n const msg = JSON.parse(ev.data);\n if (msg[0] === 'inspect') {\n const body = msg[1];\n if (body.id !== id) {\n return;\n }\n const res = valita.test(body, downSchema);\n if (res.ok) {\n if (res.value.op === 'error') {\n reject(new Error(res.value.value));\n } else {\n resolve(res.value.value);\n }\n } else {\n // Check if we got un authenticated/false response\n const authRes = valita.test(body, inspectAuthenticatedDownSchema);\n if (authRes.ok) {\n // Handle authenticated response\n assert(\n authRes.value.value === false,\n 'Expected unauthenticated response',\n );\n reject(new UnauthenticatedError());\n }\n\n reject(res.error);\n }\n socket.removeEventListener('message', f);\n }\n };\n socket.addEventListener('message', f);\n socket.send(JSON.stringify(['inspect', {...arg, id}]));\n });\n} // T extends forces T to be resolved\n\nexport function mergeMetrics(\n clientMetrics: QueryClientMetrics | undefined,\n serverMetrics: QueryServerMetricsJSON | null | undefined,\n): ClientMetrics & ServerMetrics {\n const cm = clientMetrics ?? newClientMetrics();\n return {\n ...convertClientMetrics(cm),\n ...(serverMetrics\n ? convertServerMetrics(serverMetrics)\n : newServerMetrics()),\n };\n}\n\nfunction newClientMetrics(): QueryClientMetrics {\n return {\n 'query-materialization-client': undefined,\n 'query-materialization-end-to-end': undefined,\n 'query-update-client': new TDigest(),\n };\n}\n\nfunction convertClientMetrics(metrics: QueryClientMetrics): ClientMetrics {\n const hydrateDigest = new TDigest();\n if (metrics['query-materialization-client'] !== undefined) {\n hydrateDigest.add(metrics['query-materialization-client']);\n }\n const totalDigest = new TDigest();\n if (metrics['query-materialization-end-to-end'] !== undefined) {\n totalDigest.add(metrics['query-materialization-end-to-end']);\n }\n return {\n 'query-materialization-client': hydrateDigest,\n 'query-materialization-end-to-end': totalDigest,\n 'query-update-client': metrics['query-update-client'] as TDigest,\n };\n}\n\nfunction newServerMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n\nfunction convertServerMetrics(metrics: QueryServerMetricsJSON): ServerMetrics {\n const hydrateMs = metrics['query-hydration-server-ms'];\n const hydrateDigest = new TDigest();\n if (hydrateMs !== undefined) {\n hydrateDigest.add(hydrateMs);\n }\n return {\n 'query-materialization-server': hydrateDigest,\n 'query-update-server': TDigest.fromJSON(metrics['query-update-server']),\n };\n}\n\nexport async function inspectorMetrics(\n delegate: ExtendedInspectorDelegate,\n): Promise<Metrics> {\n const clientMetrics = delegate.metrics;\n const serverMetricsJSON = await rpc(\n await delegate.getSocket(),\n {op: 'metrics'},\n inspectMetricsDownSchema,\n );\n return {\n ...(clientMetrics ?? newClientMetrics()),\n 'query-materialization-server': TDigest.fromJSON(\n serverMetricsJSON['query-materialization-server'],\n ),\n 'query-update-server': TDigest.fromJSON(\n serverMetricsJSON['query-update-server'],\n ),\n };\n}\n\nexport function inspectorClients(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead => clients(delegate, dagRead));\n}\n\nexport function inspectorClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead),\n );\n}\n\nasync function withDagRead<T>(\n delegate: ExtendedInspectorDelegate,\n f: (dagRead: Read) => Promise<T>,\n): Promise<T> {\n const {rep} = delegate;\n await rep.refresh();\n await rep.persist();\n return withRead(rep.perdag, f);\n}\n\nasync function getBTree(dagRead: Read, clientID: string): Promise<BTreeRead> {\n const client = await getClient(clientID, dagRead);\n assert(client, `Client not found: ${clientID}`);\n const {clientGroupID} = client;\n const clientGroup = await getClientGroup(clientGroupID, dagRead);\n assert(clientGroup, `Client group not found: ${clientGroupID}`);\n const dbRead = await readFromHash(\n clientGroup.headHash,\n dagRead,\n FormatVersion.Latest,\n );\n return dbRead.map;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype MapEntry<T extends ReadonlyMap<any, any>> =\n T extends ReadonlyMap<infer K, infer V> ? readonly [K, V] : never;\n\nasync function clients(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const clients = await getClients(dagRead);\n return [...clients.entries()]\n .filter(predicate)\n .map(\n ([clientID, {clientGroupID}]) =>\n new Client(delegate, clientID, clientGroupID),\n );\n}\n\nasync function clientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const allClients = await clients(delegate, dagRead, predicate);\n const clientsWithQueries: Client[] = [];\n await Promise.all(\n allClients.map(async client => {\n const queries = await client.queries();\n if (queries.length > 0) {\n clientsWithQueries.push(client);\n }\n }),\n );\n return clientsWithQueries;\n}\n\nexport async function clientGroupClients(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clients(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport async function clientGroupClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport function clientGroupQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries'});\n}\nexport function clientMap(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Map<string, ReadonlyJSONValue>> {\n return withDagRead(delegate, async dagRead => {\n const tree = await getBTree(dagRead, clientID);\n const map = new Map<string, ReadonlyJSONValue>();\n for await (const [key, value] of tree.scan('')) {\n map.set(key, value);\n }\n return map;\n });\n}\n\nexport function clientRows(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n tableName: string,\n): Promise<Row[]> {\n return withDagRead(delegate, async dagRead => {\n const prefix = ENTITIES_KEY_PREFIX + tableName + '/';\n const tree = await getBTree(dagRead, clientID);\n const rows: Row[] = [];\n for await (const [key, value] of tree.scan(prefix)) {\n if (!key.startsWith(prefix)) {\n break;\n }\n rows.push(value as Row);\n }\n return rows;\n });\n}\n\nexport async function serverVersion(\n delegate: ExtendedInspectorDelegate,\n): Promise<string> {\n return rpc(\n await delegate.getSocket(),\n {op: 'version'},\n inspectVersionDownSchema,\n );\n}\n\nexport function clientQueries(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries', clientID});\n}\n\nasync function queries(\n delegate: ExtendedInspectorDelegate,\n arg: {op: 'queries'; clientID?: string},\n): Promise<Query[]> {\n const rows: InspectQueryRow[] = await rpc(\n await delegate.getSocket(),\n arg,\n inspectQueriesDownSchema,\n );\n const queries = rows.map(row => new Query(row, delegate, delegate.getSocket));\n queries.sort((a, b) => (b.hydrateServer ?? 0) - (a.hydrateServer ?? 0));\n return queries;\n}\n\nexport async function analyzeQuery(\n delegate: ExtendedInspectorDelegate,\n query: AnyQuery,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n const qi = asQueryInternals(query);\n const {customQueryID} = qi;\n const queryParameters = customQueryID\n ? {name: customQueryID.name, args: customQueryID.args}\n : {ast: delegate.mapClientASTToServer(qi.ast)};\n\n return rpc(\n await delegate.getSocket(),\n {\n op: 'analyze-query',\n ...queryParameters,\n options,\n },\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeServerAST(\n delegate: ExtendedInspectorDelegate,\n ast: AST,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', ast, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeNamedQuery(\n delegate: ExtendedInspectorDelegate,\n name: string,\n args: ReadonlyArray<ReadonlyJSONValue>,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', name, args, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\n/**\n * Sends an `authenticate` op to the server. Returns `true` if the password\n * is accepted (or the server is in a development mode that bypasses the\n * check). Use this from Node contexts where the default interactive HTML\n * prompt used by other inspector RPCs is not available.\n */\nexport async function authenticate(\n delegate: ExtendedInspectorDelegate,\n password: string,\n): Promise<boolean> {\n return rpc(\n await delegate.getSocket(),\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n}\n\nclass UnauthenticatedError extends Error {}\n\nexport interface InspectorDelegate {\n getQueryMetrics(hash: string): QueryClientMetrics | undefined;\n getAST(queryID: string): AST | undefined;\n readonly metrics: ClientMetrics;\n mapClientASTToServer(ast: AST): AST;\n setQueryEvictedCallback(\n cb: (hash: string, ast: AST, metrics: QueryClientMetrics) => void,\n ): void;\n}\n\nexport interface ExtendedInspectorDelegate extends InspectorDelegate {\n readonly rep: Rep;\n readonly getSocket: () => Promise<WebSocket>;\n readonly queryDelegate: QueryDelegate;\n lazy: Promise<Lazy>;\n}\n\nexport type Rep = ReplicacheImpl<MutatorDefs>;\n\nexport type QueryClientMetrics = {\n readonly 'query-materialization-client': number | undefined;\n readonly 'query-materialization-end-to-end': number | undefined;\n readonly 'query-update-client': ReadonlyTDigest;\n};\n\nexport type ClientMetrics = {\n readonly [K in keyof ClientMetricMap]: ReadonlyTDigest;\n};\n\nexport type ServerMetrics = {\n readonly [K in keyof ServerMetricMap]: ReadonlyTDigest;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lazy-inspector.js","names":[],"sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"sourcesContent":["import type {BTreeRead} from '../../../../replicache/src/btree/read.ts';\nimport type {Read} from '../../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../../replicache/src/format-version-enum.ts';\nimport {getClientGroup} from '../../../../replicache/src/persist/client-groups.ts';\nimport {\n getClient,\n getClients,\n type ClientMap,\n} from '../../../../replicache/src/persist/clients.ts';\nimport type {ReplicacheImpl} from '../../../../replicache/src/replicache-impl.ts';\nimport {withRead} from '../../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../../shared/src/json.ts';\nimport {TDigest, type ReadonlyTDigest} from '../../../../shared/src/tdigest.ts';\nimport * as valita from '../../../../shared/src/valita.ts';\nimport type {AnalyzeQueryResult} from '../../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport {\n inspectAnalyzeQueryDownSchema,\n inspectAuthenticatedDownSchema,\n inspectErrorDownSchema,\n inspectMetricsDownSchema,\n inspectQueriesDownSchema,\n inspectVersionDownSchema,\n type InspectDownBody,\n type InspectQueryRow,\n type QueryServerMetrics as QueryServerMetricsJSON,\n} from '../../../../zero-protocol/src/inspect-down.ts';\nimport type {\n AnalyzeQueryOptions,\n InspectUpBody,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport type {\n ClientMetricMap,\n ServerMetricMap,\n} from '../../../../zql/src/query/metrics-delegate.ts';\nimport type {QueryDelegate} from '../../../../zql/src/query/query-delegate.ts';\nimport {asQueryInternals} from '../../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../../zql/src/query/query.ts';\nimport {nanoid} from '../../util/nanoid.ts';\nimport {ENTITIES_KEY_PREFIX} from '../keys.ts';\nimport type {MutatorDefs} from '../replicache-types.ts';\nimport {Client} from './client.ts';\nimport {createHTMLPasswordPrompt} from './html-dialog-prompt.ts';\nimport {type Lazy} from './inspector.ts';\nimport {Query} from './query.ts';\n\nexport type GetWebSocket = () => Promise<WebSocket>;\n\nexport type Metrics = {\n readonly [K in keyof (ClientMetricMap & ServerMetricMap)]: ReadonlyTDigest;\n};\n\ntype DistributiveOmit<T, K extends string> = T extends object\n ? Omit<T, K>\n : never;\n\nexport async function rpc<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n try {\n return await rpcNoAuthTry(socket, arg, downSchema);\n } catch (e) {\n if (e instanceof UnauthenticatedError) {\n const password = await createHTMLPasswordPrompt('Enter password:');\n if (password) {\n // Do authenticate rpc\n const authRes = await rpcNoAuthTry(\n socket,\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n if (authRes) {\n // If authentication is successful, retry the original RPC\n return rpcNoAuthTry(socket, arg, downSchema);\n }\n }\n throw new Error('Authentication failed');\n }\n throw e;\n }\n}\n\nfunction rpcNoAuthTry<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n return new Promise((resolve, reject) => {\n const id = nanoid();\n const f = (ev: MessageEvent) => {\n if (typeof ev.data !== 'string') {\n // Binary messages (such as poke chunks) are not inspector messages.\n return;\n }\n let msg: unknown;\n try {\n msg = JSON.parse(ev.data);\n } catch {\n return;\n }\n if (!Array.isArray(msg) || msg[0] !== 'inspect') {\n return;\n }\n const body = msg[1];\n if (body.id !== id) {\n return;\n }\n const errorRes = valita.test(body, inspectErrorDownSchema);\n if (errorRes.ok) {\n reject(new Error(errorRes.value.value));\n } else {\n const res = valita.test(body, downSchema);\n if (res.ok) {\n resolve(res.value.value);\n } else {\n // Check if we got un authenticated/false response\n const authRes = valita.test(body, inspectAuthenticatedDownSchema);\n if (authRes.ok) {\n // Handle authenticated response\n assert(\n authRes.value.value === false,\n 'Expected unauthenticated response',\n );\n reject(new UnauthenticatedError());\n } else {\n reject(res.error);\n }\n }\n }\n socket.removeEventListener('message', f);\n };\n socket.addEventListener('message', f);\n socket.send(JSON.stringify(['inspect', {...arg, id}]));\n });\n} // T extends forces T to be resolved\n\nexport function mergeMetrics(\n clientMetrics: QueryClientMetrics | undefined,\n serverMetrics: QueryServerMetricsJSON | null | undefined,\n): ClientMetrics & ServerMetrics {\n const cm = clientMetrics ?? newClientMetrics();\n return {\n ...convertClientMetrics(cm),\n ...(serverMetrics\n ? convertServerMetrics(serverMetrics)\n : newServerMetrics()),\n };\n}\n\nfunction newClientMetrics(): QueryClientMetrics {\n return {\n 'query-materialization-client': undefined,\n 'query-materialization-end-to-end': undefined,\n 'query-update-client': new TDigest(),\n };\n}\n\nfunction convertClientMetrics(metrics: QueryClientMetrics): ClientMetrics {\n const hydrateDigest = new TDigest();\n if (metrics['query-materialization-client'] !== undefined) {\n hydrateDigest.add(metrics['query-materialization-client']);\n }\n const totalDigest = new TDigest();\n if (metrics['query-materialization-end-to-end'] !== undefined) {\n totalDigest.add(metrics['query-materialization-end-to-end']);\n }\n return {\n 'query-materialization-client': hydrateDigest,\n 'query-materialization-end-to-end': totalDigest,\n 'query-update-client': metrics['query-update-client'] as TDigest,\n };\n}\n\nfunction newServerMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n\nfunction convertServerMetrics(metrics: QueryServerMetricsJSON): ServerMetrics {\n const hydrateMs = metrics['query-hydration-server-ms'];\n const hydrateDigest = new TDigest();\n if (hydrateMs !== undefined) {\n hydrateDigest.add(hydrateMs);\n }\n return {\n 'query-materialization-server': hydrateDigest,\n 'query-update-server': TDigest.fromJSON(metrics['query-update-server']),\n };\n}\n\nexport async function inspectorMetrics(\n delegate: ExtendedInspectorDelegate,\n): Promise<Metrics> {\n const clientMetrics = delegate.metrics;\n const serverMetricsJSON = await rpc(\n await delegate.getSocket(),\n {op: 'metrics'},\n inspectMetricsDownSchema,\n );\n return {\n ...(clientMetrics ?? newClientMetrics()),\n 'query-materialization-server': TDigest.fromJSON(\n serverMetricsJSON['query-materialization-server'],\n ),\n 'query-update-server': TDigest.fromJSON(\n serverMetricsJSON['query-update-server'],\n ),\n };\n}\n\nexport function inspectorClients(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead => clients(delegate, dagRead));\n}\n\nexport function inspectorClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead),\n );\n}\n\nasync function withDagRead<T>(\n delegate: ExtendedInspectorDelegate,\n f: (dagRead: Read) => Promise<T>,\n): Promise<T> {\n const {rep} = delegate;\n await rep.refresh();\n await rep.persist();\n return withRead(rep.perdag, f);\n}\n\nasync function getBTree(dagRead: Read, clientID: string): Promise<BTreeRead> {\n const client = await getClient(clientID, dagRead);\n assert(client, `Client not found: ${clientID}`);\n const {clientGroupID} = client;\n const clientGroup = await getClientGroup(clientGroupID, dagRead);\n assert(clientGroup, `Client group not found: ${clientGroupID}`);\n const dbRead = await readFromHash(\n clientGroup.headHash,\n dagRead,\n FormatVersion.Latest,\n );\n return dbRead.map;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype MapEntry<T extends ReadonlyMap<any, any>> =\n T extends ReadonlyMap<infer K, infer V> ? readonly [K, V] : never;\n\nasync function clients(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const clients = await getClients(dagRead);\n return [...clients.entries()]\n .filter(predicate)\n .map(\n ([clientID, {clientGroupID}]) =>\n new Client(delegate, clientID, clientGroupID),\n );\n}\n\nasync function clientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const allClients = await clients(delegate, dagRead, predicate);\n const clientsWithQueries: Client[] = [];\n await Promise.all(\n allClients.map(async client => {\n const queries = await client.queries();\n if (queries.length > 0) {\n clientsWithQueries.push(client);\n }\n }),\n );\n return clientsWithQueries;\n}\n\nexport async function clientGroupClients(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clients(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport async function clientGroupClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport function clientGroupQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries'});\n}\nexport function clientMap(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Map<string, ReadonlyJSONValue>> {\n return withDagRead(delegate, async dagRead => {\n const tree = await getBTree(dagRead, clientID);\n const map = new Map<string, ReadonlyJSONValue>();\n for await (const [key, value] of tree.scan('')) {\n map.set(key, value);\n }\n return map;\n });\n}\n\nexport function clientRows(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n tableName: string,\n): Promise<Row[]> {\n return withDagRead(delegate, async dagRead => {\n const prefix = ENTITIES_KEY_PREFIX + tableName + '/';\n const tree = await getBTree(dagRead, clientID);\n const rows: Row[] = [];\n for await (const [key, value] of tree.scan(prefix)) {\n if (!key.startsWith(prefix)) {\n break;\n }\n rows.push(value as Row);\n }\n return rows;\n });\n}\n\nexport async function serverVersion(\n delegate: ExtendedInspectorDelegate,\n): Promise<string> {\n return rpc(\n await delegate.getSocket(),\n {op: 'version'},\n inspectVersionDownSchema,\n );\n}\n\nexport function clientQueries(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries', clientID});\n}\n\nasync function queries(\n delegate: ExtendedInspectorDelegate,\n arg: {op: 'queries'; clientID?: string},\n): Promise<Query[]> {\n const rows: InspectQueryRow[] = await rpc(\n await delegate.getSocket(),\n arg,\n inspectQueriesDownSchema,\n );\n const queries = rows.map(row => new Query(row, delegate, delegate.getSocket));\n queries.sort((a, b) => (b.hydrateServer ?? 0) - (a.hydrateServer ?? 0));\n return queries;\n}\n\nexport async function analyzeQuery(\n delegate: ExtendedInspectorDelegate,\n query: AnyQuery,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n const qi = asQueryInternals(query);\n const {customQueryID} = qi;\n const queryParameters = customQueryID\n ? {name: customQueryID.name, args: customQueryID.args}\n : {ast: delegate.mapClientASTToServer(qi.ast)};\n\n return rpc(\n await delegate.getSocket(),\n {\n op: 'analyze-query',\n ...queryParameters,\n options,\n },\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeServerAST(\n delegate: ExtendedInspectorDelegate,\n ast: AST,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', ast, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeNamedQuery(\n delegate: ExtendedInspectorDelegate,\n name: string,\n args: ReadonlyArray<ReadonlyJSONValue>,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', name, args, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\n/**\n * Sends an `authenticate` op to the server. Returns `true` if the password\n * is accepted (or the server is in a development mode that bypasses the\n * check). Use this from Node contexts where the default interactive HTML\n * prompt used by other inspector RPCs is not available.\n */\nexport async function authenticate(\n delegate: ExtendedInspectorDelegate,\n password: string,\n): Promise<boolean> {\n return rpc(\n await delegate.getSocket(),\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n}\n\nclass UnauthenticatedError extends Error {}\n\nexport interface InspectorDelegate {\n getQueryMetrics(hash: string): QueryClientMetrics | undefined;\n getAST(queryID: string): AST | undefined;\n readonly metrics: ClientMetrics;\n mapClientASTToServer(ast: AST): AST;\n setQueryEvictedCallback(\n cb: (hash: string, ast: AST, metrics: QueryClientMetrics) => void,\n ): void;\n}\n\nexport interface ExtendedInspectorDelegate extends InspectorDelegate {\n readonly rep: Rep;\n readonly getSocket: () => Promise<WebSocket>;\n readonly queryDelegate: QueryDelegate;\n lazy: Promise<Lazy>;\n}\n\nexport type Rep = ReplicacheImpl<MutatorDefs>;\n\nexport type QueryClientMetrics = {\n readonly 'query-materialization-client': number | undefined;\n readonly 'query-materialization-end-to-end': number | undefined;\n readonly 'query-update-client': ReadonlyTDigest;\n};\n\nexport type ClientMetrics = {\n readonly [K in keyof ClientMetricMap]: ReadonlyTDigest;\n};\n\nexport type ServerMetrics = {\n readonly [K in keyof ServerMetricMap]: ReadonlyTDigest;\n};\n"],"mappings":"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"}
|
|
@@ -117,7 +117,7 @@ export declare const transformResponseMessageSchema: v.UnionType<[v.TupleType<[v
|
|
|
117
117
|
message: v.Type<string>;
|
|
118
118
|
}, "reason" | "origin"> & {
|
|
119
119
|
origin: v.Type<"zeroCache">;
|
|
120
|
-
reason: v.Type<"parse" | "
|
|
120
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
121
121
|
}, undefined>]>]>]>;
|
|
122
122
|
export type TransformResponseMessage = v.Infer<typeof transformResponseMessageSchema>;
|
|
123
123
|
//# sourceMappingURL=custom-queries.d.ts.map
|
|
@@ -55,7 +55,7 @@ export declare const pushFailedBodySchema: v.UnionType<[v.ObjectType<Omit<{
|
|
|
55
55
|
message: v.Type<string>;
|
|
56
56
|
}, "reason" | "origin"> & {
|
|
57
57
|
origin: v.Type<"zeroCache">;
|
|
58
|
-
reason: v.Type<"parse" | "
|
|
58
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
59
59
|
}, undefined>]>;
|
|
60
60
|
export declare const transformFailedBodySchema: v.UnionType<[v.ObjectType<Omit<{
|
|
61
61
|
kind: v.Type<"TransformFailed">;
|
|
@@ -91,7 +91,7 @@ export declare const transformFailedBodySchema: v.UnionType<[v.ObjectType<Omit<{
|
|
|
91
91
|
message: v.Type<string>;
|
|
92
92
|
}, "reason" | "origin"> & {
|
|
93
93
|
origin: v.Type<"zeroCache">;
|
|
94
|
-
reason: v.Type<"parse" | "
|
|
94
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
95
95
|
}, undefined>]>;
|
|
96
96
|
export declare const errorBodySchema: v.UnionType<[v.ObjectType<{
|
|
97
97
|
kind: v.Type<"AuthInvalidated" | "ClientNotFound" | "InvalidConnectionRequest" | "InvalidConnectionRequestBaseCookie" | "InvalidConnectionRequestLastMutationID" | "InvalidConnectionRequestClientDeleted" | "InvalidMessage" | "InvalidPush" | "MutationFailed" | "MutationRateLimited" | "Unauthorized" | "VersionNotSupported" | "SchemaVersionNotSupported" | "Internal">;
|
|
@@ -150,7 +150,7 @@ export declare const errorBodySchema: v.UnionType<[v.ObjectType<{
|
|
|
150
150
|
message: v.Type<string>;
|
|
151
151
|
}, "reason" | "origin"> & {
|
|
152
152
|
origin: v.Type<"zeroCache">;
|
|
153
|
-
reason: v.Type<"parse" | "
|
|
153
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
154
154
|
}, undefined>]>, v.UnionType<[v.ObjectType<Omit<{
|
|
155
155
|
kind: v.Type<"TransformFailed">;
|
|
156
156
|
details: v.Optional<import("../../shared/src/json.ts").ReadonlyJSONValue>;
|
|
@@ -185,7 +185,7 @@ export declare const errorBodySchema: v.UnionType<[v.ObjectType<{
|
|
|
185
185
|
message: v.Type<string>;
|
|
186
186
|
}, "reason" | "origin"> & {
|
|
187
187
|
origin: v.Type<"zeroCache">;
|
|
188
|
-
reason: v.Type<"parse" | "
|
|
188
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
189
189
|
}, undefined>]>]>;
|
|
190
190
|
export type BackoffBody = v.Infer<typeof backoffBodySchema>;
|
|
191
191
|
export type PushFailedBody = v.Infer<typeof pushFailedBodySchema>;
|
|
@@ -59,6 +59,6 @@ var inspectErrorDownSchema = inspectBaseDownSchema.extend({
|
|
|
59
59
|
var inspectDownBodySchema = valita_exports.union(inspectQueriesDownSchema, inspectMetricsDownSchema, inspectVersionDownSchema, inspectAuthenticatedDownSchema, inspectAnalyzeQueryDownSchema, inspectErrorDownSchema);
|
|
60
60
|
var inspectDownMessageSchema = valita_exports.tuple([valita_exports.literal("inspect"), inspectDownBodySchema]);
|
|
61
61
|
//#endregion
|
|
62
|
-
export { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectDownMessageSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema };
|
|
62
|
+
export { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectDownMessageSchema, inspectErrorDownSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema };
|
|
63
63
|
|
|
64
64
|
//# sourceMappingURL=inspect-down.js.map
|
|
@@ -73,7 +73,7 @@ export declare const mutateResponseSchema: v.UnionType<[v.ObjectType<{
|
|
|
73
73
|
message: v.Type<string>;
|
|
74
74
|
}, "reason" | "origin"> & {
|
|
75
75
|
origin: v.Type<"zeroCache">;
|
|
76
|
-
reason: v.Type<"parse" | "
|
|
76
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
77
77
|
}, undefined>]>, v.UnionType<[v.ObjectType<{
|
|
78
78
|
mutations: v.ArrayType<v.ObjectType<{
|
|
79
79
|
id: v.ObjectType<{
|
|
@@ -152,7 +152,7 @@ export declare const mutateResponseSchema: v.UnionType<[v.ObjectType<{
|
|
|
152
152
|
message: v.Type<string>;
|
|
153
153
|
}, "reason" | "origin"> & {
|
|
154
154
|
origin: v.Type<"zeroCache">;
|
|
155
|
-
reason: v.Type<"parse" | "
|
|
155
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
156
156
|
}, undefined>]>]>]>;
|
|
157
157
|
export type MutateResponse = v.Infer<typeof mutateResponseSchema>;
|
|
158
158
|
/**
|
|
@@ -307,7 +307,7 @@ export declare const pushResponseSchema: v.UnionType<[v.UnionType<[v.ObjectType<
|
|
|
307
307
|
message: v.Type<string>;
|
|
308
308
|
}, "reason" | "origin"> & {
|
|
309
309
|
origin: v.Type<"zeroCache">;
|
|
310
|
-
reason: v.Type<"parse" | "
|
|
310
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
311
311
|
}, undefined>]>]>;
|
|
312
312
|
export declare const pushResponseMessageSchema: v.TupleType<[v.Type<"pushResponse">, v.UnionType<[v.ObjectType<{
|
|
313
313
|
mutations: v.ArrayType<v.ObjectType<{
|
|
@@ -102,7 +102,7 @@ export declare const queryResponseSchema: v.UnionType<[v.ObjectType<{
|
|
|
102
102
|
message: v.Type<string>;
|
|
103
103
|
}, "reason" | "origin"> & {
|
|
104
104
|
origin: v.Type<"zeroCache">;
|
|
105
|
-
reason: v.Type<"parse" | "
|
|
105
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
106
106
|
}, undefined>]>, v.UnionType<[v.TupleType<[v.Type<"transformed">, v.ArrayType<v.UnionType<[v.ObjectType<{
|
|
107
107
|
id: v.Type<string>;
|
|
108
108
|
name: v.Type<string>;
|
|
@@ -144,7 +144,7 @@ export declare const queryResponseSchema: v.UnionType<[v.ObjectType<{
|
|
|
144
144
|
message: v.Type<string>;
|
|
145
145
|
}, "reason" | "origin"> & {
|
|
146
146
|
origin: v.Type<"zeroCache">;
|
|
147
|
-
reason: v.Type<"parse" | "
|
|
147
|
+
reason: v.Type<"parse" | "timeout" | "internal">;
|
|
148
148
|
}, undefined>]>]>]>]>;
|
|
149
149
|
export type QueryResponse = v.Infer<typeof queryResponseSchema>;
|
|
150
150
|
//# sourceMappingURL=query-server.d.ts.map
|