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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/errors.ts","../src/better-sqlite3.ts"],"sourcesContent":["/** Base class for every error this package throws intentionally. */\r\nexport class SqliteDumperError extends Error {\r\n readonly code: string;\r\n\r\n constructor(code: string, message: string, options?: { cause?: unknown }) {\r\n super(message, options);\r\n this.name = 'SqliteDumperError';\r\n this.code = code;\r\n }\r\n}\r\n\r\n/** Thrown when an operation stops because its `AbortSignal` was triggered. */\r\nexport class OperationCancelledError extends SqliteDumperError {\r\n constructor(message = 'The operation was cancelled') {\r\n super('operation-cancelled', message);\r\n this.name = 'OperationCancelledError';\r\n }\r\n}\r\n\r\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\r\n if (signal?.aborted) {\r\n throw new OperationCancelledError();\r\n }\r\n}\r\n\r\n/**\r\n * True for both cancellation shapes this package can observe: its own\r\n * {@link OperationCancelledError} and the `DOMException` an `AbortSignal`\r\n * (or a Node stream aborted through one) raises.\r\n */\r\nexport function isAbortError(error: unknown): boolean {\r\n return (\r\n (error instanceof DOMException && error.name === 'AbortError') ||\r\n (error instanceof Error && error.name === 'OperationCancelledError')\r\n );\r\n}\r\n","/**\r\n * Optional adapter for the `better-sqlite3` package.\r\n *\r\n * Wraps a caller-owned `better-sqlite3` `Database` as a\r\n * {@link SqliteConnection}. This module is never imported by the core\r\n * package; `better-sqlite3` is an optional peer dependency and is only\r\n * resolved when a consumer imports `dbgate-sqlite-dumper/better-sqlite3`\r\n * themselves.\r\n *\r\n * `better-sqlite3` is the driver DbGate's own SQLite plugin uses, which is\r\n * why it is the bundled adapter. Any other driver works through the same\r\n * {@link SqliteConnection} interface.\r\n */\r\nimport type BetterSqlite3 from 'better-sqlite3';\r\nimport type {\r\n SqliteConnection,\r\n SqliteErrorInfo,\r\n SqliteExecResult,\r\n SqliteParameterValue,\r\n SqliteQuery,\r\n SqliteQueryResult,\r\n SqliteRow,\r\n SqliteStreamOptions,\r\n} from './connection/types.js';\r\nimport { OperationCancelledError, throwIfAborted } from './utils/errors.js';\r\n\r\ntype Database = BetterSqlite3.Database;\r\ntype Statement = BetterSqlite3.Statement<unknown[], unknown>;\r\n\r\nfunction toDriverParameters(parameters: readonly SqliteParameterValue[] | undefined): unknown[] {\r\n return (parameters ?? []).map(value =>\r\n value instanceof Uint8Array && !Buffer.isBuffer(value)\r\n ? Buffer.from(value.buffer, value.byteOffset, value.byteLength)\r\n : value,\r\n );\r\n}\r\n\r\n/**\r\n * Prepares a statement with 64-bit integers returned as `bigint`, so no\r\n * catalog value is ever rounded. (Row data does not depend on this: the core\r\n * reads it through SQL that renders numbers as text — see `valueQuery.ts`.)\r\n */\r\nfunction prepare(database: Database, sql: string): Statement {\r\n const statement = database.prepare(sql) as Statement;\r\n statement.safeIntegers(true);\r\n return statement;\r\n}\r\n\r\nfunction isMultipleStatementsError(error: unknown): boolean {\r\n return error instanceof RangeError && /more than one statement/i.test(error.message);\r\n}\r\n\r\n/**\r\n * Wraps an open `better-sqlite3` database. The database is **borrowed**:\r\n * this package never closes it.\r\n *\r\n * `better-sqlite3` is synchronous, which shapes three behaviours:\r\n *\r\n * - `stream()` steps the statement one row at a time with `iterate()`,\r\n * yielding between rows, so a table of any size streams in constant\r\n * memory. While a stream is open the database cannot run another\r\n * statement — this package never asks it to.\r\n * - `cancel()` cannot interrupt a statement mid-step. Cancellation takes\r\n * effect between rows and between statements, which for a dump or a\r\n * restore (each statement is short) is prompt.\r\n * - `setDefensive()` is implemented with `unsafeMode()`, which is how\r\n * `better-sqlite3` exposes `SQLITE_DBCONFIG_DEFENSIVE`. (Unsafe mode also\r\n * relaxes the driver's own guard against writing while iterating; this\r\n * package never does that.)\r\n */\r\nexport function fromBetterSqlite3(database: Database): SqliteConnection {\r\n const connection: SqliteConnection = {\r\n async query<Row extends SqliteRow = SqliteRow>(\r\n query: SqliteQuery,\r\n signal?: AbortSignal,\r\n ): Promise<SqliteQueryResult<Row>> {\r\n throwIfAborted(signal);\r\n const statement = prepare(database, query.sql);\r\n const parameters = toDriverParameters(query.parameters);\r\n if (statement.reader) {\r\n const rows = statement.all(...parameters) as Row[];\r\n return { rows, columns: statement.columns().map(column => column.name) };\r\n }\r\n statement.run(...parameters);\r\n return { rows: [] };\r\n },\r\n\r\n stream<Row extends SqliteRow = SqliteRow>(\r\n query: SqliteQuery,\r\n options?: SqliteStreamOptions,\r\n ): AsyncIterable<Row> {\r\n const signal = options?.signal;\r\n return {\r\n async *[Symbol.asyncIterator](): AsyncGenerator<Row> {\r\n throwIfAborted(signal);\r\n const statement = prepare(database, query.sql);\r\n if (!statement.reader) {\r\n statement.run(...toDriverParameters(query.parameters));\r\n return;\r\n }\r\n const iterator = statement.iterate(\r\n ...toDriverParameters(query.parameters),\r\n ) as IterableIterator<Row>;\r\n try {\r\n for (const row of iterator) {\r\n if (signal?.aborted) {\r\n throw new OperationCancelledError();\r\n }\r\n yield row;\r\n }\r\n } finally {\r\n // Finalizes the statement even when the consumer stops early,\r\n // which is what frees the database for the next statement.\r\n iterator.return?.();\r\n }\r\n },\r\n };\r\n },\r\n\r\n async execute(sql: string, signal?: AbortSignal): Promise<SqliteExecResult> {\r\n throwIfAborted(signal);\r\n let statement: Statement;\r\n try {\r\n statement = prepare(database, sql);\r\n } catch (error) {\r\n if (!isMultipleStatementsError(error)) {\r\n throw error;\r\n }\r\n // Text the shell would run as several statements at once. Rare in a\r\n // restore (the parser splits statements), but valid input.\r\n const before = totalChanges(database);\r\n database.exec(sql);\r\n return { changes: totalChanges(database) - before };\r\n }\r\n if (statement.reader) {\r\n // A script's own SELECT or PRAGMA query: step it to completion, as\r\n // the shell would, without keeping the rows.\r\n for (const _row of statement.iterate()) {\r\n // discard\r\n }\r\n return { changes: 0 };\r\n }\r\n const info = statement.run();\r\n return { changes: Number(info.changes) };\r\n },\r\n\r\n describeError(error: unknown): SqliteErrorInfo | undefined {\r\n if (!(error instanceof Error)) {\r\n return undefined;\r\n }\r\n const code = (error as { code?: unknown }).code;\r\n return {\r\n ...(typeof code === 'string' ? { code } : {}),\r\n message: error.message,\r\n };\r\n },\r\n\r\n isInTransaction(): boolean {\r\n return database.inTransaction;\r\n },\r\n\r\n async setDefensive(enabled: boolean): Promise<void> {\r\n database.unsafeMode(!enabled);\r\n },\r\n\r\n async cancel(): Promise<void> {\r\n // better-sqlite3 runs each statement synchronously to completion;\r\n // there is nothing in flight to interrupt.\r\n },\r\n };\r\n return connection;\r\n}\r\n\r\nfunction totalChanges(database: Database): number {\r\n return Number(database.prepare('SELECT total_changes()').pluck().get());\r\n}\r\n\r\nexport interface ConnectedBetterSqlite3 {\r\n readonly connection: SqliteConnection;\r\n /** The underlying `better-sqlite3` database, for anything the adapter does not cover. */\r\n readonly database: Database;\r\n /** Closes the database. Idempotent. */\r\n close(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Opens a database file with `better-sqlite3` and wraps it. Unlike\r\n * {@link fromBetterSqlite3}, the returned `close()` owns the handle.\r\n *\r\n * `better-sqlite3` is loaded lazily, so importing this module does not\r\n * require the optional peer dependency to be installed until this function\r\n * is actually called.\r\n */\r\nexport async function connectBetterSqlite3(\r\n filename: string,\r\n options?: BetterSqlite3.Options,\r\n): Promise<ConnectedBetterSqlite3> {\r\n const module = await import('better-sqlite3');\r\n const DatabaseConstructor = (module.default ?? module) as unknown as new (\r\n filename: string,\r\n options?: BetterSqlite3.Options,\r\n ) => Database;\r\n const database = new DatabaseConstructor(filename, options);\r\n let closed = false;\r\n return {\r\n connection: fromBetterSqlite3(database),\r\n database,\r\n close: async () => {\r\n if (!closed) {\r\n closed = true;\r\n database.close();\r\n }\r\n },\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/utils/errors.ts","../src/better-sqlite3.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 the `better-sqlite3` package.\n *\n * Wraps a caller-owned `better-sqlite3` `Database` as a\n * {@link SqliteConnection}. This module is never imported by the core\n * package; `better-sqlite3` is an optional peer dependency and is only\n * resolved when a consumer imports `dbgate-sqlite-dumper/better-sqlite3`\n * themselves.\n *\n * `better-sqlite3` is the driver DbGate's own SQLite plugin uses, which is\n * why it is the bundled adapter. Any other driver works through the same\n * {@link SqliteConnection} interface.\n */\nimport type BetterSqlite3 from 'better-sqlite3';\nimport type {\n SqliteConnection,\n SqliteErrorInfo,\n SqliteExecResult,\n SqliteParameterValue,\n SqliteQuery,\n SqliteQueryResult,\n SqliteRow,\n SqliteStreamOptions,\n} from './connection/types.js';\nimport { OperationCancelledError, throwIfAborted } from './utils/errors.js';\n\ntype Database = BetterSqlite3.Database;\ntype Statement = BetterSqlite3.Statement<unknown[], unknown>;\n\nfunction toDriverParameters(parameters: readonly SqliteParameterValue[] | undefined): unknown[] {\n return (parameters ?? []).map(value =>\n value instanceof Uint8Array && !Buffer.isBuffer(value)\n ? Buffer.from(value.buffer, value.byteOffset, value.byteLength)\n : value,\n );\n}\n\n/**\n * Prepares a statement with 64-bit integers returned as `bigint`, so no\n * catalog value is ever rounded. (Row data does not depend on this: the core\n * reads it through SQL that renders numbers as text — see `valueQuery.ts`.)\n */\nfunction prepare(database: Database, sql: string): Statement {\n const statement = database.prepare(sql) as Statement;\n statement.safeIntegers(true);\n return statement;\n}\n\nfunction isMultipleStatementsError(error: unknown): boolean {\n return error instanceof RangeError && /more than one statement/i.test(error.message);\n}\n\n/**\n * Wraps an open `better-sqlite3` database. The database is **borrowed**:\n * this package never closes it.\n *\n * `better-sqlite3` is synchronous, which shapes three behaviours:\n *\n * - `stream()` steps the statement one row at a time with `iterate()`,\n * yielding between rows, so a table of any size streams in constant\n * memory. While a stream is open the database cannot run another\n * statement — this package never asks it to.\n * - `cancel()` cannot interrupt a statement mid-step. Cancellation takes\n * effect between rows and between statements, which for a dump or a\n * restore (each statement is short) is prompt.\n * - `setDefensive()` is implemented with `unsafeMode()`, which is how\n * `better-sqlite3` exposes `SQLITE_DBCONFIG_DEFENSIVE`. (Unsafe mode also\n * relaxes the driver's own guard against writing while iterating; this\n * package never does that.)\n */\nexport function fromBetterSqlite3(database: Database): SqliteConnection {\n const connection: SqliteConnection = {\n async query<Row extends SqliteRow = SqliteRow>(\n query: SqliteQuery,\n signal?: AbortSignal,\n ): Promise<SqliteQueryResult<Row>> {\n throwIfAborted(signal);\n const statement = prepare(database, query.sql);\n const parameters = toDriverParameters(query.parameters);\n if (statement.reader) {\n const rows = statement.all(...parameters) as Row[];\n return { rows, columns: statement.columns().map(column => column.name) };\n }\n statement.run(...parameters);\n return { rows: [] };\n },\n\n stream<Row extends SqliteRow = SqliteRow>(\n query: SqliteQuery,\n options?: SqliteStreamOptions,\n ): AsyncIterable<Row> {\n const signal = options?.signal;\n return {\n async *[Symbol.asyncIterator](): AsyncGenerator<Row> {\n throwIfAborted(signal);\n const statement = prepare(database, query.sql);\n if (!statement.reader) {\n statement.run(...toDriverParameters(query.parameters));\n return;\n }\n const iterator = statement.iterate(\n ...toDriverParameters(query.parameters),\n ) as IterableIterator<Row>;\n try {\n for (const row of iterator) {\n if (signal?.aborted) {\n throw new OperationCancelledError();\n }\n yield row;\n }\n } finally {\n // Finalizes the statement even when the consumer stops early,\n // which is what frees the database for the next statement.\n iterator.return?.();\n }\n },\n };\n },\n\n async execute(sql: string, signal?: AbortSignal): Promise<SqliteExecResult> {\n throwIfAborted(signal);\n let statement: Statement;\n try {\n statement = prepare(database, sql);\n } catch (error) {\n if (!isMultipleStatementsError(error)) {\n throw error;\n }\n // Text the shell would run as several statements at once. Rare in a\n // restore (the parser splits statements), but valid input.\n const before = totalChanges(database);\n database.exec(sql);\n return { changes: totalChanges(database) - before };\n }\n if (statement.reader) {\n // A script's own SELECT or PRAGMA query: step it to completion, as\n // the shell would, without keeping the rows.\n for (const _row of statement.iterate()) {\n // discard\n }\n return { changes: 0 };\n }\n const info = statement.run();\n return { changes: Number(info.changes) };\n },\n\n describeError(error: unknown): SqliteErrorInfo | undefined {\n if (!(error instanceof Error)) {\n return undefined;\n }\n const code = (error as { code?: unknown }).code;\n return {\n ...(typeof code === 'string' ? { code } : {}),\n message: error.message,\n };\n },\n\n isInTransaction(): boolean {\n return database.inTransaction;\n },\n\n async setDefensive(enabled: boolean): Promise<void> {\n database.unsafeMode(!enabled);\n },\n\n async cancel(): Promise<void> {\n // better-sqlite3 runs each statement synchronously to completion;\n // there is nothing in flight to interrupt.\n },\n };\n return connection;\n}\n\nfunction totalChanges(database: Database): number {\n return Number(database.prepare('SELECT total_changes()').pluck().get());\n}\n\nexport interface ConnectedBetterSqlite3 {\n readonly connection: SqliteConnection;\n /** The underlying `better-sqlite3` database, for anything the adapter does not cover. */\n readonly database: Database;\n /** Closes the database. Idempotent. */\n close(): Promise<void>;\n}\n\n/**\n * Opens a database file with `better-sqlite3` and wraps it. Unlike\n * {@link fromBetterSqlite3}, the returned `close()` owns the handle.\n *\n * `better-sqlite3` is loaded lazily, so importing this module does not\n * require the optional peer dependency to be installed until this function\n * is actually called.\n */\nexport async function connectBetterSqlite3(\n filename: string,\n options?: BetterSqlite3.Options,\n): Promise<ConnectedBetterSqlite3> {\n const module = await import('better-sqlite3');\n const DatabaseConstructor = (module.default ?? module) as unknown as new (\n filename: string,\n options?: BetterSqlite3.Options,\n ) => Database;\n const database = new DatabaseConstructor(filename, options);\n let closed = false;\n return {\n connection: fromBetterSqlite3(database),\n database,\n close: async () => {\n if (!closed) {\n closed = true;\n database.close();\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
package/dist/d1.cjs ADDED
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/d1.ts
21
+ var d1_exports = {};
22
+ __export(d1_exports, {
23
+ D1Error: () => D1Error,
24
+ DEFAULT_D1_PAGE_SIZE: () => DEFAULT_D1_PAGE_SIZE,
25
+ d1ConnectionFeatures: () => d1ConnectionFeatures,
26
+ fromD1Binding: () => fromD1Binding,
27
+ fromD1Http: () => fromD1Http
28
+ });
29
+ module.exports = __toCommonJS(d1_exports);
30
+
31
+ // src/utils/errors.ts
32
+ var SqliteDumperError = class extends Error {
33
+ code;
34
+ constructor(code, message, options) {
35
+ super(message, options);
36
+ this.name = "SqliteDumperError";
37
+ this.code = code;
38
+ }
39
+ };
40
+ var OperationCancelledError = class extends SqliteDumperError {
41
+ constructor(message = "The operation was cancelled") {
42
+ super("operation-cancelled", message);
43
+ this.name = "OperationCancelledError";
44
+ }
45
+ };
46
+ function throwIfAborted(signal) {
47
+ if (signal?.aborted) {
48
+ throw new OperationCancelledError();
49
+ }
50
+ }
51
+
52
+ // src/d1.ts
53
+ var DEFAULT_D1_PAGE_SIZE = 1e3;
54
+ var DEFAULT_API_BASE_URL = "https://api.cloudflare.com/client/v4";
55
+ function d1ConnectionFeatures(pageSize = DEFAULT_D1_PAGE_SIZE) {
56
+ return {
57
+ transactions: false,
58
+ pragmaFunctions: false,
59
+ schemaQualifiedNames: false,
60
+ binaryTransport: "hex",
61
+ pagedReadSize: pageSize,
62
+ reservedNamePrefixes: ["_cf_"]
63
+ };
64
+ }
65
+ var D1Error = class extends SqliteDumperError {
66
+ /** HTTP status of the response, for the REST API. */
67
+ status;
68
+ /** SQLite's symbolic result code, when D1's message names one (`SQLITE_ERROR`). */
69
+ sqliteCode;
70
+ constructor(message, options) {
71
+ super("d1-error", message, options?.cause === void 0 ? {} : { cause: options.cause });
72
+ this.name = "D1Error";
73
+ this.status = options?.status;
74
+ this.sqliteCode = /\b(SQLITE_[A-Z_]+)\b/.exec(message)?.[1];
75
+ }
76
+ };
77
+ function toD1Parameter(value) {
78
+ if (value === null || typeof value === "string" || typeof value === "number") {
79
+ return value;
80
+ }
81
+ if (typeof value === "bigint") {
82
+ return Number.isSafeInteger(Number(value)) ? Number(value) : value.toString();
83
+ }
84
+ throw new D1Error("Binary parameters cannot be sent to D1");
85
+ }
86
+ function rowsFromArrays(columns, rows) {
87
+ return rows.map((values) => {
88
+ const row = {};
89
+ columns.forEach((column, index) => {
90
+ row[column] = values[index] ?? null;
91
+ });
92
+ return row;
93
+ });
94
+ }
95
+ function d1Connection(run, cancel, pageSize) {
96
+ return {
97
+ features: d1ConnectionFeatures(pageSize),
98
+ async query(query, signal) {
99
+ throwIfAborted(signal);
100
+ const [result] = await run([query], signal);
101
+ return result ?? { rows: [] };
102
+ },
103
+ async queryBatch(queries, signal) {
104
+ throwIfAborted(signal);
105
+ return queries.length === 0 ? [] : run(queries, signal);
106
+ },
107
+ stream(query, options) {
108
+ return {
109
+ async *[Symbol.asyncIterator]() {
110
+ throwIfAborted(options?.signal);
111
+ const [result] = await run([query], options?.signal);
112
+ for (const row of result?.rows ?? []) {
113
+ if (options?.signal?.aborted) {
114
+ throw new OperationCancelledError();
115
+ }
116
+ yield row;
117
+ }
118
+ }
119
+ };
120
+ },
121
+ describeError(error) {
122
+ if (!(error instanceof Error)) {
123
+ return void 0;
124
+ }
125
+ const code = error instanceof D1Error ? error.sqliteCode : void 0;
126
+ return { ...code === void 0 ? {} : { code }, message: error.message };
127
+ },
128
+ cancel
129
+ };
130
+ }
131
+ function envelopeError(envelope, fallback) {
132
+ const messages = (envelope?.errors ?? []).map((error) => error.message).filter((message) => typeof message === "string" && message !== "");
133
+ return messages.length > 0 ? messages.join("; ") : fallback;
134
+ }
135
+ function fromD1Http(options) {
136
+ const accountId = String(options.accountId ?? "").trim();
137
+ const databaseId = String(options.databaseId ?? "").trim();
138
+ const apiToken = String(options.apiToken ?? "").trim();
139
+ if (!accountId || !databaseId || !apiToken) {
140
+ throw new D1Error("A D1 connection needs an account ID, a database ID and an API token");
141
+ }
142
+ const doFetch = options.fetch ?? globalThis.fetch;
143
+ if (typeof doFetch !== "function") {
144
+ throw new D1Error("No fetch implementation is available; pass one as options.fetch");
145
+ }
146
+ const baseUrl = (options.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
147
+ const url = `${baseUrl}/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}/raw`;
148
+ const inFlight = /* @__PURE__ */ new Set();
149
+ const run = async (queries, signal) => {
150
+ const statements = queries.map((query) => ({
151
+ sql: query.sql,
152
+ ...query.parameters === void 0 || query.parameters.length === 0 ? {} : { params: query.parameters.map(toD1Parameter) }
153
+ }));
154
+ const body = statements.length === 1 ? statements[0] : { batch: statements };
155
+ const controller = new AbortController();
156
+ const onAbort = () => controller.abort();
157
+ signal?.addEventListener("abort", onAbort, { once: true });
158
+ inFlight.add(controller);
159
+ let response;
160
+ let text;
161
+ try {
162
+ response = await doFetch(url, {
163
+ method: "POST",
164
+ headers: {
165
+ Authorization: `Bearer ${apiToken}`,
166
+ "Content-Type": "application/json",
167
+ Accept: "application/json"
168
+ },
169
+ body: JSON.stringify(body),
170
+ signal: controller.signal
171
+ });
172
+ text = await response.text();
173
+ } catch (error) {
174
+ if (signal?.aborted || controller.signal.aborted) {
175
+ throw new OperationCancelledError();
176
+ }
177
+ throw new D1Error(
178
+ `D1 request failed: ${error instanceof Error ? error.message : String(error)}`,
179
+ {
180
+ cause: error
181
+ }
182
+ );
183
+ } finally {
184
+ signal?.removeEventListener("abort", onAbort);
185
+ inFlight.delete(controller);
186
+ }
187
+ let envelope;
188
+ try {
189
+ envelope = JSON.parse(text);
190
+ } catch {
191
+ envelope = void 0;
192
+ }
193
+ if (!response.ok || !envelope || envelope.success === false) {
194
+ throw new D1Error(
195
+ envelopeError(
196
+ envelope,
197
+ `D1 request failed with HTTP ${response.status}${envelope ? "" : " and a response that is not JSON"}`
198
+ ),
199
+ { status: response.status }
200
+ );
201
+ }
202
+ const items = envelope.result ?? [];
203
+ if (items.length !== queries.length) {
204
+ throw new D1Error(`D1 returned ${items.length} results for ${queries.length} statements`, {
205
+ status: response.status
206
+ });
207
+ }
208
+ return items.map((item) => {
209
+ if (item.success === false) {
210
+ throw new D1Error(item.error ?? "D1 reported a failed statement", {
211
+ status: response.status
212
+ });
213
+ }
214
+ const columns = item.results?.columns ?? [];
215
+ return { rows: rowsFromArrays(columns, item.results?.rows ?? []), columns };
216
+ });
217
+ };
218
+ return d1Connection(
219
+ run,
220
+ async () => {
221
+ for (const controller of inFlight) {
222
+ controller.abort();
223
+ }
224
+ },
225
+ options.pageSize ?? DEFAULT_D1_PAGE_SIZE
226
+ );
227
+ }
228
+ function fromD1Binding(database, options) {
229
+ const prepare = (query) => {
230
+ const statement = database.prepare(query.sql);
231
+ return query.parameters === void 0 || query.parameters.length === 0 ? statement : statement.bind(...query.parameters.map(toD1Parameter));
232
+ };
233
+ const run = async (queries, signal) => {
234
+ throwIfAborted(signal);
235
+ try {
236
+ if (queries.length === 1) {
237
+ const [columns, ...rows] = await prepare(queries[0]).raw({
238
+ columnNames: true
239
+ });
240
+ return [{ rows: rowsFromArrays(columns ?? [], rows), columns: columns ?? [] }];
241
+ }
242
+ const results = await database.batch(queries.map(prepare));
243
+ return results.map((result) => ({ rows: result.results ?? [] }));
244
+ } catch (error) {
245
+ if (error instanceof SqliteDumperError) throw error;
246
+ throw new D1Error(error instanceof Error ? error.message : String(error), { cause: error });
247
+ }
248
+ };
249
+ return d1Connection(run, async () => {
250
+ }, options?.pageSize ?? DEFAULT_D1_PAGE_SIZE);
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ D1Error,
255
+ DEFAULT_D1_PAGE_SIZE,
256
+ d1ConnectionFeatures,
257
+ fromD1Binding,
258
+ fromD1Http
259
+ });
260
+ //# sourceMappingURL=d1.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/d1.ts","../src/utils/errors.ts"],"sourcesContent":["/**\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","/** 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"],"mappings":"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","names":[]}
package/dist/d1.d.cts ADDED
@@ -0,0 +1,101 @@
1
+ import { a as SqliteConnectionFeatures, S as SqliteConnection } from './types-DziV5ysl.cjs';
2
+ import { S as SqliteDumperError } from './errors-CplNzuYs.cjs';
3
+
4
+ /**
5
+ * Optional adapter for Cloudflare D1.
6
+ *
7
+ * D1 is SQLite, but not a SQLite handle: it is reached over Cloudflare's REST
8
+ * API (or, inside a Worker, through a binding), every statement is a request,
9
+ * results arrive whole as JSON, and its authorizer refuses part of what an
10
+ * embedded handle allows. The adapter declares those restrictions as
11
+ * {@link SqliteConnectionFeatures}, and the core reads the database with the
12
+ * equivalent queries D1 does allow — so the dump is the same plain-SQL,
13
+ * native-`.dump`-compatible file a local copy of the database would give.
14
+ *
15
+ * Dump only. A dump's script relies on `BEGIN TRANSACTION` / `COMMIT` and
16
+ * `PRAGMA writable_schema`, which D1 refuses; to load a dump into D1, use
17
+ * `wrangler d1 execute --file` (or the D1 import API), which accept it once
18
+ * those statements are removed.
19
+ *
20
+ * Uses only the global `fetch` (or the one passed in): no dependency.
21
+ */
22
+
23
+ /** Rows per request when a table's data is read. */
24
+ declare const DEFAULT_D1_PAGE_SIZE = 1000;
25
+ /**
26
+ * What D1 does not allow, as the core understands it:
27
+ *
28
+ * - no `BEGIN` / `SAVEPOINT`, so no read snapshot;
29
+ * - no table-valued `pragma_xxx()` functions;
30
+ * - one database (`main`), and statements need not name it;
31
+ * - JSON results, so bytes are fetched as `hex()` text;
32
+ * - whole results, so table data is read in keyed pages;
33
+ * - reserved `_cf_` tables, which sit in `sqlite_schema` but cannot be read.
34
+ */
35
+ declare function d1ConnectionFeatures(pageSize?: number): SqliteConnectionFeatures;
36
+ /** An error D1 reported for a statement or a request. */
37
+ declare class D1Error extends SqliteDumperError {
38
+ /** HTTP status of the response, for the REST API. */
39
+ readonly status: number | undefined;
40
+ /** SQLite's symbolic result code, when D1's message names one (`SQLITE_ERROR`). */
41
+ readonly sqliteCode: string | undefined;
42
+ constructor(message: string, options?: {
43
+ status?: number;
44
+ cause?: unknown;
45
+ });
46
+ }
47
+ interface D1HttpOptions {
48
+ /** Cloudflare account ID. */
49
+ readonly accountId: string;
50
+ /** The database's UUID (shown by `wrangler d1 list`). */
51
+ readonly databaseId: string;
52
+ /** An API token with the `D1 Read` (or `D1 Edit`) permission. */
53
+ readonly apiToken: string;
54
+ /** Defaults to `https://api.cloudflare.com/client/v4`. */
55
+ readonly apiBaseUrl?: string;
56
+ /** Rows per request when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */
57
+ readonly pageSize?: number;
58
+ /** Defaults to the global `fetch`. */
59
+ readonly fetch?: typeof fetch;
60
+ }
61
+ /**
62
+ * A connection to a D1 database through Cloudflare's REST API — the `/raw`
63
+ * query endpoint, which returns rows as arrays. Several catalog queries go
64
+ * out as one `batch` request.
65
+ *
66
+ * ```ts
67
+ * import { dumpSqlite } from 'dbgate-sqlite-dumper';
68
+ * import { fromD1Http } from 'dbgate-sqlite-dumper/d1';
69
+ *
70
+ * const connection = fromD1Http({ accountId, databaseId, apiToken });
71
+ * await dumpSqlite(connection, {}, fs.createWriteStream('backup.sql'));
72
+ * ```
73
+ *
74
+ * The token is sent only in the `Authorization` header, and never appears in
75
+ * an error message.
76
+ */
77
+ declare function fromD1Http(options: D1HttpOptions): SqliteConnection;
78
+ /** The part of a Workers D1 binding (`env.DB`) this adapter uses. */
79
+ interface D1DatabaseBinding {
80
+ prepare(sql: string): D1PreparedStatementBinding;
81
+ batch(statements: D1PreparedStatementBinding[]): Promise<{
82
+ results?: unknown[];
83
+ }[]>;
84
+ }
85
+ interface D1PreparedStatementBinding {
86
+ bind(...values: unknown[]): D1PreparedStatementBinding;
87
+ raw(options: {
88
+ columnNames: true;
89
+ }): Promise<[string[], ...unknown[][]]>;
90
+ }
91
+ interface D1BindingOptions {
92
+ /** Rows per query when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */
93
+ readonly pageSize?: number;
94
+ }
95
+ /**
96
+ * A connection through a D1 binding, for a dump taken inside a Worker
97
+ * (`env.DB`). Needs the `nodejs_compat` compatibility flag, for `Buffer`.
98
+ */
99
+ declare function fromD1Binding(database: D1DatabaseBinding, options?: D1BindingOptions): SqliteConnection;
100
+
101
+ export { type D1BindingOptions, type D1DatabaseBinding, D1Error, type D1HttpOptions, type D1PreparedStatementBinding, DEFAULT_D1_PAGE_SIZE, d1ConnectionFeatures, fromD1Binding, fromD1Http };
package/dist/d1.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ import { a as SqliteConnectionFeatures, S as SqliteConnection } from './types-DziV5ysl.js';
2
+ import { S as SqliteDumperError } from './errors-CplNzuYs.js';
3
+
4
+ /**
5
+ * Optional adapter for Cloudflare D1.
6
+ *
7
+ * D1 is SQLite, but not a SQLite handle: it is reached over Cloudflare's REST
8
+ * API (or, inside a Worker, through a binding), every statement is a request,
9
+ * results arrive whole as JSON, and its authorizer refuses part of what an
10
+ * embedded handle allows. The adapter declares those restrictions as
11
+ * {@link SqliteConnectionFeatures}, and the core reads the database with the
12
+ * equivalent queries D1 does allow — so the dump is the same plain-SQL,
13
+ * native-`.dump`-compatible file a local copy of the database would give.
14
+ *
15
+ * Dump only. A dump's script relies on `BEGIN TRANSACTION` / `COMMIT` and
16
+ * `PRAGMA writable_schema`, which D1 refuses; to load a dump into D1, use
17
+ * `wrangler d1 execute --file` (or the D1 import API), which accept it once
18
+ * those statements are removed.
19
+ *
20
+ * Uses only the global `fetch` (or the one passed in): no dependency.
21
+ */
22
+
23
+ /** Rows per request when a table's data is read. */
24
+ declare const DEFAULT_D1_PAGE_SIZE = 1000;
25
+ /**
26
+ * What D1 does not allow, as the core understands it:
27
+ *
28
+ * - no `BEGIN` / `SAVEPOINT`, so no read snapshot;
29
+ * - no table-valued `pragma_xxx()` functions;
30
+ * - one database (`main`), and statements need not name it;
31
+ * - JSON results, so bytes are fetched as `hex()` text;
32
+ * - whole results, so table data is read in keyed pages;
33
+ * - reserved `_cf_` tables, which sit in `sqlite_schema` but cannot be read.
34
+ */
35
+ declare function d1ConnectionFeatures(pageSize?: number): SqliteConnectionFeatures;
36
+ /** An error D1 reported for a statement or a request. */
37
+ declare class D1Error extends SqliteDumperError {
38
+ /** HTTP status of the response, for the REST API. */
39
+ readonly status: number | undefined;
40
+ /** SQLite's symbolic result code, when D1's message names one (`SQLITE_ERROR`). */
41
+ readonly sqliteCode: string | undefined;
42
+ constructor(message: string, options?: {
43
+ status?: number;
44
+ cause?: unknown;
45
+ });
46
+ }
47
+ interface D1HttpOptions {
48
+ /** Cloudflare account ID. */
49
+ readonly accountId: string;
50
+ /** The database's UUID (shown by `wrangler d1 list`). */
51
+ readonly databaseId: string;
52
+ /** An API token with the `D1 Read` (or `D1 Edit`) permission. */
53
+ readonly apiToken: string;
54
+ /** Defaults to `https://api.cloudflare.com/client/v4`. */
55
+ readonly apiBaseUrl?: string;
56
+ /** Rows per request when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */
57
+ readonly pageSize?: number;
58
+ /** Defaults to the global `fetch`. */
59
+ readonly fetch?: typeof fetch;
60
+ }
61
+ /**
62
+ * A connection to a D1 database through Cloudflare's REST API — the `/raw`
63
+ * query endpoint, which returns rows as arrays. Several catalog queries go
64
+ * out as one `batch` request.
65
+ *
66
+ * ```ts
67
+ * import { dumpSqlite } from 'dbgate-sqlite-dumper';
68
+ * import { fromD1Http } from 'dbgate-sqlite-dumper/d1';
69
+ *
70
+ * const connection = fromD1Http({ accountId, databaseId, apiToken });
71
+ * await dumpSqlite(connection, {}, fs.createWriteStream('backup.sql'));
72
+ * ```
73
+ *
74
+ * The token is sent only in the `Authorization` header, and never appears in
75
+ * an error message.
76
+ */
77
+ declare function fromD1Http(options: D1HttpOptions): SqliteConnection;
78
+ /** The part of a Workers D1 binding (`env.DB`) this adapter uses. */
79
+ interface D1DatabaseBinding {
80
+ prepare(sql: string): D1PreparedStatementBinding;
81
+ batch(statements: D1PreparedStatementBinding[]): Promise<{
82
+ results?: unknown[];
83
+ }[]>;
84
+ }
85
+ interface D1PreparedStatementBinding {
86
+ bind(...values: unknown[]): D1PreparedStatementBinding;
87
+ raw(options: {
88
+ columnNames: true;
89
+ }): Promise<[string[], ...unknown[][]]>;
90
+ }
91
+ interface D1BindingOptions {
92
+ /** Rows per query when table data is read. Defaults to {@link DEFAULT_D1_PAGE_SIZE}. */
93
+ readonly pageSize?: number;
94
+ }
95
+ /**
96
+ * A connection through a D1 binding, for a dump taken inside a Worker
97
+ * (`env.DB`). Needs the `nodejs_compat` compatibility flag, for `Buffer`.
98
+ */
99
+ declare function fromD1Binding(database: D1DatabaseBinding, options?: D1BindingOptions): SqliteConnection;
100
+
101
+ export { type D1BindingOptions, type D1DatabaseBinding, D1Error, type D1HttpOptions, type D1PreparedStatementBinding, DEFAULT_D1_PAGE_SIZE, d1ConnectionFeatures, fromD1Binding, fromD1Http };