@retaw106/mmc-pi 2.4.0 → 2.6.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.
@@ -166,6 +166,177 @@ function topLevelCommaSegments(text) {
166
166
  function getTagIdentityWriteGeneration() {
167
167
  return tagIdentityWriteGeneration;
168
168
  }
169
+ var busyTimeoutStates = new WeakMap;
170
+ var BUSY_TIMEOUT_QUERY = /^\s*PRAGMA\s+(?:(?:\w+|"[^"]+"|`[^`]+`|\[[^\]]+\])\s*\.\s*)?(?:busy_timeout|"busy_timeout"|`busy_timeout`|\[busy_timeout\])\s*;?\s*$/i;
171
+ var BUSY_TIMEOUT_SETTER = /^\s*PRAGMA\s+(?:(?:\w+|"[^"]+"|`[^`]+`|\[[^\]]+\])\s*\.\s*)?(?:busy_timeout|"busy_timeout"|`busy_timeout`|\[busy_timeout\])\s*(?==|\()/i;
172
+ function literalBusyTimeout(sql) {
173
+ const head = BUSY_TIMEOUT_SETTER.exec(sql);
174
+ if (!head)
175
+ return;
176
+ const match = /^(?:=\s*|\(\s*)(?:'(-?\d+)'|"(-?\d+)"|(-?\d+))\s*\)?\s*;?\s*$/.exec(sql.slice(head[0].length));
177
+ if (!match)
178
+ return;
179
+ const value = Number(match[1] ?? match[2] ?? match[3]);
180
+ return value < 0 ? 0 : value <= 2147483647 ? value : undefined;
181
+ }
182
+ function busyTimeoutStatements(sql) {
183
+ const statements = [];
184
+ let statement = "";
185
+ let quote = "";
186
+ for (let i = 0;i < sql.length; i++) {
187
+ const char = sql[i];
188
+ if (quote) {
189
+ statement += char;
190
+ if (char === quote) {
191
+ if (sql[i + 1] === quote)
192
+ statement += sql[++i];
193
+ else
194
+ quote = "";
195
+ }
196
+ } else if (char === "'" || char === '"' || char === "`" || char === "[") {
197
+ quote = char === "[" ? "]" : char;
198
+ statement += char;
199
+ } else if (char === "-" && sql[i + 1] === "-") {
200
+ while (i + 1 < sql.length && sql[i + 1] !== `
201
+ `)
202
+ i++;
203
+ statement += " ";
204
+ } else if (char === "/" && sql[i + 1] === "*") {
205
+ i += 2;
206
+ while (i < sql.length && !(sql[i] === "*" && sql[i + 1] === "/"))
207
+ i++;
208
+ i++;
209
+ statement += " ";
210
+ } else if (char === ";") {
211
+ statements.push(statement);
212
+ statement = "";
213
+ } else
214
+ statement += char;
215
+ }
216
+ statements.push(statement);
217
+ return statements.filter((part) => part.trim().length > 0);
218
+ }
219
+ function installNativeDeadlineRouting(db) {
220
+ const rawExec = db.exec.bind(db);
221
+ const rawPrepare = db.prepare.bind(db);
222
+ const readTimeout = () => {
223
+ const statement = rawPrepare("PRAGMA busy_timeout");
224
+ try {
225
+ return Object.values(statement.get())[0];
226
+ } finally {
227
+ statement.finalize?.();
228
+ }
229
+ };
230
+ const timeoutState = { current: readTimeout() };
231
+ busyTimeoutStates.set(db, timeoutState);
232
+ const setBusyTimeout = (timeout) => {
233
+ rawExec(`PRAGMA busy_timeout=${timeout}`);
234
+ timeoutState.current = timeout;
235
+ };
236
+ const withinDeadline = (operation) => {
237
+ const deadline = nativeWaitScope?.getStore();
238
+ if (!deadline?.active || !deadline.remainingMs)
239
+ return operation();
240
+ const remaining = deadline.remainingMs();
241
+ if (remaining === Infinity)
242
+ return operation();
243
+ const previous = timeoutState.current;
244
+ const timeout = Math.min(previous, Math.max(0, Math.floor(remaining)));
245
+ if (timeout === previous)
246
+ return operation();
247
+ setBusyTimeout(timeout);
248
+ try {
249
+ return operation();
250
+ } finally {
251
+ setBusyTimeout(previous);
252
+ }
253
+ };
254
+ Object.defineProperty(db, "exec", {
255
+ configurable: true,
256
+ writable: true,
257
+ value: (sql) => {
258
+ const parts = /\bbusy_timeout\b/i.test(sql) ? busyTimeoutStatements(sql) : [];
259
+ if (parts.length === 1 && BUSY_TIMEOUT_QUERY.test(parts[0]))
260
+ return rawExec(sql);
261
+ const singleSetter = parts.length === 1 && BUSY_TIMEOUT_SETTER.test(parts[0]);
262
+ const setting = singleSetter ? literalBusyTimeout(parts[0]) : undefined;
263
+ if (singleSetter) {
264
+ const result = rawExec(sql);
265
+ timeoutState.current = setting ?? readTimeout();
266
+ return result;
267
+ }
268
+ if (!/\bbusy_timeout\b/i.test(sql))
269
+ return withinDeadline(() => rawExec(sql));
270
+ const setters = parts.filter((part) => BUSY_TIMEOUT_SETTER.test(part));
271
+ return withinDeadline(() => {
272
+ try {
273
+ const result = rawExec(sql);
274
+ if (setters.length)
275
+ timeoutState.current = literalBusyTimeout(setters[setters.length - 1]) ?? readTimeout();
276
+ return result;
277
+ } catch (error) {
278
+ if (setters.length)
279
+ timeoutState.current = readTimeout();
280
+ throw error;
281
+ }
282
+ });
283
+ }
284
+ });
285
+ const nativePrepare = (sql) => BUSY_TIMEOUT_QUERY.test(sql) || BUSY_TIMEOUT_SETTER.test(sql) ? rawPrepare(sql) : withinDeadline(() => rawPrepare(sql));
286
+ Object.defineProperty(db, "prepare", {
287
+ configurable: true,
288
+ writable: true,
289
+ value: (sql) => {
290
+ const statement = nativePrepare(sql);
291
+ const normalized = /\bbusy_timeout\b/i.test(sql) ? busyTimeoutStatements(sql)[0] ?? sql : sql;
292
+ const setting = literalBusyTimeout(normalized);
293
+ const timeoutSetter = BUSY_TIMEOUT_SETTER.test(normalized);
294
+ const timeoutQuery = BUSY_TIMEOUT_QUERY.test(normalized);
295
+ if (timeoutSetter)
296
+ timeoutState.current = setting ?? readTimeout();
297
+ for (const method of ["run", "get", "all", "values"]) {
298
+ const execute = statement[method]?.bind(statement);
299
+ if (!execute)
300
+ continue;
301
+ Object.defineProperty(statement, method, {
302
+ configurable: true,
303
+ value: (...args) => {
304
+ if (timeoutQuery)
305
+ return execute(...args);
306
+ if (!timeoutSetter)
307
+ return withinDeadline(() => execute(...args));
308
+ const result = execute(...args);
309
+ timeoutState.current = readTimeout();
310
+ return result;
311
+ }
312
+ });
313
+ }
314
+ if (typeof statement.iterate === "function") {
315
+ const iterate = statement.iterate.bind(statement);
316
+ Object.defineProperty(statement, "iterate", {
317
+ configurable: true,
318
+ value: (...args) => {
319
+ const iterator = !timeoutSetter && !timeoutQuery ? withinDeadline(() => iterate(...args)) : iterate(...args);
320
+ const next = iterator.next.bind(iterator);
321
+ if (timeoutSetter)
322
+ timeoutState.current = readTimeout();
323
+ iterator.next = () => {
324
+ if (timeoutQuery)
325
+ return next();
326
+ if (!timeoutSetter)
327
+ return withinDeadline(next);
328
+ const result = next();
329
+ timeoutState.current = readTimeout();
330
+ return result;
331
+ };
332
+ return iterator;
333
+ }
334
+ });
335
+ }
336
+ return statement;
337
+ }
338
+ });
339
+ }
169
340
  function installTransactionRouting(db, readonly) {
170
341
  const nativeExec = db.exec.bind(db);
171
342
  Object.defineProperty(db, "exec", {
@@ -303,7 +474,7 @@ function wrapRemainingTagWriteForms(statement) {
303
474
  });
304
475
  }
305
476
  }
306
- function trackSqliteConnection(db, filename, options) {
477
+ function trackSqliteConnection(db, filename, options, turnDeadlines = false) {
307
478
  const originalClose = db.close.bind(db);
308
479
  const sequence = nextSqliteConnectionSequence++;
309
480
  const metadata = {
@@ -311,6 +482,8 @@ function trackSqliteConnection(db, filename, options) {
311
482
  filename: typeof filename === "string" ? filename : Buffer.isBuffer(filename) ? "<buffer>" : ":memory:",
312
483
  readonly: Boolean(options) && typeof options === "object" && (options.readonly === true || options.readOnly === true)
313
484
  };
485
+ if (turnDeadlines)
486
+ installNativeDeadlineRouting(db);
314
487
  installTransactionRouting(db, metadata.readonly);
315
488
  Object.defineProperty(db, "close", {
316
489
  configurable: true,
@@ -363,6 +536,12 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
363
536
  return NodeSqliteDatabase;
364
537
  }
365
538
  var Database = TrackedDatabase;
539
+ function createSqliteTurnDatabase(filename, options) {
540
+ return trackSqliteConnection(new DatabaseImpl(filename, options), filename, options, true);
541
+ }
542
+ function hasSqliteTurnDeadlineRouting(db) {
543
+ return busyTimeoutStates.has(db);
544
+ }
366
545
  function getSqliteDatabasePath(db) {
367
546
  for (const connection of trackedSqliteConnections.values()) {
368
547
  if (connection.reference.deref() === db) {
@@ -382,6 +561,11 @@ function pragmaValue(db, name) {
382
561
  return row[name] ?? Object.values(row)[0];
383
562
  }
384
563
  function pragmaNumber(db, name) {
564
+ if (name === "busy_timeout") {
565
+ const state = busyTimeoutStates.get(db);
566
+ if (state)
567
+ return state.current;
568
+ }
385
569
  try {
386
570
  const value = pragmaValue(db, name);
387
571
  return typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -447,11 +631,34 @@ function prepareCachedStatement(db, sql) {
447
631
  return ownPreparedStatement(db, db.prepare(sql));
448
632
  }
449
633
  var privilegeDepth = new WeakMap;
634
+ var nativeWaitScope;
635
+ function releaseNativeWaitLease(lease) {
636
+ lease.active = false;
637
+ lease.remainingMs = undefined;
638
+ }
639
+ function withSqliteTurnDeadline(remainingMs, operation) {
640
+ const scope = nativeWaitScope ??= new AsyncLocalStorage;
641
+ const lease = { active: true, remainingMs };
642
+ return scope.run(lease, () => {
643
+ try {
644
+ const result = operation();
645
+ if (result && typeof result.then === "function")
646
+ return Promise.resolve(result).finally(releaseNativeWaitLease.bind(null, lease));
647
+ releaseNativeWaitLease(lease);
648
+ return result;
649
+ } catch (error) {
650
+ releaseNativeWaitLease(lease);
651
+ throw error;
652
+ }
653
+ });
654
+ }
450
655
  var transformPassScope = new AsyncLocalStorage;
451
656
  var admissionScope = new AsyncLocalStorage;
452
657
  var backgroundWriterScope = new AsyncLocalStorage;
453
658
  function withSqliteBackgroundWriter(operation) {
454
- return backgroundWriterScope.run(true, operation);
659
+ if (!nativeWaitScope?.getStore())
660
+ return backgroundWriterScope.run(true, operation);
661
+ return nativeWaitScope.run(undefined, () => backgroundWriterScope.run(true, operation));
455
662
  }
456
663
  function withSqliteTransformPass(operation) {
457
664
  const existing = transformPassScope.getStore();
@@ -482,7 +689,9 @@ function withSqliteTransformPass(operation) {
482
689
  return typeof reservation === "function" ? run(reservation) : reservation.then(run);
483
690
  }
484
691
  function withoutSqliteTransformPass(operation) {
485
- return transformPassScope.run(undefined, operation);
692
+ if (!nativeWaitScope?.getStore())
693
+ return transformPassScope.run(undefined, operation);
694
+ return nativeWaitScope.run(undefined, () => transformPassScope.run(undefined, operation));
486
695
  }
487
696
  function guardSqliteTransformPass(guard) {
488
697
  const lease = transformPassScope.getStore();
@@ -656,7 +865,7 @@ async function withAsyncPrivilegedWriter(db, operation, options = {}) {
656
865
  } catch (error) {
657
866
  if (entered || !(error instanceof SqliteAcquisitionBusyError))
658
867
  throw error;
659
- options.beforeRetry?.(error);
868
+ await options.beforeRetry?.(error);
660
869
  const elapsed = performance.now() - started;
661
870
  if (elapsed >= budgetMs) {
662
871
  reportSqliteDiagnostic(`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`);
@@ -791,4 +1000,4 @@ async function withIndexChunk(input, operation) {
791
1000
  }
792
1001
  }
793
1002
 
794
- export { inRootAsyncContext, registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, getTagIdentityWriteGeneration, Database, getSqliteDatabasePath, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter, indexCoordinationInput, withIndexChunk };
1003
+ export { inRootAsyncContext, registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, getTagIdentityWriteGeneration, Database, createSqliteTurnDatabase, hasSqliteTurnDeadlineRouting, getSqliteDatabasePath, prepareCachedStatement, withSqliteTurnDeadline, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter, indexCoordinationInput, withIndexChunk };