orez 0.2.19 → 0.2.24

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.
@@ -1,4 +1,6 @@
1
+ import type { HttpRequest, HttpResponse } from './browser-admin.js';
1
2
  import type { PGlite } from '@electric-sql/pglite';
3
+ export type { HttpRequest, HttpResponse } from './browser-admin.js';
2
4
  export interface ZeroCacheEmbedBrowserOptions {
3
5
  /** PGlite instance */
4
6
  pglite: PGlite;
@@ -16,17 +18,16 @@ export interface ZeroCacheEmbedBrowserOptions {
16
18
  env?: Record<string, string>;
17
19
  /** timeout in ms waiting for zero-cache ready (default: 30000) */
18
20
  readyTimeout?: number;
19
- }
20
- export interface HttpRequest {
21
- method: string;
22
- url: string;
23
- headers?: Record<string, string>;
24
- body?: string | null;
25
- }
26
- export interface HttpResponse {
27
- status: number;
28
- headers: Record<string, string>;
29
- body: string;
21
+ /**
22
+ * intercept browser-mode orez admin routes before they reach zero-cache.
23
+ *
24
+ * browser embeds do not run the node admin dashboard or log store. leaving
25
+ * `/__orez/*` to fall through to zero-cache makes disabled admin look like
26
+ * an app/zero route miss. keep the contract explicit by returning empty
27
+ * admin responses for the small read-only surface and 404 for the rest.
28
+ * default: true.
29
+ */
30
+ disableAdminApi?: boolean;
30
31
  }
31
32
  /** WebSocket-like object — matches CF WebSocket, browser WebSocket, or MessagePort adapter */
32
33
  interface WsLike {
@@ -54,5 +55,4 @@ export interface ZeroCacheEmbedBrowser {
54
55
  stop(): Promise<void>;
55
56
  }
56
57
  export declare function startZeroCacheEmbedBrowser(opts: ZeroCacheEmbedBrowserOptions): Promise<ZeroCacheEmbedBrowser>;
57
- export {};
58
58
  //# sourceMappingURL=browser-embed.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-embed.d.ts","sourceRoot":"","sources":["../../src/worker/browser-embed.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAOlD,MAAM,WAAW,4BAA4B;IAC3C,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IAEd;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IAEvB,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE5B,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,8FAA8F;AAC9F,UAAU,MAAM;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAA;IACxD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAA;IACnE,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAA;CACvE;AAED,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAE/C;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAEvD,sBAAsB;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,qBAAqB,CAAC,CA6NhC"}
1
+ {"version":3,"file":"browser-embed.d.ts","sourceRoot":"","sources":["../../src/worker/browser-embed.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAOnE,MAAM,WAAW,4BAA4B;IAC3C,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IAEd;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IAEvB,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE5B,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,8FAA8F;AAC9F,UAAU,MAAM;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAA;IACxD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAA;IACnE,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAA;CACvE;AAED,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAE/C;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAEvD,sBAAsB;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAmOhC"}
@@ -32,11 +32,13 @@ import EventEmitter from 'node:events';
32
32
  // static import so the bundler can follow the dependency tree.
33
33
  // @ts-expect-error — internal zero-cache module, no type declarations
34
34
  import { runWorker as _runWorker } from '@rocicorp/zero/out/zero-cache/src/server/runner/run-worker.js';
35
+ import { handleDisabledBrowserAdminRequest } from './browser-admin.js';
35
36
  const runWorkerFn = _runWorker;
36
37
  export async function startZeroCacheEmbedBrowser(opts) {
37
38
  const appId = opts.appId || 'zero';
38
39
  const publications = opts.publications?.join(',') || `orez_${appId}_public`;
39
40
  const readyTimeout = opts.readyTimeout ?? 30000;
41
+ const disableAdminApi = opts.disableAdminApi ?? true;
40
42
  // set up sqlite storage from sql.js or in-memory
41
43
  if (opts.sqlite) {
42
44
  // consumer provided a sql.js Database — create adapter
@@ -196,6 +198,11 @@ export async function startZeroCacheEmbedBrowser(opts) {
196
198
  }
197
199
  },
198
200
  async handleHttp(request) {
201
+ if (disableAdminApi) {
202
+ const adminResponse = handleDisabledBrowserAdminRequest(request);
203
+ if (adminResponse)
204
+ return adminResponse;
205
+ }
199
206
  if (!isReady || !fastifyInstance?.inject) {
200
207
  return { status: 503, headers: {}, body: 'not ready' };
201
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"browser-embed.js","sourceRoot":"","sources":["../../src/worker/browser-embed.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-embed.js","sourceRoot":"","sources":["../../src/worker/browser-embed.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orez",
3
- "version": "0.2.19",
3
+ "version": "0.2.24",
4
4
  "description": "PGlite-powered zero-sync development backend. No Docker required.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -63,12 +63,23 @@
63
63
  "test:chat": "bun scripts/test-chat-integration.ts",
64
64
  "test:chat:smoke": "bun scripts/test-chat-integration.ts --smoke",
65
65
  "test:chat:e2e": "bun scripts/test-chat-e2e.ts",
66
- "release": "bun scripts/release.ts"
66
+ "release": "bun scripts/release.ts",
67
+ "perf:load": "bun run perf/load/harness.ts",
68
+ "perf:memory": "bun run perf/memory/profile.ts",
69
+ "perf:bench": "bun run perf/scripts/bench-all.ts",
70
+ "perf:overhead": "bun run perf/scripts/bench-proxy-overhead.ts",
71
+ "perf:soak": "bun run perf/stability/soak.ts",
72
+ "perf:crash": "bun run perf/stability/crash-recovery.ts",
73
+ "perf:correctness": "bun test perf/stability/correctness.test.ts",
74
+ "perf:diagnose": "bun run perf/scripts/diagnose.ts",
75
+ "perf:quick": "bun run perf/scripts/quick-check.ts",
76
+ "perf:all": "bun run perf:bench && bun run perf:overhead && bun run perf:correctness && bun run perf:crash",
77
+ "perf:ci": "bun run perf:bench -- --output=perf/reports/bench.json && bun run perf:overhead && bun run perf:correctness"
67
78
  },
68
79
  "dependencies": {
69
80
  "@electric-sql/pglite": "0.4.1",
70
81
  "@electric-sql/pglite-tools": "^0.3.1",
71
- "bedrock-sqlite": "0.2.19",
82
+ "bedrock-sqlite": "0.2.24",
72
83
  "citty": "^0.2.0",
73
84
  "pg-gateway": "0.3.0-beta.4",
74
85
  "pgsql-parser": "^17.9.11",
package/src/browser.ts CHANGED
@@ -35,6 +35,12 @@ export interface OrezBrowserConfig {
35
35
 
36
36
  /** log level */
37
37
  logLevel?: string
38
+
39
+ /**
40
+ * intercept browser-mode orez admin routes before they reach zero-cache.
41
+ * default: true.
42
+ */
43
+ disableAdminApi?: boolean
38
44
  }
39
45
 
40
46
  export interface OrezBrowserInstance {
@@ -157,6 +163,7 @@ export async function startOrezBrowser(
157
163
  pglite: pgPostgres as unknown as PGlite,
158
164
  appId,
159
165
  publications: config.publications,
166
+ disableAdminApi: config.disableAdminApi ?? true,
160
167
  env: {
161
168
  ZERO_LOG_LEVEL: config.logLevel || 'info',
162
169
  },
package/src/pg-proxy.ts CHANGED
@@ -45,6 +45,17 @@ const SCHEMA_CACHE_TTL_MS = 30_000
45
45
  // performance tracking
46
46
  const proxyStats = { totalWaitMs: 0, totalExecMs: 0, count: 0, batches: 0 }
47
47
 
48
+ // query classification cache — avoids re-running regex on every repeated query.
49
+ // keys are trimmed+lowercased original query texts (before rewrites).
50
+ // entries are invalidated on DDL (schema changes may affect classification).
51
+ interface QueryClass {
52
+ isWrite: boolean
53
+ isDDL: boolean
54
+ isCacheable: boolean
55
+ }
56
+ const queryClassCache = new Map<string, QueryClass>()
57
+ const QUERY_CLASS_CACHE_MAX = 500
58
+
48
59
  // query classification helpers — operate on pre-normalized (trimmed+lowercased) query strings
49
60
  const SCHEMA_QUERY_MARKERS = [
50
61
  'information_schema.',
@@ -86,6 +97,33 @@ function isDDLNormalized(q: string): boolean {
86
97
  return false
87
98
  }
88
99
 
100
+ /**
101
+ * classify a query and cache the result.
102
+ * repeated queries (common in app workloads) hit the cache and skip all regex.
103
+ * invalidated on DDL (schema changes may reclassify queries).
104
+ */
105
+ function classifyQuery(q: string): QueryClass {
106
+ const cached = queryClassCache.get(q)
107
+ if (cached) return cached
108
+
109
+ const result: QueryClass = {
110
+ isWrite: isWriteNormalized(q),
111
+ isDDL: isDDLNormalized(q),
112
+ isCacheable: isCacheableNormalized(q),
113
+ }
114
+
115
+ // LRU-ish eviction: clear oldest half when full
116
+ if (queryClassCache.size >= QUERY_CLASS_CACHE_MAX) {
117
+ const keys = [...queryClassCache.keys()]
118
+ for (let i = 0; i < Math.floor(keys.length / 2); i++) {
119
+ queryClassCache.delete(keys[i])
120
+ }
121
+ }
122
+
123
+ queryClassCache.set(q, result)
124
+ return result
125
+ }
126
+
89
127
  function extractQueryText(data: Uint8Array): string | null {
90
128
  if (data[0] === 0x51) {
91
129
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
@@ -100,6 +138,7 @@ function extractQueryText(data: Uint8Array): string | null {
100
138
 
101
139
  function invalidateSchemaCache() {
102
140
  schemaQueryCache.clear()
141
+ queryClassCache.clear()
103
142
  }
104
143
 
105
144
  // abort previous replication handler when a new one starts
@@ -300,22 +339,6 @@ function interceptQuery(data: Uint8Array): Uint8Array {
300
339
  return data
301
340
  }
302
341
 
303
- /**
304
- * check if a query should be intercepted as a no-op.
305
- */
306
- function isNoopQuery(data: Uint8Array): boolean {
307
- let query: string | null = null
308
- if (data[0] === 0x51) {
309
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
310
- const len = view.getInt32(1)
311
- query = textDecoder.decode(data.subarray(5, 1 + len - 1)).replace(/\0$/, '')
312
- } else if (data[0] === 0x50) {
313
- query = extractParseQuery(data)
314
- }
315
- if (!query) return false
316
- return NOOP_QUERY_PATTERNS.some((p) => p.test(query!))
317
- }
318
-
319
342
  /**
320
343
  * build a synthetic "SET" command complete response.
321
344
  */
@@ -495,6 +518,7 @@ function extractNoticeCode(
495
518
  /**
496
519
  * single-pass response message filter. strips ReadyForQuery messages (when
497
520
  * stripRfq=true) and benign transaction state warnings in one scan.
521
+ * returns the original buffer unchanged when nothing was stripped.
498
522
  */
499
523
  function stripResponseMessages(data: Uint8Array, stripRfq: boolean): Uint8Array {
500
524
  if (data.length === 0) return data
@@ -837,43 +861,50 @@ export async function startPgProxy(
837
861
 
838
862
  // Simple Query (0x51) or standalone Sync — per-message mutex
839
863
 
840
- // fast-path for ping queries (SELECT 1, SELECT 2, etc.)
841
- // zero-cache fires these in parallel during warmup — bypass mutex entirely
864
+ // extract query text ONCE for all checks (ping, noop, classification, caching)
865
+ let queryText: string | null = null
866
+ let queryClass: QueryClass | null = null
867
+ // cache/dedup key — the rewritten query when a rewrite applies, else
868
+ // the original. read and write paths MUST use the same key.
869
+ let schemaCacheKey: string | null = null
842
870
  if (msgType === 0x51) {
843
- const queryText = extractQueryText(data)
871
+ queryText = extractQueryText(data)
844
872
  if (queryText) {
873
+ // fast-path: ping queries — bypass mutex entirely
845
874
  const pingMatch = queryText.match(PING_QUERY_RE)
846
875
  if (pingMatch) {
847
876
  return buildSelectIntResponse(pingMatch[1])
848
877
  }
849
- }
850
- }
851
878
 
852
- // check for no-op queries (only SimpleQuery has queries worth intercepting)
853
- if (isNoopQuery(data)) {
854
- if (msgType === 0x51) {
855
- return buildSetCompleteResponse()
856
- }
857
- }
858
-
859
- // intercept and rewrite queries
860
- data = interceptQuery(data)
861
-
862
- // normalize query once for all classification checks
863
- const isSimpleQuery = msgType === 0x51
864
- const queryText = isSimpleQuery ? extractQueryText(data) : null
865
- const queryNorm = queryText ? queryText.trimStart().toLowerCase() : null
866
- const cacheable = queryNorm && isCacheableNormalized(queryNorm)
879
+ // fast-path: no-op queries synthetic response, no mutex
880
+ if (NOOP_QUERY_PATTERNS.some((p) => p.test(queryText!))) {
881
+ return buildSetCompleteResponse()
882
+ }
867
883
 
868
- // cache Simple Query schema queries
869
- if (cacheable) {
870
- const cached = schemaQueryCache.get(queryText!)
871
- if (cached && Date.now() < cached.expiresAt) {
872
- return stripResponseMessages(cached.result, false)
873
- }
874
- const inflight = schemaQueryInFlight.get(queryText!)
875
- if (inflight) {
876
- return stripResponseMessages(await inflight, false)
884
+ // normalize once, classify once (cached for repeated queries)
885
+ queryClass = classifyQuery(queryText.trimStart().toLowerCase())
886
+
887
+ // schema query cache: identical information_schema queries deduplicated
888
+ if (queryClass.isCacheable) {
889
+ // apply rewrites before caching (version() etc. change the query)
890
+ const rewritten = applyRewrites(queryText)
891
+ schemaCacheKey = rewritten !== queryText ? rewritten : queryText
892
+ const cached = schemaQueryCache.get(schemaCacheKey)
893
+ if (cached && Date.now() < cached.expiresAt) {
894
+ return stripResponseMessages(cached.result, false)
895
+ }
896
+ const inflight = schemaQueryInFlight.get(schemaCacheKey)
897
+ if (inflight) {
898
+ return stripResponseMessages(await inflight, false)
899
+ }
900
+ // rewrite data for execution
901
+ if (rewritten !== queryText) {
902
+ data = rebuildSimpleQuery(rewritten)
903
+ }
904
+ } else {
905
+ // apply query rewrites for non-cacheable queries
906
+ data = interceptQuery(data)
907
+ }
877
908
  }
878
909
  }
879
910
 
@@ -914,21 +945,22 @@ export async function startPgProxy(
914
945
  }
915
946
 
916
947
  let result: Uint8Array
917
- if (cacheable) {
948
+ const cacheable = queryClass?.isCacheable ?? false
949
+ if (cacheable && schemaCacheKey) {
918
950
  const promise = execute()
919
- schemaQueryInFlight.set(queryText!, promise)
951
+ schemaQueryInFlight.set(schemaCacheKey, promise)
920
952
  try {
921
953
  result = await promise
922
- schemaQueryCache.set(queryText!, {
954
+ schemaQueryCache.set(schemaCacheKey, {
923
955
  result,
924
956
  expiresAt: Date.now() + SCHEMA_CACHE_TTL_MS,
925
957
  })
926
958
  } finally {
927
- schemaQueryInFlight.delete(queryText!)
959
+ schemaQueryInFlight.delete(schemaCacheKey)
928
960
  }
929
961
  } else {
930
962
  result = await execute()
931
- if (queryNorm && isDDLNormalized(queryNorm)) {
963
+ if (queryClass?.isDDL) {
932
964
  invalidateSchemaCache()
933
965
  }
934
966
  }
@@ -937,7 +969,7 @@ export async function startPgProxy(
937
969
  result = stripResponseMessages(result, stripRfq)
938
970
 
939
971
  // signal replication handler on postgres writes for instant sync
940
- if (dbName === 'postgres' && queryNorm && isWriteNormalized(queryNorm)) {
972
+ if (dbName === 'postgres' && queryClass?.isWrite) {
941
973
  signalReplicationChange()
942
974
  }
943
975
 
@@ -115,7 +115,6 @@ async function ensurePublication(db: {
115
115
  }
116
116
  }
117
117
 
118
- // pglite startParams replaces defaults, so always include required flags
119
118
  const PGLITE_BASE_FLAGS = [
120
119
  '--single',
121
120
  '-F',
@@ -127,6 +126,27 @@ const PGLITE_BASE_FLAGS = [
127
126
  'exit_on_error=false',
128
127
  '-c',
129
128
  'log_checkpoints=false',
129
+ '-c',
130
+ 'jit=off',
131
+ '-c',
132
+ 'max_connections=5',
133
+ '-c',
134
+ 'temp_buffers=1MB',
135
+ ]
136
+
137
+ // main instance: tuned for development (matching soot browser config)
138
+ const MAIN_START_PARAMS = [
139
+ ...PGLITE_BASE_FLAGS,
140
+ '-c',
141
+ 'shared_buffers=1MB',
142
+ '-c',
143
+ 'wal_buffers=64kB',
144
+ '-c',
145
+ 'work_mem=1MB',
146
+ '-c',
147
+ 'maintenance_work_mem=4MB',
148
+ '-c',
149
+ 'effective_cache_size=16MB',
130
150
  ]
131
151
 
132
152
  // cvr/cdb are just zero-cache bookkeeping — minimal fixed memory
@@ -135,13 +155,15 @@ const ZERO_START_PARAMS = [
135
155
  '-c',
136
156
  'shared_buffers=128kB',
137
157
  '-c',
138
- 'wal_buffers=64kB',
158
+ 'wal_buffers=32kB',
139
159
  '-c',
140
160
  'work_mem=64kB',
141
161
  '-c',
142
- 'maintenance_work_mem=1MB',
162
+ 'maintenance_work_mem=512kB',
163
+ '-c',
164
+ 'temp_buffers=400kB',
143
165
  '-c',
144
- 'temp_buffers=800kB',
166
+ 'max_connections=1',
145
167
  ]
146
168
 
147
169
  // create a single pglite instance with given dataDir suffix
@@ -176,18 +198,12 @@ async function createInstance(
176
198
  dataDir: dataPath,
177
199
  debug: config.logLevel === 'debug' ? 1 : 0,
178
200
  relaxedDurability: true,
179
- initialMemory: isMain ? 32 * 1024 * 1024 : 16 * 1024 * 1024,
201
+ initialMemory: isMain ? 16 * 1024 * 1024 : 8 * 1024 * 1024,
180
202
  ...(isMain ? {} : { startParams: ZERO_START_PARAMS }),
181
203
  // main instance: user overrides via pgliteOptions, zero instances: fixed
182
204
  ...(isMain
183
205
  ? {
184
- startParams: [
185
- ...PGLITE_BASE_FLAGS,
186
- '-c',
187
- 'shared_buffers=4MB',
188
- '-c',
189
- 'wal_buffers=1MB',
190
- ],
206
+ startParams: MAIN_START_PARAMS,
191
207
  ...userOpts,
192
208
  extensions: userOpts.extensions || {
193
209
  vector,
@@ -211,14 +227,8 @@ async function createInstance(
211
227
 
212
228
  if (isMain) {
213
229
  await db.exec(`
214
- SET work_mem = '4MB';
215
- SET maintenance_work_mem = '16MB';
216
- SET effective_cache_size = '64MB';
217
230
  SET random_page_cost = 1.1;
218
- SET jit = off;
219
231
  `)
220
- } else {
221
- await db.exec(`SET jit = off;`)
222
232
  }
223
233
 
224
234
  log.debug.pglite(`${name} ready`)
@@ -0,0 +1,52 @@
1
+ export interface HttpRequest {
2
+ method: string
3
+ url: string
4
+ headers?: Record<string, string>
5
+ body?: string | null
6
+ }
7
+
8
+ export interface HttpResponse {
9
+ status: number
10
+ headers: Record<string, string>
11
+ body: string
12
+ }
13
+
14
+ const ADMIN_HEADERS = {
15
+ 'access-control-allow-origin': '*',
16
+ 'access-control-allow-methods': 'GET, OPTIONS',
17
+ 'access-control-allow-headers': '*',
18
+ 'content-type': 'application/json',
19
+ }
20
+
21
+ function jsonResponse(status: number, body: unknown): HttpResponse {
22
+ return {
23
+ status,
24
+ headers: ADMIN_HEADERS,
25
+ body: JSON.stringify(body),
26
+ }
27
+ }
28
+
29
+ export function handleDisabledBrowserAdminRequest(
30
+ request: HttpRequest
31
+ ): HttpResponse | null {
32
+ const url = new URL(request.url, 'http://localhost')
33
+ if (!url.pathname.startsWith('/__orez/')) return null
34
+
35
+ const method = request.method.toUpperCase()
36
+ if (method === 'OPTIONS') {
37
+ return { status: 200, headers: ADMIN_HEADERS, body: '' }
38
+ }
39
+ if (method !== 'GET') {
40
+ return jsonResponse(405, { error: 'method not allowed', admin: 'disabled' })
41
+ }
42
+
43
+ if (url.pathname === '/__orez/api/logs') {
44
+ return jsonResponse(200, { entries: [], cursor: 0, admin: 'disabled' })
45
+ }
46
+
47
+ if (url.pathname === '/__orez/api/status') {
48
+ return jsonResponse(200, { ready: true, admin: 'disabled' })
49
+ }
50
+
51
+ return jsonResponse(404, { error: 'not found', admin: 'disabled' })
52
+ }
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { handleDisabledBrowserAdminRequest } from './browser-admin.js'
4
+
5
+ describe('disabled browser admin api', () => {
6
+ it('ignores non-admin routes', () => {
7
+ expect(
8
+ handleDisabledBrowserAdminRequest({
9
+ method: 'GET',
10
+ url: '/sync/v1/connect',
11
+ })
12
+ ).toBeNull()
13
+ })
14
+
15
+ it('serves empty logs for disabled browser admin', () => {
16
+ const response = handleDisabledBrowserAdminRequest({
17
+ method: 'GET',
18
+ url: '/__orez/api/logs?limit=100',
19
+ })
20
+
21
+ expect(response?.status).toBe(200)
22
+ expect(response?.headers['content-type']).toBe('application/json')
23
+ expect(JSON.parse(response?.body ?? '')).toEqual({
24
+ entries: [],
25
+ cursor: 0,
26
+ admin: 'disabled',
27
+ })
28
+ })
29
+
30
+ it('serves status for disabled browser admin', () => {
31
+ const response = handleDisabledBrowserAdminRequest({
32
+ method: 'GET',
33
+ url: 'http://localhost:7849/__orez/api/status',
34
+ })
35
+
36
+ expect(response?.status).toBe(200)
37
+ expect(JSON.parse(response?.body ?? '')).toEqual({
38
+ ready: true,
39
+ admin: 'disabled',
40
+ })
41
+ })
42
+
43
+ it('handles preflight and disallows writes', () => {
44
+ expect(
45
+ handleDisabledBrowserAdminRequest({
46
+ method: 'OPTIONS',
47
+ url: '/__orez/api/logs',
48
+ })?.status
49
+ ).toBe(200)
50
+
51
+ const response = handleDisabledBrowserAdminRequest({
52
+ method: 'POST',
53
+ url: '/__orez/api/actions/restart-zero',
54
+ })
55
+
56
+ expect(response?.status).toBe(405)
57
+ expect(JSON.parse(response?.body ?? '')).toEqual({
58
+ error: 'method not allowed',
59
+ admin: 'disabled',
60
+ })
61
+ })
62
+
63
+ it('keeps unknown admin routes explicit', () => {
64
+ const response = handleDisabledBrowserAdminRequest({
65
+ method: 'GET',
66
+ url: '/__orez/api/actions/restart-zero',
67
+ })
68
+
69
+ expect(response?.status).toBe(404)
70
+ expect(JSON.parse(response?.body ?? '')).toEqual({
71
+ error: 'not found',
72
+ admin: 'disabled',
73
+ })
74
+ })
75
+ })
@@ -36,8 +36,13 @@ import EventEmitter from 'node:events'
36
36
  // @ts-expect-error — internal zero-cache module, no type declarations
37
37
  import { runWorker as _runWorker } from '@rocicorp/zero/out/zero-cache/src/server/runner/run-worker.js'
38
38
 
39
+ import { handleDisabledBrowserAdminRequest } from './browser-admin.js'
40
+
41
+ import type { HttpRequest, HttpResponse } from './browser-admin.js'
39
42
  import type { PGlite } from '@electric-sql/pglite'
40
43
 
44
+ export type { HttpRequest, HttpResponse } from './browser-admin.js'
45
+
41
46
  const runWorkerFn = _runWorker as (
42
47
  parent: unknown,
43
48
  env: Record<string, string>
@@ -65,19 +70,17 @@ export interface ZeroCacheEmbedBrowserOptions {
65
70
 
66
71
  /** timeout in ms waiting for zero-cache ready (default: 30000) */
67
72
  readyTimeout?: number
68
- }
69
-
70
- export interface HttpRequest {
71
- method: string
72
- url: string
73
- headers?: Record<string, string>
74
- body?: string | null
75
- }
76
73
 
77
- export interface HttpResponse {
78
- status: number
79
- headers: Record<string, string>
80
- body: string
74
+ /**
75
+ * intercept browser-mode orez admin routes before they reach zero-cache.
76
+ *
77
+ * browser embeds do not run the node admin dashboard or log store. leaving
78
+ * `/__orez/*` to fall through to zero-cache makes disabled admin look like
79
+ * an app/zero route miss. keep the contract explicit by returning empty
80
+ * admin responses for the small read-only surface and 404 for the rest.
81
+ * default: true.
82
+ */
83
+ disableAdminApi?: boolean
81
84
  }
82
85
 
83
86
  /** WebSocket-like object — matches CF WebSocket, browser WebSocket, or MessagePort adapter */
@@ -116,6 +119,7 @@ export async function startZeroCacheEmbedBrowser(
116
119
  const appId = opts.appId || 'zero'
117
120
  const publications = opts.publications?.join(',') || `orez_${appId}_public`
118
121
  const readyTimeout = opts.readyTimeout ?? 30000
122
+ const disableAdminApi = opts.disableAdminApi ?? true
119
123
 
120
124
  // set up sqlite storage from sql.js or in-memory
121
125
  if (opts.sqlite) {
@@ -303,6 +307,11 @@ export async function startZeroCacheEmbedBrowser(
303
307
  },
304
308
 
305
309
  async handleHttp(request: HttpRequest): Promise<HttpResponse> {
310
+ if (disableAdminApi) {
311
+ const adminResponse = handleDisabledBrowserAdminRequest(request)
312
+ if (adminResponse) return adminResponse
313
+ }
314
+
306
315
  if (!isReady || !fastifyInstance?.inject) {
307
316
  return { status: 503, headers: {}, body: 'not ready' }
308
317
  }