@retaw106/mmc-pi 0.0.0-stage → 1.2.2
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 +201 -2
- package/dist/historian-calibration-extension.js +62 -0
- package/dist/index-54cgx9gm.js +489 -0
- package/dist/index-65ee50rm.js +16 -0
- package/dist/index-b3eqj1g6.js +51 -0
- package/dist/index-v6scmxvt.js +40 -0
- package/dist/index-wv28nczk.js +464 -0
- package/dist/index-ztb4k1mt.js +5459 -0
- package/dist/index.js +55678 -0
- package/dist/message-time-backfill-h02fzzp8.js +116 -0
- package/dist/read-session-chunk-af3sdznq.js +56 -0
- package/dist/rpc-notifications-jxy30tgz.js +8 -0
- package/dist/safe-notification-target-hn43s7bc.js +9 -0
- package/dist/web/main.js +6322 -0
- package/dist/web/ui/favicon.svg +4 -0
- package/dist/web/ui/index.html +36 -0
- package/dist/web/ui/panel.css +8 -0
- package/dist/web/ui/panel.js +373 -0
- package/package.json +86 -5
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// ../plugin/src/shared/sqlite.ts
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
var reportSlowPrivilegedWrite;
|
|
4
|
+
function registerSlowWriteReporter(reporter) {
|
|
5
|
+
reportSlowPrivilegedWrite = reporter;
|
|
6
|
+
}
|
|
7
|
+
var sqliteDiagnosticSink;
|
|
8
|
+
function registerSqliteDiagnosticSink(sink) {
|
|
9
|
+
sqliteDiagnosticSink = sink;
|
|
10
|
+
}
|
|
11
|
+
function reportSqliteDiagnostic(message) {
|
|
12
|
+
try {
|
|
13
|
+
sqliteDiagnosticSink?.(`[magic-context] ${message}`);
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
function detectSqliteRuntime() {
|
|
17
|
+
const hasBunVersion = typeof process !== "undefined" && typeof process.versions?.bun === "string";
|
|
18
|
+
const hasBunGlobal = typeof globalThis !== "undefined" && typeof globalThis.Bun !== "undefined";
|
|
19
|
+
return hasBunVersion || hasBunGlobal ? "Bun" : "Node.js";
|
|
20
|
+
}
|
|
21
|
+
var bunSpec = "bun:" + "sqlite";
|
|
22
|
+
var nodeSpec = "node:" + "sqlite";
|
|
23
|
+
async function importSqliteModule(specifier) {
|
|
24
|
+
return await import(specifier);
|
|
25
|
+
}
|
|
26
|
+
function isModuleNotFoundError(error, specifier) {
|
|
27
|
+
const candidate = error;
|
|
28
|
+
const code = typeof candidate?.code === "string" ? candidate.code : "";
|
|
29
|
+
const name = typeof candidate?.name === "string" ? candidate.name : "";
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
31
|
+
const details = `${code} ${name} ${message}`.toLowerCase();
|
|
32
|
+
const mentionsSpecifier = details.includes(specifier.toLowerCase());
|
|
33
|
+
if (!mentionsSpecifier)
|
|
34
|
+
return false;
|
|
35
|
+
return code === "ERR_MODULE_NOT_FOUND" || code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND" || name === "ResolveMessage" || details.includes("module not found") || details.includes("cannot find module") || details.includes("cannot find package") || details.includes("no such built-in module");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class SqliteRuntimeUnavailableError extends Error {
|
|
39
|
+
runtime;
|
|
40
|
+
specifier;
|
|
41
|
+
constructor(runtime, specifier, cause) {
|
|
42
|
+
const requirement = specifier === nodeSpec ? "Requires Node.js >= 24, or Bun with bun:sqlite — this Bun build lacks node:sqlite." : "Requires Bun with bun:sqlite, or Node.js >= 24 — this Bun build lacks bun:sqlite.";
|
|
43
|
+
super(`Magic Context detected ${runtime}, but could not load ${specifier}. ${requirement}`, { cause });
|
|
44
|
+
this.name = "SqliteRuntimeUnavailableError";
|
|
45
|
+
this.runtime = runtime;
|
|
46
|
+
this.specifier = specifier;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = importSqliteModule) {
|
|
50
|
+
const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
|
|
51
|
+
try {
|
|
52
|
+
return await importer(specifier);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (isModuleNotFoundError(error, specifier)) {
|
|
55
|
+
throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
var detectedRuntime = detectSqliteRuntime();
|
|
61
|
+
var isBun = detectedRuntime === "Bun";
|
|
62
|
+
var sqliteModule = await loadSqliteModule(detectedRuntime);
|
|
63
|
+
var DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
|
|
64
|
+
var trackedSqliteConnections = new Map;
|
|
65
|
+
var nextSqliteConnectionSequence = 1;
|
|
66
|
+
function installTransactionRouting(db, readonly) {
|
|
67
|
+
const nativeExec = db.exec.bind(db);
|
|
68
|
+
Object.defineProperty(db, "exec", {
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true,
|
|
71
|
+
value: (sql) => {
|
|
72
|
+
if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
|
|
73
|
+
openWriterTransactions.delete(db);
|
|
74
|
+
if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
|
|
75
|
+
acquireShort(db, () => nativeExec(sql), sql.trim());
|
|
76
|
+
else
|
|
77
|
+
nativeExec(sql);
|
|
78
|
+
return db;
|
|
79
|
+
}
|
|
80
|
+
if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
|
|
81
|
+
return endWriterTransaction(db, () => nativeExec(sql), sql);
|
|
82
|
+
if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
|
|
83
|
+
return withAutocommitTimeout(db, () => nativeExec(sql));
|
|
84
|
+
return nativeExec(sql);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const nativePrepare = db.prepare.bind(db);
|
|
88
|
+
Object.defineProperty(db, "prepare", {
|
|
89
|
+
configurable: true,
|
|
90
|
+
writable: true,
|
|
91
|
+
value: (sql) => {
|
|
92
|
+
const statement = nativePrepare(sql);
|
|
93
|
+
if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
|
|
94
|
+
for (const method of ["run", "get", "all"]) {
|
|
95
|
+
const execute = statement[method].bind(statement);
|
|
96
|
+
Object.defineProperty(statement, method, {
|
|
97
|
+
configurable: true,
|
|
98
|
+
value: (...args) => withAutocommitTimeout(db, () => execute(...args))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return statement;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(db, "transaction", {
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value: (fn) => {
|
|
109
|
+
const make = (mode) => function(...args) {
|
|
110
|
+
const nested = isInTransaction(db);
|
|
111
|
+
const savepoint = "mc_tx_sp";
|
|
112
|
+
db.exec(nested ? `SAVEPOINT ${savepoint}` : `BEGIN ${mode}`);
|
|
113
|
+
try {
|
|
114
|
+
const result = fn.apply(this, args);
|
|
115
|
+
db.exec(nested ? `RELEASE ${savepoint}` : "COMMIT");
|
|
116
|
+
return result;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (isInTransaction(db)) {
|
|
119
|
+
if (nested) {
|
|
120
|
+
db.exec(`ROLLBACK TO ${savepoint}`);
|
|
121
|
+
db.exec(`RELEASE ${savepoint}`);
|
|
122
|
+
} else
|
|
123
|
+
db.exec("ROLLBACK");
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const defaultTransaction = make(readonly ? "DEFERRED" : "IMMEDIATE");
|
|
129
|
+
const variants = {
|
|
130
|
+
default: defaultTransaction,
|
|
131
|
+
deferred: make("DEFERRED"),
|
|
132
|
+
immediate: make("IMMEDIATE"),
|
|
133
|
+
exclusive: make("EXCLUSIVE"),
|
|
134
|
+
database: db
|
|
135
|
+
};
|
|
136
|
+
for (const transaction of [
|
|
137
|
+
variants.default,
|
|
138
|
+
variants.deferred,
|
|
139
|
+
variants.immediate,
|
|
140
|
+
variants.exclusive
|
|
141
|
+
]) {
|
|
142
|
+
Object.assign(transaction, variants);
|
|
143
|
+
}
|
|
144
|
+
return defaultTransaction;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function trackSqliteConnection(db, filename, options) {
|
|
149
|
+
const originalClose = db.close.bind(db);
|
|
150
|
+
const sequence = nextSqliteConnectionSequence++;
|
|
151
|
+
const metadata = {
|
|
152
|
+
sequence,
|
|
153
|
+
filename: typeof filename === "string" ? filename : Buffer.isBuffer(filename) ? "<buffer>" : ":memory:",
|
|
154
|
+
readonly: Boolean(options) && typeof options === "object" && (options.readonly === true || options.readOnly === true)
|
|
155
|
+
};
|
|
156
|
+
installTransactionRouting(db, metadata.readonly);
|
|
157
|
+
Object.defineProperty(db, "close", {
|
|
158
|
+
configurable: true,
|
|
159
|
+
value: (...args) => {
|
|
160
|
+
try {
|
|
161
|
+
return originalClose(...args);
|
|
162
|
+
} finally {
|
|
163
|
+
trackedSqliteConnections.delete(sequence);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
trackedSqliteConnections.set(sequence, {
|
|
168
|
+
...metadata,
|
|
169
|
+
reference: new WeakRef(db)
|
|
170
|
+
});
|
|
171
|
+
const prepare = db.prepare.bind(db);
|
|
172
|
+
Object.defineProperty(db, "prepare", {
|
|
173
|
+
configurable: true,
|
|
174
|
+
writable: true,
|
|
175
|
+
value: (sql) => ownPreparedStatement(db, prepare(sql))
|
|
176
|
+
});
|
|
177
|
+
return db;
|
|
178
|
+
}
|
|
179
|
+
var TrackedDatabase = new Proxy(DatabaseImpl, {
|
|
180
|
+
construct(target, args) {
|
|
181
|
+
const db = Reflect.construct(target, args, target);
|
|
182
|
+
return trackSqliteConnection(db, args[0], args[1]);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
186
|
+
|
|
187
|
+
class NodeSqliteDatabase extends DatabaseSync {
|
|
188
|
+
constructor(filename, options) {
|
|
189
|
+
const translated = { ...options };
|
|
190
|
+
if (options && "readonly" in options) {
|
|
191
|
+
translated.readOnly = options.readonly;
|
|
192
|
+
delete translated.readonly;
|
|
193
|
+
}
|
|
194
|
+
super(typeof filename === "string" ? filename : ":memory:", translated);
|
|
195
|
+
}
|
|
196
|
+
prepare(sql) {
|
|
197
|
+
const stmt = super.prepare(sql);
|
|
198
|
+
for (const method of ["run", "get", "all"]) {
|
|
199
|
+
const original = stmt[method].bind(stmt);
|
|
200
|
+
stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
|
|
201
|
+
}
|
|
202
|
+
return stmt;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return NodeSqliteDatabase;
|
|
206
|
+
}
|
|
207
|
+
var Database = TrackedDatabase;
|
|
208
|
+
function pragmaValue(db, name) {
|
|
209
|
+
const row = db.prepare(`PRAGMA ${name}`).get();
|
|
210
|
+
if (!row)
|
|
211
|
+
return;
|
|
212
|
+
return row[name] ?? Object.values(row)[0];
|
|
213
|
+
}
|
|
214
|
+
function pragmaNumber(db, name) {
|
|
215
|
+
try {
|
|
216
|
+
const value = pragmaValue(db, name);
|
|
217
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
var statementOwners = new WeakMap;
|
|
223
|
+
var FIRST_PRUNE_AT = 256;
|
|
224
|
+
function ownPreparedStatement(db, statement) {
|
|
225
|
+
const finalizable = statement;
|
|
226
|
+
if (typeof finalizable.finalize !== "function")
|
|
227
|
+
return statement;
|
|
228
|
+
let owner = statementOwners.get(db);
|
|
229
|
+
if (!owner) {
|
|
230
|
+
const references = new Set;
|
|
231
|
+
owner = { references, registered: new WeakSet, pruneAt: FIRST_PRUNE_AT };
|
|
232
|
+
statementOwners.set(db, owner);
|
|
233
|
+
const close = db.close.bind(db);
|
|
234
|
+
Object.defineProperty(db, "close", {
|
|
235
|
+
configurable: true,
|
|
236
|
+
writable: true,
|
|
237
|
+
value: (...args) => {
|
|
238
|
+
let failure;
|
|
239
|
+
for (const reference of references) {
|
|
240
|
+
try {
|
|
241
|
+
reference.deref()?.finalize();
|
|
242
|
+
} catch (error) {
|
|
243
|
+
failure ??= error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
references.clear();
|
|
247
|
+
const result = close(...args);
|
|
248
|
+
if (failure)
|
|
249
|
+
throw failure;
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (!owner.registered.has(finalizable)) {
|
|
255
|
+
if (owner.references.size >= owner.pruneAt) {
|
|
256
|
+
for (const reference of owner.references) {
|
|
257
|
+
if (reference.deref() === undefined)
|
|
258
|
+
owner.references.delete(reference);
|
|
259
|
+
}
|
|
260
|
+
owner.pruneAt = Math.max(FIRST_PRUNE_AT, owner.references.size * 2);
|
|
261
|
+
}
|
|
262
|
+
owner.references.add(new WeakRef(finalizable));
|
|
263
|
+
owner.registered.add(finalizable);
|
|
264
|
+
}
|
|
265
|
+
return statement;
|
|
266
|
+
}
|
|
267
|
+
function prepareCachedStatement(db, sql) {
|
|
268
|
+
return ownPreparedStatement(db, db.prepare(sql));
|
|
269
|
+
}
|
|
270
|
+
var privilegeDepth = new WeakMap;
|
|
271
|
+
var transformPassScope = new AsyncLocalStorage;
|
|
272
|
+
var admissionScope = new AsyncLocalStorage;
|
|
273
|
+
var backgroundWriterScope = new AsyncLocalStorage;
|
|
274
|
+
function withSqliteBackgroundWriter(operation) {
|
|
275
|
+
return backgroundWriterScope.run(true, operation);
|
|
276
|
+
}
|
|
277
|
+
function withSqliteTransformPass(operation) {
|
|
278
|
+
const existing = transformPassScope.getStore();
|
|
279
|
+
if (existing?.active)
|
|
280
|
+
return operation();
|
|
281
|
+
const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
|
|
282
|
+
return transformPassScope.run(lease, () => {
|
|
283
|
+
try {
|
|
284
|
+
const result = operation();
|
|
285
|
+
if (result && typeof result.then === "function") {
|
|
286
|
+
return Promise.resolve(result).finally(() => {
|
|
287
|
+
lease.active = false;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
lease.active = false;
|
|
291
|
+
return result;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
lease.active = false;
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function withoutSqliteTransformPass(operation) {
|
|
299
|
+
return transformPassScope.run(undefined, operation);
|
|
300
|
+
}
|
|
301
|
+
function isTransientSqliteError(error) {
|
|
302
|
+
if (!error || typeof error !== "object")
|
|
303
|
+
return false;
|
|
304
|
+
const value = error;
|
|
305
|
+
return typeof value.code === "string" && /^(SQLITE_BUSY|SQLITE_LOCKED)(_|$)/.test(value.code) || typeof value.errcode === "number" && [5, 6].includes(value.errcode & 255);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
class SqliteAcquisitionBusyError extends Error {
|
|
309
|
+
code = "SQLITE_BUSY";
|
|
310
|
+
stage;
|
|
311
|
+
constructor(cause, stage = "BEGIN IMMEDIATE") {
|
|
312
|
+
super("SQLite writer acquisition remained busy", { cause });
|
|
313
|
+
this.name = "SqliteAcquisitionBusyError";
|
|
314
|
+
this.stage = stage;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
var SHORT_BUSY_TIMEOUT_MS = 25;
|
|
318
|
+
var FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250;
|
|
319
|
+
var SLOW_WRITER_LOG_MS = 250;
|
|
320
|
+
var WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
|
|
321
|
+
var writerAcquisitionScope = new AsyncLocalStorage;
|
|
322
|
+
var openWriterTransactions = new WeakMap;
|
|
323
|
+
function foregroundWaitLease() {
|
|
324
|
+
const lease = transformPassScope.getStore();
|
|
325
|
+
return lease?.active && !admissionScope.getStore() && !backgroundWriterScope.getStore() ? lease : undefined;
|
|
326
|
+
}
|
|
327
|
+
function withAutocommitTimeout(db, operation) {
|
|
328
|
+
const lease = foregroundWaitLease();
|
|
329
|
+
if (isInTransaction(db) || !lease && !backgroundWriterScope.getStore())
|
|
330
|
+
return operation();
|
|
331
|
+
const previous = pragmaNumber(db, "busy_timeout");
|
|
332
|
+
db.exec(`PRAGMA busy_timeout=${lease ? Math.floor(lease.remainingWaitMs) : SHORT_BUSY_TIMEOUT_MS}`);
|
|
333
|
+
const started = performance.now();
|
|
334
|
+
try {
|
|
335
|
+
return operation();
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (lease && isTransientSqliteError(error))
|
|
338
|
+
throw new SqliteAcquisitionBusyError(error, "autocommit");
|
|
339
|
+
throw error;
|
|
340
|
+
} finally {
|
|
341
|
+
if (lease)
|
|
342
|
+
lease.remainingWaitMs = Math.max(0, lease.remainingWaitMs - (performance.now() - started));
|
|
343
|
+
if (previous !== null)
|
|
344
|
+
db.exec(`PRAGMA busy_timeout=${previous}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function acquireShort(db, acquire, site) {
|
|
348
|
+
const started = performance.now();
|
|
349
|
+
const previous = pragmaNumber(db, "busy_timeout");
|
|
350
|
+
const outer = writerAcquisitionScope.getStore();
|
|
351
|
+
const owner = outer && !outer.claimed ? outer : undefined;
|
|
352
|
+
if (owner)
|
|
353
|
+
owner.claimed = true;
|
|
354
|
+
const writerSite = owner?.site ?? site;
|
|
355
|
+
const lane = owner?.lane ?? (transformPassScope.getStore()?.active ? "foreground" : "background");
|
|
356
|
+
let acquiredAt;
|
|
357
|
+
try {
|
|
358
|
+
const lease = foregroundWaitLease();
|
|
359
|
+
const timeout = lease ? Math.floor(lease.remainingWaitMs) : SHORT_BUSY_TIMEOUT_MS;
|
|
360
|
+
db.exec(`PRAGMA busy_timeout=${timeout}`);
|
|
361
|
+
acquire();
|
|
362
|
+
acquiredAt = performance.now();
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (transformPassScope.getStore()?.active && isTransientSqliteError(error))
|
|
365
|
+
throw new SqliteAcquisitionBusyError(error, site);
|
|
366
|
+
throw error;
|
|
367
|
+
} finally {
|
|
368
|
+
const attemptMs = (acquiredAt ?? performance.now()) - started;
|
|
369
|
+
const lease = foregroundWaitLease();
|
|
370
|
+
if (lease)
|
|
371
|
+
lease.remainingWaitMs = Math.max(0, lease.remainingWaitMs - attemptMs);
|
|
372
|
+
if (previous !== null)
|
|
373
|
+
db.exec(`PRAGMA busy_timeout=${previous}`);
|
|
374
|
+
if (acquiredAt !== undefined) {
|
|
375
|
+
openWriterTransactions.set(db, {
|
|
376
|
+
site: writerSite,
|
|
377
|
+
lane,
|
|
378
|
+
acquireMs: acquiredAt - (owner?.startedAt ?? started),
|
|
379
|
+
attempts: owner?.attempts ?? 1,
|
|
380
|
+
acquiredAt
|
|
381
|
+
});
|
|
382
|
+
} else if (attemptMs >= SLOW_WRITER_LOG_MS) {
|
|
383
|
+
reportSqliteDiagnostic(`sqlite writer site=${writerSite} lane=${lane} acquire_ms=${Math.round(attemptMs)} attempts=${owner?.attempts ?? 1} outcome=busy`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function endWriterTransaction(db, end, sql) {
|
|
388
|
+
let ended = false;
|
|
389
|
+
try {
|
|
390
|
+
const result = end();
|
|
391
|
+
ended = true;
|
|
392
|
+
return result;
|
|
393
|
+
} finally {
|
|
394
|
+
const open = openWriterTransactions.get(db);
|
|
395
|
+
if (open && !isInTransaction(db)) {
|
|
396
|
+
openWriterTransactions.delete(db);
|
|
397
|
+
const holdMs = performance.now() - open.acquiredAt;
|
|
398
|
+
if (open.acquireMs >= SLOW_WRITER_LOG_MS || holdMs >= SLOW_WRITER_LOG_MS) {
|
|
399
|
+
const outcome = /^\s*ROLLBACK/i.test(sql) ? "rolled_back" : ended ? "committed" : "failed";
|
|
400
|
+
reportSqliteDiagnostic(`sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} attempts=${open.attempts} outcome=${outcome}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function isInTransaction(db) {
|
|
406
|
+
const candidate = db;
|
|
407
|
+
return candidate.inTransaction === true || candidate.isTransaction === true;
|
|
408
|
+
}
|
|
409
|
+
function withPrivilegedWriter(db, operation) {
|
|
410
|
+
const previousDepth = privilegeDepth.get(db) ?? 0;
|
|
411
|
+
const nested = isInTransaction(db);
|
|
412
|
+
const savepoint = "mc_privilege_scope";
|
|
413
|
+
let transactionStartedAt;
|
|
414
|
+
if (nested) {
|
|
415
|
+
db.exec(`SAVEPOINT ${savepoint}`);
|
|
416
|
+
} else {
|
|
417
|
+
try {
|
|
418
|
+
db.exec("BEGIN IMMEDIATE");
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (transformPassScope.getStore()?.active && isTransientSqliteError(error))
|
|
421
|
+
throw new SqliteAcquisitionBusyError(error);
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
transactionStartedAt = performance.now();
|
|
425
|
+
}
|
|
426
|
+
privilegeDepth.set(db, previousDepth + 1);
|
|
427
|
+
try {
|
|
428
|
+
db.prepare("INSERT INTO context_privilege_state(id, enabled) VALUES (1, 1) ON CONFLICT(id) DO UPDATE SET enabled = 1").run();
|
|
429
|
+
const result = operation();
|
|
430
|
+
if (previousDepth === 0) {
|
|
431
|
+
db.prepare("UPDATE context_privilege_state SET enabled = 0 WHERE id = 1").run();
|
|
432
|
+
}
|
|
433
|
+
if (nested) {
|
|
434
|
+
db.exec(`RELEASE ${savepoint}`);
|
|
435
|
+
} else {
|
|
436
|
+
db.exec("COMMIT");
|
|
437
|
+
if (transactionStartedAt !== undefined) {
|
|
438
|
+
reportSlowPrivilegedWrite?.("privileged_writer", transactionStartedAt);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (previousDepth > 0)
|
|
442
|
+
privilegeDepth.set(db, previousDepth);
|
|
443
|
+
else
|
|
444
|
+
privilegeDepth.delete(db);
|
|
445
|
+
return result;
|
|
446
|
+
} catch (error) {
|
|
447
|
+
try {
|
|
448
|
+
if (nested) {
|
|
449
|
+
db.exec(`ROLLBACK TO ${savepoint}`);
|
|
450
|
+
db.exec(`RELEASE ${savepoint}`);
|
|
451
|
+
} else {
|
|
452
|
+
db.exec("ROLLBACK");
|
|
453
|
+
}
|
|
454
|
+
} finally {
|
|
455
|
+
if (previousDepth > 0)
|
|
456
|
+
privilegeDepth.set(db, previousDepth);
|
|
457
|
+
else
|
|
458
|
+
privilegeDepth.delete(db);
|
|
459
|
+
}
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, isTransientSqliteError, withPrivilegedWriter };
|