zuzu-js 0.1.0
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/LICENSE +5 -0
- package/README.md +113 -0
- package/bin/zuzu +17 -0
- package/bin/zuzu-build-browser-bundle +57 -0
- package/bin/zuzu-generate-browser-stdlib +584 -0
- package/bin/zuzu-js +23 -0
- package/bin/zuzu-js-compile +152 -0
- package/bin/zuzu-js-electron +19 -0
- package/dist/zuzu-browser-worker.js +45574 -0
- package/dist/zuzu-browser.js +45362 -0
- package/lib/browser-bundle-entry.js +160 -0
- package/lib/browser-gui-renderer.js +387 -0
- package/lib/browser-runtime.js +167 -0
- package/lib/browser-worker-entry.js +413 -0
- package/lib/browser-ztests/runner.html +103 -0
- package/lib/browser-ztests/runner.js +369 -0
- package/lib/cli.js +350 -0
- package/lib/collections.js +367 -0
- package/lib/compiler.js +303 -0
- package/lib/electron/launcher.js +70 -0
- package/lib/electron/main.js +956 -0
- package/lib/electron/preload.js +80 -0
- package/lib/electron/renderer.html +122 -0
- package/lib/electron/renderer.js +24 -0
- package/lib/execution-metadata.js +18 -0
- package/lib/gui/dom-renderer.js +778 -0
- package/lib/host/browser-host.js +278 -0
- package/lib/host/capabilities.js +47 -0
- package/lib/host/electron-host.js +15 -0
- package/lib/host/node-host.js +74 -0
- package/lib/paths.js +150 -0
- package/lib/runtime-entrypoints.js +60 -0
- package/lib/runtime-helpers.js +886 -0
- package/lib/runtime.js +3529 -0
- package/lib/tap.js +37 -0
- package/lib/transpiler-new/ast.js +23 -0
- package/lib/transpiler-new/codegen.js +2455 -0
- package/lib/transpiler-new/errors.js +28 -0
- package/lib/transpiler-new/index.js +26 -0
- package/lib/transpiler-new/lexer.js +834 -0
- package/lib/transpiler-new/parser.js +2332 -0
- package/lib/transpiler-new/validate-bindings.js +326 -0
- package/lib/transpiler-utils.js +95 -0
- package/lib/transpiler.js +33 -0
- package/lib/zuzu.js +53 -0
- package/modules/javascript.js +193 -0
- package/modules/std/archive.js +603 -0
- package/modules/std/clib.js +338 -0
- package/modules/std/data/csv.js +1331 -0
- package/modules/std/data/json.js +531 -0
- package/modules/std/data/xml.js +441 -0
- package/modules/std/data/yaml.js +256 -0
- package/modules/std/db-worker.js +250 -0
- package/modules/std/db.js +664 -0
- package/modules/std/digest/_hash.js +443 -0
- package/modules/std/digest/md5.js +26 -0
- package/modules/std/digest/sha.js +72 -0
- package/modules/std/eval.js +10 -0
- package/modules/std/gui/objects.js +1519 -0
- package/modules/std/internals.js +571 -0
- package/modules/std/io/socks-worker.js +318 -0
- package/modules/std/io/socks.js +186 -0
- package/modules/std/io.js +475 -0
- package/modules/std/marshal/cbor.js +463 -0
- package/modules/std/marshal/graph.js +1624 -0
- package/modules/std/marshal.js +87 -0
- package/modules/std/math/bignum.js +91 -0
- package/modules/std/math.js +79 -0
- package/modules/std/net/dns.js +306 -0
- package/modules/std/net/http.js +820 -0
- package/modules/std/net/smtp.js +943 -0
- package/modules/std/net/url.js +109 -0
- package/modules/std/proc.js +602 -0
- package/modules/std/secure.js +3724 -0
- package/modules/std/string/base64.js +138 -0
- package/modules/std/string.js +299 -0
- package/modules/std/task.js +914 -0
- package/modules/std/time.js +579 -0
- package/modules/std/tui.js +188 -0
- package/modules/std/worker-thread.js +246 -0
- package/modules/std/worker.js +790 -0
- package/package.json +67 -0
- package/stdlib/modules/javascript.zzm +99 -0
- package/stdlib/modules/perl.zzm +105 -0
- package/stdlib/modules/std/archive.zzm +132 -0
- package/stdlib/modules/std/cache/lru.zzm +174 -0
- package/stdlib/modules/std/clib.zzm +112 -0
- package/stdlib/modules/std/colour.zzm +220 -0
- package/stdlib/modules/std/config.zzm +818 -0
- package/stdlib/modules/std/data/cbor.zzm +497 -0
- package/stdlib/modules/std/data/csv.zzm +285 -0
- package/stdlib/modules/std/data/ini.zzm +472 -0
- package/stdlib/modules/std/data/json/schema/core.zzm +573 -0
- package/stdlib/modules/std/data/json/schema/format.zzm +581 -0
- package/stdlib/modules/std/data/json/schema/model.zzm +255 -0
- package/stdlib/modules/std/data/json/schema/output.zzm +272 -0
- package/stdlib/modules/std/data/json/schema/relative_pointer.zzm +299 -0
- package/stdlib/modules/std/data/json/schema/validation.zzm +1503 -0
- package/stdlib/modules/std/data/json/schema.zzm +306 -0
- package/stdlib/modules/std/data/json.zzm +102 -0
- package/stdlib/modules/std/data/kdl/json.zzm +460 -0
- package/stdlib/modules/std/data/kdl/xml.zzm +387 -0
- package/stdlib/modules/std/data/kdl.zzm +1631 -0
- package/stdlib/modules/std/data/toml.zzm +756 -0
- package/stdlib/modules/std/data/toon.zzm +1017 -0
- package/stdlib/modules/std/data/xml/escape.zzm +156 -0
- package/stdlib/modules/std/data/xml.zzm +276 -0
- package/stdlib/modules/std/data/yaml.zzm +94 -0
- package/stdlib/modules/std/db.zzm +173 -0
- package/stdlib/modules/std/defer.zzm +75 -0
- package/stdlib/modules/std/digest/crc32.zzm +196 -0
- package/stdlib/modules/std/digest/md5.zzm +54 -0
- package/stdlib/modules/std/digest/sha.zzm +83 -0
- package/stdlib/modules/std/dump.zzm +317 -0
- package/stdlib/modules/std/eval.zzm +63 -0
- package/stdlib/modules/std/getopt.zzm +432 -0
- package/stdlib/modules/std/gui/dialogue.zzm +592 -0
- package/stdlib/modules/std/gui/objects.zzm +123 -0
- package/stdlib/modules/std/gui.zzm +1914 -0
- package/stdlib/modules/std/internals.zzm +139 -0
- package/stdlib/modules/std/io/socks.zzm +139 -0
- package/stdlib/modules/std/io.zzm +157 -0
- package/stdlib/modules/std/lingua/en.zzm +347 -0
- package/stdlib/modules/std/log.zzm +169 -0
- package/stdlib/modules/std/mail.zzm +2726 -0
- package/stdlib/modules/std/marshal.zzm +138 -0
- package/stdlib/modules/std/math/bignum.zzm +98 -0
- package/stdlib/modules/std/math/range.zzm +116 -0
- package/stdlib/modules/std/math/roman.zzm +156 -0
- package/stdlib/modules/std/math.zzm +141 -0
- package/stdlib/modules/std/net/dns.zzm +93 -0
- package/stdlib/modules/std/net/http.zzm +278 -0
- package/stdlib/modules/std/net/smtp.zzm +257 -0
- package/stdlib/modules/std/net/url.zzm +69 -0
- package/stdlib/modules/std/path/jsonpointer.zzm +526 -0
- package/stdlib/modules/std/path/kdl.zzm +1003 -0
- package/stdlib/modules/std/path/simple.zzm +520 -0
- package/stdlib/modules/std/path/z/context.zzm +147 -0
- package/stdlib/modules/std/path/z/evaluate.zzm +549 -0
- package/stdlib/modules/std/path/z/functions.zzm +874 -0
- package/stdlib/modules/std/path/z/lexer.zzm +490 -0
- package/stdlib/modules/std/path/z/node.zzm +1455 -0
- package/stdlib/modules/std/path/z/operators.zzm +445 -0
- package/stdlib/modules/std/path/z/parser.zzm +359 -0
- package/stdlib/modules/std/path/z.zzm +403 -0
- package/stdlib/modules/std/path/zz/functions.zzm +828 -0
- package/stdlib/modules/std/path/zz/operators.zzm +1036 -0
- package/stdlib/modules/std/path/zz.zzm +100 -0
- package/stdlib/modules/std/proc.zzm +155 -0
- package/stdlib/modules/std/result.zzm +149 -0
- package/stdlib/modules/std/secure.zzm +606 -0
- package/stdlib/modules/std/string/base64.zzm +66 -0
- package/stdlib/modules/std/string/quoted_printable.zzm +485 -0
- package/stdlib/modules/std/string.zzm +179 -0
- package/stdlib/modules/std/task.zzm +221 -0
- package/stdlib/modules/std/template/z.zzm +531 -0
- package/stdlib/modules/std/template/zz.zzm +62 -0
- package/stdlib/modules/std/time.zzm +188 -0
- package/stdlib/modules/std/tui.zzm +89 -0
- package/stdlib/modules/std/uuid.zzm +223 -0
- package/stdlib/modules/std/web/session.zzm +388 -0
- package/stdlib/modules/std/web/static.zzm +329 -0
- package/stdlib/modules/std/web.zzm +1942 -0
- package/stdlib/modules/std/worker.zzm +202 -0
- package/stdlib/modules/std/zuzuzoo.zzm +3960 -0
- package/stdlib/modules/test/more.zzm +528 -0
- package/stdlib/modules/test/parser.zzm +209 -0
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
let runtimePolicy = {
|
|
4
|
+
host_name: 'node',
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
function dbError( message ) {
|
|
8
|
+
const err = new Error( String( message ) );
|
|
9
|
+
err.to_String = function to_String() {
|
|
10
|
+
return this.message;
|
|
11
|
+
};
|
|
12
|
+
return err;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function fail( message ) {
|
|
16
|
+
throw dbError( message );
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function assertWorkerCapability() {
|
|
20
|
+
if ( runtimePolicy.deny_worker ) {
|
|
21
|
+
fail( 'connect failed: DB worker backend is denied by runtime policy' );
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function loadBuiltinSqlite() {
|
|
26
|
+
try {
|
|
27
|
+
const sqlite = require( 'node:sqlite' );
|
|
28
|
+
if ( sqlite && typeof sqlite.DatabaseSync === 'function' ) {
|
|
29
|
+
return sqlite;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch ( _err ) {
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadBetterSqlite3() {
|
|
38
|
+
try {
|
|
39
|
+
return require( 'better-sqlite3' );
|
|
40
|
+
}
|
|
41
|
+
catch ( err ) {
|
|
42
|
+
fail( `connect failed: ${err.message}` );
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function loadNodeModule( name ) {
|
|
47
|
+
try {
|
|
48
|
+
return require( name );
|
|
49
|
+
}
|
|
50
|
+
catch ( err ) {
|
|
51
|
+
fail( `connect failed: ${err.message}` );
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pathString( value ) {
|
|
56
|
+
if ( value == null ) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
if ( typeof value === 'string' ) {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
if ( typeof value.to_String === 'function' ) {
|
|
63
|
+
return String( value.to_String() );
|
|
64
|
+
}
|
|
65
|
+
return String( value );
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sqlLiteral( value ) {
|
|
69
|
+
if ( value == null ) {
|
|
70
|
+
return 'NULL';
|
|
71
|
+
}
|
|
72
|
+
if ( typeof value === 'number' ) {
|
|
73
|
+
return Number.isFinite( value ) ? String( value ) : 'NULL';
|
|
74
|
+
}
|
|
75
|
+
if ( typeof value === 'boolean' ) {
|
|
76
|
+
return value ? '1' : '0';
|
|
77
|
+
}
|
|
78
|
+
return `'${String( value ).replace( /'/gu, "''" )}'`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function inferCode( value ) {
|
|
82
|
+
if ( typeof value === 'number' ) {
|
|
83
|
+
return Number.isInteger( value ) ? 'INTEGER' : 'REAL';
|
|
84
|
+
}
|
|
85
|
+
if ( typeof value === 'string' ) {
|
|
86
|
+
return 'TEXT';
|
|
87
|
+
}
|
|
88
|
+
if ( typeof value === 'boolean' ) {
|
|
89
|
+
return 'BOOLEAN';
|
|
90
|
+
}
|
|
91
|
+
return 'UNKNOWN';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function decorateDict( value ) {
|
|
95
|
+
const methods = [
|
|
96
|
+
[ 'length', function _length() { return Object.keys( this ).length; } ],
|
|
97
|
+
[ 'count', function _count() { return Object.keys( this ).length; } ],
|
|
98
|
+
[ 'empty', function _empty() { return Object.keys( this ).length === 0 ? 1 : 0; } ],
|
|
99
|
+
[ 'keys', function _keys() { return Object.keys( this ).sort(); } ],
|
|
100
|
+
[ 'values', function _values() { return this.keys().map( (key) => this[key] ); } ],
|
|
101
|
+
[
|
|
102
|
+
'has',
|
|
103
|
+
function _has( key ) {
|
|
104
|
+
return Object.prototype.hasOwnProperty.call( this, String( key ) ) ? 1 : 0;
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
[ 'contains', function _contains( key ) { return this.has( key ); } ],
|
|
108
|
+
[ 'exists', function _exists( key ) { return this.has( key ); } ],
|
|
109
|
+
[
|
|
110
|
+
'defined',
|
|
111
|
+
function _defined( key ) {
|
|
112
|
+
return Object.prototype.hasOwnProperty.call( this, String( key ) )
|
|
113
|
+
&& this[String( key )] != null ? 1 : 0;
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
[ 'get', function _get( key, fallback = null ) { return this.has( key ) ? this[String( key )] : fallback; } ],
|
|
117
|
+
[ 'set', function _set( key, item ) { this[String( key )] = item; return this; } ],
|
|
118
|
+
[ 'add', function _add( key, item ) { this[String( key )] = item; return this; } ],
|
|
119
|
+
[ 'remove', function _remove( key ) { delete this[String( key )]; return this; } ],
|
|
120
|
+
[ 'sorted_keys', function _sorted_keys() { return this.keys(); } ],
|
|
121
|
+
[ 'to_Iterator', function _to_iterator() { return this.keys()[Symbol.iterator](); } ],
|
|
122
|
+
];
|
|
123
|
+
for ( const [ name, fn ] of methods ) {
|
|
124
|
+
if ( !Object.prototype.hasOwnProperty.call( value, name ) ) {
|
|
125
|
+
Object.defineProperty( value, name, { value: fn, enumerable: false } );
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseSelectedTable( sql ) {
|
|
132
|
+
const match = String( sql ).match( /\bfrom\s+([A-Za-z_][A-Za-z0-9_]*)/iu );
|
|
133
|
+
return match ? match[1] : null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function schemaForQuery( dbh, sql, columns, rows ) {
|
|
137
|
+
if ( Array.isArray( columns ) && columns.length > 0 ) {
|
|
138
|
+
const typed = columns.map( (col) => ( {
|
|
139
|
+
name: col.name,
|
|
140
|
+
code: col.type || null,
|
|
141
|
+
} ) );
|
|
142
|
+
if ( typed.some( (col) => col.code ) ) {
|
|
143
|
+
return typed.map( (col) => ( {
|
|
144
|
+
name: col.name,
|
|
145
|
+
code: col.code || 'UNKNOWN',
|
|
146
|
+
} ) );
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const table = parseSelectedTable( sql );
|
|
150
|
+
if ( table ) {
|
|
151
|
+
try {
|
|
152
|
+
const cols = dbh.backend.query(
|
|
153
|
+
`pragma table_info(${quoteSqliteIdentifier( table )});`,
|
|
154
|
+
[]
|
|
155
|
+
).rows;
|
|
156
|
+
if ( Array.isArray( cols ) && cols.length > 0 ) {
|
|
157
|
+
return cols.map( (col) => ( {
|
|
158
|
+
name: col.name,
|
|
159
|
+
code: col.type || 'UNKNOWN',
|
|
160
|
+
} ) );
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch ( _err ) {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const first = rows[0] || {};
|
|
167
|
+
return Object.keys( first ).map( (name) => ( {
|
|
168
|
+
name,
|
|
169
|
+
code: inferCode( first[name] ),
|
|
170
|
+
} ) );
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function quoteSqliteIdentifier( value ) {
|
|
174
|
+
return `"${String( value ).replace( /"/gu, '""' )}"`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizeBindValues( values ) {
|
|
178
|
+
return values.map( (value) => ( typeof value === 'boolean' ? ( value ? 1 : 0 ) : value ) );
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseDbiDsn( dsn ) {
|
|
182
|
+
const text = String( dsn ?? '' );
|
|
183
|
+
const match = text.match( /^dbi:([^:]+):(.*)$/iu );
|
|
184
|
+
if ( !match ) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const attrs = Object.create( null );
|
|
188
|
+
const body = match[2];
|
|
189
|
+
for ( const part of body.split( ';' ) ) {
|
|
190
|
+
if ( part === '' ) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const eq = part.indexOf( '=' );
|
|
194
|
+
if ( eq < 0 ) {
|
|
195
|
+
attrs[part.toLowerCase()] = '';
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
attrs[part.slice( 0, eq ).toLowerCase()] = part.slice( eq + 1 );
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
driver: match[1].toLowerCase(),
|
|
202
|
+
attrs,
|
|
203
|
+
text,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function sqliteBeginSql( settings ) {
|
|
208
|
+
const isolation = String( settings?.isolation_level || 'deferred' ).toLowerCase();
|
|
209
|
+
if ( ![ 'deferred', 'immediate', 'exclusive' ].includes( isolation ) ) {
|
|
210
|
+
fail( `connect failed: unsupported SQLite isolation_level ${isolation}` );
|
|
211
|
+
}
|
|
212
|
+
return `begin ${isolation}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
class BetterSqliteBackend {
|
|
216
|
+
constructor( dbPath, settings = {} ) {
|
|
217
|
+
this.kind = 'sqlite';
|
|
218
|
+
this.dbPath = dbPath;
|
|
219
|
+
this.settings = settings && typeof settings === 'object' ? settings : {};
|
|
220
|
+
const BetterSqlite3 = loadBetterSqlite3();
|
|
221
|
+
try {
|
|
222
|
+
this.db = new BetterSqlite3( dbPath );
|
|
223
|
+
}
|
|
224
|
+
catch ( err ) {
|
|
225
|
+
fail( `connect failed: ${err.message}` );
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
query( sql, values ) {
|
|
230
|
+
try {
|
|
231
|
+
const stmt = this.db.prepare( String( sql ) );
|
|
232
|
+
const bind = normalizeBindValues( values );
|
|
233
|
+
if ( stmt.reader ) {
|
|
234
|
+
return {
|
|
235
|
+
rows: stmt.all( ...bind ),
|
|
236
|
+
columns: stmt.columns(),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
stmt.run( ...bind );
|
|
240
|
+
return { rows: [], columns: [] };
|
|
241
|
+
}
|
|
242
|
+
catch ( err ) {
|
|
243
|
+
fail( err.message || err );
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
begin() {
|
|
248
|
+
try {
|
|
249
|
+
this.db.exec( sqliteBeginSql( this.settings ) );
|
|
250
|
+
}
|
|
251
|
+
catch ( err ) {
|
|
252
|
+
fail( err.message || err );
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
commit() {
|
|
257
|
+
try {
|
|
258
|
+
this.db.exec( 'commit' );
|
|
259
|
+
}
|
|
260
|
+
catch ( err ) {
|
|
261
|
+
fail( err.message || err );
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
rollback() {
|
|
266
|
+
try {
|
|
267
|
+
this.db.exec( 'rollback' );
|
|
268
|
+
}
|
|
269
|
+
catch ( err ) {
|
|
270
|
+
fail( err.message || err );
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
class BuiltinSqliteBackend {
|
|
276
|
+
constructor( dbPath, settings = {} ) {
|
|
277
|
+
this.kind = 'sqlite';
|
|
278
|
+
this.dbPath = dbPath;
|
|
279
|
+
this.settings = settings && typeof settings === 'object' ? settings : {};
|
|
280
|
+
const sqlite = loadBuiltinSqlite();
|
|
281
|
+
if ( !sqlite ) {
|
|
282
|
+
fail( 'connect failed: node:sqlite is not available' );
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
this.db = new sqlite.DatabaseSync( dbPath );
|
|
286
|
+
}
|
|
287
|
+
catch ( err ) {
|
|
288
|
+
fail( `connect failed: ${err.message}` );
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
query( sql, values ) {
|
|
293
|
+
try {
|
|
294
|
+
const stmt = this.db.prepare( String( sql ) );
|
|
295
|
+
const bind = normalizeBindValues( values );
|
|
296
|
+
const columns = stmt.columns();
|
|
297
|
+
return {
|
|
298
|
+
rows: stmt.all( ...bind ),
|
|
299
|
+
columns,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
catch ( err ) {
|
|
303
|
+
fail( err.message || err );
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
begin() {
|
|
308
|
+
try {
|
|
309
|
+
this.db.exec( sqliteBeginSql( this.settings ) );
|
|
310
|
+
}
|
|
311
|
+
catch ( err ) {
|
|
312
|
+
fail( err.message || err );
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
commit() {
|
|
317
|
+
try {
|
|
318
|
+
this.db.exec( 'commit' );
|
|
319
|
+
}
|
|
320
|
+
catch ( err ) {
|
|
321
|
+
fail( err.message || err );
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
rollback() {
|
|
326
|
+
try {
|
|
327
|
+
this.db.exec( 'rollback' );
|
|
328
|
+
}
|
|
329
|
+
catch ( err ) {
|
|
330
|
+
fail( err.message || err );
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function createSqliteBackend( dbPath, settings ) {
|
|
336
|
+
if ( loadBuiltinSqlite() ) {
|
|
337
|
+
return new BuiltinSqliteBackend( dbPath, settings );
|
|
338
|
+
}
|
|
339
|
+
return new BetterSqliteBackend( dbPath, settings );
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function driverWorkerConfig( driver, attrs, settings = {} ) {
|
|
343
|
+
const config = {
|
|
344
|
+
...attrs,
|
|
345
|
+
...( settings && typeof settings === 'object' ? settings : {} ),
|
|
346
|
+
};
|
|
347
|
+
const database = config.database || config.dbname;
|
|
348
|
+
const out = {};
|
|
349
|
+
if ( database ) {
|
|
350
|
+
out.database = String( database );
|
|
351
|
+
}
|
|
352
|
+
if ( config.user ) {
|
|
353
|
+
out.user = String( config.user );
|
|
354
|
+
}
|
|
355
|
+
if ( config.password || config.pass ) {
|
|
356
|
+
out.password = String( config.password || config.pass );
|
|
357
|
+
}
|
|
358
|
+
if ( config.port ) {
|
|
359
|
+
out.port = Number( config.port );
|
|
360
|
+
}
|
|
361
|
+
if ( config.host ) {
|
|
362
|
+
out.host = String( config.host );
|
|
363
|
+
}
|
|
364
|
+
if ( driver === 'mysql' ) {
|
|
365
|
+
if ( config.mysql_socket || config.socket ) {
|
|
366
|
+
out.socketPath = String( config.mysql_socket || config.socket );
|
|
367
|
+
}
|
|
368
|
+
out.decimalNumbers = true;
|
|
369
|
+
out.dateStrings = true;
|
|
370
|
+
}
|
|
371
|
+
if ( driver === 'postgresql' || driver === 'pg' ) {
|
|
372
|
+
if ( config.ssl ) {
|
|
373
|
+
out.ssl = config.ssl;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
class WorkerSqlBackend {
|
|
380
|
+
constructor( driver, attrs, settings = {} ) {
|
|
381
|
+
if ( runtimePolicy.host_name === 'browser' ) {
|
|
382
|
+
fail( 'connect failed: std/db SQL clients are not available in browser' );
|
|
383
|
+
}
|
|
384
|
+
assertWorkerCapability();
|
|
385
|
+
const { Worker } = loadNodeModule( 'node:worker_threads' );
|
|
386
|
+
const path = loadNodeModule( 'node:path' );
|
|
387
|
+
this.kind = driver;
|
|
388
|
+
this.dbPath = null;
|
|
389
|
+
this.settings = settings && typeof settings === 'object' ? settings : {};
|
|
390
|
+
this.worker = new Worker(
|
|
391
|
+
path.join( __dirname, 'db-worker.js' ),
|
|
392
|
+
{
|
|
393
|
+
workerData: {
|
|
394
|
+
driver,
|
|
395
|
+
config: driverWorkerConfig( driver, attrs, this.settings ),
|
|
396
|
+
},
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
this.worker.unref();
|
|
400
|
+
this._request( { op: 'connect' } );
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
_request( request ) {
|
|
404
|
+
const fs = loadNodeModule( 'node:fs' );
|
|
405
|
+
const os = loadNodeModule( 'node:os' );
|
|
406
|
+
const path = loadNodeModule( 'node:path' );
|
|
407
|
+
const dir = fs.mkdtempSync( path.join( os.tmpdir(), 'zuzu-db-' ) );
|
|
408
|
+
const responsePath = path.join( dir, 'response.json' );
|
|
409
|
+
const signal = new SharedArrayBuffer( 4 );
|
|
410
|
+
const state = new Int32Array( signal );
|
|
411
|
+
this.worker.postMessage( {
|
|
412
|
+
...request,
|
|
413
|
+
responsePath,
|
|
414
|
+
signal,
|
|
415
|
+
} );
|
|
416
|
+
Atomics.wait( state, 0, 0 );
|
|
417
|
+
let response;
|
|
418
|
+
try {
|
|
419
|
+
response = JSON.parse( fs.readFileSync( responsePath, 'utf8' ) );
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
fs.rmSync( dir, { recursive: true, force: true } );
|
|
423
|
+
}
|
|
424
|
+
if ( !response.ok ) {
|
|
425
|
+
fail( response.error || 'database request failed' );
|
|
426
|
+
}
|
|
427
|
+
return response.result;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
query( sql, values ) {
|
|
431
|
+
return this._request( {
|
|
432
|
+
op: 'query',
|
|
433
|
+
sql: String( sql ),
|
|
434
|
+
values: normalizeBindValues( values ),
|
|
435
|
+
} );
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
begin() {
|
|
439
|
+
this._request( { op: 'begin' } );
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
commit() {
|
|
443
|
+
this._request( { op: 'commit' } );
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
rollback() {
|
|
447
|
+
this._request( { op: 'rollback' } );
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
close() {
|
|
451
|
+
if ( this.worker ) {
|
|
452
|
+
try {
|
|
453
|
+
this._request( { op: 'close' } );
|
|
454
|
+
}
|
|
455
|
+
catch ( _err ) {
|
|
456
|
+
}
|
|
457
|
+
this.worker = null;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
class StatementHandle {
|
|
463
|
+
constructor( dbh, sql ) {
|
|
464
|
+
this.dbh = dbh;
|
|
465
|
+
this.sql = String( sql );
|
|
466
|
+
this.rows = [];
|
|
467
|
+
this.columns = [];
|
|
468
|
+
this.types = [];
|
|
469
|
+
this.index = 0;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
execute( ...values ) {
|
|
473
|
+
this.index = 0;
|
|
474
|
+
const result = this.dbh.backend.query( this.sql, values );
|
|
475
|
+
if ( result.columns.length > 0 ) {
|
|
476
|
+
this.rows = result.rows;
|
|
477
|
+
this.columns = result.columns.map( (col) => col.name );
|
|
478
|
+
this.types = schemaForQuery( this.dbh, this.sql, result.columns, this.rows );
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
this.rows = [];
|
|
482
|
+
this.columns = [];
|
|
483
|
+
this.types = [];
|
|
484
|
+
}
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
execute_batch( rows ) {
|
|
489
|
+
for ( const row of Array.isArray( rows ) ? rows : [] ) {
|
|
490
|
+
this.execute( ...( Array.isArray( row ) ? row : [] ) );
|
|
491
|
+
}
|
|
492
|
+
return this;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
column_names() {
|
|
496
|
+
return this.columns.slice();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
column_types() {
|
|
500
|
+
return this.types.map( (entry) => ( { code: entry.code, name: entry.name } ) );
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
_nextRow() {
|
|
504
|
+
if ( this.index >= this.rows.length ) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
return this.rows[this.index++];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
coerceRowObject( row ) {
|
|
511
|
+
return row == null ? null : decorateDict( { ...row } );
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
next_array() {
|
|
515
|
+
const row = this._nextRow();
|
|
516
|
+
return row == null ? null : this.columns.map( (name) => row[name] );
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
next_dict() {
|
|
520
|
+
const row = this._nextRow();
|
|
521
|
+
return this.coerceRowObject( row );
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
all_array() {
|
|
525
|
+
const out = [];
|
|
526
|
+
let row;
|
|
527
|
+
while ( ( row = this.next_array() ) !== null ) {
|
|
528
|
+
out.push( row );
|
|
529
|
+
}
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
all_dict() {
|
|
534
|
+
const out = [];
|
|
535
|
+
let row;
|
|
536
|
+
while ( ( row = this.next_dict() ) !== null ) {
|
|
537
|
+
out.push( row );
|
|
538
|
+
}
|
|
539
|
+
return out;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
next_typed_array() {
|
|
543
|
+
return this.next_array();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
next_typed_dict() {
|
|
547
|
+
return this.next_dict();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
all_typed_array() {
|
|
551
|
+
return this.all_array();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
all_typed_dict() {
|
|
555
|
+
return this.all_dict();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
*[Symbol.iterator]() {
|
|
559
|
+
let row;
|
|
560
|
+
while ( ( row = this.next_typed_dict() ) !== null ) {
|
|
561
|
+
yield row;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
to_Iterator() {
|
|
566
|
+
return this[Symbol.iterator]();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
class DatabaseHandle {
|
|
571
|
+
constructor( backend, settings = {} ) {
|
|
572
|
+
this.backend = backend;
|
|
573
|
+
this.dbPath = backend.dbPath;
|
|
574
|
+
this.settings = settings && typeof settings === 'object' ? settings : {};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
prepare( sql ) {
|
|
578
|
+
return new StatementHandle( this, sql );
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
quote( value ) {
|
|
582
|
+
return sqlLiteral( value );
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
begin() {
|
|
586
|
+
this.backend.begin();
|
|
587
|
+
return this;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
commit() {
|
|
591
|
+
this.backend.commit();
|
|
592
|
+
return this;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
rollback() {
|
|
596
|
+
this.backend.rollback();
|
|
597
|
+
return this;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
execute_batch( sql, rows ) {
|
|
601
|
+
return this.prepare( sql ).execute_batch( rows );
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
close() {
|
|
605
|
+
if ( this.backend && typeof this.backend.close === 'function' ) {
|
|
606
|
+
this.backend.close();
|
|
607
|
+
}
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function sqliteBackendFromDsn( dsn, settings ) {
|
|
613
|
+
const text = String( dsn ?? '' );
|
|
614
|
+
const match = text.match( /^dbi:sqlite:dbname=(.+)$/iu );
|
|
615
|
+
if ( !match ) {
|
|
616
|
+
fail( `connect failed: unsupported dsn ${text}` );
|
|
617
|
+
}
|
|
618
|
+
return createSqliteBackend( match[1], settings );
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function backendFromDsn( dsn, settings ) {
|
|
622
|
+
const parsed = parseDbiDsn( dsn );
|
|
623
|
+
if ( !parsed ) {
|
|
624
|
+
fail( `connect failed: unsupported dsn ${String( dsn ?? '' )}` );
|
|
625
|
+
}
|
|
626
|
+
if ( parsed.driver === 'sqlite' ) {
|
|
627
|
+
return sqliteBackendFromDsn( parsed.text, settings );
|
|
628
|
+
}
|
|
629
|
+
if ( parsed.driver === 'mysql' ) {
|
|
630
|
+
return new WorkerSqlBackend( 'mysql', parsed.attrs, settings );
|
|
631
|
+
}
|
|
632
|
+
if ( parsed.driver === 'pg' || parsed.driver === 'postgresql' ) {
|
|
633
|
+
return new WorkerSqlBackend( 'postgresql', parsed.attrs, settings );
|
|
634
|
+
}
|
|
635
|
+
fail( `connect failed: unsupported dsn ${parsed.text}` );
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function sqliteHandle( dbPath, settings ) {
|
|
639
|
+
return new DatabaseHandle( createSqliteBackend( dbPath, settings ), settings );
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const DB = {
|
|
643
|
+
connect( dsn, settings = {} ) {
|
|
644
|
+
return new DatabaseHandle( backendFromDsn( dsn, settings ), settings );
|
|
645
|
+
},
|
|
646
|
+
temp( settings = {} ) {
|
|
647
|
+
return sqliteHandle( ':memory:', settings );
|
|
648
|
+
},
|
|
649
|
+
open( target, settings = {} ) {
|
|
650
|
+
return sqliteHandle( pathString( target ), settings );
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
module.exports = {
|
|
655
|
+
DB,
|
|
656
|
+
DatabaseHandle,
|
|
657
|
+
StatementHandle,
|
|
658
|
+
__zuzu_set_runtime_policy( policy = {} ) {
|
|
659
|
+
runtimePolicy = {
|
|
660
|
+
...runtimePolicy,
|
|
661
|
+
...( policy && typeof policy === 'object' ? policy : {} ),
|
|
662
|
+
};
|
|
663
|
+
},
|
|
664
|
+
};
|