orez 0.1.3 → 0.1.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/README.md +4 -4
- package/dist/admin/server.d.ts.map +1 -1
- package/dist/admin/server.js +1 -0
- package/dist/admin/server.js.map +1 -1
- package/dist/admin/ui.d.ts.map +1 -1
- package/dist/admin/ui.js +12 -0
- package/dist/admin/ui.js.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -85
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +1 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +2 -2
- package/dist/log.js.map +1 -1
- package/dist/sqlite-mode/apply-mode.d.ts +49 -0
- package/dist/sqlite-mode/apply-mode.d.ts.map +1 -0
- package/dist/sqlite-mode/apply-mode.js +190 -0
- package/dist/sqlite-mode/apply-mode.js.map +1 -0
- package/dist/sqlite-mode/index.d.ts +14 -0
- package/dist/sqlite-mode/index.d.ts.map +1 -0
- package/dist/sqlite-mode/index.js +14 -0
- package/dist/sqlite-mode/index.js.map +1 -0
- package/dist/sqlite-mode/resolve-mode.d.ts +24 -0
- package/dist/sqlite-mode/resolve-mode.d.ts.map +1 -0
- package/dist/sqlite-mode/resolve-mode.js +61 -0
- package/dist/sqlite-mode/resolve-mode.js.map +1 -0
- package/dist/sqlite-mode/shim-template.d.ts +20 -0
- package/dist/sqlite-mode/shim-template.d.ts.map +1 -0
- package/dist/sqlite-mode/shim-template.js +144 -0
- package/dist/sqlite-mode/shim-template.js.map +1 -0
- package/dist/sqlite-mode/types.d.ts +16 -0
- package/dist/sqlite-mode/types.d.ts.map +1 -0
- package/dist/sqlite-mode/types.js +18 -0
- package/dist/sqlite-mode/types.js.map +1 -0
- package/package.json +3 -2
- package/src/admin/server.ts +1 -0
- package/src/admin/ui.ts +12 -0
- package/src/cli.ts +5 -5
- package/src/index.ts +68 -94
- package/src/log.ts +2 -2
- package/src/shim/hooks.mjs +42 -18
- package/src/sqlite-mode/apply-mode.ts +224 -0
- package/src/sqlite-mode/index.ts +14 -0
- package/src/sqlite-mode/resolve-mode.ts +71 -0
- package/src/sqlite-mode/shim-template.ts +158 -0
- package/src/sqlite-mode/sqlite-mode.test.ts +423 -0
- package/src/sqlite-mode/types.ts +30 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shim template generator - single source of truth for both cjs and esm shims.
|
|
3
|
+
* generates the shim code that wraps bedrock-sqlite to be compatible with
|
|
4
|
+
* @rocicorp/zero-sqlite3 (better-sqlite3 api).
|
|
5
|
+
*/
|
|
6
|
+
import { COMMON_PRAGMAS, JOURNAL_MODE } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* generate the core shim code (shared between cjs and esm)
|
|
9
|
+
* this is the constructor wrapper and api polyfills
|
|
10
|
+
*/
|
|
11
|
+
function generateShimCore(opts) {
|
|
12
|
+
const journalMode = JOURNAL_MODE[opts.mode];
|
|
13
|
+
const { busy_timeout, synchronous } = COMMON_PRAGMAS;
|
|
14
|
+
return `
|
|
15
|
+
var OrigDatabase = mod.Database;
|
|
16
|
+
var SqliteError = mod.SqliteError;
|
|
17
|
+
|
|
18
|
+
function Database() {
|
|
19
|
+
var db = new OrigDatabase(...arguments);
|
|
20
|
+
try {
|
|
21
|
+
db.pragma('journal_mode = ${journalMode}');
|
|
22
|
+
db.pragma('busy_timeout = ${busy_timeout}');
|
|
23
|
+
db.pragma('synchronous = ${synchronous}');
|
|
24
|
+
} catch(e) {}
|
|
25
|
+
return db;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Database.prototype = OrigDatabase.prototype;
|
|
29
|
+
Database.prototype.constructor = Database;
|
|
30
|
+
Object.keys(OrigDatabase).forEach(function(k) { Database[k] = OrigDatabase[k]; });
|
|
31
|
+
|
|
32
|
+
// api polyfills for better-sqlite3 compatibility
|
|
33
|
+
Database.prototype.unsafeMode = function() { return this; };
|
|
34
|
+
if (!Database.prototype.defaultSafeIntegers) {
|
|
35
|
+
Database.prototype.defaultSafeIntegers = function() { return this; };
|
|
36
|
+
}
|
|
37
|
+
if (!Database.prototype.serialize) {
|
|
38
|
+
Database.prototype.serialize = function() { throw new Error('not supported in wasm'); };
|
|
39
|
+
}
|
|
40
|
+
if (!Database.prototype.backup) {
|
|
41
|
+
Database.prototype.backup = function() { throw new Error('not supported in wasm'); };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// wrap pragma to skip optimize (can corrupt wasm vfs) and swallow sqlite errors
|
|
45
|
+
var origPragma = OrigDatabase.prototype.pragma;
|
|
46
|
+
Database.prototype.pragma = function(str, opts) {
|
|
47
|
+
if (str && str.trim().toLowerCase().startsWith('optimize')) return [];
|
|
48
|
+
try { return origPragma.call(this, str, opts); }
|
|
49
|
+
catch(e) { if (e && (e.code === 'SQLITE_CORRUPT' || e.code === 'SQLITE_IOERR')) return []; throw e; }
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// wrap close to swallow wasm errors during shutdown
|
|
53
|
+
var origClose = OrigDatabase.prototype.close;
|
|
54
|
+
Database.prototype.close = function() {
|
|
55
|
+
try { return origClose.call(this); }
|
|
56
|
+
catch(e) { console.error('[orez-shim] close error (swallowed):', e?.message || e); }
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// statement prototype polyfills
|
|
60
|
+
var tmpDb = new OrigDatabase(':memory:');
|
|
61
|
+
var tmpStmt = tmpDb.prepare('SELECT 1');
|
|
62
|
+
var SP = Object.getPrototypeOf(tmpStmt);
|
|
63
|
+
if (!SP.safeIntegers) SP.safeIntegers = function() { return this; };
|
|
64
|
+
SP.scanStatus = function() { return undefined; };
|
|
65
|
+
SP.scanStatusV2 = function() { return []; };
|
|
66
|
+
SP.scanStatusReset = function() {};
|
|
67
|
+
tmpDb.close();
|
|
68
|
+
|
|
69
|
+
// scanstat constants for query planner compatibility
|
|
70
|
+
Database.SQLITE_SCANSTAT_NLOOP = 0;
|
|
71
|
+
Database.SQLITE_SCANSTAT_NVISIT = 1;
|
|
72
|
+
Database.SQLITE_SCANSTAT_EST = 2;
|
|
73
|
+
Database.SQLITE_SCANSTAT_NAME = 3;
|
|
74
|
+
Database.SQLITE_SCANSTAT_EXPLAIN = 4;
|
|
75
|
+
Database.SQLITE_SCANSTAT_SELECTID = 5;
|
|
76
|
+
Database.SQLITE_SCANSTAT_PARENTID = 6;
|
|
77
|
+
Database.SQLITE_SCANSTAT_NCYCLE = 7;
|
|
78
|
+
Database.SQLITE_SCANSTAT_COMPLEX = 8;
|
|
79
|
+
`.trim();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* generate debug tracing code for run() method
|
|
83
|
+
*/
|
|
84
|
+
function generateTracing() {
|
|
85
|
+
return `
|
|
86
|
+
// trace writes to _zero.changeLog and _zero.replicationState for debugging
|
|
87
|
+
var origRun = OrigDatabase.prototype.run;
|
|
88
|
+
Database.prototype.run = function(sql) {
|
|
89
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
90
|
+
if (typeof sql === 'string') {
|
|
91
|
+
if (sql.includes('_zero.changeLog')) {
|
|
92
|
+
console.info('[orez-shim] changeLog write:', sql.slice(0, 120), args.length ? JSON.stringify(args[0]).slice(0, 80) : '');
|
|
93
|
+
}
|
|
94
|
+
if (sql.includes('_zero.replicationState') && (sql.includes('UPDATE') || sql.includes('INSERT'))) {
|
|
95
|
+
console.info('[orez-shim] replicationState update:', sql.slice(0, 120), args.length ? JSON.stringify(args[0]).slice(0, 80) : '');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return origRun.apply(this, arguments);
|
|
99
|
+
};
|
|
100
|
+
`.trim();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* generate commonjs shim for in-place patching of @rocicorp/zero-sqlite3
|
|
104
|
+
*/
|
|
105
|
+
export function generateCjsShim(opts) {
|
|
106
|
+
const core = generateShimCore(opts);
|
|
107
|
+
const tracing = opts.includeTracing ? '\n' + generateTracing() : '';
|
|
108
|
+
return `'use strict';
|
|
109
|
+
// orez sqlite shim - wraps bedrock-sqlite for zero-cache compatibility
|
|
110
|
+
// mode: ${opts.mode}, journal_mode: ${JOURNAL_MODE[opts.mode]}
|
|
111
|
+
var mod = require('${opts.bedrockPath}');
|
|
112
|
+
${core}
|
|
113
|
+
${tracing}
|
|
114
|
+
module.exports = Database;
|
|
115
|
+
module.exports.SqliteError = SqliteError;
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* generate esm shim for loader hooks
|
|
120
|
+
*/
|
|
121
|
+
export function generateEsmShim(opts) {
|
|
122
|
+
const core = generateShimCore(opts);
|
|
123
|
+
const tracing = opts.includeTracing ? '\n' + generateTracing() : '';
|
|
124
|
+
return `// orez sqlite shim - wraps bedrock-sqlite for zero-cache compatibility
|
|
125
|
+
// mode: ${opts.mode}, journal_mode: ${JOURNAL_MODE[opts.mode]}
|
|
126
|
+
|
|
127
|
+
// catch uncaught exceptions from bedrock-sqlite wasm clearly
|
|
128
|
+
process.on('uncaughtException', (err) => {
|
|
129
|
+
console.error('[orez-shim] UNCAUGHT EXCEPTION:', err?.message || err);
|
|
130
|
+
console.error('[orez-shim] code:', err?.code, 'name:', err?.name);
|
|
131
|
+
console.error('[orez-shim] stack:', err?.stack?.split('\\n').slice(0, 5).join('\\n'));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
import { createRequire } from 'node:module';
|
|
136
|
+
const require = createRequire('${opts.bedrockPath}');
|
|
137
|
+
var mod = require('${opts.bedrockPath}');
|
|
138
|
+
${core}
|
|
139
|
+
${tracing}
|
|
140
|
+
export default Database;
|
|
141
|
+
export { SqliteError };
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=shim-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shim-template.js","sourceRoot":"","sources":["../../src/sqlite-mode/shim-template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAA;AAS1E;;;GAGG;AACH,SAAS,gBAAgB,CAAC,IAAiB;IACzC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3C,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,cAAc,CAAA;IAEpD,OAAO;;;;;;;gCAOuB,WAAW;gCACX,YAAY;+BACb,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDzC,CAAC,IAAI,EAAE,CAAA;AACR,CAAC;AAED;;GAEG;AACH,SAAS,eAAe;IACtB,OAAO;;;;;;;;;;;;;;;CAeR,CAAC,IAAI,EAAE,CAAA;AACR,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnE,OAAO;;WAEE,IAAI,CAAC,IAAI,mBAAmB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;qBACzC,IAAI,CAAC,WAAW;EACnC,IAAI;EACJ,OAAO;;;CAGR,CAAA;AACD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnE,OAAO;WACE,IAAI,CAAC,IAAI,mBAAmB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;iCAW7B,IAAI,CAAC,WAAW;qBAC5B,IAAI,CAAC,WAAW;EACnC,IAAI;EACJ,OAAO;;;CAGR,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sqlite mode types and constants
|
|
3
|
+
*/
|
|
4
|
+
export type SqliteMode = 'native' | 'wasm';
|
|
5
|
+
export interface SqliteModeConfig {
|
|
6
|
+
mode: SqliteMode;
|
|
7
|
+
bedrockPath?: string;
|
|
8
|
+
zeroSqlitePath?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const JOURNAL_MODE: Record<SqliteMode, string>;
|
|
11
|
+
export declare const COMMON_PRAGMAS: {
|
|
12
|
+
busy_timeout: string;
|
|
13
|
+
synchronous: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const BACKUP_MARKER = ".orez-backup";
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sqlite-mode/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;IAEhB,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAKD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAGnD,CAAA;AAGD,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAGD,eAAO,MAAM,aAAa,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sqlite mode types and constants
|
|
3
|
+
*/
|
|
4
|
+
// journal mode differs between native and wasm
|
|
5
|
+
// - native: wal2 for better concurrency
|
|
6
|
+
// - wasm: delete for compatibility (wal2 corrupts wasm vfs on certain operations)
|
|
7
|
+
export const JOURNAL_MODE = {
|
|
8
|
+
native: 'wal2',
|
|
9
|
+
wasm: 'delete',
|
|
10
|
+
};
|
|
11
|
+
// common pragmas shared by both modes
|
|
12
|
+
export const COMMON_PRAGMAS = {
|
|
13
|
+
busy_timeout: '30000',
|
|
14
|
+
synchronous: 'normal',
|
|
15
|
+
};
|
|
16
|
+
// backup file marker for identifying orez-shimmed packages
|
|
17
|
+
export const BACKUP_MARKER = '.orez-backup';
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sqlite-mode/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,+CAA+C;AAC/C,wCAAwC;AACxC,kFAAkF;AAClF,MAAM,CAAC,MAAM,YAAY,GAA+B;IACtD,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,QAAQ;CACf,CAAA;AAED,sCAAsC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,OAAO;IACrB,WAAW,EAAE,QAAQ;CACtB,CAAA;AAED,2DAA2D;AAC3D,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "PGlite-powered zero-sync development backend. No Docker required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@electric-sql/pglite": "^0.3.15",
|
|
50
50
|
"@electric-sql/pglite-tools": "^0.2.20",
|
|
51
51
|
"@rocicorp/zero": ">=0.1.0",
|
|
52
|
-
"bedrock-sqlite": "0.1.
|
|
52
|
+
"bedrock-sqlite": "0.1.5",
|
|
53
53
|
"citty": "^0.2.0",
|
|
54
54
|
"pg-gateway": "0.3.0-beta.4",
|
|
55
55
|
"pgsql-parser": "^17.9.11",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
+
"@rocicorp/zero-sqlite3": "^1.0.15",
|
|
67
68
|
"@types/node": "^22.0.0",
|
|
68
69
|
"@types/ws": "^8.18.1",
|
|
69
70
|
"oxfmt": "latest",
|
package/src/admin/server.ts
CHANGED
|
@@ -93,6 +93,7 @@ export function startAdminServer(opts: AdminServerOpts): Promise<Server> {
|
|
|
93
93
|
uptime: Math.floor((Date.now() - startTime) / 1000),
|
|
94
94
|
logLevel: config.logLevel,
|
|
95
95
|
skipZeroCache: config.skipZeroCache,
|
|
96
|
+
sqliteMode: config.disableWasmSqlite ? 'native' : 'wasm',
|
|
96
97
|
})
|
|
97
98
|
return
|
|
98
99
|
}
|
package/src/admin/ui.ts
CHANGED
|
@@ -341,6 +341,7 @@ export function getAdminHtml(): string {
|
|
|
341
341
|
' <div class="spacer"></div>\n' +
|
|
342
342
|
' <span class="badge"><span class="dot"></span> pg <span id="pg-port">-</span></span>\n' +
|
|
343
343
|
' <span class="badge"><span class="dot"></span> zero <span id="zero-port">-</span></span>\n' +
|
|
344
|
+
' <span class="badge" id="sqlite-badge">sqlite: --</span>\n' +
|
|
344
345
|
' <span class="badge" id="uptime-badge">⏱ --</span>\n' +
|
|
345
346
|
' </div>\n' +
|
|
346
347
|
'\n' +
|
|
@@ -458,6 +459,16 @@ export function getAdminHtml(): string {
|
|
|
458
459
|
' logView.style.display = "block";\n' +
|
|
459
460
|
' toolbar.style.display = "flex";\n' +
|
|
460
461
|
' activeSource = source;\n' +
|
|
462
|
+
' // default zero tab to info level (too verbose otherwise)\n' +
|
|
463
|
+
' var levelSelect = document.getElementById("level-filter");\n' +
|
|
464
|
+
' if (source === "zero") {\n' +
|
|
465
|
+
' activeLevel = "info";\n' +
|
|
466
|
+
' levelSelect.value = "info";\n' +
|
|
467
|
+
' } else if (activeLevel === "info" && levelSelect.value === "info") {\n' +
|
|
468
|
+
' // reset to all levels when leaving zero tab if still on info\n' +
|
|
469
|
+
' activeLevel = "";\n' +
|
|
470
|
+
' levelSelect.value = "";\n' +
|
|
471
|
+
' }\n' +
|
|
461
472
|
' lastCursor = 0;\n' +
|
|
462
473
|
' logView.innerHTML = "";\n' +
|
|
463
474
|
' fetchLogs();\n' +
|
|
@@ -624,6 +635,7 @@ export function getAdminHtml(): string {
|
|
|
624
635
|
' fetch("/api/status").then(function(res) { return res.json(); }).then(function(data) {\n' +
|
|
625
636
|
' document.getElementById("pg-port").textContent = ":" + data.pgPort;\n' +
|
|
626
637
|
' document.getElementById("zero-port").textContent = ":" + data.zeroPort;\n' +
|
|
638
|
+
' document.getElementById("sqlite-badge").textContent = "sqlite: " + (data.sqliteMode || "wasm");\n' +
|
|
627
639
|
' var m = Math.floor(data.uptime / 60);\n' +
|
|
628
640
|
' var s = data.uptime % 60;\n' +
|
|
629
641
|
' document.getElementById("uptime-badge").textContent = "\\u23F1 " + (m > 0 ? m + "m " : "") + s + "s";\n' +
|
package/src/cli.ts
CHANGED
|
@@ -895,9 +895,9 @@ const main = defineCommand({
|
|
|
895
895
|
description: 'command to run once all services are healthy',
|
|
896
896
|
default: '',
|
|
897
897
|
},
|
|
898
|
-
admin: {
|
|
898
|
+
'disable-admin': {
|
|
899
899
|
type: 'boolean',
|
|
900
|
-
description: '
|
|
900
|
+
description: 'disable admin dashboard',
|
|
901
901
|
default: false,
|
|
902
902
|
},
|
|
903
903
|
'admin-port': {
|
|
@@ -912,7 +912,7 @@ const main = defineCommand({
|
|
|
912
912
|
pg_restore: pgRestoreCommand,
|
|
913
913
|
},
|
|
914
914
|
async run({ args }) {
|
|
915
|
-
const adminPort = args
|
|
915
|
+
const adminPort = args['disable-admin'] ? 0 : Number(args['admin-port'])
|
|
916
916
|
const {
|
|
917
917
|
config,
|
|
918
918
|
stop,
|
|
@@ -948,7 +948,7 @@ const main = defineCommand({
|
|
|
948
948
|
}
|
|
949
949
|
|
|
950
950
|
let adminServer: import('node:http').Server | null = null
|
|
951
|
-
if (args
|
|
951
|
+
if (!args['disable-admin'] && logStore && zeroEnv) {
|
|
952
952
|
const { startAdminServer } = await import('./admin/server.js')
|
|
953
953
|
adminServer = await startAdminServer({
|
|
954
954
|
port: config.adminPort,
|
|
@@ -963,7 +963,7 @@ const main = defineCommand({
|
|
|
963
963
|
}
|
|
964
964
|
|
|
965
965
|
log.pg(
|
|
966
|
-
`postgresql://${config.pgUser}:${config.pgPassword}@127.0.0.1:${config.pgPort}/postgres`
|
|
966
|
+
`ready ${url(`postgresql://${config.pgUser}:${config.pgPassword}@127.0.0.1:${config.pgPort}/postgres`)}`
|
|
967
967
|
)
|
|
968
968
|
|
|
969
969
|
let stopping = false
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import { spawn, type ChildProcess } from 'node:child_process'
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
11
|
-
import { createRequire } from 'node:module'
|
|
12
11
|
import { resolve } from 'node:path'
|
|
13
12
|
|
|
14
13
|
import {
|
|
@@ -23,6 +22,14 @@ import { startPgProxy } from './pg-proxy.js'
|
|
|
23
22
|
import { createPGliteInstances, runMigrations } from './pglite-manager.js'
|
|
24
23
|
import { findPort } from './port.js'
|
|
25
24
|
import { installChangeTracking } from './replication/change-tracker.js'
|
|
25
|
+
import {
|
|
26
|
+
applySqliteMode,
|
|
27
|
+
cleanupShim,
|
|
28
|
+
resolveSqliteMode,
|
|
29
|
+
resolveSqliteModeConfig,
|
|
30
|
+
type SqliteMode,
|
|
31
|
+
type SqliteModeConfig,
|
|
32
|
+
} from './sqlite-mode/index.js'
|
|
26
33
|
|
|
27
34
|
import type { ZeroLiteConfig } from './config.js'
|
|
28
35
|
import type { PGlite } from '@electric-sql/pglite'
|
|
@@ -65,18 +72,8 @@ async function runHook(
|
|
|
65
72
|
})
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const resolved = import.meta.resolve(pkg)
|
|
72
|
-
if (resolved) return resolved.replace('file://', '')
|
|
73
|
-
} catch {}
|
|
74
|
-
try {
|
|
75
|
-
const require = createRequire(import.meta.url)
|
|
76
|
-
return require.resolve(pkg)
|
|
77
|
-
} catch {}
|
|
78
|
-
return ''
|
|
79
|
-
}
|
|
75
|
+
// resolvePackage moved to sqlite-mode/resolve-mode.ts
|
|
76
|
+
import { resolvePackage } from './sqlite-mode/resolve-mode.js'
|
|
80
77
|
|
|
81
78
|
export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
82
79
|
const config = getConfig(overrides)
|
|
@@ -111,6 +108,20 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
111
108
|
|
|
112
109
|
log.debug.orez(`data dir: ${resolve(config.dataDir)}`)
|
|
113
110
|
|
|
111
|
+
// resolve sqlite mode config early (used for shim application and cleanup)
|
|
112
|
+
// requested wasm can fall back to native if required packages are missing.
|
|
113
|
+
let sqliteMode = resolveSqliteMode(config.disableWasmSqlite)
|
|
114
|
+
let sqliteModeConfig = resolveSqliteModeConfig(config.disableWasmSqlite)
|
|
115
|
+
if (sqliteMode === 'wasm' && !sqliteModeConfig) {
|
|
116
|
+
log.orez(
|
|
117
|
+
'warning: wasm sqlite requested but dependencies are missing, falling back to native sqlite'
|
|
118
|
+
)
|
|
119
|
+
sqliteMode = 'native'
|
|
120
|
+
config.disableWasmSqlite = true
|
|
121
|
+
sqliteModeConfig = resolveSqliteModeConfig(true)
|
|
122
|
+
}
|
|
123
|
+
log.orez(`sqlite: ${sqliteMode}`)
|
|
124
|
+
|
|
114
125
|
mkdirSync(config.dataDir, { recursive: true })
|
|
115
126
|
|
|
116
127
|
// write pid file for IPC (pg_restore uses this to signal restart)
|
|
@@ -174,7 +185,12 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
174
185
|
if (!config.skipZeroCache) {
|
|
175
186
|
// use internal port when http proxy is enabled
|
|
176
187
|
const zeroConfig = httpLog ? { ...config, zeroPort: zeroInternalPort } : config
|
|
177
|
-
const result = await startZeroCache(
|
|
188
|
+
const result = await startZeroCache(
|
|
189
|
+
zeroConfig,
|
|
190
|
+
logStore,
|
|
191
|
+
sqliteMode,
|
|
192
|
+
sqliteModeConfig
|
|
193
|
+
)
|
|
178
194
|
zeroCacheProcess = result.process
|
|
179
195
|
zeroEnv = result.env
|
|
180
196
|
await waitForZeroCache(zeroConfig)
|
|
@@ -189,7 +205,7 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
189
205
|
log.debug.orez(`http proxy listening on ${config.zeroPort}`)
|
|
190
206
|
}
|
|
191
207
|
|
|
192
|
-
log.zero(`ready ${port(config.zeroPort, 'magenta')}`)
|
|
208
|
+
log.zero(`ready ${port(config.zeroPort, 'magenta')} (sqlite: ${sqliteMode})`)
|
|
193
209
|
} else {
|
|
194
210
|
log.orez('skip zero-cache')
|
|
195
211
|
}
|
|
@@ -225,7 +241,12 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
225
241
|
await killZeroCache()
|
|
226
242
|
// use internal port when http proxy is enabled
|
|
227
243
|
const zeroConfig = httpLog ? { ...config, zeroPort: zeroInternalPort } : config
|
|
228
|
-
const result = await startZeroCache(
|
|
244
|
+
const result = await startZeroCache(
|
|
245
|
+
zeroConfig,
|
|
246
|
+
logStore,
|
|
247
|
+
sqliteMode,
|
|
248
|
+
sqliteModeConfig
|
|
249
|
+
)
|
|
229
250
|
zeroCacheProcess = result.process
|
|
230
251
|
zeroEnv = result.env
|
|
231
252
|
await waitForZeroCache(zeroConfig)
|
|
@@ -322,7 +343,12 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
322
343
|
log.orez('starting zero-cache...')
|
|
323
344
|
// use internal port when http proxy is enabled
|
|
324
345
|
const zeroConfig = httpLog ? { ...config, zeroPort: zeroInternalPort } : config
|
|
325
|
-
const result = await startZeroCache(
|
|
346
|
+
const result = await startZeroCache(
|
|
347
|
+
zeroConfig,
|
|
348
|
+
logStore,
|
|
349
|
+
sqliteMode,
|
|
350
|
+
sqliteModeConfig
|
|
351
|
+
)
|
|
326
352
|
zeroCacheProcess = result.process
|
|
327
353
|
zeroEnv = result.env
|
|
328
354
|
|
|
@@ -364,6 +390,9 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
364
390
|
try {
|
|
365
391
|
unlinkSync(pidFile)
|
|
366
392
|
} catch {}
|
|
393
|
+
// restore original @rocicorp/zero-sqlite3 if we shimmed it
|
|
394
|
+
// (next start will re-apply shim if needed for wasm mode)
|
|
395
|
+
cleanupShim(sqliteModeConfig?.zeroSqlitePath)
|
|
367
396
|
log.debug.orez('stopped')
|
|
368
397
|
}
|
|
369
398
|
|
|
@@ -435,73 +464,13 @@ async function seedIfNeeded(db: PGlite, config: ZeroLiteConfig): Promise<void> {
|
|
|
435
464
|
log.orez('seeded')
|
|
436
465
|
}
|
|
437
466
|
|
|
438
|
-
//
|
|
439
|
-
// bedrock-sqlite (wasm). uses NODE_PATH to make node resolve our shim first —
|
|
440
|
-
// no require hooks, no Module._resolveFilename monkey-patching, no .cjs files
|
|
441
|
-
// in the package (which all break vite).
|
|
442
|
-
function writeSqliteShim(): string {
|
|
443
|
-
const tmp = process.env.TMPDIR || process.env.TEMP || '/tmp'
|
|
444
|
-
const dir = resolve(tmp, 'orez-sqlite', 'node_modules', '@rocicorp', 'zero-sqlite3')
|
|
445
|
-
mkdirSync(dir, { recursive: true })
|
|
446
|
-
|
|
447
|
-
const bedrockEntry = resolvePackage('bedrock-sqlite')
|
|
448
|
-
|
|
449
|
-
writeFileSync(
|
|
450
|
-
resolve(dir, 'package.json'),
|
|
451
|
-
'{"name":"@rocicorp/zero-sqlite3","main":"./index.js"}\n'
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
writeFileSync(
|
|
455
|
-
resolve(dir, 'index.js'),
|
|
456
|
-
`'use strict';
|
|
457
|
-
var mod = require('${bedrockEntry}');
|
|
458
|
-
var OrigDatabase = mod.Database;
|
|
459
|
-
var SqliteError = mod.SqliteError;
|
|
460
|
-
function Database() {
|
|
461
|
-
var db = new OrigDatabase(...arguments);
|
|
462
|
-
try {
|
|
463
|
-
db.pragma('journal_mode = delete');
|
|
464
|
-
db.pragma('busy_timeout = 30000');
|
|
465
|
-
db.pragma('synchronous = normal');
|
|
466
|
-
} catch(e) {}
|
|
467
|
-
return db;
|
|
468
|
-
}
|
|
469
|
-
Database.prototype = OrigDatabase.prototype;
|
|
470
|
-
Database.prototype.constructor = Database;
|
|
471
|
-
Object.keys(OrigDatabase).forEach(function(k) { Database[k] = OrigDatabase[k]; });
|
|
472
|
-
Database.prototype.unsafeMode = function() { return this; };
|
|
473
|
-
if (!Database.prototype.defaultSafeIntegers) Database.prototype.defaultSafeIntegers = function() { return this; };
|
|
474
|
-
if (!Database.prototype.serialize) Database.prototype.serialize = function() { throw new Error('not supported in wasm'); };
|
|
475
|
-
if (!Database.prototype.backup) Database.prototype.backup = function() { throw new Error('not supported in wasm'); };
|
|
476
|
-
var tmpDb = new OrigDatabase(':memory:');
|
|
477
|
-
var tmpStmt = tmpDb.prepare('SELECT 1');
|
|
478
|
-
var SP = Object.getPrototypeOf(tmpStmt);
|
|
479
|
-
if (!SP.safeIntegers) SP.safeIntegers = function() { return this; };
|
|
480
|
-
SP.scanStatus = function() { return undefined; };
|
|
481
|
-
SP.scanStatusV2 = function() { return []; };
|
|
482
|
-
SP.scanStatusReset = function() {};
|
|
483
|
-
tmpDb.close();
|
|
484
|
-
Database.SQLITE_SCANSTAT_NLOOP = 0;
|
|
485
|
-
Database.SQLITE_SCANSTAT_NVISIT = 1;
|
|
486
|
-
Database.SQLITE_SCANSTAT_EST = 2;
|
|
487
|
-
Database.SQLITE_SCANSTAT_NAME = 3;
|
|
488
|
-
Database.SQLITE_SCANSTAT_EXPLAIN = 4;
|
|
489
|
-
Database.SQLITE_SCANSTAT_SELECTID = 5;
|
|
490
|
-
Database.SQLITE_SCANSTAT_PARENTID = 6;
|
|
491
|
-
Database.SQLITE_SCANSTAT_NCYCLE = 7;
|
|
492
|
-
Database.SQLITE_SCANSTAT_COMPLEX = 8;
|
|
493
|
-
module.exports = Database;
|
|
494
|
-
module.exports.SqliteError = SqliteError;
|
|
495
|
-
`
|
|
496
|
-
)
|
|
497
|
-
|
|
498
|
-
// return the node_modules root so it can be prepended to NODE_PATH
|
|
499
|
-
return resolve(tmp, 'orez-sqlite', 'node_modules')
|
|
500
|
-
}
|
|
467
|
+
// writeSqliteShim replaced by sqlite-mode/apply-mode.ts applySqliteMode()
|
|
501
468
|
|
|
502
469
|
async function startZeroCache(
|
|
503
470
|
config: ZeroLiteConfig,
|
|
504
|
-
logStore?: LogStore
|
|
471
|
+
logStore?: LogStore,
|
|
472
|
+
sqliteMode: SqliteMode = resolveSqliteMode(config.disableWasmSqlite),
|
|
473
|
+
sqliteModeConfig?: SqliteModeConfig | null
|
|
505
474
|
): Promise<{ process: ChildProcess; env: Record<string, string> }> {
|
|
506
475
|
// resolve @rocicorp/zero entry for finding zero-cache modules
|
|
507
476
|
const zeroEntry = resolvePackage('@rocicorp/zero')
|
|
@@ -510,7 +479,7 @@ async function startZeroCache(
|
|
|
510
479
|
throw new Error('zero-cache not found. install @rocicorp/zero')
|
|
511
480
|
}
|
|
512
481
|
|
|
513
|
-
if (
|
|
482
|
+
if (sqliteMode === 'native') {
|
|
514
483
|
log.debug.orez('wasm sqlite disabled, using native @rocicorp/zero-sqlite3')
|
|
515
484
|
}
|
|
516
485
|
|
|
@@ -551,20 +520,25 @@ async function startZeroCache(
|
|
|
551
520
|
throw new Error('zero-cache cli.js not found. install @rocicorp/zero')
|
|
552
521
|
}
|
|
553
522
|
|
|
554
|
-
//
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
523
|
+
// apply sqlite mode - either wasm shim or restore to native
|
|
524
|
+
if (sqliteModeConfig) {
|
|
525
|
+
const result = applySqliteMode(sqliteModeConfig)
|
|
526
|
+
if (!result.success) {
|
|
527
|
+
// native mode failure is fatal - user explicitly requested native but we can't provide it
|
|
528
|
+
if (sqliteMode === 'native') {
|
|
529
|
+
throw new Error(`cannot use native sqlite: ${result.error}`)
|
|
530
|
+
}
|
|
531
|
+
// wasm mode failure is also fatal now - can't safely proceed without backup
|
|
532
|
+
throw new Error(`cannot apply sqlite mode: ${result.error}`)
|
|
533
|
+
} else if (result.shimPath) {
|
|
534
|
+
log.debug.orez(`shimmed @rocicorp/zero-sqlite3 at ${result.shimPath}`)
|
|
535
|
+
}
|
|
563
536
|
}
|
|
564
537
|
|
|
565
|
-
const nodeOptions =
|
|
566
|
-
|
|
567
|
-
|
|
538
|
+
const nodeOptions =
|
|
539
|
+
sqliteMode === 'wasm'
|
|
540
|
+
? `--max-old-space-size=16384 ${process.env.NODE_OPTIONS || ''}`
|
|
541
|
+
: process.env.NODE_OPTIONS || ''
|
|
568
542
|
if (nodeOptions.trim()) env.NODE_OPTIONS = nodeOptions.trim()
|
|
569
543
|
|
|
570
544
|
const child = spawn(zeroCacheBin, [], {
|
package/src/log.ts
CHANGED
|
@@ -41,9 +41,9 @@ export function port(n: number, color: keyof typeof COLORS): string {
|
|
|
41
41
|
return `${DIM}${COLORS[color]}:${n}${RESET}`
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** format a url with
|
|
44
|
+
/** format a url with green color */
|
|
45
45
|
export function url(u: string): string {
|
|
46
|
-
return `${COLORS.
|
|
46
|
+
return `${COLORS.green}${u}${RESET}`
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// map logger labels to logStore source names
|