@rocicorp/zero 1.4.0-canary.1 → 1.4.0-canary.3
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/analyze-query/src/analyze-cli.d.ts +0 -1
- package/out/analyze-query/src/analyze-cli.d.ts.map +1 -1
- package/out/analyze-query/src/analyze-cli.js +0 -1
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +11 -10
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +1 -1
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +34 -21
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/write.js +1 -2
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +7 -1
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/with-transactions.d.ts.map +1 -1
- package/out/replicache/src/with-transactions.js +16 -2
- package/out/replicache/src/with-transactions.js.map +1 -1
- package/out/shared/src/btree-set.d.ts +6 -0
- package/out/shared/src/btree-set.d.ts.map +1 -1
- package/out/shared/src/btree-set.js +34 -0
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/zero/package.js +8 -2
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/adapters/kysely.d.ts +2 -0
- package/out/zero/src/adapters/kysely.d.ts.map +1 -0
- package/out/zero/src/adapters/kysely.js +2 -0
- package/out/zero/src/zero.js +2 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +14 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +18 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +35 -3
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +8 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.d.ts +1 -1
- package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js +13 -13
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts +36 -6
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js +55 -10
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.d.ts.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js +3 -3
- package/out/zero-cache/src/scripts/decommission.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +4 -5
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +6 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +1 -4
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +35 -0
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +2 -8
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts +1 -0
- package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js +2 -1
- package/out/zero-cache/src/server/worker-urls.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 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +8 -1
- 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 +31 -18
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +48 -47
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +6 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +64 -22
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +2 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +10 -3
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js +49 -0
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -0
- package/out/zero-cache/src/services/statz.js +3 -3
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +3 -6
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +1 -0
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +16 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +19 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +8 -2
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +50 -10
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +4 -7
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts +17 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.js +29 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js +5 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +105 -0
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +8 -4
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +18 -28
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +1 -1
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +8 -2
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/timeout.d.ts +11 -0
- package/out/zero-cache/src/types/timeout.d.ts.map +1 -0
- package/out/zero-cache/src/types/timeout.js +26 -0
- package/out/zero-cache/src/types/timeout.js.map +1 -0
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/mod.d.ts +1 -0
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-client/src/mod.js +1 -0
- package/out/zero-react/src/zero.js +1 -0
- package/out/zero-server/src/adapters/kysely.d.ts +69 -0
- package/out/zero-server/src/adapters/kysely.d.ts.map +1 -0
- package/out/zero-server/src/adapters/kysely.js +82 -0
- package/out/zero-server/src/adapters/kysely.js.map +1 -0
- package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-solid/src/zero.js +1 -0
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +3 -3
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/validate-input.d.ts +8 -0
- package/out/zql/src/query/validate-input.d.ts.map +1 -1
- package/out/zql/src/query/validate-input.js +15 -2
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/query-builder.js +19 -7
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/package.json +10 -2
- package/out/analyze-query/src/explain-queries.d.ts +0 -4
- package/out/analyze-query/src/explain-queries.d.ts.map +0 -1
- package/out/analyze-query/src/explain-queries.js +0 -13
- package/out/analyze-query/src/explain-queries.js.map +0 -1
- package/out/otel/src/test-log-config.d.ts +0 -8
- package/out/otel/src/test-log-config.d.ts.map +0 -1
- package/out/otel/src/test-log-config.js +0 -12
- package/out/otel/src/test-log-config.js.map +0 -1
|
@@ -142,7 +142,8 @@ var postgresTypeConfig = ({ sendStringAsJson } = {}) => ({ types: {
|
|
|
142
142
|
parse: (x) => Number(x)
|
|
143
143
|
}
|
|
144
144
|
} });
|
|
145
|
-
function pgClient(lc, connectionURI, options, opts) {
|
|
145
|
+
function pgClient(lc, connectionURI, applicationName, options, opts) {
|
|
146
|
+
applicationName = `zero-${applicationName}`;
|
|
146
147
|
const onnotice = (n) => {
|
|
147
148
|
switch (n.severity) {
|
|
148
149
|
case "NOTICE": return;
|
|
@@ -165,12 +166,17 @@ function pgClient(lc, connectionURI, options, opts) {
|
|
|
165
166
|
else if (sslFlag === "no-verify") ssl = { rejectUnauthorized: false };
|
|
166
167
|
else ssl = sslFlag;
|
|
167
168
|
const maxLifetimeSeconds = randInt(300, 600);
|
|
169
|
+
const providedConnection = options?.connection ?? {};
|
|
168
170
|
return postgres(connectionURI, {
|
|
169
171
|
...postgresTypeConfig(opts),
|
|
170
172
|
onnotice,
|
|
171
173
|
["max_lifetime"]: maxLifetimeSeconds,
|
|
172
174
|
ssl,
|
|
173
|
-
...options
|
|
175
|
+
...options,
|
|
176
|
+
connection: {
|
|
177
|
+
...providedConnection,
|
|
178
|
+
["application_name"]: applicationName
|
|
179
|
+
}
|
|
174
180
|
});
|
|
175
181
|
}
|
|
176
182
|
Object.freeze(Object.fromEntries(Object.entries(OID).map(([name, oid]) => [oid, name.startsWith("_") ? `${name.substring(1)}[]` : name])));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {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 {\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\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\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?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(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\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {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 {\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\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 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?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(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 return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\n connection: {\n ...providedConnection,\n ['application_name']: applicationName,\n },\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"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves to the the string `"timed-out"` if `timeoutMs` elapses before
|
|
3
|
+
* the specified `promise` resolves.
|
|
4
|
+
*/
|
|
5
|
+
export declare function orTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | 'timed-out'>;
|
|
6
|
+
/**
|
|
7
|
+
* Resolves to the specified `timeoutValue` if `timeoutMs` elapses before
|
|
8
|
+
* the specified `promise` resolves.
|
|
9
|
+
*/
|
|
10
|
+
export declare function orTimeoutWith<T, U>(promise: Promise<T>, timeoutMs: number, timeoutValue: U): Promise<T | U>;
|
|
11
|
+
//# sourceMappingURL=timeout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/timeout.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,GAAG,WAAW,CAAC,CAE1B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,CAAC,EACtC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,CAAC,GACd,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAQhB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sleepWithAbort } from "../../../shared/src/sleep.js";
|
|
2
|
+
//#region ../zero-cache/src/types/timeout.ts
|
|
3
|
+
/**
|
|
4
|
+
* Resolves to the the string `"timed-out"` if `timeoutMs` elapses before
|
|
5
|
+
* the specified `promise` resolves.
|
|
6
|
+
*/
|
|
7
|
+
function orTimeout(promise, timeoutMs) {
|
|
8
|
+
return orTimeoutWith(promise, timeoutMs, "timed-out");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves to the specified `timeoutValue` if `timeoutMs` elapses before
|
|
12
|
+
* the specified `promise` resolves.
|
|
13
|
+
*/
|
|
14
|
+
async function orTimeoutWith(promise, timeoutMs, timeoutValue) {
|
|
15
|
+
const ac = new AbortController();
|
|
16
|
+
const [timeout] = sleepWithAbort(timeoutMs, ac.signal);
|
|
17
|
+
try {
|
|
18
|
+
return await Promise.race([promise, timeout.then(() => timeoutValue)]);
|
|
19
|
+
} finally {
|
|
20
|
+
ac.abort();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { orTimeout };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=timeout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeout.js","names":[],"sources":["../../../../../zero-cache/src/types/timeout.ts"],"sourcesContent":["import {sleepWithAbort} from '../../../shared/src/sleep.ts';\n\n/**\n * Resolves to the the string `\"timed-out\"` if `timeoutMs` elapses before\n * the specified `promise` resolves.\n */\nexport function orTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n): Promise<T | 'timed-out'> {\n return orTimeoutWith(promise, timeoutMs, 'timed-out');\n}\n\n/**\n * Resolves to the specified `timeoutValue` if `timeoutMs` elapses before\n * the specified `promise` resolves.\n */\nexport async function orTimeoutWith<T, U>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutValue: U,\n): Promise<T | U> {\n const ac = new AbortController();\n const [timeout] = sleepWithAbort(timeoutMs, ac.signal);\n try {\n return await Promise.race([promise, timeout.then(() => timeoutValue)]);\n } finally {\n ac.abort();\n }\n}\n"],"mappings":";;;;;;AAMA,SAAgB,UACd,SACA,WAC0B;AAC1B,QAAO,cAAc,SAAS,WAAW,YAAY;;;;;;AAOvD,eAAsB,cACpB,SACA,WACA,cACgB;CAChB,MAAM,KAAK,IAAI,iBAAiB;CAChC,MAAM,CAAC,WAAW,eAAe,WAAW,GAAG,OAAO;AACtD,KAAI;AACF,SAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,WAAW,aAAa,CAAC,CAAC;WAC9D;AACR,KAAG,OAAO"}
|
|
@@ -93,12 +93,12 @@ var Connection = class {
|
|
|
93
93
|
try {
|
|
94
94
|
msg = parse(JSON.parse(data), upstreamSchema);
|
|
95
95
|
} catch (e) {
|
|
96
|
-
|
|
97
|
-
this.#closeWithError({
|
|
96
|
+
const errorBody = {
|
|
98
97
|
kind: InvalidMessage,
|
|
99
98
|
message: String(e),
|
|
100
99
|
origin: ZeroCache
|
|
101
|
-
}
|
|
100
|
+
};
|
|
101
|
+
this.#closeWithError(errorBody, new ProtocolErrorWithLevel(errorBody, "warn"));
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","names":["#ws","#wsID","#protocolVersion","#lc","#onClose","#messageHandler","#downstreamMsgTimer","#handleClose","#handleError","#proxyInbound","#maybeSendPong","#closeWithError","#closed","#viewSyncerOutboundStream","#pusherOutboundStream","#handleMessage","#handleMessageResult","#closeWithThrown","#proxyOutbound","#lastDownstreamMsgTime"],"sources":["../../../../../zero-cache/src/workers/connection.ts"],"sourcesContent":["import {pipeline, Readable, Writable} from 'node:stream';\nimport type {LogContext, LogLevel} from '@rocicorp/logger';\nimport type {CloseEvent, Data, ErrorEvent} from 'ws';\nimport WebSocket, {createWebSocketStream} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport type {ConnectedMessage} from '../../../zero-protocol/src/connect.ts';\nimport type {Downstream} from '../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport {\n isProtocolError,\n type ProtocolError,\n} from '../../../zero-protocol/src/error.ts';\nimport {\n MIN_SERVER_SUPPORTED_SYNC_PROTOCOL,\n PROTOCOL_VERSION,\n} from '../../../zero-protocol/src/protocol-version.ts';\nimport {upstreamSchema, type Upstream} from '../../../zero-protocol/src/up.ts';\nimport {\n ProtocolErrorWithLevel,\n getLogLevel,\n wrapWithProtocolError,\n} from '../types/error-with-level.ts';\nimport type {Source} from '../types/streams.ts';\nimport type {ConnectParams} from './connect-params.ts';\n\nexport type HandlerResult =\n | {\n type: 'ok';\n }\n | {\n type: 'fatal';\n error: ErrorBody;\n }\n | {\n type: 'transient';\n errors: ErrorBody[];\n }\n | StreamResult;\n\nexport type StreamResult = {\n type: 'stream';\n source: 'viewSyncer' | 'pusher';\n stream: Source<Downstream>;\n};\n\nexport interface MessageHandler {\n handleMessage(msg: Upstream): Promise<HandlerResult[]>;\n}\n\n// Ensures that a downstream message is sent at least every interval, sending a\n// 'pong' if necessary. This is set to be slightly longer than the client-side\n// PING_INTERVAL of 5 seconds, so that in the common case, 'pong's are sent in\n// response to client-initiated 'ping's. However, if the inbound stream is\n// backed up because a command is taking a long time to process, the pings\n// will be stuck in the queue (i.e. back-pressured), in which case pongs will\n// be manually sent to notify the client of server liveness.\n//\n// This is equivalent to what is done for Postgres keepalives on the\n// replication stream (which can similarly be back-pressured):\n// https://github.com/rocicorp/mono/blob/f98cb369a2dbb15650328859c732db358f187ef0/packages/zero-cache/src/services/change-source/pg/logical-replication/stream.ts#L21\nconst DOWNSTREAM_MSG_INTERVAL_MS = 6_000;\n\n/**\n * Represents a connection between the client and server.\n *\n * Handles incoming messages on the connection and dispatches\n * them to the correct service.\n *\n * Listens to the ViewSyncer and sends messages to the client.\n */\nexport class Connection {\n readonly #ws: WebSocket;\n readonly #wsID: string;\n readonly #protocolVersion: number;\n readonly #lc: LogContext;\n readonly #onClose: () => void;\n readonly #messageHandler: MessageHandler;\n readonly #downstreamMsgTimer: NodeJS.Timeout | undefined;\n\n #viewSyncerOutboundStream: Source<Downstream> | undefined;\n #pusherOutboundStream: Source<Downstream> | undefined;\n #closed = false;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n ws: WebSocket,\n messageHandler: MessageHandler,\n onClose: () => void,\n ) {\n const {clientGroupID, clientID, wsID, protocolVersion} = connectParams;\n this.#messageHandler = messageHandler;\n\n this.#ws = ws;\n this.#wsID = wsID;\n this.#protocolVersion = protocolVersion;\n\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#lc.debug?.('new connection');\n this.#onClose = onClose;\n\n this.#ws.addEventListener('close', this.#handleClose);\n this.#ws.addEventListener('error', this.#handleError);\n\n this.#proxyInbound();\n this.#downstreamMsgTimer = setInterval(\n this.#maybeSendPong,\n DOWNSTREAM_MSG_INTERVAL_MS / 2,\n );\n }\n\n /**\n * Checks the protocol version and errors for unsupported protocols,\n * sending the initial `connected` response on success.\n *\n * This is early in the connection lifecycle because {@link #handleMessage}\n * will only parse messages with schema(s) of supported protocol versions.\n */\n init(): boolean {\n if (\n this.#protocolVersion > PROTOCOL_VERSION ||\n this.#protocolVersion < MIN_SERVER_SUPPORTED_SYNC_PROTOCOL\n ) {\n this.#closeWithError({\n kind: ErrorKind.VersionNotSupported,\n message: `server is at sync protocol v${PROTOCOL_VERSION} and does not support v${\n this.#protocolVersion\n }. The ${\n this.#protocolVersion > PROTOCOL_VERSION ? 'server' : 'client'\n } must be updated to a newer release.`,\n origin: ErrorOrigin.ZeroCache,\n });\n } else {\n const connectedMessage: ConnectedMessage = [\n 'connected',\n {wsid: this.#wsID, timestamp: Date.now()},\n ];\n this.send(connectedMessage, 'ignore-backpressure');\n return true;\n }\n return false;\n }\n\n close(reason: string, ...args: unknown[]) {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n this.#lc.info?.(`closing connection: ${reason}`, ...args);\n this.#ws.removeEventListener('close', this.#handleClose);\n this.#ws.removeEventListener('error', this.#handleError);\n this.#viewSyncerOutboundStream?.cancel();\n this.#viewSyncerOutboundStream = undefined;\n this.#pusherOutboundStream?.cancel();\n this.#pusherOutboundStream = undefined;\n this.#onClose();\n if (this.#ws.readyState !== this.#ws.CLOSED) {\n this.#ws.close();\n }\n clearTimeout(this.#downstreamMsgTimer);\n\n // spin down services if we have\n // no more client connections for the client group?\n }\n\n handleInitConnection(initConnectionMsg: string) {\n return this.#handleMessage({data: initConnectionMsg});\n }\n\n #handleMessage = async (event: {data: Data}) => {\n const data = event.data.toString();\n if (this.#closed) {\n this.#lc.debug?.('Ignoring message received after closed', data);\n return;\n }\n\n let msg;\n try {\n const value = JSON.parse(data);\n msg = valita.parse(value, upstreamSchema);\n } catch (e) {\n this.#lc.warn?.(`failed to parse message \"${data}\": ${String(e)}`);\n this.#closeWithError(\n {\n kind: ErrorKind.InvalidMessage,\n message: String(e),\n origin: ErrorOrigin.ZeroCache,\n },\n e,\n );\n return;\n }\n\n try {\n const msgType = msg[0];\n if (msgType === 'ping') {\n this.send(['pong', {}], 'ignore-backpressure');\n return;\n }\n\n const result = await this.#messageHandler.handleMessage(msg);\n for (const r of result) {\n this.#handleMessageResult(r);\n }\n } catch (e) {\n this.#closeWithThrown(e);\n }\n };\n\n #handleMessageResult(result: HandlerResult): void {\n switch (result.type) {\n case 'fatal':\n this.#closeWithError(result.error);\n break;\n case 'ok':\n break;\n case 'stream': {\n switch (result.source) {\n case 'viewSyncer':\n assert(\n this.#viewSyncerOutboundStream === undefined,\n 'Outbound stream already set for this connection!',\n );\n this.#viewSyncerOutboundStream = result.stream;\n break;\n case 'pusher':\n assert(\n this.#pusherOutboundStream === undefined,\n 'Outbound stream already set for this connection!',\n );\n this.#pusherOutboundStream = result.stream;\n break;\n }\n this.#proxyOutbound(result.stream);\n break;\n }\n case 'transient': {\n for (const error of result.errors) {\n this.sendError(error);\n }\n }\n }\n }\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.close('WebSocket close event', {code, reason, wasClean});\n };\n\n #handleError = (e: ErrorEvent) => {\n this.#lc.error?.('WebSocket error event', e.message, e.error);\n };\n\n #proxyInbound() {\n pipeline(\n createWebSocketStream(this.#ws),\n new Writable({\n write: (data, _encoding, callback) => {\n this.#handleMessage({data}).then(() => callback(), callback);\n },\n }),\n // The done callback is not used, as #handleClose and #handleError,\n // configured on the underlying WebSocket, provide more complete\n // information.\n () => {},\n );\n }\n\n #proxyOutbound(outboundStream: Source<Downstream>) {\n // Note: createWebSocketStream() is avoided here in order to control\n // exception handling with #closeWithThrown(). If the Writable\n // from createWebSocketStream() were instead used, exceptions\n // from the outboundStream result in the Writable closing the\n // the websocket before the error message can be sent.\n pipeline(\n Readable.from(outboundStream),\n new Writable({\n objectMode: true,\n write: (downstream: Downstream, _encoding, callback) =>\n this.send(downstream, callback),\n }),\n e =>\n e\n ? this.#closeWithThrown(e)\n : this.close(`downstream closed by ViewSyncer`),\n );\n }\n\n #closeWithThrown(e: unknown) {\n const errorBody =\n findProtocolError(e)?.errorBody ?? wrapWithProtocolError(e).errorBody;\n\n this.#closeWithError(errorBody, e);\n }\n\n #closeWithError(errorBody: ErrorBody, thrown?: unknown) {\n this.sendError(errorBody, thrown);\n this.close(\n `${errorBody.kind} (${errorBody.origin}): ${errorBody.message}`,\n errorBody,\n );\n }\n\n #lastDownstreamMsgTime = Date.now();\n\n #maybeSendPong = () => {\n if (Date.now() - this.#lastDownstreamMsgTime > DOWNSTREAM_MSG_INTERVAL_MS) {\n this.#lc.debug?.('manually sending pong');\n this.send(['pong', {}], 'ignore-backpressure');\n }\n };\n\n send(\n data: Downstream,\n callback: ((err?: Error | null) => void) | 'ignore-backpressure',\n ) {\n this.#lastDownstreamMsgTime = Date.now();\n return send(this.#lc, this.#ws, data, callback);\n }\n\n sendError(errorBody: ErrorBody, thrown?: unknown) {\n sendError(this.#lc, this.#ws, errorBody, thrown);\n }\n}\n\nexport type WebSocketLike = Pick<WebSocket, 'readyState'> & {\n send(data: string, cb?: (err?: Error) => void): void;\n};\n\n// Exported for testing purposes.\nexport function send(\n lc: LogContext,\n ws: WebSocketLike,\n data: Downstream,\n callback: ((err?: Error | null) => void) | 'ignore-backpressure',\n) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(\n JSON.stringify(data),\n callback === 'ignore-backpressure' ? undefined : callback,\n );\n } else {\n lc.debug?.(`Dropping outbound message on ws (state: ${ws.readyState})`, {\n dropped: data,\n });\n if (callback !== 'ignore-backpressure') {\n callback(\n new ProtocolErrorWithLevel(\n {\n kind: ErrorKind.Internal,\n message: 'WebSocket closed',\n origin: ErrorOrigin.ZeroCache,\n },\n 'info',\n ),\n );\n }\n }\n}\n\nexport function sendError(\n lc: LogContext,\n ws: WebSocket,\n errorBody: ErrorBody,\n thrown?: unknown,\n) {\n lc = lc.withContext('errorKind', errorBody.kind);\n\n let logLevel: LogLevel;\n\n // If the thrown error is a ProtocolErrorWithLevel, its explicit logLevel takes precedence\n if (thrown instanceof ProtocolErrorWithLevel) {\n logLevel = thrown.logLevel;\n }\n // Errors with errno or transient socket codes are low-level, transient I/O issues\n // (e.g., EPIPE, ECONNRESET) and should be warnings, not errors\n else if (\n hasErrno(thrown) ||\n hasTransientSocketCode(thrown) ||\n isTransientSocketMessage(errorBody.message)\n ) {\n logLevel = 'warn';\n }\n // Fallback: check errorBody.kind for errors that weren't thrown as ProtocolErrorWithLevel\n else if (\n errorBody.kind === ErrorKind.ClientNotFound ||\n errorBody.kind === ErrorKind.TransformFailed\n ) {\n logLevel = 'warn';\n } else {\n logLevel = thrown ? getLogLevel(thrown) : 'info';\n }\n\n lc[logLevel]?.('Sending error on WebSocket', errorBody, thrown ?? '');\n send(lc, ws, ['error', errorBody], 'ignore-backpressure');\n}\n\nexport function findProtocolError(error: unknown): ProtocolError | undefined {\n if (isProtocolError(error)) {\n return error;\n }\n if (error instanceof Error && error.cause) {\n return findProtocolError(error.cause);\n }\n return undefined;\n}\n\nfunction hasErrno(error: unknown): boolean {\n return Boolean(\n error &&\n typeof error === 'object' &&\n 'errno' in error &&\n typeof (error as {errno: unknown}).errno !== 'undefined',\n );\n}\n\n// System error codes that indicate transient socket conditions.\n// These are checked via the `code` property on errors.\nconst TRANSIENT_SOCKET_ERROR_CODES = new Set([\n 'EPIPE',\n 'ECONNRESET',\n 'ECANCELED',\n]);\n\n// Error messages that indicate transient socket conditions but don't have\n// standard error codes (e.g., WebSocket library errors).\nconst TRANSIENT_SOCKET_MESSAGE_PATTERNS = [\n 'socket was closed while data was being compressed',\n];\n\nfunction hasTransientSocketCode(error: unknown): boolean {\n if (!error || typeof error !== 'object') {\n return false;\n }\n const maybeCode =\n 'code' in error ? String((error as {code?: unknown}).code) : undefined;\n return Boolean(\n maybeCode && TRANSIENT_SOCKET_ERROR_CODES.has(maybeCode.toUpperCase()),\n );\n}\n\nfunction isTransientSocketMessage(message: string | undefined): boolean {\n if (!message) {\n return false;\n }\n const lower = message.toLowerCase();\n return TRANSIENT_SOCKET_MESSAGE_PATTERNS.some(pattern =>\n lower.includes(pattern),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection.js","names":["#ws","#wsID","#protocolVersion","#lc","#onClose","#messageHandler","#downstreamMsgTimer","#handleClose","#handleError","#proxyInbound","#maybeSendPong","#closeWithError","#closed","#viewSyncerOutboundStream","#pusherOutboundStream","#handleMessage","#handleMessageResult","#closeWithThrown","#proxyOutbound","#lastDownstreamMsgTime"],"sources":["../../../../../zero-cache/src/workers/connection.ts"],"sourcesContent":["import {pipeline, Readable, Writable} from 'node:stream';\nimport type {LogContext, LogLevel} from '@rocicorp/logger';\nimport type {CloseEvent, Data, ErrorEvent} from 'ws';\nimport WebSocket, {createWebSocketStream} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport type {ConnectedMessage} from '../../../zero-protocol/src/connect.ts';\nimport type {Downstream} from '../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport {\n isProtocolError,\n type ProtocolError,\n} from '../../../zero-protocol/src/error.ts';\nimport {\n MIN_SERVER_SUPPORTED_SYNC_PROTOCOL,\n PROTOCOL_VERSION,\n} from '../../../zero-protocol/src/protocol-version.ts';\nimport {upstreamSchema, type Upstream} from '../../../zero-protocol/src/up.ts';\nimport {\n ProtocolErrorWithLevel,\n getLogLevel,\n wrapWithProtocolError,\n} from '../types/error-with-level.ts';\nimport type {Source} from '../types/streams.ts';\nimport type {ConnectParams} from './connect-params.ts';\n\nexport type HandlerResult =\n | {\n type: 'ok';\n }\n | {\n type: 'fatal';\n error: ErrorBody;\n }\n | {\n type: 'transient';\n errors: ErrorBody[];\n }\n | StreamResult;\n\nexport type StreamResult = {\n type: 'stream';\n source: 'viewSyncer' | 'pusher';\n stream: Source<Downstream>;\n};\n\nexport interface MessageHandler {\n handleMessage(msg: Upstream): Promise<HandlerResult[]>;\n}\n\n// Ensures that a downstream message is sent at least every interval, sending a\n// 'pong' if necessary. This is set to be slightly longer than the client-side\n// PING_INTERVAL of 5 seconds, so that in the common case, 'pong's are sent in\n// response to client-initiated 'ping's. However, if the inbound stream is\n// backed up because a command is taking a long time to process, the pings\n// will be stuck in the queue (i.e. back-pressured), in which case pongs will\n// be manually sent to notify the client of server liveness.\n//\n// This is equivalent to what is done for Postgres keepalives on the\n// replication stream (which can similarly be back-pressured):\n// https://github.com/rocicorp/mono/blob/f98cb369a2dbb15650328859c732db358f187ef0/packages/zero-cache/src/services/change-source/pg/logical-replication/stream.ts#L21\nconst DOWNSTREAM_MSG_INTERVAL_MS = 6_000;\n\n/**\n * Represents a connection between the client and server.\n *\n * Handles incoming messages on the connection and dispatches\n * them to the correct service.\n *\n * Listens to the ViewSyncer and sends messages to the client.\n */\nexport class Connection {\n readonly #ws: WebSocket;\n readonly #wsID: string;\n readonly #protocolVersion: number;\n readonly #lc: LogContext;\n readonly #onClose: () => void;\n readonly #messageHandler: MessageHandler;\n readonly #downstreamMsgTimer: NodeJS.Timeout | undefined;\n\n #viewSyncerOutboundStream: Source<Downstream> | undefined;\n #pusherOutboundStream: Source<Downstream> | undefined;\n #closed = false;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n ws: WebSocket,\n messageHandler: MessageHandler,\n onClose: () => void,\n ) {\n const {clientGroupID, clientID, wsID, protocolVersion} = connectParams;\n this.#messageHandler = messageHandler;\n\n this.#ws = ws;\n this.#wsID = wsID;\n this.#protocolVersion = protocolVersion;\n\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#lc.debug?.('new connection');\n this.#onClose = onClose;\n\n this.#ws.addEventListener('close', this.#handleClose);\n this.#ws.addEventListener('error', this.#handleError);\n\n this.#proxyInbound();\n this.#downstreamMsgTimer = setInterval(\n this.#maybeSendPong,\n DOWNSTREAM_MSG_INTERVAL_MS / 2,\n );\n }\n\n /**\n * Checks the protocol version and errors for unsupported protocols,\n * sending the initial `connected` response on success.\n *\n * This is early in the connection lifecycle because {@link #handleMessage}\n * will only parse messages with schema(s) of supported protocol versions.\n */\n init(): boolean {\n if (\n this.#protocolVersion > PROTOCOL_VERSION ||\n this.#protocolVersion < MIN_SERVER_SUPPORTED_SYNC_PROTOCOL\n ) {\n this.#closeWithError({\n kind: ErrorKind.VersionNotSupported,\n message: `server is at sync protocol v${PROTOCOL_VERSION} and does not support v${\n this.#protocolVersion\n }. The ${\n this.#protocolVersion > PROTOCOL_VERSION ? 'server' : 'client'\n } must be updated to a newer release.`,\n origin: ErrorOrigin.ZeroCache,\n });\n } else {\n const connectedMessage: ConnectedMessage = [\n 'connected',\n {wsid: this.#wsID, timestamp: Date.now()},\n ];\n this.send(connectedMessage, 'ignore-backpressure');\n return true;\n }\n return false;\n }\n\n close(reason: string, ...args: unknown[]) {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n this.#lc.info?.(`closing connection: ${reason}`, ...args);\n this.#ws.removeEventListener('close', this.#handleClose);\n this.#ws.removeEventListener('error', this.#handleError);\n this.#viewSyncerOutboundStream?.cancel();\n this.#viewSyncerOutboundStream = undefined;\n this.#pusherOutboundStream?.cancel();\n this.#pusherOutboundStream = undefined;\n this.#onClose();\n if (this.#ws.readyState !== this.#ws.CLOSED) {\n this.#ws.close();\n }\n clearTimeout(this.#downstreamMsgTimer);\n\n // spin down services if we have\n // no more client connections for the client group?\n }\n\n handleInitConnection(initConnectionMsg: string) {\n return this.#handleMessage({data: initConnectionMsg});\n }\n\n #handleMessage = async (event: {data: Data}) => {\n const data = event.data.toString();\n if (this.#closed) {\n this.#lc.debug?.('Ignoring message received after closed', data);\n return;\n }\n\n let msg;\n try {\n const value = JSON.parse(data);\n msg = valita.parse(value, upstreamSchema);\n } catch (e) {\n const errorBody = {\n kind: ErrorKind.InvalidMessage,\n message: String(e),\n origin: ErrorOrigin.ZeroCache,\n } as const;\n this.#closeWithError(\n errorBody,\n new ProtocolErrorWithLevel(errorBody, 'warn'),\n );\n return;\n }\n\n try {\n const msgType = msg[0];\n if (msgType === 'ping') {\n this.send(['pong', {}], 'ignore-backpressure');\n return;\n }\n\n const result = await this.#messageHandler.handleMessage(msg);\n for (const r of result) {\n this.#handleMessageResult(r);\n }\n } catch (e) {\n this.#closeWithThrown(e);\n }\n };\n\n #handleMessageResult(result: HandlerResult): void {\n switch (result.type) {\n case 'fatal':\n this.#closeWithError(result.error);\n break;\n case 'ok':\n break;\n case 'stream': {\n switch (result.source) {\n case 'viewSyncer':\n assert(\n this.#viewSyncerOutboundStream === undefined,\n 'Outbound stream already set for this connection!',\n );\n this.#viewSyncerOutboundStream = result.stream;\n break;\n case 'pusher':\n assert(\n this.#pusherOutboundStream === undefined,\n 'Outbound stream already set for this connection!',\n );\n this.#pusherOutboundStream = result.stream;\n break;\n }\n this.#proxyOutbound(result.stream);\n break;\n }\n case 'transient': {\n for (const error of result.errors) {\n this.sendError(error);\n }\n }\n }\n }\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.close('WebSocket close event', {code, reason, wasClean});\n };\n\n #handleError = (e: ErrorEvent) => {\n this.#lc.error?.('WebSocket error event', e.message, e.error);\n };\n\n #proxyInbound() {\n pipeline(\n createWebSocketStream(this.#ws),\n new Writable({\n write: (data, _encoding, callback) => {\n this.#handleMessage({data}).then(() => callback(), callback);\n },\n }),\n // The done callback is not used, as #handleClose and #handleError,\n // configured on the underlying WebSocket, provide more complete\n // information.\n () => {},\n );\n }\n\n #proxyOutbound(outboundStream: Source<Downstream>) {\n // Note: createWebSocketStream() is avoided here in order to control\n // exception handling with #closeWithThrown(). If the Writable\n // from createWebSocketStream() were instead used, exceptions\n // from the outboundStream result in the Writable closing the\n // the websocket before the error message can be sent.\n pipeline(\n Readable.from(outboundStream),\n new Writable({\n objectMode: true,\n write: (downstream: Downstream, _encoding, callback) =>\n this.send(downstream, callback),\n }),\n e =>\n e\n ? this.#closeWithThrown(e)\n : this.close(`downstream closed by ViewSyncer`),\n );\n }\n\n #closeWithThrown(e: unknown) {\n const errorBody =\n findProtocolError(e)?.errorBody ?? wrapWithProtocolError(e).errorBody;\n\n this.#closeWithError(errorBody, e);\n }\n\n #closeWithError(errorBody: ErrorBody, thrown?: unknown) {\n this.sendError(errorBody, thrown);\n this.close(\n `${errorBody.kind} (${errorBody.origin}): ${errorBody.message}`,\n errorBody,\n );\n }\n\n #lastDownstreamMsgTime = Date.now();\n\n #maybeSendPong = () => {\n if (Date.now() - this.#lastDownstreamMsgTime > DOWNSTREAM_MSG_INTERVAL_MS) {\n this.#lc.debug?.('manually sending pong');\n this.send(['pong', {}], 'ignore-backpressure');\n }\n };\n\n send(\n data: Downstream,\n callback: ((err?: Error | null) => void) | 'ignore-backpressure',\n ) {\n this.#lastDownstreamMsgTime = Date.now();\n return send(this.#lc, this.#ws, data, callback);\n }\n\n sendError(errorBody: ErrorBody, thrown?: unknown) {\n sendError(this.#lc, this.#ws, errorBody, thrown);\n }\n}\n\nexport type WebSocketLike = Pick<WebSocket, 'readyState'> & {\n send(data: string, cb?: (err?: Error) => void): void;\n};\n\n// Exported for testing purposes.\nexport function send(\n lc: LogContext,\n ws: WebSocketLike,\n data: Downstream,\n callback: ((err?: Error | null) => void) | 'ignore-backpressure',\n) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(\n JSON.stringify(data),\n callback === 'ignore-backpressure' ? undefined : callback,\n );\n } else {\n lc.debug?.(`Dropping outbound message on ws (state: ${ws.readyState})`, {\n dropped: data,\n });\n if (callback !== 'ignore-backpressure') {\n callback(\n new ProtocolErrorWithLevel(\n {\n kind: ErrorKind.Internal,\n message: 'WebSocket closed',\n origin: ErrorOrigin.ZeroCache,\n },\n 'info',\n ),\n );\n }\n }\n}\n\nexport function sendError(\n lc: LogContext,\n ws: WebSocket,\n errorBody: ErrorBody,\n thrown?: unknown,\n) {\n lc = lc.withContext('errorKind', errorBody.kind);\n\n let logLevel: LogLevel;\n\n // If the thrown error is a ProtocolErrorWithLevel, its explicit logLevel takes precedence\n if (thrown instanceof ProtocolErrorWithLevel) {\n logLevel = thrown.logLevel;\n }\n // Errors with errno or transient socket codes are low-level, transient I/O issues\n // (e.g., EPIPE, ECONNRESET) and should be warnings, not errors\n else if (\n hasErrno(thrown) ||\n hasTransientSocketCode(thrown) ||\n isTransientSocketMessage(errorBody.message)\n ) {\n logLevel = 'warn';\n }\n // Fallback: check errorBody.kind for errors that weren't thrown as ProtocolErrorWithLevel\n else if (\n errorBody.kind === ErrorKind.ClientNotFound ||\n errorBody.kind === ErrorKind.TransformFailed\n ) {\n logLevel = 'warn';\n } else {\n logLevel = thrown ? getLogLevel(thrown) : 'info';\n }\n\n lc[logLevel]?.('Sending error on WebSocket', errorBody, thrown ?? '');\n send(lc, ws, ['error', errorBody], 'ignore-backpressure');\n}\n\nexport function findProtocolError(error: unknown): ProtocolError | undefined {\n if (isProtocolError(error)) {\n return error;\n }\n if (error instanceof Error && error.cause) {\n return findProtocolError(error.cause);\n }\n return undefined;\n}\n\nfunction hasErrno(error: unknown): boolean {\n return Boolean(\n error &&\n typeof error === 'object' &&\n 'errno' in error &&\n typeof (error as {errno: unknown}).errno !== 'undefined',\n );\n}\n\n// System error codes that indicate transient socket conditions.\n// These are checked via the `code` property on errors.\nconst TRANSIENT_SOCKET_ERROR_CODES = new Set([\n 'EPIPE',\n 'ECONNRESET',\n 'ECANCELED',\n]);\n\n// Error messages that indicate transient socket conditions but don't have\n// standard error codes (e.g., WebSocket library errors).\nconst TRANSIENT_SOCKET_MESSAGE_PATTERNS = [\n 'socket was closed while data was being compressed',\n];\n\nfunction hasTransientSocketCode(error: unknown): boolean {\n if (!error || typeof error !== 'object') {\n return false;\n }\n const maybeCode =\n 'code' in error ? String((error as {code?: unknown}).code) : undefined;\n return Boolean(\n maybeCode && TRANSIENT_SOCKET_ERROR_CODES.has(maybeCode.toUpperCase()),\n );\n}\n\nfunction isTransientSocketMessage(message: string | undefined): boolean {\n if (!message) {\n return false;\n }\n const lower = message.toLowerCase();\n return TRANSIENT_SOCKET_MESSAGE_PATTERNS.some(pattern =>\n lower.includes(pattern),\n );\n}\n"],"mappings":"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"}
|
|
@@ -37,6 +37,7 @@ export type { AnyTransaction, Location, ServerTransaction, Transaction, Transact
|
|
|
37
37
|
export { defineMutators, defineMutatorsWithType, getMutator, isMutatorRegistry, mustGetMutator, type AnyMutatorRegistry, type AssertMutatorDefinitions, type EnsureMutatorDefinitions, type MutatorDefinitions, type MutatorRegistry, type ToMutatorTree, } from '../../zql/src/mutate/mutator-registry.ts';
|
|
38
38
|
export { defineMutator, defineMutatorWithType, isMutator, isMutatorDefinition, type MutateRequest, type Mutator, type MutatorDefinition, } from '../../zql/src/mutate/mutator.ts';
|
|
39
39
|
export { createBuilder } from '../../zql/src/query/create-builder.ts';
|
|
40
|
+
export { InputValidationError } from '../../zql/src/query/validate-input.ts';
|
|
40
41
|
export { escapeLike } from '../../zql/src/query/escape-like.ts';
|
|
41
42
|
export type { ExpressionBuilder, ExpressionFactory, } from '../../zql/src/query/expression.ts';
|
|
42
43
|
export { syncedQuery, syncedQueryWithContext, withValidation, type CustomQueryID, type HasParseFn, type ParseFn, type Parser, type QueryFn, type SyncedQuery, } from '../../zql/src/query/named.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../../zero-client/src/mod.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../../zero-client/src/mod.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ import "../../zero-schema/src/builder/relationship-builder.js";
|
|
|
9
9
|
import "../../zero-schema/src/builder/table-builder.js";
|
|
10
10
|
import "../../zero-schema/src/builder/schema-builder.js";
|
|
11
11
|
import "../../zero-schema/src/permissions.js";
|
|
12
|
+
import "../../zql/src/query/validate-input.js";
|
|
12
13
|
import "../../zql/src/mutate/mutator.js";
|
|
13
14
|
import "../../zql/src/mutate/mutator-registry.js";
|
|
14
15
|
import "../../zql/src/query/create-builder.js";
|
|
@@ -9,6 +9,7 @@ import "../../zero-schema/src/builder/relationship-builder.js";
|
|
|
9
9
|
import "../../zero-schema/src/builder/table-builder.js";
|
|
10
10
|
import "../../zero-schema/src/builder/schema-builder.js";
|
|
11
11
|
import "../../zero-schema/src/permissions.js";
|
|
12
|
+
import "../../zql/src/query/validate-input.js";
|
|
12
13
|
import "../../zql/src/mutate/mutator.js";
|
|
13
14
|
import "../../zql/src/mutate/mutator-registry.js";
|
|
14
15
|
import "../../zql/src/query/create-builder.js";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Kysely, Transaction as WrappedKyselyTransaction } from 'kysely';
|
|
2
|
+
import type { Schema } from '../../../zero-types/src/schema.ts';
|
|
3
|
+
import type { DBConnection, DBTransaction } from '../../../zql/src/mutate/custom.ts';
|
|
4
|
+
import { ZQLDatabase } from '../zql-database.ts';
|
|
5
|
+
export type { ZQLDatabase };
|
|
6
|
+
export type KyselyDatabase<TDatabase = unknown> = Kysely<TDatabase>;
|
|
7
|
+
/**
|
|
8
|
+
* Helper type for the wrapped transaction used by Kysely.
|
|
9
|
+
*
|
|
10
|
+
* @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, KyselyTransaction<typeof db>>`.
|
|
11
|
+
*/
|
|
12
|
+
export type KyselyTransaction<TDbOrSchema = KyselyDatabase> = TDbOrSchema extends Kysely<infer TInferredDatabase> ? WrappedKyselyTransaction<TInferredDatabase> : WrappedKyselyTransaction<TDbOrSchema>;
|
|
13
|
+
export declare class KyselyConnection<TDatabase> implements DBConnection<WrappedKyselyTransaction<TDatabase>> {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(client: Kysely<TDatabase>);
|
|
16
|
+
transaction<T>(fn: (tx: DBTransaction<WrappedKyselyTransaction<TDatabase>>) => Promise<T>): Promise<T>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wrap a Postgres-backed Kysely client for Zero ZQL.
|
|
20
|
+
*
|
|
21
|
+
* Provides ZQL querying plus access to the underlying Kysely transaction.
|
|
22
|
+
* Use {@link KyselyTransaction} to type your server mutator transaction.
|
|
23
|
+
*
|
|
24
|
+
* @param schema - Zero schema.
|
|
25
|
+
* @param client - Kysely client.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import {Pool} from 'pg';
|
|
30
|
+
* import {Kysely, PostgresDialect} from 'kysely';
|
|
31
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
32
|
+
* import {zeroKysely} from '@rocicorp/zero/server/adapters/kysely';
|
|
33
|
+
* import {z} from 'zod/mini';
|
|
34
|
+
*
|
|
35
|
+
* interface Database {
|
|
36
|
+
* user: {
|
|
37
|
+
* id: string;
|
|
38
|
+
* name: string | null;
|
|
39
|
+
* status: 'active' | 'inactive';
|
|
40
|
+
* };
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* const db = new Kysely<Database>({
|
|
44
|
+
* dialect: new PostgresDialect({
|
|
45
|
+
* pool: new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!}),
|
|
46
|
+
* }),
|
|
47
|
+
* });
|
|
48
|
+
* const zql = zeroKysely(schema, db);
|
|
49
|
+
*
|
|
50
|
+
* export const serverMutators = defineMutators({
|
|
51
|
+
* user: {
|
|
52
|
+
* create: defineMutator(
|
|
53
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
54
|
+
* async ({tx, args}) => {
|
|
55
|
+
* if (tx.location !== 'server') {
|
|
56
|
+
* throw new Error('Server-only mutator');
|
|
57
|
+
* }
|
|
58
|
+
* await tx.dbTransaction.wrappedTransaction
|
|
59
|
+
* .insertInto('user')
|
|
60
|
+
* .values({id: args.id, name: args.name, status: 'active'})
|
|
61
|
+
* .execute();
|
|
62
|
+
* },
|
|
63
|
+
* ),
|
|
64
|
+
* },
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function zeroKysely<TSchema extends Schema, TDatabase>(schema: TSchema, client: Kysely<TDatabase>): ZQLDatabase<TSchema, WrappedKyselyTransaction<TDatabase>>;
|
|
69
|
+
//# sourceMappingURL=kysely.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/kysely.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,MAAM,EAAE,WAAW,IAAI,wBAAwB,EAAC,MAAM,QAAQ,CAAC;AAG5E,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAE9D,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EAEd,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B,MAAM,MAAM,cAAc,CAAC,SAAS,GAAG,OAAO,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,WAAW,GAAG,cAAc,IACxD,WAAW,SAAS,MAAM,CAAC,MAAM,iBAAiB,CAAC,GAC/C,wBAAwB,CAAC,iBAAiB,CAAC,GAC3C,wBAAwB,CAAC,WAAW,CAAC,CAAC;AAE5C,qBAAa,gBAAgB,CAAC,SAAS,CAAE,YAAW,YAAY,CAC9D,wBAAwB,CAAC,SAAS,CAAC,CACpC;;gBAGa,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC;IAIrC,WAAW,CAAC,CAAC,EACX,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACzE,OAAO,CAAC,CAAC,CAAC;CAKd;AAkCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,EAC1D,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,GACxB,WAAW,CAAC,OAAO,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAE3D"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { executePostgresQuery } from "../pg-query-executor.js";
|
|
2
|
+
import { ZQLDatabase } from "../zql-database.js";
|
|
3
|
+
import { CompiledQuery } from "kysely";
|
|
4
|
+
//#region ../zero-server/src/adapters/kysely.ts
|
|
5
|
+
var KyselyConnection = class {
|
|
6
|
+
#client;
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.#client = client;
|
|
9
|
+
}
|
|
10
|
+
transaction(fn) {
|
|
11
|
+
return this.#client.transaction().execute((kyselyTx) => fn(new KyselyInternalTransaction(kyselyTx)));
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var KyselyInternalTransaction = class {
|
|
15
|
+
wrappedTransaction;
|
|
16
|
+
constructor(kyselyTx) {
|
|
17
|
+
this.wrappedTransaction = kyselyTx;
|
|
18
|
+
}
|
|
19
|
+
runQuery(ast, format, schema, serverSchema) {
|
|
20
|
+
return executePostgresQuery(this, ast, format, schema, serverSchema);
|
|
21
|
+
}
|
|
22
|
+
async query(sql, params) {
|
|
23
|
+
return (await this.wrappedTransaction.executeQuery(CompiledQuery.raw(sql, params))).rows;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a Postgres-backed Kysely client for Zero ZQL.
|
|
28
|
+
*
|
|
29
|
+
* Provides ZQL querying plus access to the underlying Kysely transaction.
|
|
30
|
+
* Use {@link KyselyTransaction} to type your server mutator transaction.
|
|
31
|
+
*
|
|
32
|
+
* @param schema - Zero schema.
|
|
33
|
+
* @param client - Kysely client.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import {Pool} from 'pg';
|
|
38
|
+
* import {Kysely, PostgresDialect} from 'kysely';
|
|
39
|
+
* import {defineMutator, defineMutators} from '@rocicorp/zero';
|
|
40
|
+
* import {zeroKysely} from '@rocicorp/zero/server/adapters/kysely';
|
|
41
|
+
* import {z} from 'zod/mini';
|
|
42
|
+
*
|
|
43
|
+
* interface Database {
|
|
44
|
+
* user: {
|
|
45
|
+
* id: string;
|
|
46
|
+
* name: string | null;
|
|
47
|
+
* status: 'active' | 'inactive';
|
|
48
|
+
* };
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* const db = new Kysely<Database>({
|
|
52
|
+
* dialect: new PostgresDialect({
|
|
53
|
+
* pool: new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!}),
|
|
54
|
+
* }),
|
|
55
|
+
* });
|
|
56
|
+
* const zql = zeroKysely(schema, db);
|
|
57
|
+
*
|
|
58
|
+
* export const serverMutators = defineMutators({
|
|
59
|
+
* user: {
|
|
60
|
+
* create: defineMutator(
|
|
61
|
+
* z.object({id: z.string(), name: z.string()}),
|
|
62
|
+
* async ({tx, args}) => {
|
|
63
|
+
* if (tx.location !== 'server') {
|
|
64
|
+
* throw new Error('Server-only mutator');
|
|
65
|
+
* }
|
|
66
|
+
* await tx.dbTransaction.wrappedTransaction
|
|
67
|
+
* .insertInto('user')
|
|
68
|
+
* .values({id: args.id, name: args.name, status: 'active'})
|
|
69
|
+
* .execute();
|
|
70
|
+
* },
|
|
71
|
+
* ),
|
|
72
|
+
* },
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function zeroKysely(schema, client) {
|
|
77
|
+
return new ZQLDatabase(new KyselyConnection(client), schema);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { KyselyConnection, zeroKysely };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=kysely.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely.js","names":["#client"],"sources":["../../../../../zero-server/src/adapters/kysely.ts"],"sourcesContent":["import {CompiledQuery} from 'kysely';\nimport type {Kysely, Transaction as WrappedKyselyTransaction} from 'kysely';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\nexport type KyselyDatabase<TDatabase = unknown> = Kysely<TDatabase>;\n\n/**\n * Helper type for the wrapped transaction used by Kysely.\n *\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, KyselyTransaction<typeof db>>`.\n */\nexport type KyselyTransaction<TDbOrSchema = KyselyDatabase> =\n TDbOrSchema extends Kysely<infer TInferredDatabase>\n ? WrappedKyselyTransaction<TInferredDatabase>\n : WrappedKyselyTransaction<TDbOrSchema>;\n\nexport class KyselyConnection<TDatabase> implements DBConnection<\n WrappedKyselyTransaction<TDatabase>\n> {\n readonly #client: Kysely<TDatabase>;\n\n constructor(client: Kysely<TDatabase>) {\n this.#client = client;\n }\n\n transaction<T>(\n fn: (tx: DBTransaction<WrappedKyselyTransaction<TDatabase>>) => Promise<T>,\n ): Promise<T> {\n return this.#client\n .transaction()\n .execute(kyselyTx => fn(new KyselyInternalTransaction(kyselyTx)));\n }\n}\n\nclass KyselyInternalTransaction<TDatabase> implements DBTransaction<\n WrappedKyselyTransaction<TDatabase>\n> {\n readonly wrappedTransaction: WrappedKyselyTransaction<TDatabase>;\n\n constructor(kyselyTx: WrappedKyselyTransaction<TDatabase>) {\n this.wrappedTransaction = kyselyTx;\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n\n async query(sql: string, params: unknown[]): Promise<Row[]> {\n const result = await this.wrappedTransaction.executeQuery<Row>(\n CompiledQuery.raw(sql, params),\n );\n return result.rows;\n }\n}\n\n/**\n * Wrap a Postgres-backed Kysely client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying Kysely transaction.\n * Use {@link KyselyTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param client - Kysely client.\n *\n * @example\n * ```ts\n * import {Pool} from 'pg';\n * import {Kysely, PostgresDialect} from 'kysely';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroKysely} from '@rocicorp/zero/server/adapters/kysely';\n * import {z} from 'zod/mini';\n *\n * interface Database {\n * user: {\n * id: string;\n * name: string | null;\n * status: 'active' | 'inactive';\n * };\n * }\n *\n * const db = new Kysely<Database>({\n * dialect: new PostgresDialect({\n * pool: new Pool({connectionString: process.env.ZERO_UPSTREAM_DB!}),\n * }),\n * });\n * const zql = zeroKysely(schema, db);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction\n * .insertInto('user')\n * .values({id: args.id, name: args.name, status: 'active'})\n * .execute();\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroKysely<TSchema extends Schema, TDatabase>(\n schema: TSchema,\n client: Kysely<TDatabase>,\n): ZQLDatabase<TSchema, WrappedKyselyTransaction<TDatabase>> {\n return new ZQLDatabase(new KyselyConnection(client), schema);\n}\n"],"mappings":";;;;AA6BA,IAAa,mBAAb,MAEE;CACA;CAEA,YAAY,QAA2B;AACrC,QAAA,SAAe;;CAGjB,YACE,IACY;AACZ,SAAO,MAAA,OACJ,aAAa,CACb,SAAQ,aAAY,GAAG,IAAI,0BAA0B,SAAS,CAAC,CAAC;;;AAIvE,IAAM,4BAAN,MAEE;CACA;CAEA,YAAY,UAA+C;AACzD,OAAK,qBAAqB;;CAG5B,SACE,KACA,QACA,QACA,cACiC;AACjC,SAAO,qBACL,MACA,KACA,QACA,QACA,aACD;;CAGH,MAAM,MAAM,KAAa,QAAmC;AAI1D,UAHe,MAAM,KAAK,mBAAmB,aAC3C,cAAc,IAAI,KAAK,OAAO,CAC/B,EACa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDlB,SAAgB,WACd,QACA,QAC2D;AAC3D,QAAO,IAAI,YAAY,IAAI,iBAAiB,OAAO,EAAE,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresjs.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAE/B,qBAAa,oBAAoB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjC,YAAW,YAAY,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;;gBAErC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAI/B,WAAW,CAAC,IAAI,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC;CAKjB;AAED,qBAAa,6BAA6B,CACxC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjC,YAAW,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAI1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIrD,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;CASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"postgresjs.d.ts","sourceRoot":"","sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,GAAG,EACJ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,YAAY,EAAC,WAAW,EAAC,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAE/B,qBAAa,oBAAoB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjC,YAAW,YAAY,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;;gBAErC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAI/B,WAAW,CAAC,IAAI,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC;CAKjB;AAED,qBAAa,6BAA6B,CACxC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjC,YAAW,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAI1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIrD,QAAQ,CAAC,OAAO,EACd,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;CASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,4CAOxC"}
|
|
@@ -61,7 +61,7 @@ var PostgresJsTransactionInternal = class {
|
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
63
|
function zeroPostgresJS(schema, pg) {
|
|
64
|
-
if (typeof pg === "string") pg = postgres(pg);
|
|
64
|
+
if (typeof pg === "string") pg = postgres(pg, { connection: { ["application_name"]: "zero-server" } });
|
|
65
65
|
return new ZQLDatabase(new PostgresJSConnection(pg), schema);
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresjs.js","names":["#pg"],"sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by postgres.js.\n *\n * @typeParam T - The row-shape context bound to the postgres.js client.\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PostgresJsTransaction>`.\n */\nexport type PostgresJsTransaction<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = postgres.TransactionSql<T>;\n\nexport class PostgresJSConnection<\n T extends Record<string, unknown>,\n> implements DBConnection<PostgresJsTransaction<T>> {\n readonly #pg: postgres.Sql<T>;\n constructor(pg: postgres.Sql<T>) {\n this.#pg = pg;\n }\n\n transaction<TRet>(\n fn: (tx: DBTransaction<PostgresJsTransaction<T>>) => Promise<TRet>,\n ): Promise<TRet> {\n return this.#pg.begin(pgTx =>\n fn(new PostgresJsTransactionInternal(pgTx)),\n ) as Promise<TRet>;\n }\n}\n\nexport class PostgresJsTransactionInternal<\n T extends Record<string, unknown>,\n> implements DBTransaction<PostgresJsTransaction<T>> {\n readonly wrappedTransaction: PostgresJsTransaction<T>;\n constructor(pgTx: PostgresJsTransaction<T>) {\n this.wrappedTransaction = pgTx;\n }\n\n query(sql: string, params: unknown[]): Promise<Row[]> {\n return this.wrappedTransaction.unsafe(sql, params as JSONValue[]);\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n}\n\n/**\n * Wrap a `postgres` client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying postgres.js transaction.\n * Use {@link PostgresJsTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `postgres` client or connection string.\n *\n * @example\n * ```ts\n * import postgres from 'postgres';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroPostgresJS} from '@rocicorp/zero/server/adapters/postgresjs';\n * import {z} from 'zod/mini';\n *\n * const sql = postgres(process.env.ZERO_UPSTREAM_DB!);\n * const zql = zeroPostgresJS(schema, sql);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction`\n * INSERT INTO \"user\" (id, name, status)\n * VALUES (${args.id}, ${args.name}, ${'active'})\n * `;\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroPostgresJS<\n S extends Schema,\n T extends Record<string, unknown> = Record<string, unknown>,\n>(schema: S, pg: postgres.Sql<T> | string) {\n if (typeof pg === 'string') {\n pg = postgres(pg) as postgres.Sql<T>;\n }\n return new ZQLDatabase(new PostgresJSConnection(pg), schema);\n}\n"],"mappings":";;;;AA2BA,IAAa,uBAAb,MAEoD;CAClD;CACA,YAAY,IAAqB;AAC/B,QAAA,KAAW;;CAGb,YACE,IACe;AACf,SAAO,MAAA,GAAS,OAAM,SACpB,GAAG,IAAI,8BAA8B,KAAK,CAAC,CAC5C;;;AAIL,IAAa,gCAAb,MAEqD;CACnD;CACA,YAAY,MAAgC;AAC1C,OAAK,qBAAqB;;CAG5B,MAAM,KAAa,QAAmC;AACpD,SAAO,KAAK,mBAAmB,OAAO,KAAK,OAAsB;;CAGnE,SACE,KACA,QACA,QACA,cACiC;AACjC,SAAO,qBACL,MACA,KACA,QACA,QACA,aACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCL,SAAgB,eAGd,QAAW,IAA8B;AACzC,KAAI,OAAO,OAAO,SAChB,MAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"postgresjs.js","names":["#pg"],"sources":["../../../../../zero-server/src/adapters/postgresjs.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {Format} from '../../../zero-types/src/format.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../../zero-types/src/server-schema.ts';\nimport type {\n DBConnection,\n DBTransaction,\n Row,\n} from '../../../zql/src/mutate/custom.ts';\nimport type {HumanReadable} from '../../../zql/src/query/query.ts';\nimport {executePostgresQuery} from '../pg-query-executor.ts';\nimport {ZQLDatabase} from '../zql-database.ts';\n\nexport type {ZQLDatabase};\n\n/**\n * Helper type for the wrapped transaction used by postgres.js.\n *\n * @typeParam T - The row-shape context bound to the postgres.js client.\n * @remarks Use with `ServerTransaction` as `ServerTransaction<Schema, PostgresJsTransaction>`.\n */\nexport type PostgresJsTransaction<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = postgres.TransactionSql<T>;\n\nexport class PostgresJSConnection<\n T extends Record<string, unknown>,\n> implements DBConnection<PostgresJsTransaction<T>> {\n readonly #pg: postgres.Sql<T>;\n constructor(pg: postgres.Sql<T>) {\n this.#pg = pg;\n }\n\n transaction<TRet>(\n fn: (tx: DBTransaction<PostgresJsTransaction<T>>) => Promise<TRet>,\n ): Promise<TRet> {\n return this.#pg.begin(pgTx =>\n fn(new PostgresJsTransactionInternal(pgTx)),\n ) as Promise<TRet>;\n }\n}\n\nexport class PostgresJsTransactionInternal<\n T extends Record<string, unknown>,\n> implements DBTransaction<PostgresJsTransaction<T>> {\n readonly wrappedTransaction: PostgresJsTransaction<T>;\n constructor(pgTx: PostgresJsTransaction<T>) {\n this.wrappedTransaction = pgTx;\n }\n\n query(sql: string, params: unknown[]): Promise<Row[]> {\n return this.wrappedTransaction.unsafe(sql, params as JSONValue[]);\n }\n\n runQuery<TReturn>(\n ast: AST,\n format: Format,\n schema: Schema,\n serverSchema: ServerSchema,\n ): Promise<HumanReadable<TReturn>> {\n return executePostgresQuery<TReturn>(\n this,\n ast,\n format,\n schema,\n serverSchema,\n );\n }\n}\n\n/**\n * Wrap a `postgres` client for Zero ZQL.\n *\n * Provides ZQL querying plus access to the underlying postgres.js transaction.\n * Use {@link PostgresJsTransaction} to type your server mutator transaction.\n *\n * @param schema - Zero schema.\n * @param pg - `postgres` client or connection string.\n *\n * @example\n * ```ts\n * import postgres from 'postgres';\n * import {defineMutator, defineMutators} from '@rocicorp/zero';\n * import {zeroPostgresJS} from '@rocicorp/zero/server/adapters/postgresjs';\n * import {z} from 'zod/mini';\n *\n * const sql = postgres(process.env.ZERO_UPSTREAM_DB!);\n * const zql = zeroPostgresJS(schema, sql);\n *\n * export const serverMutators = defineMutators({\n * user: {\n * create: defineMutator(\n * z.object({id: z.string(), name: z.string()}),\n * async ({tx, args}) => {\n * if (tx.location !== 'server') {\n * throw new Error('Server-only mutator');\n * }\n * await tx.dbTransaction.wrappedTransaction`\n * INSERT INTO \"user\" (id, name, status)\n * VALUES (${args.id}, ${args.name}, ${'active'})\n * `;\n * },\n * ),\n * },\n * });\n * ```\n */\nexport function zeroPostgresJS<\n S extends Schema,\n T extends Record<string, unknown> = Record<string, unknown>,\n>(schema: S, pg: postgres.Sql<T> | string) {\n if (typeof pg === 'string') {\n pg = postgres(pg, {\n connection: {['application_name']: 'zero-server'},\n }) as postgres.Sql<T>;\n }\n return new ZQLDatabase(new PostgresJSConnection(pg), schema);\n}\n"],"mappings":";;;;AA2BA,IAAa,uBAAb,MAEoD;CAClD;CACA,YAAY,IAAqB;AAC/B,QAAA,KAAW;;CAGb,YACE,IACe;AACf,SAAO,MAAA,GAAS,OAAM,SACpB,GAAG,IAAI,8BAA8B,KAAK,CAAC,CAC5C;;;AAIL,IAAa,gCAAb,MAEqD;CACnD;CACA,YAAY,MAAgC;AAC1C,OAAK,qBAAqB;;CAG5B,MAAM,KAAa,QAAmC;AACpD,SAAO,KAAK,mBAAmB,OAAO,KAAK,OAAsB;;CAGnE,SACE,KACA,QACA,QACA,cACiC;AACjC,SAAO,qBACL,MACA,KACA,QACA,QACA,aACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCL,SAAgB,eAGd,QAAW,IAA8B;AACzC,KAAI,OAAO,OAAO,SAChB,MAAK,SAAS,IAAI,EAChB,YAAY,GAAE,qBAAqB,eAAc,EAClD,CAAC;AAEJ,QAAO,IAAI,YAAY,IAAI,qBAAqB,GAAG,EAAE,OAAO"}
|
|
@@ -9,6 +9,7 @@ import "../../zero-schema/src/builder/relationship-builder.js";
|
|
|
9
9
|
import "../../zero-schema/src/builder/table-builder.js";
|
|
10
10
|
import "../../zero-schema/src/builder/schema-builder.js";
|
|
11
11
|
import "../../zero-schema/src/permissions.js";
|
|
12
|
+
import "../../zql/src/query/validate-input.js";
|
|
12
13
|
import "../../zql/src/mutate/mutator.js";
|
|
13
14
|
import "../../zql/src/mutate/mutator-registry.js";
|
|
14
15
|
import "../../zql/src/query/create-builder.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAC,MAAM,kCAAkC,CAAC;AAI1D,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EAET,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,GAAG,EAAQ,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,IAAI,EACV,MAAM,WAAW,CAAC;AAEnB,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,MAAM,EACX,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAIZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC;IACrB,MAAM,EAAE,GAAG,GAAG,SAAS,CAAC;CACzB,CAAC;AAQF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,EACH;QACE,SAAS,EAAE,mBAAmB,CAAC;QAC/B,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;KAClC,GACD,SAAS,CAAC;IACd,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,MAAM;;gBAYvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,UAAU,EAAE,UAAU,EACtB,gBAAgB,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC;IAclC,IAAI,WAAW;;;;MAMd;IAED,IAAI;IAUJ,IAAI,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,CAExB;IAeD,OAAO,CACL,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,OAAO,CAAC,EAAE,SAAS,EACnB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,WAAW;
|
|
1
|
+
{"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
|