dbgate-sqlite-dumper 0.1.0 → 0.1.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.
- package/LICENSE +674 -674
- package/README.md +247 -230
- package/dist/better-sqlite3.cjs.map +1 -1
- package/dist/better-sqlite3.d.cts +1 -1
- package/dist/better-sqlite3.d.ts +1 -1
- package/dist/better-sqlite3.js.map +1 -1
- package/dist/d1.cjs +260 -0
- package/dist/d1.cjs.map +1 -0
- package/dist/d1.d.cts +101 -0
- package/dist/d1.d.ts +101 -0
- package/dist/d1.js +229 -0
- package/dist/d1.js.map +1 -0
- package/dist/errors-CplNzuYs.d.cts +20 -0
- package/dist/errors-CplNzuYs.d.ts +20 -0
- package/dist/index.cjs +405 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -24
- package/dist/index.d.ts +58 -24
- package/dist/index.js +400 -89
- package/dist/index.js.map +1 -1
- package/dist/{types-CTyJTzB6.d.cts → types-DziV5ysl.d.cts} +67 -2
- package/dist/{types-CTyJTzB6.d.ts → types-DziV5ysl.d.ts} +67 -2
- package/docs/architecture.md +180 -157
- package/docs/better-sqlite3-adapter.md +83 -83
- package/docs/d1-adapter.md +98 -0
- package/docs/dump-api.md +253 -253
- package/docs/known-limitations.md +100 -88
- package/docs/native-compatibility.md +124 -124
- package/docs/restore-api.md +234 -234
- package/docs/round-trip-testing.md +84 -84
- package/docs/supported-data-types.md +74 -74
- package/docs/supported-objects.md +85 -85
- package/package.json +96 -89
package/dist/d1.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// src/utils/errors.ts
|
|
2
|
+
var SqliteDumperError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message, options) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.name = "SqliteDumperError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var OperationCancelledError = class extends SqliteDumperError {
|
|
11
|
+
constructor(message = "The operation was cancelled") {
|
|
12
|
+
super("operation-cancelled", message);
|
|
13
|
+
this.name = "OperationCancelledError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function throwIfAborted(signal) {
|
|
17
|
+
if (signal?.aborted) {
|
|
18
|
+
throw new OperationCancelledError();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/d1.ts
|
|
23
|
+
var DEFAULT_D1_PAGE_SIZE = 1e3;
|
|
24
|
+
var DEFAULT_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
25
|
+
function d1ConnectionFeatures(pageSize = DEFAULT_D1_PAGE_SIZE) {
|
|
26
|
+
return {
|
|
27
|
+
transactions: false,
|
|
28
|
+
pragmaFunctions: false,
|
|
29
|
+
schemaQualifiedNames: false,
|
|
30
|
+
binaryTransport: "hex",
|
|
31
|
+
pagedReadSize: pageSize,
|
|
32
|
+
reservedNamePrefixes: ["_cf_"]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
var D1Error = class extends SqliteDumperError {
|
|
36
|
+
/** HTTP status of the response, for the REST API. */
|
|
37
|
+
status;
|
|
38
|
+
/** SQLite's symbolic result code, when D1's message names one (`SQLITE_ERROR`). */
|
|
39
|
+
sqliteCode;
|
|
40
|
+
constructor(message, options) {
|
|
41
|
+
super("d1-error", message, options?.cause === void 0 ? {} : { cause: options.cause });
|
|
42
|
+
this.name = "D1Error";
|
|
43
|
+
this.status = options?.status;
|
|
44
|
+
this.sqliteCode = /\b(SQLITE_[A-Z_]+)\b/.exec(message)?.[1];
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function toD1Parameter(value) {
|
|
48
|
+
if (value === null || typeof value === "string" || typeof value === "number") {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "bigint") {
|
|
52
|
+
return Number.isSafeInteger(Number(value)) ? Number(value) : value.toString();
|
|
53
|
+
}
|
|
54
|
+
throw new D1Error("Binary parameters cannot be sent to D1");
|
|
55
|
+
}
|
|
56
|
+
function rowsFromArrays(columns, rows) {
|
|
57
|
+
return rows.map((values) => {
|
|
58
|
+
const row = {};
|
|
59
|
+
columns.forEach((column, index) => {
|
|
60
|
+
row[column] = values[index] ?? null;
|
|
61
|
+
});
|
|
62
|
+
return row;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function d1Connection(run, cancel, pageSize) {
|
|
66
|
+
return {
|
|
67
|
+
features: d1ConnectionFeatures(pageSize),
|
|
68
|
+
async query(query, signal) {
|
|
69
|
+
throwIfAborted(signal);
|
|
70
|
+
const [result] = await run([query], signal);
|
|
71
|
+
return result ?? { rows: [] };
|
|
72
|
+
},
|
|
73
|
+
async queryBatch(queries, signal) {
|
|
74
|
+
throwIfAborted(signal);
|
|
75
|
+
return queries.length === 0 ? [] : run(queries, signal);
|
|
76
|
+
},
|
|
77
|
+
stream(query, options) {
|
|
78
|
+
return {
|
|
79
|
+
async *[Symbol.asyncIterator]() {
|
|
80
|
+
throwIfAborted(options?.signal);
|
|
81
|
+
const [result] = await run([query], options?.signal);
|
|
82
|
+
for (const row of result?.rows ?? []) {
|
|
83
|
+
if (options?.signal?.aborted) {
|
|
84
|
+
throw new OperationCancelledError();
|
|
85
|
+
}
|
|
86
|
+
yield row;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
describeError(error) {
|
|
92
|
+
if (!(error instanceof Error)) {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
const code = error instanceof D1Error ? error.sqliteCode : void 0;
|
|
96
|
+
return { ...code === void 0 ? {} : { code }, message: error.message };
|
|
97
|
+
},
|
|
98
|
+
cancel
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function envelopeError(envelope, fallback) {
|
|
102
|
+
const messages = (envelope?.errors ?? []).map((error) => error.message).filter((message) => typeof message === "string" && message !== "");
|
|
103
|
+
return messages.length > 0 ? messages.join("; ") : fallback;
|
|
104
|
+
}
|
|
105
|
+
function fromD1Http(options) {
|
|
106
|
+
const accountId = String(options.accountId ?? "").trim();
|
|
107
|
+
const databaseId = String(options.databaseId ?? "").trim();
|
|
108
|
+
const apiToken = String(options.apiToken ?? "").trim();
|
|
109
|
+
if (!accountId || !databaseId || !apiToken) {
|
|
110
|
+
throw new D1Error("A D1 connection needs an account ID, a database ID and an API token");
|
|
111
|
+
}
|
|
112
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
113
|
+
if (typeof doFetch !== "function") {
|
|
114
|
+
throw new D1Error("No fetch implementation is available; pass one as options.fetch");
|
|
115
|
+
}
|
|
116
|
+
const baseUrl = (options.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
|
|
117
|
+
const url = `${baseUrl}/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}/raw`;
|
|
118
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
119
|
+
const run = async (queries, signal) => {
|
|
120
|
+
const statements = queries.map((query) => ({
|
|
121
|
+
sql: query.sql,
|
|
122
|
+
...query.parameters === void 0 || query.parameters.length === 0 ? {} : { params: query.parameters.map(toD1Parameter) }
|
|
123
|
+
}));
|
|
124
|
+
const body = statements.length === 1 ? statements[0] : { batch: statements };
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
const onAbort = () => controller.abort();
|
|
127
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
128
|
+
inFlight.add(controller);
|
|
129
|
+
let response;
|
|
130
|
+
let text;
|
|
131
|
+
try {
|
|
132
|
+
response = await doFetch(url, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
Authorization: `Bearer ${apiToken}`,
|
|
136
|
+
"Content-Type": "application/json",
|
|
137
|
+
Accept: "application/json"
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(body),
|
|
140
|
+
signal: controller.signal
|
|
141
|
+
});
|
|
142
|
+
text = await response.text();
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (signal?.aborted || controller.signal.aborted) {
|
|
145
|
+
throw new OperationCancelledError();
|
|
146
|
+
}
|
|
147
|
+
throw new D1Error(
|
|
148
|
+
`D1 request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
149
|
+
{
|
|
150
|
+
cause: error
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
} finally {
|
|
154
|
+
signal?.removeEventListener("abort", onAbort);
|
|
155
|
+
inFlight.delete(controller);
|
|
156
|
+
}
|
|
157
|
+
let envelope;
|
|
158
|
+
try {
|
|
159
|
+
envelope = JSON.parse(text);
|
|
160
|
+
} catch {
|
|
161
|
+
envelope = void 0;
|
|
162
|
+
}
|
|
163
|
+
if (!response.ok || !envelope || envelope.success === false) {
|
|
164
|
+
throw new D1Error(
|
|
165
|
+
envelopeError(
|
|
166
|
+
envelope,
|
|
167
|
+
`D1 request failed with HTTP ${response.status}${envelope ? "" : " and a response that is not JSON"}`
|
|
168
|
+
),
|
|
169
|
+
{ status: response.status }
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
const items = envelope.result ?? [];
|
|
173
|
+
if (items.length !== queries.length) {
|
|
174
|
+
throw new D1Error(`D1 returned ${items.length} results for ${queries.length} statements`, {
|
|
175
|
+
status: response.status
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return items.map((item) => {
|
|
179
|
+
if (item.success === false) {
|
|
180
|
+
throw new D1Error(item.error ?? "D1 reported a failed statement", {
|
|
181
|
+
status: response.status
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const columns = item.results?.columns ?? [];
|
|
185
|
+
return { rows: rowsFromArrays(columns, item.results?.rows ?? []), columns };
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
return d1Connection(
|
|
189
|
+
run,
|
|
190
|
+
async () => {
|
|
191
|
+
for (const controller of inFlight) {
|
|
192
|
+
controller.abort();
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
options.pageSize ?? DEFAULT_D1_PAGE_SIZE
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function fromD1Binding(database, options) {
|
|
199
|
+
const prepare = (query) => {
|
|
200
|
+
const statement = database.prepare(query.sql);
|
|
201
|
+
return query.parameters === void 0 || query.parameters.length === 0 ? statement : statement.bind(...query.parameters.map(toD1Parameter));
|
|
202
|
+
};
|
|
203
|
+
const run = async (queries, signal) => {
|
|
204
|
+
throwIfAborted(signal);
|
|
205
|
+
try {
|
|
206
|
+
if (queries.length === 1) {
|
|
207
|
+
const [columns, ...rows] = await prepare(queries[0]).raw({
|
|
208
|
+
columnNames: true
|
|
209
|
+
});
|
|
210
|
+
return [{ rows: rowsFromArrays(columns ?? [], rows), columns: columns ?? [] }];
|
|
211
|
+
}
|
|
212
|
+
const results = await database.batch(queries.map(prepare));
|
|
213
|
+
return results.map((result) => ({ rows: result.results ?? [] }));
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (error instanceof SqliteDumperError) throw error;
|
|
216
|
+
throw new D1Error(error instanceof Error ? error.message : String(error), { cause: error });
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return d1Connection(run, async () => {
|
|
220
|
+
}, options?.pageSize ?? DEFAULT_D1_PAGE_SIZE);
|
|
221
|
+
}
|
|
222
|
+
export {
|
|
223
|
+
D1Error,
|
|
224
|
+
DEFAULT_D1_PAGE_SIZE,
|
|
225
|
+
d1ConnectionFeatures,
|
|
226
|
+
fromD1Binding,
|
|
227
|
+
fromD1Http
|
|
228
|
+
};
|
|
229
|
+
//# sourceMappingURL=d1.js.map
|
package/dist/d1.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors.ts","../src/d1.ts"],"sourcesContent":["/** Base class for every error this package throws intentionally. */\nexport class SqliteDumperError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'SqliteDumperError';\n this.code = code;\n }\n}\n\n/** Thrown when an operation stops because its `AbortSignal` was triggered. */\nexport class OperationCancelledError extends SqliteDumperError {\n constructor(message = 'The operation was cancelled') {\n super('operation-cancelled', message);\n this.name = 'OperationCancelledError';\n }\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw new OperationCancelledError();\n }\n}\n\n/**\n * True for both cancellation shapes this package can observe: its own\n * {@link OperationCancelledError} and the `DOMException` an `AbortSignal`\n * (or a Node stream aborted through one) raises.\n */\nexport function isAbortError(error: unknown): boolean {\n return (\n (error instanceof DOMException && error.name === 'AbortError') ||\n (error instanceof Error && error.name === 'OperationCancelledError')\n );\n}\n","/**\n * Optional adapter for Cloudflare D1.\n *\n * D1 is SQLite, but not a SQLite handle: it is reached over Cloudflare's REST\n * API (or, inside a Worker, through a binding), every statement is a request,\n * results arrive whole as JSON, and its authorizer refuses part of what an\n * embedded handle allows. The adapter declares those restrictions as\n * {@link SqliteConnectionFeatures}, and the core reads the database with the\n * equivalent queries D1 does allow — so the dump is the same plain-SQL,\n * native-`.dump`-compatible file a local copy of the database would give.\n *\n * Dump only. A dump's script relies on `BEGIN TRANSACTION` / `COMMIT` and\n * `PRAGMA writable_schema`, which D1 refuses; to load a dump into D1, use\n * `wrangler d1 execute --file` (or the D1 import API), which accept it once\n * those statements are removed.\n *\n * Uses only the global `fetch` (or the one passed in): no dependency.\n */\nimport type {\n SqliteConnection,\n SqliteConnectionFeatures,\n SqliteErrorInfo,\n SqliteParameterValue,\n SqliteQuery,\n SqliteQueryResult,\n SqliteRow,\n SqliteStreamOptions,\n} from './connection/types.js';\nimport { OperationCancelledError, SqliteDumperError, throwIfAborted } from './utils/errors.js';\n\n/** Rows per request when a table's data is read. */\nexport const DEFAULT_D1_PAGE_SIZE = 1000;\n\nconst DEFAULT_API_BASE_URL = 'https://api.cloudflare.com/client/v4';\n\n/**\n * What D1 does not allow, as the core understands it:\n *\n * - no `BEGIN` / `SAVEPOINT`, so no read snapshot;\n * - no table-valued `pragma_xxx()` functions;\n * - one database (`main`), and statements need not name it;\n * - JSON results, so bytes are fetched as `hex()` text;\n * - whole results, so table data is read in keyed pages;\n * - reserved `_cf_` tables, which sit in `sqlite_schema` but cannot be read.\n */\nexport function d1ConnectionFeatures(pageSize = DEFAULT_D1_PAGE_SIZE): SqliteConnectionFeatures {\n return {\n transactions: false,\n pragmaFunctions: false,\n schemaQualifiedNames: false,\n binaryTransport: 'hex',\n pagedReadSize: pageSize,\n reservedNamePrefixes: ['_cf_'],\n };\n}\n\n/** An error D1 reported for a statement or a request. */\nexport class D1Error extends SqliteDumperError {\n /** HTTP status of the response, for the REST API. */\n readonly status: number | undefined;\n /** SQLite's symbolic result code, when D1's message names one (`SQLITE_ERROR`). */\n readonly sqliteCode: string | undefined;\n\n constructor(message: string, options?: { status?: number; cause?: unknown }) {\n super('d1-error', message, options?.cause === undefined ? {} : { cause: options.cause });\n this.name = 'D1Error';\n this.status = options?.status;\n this.sqliteCode = /\\b(SQLITE_[A-Z_]+)\\b/.exec(message)?.[1];\n }\n}\n\nfunction toD1Parameter(value: SqliteParameterValue): string | number | null {\n if (value === null || typeof value === 'string' || typeof value === 'number') {\n return value;\n }\n if (typeof value === 'bigint') {\n return Number.isSafeInteger(Number(value)) ? Number(value) : value.toString();\n }\n // JSON has no bytes; the core never binds them when reading.\n throw new D1Error('Binary parameters cannot be sent to D1');\n}\n\nfunction rowsFromArrays(\n columns: readonly string[],\n rows: readonly (readonly unknown[])[],\n): SqliteRow[] {\n return rows.map(values => {\n const row: Record<string, unknown> = {};\n columns.forEach((column, index) => {\n row[column] = values[index] ?? null;\n });\n return row as SqliteRow;\n });\n}\n\n/** A connection that can only run whole queries, given the two ways to run them. */\nfunction d1Connection(\n run: (queries: readonly SqliteQuery[], signal?: AbortSignal) => Promise<SqliteQueryResult[]>,\n cancel: () => Promise<void>,\n pageSize: number,\n): SqliteConnection {\n return {\n features: d1ConnectionFeatures(pageSize),\n\n async query<Row extends SqliteRow = SqliteRow>(\n query: SqliteQuery,\n signal?: AbortSignal,\n ): Promise<SqliteQueryResult<Row>> {\n throwIfAborted(signal);\n const [result] = await run([query], signal);\n return (result ?? { rows: [] }) as SqliteQueryResult<Row>;\n },\n\n async queryBatch(\n queries: readonly SqliteQuery[],\n signal?: AbortSignal,\n ): Promise<readonly SqliteQueryResult[]> {\n throwIfAborted(signal);\n return queries.length === 0 ? [] : run(queries, signal);\n },\n\n stream<Row extends SqliteRow = SqliteRow>(\n query: SqliteQuery,\n options?: SqliteStreamOptions,\n ): AsyncIterable<Row> {\n // D1 returns a result whole; the core reads table data in pages\n // (`pagedReadSize`) and does not stream from this connection.\n return {\n async *[Symbol.asyncIterator](): AsyncGenerator<Row> {\n throwIfAborted(options?.signal);\n const [result] = await run([query], options?.signal);\n for (const row of result?.rows ?? []) {\n if (options?.signal?.aborted) {\n throw new OperationCancelledError();\n }\n yield row as Row;\n }\n },\n };\n },\n\n describeError(error: unknown): SqliteErrorInfo | undefined {\n if (!(error instanceof Error)) {\n return undefined;\n }\n const code = error instanceof D1Error ? error.sqliteCode : undefined;\n return { ...(code === undefined ? {} : { code }), message: error.message };\n },\n\n cancel,\n };\n}\n\nexport interface D1HttpOptions {\n /** Cloudflare account ID. */\n readonly accountId: string;\n /** The database's UUID (shown by `wrangler d1 list`). */\n readonly databaseId: string;\n /** An API token with the `D1 Read` (or `D1 Edit`) permission. */\n readonly apiToken: string;\n /** Defaults to `https://api.cloudflare.com/client/v4`. */\n readonly apiBaseUrl?: string;\n /** Rows per request when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */\n readonly pageSize?: number;\n /** Defaults to the global `fetch`. */\n readonly fetch?: typeof fetch;\n}\n\ninterface D1Envelope {\n readonly success?: boolean;\n readonly errors?: readonly { readonly code?: number; readonly message?: string }[];\n readonly result?: readonly {\n readonly success?: boolean;\n readonly error?: string;\n readonly results?: {\n readonly columns?: readonly string[];\n readonly rows?: readonly unknown[][];\n };\n }[];\n}\n\nfunction envelopeError(envelope: D1Envelope | undefined, fallback: string): string {\n const messages = (envelope?.errors ?? [])\n .map(error => error.message)\n .filter((message): message is string => typeof message === 'string' && message !== '');\n return messages.length > 0 ? messages.join('; ') : fallback;\n}\n\n/**\n * A connection to a D1 database through Cloudflare's REST API — the `/raw`\n * query endpoint, which returns rows as arrays. Several catalog queries go\n * out as one `batch` request.\n *\n * ```ts\n * import { dumpSqlite } from 'dbgate-sqlite-dumper';\n * import { fromD1Http } from 'dbgate-sqlite-dumper/d1';\n *\n * const connection = fromD1Http({ accountId, databaseId, apiToken });\n * await dumpSqlite(connection, {}, fs.createWriteStream('backup.sql'));\n * ```\n *\n * The token is sent only in the `Authorization` header, and never appears in\n * an error message.\n */\nexport function fromD1Http(options: D1HttpOptions): SqliteConnection {\n const accountId = String(options.accountId ?? '').trim();\n const databaseId = String(options.databaseId ?? '').trim();\n const apiToken = String(options.apiToken ?? '').trim();\n if (!accountId || !databaseId || !apiToken) {\n throw new D1Error('A D1 connection needs an account ID, a database ID and an API token');\n }\n const doFetch = options.fetch ?? globalThis.fetch;\n if (typeof doFetch !== 'function') {\n throw new D1Error('No fetch implementation is available; pass one as options.fetch');\n }\n const baseUrl = (options.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\\/+$/, '');\n const url = `${baseUrl}/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}/raw`;\n const inFlight = new Set<AbortController>();\n\n const run = async (\n queries: readonly SqliteQuery[],\n signal?: AbortSignal,\n ): Promise<SqliteQueryResult[]> => {\n const statements = queries.map(query => ({\n sql: query.sql,\n ...(query.parameters === undefined || query.parameters.length === 0\n ? {}\n : { params: query.parameters.map(toD1Parameter) }),\n }));\n const body = statements.length === 1 ? statements[0] : { batch: statements };\n\n const controller = new AbortController();\n const onAbort = (): void => controller.abort();\n signal?.addEventListener('abort', onAbort, { once: true });\n inFlight.add(controller);\n let response: Response;\n let text: string;\n try {\n response = await doFetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n text = await response.text();\n } catch (error) {\n if (signal?.aborted || controller.signal.aborted) {\n throw new OperationCancelledError();\n }\n throw new D1Error(\n `D1 request failed: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n } finally {\n signal?.removeEventListener('abort', onAbort);\n inFlight.delete(controller);\n }\n\n let envelope: D1Envelope | undefined;\n try {\n envelope = JSON.parse(text) as D1Envelope;\n } catch {\n envelope = undefined;\n }\n if (!response.ok || !envelope || envelope.success === false) {\n throw new D1Error(\n envelopeError(\n envelope,\n `D1 request failed with HTTP ${response.status}${envelope ? '' : ' and a response that is not JSON'}`,\n ),\n { status: response.status },\n );\n }\n const items = envelope.result ?? [];\n if (items.length !== queries.length) {\n throw new D1Error(`D1 returned ${items.length} results for ${queries.length} statements`, {\n status: response.status,\n });\n }\n return items.map(item => {\n if (item.success === false) {\n throw new D1Error(item.error ?? 'D1 reported a failed statement', {\n status: response.status,\n });\n }\n const columns = item.results?.columns ?? [];\n return { rows: rowsFromArrays(columns, item.results?.rows ?? []), columns };\n });\n };\n\n return d1Connection(\n run,\n async () => {\n for (const controller of inFlight) {\n controller.abort();\n }\n },\n options.pageSize ?? DEFAULT_D1_PAGE_SIZE,\n );\n}\n\n/** The part of a Workers D1 binding (`env.DB`) this adapter uses. */\nexport interface D1DatabaseBinding {\n prepare(sql: string): D1PreparedStatementBinding;\n batch(statements: D1PreparedStatementBinding[]): Promise<{ results?: unknown[] }[]>;\n}\n\nexport interface D1PreparedStatementBinding {\n bind(...values: unknown[]): D1PreparedStatementBinding;\n raw(options: { columnNames: true }): Promise<[string[], ...unknown[][]]>;\n}\n\nexport interface D1BindingOptions {\n /** Rows per query when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */\n readonly pageSize?: number;\n}\n\n/**\n * A connection through a D1 binding, for a dump taken inside a Worker\n * (`env.DB`). Needs the `nodejs_compat` compatibility flag, for `Buffer`.\n */\nexport function fromD1Binding(\n database: D1DatabaseBinding,\n options?: D1BindingOptions,\n): SqliteConnection {\n const prepare = (query: SqliteQuery): D1PreparedStatementBinding => {\n const statement = database.prepare(query.sql);\n return query.parameters === undefined || query.parameters.length === 0\n ? statement\n : statement.bind(...query.parameters.map(toD1Parameter));\n };\n const run = async (\n queries: readonly SqliteQuery[],\n signal?: AbortSignal,\n ): Promise<SqliteQueryResult[]> => {\n throwIfAborted(signal);\n try {\n if (queries.length === 1) {\n const [columns, ...rows] = await prepare(queries[0] as SqliteQuery).raw({\n columnNames: true,\n });\n return [{ rows: rowsFromArrays(columns ?? [], rows), columns: columns ?? [] }];\n }\n const results = await database.batch(queries.map(prepare));\n return results.map(result => ({ rows: (result.results ?? []) as SqliteRow[] }));\n } catch (error) {\n if (error instanceof SqliteDumperError) throw error;\n throw new D1Error(error instanceof Error ? error.message : String(error), { cause: error });\n }\n };\n // A binding call cannot be interrupted; cancellation takes effect between queries.\n return d1Connection(run, async () => {}, options?.pageSize ?? DEFAULT_D1_PAGE_SIZE);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Base class for every error this package throws intentionally. */
|
|
2
|
+
declare class SqliteDumperError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
constructor(code: string, message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
/** Thrown when an operation stops because its `AbortSignal` was triggered. */
|
|
9
|
+
declare class OperationCancelledError extends SqliteDumperError {
|
|
10
|
+
constructor(message?: string);
|
|
11
|
+
}
|
|
12
|
+
declare function throwIfAborted(signal: AbortSignal | undefined): void;
|
|
13
|
+
/**
|
|
14
|
+
* True for both cancellation shapes this package can observe: its own
|
|
15
|
+
* {@link OperationCancelledError} and the `DOMException` an `AbortSignal`
|
|
16
|
+
* (or a Node stream aborted through one) raises.
|
|
17
|
+
*/
|
|
18
|
+
declare function isAbortError(error: unknown): boolean;
|
|
19
|
+
|
|
20
|
+
export { OperationCancelledError as O, SqliteDumperError as S, isAbortError as i, throwIfAborted as t };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Base class for every error this package throws intentionally. */
|
|
2
|
+
declare class SqliteDumperError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
constructor(code: string, message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
/** Thrown when an operation stops because its `AbortSignal` was triggered. */
|
|
9
|
+
declare class OperationCancelledError extends SqliteDumperError {
|
|
10
|
+
constructor(message?: string);
|
|
11
|
+
}
|
|
12
|
+
declare function throwIfAborted(signal: AbortSignal | undefined): void;
|
|
13
|
+
/**
|
|
14
|
+
* True for both cancellation shapes this package can observe: its own
|
|
15
|
+
* {@link OperationCancelledError} and the `DOMException` an `AbortSignal`
|
|
16
|
+
* (or a Node stream aborted through one) raises.
|
|
17
|
+
*/
|
|
18
|
+
declare function isAbortError(error: unknown): boolean;
|
|
19
|
+
|
|
20
|
+
export { OperationCancelledError as O, SqliteDumperError as S, isAbortError as i, throwIfAborted as t };
|