@absurd-sqlite/sdk 0.2.0 → 0.2.1-alpha.1

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.
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.downloadExtension = downloadExtension;
4
+ exports.resolveExtensionPath = resolveExtensionPath;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const node_os_1 = require("node:os");
8
+ const node_crypto_1 = require("node:crypto");
9
+ function getPlatformInfo() {
10
+ const platform = process.platform;
11
+ const arch = process.arch;
12
+ let os;
13
+ let ext;
14
+ switch (platform) {
15
+ case "darwin":
16
+ os = "macOS";
17
+ ext = "dylib";
18
+ break;
19
+ case "linux":
20
+ os = "Linux";
21
+ ext = "so";
22
+ break;
23
+ case "win32":
24
+ os = "Windows";
25
+ ext = "dll";
26
+ break;
27
+ default:
28
+ throw new Error(`Unsupported platform: ${platform}`);
29
+ }
30
+ let archStr;
31
+ switch (arch) {
32
+ case "x64":
33
+ archStr = "X64";
34
+ break;
35
+ case "arm64":
36
+ archStr = "ARM64";
37
+ break;
38
+ default:
39
+ throw new Error(`Unsupported architecture: ${arch}`);
40
+ }
41
+ return { os, arch: archStr, ext };
42
+ }
43
+ function getDefaultCacheDir() {
44
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), ".cache", "absurd-sqlite", "extensions");
45
+ }
46
+ function getAssetName(version, platform) {
47
+ // Format: absurd-absurd-sqlite-extension-vX.Y.Z-{OS}-{ARCH}.{ext}
48
+ return `absurd-absurd-sqlite-extension-${version}-${platform.os}-${platform.arch}.${platform.ext}`;
49
+ }
50
+ function getTag(version) {
51
+ return `absurd-sqlite-extension/${version}`;
52
+ }
53
+ async function fetchLatestVersion(owner, repo) {
54
+ const url = `https://api.github.com/repos/${owner}/${repo}/releases`;
55
+ const response = await fetch(url);
56
+ if (!response.ok) {
57
+ throw new Error(`Failed to fetch releases: ${response.status} ${response.statusText}`);
58
+ }
59
+ const releases = (await response.json());
60
+ // Find the latest non-draft extension release
61
+ for (const release of releases) {
62
+ if (!release.draft &&
63
+ release.tag_name.startsWith("absurd-sqlite-extension/")) {
64
+ // Extract version from tag (e.g., "absurd-sqlite-extension/v0.1.0-alpha.3" -> "v0.1.0-alpha.3")
65
+ return release.tag_name.replace("absurd-sqlite-extension/", "");
66
+ }
67
+ }
68
+ throw new Error("No extension releases found");
69
+ }
70
+ async function downloadAsset(owner, repo, tag, assetName, destPath, expectedChecksum) {
71
+ const url = `https://github.com/${owner}/${repo}/releases/download/${tag}/${assetName}`;
72
+ const response = await fetch(url);
73
+ if (!response.ok) {
74
+ throw new Error(`Failed to download extension: ${response.status} ${response.statusText} from ${url}`);
75
+ }
76
+ const buffer = await response.arrayBuffer();
77
+ (0, node_fs_1.writeFileSync)(destPath, Buffer.from(buffer));
78
+ // Verify checksum if provided
79
+ if (expectedChecksum) {
80
+ const actualChecksum = calculateChecksum(destPath);
81
+ if (actualChecksum !== expectedChecksum.toLowerCase()) {
82
+ throw new Error(`Checksum verification failed. Expected: ${expectedChecksum.toLowerCase()}, Got: ${actualChecksum}`);
83
+ }
84
+ }
85
+ // Make the extension executable on Unix-like systems
86
+ if (process.platform !== "win32") {
87
+ (0, node_fs_1.chmodSync)(destPath, 0o755);
88
+ }
89
+ }
90
+ function calculateChecksum(filePath) {
91
+ const fileBuffer = (0, node_fs_1.readFileSync)(filePath);
92
+ const hash = (0, node_crypto_1.createHash)("sha256");
93
+ hash.update(fileBuffer);
94
+ return hash.digest("hex");
95
+ }
96
+ function getCachedPath(cacheDir, version, platform) {
97
+ // Use a consistent name that SQLite expects: libabsurd.{ext}
98
+ const ext = platform.ext;
99
+ return (0, node_path_1.join)(cacheDir, version, `libabsurd.${ext}`);
100
+ }
101
+ /**
102
+ * Downloads the absurd-sqlite extension from GitHub releases.
103
+ * Returns the path to the downloaded extension file.
104
+ *
105
+ * @param options - Download options
106
+ * @returns Path to the extension file
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * import { downloadExtension } from "@absurd-sqlite/sdk";
111
+ *
112
+ * // Download latest version
113
+ * const extensionPath = await downloadExtension();
114
+ *
115
+ * // Download specific version
116
+ * const extensionPath = await downloadExtension({ version: "v0.1.0-alpha.3" });
117
+ *
118
+ * // Download with checksum verification
119
+ * const extensionPath = await downloadExtension({
120
+ * version: "v0.1.0-alpha.3",
121
+ * expectedChecksum: "abc123..."
122
+ * });
123
+ * ```
124
+ */
125
+ async function downloadExtension(options = {}) {
126
+ const owner = options.owner ?? "b4fun";
127
+ const repo = options.repo ?? "absurd-sqlite";
128
+ const cacheDir = options.cacheDir ?? getDefaultCacheDir();
129
+ const force = options.force ?? false;
130
+ // Resolve version
131
+ let version = options.version ?? "latest";
132
+ if (version === "latest") {
133
+ version = await fetchLatestVersion(owner, repo);
134
+ }
135
+ // Get platform info
136
+ const platform = getPlatformInfo();
137
+ const assetName = getAssetName(version, platform);
138
+ const tag = getTag(version);
139
+ // Check cache
140
+ const cachedPath = getCachedPath(cacheDir, version, platform);
141
+ if (!force && (0, node_fs_1.existsSync)(cachedPath)) {
142
+ // If checksum is provided, verify cached file
143
+ if (options.expectedChecksum) {
144
+ const actualChecksum = calculateChecksum(cachedPath);
145
+ if (actualChecksum !== options.expectedChecksum.toLowerCase()) {
146
+ throw new Error(`Cached file checksum verification failed. Expected: ${options.expectedChecksum.toLowerCase()}, Got: ${actualChecksum}`);
147
+ }
148
+ }
149
+ return cachedPath;
150
+ }
151
+ // Ensure cache directory exists
152
+ const versionDir = (0, node_path_1.join)(cacheDir, version);
153
+ (0, node_fs_1.mkdirSync)(versionDir, { recursive: true });
154
+ // Download asset
155
+ await downloadAsset(owner, repo, tag, assetName, cachedPath, options.expectedChecksum);
156
+ return cachedPath;
157
+ }
158
+ /**
159
+ * Resolves the extension path, either from the provided path, environment variable,
160
+ * or by downloading from GitHub releases.
161
+ *
162
+ * @param extensionPath - Optional path to the extension file
163
+ * @param downloadOptions - Options for downloading if no path is provided
164
+ * @returns Path to the extension file
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * import { resolveExtensionPath } from "@absurd-sqlite/sdk";
169
+ *
170
+ * // Use provided path
171
+ * const path1 = await resolveExtensionPath("/path/to/extension.so");
172
+ *
173
+ * // Use environment variable or download
174
+ * const path2 = await resolveExtensionPath();
175
+ * ```
176
+ */
177
+ async function resolveExtensionPath(extensionPath, downloadOptions) {
178
+ // If path is provided, use it
179
+ if (extensionPath) {
180
+ return extensionPath;
181
+ }
182
+ // Try environment variable
183
+ const envPath = process.env.ABSURD_SQLITE_EXTENSION_PATH;
184
+ if (envPath) {
185
+ return envPath;
186
+ }
187
+ // Download from GitHub releases
188
+ return downloadExtension(downloadOptions);
189
+ }
package/dist/cjs/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Absurd = void 0;
3
+ exports.Absurd = exports.resolveExtensionPath = exports.downloadExtension = void 0;
4
4
  const absurd_sdk_1 = require("absurd-sdk");
5
5
  const sqlite_1 = require("./sqlite");
6
+ var extension_downloader_1 = require("./extension-downloader");
7
+ Object.defineProperty(exports, "downloadExtension", { enumerable: true, get: function () { return extension_downloader_1.downloadExtension; } });
8
+ Object.defineProperty(exports, "resolveExtensionPath", { enumerable: true, get: function () { return extension_downloader_1.resolveExtensionPath; } });
6
9
  class Absurd extends absurd_sdk_1.Absurd {
7
10
  db;
8
11
  constructor(db, extensionPath) {
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SqliteConnection = void 0;
4
4
  class SqliteConnection {
5
5
  db;
6
+ maxRetries = 5;
7
+ baseRetryDelayMs = 50;
6
8
  constructor(db) {
7
9
  this.db = db;
8
10
  // TODO: verbose logging
@@ -16,15 +18,31 @@ class SqliteConnection {
16
18
  // https://github.com/WiseLibs/better-sqlite3/blob/6209be238d6a1b181f516e4e636986604b0f62e1/src/objects/statement.cpp#L134C83-L134C95
17
19
  throw new Error("The query() method is only statements that return data");
18
20
  }
19
- const rowsDecoded = statement
21
+ const rowsDecoded = await this.runWithRetry(() => statement
20
22
  .all(sqliteParams)
21
- .map((row) => decodeRowValues(statement, row));
23
+ .map((row) => decodeRowValues(statement, row)));
22
24
  return { rows: rowsDecoded };
23
25
  }
24
26
  async exec(sql, params) {
25
27
  const sqliteQuery = rewritePostgresQuery(sql);
26
28
  const sqliteParams = rewritePostgresParams(params);
27
- this.db.prepare(sqliteQuery).run(sqliteParams);
29
+ const statement = this.db.prepare(sqliteQuery);
30
+ await this.runWithRetry(() => statement.run(sqliteParams));
31
+ }
32
+ async runWithRetry(operation) {
33
+ let attempt = 0;
34
+ while (true) {
35
+ try {
36
+ return operation();
37
+ }
38
+ catch (err) {
39
+ if (!isRetryableSQLiteError(err) || attempt >= this.maxRetries) {
40
+ throw err;
41
+ }
42
+ attempt++;
43
+ await delay(this.baseRetryDelayMs * attempt);
44
+ }
45
+ }
28
46
  }
29
47
  }
30
48
  exports.SqliteConnection = SqliteConnection;
@@ -115,3 +133,26 @@ function encodeColumnValue(value) {
115
133
  }
116
134
  return value;
117
135
  }
136
+ const sqliteRetryableErrorCodes = new Set(["SQLITE_BUSY", "SQLITE_LOCKED"]);
137
+ const sqliteRetryableErrnos = new Set([5, 6]);
138
+ function isRetryableSQLiteError(err) {
139
+ if (!err || typeof err !== "object") {
140
+ return false;
141
+ }
142
+ const code = err.code;
143
+ if (typeof code === "string") {
144
+ for (const retryableCode of sqliteRetryableErrorCodes) {
145
+ if (code.startsWith(retryableCode)) {
146
+ return true;
147
+ }
148
+ }
149
+ }
150
+ const errno = err.errno;
151
+ if (typeof errno === "number" && sqliteRetryableErrnos.has(errno)) {
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+ function delay(ms) {
157
+ return new Promise((resolve) => setTimeout(resolve, ms));
158
+ }
@@ -0,0 +1,76 @@
1
+ export interface DownloadExtensionOptions {
2
+ /**
3
+ * Version of the extension to download. If not specified, uses "latest".
4
+ * Examples: "v0.1.0-alpha.3", "latest"
5
+ */
6
+ version?: string;
7
+ /**
8
+ * GitHub repository owner. Defaults to "b4fun".
9
+ */
10
+ owner?: string;
11
+ /**
12
+ * GitHub repository name. Defaults to "absurd-sqlite".
13
+ */
14
+ repo?: string;
15
+ /**
16
+ * Custom cache directory for storing downloaded extensions.
17
+ * If not specified, uses a default cache directory in user's home.
18
+ */
19
+ cacheDir?: string;
20
+ /**
21
+ * Force re-download even if cached version exists.
22
+ */
23
+ force?: boolean;
24
+ /**
25
+ * Expected SHA256 checksum of the downloaded file for validation.
26
+ * If provided, the downloaded file's checksum will be verified against this value.
27
+ * If the checksum doesn't match, an error will be thrown.
28
+ */
29
+ expectedChecksum?: string;
30
+ }
31
+ /**
32
+ * Downloads the absurd-sqlite extension from GitHub releases.
33
+ * Returns the path to the downloaded extension file.
34
+ *
35
+ * @param options - Download options
36
+ * @returns Path to the extension file
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { downloadExtension } from "@absurd-sqlite/sdk";
41
+ *
42
+ * // Download latest version
43
+ * const extensionPath = await downloadExtension();
44
+ *
45
+ * // Download specific version
46
+ * const extensionPath = await downloadExtension({ version: "v0.1.0-alpha.3" });
47
+ *
48
+ * // Download with checksum verification
49
+ * const extensionPath = await downloadExtension({
50
+ * version: "v0.1.0-alpha.3",
51
+ * expectedChecksum: "abc123..."
52
+ * });
53
+ * ```
54
+ */
55
+ export declare function downloadExtension(options?: DownloadExtensionOptions): Promise<string>;
56
+ /**
57
+ * Resolves the extension path, either from the provided path, environment variable,
58
+ * or by downloading from GitHub releases.
59
+ *
60
+ * @param extensionPath - Optional path to the extension file
61
+ * @param downloadOptions - Options for downloading if no path is provided
62
+ * @returns Path to the extension file
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * import { resolveExtensionPath } from "@absurd-sqlite/sdk";
67
+ *
68
+ * // Use provided path
69
+ * const path1 = await resolveExtensionPath("/path/to/extension.so");
70
+ *
71
+ * // Use environment variable or download
72
+ * const path2 = await resolveExtensionPath();
73
+ * ```
74
+ */
75
+ export declare function resolveExtensionPath(extensionPath?: string, downloadOptions?: DownloadExtensionOptions): Promise<string>;
76
+ //# sourceMappingURL=extension-downloader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-downloader.d.ts","sourceRoot":"","sources":["../src/extension-downloader.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkJD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CAyCjB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,oBAAoB,CACxC,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,wBAAwB,GACzC,OAAO,CAAC,MAAM,CAAC,CAcjB"}
@@ -0,0 +1,186 @@
1
+ import { existsSync, mkdirSync, writeFileSync, chmodSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { createHash } from "node:crypto";
5
+ function getPlatformInfo() {
6
+ const platform = process.platform;
7
+ const arch = process.arch;
8
+ let os;
9
+ let ext;
10
+ switch (platform) {
11
+ case "darwin":
12
+ os = "macOS";
13
+ ext = "dylib";
14
+ break;
15
+ case "linux":
16
+ os = "Linux";
17
+ ext = "so";
18
+ break;
19
+ case "win32":
20
+ os = "Windows";
21
+ ext = "dll";
22
+ break;
23
+ default:
24
+ throw new Error(`Unsupported platform: ${platform}`);
25
+ }
26
+ let archStr;
27
+ switch (arch) {
28
+ case "x64":
29
+ archStr = "X64";
30
+ break;
31
+ case "arm64":
32
+ archStr = "ARM64";
33
+ break;
34
+ default:
35
+ throw new Error(`Unsupported architecture: ${arch}`);
36
+ }
37
+ return { os, arch: archStr, ext };
38
+ }
39
+ function getDefaultCacheDir() {
40
+ return join(homedir(), ".cache", "absurd-sqlite", "extensions");
41
+ }
42
+ function getAssetName(version, platform) {
43
+ // Format: absurd-absurd-sqlite-extension-vX.Y.Z-{OS}-{ARCH}.{ext}
44
+ return `absurd-absurd-sqlite-extension-${version}-${platform.os}-${platform.arch}.${platform.ext}`;
45
+ }
46
+ function getTag(version) {
47
+ return `absurd-sqlite-extension/${version}`;
48
+ }
49
+ async function fetchLatestVersion(owner, repo) {
50
+ const url = `https://api.github.com/repos/${owner}/${repo}/releases`;
51
+ const response = await fetch(url);
52
+ if (!response.ok) {
53
+ throw new Error(`Failed to fetch releases: ${response.status} ${response.statusText}`);
54
+ }
55
+ const releases = (await response.json());
56
+ // Find the latest non-draft extension release
57
+ for (const release of releases) {
58
+ if (!release.draft &&
59
+ release.tag_name.startsWith("absurd-sqlite-extension/")) {
60
+ // Extract version from tag (e.g., "absurd-sqlite-extension/v0.1.0-alpha.3" -> "v0.1.0-alpha.3")
61
+ return release.tag_name.replace("absurd-sqlite-extension/", "");
62
+ }
63
+ }
64
+ throw new Error("No extension releases found");
65
+ }
66
+ async function downloadAsset(owner, repo, tag, assetName, destPath, expectedChecksum) {
67
+ const url = `https://github.com/${owner}/${repo}/releases/download/${tag}/${assetName}`;
68
+ const response = await fetch(url);
69
+ if (!response.ok) {
70
+ throw new Error(`Failed to download extension: ${response.status} ${response.statusText} from ${url}`);
71
+ }
72
+ const buffer = await response.arrayBuffer();
73
+ writeFileSync(destPath, Buffer.from(buffer));
74
+ // Verify checksum if provided
75
+ if (expectedChecksum) {
76
+ const actualChecksum = calculateChecksum(destPath);
77
+ if (actualChecksum !== expectedChecksum.toLowerCase()) {
78
+ throw new Error(`Checksum verification failed. Expected: ${expectedChecksum.toLowerCase()}, Got: ${actualChecksum}`);
79
+ }
80
+ }
81
+ // Make the extension executable on Unix-like systems
82
+ if (process.platform !== "win32") {
83
+ chmodSync(destPath, 0o755);
84
+ }
85
+ }
86
+ function calculateChecksum(filePath) {
87
+ const fileBuffer = readFileSync(filePath);
88
+ const hash = createHash("sha256");
89
+ hash.update(fileBuffer);
90
+ return hash.digest("hex");
91
+ }
92
+ function getCachedPath(cacheDir, version, platform) {
93
+ // Use a consistent name that SQLite expects: libabsurd.{ext}
94
+ const ext = platform.ext;
95
+ return join(cacheDir, version, `libabsurd.${ext}`);
96
+ }
97
+ /**
98
+ * Downloads the absurd-sqlite extension from GitHub releases.
99
+ * Returns the path to the downloaded extension file.
100
+ *
101
+ * @param options - Download options
102
+ * @returns Path to the extension file
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * import { downloadExtension } from "@absurd-sqlite/sdk";
107
+ *
108
+ * // Download latest version
109
+ * const extensionPath = await downloadExtension();
110
+ *
111
+ * // Download specific version
112
+ * const extensionPath = await downloadExtension({ version: "v0.1.0-alpha.3" });
113
+ *
114
+ * // Download with checksum verification
115
+ * const extensionPath = await downloadExtension({
116
+ * version: "v0.1.0-alpha.3",
117
+ * expectedChecksum: "abc123..."
118
+ * });
119
+ * ```
120
+ */
121
+ export async function downloadExtension(options = {}) {
122
+ const owner = options.owner ?? "b4fun";
123
+ const repo = options.repo ?? "absurd-sqlite";
124
+ const cacheDir = options.cacheDir ?? getDefaultCacheDir();
125
+ const force = options.force ?? false;
126
+ // Resolve version
127
+ let version = options.version ?? "latest";
128
+ if (version === "latest") {
129
+ version = await fetchLatestVersion(owner, repo);
130
+ }
131
+ // Get platform info
132
+ const platform = getPlatformInfo();
133
+ const assetName = getAssetName(version, platform);
134
+ const tag = getTag(version);
135
+ // Check cache
136
+ const cachedPath = getCachedPath(cacheDir, version, platform);
137
+ if (!force && existsSync(cachedPath)) {
138
+ // If checksum is provided, verify cached file
139
+ if (options.expectedChecksum) {
140
+ const actualChecksum = calculateChecksum(cachedPath);
141
+ if (actualChecksum !== options.expectedChecksum.toLowerCase()) {
142
+ throw new Error(`Cached file checksum verification failed. Expected: ${options.expectedChecksum.toLowerCase()}, Got: ${actualChecksum}`);
143
+ }
144
+ }
145
+ return cachedPath;
146
+ }
147
+ // Ensure cache directory exists
148
+ const versionDir = join(cacheDir, version);
149
+ mkdirSync(versionDir, { recursive: true });
150
+ // Download asset
151
+ await downloadAsset(owner, repo, tag, assetName, cachedPath, options.expectedChecksum);
152
+ return cachedPath;
153
+ }
154
+ /**
155
+ * Resolves the extension path, either from the provided path, environment variable,
156
+ * or by downloading from GitHub releases.
157
+ *
158
+ * @param extensionPath - Optional path to the extension file
159
+ * @param downloadOptions - Options for downloading if no path is provided
160
+ * @returns Path to the extension file
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * import { resolveExtensionPath } from "@absurd-sqlite/sdk";
165
+ *
166
+ * // Use provided path
167
+ * const path1 = await resolveExtensionPath("/path/to/extension.so");
168
+ *
169
+ * // Use environment variable or download
170
+ * const path2 = await resolveExtensionPath();
171
+ * ```
172
+ */
173
+ export async function resolveExtensionPath(extensionPath, downloadOptions) {
174
+ // If path is provided, use it
175
+ if (extensionPath) {
176
+ return extensionPath;
177
+ }
178
+ // Try environment variable
179
+ const envPath = process.env.ABSURD_SQLITE_EXTENSION_PATH;
180
+ if (envPath) {
181
+ return envPath;
182
+ }
183
+ // Download from GitHub releases
184
+ return downloadExtension(downloadOptions);
185
+ }
186
+ //# sourceMappingURL=extension-downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-downloader.js","sourceRoot":"","sources":["../src/extension-downloader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA4CzC,SAAS,eAAe;IACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1B,IAAI,EAAU,CAAC;IACf,IAAI,GAAW,CAAC;IAEhB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,EAAE,GAAG,OAAO,CAAC;YACb,GAAG,GAAG,OAAO,CAAC;YACd,MAAM;QACR,KAAK,OAAO;YACV,EAAE,GAAG,OAAO,CAAC;YACb,GAAG,GAAG,IAAI,CAAC;YACX,MAAM;QACR,KAAK,OAAO;YACV,EAAE,GAAG,SAAS,CAAC;YACf,GAAG,GAAG,KAAK,CAAC;YACZ,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,OAAe,CAAC;IACpB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,KAAK;YACR,OAAO,GAAG,KAAK,CAAC;YAChB,MAAM;QACR,KAAK,OAAO;YACV,OAAO,GAAG,OAAO,CAAC;YAClB,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,OAAe,EAAE,QAAsB;IAC3D,kEAAkE;IAClE,OAAO,kCAAkC,OAAO,IAAI,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;AACrG,CAAC;AAED,SAAS,MAAM,CAAC,OAAe;IAC7B,OAAO,2BAA2B,OAAO,EAAE,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,KAAa,EACb,IAAY;IAEZ,MAAM,GAAG,GAAG,gCAAgC,KAAK,IAAI,IAAI,WAAW,CAAC;IACrE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIrC,CAAC;IAEH,8CAA8C;IAC9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IACE,CAAC,OAAO,CAAC,KAAK;YACd,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,0BAA0B,CAAC,EACvD,CAAC;YACD,gGAAgG;YAChG,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;AACjD,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,KAAa,EACb,IAAY,EACZ,GAAW,EACX,SAAiB,EACjB,QAAgB,EAChB,gBAAyB;IAEzB,MAAM,GAAG,GAAG,sBAAsB,KAAK,IAAI,IAAI,sBAAsB,GAAG,IAAI,SAAS,EAAE,CAAC;IACxF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,iCAAiC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,SAAS,GAAG,EAAE,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC5C,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAE7C,8BAA8B;IAC9B,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,cAAc,KAAK,gBAAgB,CAAC,WAAW,EAAE,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CACb,2CAA2C,gBAAgB,CAAC,WAAW,EAAE,UAAU,cAAc,EAAE,CACpG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,aAAa,CACpB,QAAgB,EAChB,OAAe,EACf,QAAsB;IAEtB,6DAA6D;IAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC;IACzB,OAAO,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAoC,EAAE;IAEtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,eAAe,CAAC;IAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,kBAAkB,EAAE,CAAC;IAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IAErC,kBAAkB;IAClB,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC1C,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,GAAG,MAAM,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAE5B,cAAc;IACd,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE9D,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,8CAA8C;QAC9C,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC7B,MAAM,cAAc,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;YACrD,IAAI,cAAc,KAAK,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC9D,MAAM,IAAI,KAAK,CACb,uDAAuD,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,UAAU,cAAc,EAAE,CACxH,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,gCAAgC;IAChC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,iBAAiB;IACjB,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEvF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,aAAsB,EACtB,eAA0C;IAE1C,8BAA8B;IAC9B,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,2BAA2B;IAC3B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IACzD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,gCAAgC;IAChC,OAAO,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAC5C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Absurd as AbsurdBase } from "absurd-sdk";
2
2
  import type { AbsurdClient } from "./absurd-types";
3
3
  import type { SQLiteDatabase } from "./sqlite-types";
4
4
  export type { AbsurdClient, Queryable, Worker } from "./absurd-types";
5
+ export { downloadExtension, resolveExtensionPath, type DownloadExtensionOptions, } from "./extension-downloader";
5
6
  export type { AbsurdHooks, AbsurdOptions, CancellationPolicy, ClaimedTask, JsonObject, JsonValue, RetryStrategy, SpawnOptions, SpawnResult, TaskContext, TaskHandler, TaskRegistrationOptions, WorkerOptions, } from "absurd-sdk";
6
7
  export type { SQLiteBindParams, SQLiteBindValue, SQLiteColumnDefinition, SQLiteDatabase, SQLiteRestBindParams, SQLiteStatement, SQLiteVerboseLog, } from "./sqlite-types";
7
8
  export declare class Absurd extends AbsurdBase implements AbsurdClient {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,uBAAuB,EACvB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,MAAO,SAAQ,UAAW,YAAW,YAAY;IAC5D,OAAO,CAAC,EAAE,CAAiB;gBAEf,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM;IAO/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,wBAAwB,GAC9B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,uBAAuB,EACvB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,MAAO,SAAQ,UAAW,YAAW,YAAY;IAC5D,OAAO,CAAC,EAAE,CAAiB;gBAEf,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM;IAO/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Absurd as AbsurdBase } from "absurd-sdk";
2
2
  import { SqliteConnection } from "./sqlite";
3
+ export { downloadExtension, resolveExtensionPath, } from "./extension-downloader";
3
4
  export class Absurd extends AbsurdBase {
4
5
  db;
5
6
  constructor(db, extensionPath) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA4B5C,MAAM,OAAO,MAAO,SAAQ,UAAU;IAC5B,EAAE,CAAiB;IAE3B,YAAY,EAAkB,EAAE,aAAqB;QACnD,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAC3C,KAAK,CAAC,SAAS,CAAC,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,GAErB,MAAM,wBAAwB,CAAC;AA0BhC,MAAM,OAAO,MAAO,SAAQ,UAAU;IAC5B,EAAE,CAAiB;IAE3B,YAAY,EAAkB,EAAE,aAAqB;QACnD,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAC3C,KAAK,CAAC,SAAS,CAAC,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}
package/dist/sqlite.d.ts CHANGED
@@ -2,10 +2,13 @@ import type { Queryable } from "./absurd-types";
2
2
  import type { SQLiteRestBindParams, SQLiteDatabase } from "./sqlite-types";
3
3
  export declare class SqliteConnection implements Queryable {
4
4
  private readonly db;
5
+ private readonly maxRetries;
6
+ private readonly baseRetryDelayMs;
5
7
  constructor(db: SQLiteDatabase);
6
8
  query<R extends object = Record<string, any>>(sql: string, params?: SQLiteRestBindParams): Promise<{
7
9
  rows: R[];
8
10
  }>;
9
11
  exec(sql: string, params?: SQLiteRestBindParams): Promise<void>;
12
+ private runWithRetry;
10
13
  }
11
14
  //# sourceMappingURL=sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EAGf,MAAM,gBAAgB,CAAC;AAExB,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;gBAExB,EAAE,EAAE,cAAc;IAKxB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC;IAkBnB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CAMtE"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EAGf,MAAM,gBAAgB,CAAC;AAExB,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;gBAE3B,EAAE,EAAE,cAAc;IAKxB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC;IAoBnB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;YAQvD,YAAY;CAc3B"}
package/dist/sqlite.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export class SqliteConnection {
2
2
  db;
3
+ maxRetries = 5;
4
+ baseRetryDelayMs = 50;
3
5
  constructor(db) {
4
6
  this.db = db;
5
7
  // TODO: verbose logging
@@ -13,15 +15,31 @@ export class SqliteConnection {
13
15
  // https://github.com/WiseLibs/better-sqlite3/blob/6209be238d6a1b181f516e4e636986604b0f62e1/src/objects/statement.cpp#L134C83-L134C95
14
16
  throw new Error("The query() method is only statements that return data");
15
17
  }
16
- const rowsDecoded = statement
18
+ const rowsDecoded = await this.runWithRetry(() => statement
17
19
  .all(sqliteParams)
18
- .map((row) => decodeRowValues(statement, row));
20
+ .map((row) => decodeRowValues(statement, row)));
19
21
  return { rows: rowsDecoded };
20
22
  }
21
23
  async exec(sql, params) {
22
24
  const sqliteQuery = rewritePostgresQuery(sql);
23
25
  const sqliteParams = rewritePostgresParams(params);
24
- this.db.prepare(sqliteQuery).run(sqliteParams);
26
+ const statement = this.db.prepare(sqliteQuery);
27
+ await this.runWithRetry(() => statement.run(sqliteParams));
28
+ }
29
+ async runWithRetry(operation) {
30
+ let attempt = 0;
31
+ while (true) {
32
+ try {
33
+ return operation();
34
+ }
35
+ catch (err) {
36
+ if (!isRetryableSQLiteError(err) || attempt >= this.maxRetries) {
37
+ throw err;
38
+ }
39
+ attempt++;
40
+ await delay(this.baseRetryDelayMs * attempt);
41
+ }
42
+ }
25
43
  }
26
44
  }
27
45
  const namedParamPrefix = "p";
@@ -111,4 +129,27 @@ function encodeColumnValue(value) {
111
129
  }
112
130
  return value;
113
131
  }
132
+ const sqliteRetryableErrorCodes = new Set(["SQLITE_BUSY", "SQLITE_LOCKED"]);
133
+ const sqliteRetryableErrnos = new Set([5, 6]);
134
+ function isRetryableSQLiteError(err) {
135
+ if (!err || typeof err !== "object") {
136
+ return false;
137
+ }
138
+ const code = err.code;
139
+ if (typeof code === "string") {
140
+ for (const retryableCode of sqliteRetryableErrorCodes) {
141
+ if (code.startsWith(retryableCode)) {
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ const errno = err.errno;
147
+ if (typeof errno === "number" && sqliteRetryableErrnos.has(errno)) {
148
+ return true;
149
+ }
150
+ return false;
151
+ }
152
+ function delay(ms) {
153
+ return new Promise((resolve) => setTimeout(resolve, ms));
154
+ }
114
155
  //# sourceMappingURL=sqlite.js.map