orez 0.1.36 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/cli-entry.js +0 -0
  2. package/dist/cli.js +7 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +1 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +1 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +14 -11
  10. package/dist/index.js.map +1 -1
  11. package/dist/pg-proxy.d.ts.map +1 -1
  12. package/dist/pg-proxy.js +8 -4
  13. package/dist/pg-proxy.js.map +1 -1
  14. package/dist/pglite-manager.d.ts +12 -0
  15. package/dist/pglite-manager.d.ts.map +1 -1
  16. package/dist/pglite-manager.js +81 -0
  17. package/dist/pglite-manager.js.map +1 -1
  18. package/dist/recovery.js +2 -2
  19. package/dist/recovery.js.map +1 -1
  20. package/dist/replication/change-tracker.js +9 -9
  21. package/dist/replication/change-tracker.js.map +1 -1
  22. package/dist/replication/handler.d.ts +12 -0
  23. package/dist/replication/handler.d.ts.map +1 -1
  24. package/dist/replication/handler.js +34 -6
  25. package/dist/replication/handler.js.map +1 -1
  26. package/dist/worker/browser-build-config.d.ts +59 -0
  27. package/dist/worker/browser-build-config.d.ts.map +1 -0
  28. package/dist/worker/browser-build-config.js +101 -0
  29. package/dist/worker/browser-build-config.js.map +1 -0
  30. package/dist/worker/browser-embed.d.ts +58 -0
  31. package/dist/worker/browser-embed.d.ts.map +1 -0
  32. package/dist/worker/browser-embed.js +195 -0
  33. package/dist/worker/browser-embed.js.map +1 -0
  34. package/dist/worker/cf-patches.d.ts +20 -0
  35. package/dist/worker/cf-patches.d.ts.map +1 -0
  36. package/dist/worker/cf-patches.js +94 -0
  37. package/dist/worker/cf-patches.js.map +1 -0
  38. package/dist/worker/index.d.ts +12 -0
  39. package/dist/worker/index.d.ts.map +1 -0
  40. package/dist/worker/index.js +105 -0
  41. package/dist/worker/index.js.map +1 -0
  42. package/dist/worker/shims/fastify.d.ts +80 -0
  43. package/dist/worker/shims/fastify.d.ts.map +1 -0
  44. package/dist/worker/shims/fastify.js +223 -0
  45. package/dist/worker/shims/fastify.js.map +1 -0
  46. package/dist/worker/shims/http-service.d.ts +104 -0
  47. package/dist/worker/shims/http-service.d.ts.map +1 -0
  48. package/dist/worker/shims/http-service.js +198 -0
  49. package/dist/worker/shims/http-service.js.map +1 -0
  50. package/dist/worker/shims/node-stub.d.ts +147 -0
  51. package/dist/worker/shims/node-stub.d.ts.map +1 -0
  52. package/dist/worker/shims/node-stub.js +204 -0
  53. package/dist/worker/shims/node-stub.js.map +1 -0
  54. package/dist/worker/shims/postgres.d.ts +115 -0
  55. package/dist/worker/shims/postgres.d.ts.map +1 -0
  56. package/dist/worker/shims/postgres.js +1181 -0
  57. package/dist/worker/shims/postgres.js.map +1 -0
  58. package/dist/worker/shims/sqlite-browser.d.ts +54 -0
  59. package/dist/worker/shims/sqlite-browser.d.ts.map +1 -0
  60. package/dist/worker/shims/sqlite-browser.js +144 -0
  61. package/dist/worker/shims/sqlite-browser.js.map +1 -0
  62. package/dist/worker/shims/sqlite.d.ts +126 -0
  63. package/dist/worker/shims/sqlite.d.ts.map +1 -0
  64. package/dist/worker/shims/sqlite.js +599 -0
  65. package/dist/worker/shims/sqlite.js.map +1 -0
  66. package/dist/worker/shims/stream-browser.d.ts +9 -0
  67. package/dist/worker/shims/stream-browser.d.ts.map +1 -0
  68. package/dist/worker/shims/stream-browser.js +13 -0
  69. package/dist/worker/shims/stream-browser.js.map +1 -0
  70. package/dist/worker/shims/ws-browser.d.ts +50 -0
  71. package/dist/worker/shims/ws-browser.d.ts.map +1 -0
  72. package/dist/worker/shims/ws-browser.js +105 -0
  73. package/dist/worker/shims/ws-browser.js.map +1 -0
  74. package/dist/worker/shims/ws.d.ts +62 -0
  75. package/dist/worker/shims/ws.d.ts.map +1 -0
  76. package/dist/worker/shims/ws.js +310 -0
  77. package/dist/worker/shims/ws.js.map +1 -0
  78. package/dist/worker/types.d.ts +57 -0
  79. package/dist/worker/types.d.ts.map +1 -0
  80. package/dist/worker/types.js +9 -0
  81. package/dist/worker/types.js.map +1 -0
  82. package/dist/worker/zero-cache-embed-cf.d.ts +63 -0
  83. package/dist/worker/zero-cache-embed-cf.d.ts.map +1 -0
  84. package/dist/worker/zero-cache-embed-cf.js +268 -0
  85. package/dist/worker/zero-cache-embed-cf.js.map +1 -0
  86. package/dist/worker/zero-cache-embed.d.ts +66 -0
  87. package/dist/worker/zero-cache-embed.d.ts.map +1 -0
  88. package/dist/worker/zero-cache-embed.js +200 -0
  89. package/dist/worker/zero-cache-embed.js.map +1 -0
  90. package/package.json +62 -3
  91. package/src/cli-entry.ts +0 -0
  92. package/src/cli.ts +8 -1
  93. package/src/config.ts +2 -0
  94. package/src/index.ts +15 -10
  95. package/src/integration/integration.test.ts +1 -1
  96. package/src/integration/restore-live-stress.test.ts +2 -2
  97. package/src/pg-proxy.ts +9 -4
  98. package/src/pglite-manager.ts +111 -0
  99. package/src/recovery.ts +2 -2
  100. package/src/replication/change-tracker.test.ts +1 -1
  101. package/src/replication/change-tracker.ts +9 -9
  102. package/src/replication/handler.test.ts +37 -0
  103. package/src/replication/handler.ts +46 -6
  104. package/src/wasm-sqlite.test.ts +2 -1
  105. package/src/worker/browser-build-config.test.ts +59 -0
  106. package/src/worker/browser-build-config.ts +105 -0
  107. package/src/worker/browser-embed.ts +306 -0
  108. package/src/worker/cf-patches.ts +114 -0
  109. package/src/worker/embed-integration.test.ts +321 -0
  110. package/src/worker/index.ts +138 -0
  111. package/src/worker/shims/fastify.test.ts +255 -0
  112. package/src/worker/shims/fastify.ts +292 -0
  113. package/src/worker/shims/http-service.test.ts +355 -0
  114. package/src/worker/shims/http-service.ts +293 -0
  115. package/src/worker/shims/node-stub.ts +223 -0
  116. package/src/worker/shims/postgres.test.ts +364 -0
  117. package/src/worker/shims/postgres.ts +1434 -0
  118. package/src/worker/shims/sqlite-browser.test.ts +233 -0
  119. package/src/worker/shims/sqlite-browser.ts +178 -0
  120. package/src/worker/shims/sqlite.test.ts +641 -0
  121. package/src/worker/shims/sqlite.ts +731 -0
  122. package/src/worker/shims/ws-browser.test.ts +184 -0
  123. package/src/worker/shims/ws-browser.ts +125 -0
  124. package/src/worker/shims/ws.test.ts +288 -0
  125. package/src/worker/shims/ws.ts +367 -0
  126. package/src/worker/types.ts +75 -0
  127. package/src/worker/worker-integration.test.ts +223 -0
  128. package/src/worker/worker.test.ts +136 -0
  129. package/src/worker/zero-cache-embed-cf.ts +367 -0
  130. package/src/worker/zero-cache-embed.ts +277 -0
@@ -0,0 +1,233 @@
1
+ import { describe, it, expect } from 'vitest'
2
+
3
+ import { createSqlJsStorage, createInMemoryStorage } from './sqlite-browser.js'
4
+ import { Database, StatementRunner } from './sqlite.js'
5
+
6
+ // mock sql.js Database for testing
7
+ function createMockSqlJsDb() {
8
+ const tables = new Map<string, { columns: string[]; rows: Record<string, unknown>[] }>()
9
+ let rowsModified = 0
10
+
11
+ return {
12
+ run(sql: string, params?: unknown[]) {
13
+ // minimal DML support for testing
14
+ const upper = sql.trim().toUpperCase()
15
+ if (
16
+ upper.startsWith('BEGIN') ||
17
+ upper.startsWith('COMMIT') ||
18
+ upper.startsWith('ROLLBACK')
19
+ ) {
20
+ return
21
+ }
22
+ if (upper.startsWith('CREATE TABLE')) {
23
+ const match = sql.match(/CREATE TABLE\s+(?:IF NOT EXISTS\s+)?(\w+)\s*\((.+)\)/i)
24
+ if (match) {
25
+ const name = match[1]
26
+ const cols = match[2].split(',').map((c) => c.trim().split(/\s/)[0])
27
+ if (!tables.has(name)) {
28
+ tables.set(name, { columns: cols, rows: [] })
29
+ }
30
+ }
31
+ return
32
+ }
33
+ if (upper.startsWith('INSERT')) {
34
+ const match = sql.match(/INSERT INTO\s+(\w+)/i)
35
+ if (match) {
36
+ const table = tables.get(match[1])
37
+ if (table && params) {
38
+ const row: Record<string, unknown> = {}
39
+ table.columns.forEach((col, i) => {
40
+ row[col] = params[i] ?? null
41
+ })
42
+ table.rows.push(row)
43
+ rowsModified = 1
44
+ }
45
+ }
46
+ return
47
+ }
48
+ },
49
+
50
+ exec(
51
+ sql: string,
52
+ params?: unknown[]
53
+ ): Array<{ columns: string[]; values: unknown[][] }> {
54
+ const upper = sql.trim().toUpperCase()
55
+ if (upper.startsWith('SELECT')) {
56
+ const match = sql.match(/FROM\s+(\w+)/i)
57
+ if (match) {
58
+ const table = tables.get(match[1])
59
+ if (table) {
60
+ return [
61
+ {
62
+ columns: table.columns,
63
+ values: table.rows.map((r) => table.columns.map((c) => r[c])),
64
+ },
65
+ ]
66
+ }
67
+ }
68
+ }
69
+ // for non-SELECT, delegate to run
70
+ this.run(sql, params)
71
+ return []
72
+ },
73
+
74
+ prepare(sql: string) {
75
+ const self = this
76
+ let boundParams: unknown[] | undefined
77
+ let resultIndex = 0
78
+ let results: Array<{ columns: string[]; values: unknown[][] }> = []
79
+ let stepped = false
80
+
81
+ return {
82
+ bind(params?: unknown[]) {
83
+ boundParams = params
84
+ // for DML (INSERT etc), just run it
85
+ const upper = sql.trim().toUpperCase()
86
+ if (
87
+ upper.startsWith('INSERT') ||
88
+ upper.startsWith('CREATE') ||
89
+ upper.startsWith('BEGIN') ||
90
+ upper.startsWith('COMMIT') ||
91
+ upper.startsWith('ROLLBACK')
92
+ ) {
93
+ self.run(sql, params)
94
+ results = []
95
+ } else {
96
+ results = self.exec(sql, params)
97
+ }
98
+ resultIndex = 0
99
+ stepped = false
100
+ return true
101
+ },
102
+ step() {
103
+ if (!stepped && !results.length) {
104
+ results = self.exec(sql, boundParams)
105
+ stepped = true
106
+ }
107
+ if (results.length === 0 || results[0].values.length <= resultIndex) {
108
+ return false
109
+ }
110
+ return true
111
+ },
112
+ getAsObject() {
113
+ const r = results[0]
114
+ if (!r || resultIndex >= r.values.length) return {}
115
+ const row: Record<string, unknown> = {}
116
+ r.columns.forEach((col, i) => {
117
+ row[col] = r.values[resultIndex][i]
118
+ })
119
+ resultIndex++
120
+ return row
121
+ },
122
+ getColumnNames() {
123
+ return results[0]?.columns || []
124
+ },
125
+ free() {
126
+ return true
127
+ },
128
+ reset() {
129
+ resultIndex = 0
130
+ },
131
+ }
132
+ },
133
+
134
+ getRowsModified() {
135
+ return rowsModified
136
+ },
137
+
138
+ close() {},
139
+ }
140
+ }
141
+
142
+ describe('createSqlJsStorage', () => {
143
+ it('creates a SqlStorageLike from sql.js Database', () => {
144
+ const sqlJs = createMockSqlJsDb()
145
+ const storage = createSqlJsStorage(sqlJs as any)
146
+ expect(storage).toBeDefined()
147
+ expect(storage.exec).toBeInstanceOf(Function)
148
+ })
149
+
150
+ it('exec runs queries and returns cursor', () => {
151
+ const sqlJs = createMockSqlJsDb()
152
+ const storage = createSqlJsStorage(sqlJs as any)
153
+
154
+ // insert directly via run (not through storage) to set up test data
155
+ sqlJs.run('CREATE TABLE test (id, name)')
156
+ sqlJs.run('INSERT INTO test VALUES (?, ?)', [1, 'hello'])
157
+
158
+ const cursor = storage.exec('SELECT * FROM test')
159
+ const rows = cursor.toArray()
160
+ expect(rows.length).toBe(1)
161
+ expect(rows[0]).toHaveProperty('id')
162
+ expect(rows[0]).toHaveProperty('name')
163
+ })
164
+
165
+ it('transactionSync wraps in BEGIN/COMMIT', () => {
166
+ const sqlJs = createMockSqlJsDb()
167
+ const storage = createSqlJsStorage(sqlJs as any)
168
+
169
+ sqlJs.run('CREATE TABLE t (v)')
170
+
171
+ storage.transactionSync!(() => {
172
+ sqlJs.run('INSERT INTO t VALUES (?)', [42])
173
+ })
174
+
175
+ const cursor = storage.exec('SELECT * FROM t')
176
+ expect(cursor.toArray().length).toBe(1)
177
+ })
178
+
179
+ it('transactionSync rolls back on error', () => {
180
+ const sqlJs = createMockSqlJsDb()
181
+ const storage = createSqlJsStorage(sqlJs as any)
182
+
183
+ expect(() => {
184
+ storage.transactionSync!(() => {
185
+ throw new Error('abort')
186
+ })
187
+ }).toThrow('abort')
188
+ })
189
+
190
+ it('works with sqlite shim Database', () => {
191
+ const sqlJs = createMockSqlJsDb()
192
+ const storage = createSqlJsStorage(sqlJs as any)
193
+
194
+ // set on globalThis for the Database constructor
195
+ const prev = (globalThis as any).__orez_do_sqlite
196
+ ;(globalThis as any).__orez_do_sqlite = storage
197
+ try {
198
+ const db = new Database(':browser-sqlite:')
199
+ expect(db.open).toBe(true)
200
+ expect(db.name).toBe(':browser-sqlite:')
201
+ } finally {
202
+ if (prev) (globalThis as any).__orez_do_sqlite = prev
203
+ else delete (globalThis as any).__orez_do_sqlite
204
+ }
205
+ })
206
+ })
207
+
208
+ describe('createInMemoryStorage', () => {
209
+ it('creates a stub storage with exec', () => {
210
+ const prev = (globalThis as any).__orez_sqljs_db
211
+ delete (globalThis as any).__orez_sqljs_db
212
+ try {
213
+ const storage = createInMemoryStorage()
214
+ expect(storage).toBeDefined()
215
+ const cursor = storage.exec('SELECT 1')
216
+ expect(cursor.toArray()).toEqual([])
217
+ } finally {
218
+ if (prev) (globalThis as any).__orez_sqljs_db = prev
219
+ }
220
+ })
221
+
222
+ it('uses globalThis.__orez_sqljs_db if available', () => {
223
+ const sqlJs = createMockSqlJsDb()
224
+ ;(globalThis as any).__orez_sqljs_db = sqlJs
225
+ try {
226
+ const storage = createInMemoryStorage()
227
+ // just verify it uses the sqlJs db (not the stub)
228
+ expect(storage.transactionSync).toBeInstanceOf(Function)
229
+ } finally {
230
+ delete (globalThis as any).__orez_sqljs_db
231
+ }
232
+ })
233
+ })
@@ -0,0 +1,178 @@
1
+ /**
2
+ * sql.js adapter for the sqlite shim.
3
+ *
4
+ * wraps a sql.js Database instance to implement the SqlStorageLike
5
+ * interface that the existing sqlite shim expects. this lets zero-cache
6
+ * use sql.js (SQLite compiled to WASM) in browser Web Workers.
7
+ *
8
+ * usage:
9
+ * import initSqlJs from 'sql.js'
10
+ * import { createSqlJsStorage } from 'orez/worker/shims/sqlite-browser'
11
+ *
12
+ * const SQL = await initSqlJs()
13
+ * const db = new SQL.Database()
14
+ * const storage = createSqlJsStorage(db)
15
+ * globalThis.__orez_do_sqlite = storage
16
+ */
17
+
18
+ import type { SqlStorageLike, SqlStorageCursor, SqlStorageValue } from './sqlite.js'
19
+
20
+ // sql.js Database interface (minimal, to avoid hard dependency)
21
+ interface SqlJsDatabase {
22
+ run(sql: string, params?: unknown[]): void
23
+ exec(
24
+ sql: string,
25
+ params?: unknown[]
26
+ ): Array<{
27
+ columns: string[]
28
+ values: unknown[][]
29
+ }>
30
+ prepare(sql: string): SqlJsStatement
31
+ getRowsModified(): number
32
+ close(): void
33
+ }
34
+
35
+ interface SqlJsStatement {
36
+ bind(params?: unknown[]): boolean
37
+ step(): boolean
38
+ getAsObject(params?: object): Record<string, unknown>
39
+ getColumnNames(): string[]
40
+ free(): boolean
41
+ reset(): void
42
+ }
43
+
44
+ /**
45
+ * create a SqlStorageLike adapter around a sql.js Database.
46
+ *
47
+ * the returned object can be set on `globalThis.__orez_do_sqlite`
48
+ * or passed directly to the sqlite shim's Database constructor.
49
+ */
50
+ export function createSqlJsStorage(sqlJsDb: SqlJsDatabase): SqlStorageLike {
51
+ return {
52
+ exec(query: string, ...bindings: SqlStorageValue[]): SqlStorageCursor {
53
+ const stmt = sqlJsDb.prepare(query)
54
+ try {
55
+ if (bindings.length > 0) {
56
+ // named parameters: single object arg → pass object with prefixed keys to sql.js
57
+ if (
58
+ bindings.length === 1 &&
59
+ bindings[0] !== null &&
60
+ typeof bindings[0] === 'object' &&
61
+ !Array.isArray(bindings[0]) &&
62
+ !(bindings[0] instanceof ArrayBuffer)
63
+ ) {
64
+ // sql.js expects keys with $/:/@ prefix for named params
65
+ // better-sqlite3 accepts keys without prefix — add @ prefix
66
+ const obj = bindings[0] as Record<string, unknown>
67
+ const prefixed: Record<string, unknown> = {}
68
+ for (const [k, v] of Object.entries(obj)) {
69
+ // add @ prefix if not already prefixed
70
+ const key =
71
+ k.startsWith('$') || k.startsWith(':') || k.startsWith('@') ? k : `@${k}`
72
+ prefixed[key] = v
73
+ }
74
+ stmt.bind(prefixed as any)
75
+ } else {
76
+ stmt.bind(bindings as unknown[])
77
+ }
78
+ }
79
+
80
+ const rows: Record<string, SqlStorageValue>[] = []
81
+ let columnNames: string[] = []
82
+
83
+ while (stmt.step()) {
84
+ const row = stmt.getAsObject() as Record<string, SqlStorageValue>
85
+ if (columnNames.length === 0) {
86
+ columnNames = stmt.getColumnNames()
87
+ }
88
+ rows.push(row)
89
+ }
90
+
91
+ // rowsWritten only meaningful for DML
92
+ const rowsWritten = sqlJsDb.getRowsModified()
93
+
94
+ return {
95
+ toArray: () => rows,
96
+ rowsRead: rows.length,
97
+ rowsWritten,
98
+ columnNames,
99
+ }
100
+ } finally {
101
+ stmt.free()
102
+ }
103
+ },
104
+
105
+ // sql.js transaction handling with nested transaction support
106
+ transactionSync<T>(fn: () => T): T {
107
+ // check if already in a transaction by trying BEGIN
108
+ let inTransaction = false
109
+ try {
110
+ sqlJsDb.run('BEGIN')
111
+ } catch {
112
+ // already in a transaction — use savepoint instead
113
+ inTransaction = true
114
+ }
115
+
116
+ if (inTransaction) {
117
+ const sp = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
118
+ sqlJsDb.run(`SAVEPOINT "${sp}"`)
119
+ try {
120
+ const result = fn()
121
+ sqlJsDb.run(`RELEASE SAVEPOINT "${sp}"`)
122
+ return result
123
+ } catch (err) {
124
+ try {
125
+ sqlJsDb.run(`ROLLBACK TO SAVEPOINT "${sp}"`)
126
+ } catch {}
127
+ throw err
128
+ }
129
+ }
130
+
131
+ try {
132
+ const result = fn()
133
+ sqlJsDb.run('COMMIT')
134
+ return result
135
+ } catch (err) {
136
+ try {
137
+ sqlJsDb.run('ROLLBACK')
138
+ } catch {}
139
+ throw err
140
+ }
141
+ },
142
+ }
143
+ }
144
+
145
+ /**
146
+ * create an in-memory sql.js-like storage for environments where
147
+ * sql.js isn't available. uses a minimal Map-based implementation
148
+ * that handles basic CREATE TABLE / INSERT / SELECT / UPDATE / DELETE.
149
+ *
150
+ * NOTE: this is a very limited stub. for production use, prefer
151
+ * a real sql.js instance. this stub exists so the browser embed
152
+ * can start without sql.js for basic testing.
153
+ */
154
+ export function createInMemoryStorage(): SqlStorageLike {
155
+ // use sql.js if available on globalThis (consumer may have loaded it)
156
+ const sqlJsDb = (globalThis as any).__orez_sqljs_db
157
+ if (sqlJsDb) {
158
+ return createSqlJsStorage(sqlJsDb)
159
+ }
160
+
161
+ // minimal stub — zero-cache's schema migrations will likely fail
162
+ // but this allows the embed to start for basic PGlite-only use cases
163
+ console.warn(
164
+ '[orez] no sql.js database available. sqlite operations will fail. ' +
165
+ 'set globalThis.__orez_sqljs_db or pass a sql.js Database to the embed.'
166
+ )
167
+
168
+ return {
169
+ exec(_query: string, ..._bindings: SqlStorageValue[]): SqlStorageCursor {
170
+ return {
171
+ toArray: () => [],
172
+ rowsRead: 0,
173
+ rowsWritten: 0,
174
+ columnNames: [],
175
+ }
176
+ },
177
+ }
178
+ }