@retaw106/mmc-pi 2.2.0 → 2.3.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/dist/cli.js +2 -2
- package/dist/config-init.js +2 -2
- package/dist/{index-v6scmxvt.js → index-1p318k54.js} +1 -1
- package/dist/{index-ny303arh.js → index-3qawqvws.js} +266 -32
- package/dist/{index-dx9vhsy8.js → index-4ktngyya.js} +1 -1
- package/dist/{index-54cgx9gm.js → index-83p1tc98.js} +6 -2
- package/dist/{index-j0jjtcwm.js → index-f7z4jc1f.js} +1447 -216
- package/dist/index.js +21969 -17257
- package/dist/{message-time-backfill-a3p9r5rh.js → message-time-backfill-mpks152d.js} +3 -3
- package/dist/{read-session-chunk-ktp56z65.js → read-session-chunk-vhejzv1k.js} +3 -3
- package/dist/{safe-notification-target-hn43s7bc.js → safe-notification-target-5z28m0n9.js} +2 -2
- package/dist/web/main.js +396 -23
- package/package.json +13 -11
package/dist/cli.js
CHANGED
|
@@ -5034,8 +5034,8 @@ var MagicContextConfigSchema = object({
|
|
|
5034
5034
|
object({ default: number2().min(0) }).catchall(number2().min(0))
|
|
5035
5035
|
]).optional().describe('User-only output-token reservation override. Number or per-model object ({ default: 16384, "provider/model": 8192 }); 0 disables reservation. Takes precedence over every derived source: an explicit value here always wins against catalog output limits, provider window-geometry facts, and the 25%-of-context fallback (usable window = context window minus this reserve). When unset, Magic Context reserves the catalog output limit (capped at 25% of context) for shared-window providers and keeps proven separate-quota Google/Gemini windows unchanged.'),
|
|
5036
5036
|
models: object({
|
|
5037
|
-
window_overlay_path:
|
|
5038
|
-
}).optional().describe("
|
|
5037
|
+
window_overlay_path: unknown().optional().describe("Deprecated: ignored. Fusiform no longer publishes window-overlay data, so this setting no longer changes model window geometry. Remove this key.").meta({ deprecated: true })
|
|
5038
|
+
}).optional().describe("Deprecated user-only Fusiform window-overlay settings; ignored."),
|
|
5039
5039
|
toast_duration_ms: number2().min(0).max(60000).default(5000).describe("TUI toast lifetime in milliseconds for Magic Context notifications. Set to 0 to disable Magic Context toasts entirely (min: 0, max: 60000, default: 5000)"),
|
|
5040
5040
|
execute_threshold_percentage: union([
|
|
5041
5041
|
number2().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE),
|
package/dist/config-init.js
CHANGED
|
@@ -5051,8 +5051,8 @@ var MagicContextConfigSchema = object({
|
|
|
5051
5051
|
object({ default: number2().min(0) }).catchall(number2().min(0))
|
|
5052
5052
|
]).optional().describe('User-only output-token reservation override. Number or per-model object ({ default: 16384, "provider/model": 8192 }); 0 disables reservation. Takes precedence over every derived source: an explicit value here always wins against catalog output limits, provider window-geometry facts, and the 25%-of-context fallback (usable window = context window minus this reserve). When unset, Magic Context reserves the catalog output limit (capped at 25% of context) for shared-window providers and keeps proven separate-quota Google/Gemini windows unchanged.'),
|
|
5053
5053
|
models: object({
|
|
5054
|
-
window_overlay_path:
|
|
5055
|
-
}).optional().describe("
|
|
5054
|
+
window_overlay_path: unknown().optional().describe("Deprecated: ignored. Fusiform no longer publishes window-overlay data, so this setting no longer changes model window geometry. Remove this key.").meta({ deprecated: true })
|
|
5055
|
+
}).optional().describe("Deprecated user-only Fusiform window-overlay settings; ignored."),
|
|
5056
5056
|
toast_duration_ms: number2().min(0).max(60000).default(5000).describe("TUI toast lifetime in milliseconds for Magic Context notifications. Set to 0 to disable Magic Context toasts entirely (min: 0, max: 60000, default: 5000)"),
|
|
5057
5057
|
execute_threshold_percentage: union([
|
|
5058
5058
|
number2().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE),
|
|
@@ -5,12 +5,15 @@ function registerSlowWriteReporter(reporter) {
|
|
|
5
5
|
reportSlowPrivilegedWrite = reporter;
|
|
6
6
|
}
|
|
7
7
|
var sqliteDiagnosticSink;
|
|
8
|
-
|
|
8
|
+
var sqliteDiagnosticHost = () => "unknown";
|
|
9
|
+
function registerSqliteDiagnosticSink(sink, host) {
|
|
9
10
|
sqliteDiagnosticSink = sink;
|
|
11
|
+
if (host)
|
|
12
|
+
sqliteDiagnosticHost = host;
|
|
10
13
|
}
|
|
11
14
|
function reportSqliteDiagnostic(message) {
|
|
12
15
|
try {
|
|
13
|
-
sqliteDiagnosticSink?.(`[magic-context] ${message}`);
|
|
16
|
+
sqliteDiagnosticSink?.(`[magic-context] ${message} pid=${process.pid} host=${sqliteDiagnosticHost()}`);
|
|
14
17
|
} catch {}
|
|
15
18
|
}
|
|
16
19
|
function detectSqliteRuntime() {
|
|
@@ -46,10 +49,31 @@ class SqliteRuntimeUnavailableError extends Error {
|
|
|
46
49
|
this.specifier = specifier;
|
|
47
50
|
}
|
|
48
51
|
}
|
|
52
|
+
function isNodeSqliteExperimentalWarning(args) {
|
|
53
|
+
const [warning, options] = args;
|
|
54
|
+
const message = warning instanceof Error ? warning.message : warning;
|
|
55
|
+
const type = typeof options === "string" ? options : options && typeof options === "object" ? options.type : undefined;
|
|
56
|
+
return type === "ExperimentalWarning" && typeof message === "string" && message.startsWith("SQLite is an experimental feature");
|
|
57
|
+
}
|
|
58
|
+
async function importSqliteModuleForRuntime(runtime, specifier, importer) {
|
|
59
|
+
if (runtime !== "Node.js")
|
|
60
|
+
return importer(specifier);
|
|
61
|
+
const originalEmitWarning = process.emitWarning;
|
|
62
|
+
process.emitWarning = function(...args) {
|
|
63
|
+
if (isNodeSqliteExperimentalWarning(args))
|
|
64
|
+
return;
|
|
65
|
+
Reflect.apply(originalEmitWarning, this, args);
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
return await importer(specifier);
|
|
69
|
+
} finally {
|
|
70
|
+
process.emitWarning = originalEmitWarning;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
49
73
|
async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = importSqliteModule) {
|
|
50
74
|
const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
|
|
51
75
|
try {
|
|
52
|
-
return await
|
|
76
|
+
return await importSqliteModuleForRuntime(runtime, specifier, importer);
|
|
53
77
|
} catch (error) {
|
|
54
78
|
if (isModuleNotFoundError(error, specifier)) {
|
|
55
79
|
throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
|
|
@@ -63,6 +87,81 @@ var sqliteModule = await loadSqliteModule(detectedRuntime);
|
|
|
63
87
|
var DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
|
|
64
88
|
var trackedSqliteConnections = new Map;
|
|
65
89
|
var nextSqliteConnectionSequence = 1;
|
|
90
|
+
var TAG_COLUMNS_OUTSIDE_IDENTITY = new Set([
|
|
91
|
+
"status",
|
|
92
|
+
"drop_mode",
|
|
93
|
+
"byte_size",
|
|
94
|
+
"input_byte_size",
|
|
95
|
+
"reasoning_byte_size",
|
|
96
|
+
"token_count",
|
|
97
|
+
"input_token_count",
|
|
98
|
+
"caveman_depth",
|
|
99
|
+
"entry_fingerprint",
|
|
100
|
+
"tool_name",
|
|
101
|
+
"harness"
|
|
102
|
+
]);
|
|
103
|
+
var tagIdentityWriteGeneration = 0;
|
|
104
|
+
function mayChangeTagIdentity(sql) {
|
|
105
|
+
if (!/\btags\b/i.test(sql) || /^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql))
|
|
106
|
+
return false;
|
|
107
|
+
if (/^\s*WITH\b/i.test(sql) && !/\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql))
|
|
108
|
+
return false;
|
|
109
|
+
if (/;\s*\S/.test(sql) || /\bREPLACE\b/i.test(sql) || /\bON\s+CONFLICT\b/i.test(sql))
|
|
110
|
+
return true;
|
|
111
|
+
const insert = /^\s*INSERT\s+INTO\s+(?:main\.)?["`]?tags["`]?\s*\(([^)]*)\)/i.exec(sql);
|
|
112
|
+
if (insert) {
|
|
113
|
+
const columns = (insert[1] ?? "").split(",").map((column) => column.trim().replace(/["`[\]]/g, "").toLowerCase());
|
|
114
|
+
return columns.includes("id") || columns.includes("rowid");
|
|
115
|
+
}
|
|
116
|
+
const update = /^\s*UPDATE\s+(?:main\.)?["`]?tags["`]?\s+SET\s+([\s\S]*?)\bWHERE\b/i.exec(sql);
|
|
117
|
+
if (update && !/\bSELECT\b/i.test(sql)) {
|
|
118
|
+
const assignments = topLevelCommaSegments(update[1] ?? "");
|
|
119
|
+
if (!assignments)
|
|
120
|
+
return true;
|
|
121
|
+
return !assignments.every((assignment) => {
|
|
122
|
+
const column = /^\s*["`]?(\w+)["`]?\s*=(?!=)/.exec(assignment)?.[1];
|
|
123
|
+
return column !== undefined && TAG_COLUMNS_OUTSIDE_IDENTITY.has(column.toLowerCase());
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
function topLevelCommaSegments(text) {
|
|
129
|
+
const segments = [];
|
|
130
|
+
let depth = 0;
|
|
131
|
+
let quote = null;
|
|
132
|
+
let start = 0;
|
|
133
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
134
|
+
const char = text[index];
|
|
135
|
+
if (quote) {
|
|
136
|
+
if (char === quote) {
|
|
137
|
+
if (text[index + 1] === quote)
|
|
138
|
+
index += 1;
|
|
139
|
+
else
|
|
140
|
+
quote = null;
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (char === "'" || char === '"' || char === "`")
|
|
145
|
+
quote = char;
|
|
146
|
+
else if (char === "(")
|
|
147
|
+
depth += 1;
|
|
148
|
+
else if (char === ")") {
|
|
149
|
+
depth -= 1;
|
|
150
|
+
if (depth < 0)
|
|
151
|
+
return null;
|
|
152
|
+
} else if (char === "," && depth === 0) {
|
|
153
|
+
segments.push(text.slice(start, index));
|
|
154
|
+
start = index + 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (quote || depth !== 0)
|
|
158
|
+
return null;
|
|
159
|
+
segments.push(text.slice(start));
|
|
160
|
+
return segments;
|
|
161
|
+
}
|
|
162
|
+
function getTagIdentityWriteGeneration() {
|
|
163
|
+
return tagIdentityWriteGeneration;
|
|
164
|
+
}
|
|
66
165
|
function installTransactionRouting(db, readonly) {
|
|
67
166
|
const nativeExec = db.exec.bind(db);
|
|
68
167
|
Object.defineProperty(db, "exec", {
|
|
@@ -73,16 +172,30 @@ function installTransactionRouting(db, readonly) {
|
|
|
73
172
|
assertTransformWrite();
|
|
74
173
|
if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
|
|
75
174
|
openWriterTransactions.delete(db);
|
|
76
|
-
if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
|
|
175
|
+
if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore()) {
|
|
77
176
|
acquireShort(db, () => nativeExec(sql), sql.trim());
|
|
78
|
-
else
|
|
177
|
+
} else {
|
|
79
178
|
nativeExec(sql);
|
|
179
|
+
openWriterTransactions.set(db, {
|
|
180
|
+
site: sql.trim(),
|
|
181
|
+
lane: "background",
|
|
182
|
+
acquireMs: 0,
|
|
183
|
+
attempts: 1,
|
|
184
|
+
acquiredAt: performance.now()
|
|
185
|
+
});
|
|
186
|
+
}
|
|
80
187
|
return db;
|
|
81
188
|
}
|
|
82
189
|
if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
|
|
83
190
|
return endWriterTransaction(db, () => nativeExec(sql), sql);
|
|
84
|
-
if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
|
|
85
|
-
|
|
191
|
+
if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql)) {
|
|
192
|
+
try {
|
|
193
|
+
return withAutocommitTimeout(db, () => nativeExec(sql));
|
|
194
|
+
} finally {
|
|
195
|
+
if (mayChangeTagIdentity(sql))
|
|
196
|
+
tagIdentityWriteGeneration += 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
86
199
|
return nativeExec(sql);
|
|
87
200
|
}
|
|
88
201
|
});
|
|
@@ -93,13 +206,23 @@ function installTransactionRouting(db, readonly) {
|
|
|
93
206
|
value: (sql) => {
|
|
94
207
|
const statement = nativePrepare(sql);
|
|
95
208
|
if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
|
|
209
|
+
const changesTagIdentity = mayChangeTagIdentity(sql);
|
|
96
210
|
for (const method of ["run", "get", "all"]) {
|
|
97
211
|
const execute = statement[method].bind(statement);
|
|
98
212
|
Object.defineProperty(statement, method, {
|
|
99
213
|
configurable: true,
|
|
100
|
-
value: (...args) =>
|
|
214
|
+
value: (...args) => {
|
|
215
|
+
try {
|
|
216
|
+
return withAutocommitTimeout(db, () => execute(...args));
|
|
217
|
+
} finally {
|
|
218
|
+
if (changesTagIdentity)
|
|
219
|
+
tagIdentityWriteGeneration += 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
101
222
|
});
|
|
102
223
|
}
|
|
224
|
+
if (changesTagIdentity)
|
|
225
|
+
wrapRemainingTagWriteForms(statement);
|
|
103
226
|
}
|
|
104
227
|
return statement;
|
|
105
228
|
}
|
|
@@ -147,6 +270,35 @@ function installTransactionRouting(db, readonly) {
|
|
|
147
270
|
}
|
|
148
271
|
});
|
|
149
272
|
}
|
|
273
|
+
function wrapRemainingTagWriteForms(statement) {
|
|
274
|
+
if (typeof statement.iterate === "function") {
|
|
275
|
+
const iterate = statement.iterate.bind(statement);
|
|
276
|
+
Object.defineProperty(statement, "iterate", {
|
|
277
|
+
configurable: true,
|
|
278
|
+
value: function* (...args) {
|
|
279
|
+
tagIdentityWriteGeneration += 1;
|
|
280
|
+
try {
|
|
281
|
+
yield* iterate(...args);
|
|
282
|
+
} finally {
|
|
283
|
+
tagIdentityWriteGeneration += 1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (typeof statement.values === "function") {
|
|
289
|
+
const values = statement.values.bind(statement);
|
|
290
|
+
Object.defineProperty(statement, "values", {
|
|
291
|
+
configurable: true,
|
|
292
|
+
value: (...args) => {
|
|
293
|
+
try {
|
|
294
|
+
return values(...args);
|
|
295
|
+
} finally {
|
|
296
|
+
tagIdentityWriteGeneration += 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
150
302
|
function trackSqliteConnection(db, filename, options) {
|
|
151
303
|
const originalClose = db.close.bind(db);
|
|
152
304
|
const sequence = nextSqliteConnectionSequence++;
|
|
@@ -207,6 +359,18 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
207
359
|
return NodeSqliteDatabase;
|
|
208
360
|
}
|
|
209
361
|
var Database = TrackedDatabase;
|
|
362
|
+
function getSqliteDatabasePath(db) {
|
|
363
|
+
for (const connection of trackedSqliteConnections.values()) {
|
|
364
|
+
if (connection.reference.deref() === db) {
|
|
365
|
+
return connection.filename === ":memory:" || connection.filename === "<buffer>" ? null : connection.filename;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const native = db;
|
|
369
|
+
const path = native.filename ?? native.name;
|
|
370
|
+
if (typeof path === "string" && path !== ":memory:" && path !== "")
|
|
371
|
+
return path;
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
210
374
|
function pragmaValue(db, name) {
|
|
211
375
|
const row = db.prepare(`PRAGMA ${name}`).get();
|
|
212
376
|
if (!row)
|
|
@@ -289,22 +453,29 @@ function withSqliteTransformPass(operation) {
|
|
|
289
453
|
const existing = transformPassScope.getStore();
|
|
290
454
|
if (existing?.active)
|
|
291
455
|
return operation();
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
456
|
+
const reservation = reserveForegroundSqliteTurn();
|
|
457
|
+
const run = (release) => {
|
|
458
|
+
const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
|
|
459
|
+
return transformPassScope.run(lease, () => {
|
|
460
|
+
try {
|
|
461
|
+
const result = operation();
|
|
462
|
+
if (result && typeof result.then === "function") {
|
|
463
|
+
return Promise.resolve(result).finally(() => {
|
|
464
|
+
lease.active = false;
|
|
465
|
+
release();
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
lease.active = false;
|
|
469
|
+
release();
|
|
470
|
+
return result;
|
|
471
|
+
} catch (error) {
|
|
472
|
+
lease.active = false;
|
|
473
|
+
release();
|
|
474
|
+
throw error;
|
|
300
475
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
lease.active = false;
|
|
305
|
-
throw error;
|
|
306
|
-
}
|
|
307
|
-
});
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
return typeof reservation === "function" ? run(reservation) : reservation.then(run);
|
|
308
479
|
}
|
|
309
480
|
function withoutSqliteTransformPass(operation) {
|
|
310
481
|
return transformPassScope.run(undefined, operation);
|
|
@@ -335,9 +506,9 @@ class SqliteAcquisitionBusyError extends Error {
|
|
|
335
506
|
}
|
|
336
507
|
}
|
|
337
508
|
var SHORT_BUSY_TIMEOUT_MS = 25;
|
|
338
|
-
var FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS =
|
|
509
|
+
var FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 2000;
|
|
339
510
|
var FOREGROUND_ACQUISITION_BUDGET_MS = 16500;
|
|
340
|
-
var SLOW_WRITER_LOG_MS =
|
|
511
|
+
var SLOW_WRITER_LOG_MS = 100;
|
|
341
512
|
var WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
|
|
342
513
|
var writerAcquisitionScope = new AsyncLocalStorage;
|
|
343
514
|
var openWriterTransactions = new WeakMap;
|
|
@@ -348,22 +519,30 @@ function foregroundWaitLease() {
|
|
|
348
519
|
function withAutocommitTimeout(db, operation) {
|
|
349
520
|
assertTransformWrite();
|
|
350
521
|
const lease = foregroundWaitLease();
|
|
351
|
-
if (isInTransaction(db)
|
|
522
|
+
if (isInTransaction(db))
|
|
352
523
|
return operation();
|
|
353
|
-
const
|
|
354
|
-
|
|
524
|
+
const scoped = lease || backgroundWriterScope.getStore();
|
|
525
|
+
const previous = scoped ? pragmaNumber(db, "busy_timeout") : null;
|
|
526
|
+
if (scoped)
|
|
527
|
+
db.exec(`PRAGMA busy_timeout=${lease ? Math.floor(Math.min(lease.remainingWaitMs, lease.guard?.remainingMs() ?? Infinity)) : SHORT_BUSY_TIMEOUT_MS}`);
|
|
355
528
|
const started = performance.now();
|
|
529
|
+
let completed = false;
|
|
356
530
|
try {
|
|
357
|
-
|
|
531
|
+
const result = operation();
|
|
532
|
+
completed = true;
|
|
533
|
+
return result;
|
|
358
534
|
} catch (error) {
|
|
359
535
|
if (lease && isTransientSqliteError(error))
|
|
360
536
|
throw new SqliteAcquisitionBusyError(error, "autocommit");
|
|
361
537
|
throw error;
|
|
362
538
|
} finally {
|
|
539
|
+
const elapsed = performance.now() - started;
|
|
363
540
|
if (lease)
|
|
364
|
-
lease.remainingWaitMs = Math.max(0, lease.remainingWaitMs -
|
|
541
|
+
lease.remainingWaitMs = Math.max(0, lease.remainingWaitMs - elapsed);
|
|
365
542
|
if (previous !== null)
|
|
366
543
|
db.exec(`PRAGMA busy_timeout=${previous}`);
|
|
544
|
+
if (elapsed >= SLOW_WRITER_LOG_MS)
|
|
545
|
+
reportSqliteDiagnostic(`sqlite writer site=autocommit lane=${lease ? "foreground" : "background"} elapsed_ms=${Math.round(elapsed)} timing=statement_including_acquisition outcome=${completed ? "completed" : "failed"} caller=${writerCaller()}`);
|
|
367
546
|
}
|
|
368
547
|
}
|
|
369
548
|
function acquireShort(db, acquire, site) {
|
|
@@ -420,11 +599,15 @@ function endWriterTransaction(db, end, sql) {
|
|
|
420
599
|
const holdMs = performance.now() - open.acquiredAt;
|
|
421
600
|
if (open.acquireMs >= SLOW_WRITER_LOG_MS || holdMs >= SLOW_WRITER_LOG_MS) {
|
|
422
601
|
const outcome = /^\s*ROLLBACK/i.test(sql) ? "rolled_back" : ended ? "committed" : "failed";
|
|
423
|
-
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}`);
|
|
602
|
+
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} timing=through_commit_upper_bound caller=${writerCaller()}`);
|
|
424
603
|
}
|
|
425
604
|
}
|
|
426
605
|
}
|
|
427
606
|
}
|
|
607
|
+
function writerCaller() {
|
|
608
|
+
return new Error().stack?.split(`
|
|
609
|
+
`).find((line) => line.includes(" at ") && !/[/\\]sqlite\.[tj]s[:)]/.test(line) && !/\b(?:writerCaller|endWriterTransaction|withAutocommitTimeout|value)\b/.test(line))?.trim().replace(/\s+/g, "_") ?? "unknown";
|
|
610
|
+
}
|
|
428
611
|
async function withAsyncPrivilegedWriter(db, operation, options = {}) {
|
|
429
612
|
const started = performance.now();
|
|
430
613
|
const budgetMs = Math.max(0, Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity));
|
|
@@ -533,5 +716,56 @@ function withPrivilegedWriter(db, operation) {
|
|
|
533
716
|
throw error;
|
|
534
717
|
}
|
|
535
718
|
}
|
|
719
|
+
var indexCoordinationKey = Symbol.for("magic-context.index-foreground-coordination.v1");
|
|
720
|
+
var indexCoordinationHeap = globalThis;
|
|
721
|
+
var indexCoordinationBuffer = indexCoordinationHeap[indexCoordinationKey] ?? new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT);
|
|
722
|
+
indexCoordinationHeap[indexCoordinationKey] = indexCoordinationBuffer;
|
|
723
|
+
var indexCoordinationState = new Int32Array(indexCoordinationBuffer);
|
|
724
|
+
function indexCoordinationInput() {
|
|
725
|
+
return {
|
|
726
|
+
indexCoordination: indexCoordinationBuffer,
|
|
727
|
+
indexOwner: Atomics.add(indexCoordinationState, 2, 1) + 1
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
var yieldIndexTurn = () => new Promise((resolve) => setTimeout(resolve, 1));
|
|
731
|
+
function reserveForegroundSqliteTurn() {
|
|
732
|
+
Atomics.add(indexCoordinationState, 0, 1);
|
|
733
|
+
const release = () => {
|
|
734
|
+
Atomics.sub(indexCoordinationState, 0, 1);
|
|
735
|
+
Atomics.notify(indexCoordinationState, 0);
|
|
736
|
+
};
|
|
737
|
+
if (Atomics.load(indexCoordinationState, 1) === 0)
|
|
738
|
+
return release;
|
|
739
|
+
return (async () => {
|
|
740
|
+
while (Atomics.load(indexCoordinationState, 1) !== 0)
|
|
741
|
+
await yieldIndexTurn();
|
|
742
|
+
return release;
|
|
743
|
+
})();
|
|
744
|
+
}
|
|
745
|
+
function releaseIndexOwner(input) {
|
|
746
|
+
if (!input.indexCoordination || !input.indexOwner)
|
|
747
|
+
return;
|
|
748
|
+
const shared = new Int32Array(input.indexCoordination);
|
|
749
|
+
Atomics.compareExchange(shared, 1, input.indexOwner, 0);
|
|
750
|
+
Atomics.notify(shared, 1);
|
|
751
|
+
}
|
|
752
|
+
async function withIndexChunk(input, operation) {
|
|
753
|
+
if (!input.indexCoordination || !input.indexOwner)
|
|
754
|
+
return operation();
|
|
755
|
+
const shared = new Int32Array(input.indexCoordination);
|
|
756
|
+
for (;; ) {
|
|
757
|
+
if (Atomics.load(shared, 0) === 0 && Atomics.compareExchange(shared, 1, 0, input.indexOwner) === 0) {
|
|
758
|
+
if (Atomics.load(shared, 0) === 0)
|
|
759
|
+
break;
|
|
760
|
+
releaseIndexOwner(input);
|
|
761
|
+
}
|
|
762
|
+
await yieldIndexTurn();
|
|
763
|
+
}
|
|
764
|
+
try {
|
|
765
|
+
return await operation();
|
|
766
|
+
} finally {
|
|
767
|
+
releaseIndexOwner(input);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
536
770
|
|
|
537
|
-
export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter };
|
|
771
|
+
export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, getTagIdentityWriteGeneration, Database, getSqliteDatabasePath, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter, indexCoordinationInput, withIndexChunk };
|
|
@@ -431,8 +431,12 @@ function flush() {
|
|
|
431
431
|
let currentSize = getCurrentLogSize(logFile);
|
|
432
432
|
const dataSize = Buffer.byteLength(data);
|
|
433
433
|
if (currentSize > 0 && currentSize + dataSize > MAX_LOG_FILE_BYTES) {
|
|
434
|
-
|
|
435
|
-
currentSize =
|
|
434
|
+
flushesSinceSizeCheck = SIZE_CHECK_INTERVAL_FLUSHES;
|
|
435
|
+
currentSize = getCurrentLogSize(logFile);
|
|
436
|
+
if (currentSize > 0 && currentSize + dataSize > MAX_LOG_FILE_BYTES) {
|
|
437
|
+
rotateLogFile(logFile, currentSize);
|
|
438
|
+
currentSize = 0;
|
|
439
|
+
}
|
|
436
440
|
}
|
|
437
441
|
fs.appendFileSync(logFile, data, { encoding: "utf8", mode: 384 });
|
|
438
442
|
buffer = [];
|