@rocicorp/zero 1.12.0-canary.4 → 1.12.0-canary.5
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.map +1 -1
- package/out/analyze-query/src/analyze-cli.js +2 -1
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +1 -1
- package/out/analyze-query/src/zql-chain.d.ts +23 -0
- package/out/analyze-query/src/zql-chain.d.ts.map +1 -0
- package/out/analyze-query/src/zql-chain.js +162 -0
- package/out/analyze-query/src/zql-chain.js.map +1 -0
- package/out/ast-to-zql/src/bin.js +1 -1
- package/out/zero/package.js +21 -22
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +1 -1
- package/out/zero-cache/src/scripts/decommission.js +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +1 -1
- package/out/zero-cache/src/server/change-streamer.js +1 -1
- package/out/zero-cache/src/server/replicator.js +1 -1
- package/out/zero-cache/src/server/syncer.js +3 -3
- package/out/zero-cache/src/services/analyze.js +2 -2
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +4 -4
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +2 -2
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +2 -2
- package/out/zero-cache/src/services/plannerz.js +2 -2
- package/out/zero-cache/src/services/replicator/change-log-db.js +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +3 -3
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
- package/out/zero-cache/src/services/run-ast.js +1 -1
- package/out/zero-cache/src/services/statz.js +2 -2
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +5 -5
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -6
- package/out/zero-cache/src/types/profiler.js +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zql/src/ivm/cap.d.ts +1 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +10 -0
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts +15 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +15 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +12 -4
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +5 -0
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +6 -3
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.js +1 -1
- package/out/zql/src/ivm/operator.d.ts +6 -0
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +4 -3
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zqlite/src/table-source.js +1 -1
- package/package.json +21 -22
- package/out/analyze-query/src/bin-analyze.d.ts +0 -2
- package/out/analyze-query/src/bin-analyze.d.ts.map +0 -1
- package/out/analyze-query/src/bin-analyze.js +0 -285
- package/out/analyze-query/src/bin-analyze.js.map +0 -1
- package/out/zero/src/analyze-query.d.ts +0 -3
- package/out/zero/src/analyze-query.d.ts.map +0 -1
- package/out/zero/src/analyze-query.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-cli.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/analyze-cli.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,CAAC;AAcpC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze-cli.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/analyze-cli.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,CAAC;AAcpC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAC;AAO3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACtC,CAAC;AAuHF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiG1E"}
|
|
@@ -7,6 +7,7 @@ import { logLevel, logOptions } from "../../otel/src/log-options.js";
|
|
|
7
7
|
import { colorConsole } from "../../shared/src/logging.js";
|
|
8
8
|
import { parseOptions } from "../../shared/src/options.js";
|
|
9
9
|
import { ZERO_ENV_VAR_PREFIX } from "../../zero-cache/src/config/zero-config.js";
|
|
10
|
+
import { evaluateZQLChain } from "./zql-chain.js";
|
|
10
11
|
import { Console } from "node:console";
|
|
11
12
|
import { styleText } from "node:util";
|
|
12
13
|
import { WebSocket } from "ws";
|
|
@@ -196,7 +197,7 @@ function buildQueryPlan(config) {
|
|
|
196
197
|
};
|
|
197
198
|
}
|
|
198
199
|
function buildZqlQuery(queryString, builder) {
|
|
199
|
-
return
|
|
200
|
+
return evaluateZQLChain(queryString, builder);
|
|
200
201
|
}
|
|
201
202
|
function resolveHandshakeHeaders(config) {
|
|
202
203
|
const headers = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-cli.js","names":["v","NodeWebSocket"],"sources":["../../../../analyze-query/src/analyze-cli.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {Console} from 'node:console';\nimport {styleText} from 'node:util';\nimport type {LogSink} from '@rocicorp/logger';\nimport {WebSocket as NodeWebSocket} from 'ws';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole} from '../../shared/src/logging.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../../zero-cache/src/config/zero-config.ts';\nimport {Zero} from '../../zero-client/src/client/zero.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {formatPlannerEvents} from '../../zql/src/planner/planner-debug.ts';\nimport {createBuilder} from '../../zql/src/query/create-builder.ts';\nimport type {AnyQuery} from '../../zql/src/query/query.ts';\nimport type {SchemaQuery} from '../../zql/src/query/schema-query.ts';\n\nexport type AnalyzeCLIOptions = {\n schema: Schema;\n /** Defaults to `process.argv.slice(2)`. */\n argv?: readonly string[] | undefined;\n};\n\nconst options = {\n zeroCacheURL: {\n type: v.string().optional(),\n desc: [\n 'URL of the remote zero-cache to analyze against.',\n 'Accepts http(s):// or ws(s):// (ws(s) is the transport actually used).',\n ],\n },\n adminPassword: {\n type: v.string().optional(),\n desc: [\n 'Admin password for zero-cache.',\n 'Required when the server is configured with one; ignored in dev mode.',\n ],\n },\n authToken: {\n type: v.string().optional(),\n desc: [\n 'Raw JWT forwarded to zero-cache.',\n 'Used server-side to fill permission variables for the query.',\n ],\n },\n cookie: {\n type: v.string().optional(),\n desc: [\n 'Cookie header value sent on the WebSocket upgrade request,',\n 'e.g. `session=abc; foo=bar`. Use this when zero-cache is behind',\n 'a proxy that resolves auth via cookies. Merged with --headers-json',\n '(--cookie wins on conflict).',\n ],\n },\n headersJson: {\n type: v.string().optional(),\n desc: [\n 'JSON object of arbitrary headers to send on the WebSocket upgrade',\n 'request, e.g. `{\"x-api-key\":\"...\"}`. Escape hatch for exotic auth',\n 'schemes; prefer --auth-token or --cookie when possible.',\n ],\n },\n userId: {\n type: v.string().optional(),\n desc: [\n 'Optional userID to report to zero-cache.',\n 'Omit for anonymous analysis.',\n ],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded AST. Exactly one of --ast / --query / --query-name is required.',\n 'The AST is sent to the server verbatim — provide it in server (post-mapping) form.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'ZQL query in chain form, e.g. `issue.related(\"comments\").limit(10)`.',\n 'Evaluated against the schema you pass to runAnalyzeCLI.',\n ],\n },\n queryName: {\n type: v.string().optional(),\n desc: [\n 'Name of a server-registered custom (named) query.',\n 'The server resolves the name + args via its registered query handler.',\n ],\n },\n queryArgs: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded array of arguments for --query-name. Defaults to `[]`.',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Include the rows read from the replica to execute the query.',\n 'Each row appears once per read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n joinPlans: {\n type: v.boolean().optional(() => false),\n desc: ['Include join planner diagnostics.'],\n },\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\ntype QueryPlan =\n | {kind: 'ast'; ast: AST}\n | {kind: 'zql'; text: string}\n | {kind: 'named'; name: string; args: ReadonlyArray<unknown>};\n\n// Route all Zero client log output to stderr so stdout contains only the\n// analyze result. Shell redirection (`2>/dev/null`) can then cleanly silence\n// logs without affecting output.\nconst stderrConsole = new Console({\n stdout: process.stderr,\n stderr: process.stderr,\n});\nconst stderrLogSink: LogSink = {\n log(level, context, ...args) {\n const ctx = context\n ? Object.entries(context).map(([k, v]) =>\n v === undefined ? k : `${k}=${v}`,\n )\n : [];\n stderrConsole[level](...ctx, ...args);\n },\n};\n\n/**\n * Entry point for a user's `cli.ts`. Parses argv, connects to a remote\n * zero-cache by standing up an in-process Zero client (in-memory storage,\n * no subscriptions), calls the inspector's `analyze-query` RPC, and\n * renders the result. Intended to be called as:\n *\n * ```ts\n * import {schema} from './schema.ts';\n * import {runAnalyzeCLI} from '@rocicorp/zero/analyze';\n * await runAnalyzeCLI({schema});\n * ```\n *\n * Exits the process with code 1 on error.\n */\nexport async function runAnalyzeCLI(opts: AnalyzeCLIOptions): Promise<void> {\n const argv = (opts.argv ?? process.argv.slice(2)).map(s =>\n s.replaceAll('\\n', ' '),\n );\n\n const config = parseOptions(options, {\n argv,\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query (remote)',\n content: `Analyze a ZQL query against a remote zero-cache.\n\n Connects to zero-cache's inspector protocol and reports the server-observed\n row scans, SQLite query plans, and timings.`,\n },\n {\n header: 'Examples',\n content: ` node cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --query-name=issueList --query-args='[]'`,\n },\n ],\n });\n\n if (!config.zeroCacheURL) {\n colorConsole.error('--zero-cache-url is required. See --help for usage.');\n process.exit(1);\n }\n\n const plan = buildQueryPlan(config);\n\n const handshakeHeaders = resolveHandshakeHeaders(config);\n if (Object.keys(handshakeHeaders).length > 0) {\n installWebSocketHeaderShim(handshakeHeaders);\n }\n\n // zero-client and replicache reference a build-time `TESTING` global that\n // bundlers replace with a boolean literal; under node there's no replacement,\n // so provide a runtime default.\n (globalThis as {TESTING?: boolean}).TESTING ??= false;\n\n const z = new Zero({\n schema: opts.schema,\n server: config.zeroCacheURL,\n auth: config.authToken,\n userID: config.userId,\n kvStore: 'mem',\n logLevel: config.log.level,\n logSink: stderrLogSink,\n });\n\n let result: AnalyzeQueryResult;\n try {\n const authOk = await z.inspector.authenticate(config.adminPassword ?? '');\n if (!authOk) {\n throw new Error(\n 'admin password rejected (or --admin-password is required)',\n );\n }\n\n const rpcOptions = {\n vendedRows: config.outputVendedRows,\n syncedRows: config.outputSyncedRows,\n joinPlans: config.joinPlans,\n };\n\n if (plan.kind === 'ast') {\n result = await z.inspector.analyzeServerAST(plan.ast, rpcOptions);\n } else if (plan.kind === 'named') {\n result = await z.inspector.analyzeNamedQuery(\n plan.name,\n plan.args as ReadonlyArray<never>,\n rpcOptions,\n );\n } else {\n const built = buildZqlQuery(plan.text, createBuilder(opts.schema));\n result = await z.inspector.analyzeQuery(built, rpcOptions);\n }\n } catch (e) {\n colorConsole.error(e instanceof Error ? e.message : String(e));\n await z.close().catch(() => {});\n process.exit(1);\n }\n\n renderResult(result, {\n outputSyncedRows: config.outputSyncedRows,\n outputVendedRows: config.outputVendedRows,\n });\n\n await z.close();\n}\n\nfunction buildQueryPlan(config: {\n ast?: string | undefined;\n query?: string | undefined;\n queryName?: string | undefined;\n queryArgs?: string | undefined;\n}): QueryPlan {\n const selectors = [\n config.ast !== undefined && 'ast',\n config.query !== undefined && 'query',\n config.queryName !== undefined && 'queryName',\n ].filter(Boolean) as string[];\n\n if (selectors.length === 0) {\n colorConsole.error(\n 'Exactly one of --ast / --query / --query-name is required.',\n );\n process.exit(1);\n }\n if (selectors.length > 1) {\n colorConsole.error(\n `Only one of --ast / --query / --query-name may be provided; got: ${selectors.join(', ')}`,\n );\n process.exit(1);\n }\n\n if (config.ast !== undefined) {\n return {kind: 'ast', ast: JSON.parse(config.ast) as AST};\n }\n if (config.query !== undefined) {\n return {kind: 'zql', text: config.query};\n }\n const args = config.queryArgs\n ? (JSON.parse(config.queryArgs) as ReadonlyArray<unknown>)\n : [];\n return {kind: 'named', name: config.queryName as string, args};\n}\n\nfunction buildZqlQuery(\n queryString: string,\n builder: SchemaQuery<Schema>,\n): AnyQuery {\n const f = new Function('builder', `return builder.${queryString};`);\n return f(builder) as AnyQuery;\n}\n\nfunction resolveHandshakeHeaders(config: {\n cookie?: string | undefined;\n headersJson?: string | undefined;\n}): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.headersJson !== undefined) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(config.headersJson);\n } catch (e) {\n colorConsole.error(\n `--headers-json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`,\n );\n process.exit(1);\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n colorConsole.error('--headers-json must be a JSON object.');\n process.exit(1);\n }\n for (const [k, val] of Object.entries(parsed)) {\n if (typeof val !== 'string') {\n colorConsole.error(\n `--headers-json values must be strings; got ${typeof val} for \"${k}\".`,\n );\n process.exit(1);\n }\n headers[k] = val;\n }\n }\n if (config.cookie !== undefined) {\n headers.cookie = config.cookie;\n }\n return headers;\n}\n\nfunction installWebSocketHeaderShim(headers: Record<string, string>): void {\n class HeaderInjectingWebSocket extends NodeWebSocket {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols, {headers});\n }\n }\n (globalThis as {WebSocket?: unknown}).WebSocket = HeaderInjectingWebSocket;\n}\n\nfunction renderResult(\n result: AnalyzeQueryResult,\n opts: {outputSyncedRows: boolean; outputVendedRows: boolean},\n) {\n if (opts.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n }\n\n colorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\n colorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n );\n\n const readRowCountsByQuery = result.readRowCountsByQuery ?? {};\n let totalRowsRead = 0;\n for (const table of Object.keys(readRowCountsByQuery).sort()) {\n const counts = readRowCountsByQuery[table];\n for (const n of Object.values(counts)) {\n totalRowsRead += n;\n }\n colorConsole.log(styleText('bold', `${table} vended:`), counts);\n }\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsRead),\n );\n const duration = result.elapsed ?? result.end - result.start;\n colorConsole.log(styleText('bold', 'time:'), colorTime(duration), 'ms');\n\n if (opts.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const [table, rows] of Object.entries(result.readRows ?? {})) {\n colorConsole.log(styleText('bold', `${table}:`), rows);\n }\n }\n\n colorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n );\n const dbScansByQuery = result.dbScansByQuery ?? {};\n let totalNVisit = 0;\n for (const [table, queries] of Object.entries(dbScansByQuery)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n }\n colorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n );\n\n colorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\n const plans = result.sqlitePlans ?? {};\n for (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n }\n\n if (result.warnings.length > 0) {\n colorConsole.log(styleText(['yellow', 'bold'], '=== Warnings: ===\\n'));\n for (const w of result.warnings) {\n colorConsole.log(styleText('yellow', w));\n }\n }\n\n if (result.joinPlans && result.joinPlans.length > 0) {\n colorConsole.log(styleText(['blue', 'bold'], '\\n=== Join Plans: ===\\n'));\n colorConsole.log(formatPlannerEvents(result.joinPlans));\n }\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,IAAM,UAAU;CACd,cAAc;EACZ,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,oDACA,wEACF;CACF;CACA,eAAe;EACb,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,kCACA,uEACF;CACF;CACA,WAAW;EACT,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,oCACA,8DACF;CACF;CACA,QAAQ;EACN,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM;GACJ;GACA;GACA;GACA;EACF;CACF;CACA,aAAa;EACX,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM;GACJ;GACA;GACA;EACF;CACF;CACA,QAAQ;EACN,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,4CACA,8BACF;CACF;CACA,KAAK;EACH,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,gFACA,oFACF;CACF;CACA,OAAO;EACL,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,0EACA,yDACF;CACF;CACA,WAAW;EACT,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,qDACA,uEACF;CACF;CACA,WAAW;EACT,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CACJ,qEACF;CACF;CACA,kBAAkB;EAChB,MAAMA,eAAE,QAAQ,CAAC,CAAC,eAAe,KAAK;EACtC,MAAM,CACJ,gEACA,iCACF;CACF;CACA,kBAAkB;EAChB,MAAMA,eAAE,QAAQ,CAAC,CAAC,eAAe,KAAK;EACtC,MAAM,CAAC,sDAAsD;CAC/D;CACA,WAAW;EACT,MAAMA,eAAE,QAAQ,CAAC,CAAC,eAAe,KAAK;EACtC,MAAM,CAAC,mCAAmC;CAC5C;CACA,KAAK;EACH,GAAG;EACH,OAAO,SAAS,eAAe,OAAO;CACxC;AACF;AAUA,IAAM,gBAAgB,IAAI,QAAQ;CAChC,QAAQ,QAAQ;CAChB,QAAQ,QAAQ;AAClB,CAAC;AACD,IAAM,gBAAyB,EAC7B,IAAI,OAAO,SAAS,GAAG,MAAM;CAC3B,MAAM,MAAM,UACR,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,OAC/B,MAAM,KAAA,IAAY,IAAI,GAAG,EAAE,GAAG,GAChC,IACA,CAAC;CACL,cAAc,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI;AACtC,EACF;;;;;;;;;;;;;;;AAgBA,eAAsB,cAAc,MAAwC;CAC1E,MAAM,QAAQ,KAAK,QAAQ,QAAQ,KAAK,MAAM,CAAC,EAAA,CAAG,KAAI,MACpD,EAAE,WAAW,MAAM,GAAG,CACxB;CAEA,MAAM,SAAS,aAAa,SAAS;EACnC;EACA,eAAe;EACf,aAAa,CACX;GACE,QAAQ;GACR,SAAS;;;;EAIX,GACA;GACE,QAAQ;GACR,SAAS;;;;;;;;;EASX,CACF;CACF,CAAC;CAED,IAAI,CAAC,OAAO,cAAc;EACxB,aAAa,MAAM,qDAAqD;EACxE,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,OAAO,eAAe,MAAM;CAElC,MAAM,mBAAmB,wBAAwB,MAAM;CACvD,IAAI,OAAO,KAAK,gBAAgB,CAAC,CAAC,SAAS,GACzC,2BAA2B,gBAAgB;CAM7C,WAAoC,YAAY;CAEhD,MAAM,IAAI,IAAI,KAAK;EACjB,QAAQ,KAAK;EACb,QAAQ,OAAO;EACf,MAAM,OAAO;EACb,QAAQ,OAAO;EACf,SAAS;EACT,UAAU,OAAO,IAAI;EACrB,SAAS;CACX,CAAC;CAED,IAAI;CACJ,IAAI;EAEF,IAAI,CAAC,MADgB,EAAE,UAAU,aAAa,OAAO,iBAAiB,EAAE,GAEtE,MAAM,IAAI,MACR,2DACF;EAGF,MAAM,aAAa;GACjB,YAAY,OAAO;GACnB,YAAY,OAAO;GACnB,WAAW,OAAO;EACpB;EAEA,IAAI,KAAK,SAAS,OAChB,SAAS,MAAM,EAAE,UAAU,iBAAiB,KAAK,KAAK,UAAU;OAC3D,IAAI,KAAK,SAAS,SACvB,SAAS,MAAM,EAAE,UAAU,kBACzB,KAAK,MACL,KAAK,MACL,UACF;OACK;GACL,MAAM,QAAQ,cAAc,KAAK,MAAM,cAAc,KAAK,MAAM,CAAC;GACjE,SAAS,MAAM,EAAE,UAAU,aAAa,OAAO,UAAU;EAC3D;CACF,SAAS,GAAG;EACV,aAAa,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;EAC7D,MAAM,EAAE,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAC9B,QAAQ,KAAK,CAAC;CAChB;CAEA,aAAa,QAAQ;EACnB,kBAAkB,OAAO;EACzB,kBAAkB,OAAO;CAC3B,CAAC;CAED,MAAM,EAAE,MAAM;AAChB;AAEA,SAAS,eAAe,QAKV;CACZ,MAAM,YAAY;EAChB,OAAO,QAAQ,KAAA,KAAa;EAC5B,OAAO,UAAU,KAAA,KAAa;EAC9B,OAAO,cAAc,KAAA,KAAa;CACpC,CAAC,CAAC,OAAO,OAAO;CAEhB,IAAI,UAAU,WAAW,GAAG;EAC1B,aAAa,MACX,4DACF;EACA,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,UAAU,SAAS,GAAG;EACxB,aAAa,MACX,oEAAoE,UAAU,KAAK,IAAI,GACzF;EACA,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,OAAO,QAAQ,KAAA,GACjB,OAAO;EAAC,MAAM;EAAO,KAAK,KAAK,MAAM,OAAO,GAAG;CAAQ;CAEzD,IAAI,OAAO,UAAU,KAAA,GACnB,OAAO;EAAC,MAAM;EAAO,MAAM,OAAO;CAAK;CAEzC,MAAM,OAAO,OAAO,YACf,KAAK,MAAM,OAAO,SAAS,IAC5B,CAAC;CACL,OAAO;EAAC,MAAM;EAAS,MAAM,OAAO;EAAqB;CAAI;AAC/D;AAEA,SAAS,cACP,aACA,SACU;CAEV,OAAO,IADO,SAAS,WAAW,kBAAkB,YAAY,EACzD,CAAA,CAAE,OAAO;AAClB;AAEA,SAAS,wBAAwB,QAGN;CACzB,MAAM,UAAkC,CAAC;CACzC,IAAI,OAAO,gBAAgB,KAAA,GAAW;EACpC,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,OAAO,WAAW;EACxC,SAAS,GAAG;GACV,aAAa,MACX,qCAAqC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,GAChF;GACA,QAAQ,KAAK,CAAC;EAChB;EACA,IACE,WAAW,QACX,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,GACpB;GACA,aAAa,MAAM,uCAAuC;GAC1D,QAAQ,KAAK,CAAC;EAChB;EACA,KAAK,MAAM,CAAC,GAAG,QAAQ,OAAO,QAAQ,MAAM,GAAG;GAC7C,IAAI,OAAO,QAAQ,UAAU;IAC3B,aAAa,MACX,8CAA8C,OAAO,IAAI,QAAQ,EAAE,GACrE;IACA,QAAQ,KAAK,CAAC;GAChB;GACA,QAAQ,KAAK;EACf;CACF;CACA,IAAI,OAAO,WAAW,KAAA,GACpB,QAAQ,SAAS,OAAO;CAE1B,OAAO;AACT;AAEA,SAAS,2BAA2B,SAAuC;CACzE,MAAM,iCAAiCC,UAAc;EACnD,YAAY,KAAmB,WAA+B;GAC5D,MAAM,KAAK,WAAW,EAAC,QAAO,CAAC;EACjC;CACF;CACA,WAAsC,YAAY;AACpD;AAEA,SAAS,aACP,QACA,MACA;CACA,IAAI,KAAK,kBAAkB;EACzB,aAAa,IAAI,UAAU,CAAC,QAAQ,MAAM,GAAG,wBAAwB,CAAC;EACtE,KAAK,MAAM,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO,cAAc,CAAC,CAAC,GAChE,aAAa,IAAI,UAAU,QAAQ,QAAQ,GAAG,GAAG,IAAI;CAEzD;CAEA,aAAa,IAAI,UAAU,CAAC,QAAQ,MAAM,GAAG,wBAAwB,CAAC;CACtE,aAAa,IACX,UAAU,QAAQ,oBAAoB,GACtC,OAAO,cACT;CAEA,MAAM,uBAAuB,OAAO,wBAAwB,CAAC;CAC7D,IAAI,gBAAgB;CACpB,KAAK,MAAM,SAAS,OAAO,KAAK,oBAAoB,CAAC,CAAC,KAAK,GAAG;EAC5D,MAAM,SAAS,qBAAqB;EACpC,KAAK,MAAM,KAAK,OAAO,OAAO,MAAM,GAClC,iBAAiB;EAEnB,aAAa,IAAI,UAAU,QAAQ,GAAG,MAAM,SAAS,GAAG,MAAM;CAChE;CACA,aAAa,IACX,UAAU,QAAQ,sBAAsB,GACxC,oBAAoB,aAAa,CACnC;CACA,MAAM,WAAW,OAAO,WAAW,OAAO,MAAM,OAAO;CACvD,aAAa,IAAI,UAAU,QAAQ,OAAO,GAAG,UAAU,QAAQ,GAAG,IAAI;CAEtE,IAAI,KAAK,kBAAkB;EACzB,aAAa,IACX,UAAU,CAAC,QAAQ,MAAM,GAAG,+BAA+B,CAC7D;EACA,KAAK,MAAM,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO,YAAY,CAAC,CAAC,GAC9D,aAAa,IAAI,UAAU,QAAQ,GAAG,MAAM,EAAE,GAAG,IAAI;CAEzD;CAEA,aAAa,IACX,UAAU,CAAC,QAAQ,MAAM,GAAG,uCAAuC,CACrE;CACA,MAAM,iBAAiB,OAAO,kBAAkB,CAAC;CACjD,IAAI,cAAc;CAClB,KAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,cAAc,GAAG;EAC7D,aAAa,IAAI,UAAU,QAAQ,GAAG,MAAM,EAAE,GAAG,OAAO;EACxD,KAAK,MAAM,SAAS,OAAO,OAAO,OAAO,GACvC,eAAe;CAEnB;CACA,aAAa,IACX,UAAU,QAAQ,qBAAqB,GACvC,oBAAoB,WAAW,CACjC;CAEA,aAAa,IAAI,UAAU,CAAC,QAAQ,MAAM,GAAG,4BAA4B,CAAC;CAC1E,MAAM,QAAQ,OAAO,eAAe,CAAC;CACrC,KAAK,MAAM,CAAC,OAAO,SAAS,OAAO,QAAQ,KAAK,GAAG;EACjD,aAAa,IAAI,UAAU,QAAQ,OAAO,GAAG,KAAK;EAClD,aAAa,IAAI,KAAK,KAAK,KAAK,MAAM,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;EACtE,aAAa,IAAI,IAAI;CACvB;CAEA,IAAI,OAAO,SAAS,SAAS,GAAG;EAC9B,aAAa,IAAI,UAAU,CAAC,UAAU,MAAM,GAAG,qBAAqB,CAAC;EACrE,KAAK,MAAM,KAAK,OAAO,UACrB,aAAa,IAAI,UAAU,UAAU,CAAC,CAAC;CAE3C;CAEA,IAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;EACnD,aAAa,IAAI,UAAU,CAAC,QAAQ,MAAM,GAAG,yBAAyB,CAAC;EACvE,aAAa,IAAI,oBAAoB,OAAO,SAAS,CAAC;CACxD;AACF;AAEA,SAAS,UAAU,UAAkB;CACnC,IAAI,WAAW,KACb,OAAO,UAAU,SAAS,SAAS,QAAQ,CAAC,IAAI,IAAI;MAC/C,IAAI,WAAW,KACpB,OAAO,UAAU,UAAU,SAAS,QAAQ,CAAC,IAAI,IAAI;CAEvD,OAAO,UAAU,OAAO,SAAS,QAAQ,CAAC,IAAI,IAAI;AACpD;AAEA,SAAS,oBAAoB,GAAW;CACtC,IAAI,IAAI,KACN,OAAO,UAAU,SAAS,EAAE,SAAS,CAAC;MACjC,IAAI,IAAI,KACb,OAAO,UAAU,UAAU,EAAE,SAAS,CAAC;CAEzC,OAAO,UAAU,OAAO,EAAE,SAAS,CAAC;AACtC;AAEA,SAAS,aAAa,KAAa,GAAW;CAC5C,IAAI,IAAI,SAAS,MAAM,GAAG;EACxB,IAAI,MAAM,GACR,OAAO,UAAU,UAAU,GAAG;EAEhC,OAAO,UAAU,OAAO,GAAG;CAC7B;CACA,OAAO,UAAU,SAAS,GAAG;AAC/B"}
|
|
1
|
+
{"version":3,"file":"analyze-cli.js","names":["v","NodeWebSocket"],"sources":["../../../../analyze-query/src/analyze-cli.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {Console} from 'node:console';\nimport {styleText} from 'node:util';\nimport type {LogSink} from '@rocicorp/logger';\nimport {WebSocket as NodeWebSocket} from 'ws';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole} from '../../shared/src/logging.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../../zero-cache/src/config/zero-config.ts';\nimport {Zero} from '../../zero-client/src/client/zero.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {formatPlannerEvents} from '../../zql/src/planner/planner-debug.ts';\nimport {createBuilder} from '../../zql/src/query/create-builder.ts';\nimport type {AnyQuery} from '../../zql/src/query/query.ts';\nimport type {SchemaQuery} from '../../zql/src/query/schema-query.ts';\nimport {evaluateZQLChain} from './zql-chain.ts';\n\nexport type AnalyzeCLIOptions = {\n schema: Schema;\n /** Defaults to `process.argv.slice(2)`. */\n argv?: readonly string[] | undefined;\n};\n\nconst options = {\n zeroCacheURL: {\n type: v.string().optional(),\n desc: [\n 'URL of the remote zero-cache to analyze against.',\n 'Accepts http(s):// or ws(s):// (ws(s) is the transport actually used).',\n ],\n },\n adminPassword: {\n type: v.string().optional(),\n desc: [\n 'Admin password for zero-cache.',\n 'Required when the server is configured with one; ignored in dev mode.',\n ],\n },\n authToken: {\n type: v.string().optional(),\n desc: [\n 'Raw JWT forwarded to zero-cache.',\n 'Used server-side to fill permission variables for the query.',\n ],\n },\n cookie: {\n type: v.string().optional(),\n desc: [\n 'Cookie header value sent on the WebSocket upgrade request,',\n 'e.g. `session=abc; foo=bar`. Use this when zero-cache is behind',\n 'a proxy that resolves auth via cookies. Merged with --headers-json',\n '(--cookie wins on conflict).',\n ],\n },\n headersJson: {\n type: v.string().optional(),\n desc: [\n 'JSON object of arbitrary headers to send on the WebSocket upgrade',\n 'request, e.g. `{\"x-api-key\":\"...\"}`. Escape hatch for exotic auth',\n 'schemes; prefer --auth-token or --cookie when possible.',\n ],\n },\n userId: {\n type: v.string().optional(),\n desc: [\n 'Optional userID to report to zero-cache.',\n 'Omit for anonymous analysis.',\n ],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded AST. Exactly one of --ast / --query / --query-name is required.',\n 'The AST is sent to the server verbatim — provide it in server (post-mapping) form.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'ZQL query in chain form, e.g. `issue.related(\"comments\").limit(10)`.',\n 'Evaluated against the schema you pass to runAnalyzeCLI.',\n ],\n },\n queryName: {\n type: v.string().optional(),\n desc: [\n 'Name of a server-registered custom (named) query.',\n 'The server resolves the name + args via its registered query handler.',\n ],\n },\n queryArgs: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded array of arguments for --query-name. Defaults to `[]`.',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Include the rows read from the replica to execute the query.',\n 'Each row appears once per read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n joinPlans: {\n type: v.boolean().optional(() => false),\n desc: ['Include join planner diagnostics.'],\n },\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\ntype QueryPlan =\n | {kind: 'ast'; ast: AST}\n | {kind: 'zql'; text: string}\n | {kind: 'named'; name: string; args: ReadonlyArray<unknown>};\n\n// Route all Zero client log output to stderr so stdout contains only the\n// analyze result. Shell redirection (`2>/dev/null`) can then cleanly silence\n// logs without affecting output.\nconst stderrConsole = new Console({\n stdout: process.stderr,\n stderr: process.stderr,\n});\nconst stderrLogSink: LogSink = {\n log(level, context, ...args) {\n const ctx = context\n ? Object.entries(context).map(([k, v]) =>\n v === undefined ? k : `${k}=${v}`,\n )\n : [];\n stderrConsole[level](...ctx, ...args);\n },\n};\n\n/**\n * Entry point for a user's `cli.ts`. Parses argv, connects to a remote\n * zero-cache by standing up an in-process Zero client (in-memory storage,\n * no subscriptions), calls the inspector's `analyze-query` RPC, and\n * renders the result. Intended to be called as:\n *\n * ```ts\n * import {schema} from './schema.ts';\n * import {runAnalyzeCLI} from '@rocicorp/zero/analyze';\n * await runAnalyzeCLI({schema});\n * ```\n *\n * Exits the process with code 1 on error.\n */\nexport async function runAnalyzeCLI(opts: AnalyzeCLIOptions): Promise<void> {\n const argv = (opts.argv ?? process.argv.slice(2)).map(s =>\n s.replaceAll('\\n', ' '),\n );\n\n const config = parseOptions(options, {\n argv,\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query (remote)',\n content: `Analyze a ZQL query against a remote zero-cache.\n\n Connects to zero-cache's inspector protocol and reports the server-observed\n row scans, SQLite query plans, and timings.`,\n },\n {\n header: 'Examples',\n content: ` node cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --query-name=issueList --query-args='[]'`,\n },\n ],\n });\n\n if (!config.zeroCacheURL) {\n colorConsole.error('--zero-cache-url is required. See --help for usage.');\n process.exit(1);\n }\n\n const plan = buildQueryPlan(config);\n\n const handshakeHeaders = resolveHandshakeHeaders(config);\n if (Object.keys(handshakeHeaders).length > 0) {\n installWebSocketHeaderShim(handshakeHeaders);\n }\n\n // zero-client and replicache reference a build-time `TESTING` global that\n // bundlers replace with a boolean literal; under node there's no replacement,\n // so provide a runtime default.\n (globalThis as {TESTING?: boolean}).TESTING ??= false;\n\n const z = new Zero({\n schema: opts.schema,\n server: config.zeroCacheURL,\n auth: config.authToken,\n userID: config.userId,\n kvStore: 'mem',\n logLevel: config.log.level,\n logSink: stderrLogSink,\n });\n\n let result: AnalyzeQueryResult;\n try {\n const authOk = await z.inspector.authenticate(config.adminPassword ?? '');\n if (!authOk) {\n throw new Error(\n 'admin password rejected (or --admin-password is required)',\n );\n }\n\n const rpcOptions = {\n vendedRows: config.outputVendedRows,\n syncedRows: config.outputSyncedRows,\n joinPlans: config.joinPlans,\n };\n\n if (plan.kind === 'ast') {\n result = await z.inspector.analyzeServerAST(plan.ast, rpcOptions);\n } else if (plan.kind === 'named') {\n result = await z.inspector.analyzeNamedQuery(\n plan.name,\n plan.args as ReadonlyArray<never>,\n rpcOptions,\n );\n } else {\n const built = buildZqlQuery(plan.text, createBuilder(opts.schema));\n result = await z.inspector.analyzeQuery(built, rpcOptions);\n }\n } catch (e) {\n colorConsole.error(e instanceof Error ? e.message : String(e));\n await z.close().catch(() => {});\n process.exit(1);\n }\n\n renderResult(result, {\n outputSyncedRows: config.outputSyncedRows,\n outputVendedRows: config.outputVendedRows,\n });\n\n await z.close();\n}\n\nfunction buildQueryPlan(config: {\n ast?: string | undefined;\n query?: string | undefined;\n queryName?: string | undefined;\n queryArgs?: string | undefined;\n}): QueryPlan {\n const selectors = [\n config.ast !== undefined && 'ast',\n config.query !== undefined && 'query',\n config.queryName !== undefined && 'queryName',\n ].filter(Boolean) as string[];\n\n if (selectors.length === 0) {\n colorConsole.error(\n 'Exactly one of --ast / --query / --query-name is required.',\n );\n process.exit(1);\n }\n if (selectors.length > 1) {\n colorConsole.error(\n `Only one of --ast / --query / --query-name may be provided; got: ${selectors.join(', ')}`,\n );\n process.exit(1);\n }\n\n if (config.ast !== undefined) {\n return {kind: 'ast', ast: JSON.parse(config.ast) as AST};\n }\n if (config.query !== undefined) {\n return {kind: 'zql', text: config.query};\n }\n const args = config.queryArgs\n ? (JSON.parse(config.queryArgs) as ReadonlyArray<unknown>)\n : [];\n return {kind: 'named', name: config.queryName as string, args};\n}\n\nfunction buildZqlQuery(\n queryString: string,\n builder: SchemaQuery<Schema>,\n): AnyQuery {\n return evaluateZQLChain(queryString, builder) as AnyQuery;\n}\n\nfunction resolveHandshakeHeaders(config: {\n cookie?: string | undefined;\n headersJson?: string | undefined;\n}): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.headersJson !== undefined) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(config.headersJson);\n } catch (e) {\n colorConsole.error(\n `--headers-json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`,\n );\n process.exit(1);\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n colorConsole.error('--headers-json must be a JSON object.');\n process.exit(1);\n }\n for (const [k, val] of Object.entries(parsed)) {\n if (typeof val !== 'string') {\n colorConsole.error(\n `--headers-json values must be strings; got ${typeof val} for \"${k}\".`,\n );\n process.exit(1);\n }\n headers[k] = val;\n }\n }\n if (config.cookie !== undefined) {\n headers.cookie = config.cookie;\n }\n return headers;\n}\n\nfunction installWebSocketHeaderShim(headers: Record<string, string>): void {\n class HeaderInjectingWebSocket extends NodeWebSocket {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols, {headers});\n }\n }\n (globalThis as {WebSocket?: unknown}).WebSocket = HeaderInjectingWebSocket;\n}\n\nfunction renderResult(\n result: AnalyzeQueryResult,\n opts: {outputSyncedRows: boolean; outputVendedRows: boolean},\n) {\n if (opts.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n }\n\n colorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\n colorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n );\n\n const readRowCountsByQuery = result.readRowCountsByQuery ?? {};\n let totalRowsRead = 0;\n for (const table of Object.keys(readRowCountsByQuery).sort()) {\n const counts = readRowCountsByQuery[table];\n for (const n of Object.values(counts)) {\n totalRowsRead += n;\n }\n colorConsole.log(styleText('bold', `${table} vended:`), counts);\n }\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsRead),\n );\n const duration = result.elapsed ?? result.end - result.start;\n colorConsole.log(styleText('bold', 'time:'), colorTime(duration), 'ms');\n\n if (opts.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const [table, rows] of Object.entries(result.readRows ?? {})) {\n colorConsole.log(styleText('bold', `${table}:`), rows);\n }\n }\n\n colorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n );\n const dbScansByQuery = result.dbScansByQuery ?? {};\n let totalNVisit = 0;\n for (const [table, queries] of Object.entries(dbScansByQuery)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n }\n colorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n );\n\n colorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\n const plans = result.sqlitePlans ?? {};\n for (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n }\n\n if (result.warnings.length > 0) {\n colorConsole.log(styleText(['yellow', 'bold'], '=== Warnings: ===\\n'));\n for (const w of result.warnings) {\n colorConsole.log(styleText('yellow', w));\n }\n }\n\n if (result.joinPlans && result.joinPlans.length > 0) {\n colorConsole.log(styleText(['blue', 'bold'], '\\n=== Join Plans: ===\\n'));\n colorConsole.log(formatPlannerEvents(result.joinPlans));\n }\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
|
|
@@ -4,9 +4,9 @@ import "../../shared/src/dotenv.js";
|
|
|
4
4
|
import { parseOptions } from "../../shared/src/options.js";
|
|
5
5
|
import { getShardID, upstreamSchema } from "../../zero-cache/src/types/shards.js";
|
|
6
6
|
import { ZERO_ENV_VAR_PREFIX, appOptions, shardOptions } from "../../zero-cache/src/config/zero-config.js";
|
|
7
|
+
import { loadSchemaAndPermissions } from "../../zero-cache/src/scripts/permissions.js";
|
|
7
8
|
import { astToZQL } from "../../ast-to-zql/src/ast-to-zql.js";
|
|
8
9
|
import { formatOutput } from "../../ast-to-zql/src/format.js";
|
|
9
|
-
import { loadSchemaAndPermissions } from "../../zero-cache/src/scripts/permissions.js";
|
|
10
10
|
import { transformAndHashQuery } from "../../zero-cache/src/auth/read-authorizer.js";
|
|
11
11
|
import { pgClient } from "../../zero-cache/src/types/pg.js";
|
|
12
12
|
import { LogContext, consoleLogSink } from "@rocicorp/logger";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evaluates a ZQL query written in chain form, e.g.
|
|
3
|
+
* `issue.where('open', true).related('comments', q => q.limit(10))`.
|
|
4
|
+
*
|
|
5
|
+
* The text is parsed with oxc and the resulting JavaScript AST is evaluated
|
|
6
|
+
* directly. It is never run as JavaScript. Only these constructs are
|
|
7
|
+
* accepted:
|
|
8
|
+
*
|
|
9
|
+
* - identifiers: table names (looked up on `root`), arrow function
|
|
10
|
+
* parameters, and `undefined`
|
|
11
|
+
* - calls to the query and expression builder methods in
|
|
12
|
+
* {@link ALLOWED_METHODS}, either as `x.method(...)` or through a
|
|
13
|
+
* destructured parameter such as `({cmp, or}) => or(cmp(...))`
|
|
14
|
+
* - string, number, boolean and null literals, negative numbers, and
|
|
15
|
+
* template literals without substitutions
|
|
16
|
+
* - array and object literals
|
|
17
|
+
* - arrow functions with an expression body whose parameters are
|
|
18
|
+
* identifiers or `{a, b}` patterns of allowed method names
|
|
19
|
+
*
|
|
20
|
+
* Anything else, including access to any other property, is rejected.
|
|
21
|
+
*/
|
|
22
|
+
export declare function evaluateZQLChain(text: string, root: object): unknown;
|
|
23
|
+
//# sourceMappingURL=zql-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zql-chain.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/zql-chain.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBpE"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
//#region ../analyze-query/src/zql-chain.ts
|
|
3
|
+
/**
|
|
4
|
+
* Evaluates a ZQL query written in chain form, e.g.
|
|
5
|
+
* `issue.where('open', true).related('comments', q => q.limit(10))`.
|
|
6
|
+
*
|
|
7
|
+
* The text is parsed with oxc and the resulting JavaScript AST is evaluated
|
|
8
|
+
* directly. It is never run as JavaScript. Only these constructs are
|
|
9
|
+
* accepted:
|
|
10
|
+
*
|
|
11
|
+
* - identifiers: table names (looked up on `root`), arrow function
|
|
12
|
+
* parameters, and `undefined`
|
|
13
|
+
* - calls to the query and expression builder methods in
|
|
14
|
+
* {@link ALLOWED_METHODS}, either as `x.method(...)` or through a
|
|
15
|
+
* destructured parameter such as `({cmp, or}) => or(cmp(...))`
|
|
16
|
+
* - string, number, boolean and null literals, negative numbers, and
|
|
17
|
+
* template literals without substitutions
|
|
18
|
+
* - array and object literals
|
|
19
|
+
* - arrow functions with an expression body whose parameters are
|
|
20
|
+
* identifiers or `{a, b}` patterns of allowed method names
|
|
21
|
+
*
|
|
22
|
+
* Anything else, including access to any other property, is rejected.
|
|
23
|
+
*/
|
|
24
|
+
function evaluateZQLChain(text, root) {
|
|
25
|
+
const { program, errors } = parseSync("query.js", text, {
|
|
26
|
+
lang: "js",
|
|
27
|
+
sourceType: "script"
|
|
28
|
+
});
|
|
29
|
+
if (errors.length > 0) {
|
|
30
|
+
const [error] = errors;
|
|
31
|
+
const pos = error.labels[0]?.start;
|
|
32
|
+
throw new SyntaxError(pos === void 0 ? error.message : `${error.message} at position ${pos}`);
|
|
33
|
+
}
|
|
34
|
+
const [statement, ...rest] = program.body;
|
|
35
|
+
if (statement?.type !== "ExpressionStatement" || rest.length > 0) throw new SyntaxError("Expected a single query expression");
|
|
36
|
+
return new Evaluator(text, root).evaluate(statement.expression, /* @__PURE__ */ new Map());
|
|
37
|
+
}
|
|
38
|
+
var QUERY_METHODS = {
|
|
39
|
+
where: true,
|
|
40
|
+
whereExists: true,
|
|
41
|
+
related: true,
|
|
42
|
+
orderBy: true,
|
|
43
|
+
limit: true,
|
|
44
|
+
one: true,
|
|
45
|
+
start: true,
|
|
46
|
+
run: false,
|
|
47
|
+
preload: false,
|
|
48
|
+
materialize: false
|
|
49
|
+
};
|
|
50
|
+
var EXPRESSION_BUILDER_METHODS = {
|
|
51
|
+
cmp: true,
|
|
52
|
+
cmpLit: true,
|
|
53
|
+
and: true,
|
|
54
|
+
or: true,
|
|
55
|
+
not: true,
|
|
56
|
+
exists: true,
|
|
57
|
+
eb: true
|
|
58
|
+
};
|
|
59
|
+
var ALLOWED_METHODS = new Set(Object.entries({
|
|
60
|
+
...QUERY_METHODS,
|
|
61
|
+
...EXPRESSION_BUILDER_METHODS
|
|
62
|
+
}).filter(([, allowed]) => allowed).map(([name]) => name));
|
|
63
|
+
var Evaluator = class {
|
|
64
|
+
#text;
|
|
65
|
+
#root;
|
|
66
|
+
constructor(text, root) {
|
|
67
|
+
this.#text = text;
|
|
68
|
+
this.#root = root;
|
|
69
|
+
}
|
|
70
|
+
#reject(node, what) {
|
|
71
|
+
const source = this.#text.slice(node.start, node.end);
|
|
72
|
+
throw new SyntaxError(`${what} is not allowed: \`${source}\` at position ${node.start}`);
|
|
73
|
+
}
|
|
74
|
+
evaluate(node, scope) {
|
|
75
|
+
switch (node.type) {
|
|
76
|
+
case "ParenthesizedExpression": return this.evaluate(node.expression, scope);
|
|
77
|
+
case "Literal":
|
|
78
|
+
if (node.regex || node.bigint) this.#reject(node, "Literal");
|
|
79
|
+
return node.value;
|
|
80
|
+
case "TemplateLiteral":
|
|
81
|
+
if (node.expressions.length > 0) this.#reject(node, "Template substitution");
|
|
82
|
+
return node.quasis[0].value.cooked;
|
|
83
|
+
case "UnaryExpression":
|
|
84
|
+
if (node.operator === "-" && node.argument.type === "Literal" && typeof node.argument.value === "number") return -node.argument.value;
|
|
85
|
+
return this.#reject(node, "Operator");
|
|
86
|
+
case "Identifier": return this.#lookup(node, node.name, scope);
|
|
87
|
+
case "ArrayExpression": return node.elements.map((e) => {
|
|
88
|
+
if (e === null || e.type === "SpreadElement") return this.#reject(node, "Array hole or spread");
|
|
89
|
+
return this.evaluate(e, scope);
|
|
90
|
+
});
|
|
91
|
+
case "ObjectExpression": return this.#evaluateObject(node, scope);
|
|
92
|
+
case "ArrowFunctionExpression": return this.#evaluateArrow(node, scope);
|
|
93
|
+
case "CallExpression": return this.#evaluateCall(node, scope);
|
|
94
|
+
default: return this.#reject(node, node.type);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
#lookup(node, name, scope) {
|
|
98
|
+
if (scope.has(name)) return scope.get(name);
|
|
99
|
+
if (name === "undefined") return;
|
|
100
|
+
if (!Object.hasOwn(this.#root, name)) this.#reject(node, "Unknown identifier");
|
|
101
|
+
return this.#root[name];
|
|
102
|
+
}
|
|
103
|
+
#evaluateObject(node, scope) {
|
|
104
|
+
const obj = {};
|
|
105
|
+
for (const prop of node.properties) {
|
|
106
|
+
if (prop.type !== "Property" || prop.kind !== "init" || prop.method || prop.computed) return this.#reject(prop, "Object member");
|
|
107
|
+
let key;
|
|
108
|
+
if (prop.key.type === "Identifier") key = prop.key.name;
|
|
109
|
+
else if (prop.key.type === "Literal" && typeof prop.key.value === "string") key = prop.key.value;
|
|
110
|
+
else return this.#reject(prop.key, "Object key");
|
|
111
|
+
if (key === "__proto__") return this.#reject(prop.key, "Object key");
|
|
112
|
+
obj[key] = this.evaluate(prop.value, scope);
|
|
113
|
+
}
|
|
114
|
+
return obj;
|
|
115
|
+
}
|
|
116
|
+
#evaluateArrow(node, scope) {
|
|
117
|
+
if (!node.expression || node.async) return this.#reject(node, "Arrow function with a block body");
|
|
118
|
+
const body = node.body;
|
|
119
|
+
const params = node.params.map((param) => {
|
|
120
|
+
if (param.type === "Identifier") return { name: param.name };
|
|
121
|
+
if (param.type !== "ObjectPattern") return this.#reject(param, "Parameter");
|
|
122
|
+
return { names: param.properties.map((p) => {
|
|
123
|
+
if (p.type !== "Property" || p.computed || p.value.type !== "Identifier" || !p.shorthand || p.key.type !== "Identifier" || !ALLOWED_METHODS.has(p.key.name)) return this.#reject(p, "Destructured parameter");
|
|
124
|
+
return p.key.name;
|
|
125
|
+
}) };
|
|
126
|
+
});
|
|
127
|
+
return (...args) => {
|
|
128
|
+
const inner = new Map(scope);
|
|
129
|
+
params.forEach((param, i) => {
|
|
130
|
+
if ("name" in param) inner.set(param.name, args[i]);
|
|
131
|
+
else {
|
|
132
|
+
const arg = args[i];
|
|
133
|
+
for (const name of param.names) inner.set(name, arg[name]);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return this.evaluate(body, inner);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
#evaluateCall(node, scope) {
|
|
140
|
+
if (node.optional) return this.#reject(node, "Optional call");
|
|
141
|
+
const args = node.arguments.map((a) => {
|
|
142
|
+
if (a.type === "SpreadElement") return this.#reject(a, "Spread");
|
|
143
|
+
return this.evaluate(a, scope);
|
|
144
|
+
});
|
|
145
|
+
const { callee } = node;
|
|
146
|
+
let thisValue;
|
|
147
|
+
let fn;
|
|
148
|
+
if (callee.type === "MemberExpression") {
|
|
149
|
+
if (callee.computed || callee.optional || callee.property.type !== "Identifier" || !ALLOWED_METHODS.has(callee.property.name)) return this.#reject(callee, "Method");
|
|
150
|
+
thisValue = this.evaluate(callee.object, scope);
|
|
151
|
+
if (thisValue === null || typeof thisValue !== "object") return this.#reject(callee.object, "Receiver");
|
|
152
|
+
fn = thisValue[callee.property.name];
|
|
153
|
+
} else if (callee.type === "Identifier" && scope.has(callee.name)) fn = scope.get(callee.name);
|
|
154
|
+
else return this.#reject(callee, "Callee");
|
|
155
|
+
if (typeof fn !== "function") return this.#reject(callee, "Non-function call");
|
|
156
|
+
return Reflect.apply(fn, thisValue, args);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
export { evaluateZQLChain };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=zql-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zql-chain.js","names":[],"sources":["../../../../analyze-query/src/zql-chain.ts"],"sourcesContent":["import {\n parseSync,\n type ArrowFunctionExpression,\n type Expression,\n type Node,\n type ObjectExpression,\n} from 'oxc-parser';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {ExpressionBuilder} from '../../zql/src/query/expression.ts';\nimport type {Query} from '../../zql/src/query/query.ts';\n\n/**\n * Evaluates a ZQL query written in chain form, e.g.\n * `issue.where('open', true).related('comments', q => q.limit(10))`.\n *\n * The text is parsed with oxc and the resulting JavaScript AST is evaluated\n * directly. It is never run as JavaScript. Only these constructs are\n * accepted:\n *\n * - identifiers: table names (looked up on `root`), arrow function\n * parameters, and `undefined`\n * - calls to the query and expression builder methods in\n * {@link ALLOWED_METHODS}, either as `x.method(...)` or through a\n * destructured parameter such as `({cmp, or}) => or(cmp(...))`\n * - string, number, boolean and null literals, negative numbers, and\n * template literals without substitutions\n * - array and object literals\n * - arrow functions with an expression body whose parameters are\n * identifiers or `{a, b}` patterns of allowed method names\n *\n * Anything else, including access to any other property, is rejected.\n */\nexport function evaluateZQLChain(text: string, root: object): unknown {\n const {program, errors} = parseSync('query.js', text, {\n lang: 'js',\n sourceType: 'script',\n });\n if (errors.length > 0) {\n const [error] = errors;\n const pos = error.labels[0]?.start;\n throw new SyntaxError(\n pos === undefined ? error.message : `${error.message} at position ${pos}`,\n );\n }\n const [statement, ...rest] = program.body;\n if (statement?.type !== 'ExpressionStatement' || rest.length > 0) {\n throw new SyntaxError('Expected a single query expression');\n }\n return new Evaluator(text, root).evaluate(statement.expression, new Map());\n}\n\n// Every string-keyed member of Query and ExpressionBuilder is listed here, so\n// adding or removing one fails type checking until it is classified as\n// allowed or not. Symbol keys can't be written in the query text.\ntype MethodName<T> = Extract<keyof T, string>;\n\nconst QUERY_METHODS = {\n where: true,\n whereExists: true,\n related: true,\n orderBy: true,\n limit: true,\n one: true,\n start: true,\n run: false,\n preload: false,\n materialize: false,\n} satisfies Record<MethodName<Query<string, Schema>>, boolean>;\n\nconst EXPRESSION_BUILDER_METHODS = {\n cmp: true,\n cmpLit: true,\n and: true,\n or: true,\n not: true,\n exists: true,\n eb: true,\n} satisfies Record<MethodName<ExpressionBuilder<string, Schema>>, boolean>;\n\nconst ALLOWED_METHODS: ReadonlySet<string> = new Set(\n Object.entries({...QUERY_METHODS, ...EXPRESSION_BUILDER_METHODS})\n .filter(([, allowed]) => allowed)\n .map(([name]) => name),\n);\n\ntype Scope = ReadonlyMap<string, unknown>;\n\nclass Evaluator {\n readonly #text: string;\n readonly #root: object;\n\n constructor(text: string, root: object) {\n this.#text = text;\n this.#root = root;\n }\n\n #reject(node: Node, what: string): never {\n const source = this.#text.slice(node.start, node.end);\n throw new SyntaxError(\n `${what} is not allowed: \\`${source}\\` at position ${node.start}`,\n );\n }\n\n evaluate(node: Expression, scope: Scope): unknown {\n switch (node.type) {\n case 'ParenthesizedExpression':\n return this.evaluate(node.expression, scope);\n case 'Literal':\n if (\n (node as {regex?: unknown}).regex ||\n (node as {bigint?: unknown}).bigint\n ) {\n this.#reject(node, 'Literal');\n }\n return node.value;\n case 'TemplateLiteral':\n if (node.expressions.length > 0) {\n this.#reject(node, 'Template substitution');\n }\n return node.quasis[0].value.cooked;\n case 'UnaryExpression':\n if (\n node.operator === '-' &&\n node.argument.type === 'Literal' &&\n typeof node.argument.value === 'number'\n ) {\n return -node.argument.value;\n }\n return this.#reject(node, 'Operator');\n case 'Identifier':\n return this.#lookup(node, node.name, scope);\n case 'ArrayExpression':\n return node.elements.map(e => {\n if (e === null || e.type === 'SpreadElement') {\n return this.#reject(node, 'Array hole or spread');\n }\n return this.evaluate(e, scope);\n });\n case 'ObjectExpression':\n return this.#evaluateObject(node, scope);\n case 'ArrowFunctionExpression':\n return this.#evaluateArrow(node, scope);\n case 'CallExpression':\n return this.#evaluateCall(node, scope);\n default:\n return this.#reject(node, node.type);\n }\n }\n\n #lookup(node: Node, name: string, scope: Scope): unknown {\n if (scope.has(name)) {\n return scope.get(name);\n }\n if (name === 'undefined') {\n return undefined;\n }\n if (!Object.hasOwn(this.#root, name)) {\n this.#reject(node, 'Unknown identifier');\n }\n return (this.#root as Record<string, unknown>)[name];\n }\n\n #evaluateObject(node: ObjectExpression, scope: Scope): unknown {\n const obj: Record<string, unknown> = {};\n for (const prop of node.properties) {\n if (\n prop.type !== 'Property' ||\n prop.kind !== 'init' ||\n prop.method ||\n prop.computed\n ) {\n return this.#reject(prop, 'Object member');\n }\n let key: string;\n if (prop.key.type === 'Identifier') {\n key = prop.key.name;\n } else if (\n prop.key.type === 'Literal' &&\n typeof prop.key.value === 'string'\n ) {\n key = prop.key.value;\n } else {\n return this.#reject(prop.key, 'Object key');\n }\n if (key === '__proto__') {\n return this.#reject(prop.key, 'Object key');\n }\n obj[key] = this.evaluate(prop.value as Expression, scope);\n }\n return obj;\n }\n\n #evaluateArrow(node: ArrowFunctionExpression, scope: Scope): unknown {\n if (!node.expression || node.async) {\n return this.#reject(node, 'Arrow function with a block body');\n }\n const body = node.body as Expression;\n const params = node.params.map(param => {\n if (param.type === 'Identifier') {\n return {name: param.name};\n }\n if (param.type !== 'ObjectPattern') {\n return this.#reject(param, 'Parameter');\n }\n return {\n names: param.properties.map(p => {\n if (\n p.type !== 'Property' ||\n p.computed ||\n p.value.type !== 'Identifier' ||\n !p.shorthand ||\n p.key.type !== 'Identifier' ||\n !ALLOWED_METHODS.has(p.key.name)\n ) {\n return this.#reject(p, 'Destructured parameter');\n }\n return p.key.name;\n }),\n };\n });\n return (...args: unknown[]) => {\n const inner = new Map(scope);\n params.forEach((param, i) => {\n if ('name' in param) {\n inner.set(param.name, args[i]);\n } else {\n const arg = args[i] as Record<string, unknown>;\n for (const name of param.names) {\n inner.set(name, arg[name]);\n }\n }\n });\n return this.evaluate(body, inner);\n };\n }\n\n #evaluateCall(\n node: Extract<Expression, {type: 'CallExpression'}>,\n scope: Scope,\n ): unknown {\n if (node.optional) {\n return this.#reject(node, 'Optional call');\n }\n const args = node.arguments.map(a => {\n if (a.type === 'SpreadElement') {\n return this.#reject(a, 'Spread');\n }\n return this.evaluate(a, scope);\n });\n const {callee} = node;\n let thisValue: unknown;\n let fn: unknown;\n if (callee.type === 'MemberExpression') {\n if (\n callee.computed ||\n callee.optional ||\n callee.property.type !== 'Identifier' ||\n !ALLOWED_METHODS.has(callee.property.name)\n ) {\n return this.#reject(callee, 'Method');\n }\n thisValue = this.evaluate(callee.object, scope);\n if (thisValue === null || typeof thisValue !== 'object') {\n return this.#reject(callee.object, 'Receiver');\n }\n fn = (thisValue as Record<string, unknown>)[callee.property.name];\n } else if (callee.type === 'Identifier' && scope.has(callee.name)) {\n fn = scope.get(callee.name);\n } else {\n return this.#reject(callee, 'Callee');\n }\n if (typeof fn !== 'function') {\n return this.#reject(callee, 'Non-function call');\n }\n return Reflect.apply(fn, thisValue, args);\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,9 +2,9 @@ import { valita_exports } from "../../shared/src/valita.js";
|
|
|
2
2
|
import { mapAST } from "../../zero-protocol/src/ast.js";
|
|
3
3
|
import { serverToClient } from "../../zero-schema/src/name-mapper.js";
|
|
4
4
|
import { parseOptions } from "../../shared/src/options.js";
|
|
5
|
+
import { loadSchemaAndPermissions } from "../../zero-cache/src/scripts/permissions.js";
|
|
5
6
|
import { astToZQL } from "./ast-to-zql.js";
|
|
6
7
|
import { formatOutput } from "./format.js";
|
|
7
|
-
import { loadSchemaAndPermissions } from "../../zero-cache/src/scripts/permissions.js";
|
|
8
8
|
import process from "node:process";
|
|
9
9
|
import { readFile } from "node:fs/promises";
|
|
10
10
|
import { createInterface } from "node:readline";
|
package/out/zero/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region package.json
|
|
2
2
|
var name = "@rocicorp/zero";
|
|
3
|
-
var version = "1.12.0-canary.
|
|
3
|
+
var version = "1.12.0-canary.5";
|
|
4
4
|
var description = "Zero is a web framework for serverless web development.";
|
|
5
5
|
var homepage = "https://zero.rocicorp.dev";
|
|
6
6
|
var bugs = { "url": "https://bugs.rocicorp.dev" };
|
|
@@ -12,7 +12,6 @@ var repository = {
|
|
|
12
12
|
"directory": "packages/zero"
|
|
13
13
|
};
|
|
14
14
|
var bin = {
|
|
15
|
-
"analyze-query": "./out/zero/src/analyze-query.js",
|
|
16
15
|
"ast-to-zql": "./out/zero/src/ast-to-zql.js",
|
|
17
16
|
"transform-query": "./out/zero/src/transform-query.js",
|
|
18
17
|
"zero-build-schema": "./out/zero/src/build-schema.js",
|
|
@@ -118,10 +117,10 @@ var dependencies = {
|
|
|
118
117
|
"@badrap/valita": "0.5.5",
|
|
119
118
|
"@databases/escape-identifier": "^1.0.3",
|
|
120
119
|
"@databases/sql": "^3.3.0",
|
|
121
|
-
"@dotenvx/dotenvx": "^1.
|
|
120
|
+
"@dotenvx/dotenvx": "^1.66.0",
|
|
122
121
|
"@drdgvhbh/postgres-error-codes": "^0.0.6",
|
|
123
|
-
"@fastify/cors": "^10.
|
|
124
|
-
"@fastify/websocket": "^11.
|
|
122
|
+
"@fastify/cors": "^10.1.0",
|
|
123
|
+
"@fastify/websocket": "^11.2.0",
|
|
125
124
|
"@google-cloud/precise-date": "^4.0.0",
|
|
126
125
|
"@opentelemetry/api": "^1.9.1",
|
|
127
126
|
"@opentelemetry/api-logs": "^0.223.0",
|
|
@@ -137,45 +136,45 @@ var dependencies = {
|
|
|
137
136
|
"@rocicorp/logger": "^6.1.0",
|
|
138
137
|
"@rocicorp/resolver": "^1.0.2",
|
|
139
138
|
"@rocicorp/zero-sqlite3": "^1.1.7",
|
|
140
|
-
"@standard-schema/spec": "^1.
|
|
139
|
+
"@standard-schema/spec": "^1.1.0",
|
|
141
140
|
"@types/basic-auth": "^1.1.8",
|
|
142
|
-
"@types/ws": "^8.
|
|
141
|
+
"@types/ws": "^8.18.1",
|
|
143
142
|
"basic-auth": "^2.0.1",
|
|
144
|
-
"chalk-template": "^1.1.
|
|
145
|
-
"chokidar": "^4.0.
|
|
143
|
+
"chalk-template": "^1.1.2",
|
|
144
|
+
"chokidar": "^4.0.3",
|
|
146
145
|
"cloudevents": "^10.0.0",
|
|
147
|
-
"command-line-args": "^6.0.
|
|
148
|
-
"command-line-usage": "^7.0.
|
|
146
|
+
"command-line-args": "^6.0.2",
|
|
147
|
+
"command-line-usage": "^7.0.4",
|
|
149
148
|
"compare-utf8": "^0.2.3",
|
|
150
|
-
"defu": "^6.1.
|
|
151
|
-
"eventemitter3": "^5.0.
|
|
149
|
+
"defu": "^6.1.7",
|
|
150
|
+
"eventemitter3": "^5.0.4",
|
|
152
151
|
"fastify": "^5.12.7",
|
|
153
152
|
"is-in-subnet": "^4.0.1",
|
|
154
|
-
"jose": "^5.
|
|
153
|
+
"jose": "^5.10.0",
|
|
155
154
|
"json-custom-numbers": "^3.1.1",
|
|
156
|
-
"kasi": "^1.1.
|
|
155
|
+
"kasi": "^1.1.2",
|
|
157
156
|
"nanoid": "^5.1.16",
|
|
158
|
-
"
|
|
157
|
+
"oxc-parser": "^0.129.0",
|
|
158
|
+
"oxfmt": "^0.74.0",
|
|
159
159
|
"parse-prometheus-text-format": "^1.1.1",
|
|
160
160
|
"pg-format": "npm:pg-format-fix@^1.0.5",
|
|
161
161
|
"postgres": "3.4.7",
|
|
162
|
-
"semver": "^7.
|
|
162
|
+
"semver": "^7.8.0",
|
|
163
163
|
"url-pattern": "^1.0.3",
|
|
164
164
|
"urlpattern-polyfill": "^10.1.0",
|
|
165
165
|
"ws": "^8.22.0"
|
|
166
166
|
};
|
|
167
167
|
var devDependencies = {
|
|
168
|
-
"@op-engineering/op-sqlite": "
|
|
168
|
+
"@op-engineering/op-sqlite": "^18.2.5",
|
|
169
169
|
"analyze-query": "workspace:*",
|
|
170
170
|
"ast-to-zql": "workspace:*",
|
|
171
171
|
"expo": "^58.0.14",
|
|
172
|
-
"expo-sqlite": "
|
|
173
|
-
"oxc-parser": "^0.129.0",
|
|
172
|
+
"expo-sqlite": "^58.0.13",
|
|
174
173
|
"react-native": "^0.88.0",
|
|
175
174
|
"replicache": "workspace:*",
|
|
176
175
|
"shared": "workspace:*",
|
|
177
|
-
"syncpack": "^14.3.
|
|
178
|
-
"typedoc": "^0.28.
|
|
176
|
+
"syncpack": "^14.3.1",
|
|
177
|
+
"typedoc": "^0.28.19",
|
|
179
178
|
"typescript": "~7.0.2",
|
|
180
179
|
"vite": "^8.3.4",
|
|
181
180
|
"vitest": "^5.0.0",
|
package/out/zero/package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.12.0-canary.
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.12.0-canary.5\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"license\": \"Apache-2.0\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"bin\": {\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\",\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ],\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./analyze\": {\n \"types\": \"./out/zero/src/analyze.d.ts\",\n \"default\": \"./out/zero/src/analyze.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/kysely\": {\n \"types\": \"./out/zero/src/adapters/kysely.d.ts\",\n \"default\": \"./out/zero/src/adapters/kysely.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --quiet --config ../../oxlint.config.ts src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\",\n \"fmt\": \"oxfmt .\",\n \"check-fmt\": \"oxfmt --check .\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.5.5\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.66.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.1.0\",\n \"@fastify/websocket\": \"^11.2.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.1\",\n \"@opentelemetry/api-logs\": \"^0.223.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.81.2\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.223.0\",\n \"@opentelemetry/resources\": \"^2.12.0\",\n \"@opentelemetry/sdk-metrics\": \"^2.12.0\",\n \"@opentelemetry/sdk-node\": \"^0.223.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.12.0\",\n \"@opentelemetry/semantic-conventions\": \"^1.43.0\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^6.1.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.1.7\",\n \"@standard-schema/spec\": \"^1.1.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.18.1\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk-template\": \"^1.1.2\",\n \"chokidar\": \"^4.0.3\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.2\",\n \"command-line-usage\": \"^7.0.4\",\n \"compare-utf8\": \"^0.2.3\",\n \"defu\": \"^6.1.7\",\n \"eventemitter3\": \"^5.0.4\",\n \"fastify\": \"^5.12.7\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.10.0\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.2\",\n \"nanoid\": \"^5.1.16\",\n \"oxc-parser\": \"^0.129.0\",\n \"oxfmt\": \"^0.74.0\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.8.0\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.22.0\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \"^18.2.5\",\n \"analyze-query\": \"workspace:*\",\n \"ast-to-zql\": \"workspace:*\",\n \"expo\": \"^58.0.14\",\n \"expo-sqlite\": \"^58.0.13\",\n \"react-native\": \"^0.88.0\",\n \"replicache\": \"workspace:*\",\n \"shared\": \"workspace:*\",\n \"syncpack\": \"^14.3.1\",\n \"typedoc\": \"^0.28.19\",\n \"typescript\": \"~7.0.2\",\n \"vite\": \"^8.3.4\",\n \"vitest\": \"^5.0.0\",\n \"zero-cache\": \"workspace:*\",\n \"zero-client\": \"workspace:*\",\n \"zero-pg\": \"workspace:*\",\n \"zero-react\": \"workspace:*\",\n \"zero-server\": \"workspace:*\",\n \"zero-solid\": \"workspace:*\",\n \"zqlite\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@11.11.0\"\n}\n"],"mappings":""}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse } from "../../../shared/src/valita.js";
|
|
2
|
-
import { computeZqlSpecs } from "../db/lite-tables.js";
|
|
3
2
|
import { permissionsConfigSchema } from "../../../zero-schema/src/compiled-permissions.js";
|
|
3
|
+
import { computeZqlSpecs } from "../db/lite-tables.js";
|
|
4
4
|
import { elide } from "../types/strings.js";
|
|
5
5
|
//#region ../zero-cache/src/auth/load-permissions.ts
|
|
6
6
|
function loadPermissions(lc, replica, appID, config) {
|
|
@@ -12,8 +12,8 @@ import { compile, sql } from "../../../zqlite/src/internal/sql.js";
|
|
|
12
12
|
import { TableSource, fromSQLiteTypes } from "../../../zqlite/src/table-source.js";
|
|
13
13
|
import { mapLiteDataTypeToZqlSchemaValue } from "../types/lite.js";
|
|
14
14
|
import { computeZqlSpecs } from "../db/lite-tables.js";
|
|
15
|
-
import { getSchema, reloadPermissionsIfChanged } from "./load-permissions.js";
|
|
16
15
|
import { StatementRunner } from "../db/statements.js";
|
|
16
|
+
import { getSchema, reloadPermissionsIfChanged } from "./load-permissions.js";
|
|
17
17
|
//#region ../zero-cache/src/auth/write-authorizer.ts
|
|
18
18
|
var WriteAuthorizerImpl = class {
|
|
19
19
|
#schema;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { logOptions } from "../../../otel/src/log-options.js";
|
|
2
2
|
import { cdcSchema, cvrSchema, getShardID } from "../types/shards.js";
|
|
3
3
|
import { appOptions, shardOptions, zeroOptions } from "../config/zero-config.js";
|
|
4
|
-
import { id } from "../types/sql.js";
|
|
5
4
|
import { pgClient } from "../types/pg.js";
|
|
5
|
+
import { id } from "../types/sql.js";
|
|
6
6
|
import { decommissionShard } from "../services/change-source/pg/decommission.js";
|
|
7
7
|
//#region ../zero-cache/src/scripts/decommission.ts
|
|
8
8
|
var decommissionOptions = {
|
|
@@ -6,10 +6,10 @@ import { colorConsole, createLogContext } from "../../../shared/src/logging.js";
|
|
|
6
6
|
import { parseOptions } from "../../../shared/src/options.js";
|
|
7
7
|
import { appSchema, getShardID, upstreamSchema } from "../types/shards.js";
|
|
8
8
|
import { ZERO_ENV_VAR_PREFIX } from "../config/zero-config.js";
|
|
9
|
-
import { liteTableName } from "../types/names.js";
|
|
10
9
|
import "../../../zero-schema/src/compiled-permissions.js";
|
|
11
10
|
import { deployPermissionsOptions, loadSchemaAndPermissions } from "./permissions.js";
|
|
12
11
|
import { pgClient } from "../types/pg.js";
|
|
12
|
+
import { liteTableName } from "../types/names.js";
|
|
13
13
|
import { runTx } from "../db/run-transaction.js";
|
|
14
14
|
import { getPublicationInfo } from "../services/change-source/pg/schema/published.js";
|
|
15
15
|
import { SHARD_CONFIG_TABLE, ensureGlobalTables } from "../services/change-source/pg/schema/shard.js";
|
|
@@ -5,9 +5,9 @@ import { broadcastWorker, childWorker, parentWorker, singleProcessMode } from ".
|
|
|
5
5
|
import { getShardConfig } from "../types/shards.js";
|
|
6
6
|
import { getNormalizedZeroConfig } from "../config/zero-config.js";
|
|
7
7
|
import { DatabaseInitError } from "../../../zqlite/src/db.js";
|
|
8
|
-
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
9
8
|
import { connectPgClient } from "../types/pg.js";
|
|
10
9
|
import { registerSQLiteCorruptionDiagnosticTarget } from "../db/sqlite-corruption.js";
|
|
10
|
+
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
11
11
|
import { SupportingProcesses, exitAfter, runUntilKilled } from "../services/life-cycle.js";
|
|
12
12
|
import { createLogContext } from "./logging.js";
|
|
13
13
|
import { REPLICATOR_URL } from "./worker-urls.js";
|
|
@@ -6,8 +6,8 @@ import { parentWorker, singleProcessMode } from "../types/processes.js";
|
|
|
6
6
|
import { getShardConfig } from "../types/shards.js";
|
|
7
7
|
import { getNormalizedZeroConfig } from "../config/zero-config.js";
|
|
8
8
|
import { Database } from "../../../zqlite/src/db.js";
|
|
9
|
-
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
10
9
|
import { registerSQLiteCorruptionDiagnosticTarget } from "../db/sqlite-corruption.js";
|
|
10
|
+
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
11
11
|
import { SupportingProcesses, exitAfter, runUntilKilled } from "../services/life-cycle.js";
|
|
12
12
|
import { createLogContext } from "./logging.js";
|
|
13
13
|
import { installProfileHandler } from "../types/profiler.js";
|
|
@@ -6,11 +6,9 @@ import { parentWorker, singleProcessMode } from "../types/processes.js";
|
|
|
6
6
|
import { getShardID } from "../types/shards.js";
|
|
7
7
|
import { getNormalizedZeroConfig } from "../config/zero-config.js";
|
|
8
8
|
import { randInt } from "../../../shared/src/rand.js";
|
|
9
|
-
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
10
|
-
import { Snapshotter } from "../services/view-syncer/snapshotter.js";
|
|
11
|
-
import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
|
|
12
9
|
import { connectPgClient } from "../types/pg.js";
|
|
13
10
|
import { registerSQLiteCorruptionDiagnosticTarget } from "../db/sqlite-corruption.js";
|
|
11
|
+
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
14
12
|
import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
|
|
15
13
|
import { createLogContext } from "./logging.js";
|
|
16
14
|
import { installProfileHandler } from "../types/profiler.js";
|
|
@@ -28,6 +26,8 @@ import { PusherService } from "../services/mutagen/pusher.js";
|
|
|
28
26
|
import { BatchPacer } from "../services/view-syncer/batch-pacer.js";
|
|
29
27
|
import { ConnectionContextManagerImpl } from "../services/view-syncer/connection-context-manager.js";
|
|
30
28
|
import { DeferredWritesBudget } from "../services/view-syncer/deferred-writes-budget.js";
|
|
29
|
+
import { Snapshotter } from "../services/view-syncer/snapshotter.js";
|
|
30
|
+
import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
|
|
31
31
|
import { QueryStats } from "../services/view-syncer/query-stats.js";
|
|
32
32
|
import { SnapshotRowCache } from "../services/view-syncer/snapshot-row-cache.js";
|
|
33
33
|
import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
|