@rindle/sql-client 0.6.4
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 +201 -0
- package/README.md +61 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1116 -0
- package/dist/client.js.map +1 -0
- package/dist/drizzle.d.ts +49 -0
- package/dist/drizzle.d.ts.map +1 -0
- package/dist/drizzle.js +180 -0
- package/dist/drizzle.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +33 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.d.ts +5 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +23 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/value.d.ts +8 -0
- package/dist/value.d.ts.map +1 -0
- package/dist/value.js +122 -0
- package/dist/value.js.map +1 -0
- package/package.json +51 -0
- package/src/client.ts +1160 -0
- package/src/drizzle.ts +257 -0
- package/src/errors.ts +51 -0
- package/src/id.ts +25 -0
- package/src/index.ts +48 -0
- package/src/types.ts +234 -0
- package/src/value.ts +112 -0
package/src/drizzle.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { createSqlClient } from "./client.ts";
|
|
2
|
+
import { RindleSqlError, valueUnsupported } from "./errors.ts";
|
|
3
|
+
import type {
|
|
4
|
+
ClientOptions,
|
|
5
|
+
SqlClient,
|
|
6
|
+
SqlTransaction,
|
|
7
|
+
SqlValue,
|
|
8
|
+
Statement,
|
|
9
|
+
StatementResult,
|
|
10
|
+
} from "./types.ts";
|
|
11
|
+
|
|
12
|
+
/** The small, structural statement shape consumed by drizzle-orm/libsql. */
|
|
13
|
+
export interface DrizzleStatement {
|
|
14
|
+
sql: string;
|
|
15
|
+
args?: DrizzleArgs;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type DrizzleArgs = readonly unknown[] | Readonly<Record<string, unknown>>;
|
|
19
|
+
export type DrizzleInputStatement = string | DrizzleStatement | readonly [sql: string, args?: DrizzleArgs];
|
|
20
|
+
export type DrizzleTransactionMode = "write" | "read" | "deferred";
|
|
21
|
+
|
|
22
|
+
// Booleans are a bind convenience only; SQLite result storage classes never decode to boolean.
|
|
23
|
+
export type DrizzleValue = Exclude<SqlValue, boolean | bigint>;
|
|
24
|
+
export type DrizzleRow = DrizzleValue[] & Record<string, DrizzleValue>;
|
|
25
|
+
|
|
26
|
+
export interface DrizzleResultSet {
|
|
27
|
+
columns: string[];
|
|
28
|
+
columnTypes: string[];
|
|
29
|
+
rows: DrizzleRow[];
|
|
30
|
+
rowsAffected: number;
|
|
31
|
+
lastInsertRowid: bigint | undefined;
|
|
32
|
+
toJSON(): unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DrizzleTransaction {
|
|
36
|
+
readonly closed: boolean;
|
|
37
|
+
execute(statement: DrizzleInputStatement): Promise<DrizzleResultSet>;
|
|
38
|
+
batch(statements: DrizzleInputStatement[]): Promise<DrizzleResultSet[]>;
|
|
39
|
+
executeMultiple(sql: string): Promise<void>;
|
|
40
|
+
commit(): Promise<void>;
|
|
41
|
+
rollback(): Promise<void>;
|
|
42
|
+
close(): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DrizzleClient {
|
|
46
|
+
readonly protocol: string;
|
|
47
|
+
readonly closed: boolean;
|
|
48
|
+
execute(statement: DrizzleInputStatement, args?: DrizzleArgs): Promise<DrizzleResultSet>;
|
|
49
|
+
batch(statements: DrizzleInputStatement[], mode?: DrizzleTransactionMode): Promise<DrizzleResultSet[]>;
|
|
50
|
+
/** Present for structural Client typing; Drizzle's libSQL migrator is deliberately unsupported. */
|
|
51
|
+
migrate(statements: DrizzleInputStatement[]): Promise<DrizzleResultSet[]>;
|
|
52
|
+
transaction(mode?: DrizzleTransactionMode): Promise<DrizzleTransaction>;
|
|
53
|
+
executeMultiple(sql: string): Promise<void>;
|
|
54
|
+
/** Embedded-replica sync is deliberately unsupported. */
|
|
55
|
+
sync(): Promise<never>;
|
|
56
|
+
reconnect(): void;
|
|
57
|
+
close(): void;
|
|
58
|
+
/** Escape hatch for Rindle-specific operations and session-cursor persistence. */
|
|
59
|
+
readonly rindle: SqlClient;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toNativeStatement(input: DrizzleInputStatement, args?: DrizzleArgs): Statement | string {
|
|
63
|
+
if (typeof input === "string") return args === undefined ? input : { sql: input, args: args as Statement["args"] };
|
|
64
|
+
if (Array.isArray(input)) return { sql: input[0], args: input[1] as Statement["args"] };
|
|
65
|
+
return {
|
|
66
|
+
sql: (input as DrizzleStatement).sql,
|
|
67
|
+
// The native codec performs the deliberate runtime refusal for binary/unsupported values.
|
|
68
|
+
args: (input as DrizzleStatement).args as Statement["args"],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function defineNamedCell(row: DrizzleRow, name: string, value: DrizzleValue): void {
|
|
73
|
+
// Array length and numeric aliases overlap the positional row representation; positions win.
|
|
74
|
+
const numericIndex = Number(name);
|
|
75
|
+
if (
|
|
76
|
+
name === "length" ||
|
|
77
|
+
(Number.isInteger(numericIndex) && numericIndex >= 0 && numericIndex < 0xffff_ffff && String(numericIndex) === name)
|
|
78
|
+
) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
Object.defineProperty(row, name, { value, writable: true, enumerable: true, configurable: true });
|
|
83
|
+
} catch {
|
|
84
|
+
// An exotic/non-configurable property name cannot be represented in the hybrid row.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toDrizzleValue(value: SqlValue): DrizzleValue {
|
|
89
|
+
if (typeof value === "bigint") {
|
|
90
|
+
const number = Number(value);
|
|
91
|
+
if (!Number.isSafeInteger(number)) {
|
|
92
|
+
throw valueUnsupported(`integer ${value.toString()} is outside Number's safe integer range required by Drizzle`);
|
|
93
|
+
}
|
|
94
|
+
return number;
|
|
95
|
+
}
|
|
96
|
+
// The native decoder never produces booleans, but normalize a structurally supplied result too.
|
|
97
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Convert Rindle's lossless positional result into libSQL's positional + named hybrid rows. */
|
|
102
|
+
export function toDrizzleResultSet(result: StatementResult): DrizzleResultSet {
|
|
103
|
+
const columns = result.columns.map((column) => column.name);
|
|
104
|
+
const columnTypes = result.columns.map((column) => column.decltype ?? "");
|
|
105
|
+
const rows = result.rows.map((cells) => {
|
|
106
|
+
const row = cells.map(toDrizzleValue) as DrizzleRow;
|
|
107
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
108
|
+
defineNamedCell(row, columns[index]!, row[index]!);
|
|
109
|
+
}
|
|
110
|
+
return row;
|
|
111
|
+
});
|
|
112
|
+
const converted: DrizzleResultSet = {
|
|
113
|
+
columns,
|
|
114
|
+
columnTypes,
|
|
115
|
+
rows,
|
|
116
|
+
rowsAffected: result.rowsAffected,
|
|
117
|
+
lastInsertRowid: result.lastInsertRowid === null ? undefined : BigInt(result.lastInsertRowid),
|
|
118
|
+
toJSON() {
|
|
119
|
+
return {
|
|
120
|
+
columns: this.columns,
|
|
121
|
+
columnTypes: this.columnTypes,
|
|
122
|
+
rows: this.rows,
|
|
123
|
+
rowsAffected: this.rowsAffected,
|
|
124
|
+
lastInsertRowid: this.lastInsertRowid?.toString(),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
return converted;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
class TransactionFacade implements DrizzleTransaction {
|
|
132
|
+
private isClosed = false;
|
|
133
|
+
private readonly tx: SqlTransaction;
|
|
134
|
+
|
|
135
|
+
constructor(tx: SqlTransaction) {
|
|
136
|
+
this.tx = tx;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get closed(): boolean {
|
|
140
|
+
return this.isClosed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async execute(statement: DrizzleInputStatement): Promise<DrizzleResultSet> {
|
|
144
|
+
return toDrizzleResultSet(await this.tx.execute(toNativeStatement(statement)));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async batch(statements: DrizzleInputStatement[]): Promise<DrizzleResultSet[]> {
|
|
148
|
+
const native = statements.map((statement) => {
|
|
149
|
+
const converted = toNativeStatement(statement);
|
|
150
|
+
return typeof converted === "string" ? { sql: converted } : converted;
|
|
151
|
+
});
|
|
152
|
+
return (await this.tx.batch(native)).map(toDrizzleResultSet);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async executeMultiple(_sql: string): Promise<void> {
|
|
156
|
+
throw new RindleSqlError({
|
|
157
|
+
code: "STATEMENT_UNSUPPORTED",
|
|
158
|
+
message: "executeMultiple is not supported inside a Rindle interactive transaction; use batch()",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async commit(): Promise<void> {
|
|
163
|
+
if (this.isClosed) return;
|
|
164
|
+
await this.tx.commit();
|
|
165
|
+
this.isClosed = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async rollback(): Promise<void> {
|
|
169
|
+
if (this.isClosed) return;
|
|
170
|
+
await this.tx.rollback();
|
|
171
|
+
this.isClosed = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
close(): void {
|
|
175
|
+
if (this.isClosed) return;
|
|
176
|
+
this.isClosed = true;
|
|
177
|
+
void this.tx.rollback().catch(() => {});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
class ClientFacade implements DrizzleClient {
|
|
182
|
+
readonly rindle: SqlClient;
|
|
183
|
+
readonly protocol = "http";
|
|
184
|
+
private isClosed = false;
|
|
185
|
+
|
|
186
|
+
constructor(rindle: SqlClient) {
|
|
187
|
+
this.rindle = rindle;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get closed(): boolean {
|
|
191
|
+
return this.isClosed;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async execute(statement: DrizzleInputStatement, args?: DrizzleArgs): Promise<DrizzleResultSet> {
|
|
195
|
+
return toDrizzleResultSet((await this.rindle.execute(toNativeStatement(statement, args))).result);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async batch(statements: DrizzleInputStatement[], _mode?: DrizzleTransactionMode): Promise<DrizzleResultSet[]> {
|
|
199
|
+
const native = statements.map((statement) => {
|
|
200
|
+
const converted = toNativeStatement(statement);
|
|
201
|
+
return typeof converted === "string" ? { sql: converted } : converted;
|
|
202
|
+
});
|
|
203
|
+
return (await this.rindle.batch(native)).results.map(toDrizzleResultSet);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async transaction(mode: DrizzleTransactionMode = "write"): Promise<DrizzleTransaction> {
|
|
207
|
+
if (mode !== "write" && mode !== "read" && mode !== "deferred") {
|
|
208
|
+
throw new TypeError(`unsupported transaction mode: ${String(mode)}`);
|
|
209
|
+
}
|
|
210
|
+
return new TransactionFacade(await this.rindle.begin({ readOnly: mode === "read" }));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
executeMultiple(sql: string): Promise<void> {
|
|
214
|
+
return this.rindle.executeMultiple(sql);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async migrate(_statements: DrizzleInputStatement[]): Promise<DrizzleResultSet[]> {
|
|
218
|
+
throw new RindleSqlError({
|
|
219
|
+
code: "MIGRATOR_UNSUPPORTED",
|
|
220
|
+
message: "drizzle-orm/libsql/migrator is not supported; apply declared migrations with SqlClient.migrate()",
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async sync(): Promise<never> {
|
|
225
|
+
throw new RindleSqlError({
|
|
226
|
+
code: "SYNC_UNSUPPORTED",
|
|
227
|
+
message: "embedded-replica sync is not supported by Rindle SQL",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
reconnect(): void {
|
|
232
|
+
throw new RindleSqlError({
|
|
233
|
+
code: "RECONNECT_UNSUPPORTED",
|
|
234
|
+
message: "a closed Rindle SQL client cannot be reopened; create a new client",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
close(): void {
|
|
239
|
+
if (this.isClosed) return;
|
|
240
|
+
this.isClosed = true;
|
|
241
|
+
this.rindle.close();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Create the structural libSQL-client facade used by drizzle-orm/libsql. */
|
|
246
|
+
export function createDrizzleClient(options: ClientOptions | SqlClient): DrizzleClient {
|
|
247
|
+
// drizzle-orm's SQLite integer mappers consume numbers (including timestamp multiplication).
|
|
248
|
+
// Keep its structural libSQL seam on that policy even though the direct Rindle client defaults
|
|
249
|
+
// to lossless bigint results. toDrizzleResultSet also normalizes an injected SqlClient.
|
|
250
|
+
const client = isSqlClient(options) ? options : createSqlClient({ ...options, intMode: "number" });
|
|
251
|
+
return new ClientFacade(client);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function isSqlClient(value: ClientOptions | SqlClient): value is SqlClient {
|
|
255
|
+
const candidate = value as Partial<SqlClient>;
|
|
256
|
+
return typeof candidate.execute === "function" && typeof candidate.close === "function";
|
|
257
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RetryScope, StatementResult, TransactionState } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
export interface RindleSqlErrorOptions {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
sqliteCode?: number;
|
|
7
|
+
retryScope?: RetryScope;
|
|
8
|
+
transactionState?: TransactionState;
|
|
9
|
+
status?: number;
|
|
10
|
+
requestId?: string;
|
|
11
|
+
statementIndex?: number;
|
|
12
|
+
partialResults?: StatementResult[];
|
|
13
|
+
cause?: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Stable error surfaced for both server envelopes and client-side contract refusals. */
|
|
17
|
+
export class RindleSqlError extends Error {
|
|
18
|
+
readonly code: string;
|
|
19
|
+
readonly sqliteCode: number | undefined;
|
|
20
|
+
readonly retryScope: RetryScope;
|
|
21
|
+
readonly transactionState: TransactionState | undefined;
|
|
22
|
+
readonly status: number | undefined;
|
|
23
|
+
readonly requestId: string | undefined;
|
|
24
|
+
readonly statementIndex: number | undefined;
|
|
25
|
+
readonly partialResults: StatementResult[] | undefined;
|
|
26
|
+
|
|
27
|
+
constructor(options: RindleSqlErrorOptions) {
|
|
28
|
+
super(options.message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
29
|
+
this.name = "RindleSqlError";
|
|
30
|
+
this.code = options.code;
|
|
31
|
+
this.sqliteCode = options.sqliteCode;
|
|
32
|
+
this.retryScope = options.retryScope ?? "never";
|
|
33
|
+
this.transactionState = options.transactionState;
|
|
34
|
+
this.status = options.status;
|
|
35
|
+
this.requestId = options.requestId;
|
|
36
|
+
this.statementIndex = options.statementIndex;
|
|
37
|
+
this.partialResults = options.partialResults;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isRindleSqlError(error: unknown): error is RindleSqlError {
|
|
42
|
+
return error instanceof RindleSqlError;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function valueUnsupported(message: string): RindleSqlError {
|
|
46
|
+
return new RindleSqlError({ code: "VALUE_UNSUPPORTED", message, retryScope: "never" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function protocolError(message: string, cause?: unknown): RindleSqlError {
|
|
50
|
+
return new RindleSqlError({ code: "PROTOCOL_ERROR", message, retryScope: "never", cause });
|
|
51
|
+
}
|
package/src/id.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function randomBytes(): Uint8Array {
|
|
2
|
+
const crypto = globalThis.crypto;
|
|
3
|
+
if (typeof crypto?.getRandomValues !== "function") {
|
|
4
|
+
throw new TypeError("Rindle SQL requires Web Crypto getRandomValues for request identities");
|
|
5
|
+
}
|
|
6
|
+
return crypto.getRandomValues(new Uint8Array(16));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hexBytes(bytes: Uint8Array): string {
|
|
10
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Runtime-portable one-shot idempotency key; its wire format is intentionally centralized here. */
|
|
14
|
+
export function newIdempotencyKey(): string {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
if (!Number.isSafeInteger(now) || now < 0 || now > 9_999_999_999_999) {
|
|
17
|
+
throw new TypeError("system clock is outside the canonical SQL idempotency-key range");
|
|
18
|
+
}
|
|
19
|
+
return `sql1.${now.toString().padStart(13, "0")}.${hexBytes(randomBytes())}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One canonical design-225 logical request id backed by 128 cryptographically random bits. */
|
|
23
|
+
export function newRequestId(): string {
|
|
24
|
+
return `rid1.${hexBytes(randomBytes())}`;
|
|
25
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export { createSqlClient } from "./client.ts";
|
|
2
|
+
export { RindleSqlError, isRindleSqlError } from "./errors.ts";
|
|
3
|
+
export { decodeSqlValue, encodeArgs, encodeSqlValue, encodeStatement } from "./value.ts";
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
BatchOptions,
|
|
7
|
+
BatchResult,
|
|
8
|
+
BeginMutationInput,
|
|
9
|
+
BeginMutationResult,
|
|
10
|
+
ClientOptions,
|
|
11
|
+
Column,
|
|
12
|
+
ExecuteMutationInput,
|
|
13
|
+
ExecuteOptions,
|
|
14
|
+
ExecuteResult,
|
|
15
|
+
Fetch,
|
|
16
|
+
IntMode,
|
|
17
|
+
MigrationInput,
|
|
18
|
+
MigrationResult,
|
|
19
|
+
MutationIdentity,
|
|
20
|
+
MutationReceipt,
|
|
21
|
+
MutationRows,
|
|
22
|
+
OperationOptions,
|
|
23
|
+
ReadConsistency,
|
|
24
|
+
RejectMutationInput,
|
|
25
|
+
RetryOptions,
|
|
26
|
+
RetryScope,
|
|
27
|
+
RoutingMetadata,
|
|
28
|
+
SqlArgs,
|
|
29
|
+
SqlClient,
|
|
30
|
+
SqlMutationTransaction,
|
|
31
|
+
SqlSession,
|
|
32
|
+
SqlTransaction,
|
|
33
|
+
SqlValue,
|
|
34
|
+
Statement,
|
|
35
|
+
StatementResult,
|
|
36
|
+
TransactionOptions,
|
|
37
|
+
TransactionState,
|
|
38
|
+
WireArgs,
|
|
39
|
+
WireBatchResponse,
|
|
40
|
+
WireColumn,
|
|
41
|
+
WireExecuteResponse,
|
|
42
|
+
WireFloat,
|
|
43
|
+
WireI64,
|
|
44
|
+
WireRoutingMetadata,
|
|
45
|
+
WireSqlValue,
|
|
46
|
+
WireStatement,
|
|
47
|
+
WireStatementResult,
|
|
48
|
+
} from "./types.ts";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/** A value accepted by Rindle SQL's v1 client. Binary values are deliberately unsupported. */
|
|
2
|
+
export type SqlValue = null | string | number | bigint | boolean;
|
|
3
|
+
|
|
4
|
+
export type SqlArgs = readonly SqlValue[] | Readonly<Record<string, SqlValue>>;
|
|
5
|
+
|
|
6
|
+
export interface Statement {
|
|
7
|
+
sql: string;
|
|
8
|
+
args?: SqlArgs;
|
|
9
|
+
wantRows?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Column {
|
|
13
|
+
name: string;
|
|
14
|
+
decltype: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface StatementResult {
|
|
18
|
+
columns: Column[];
|
|
19
|
+
rows: SqlValue[][];
|
|
20
|
+
rowsAffected: number;
|
|
21
|
+
lastInsertRowid: string | null;
|
|
22
|
+
rowsRead: number | null;
|
|
23
|
+
rowsWritten: number | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RoutingMetadata {
|
|
27
|
+
servedBy: "master" | "follower";
|
|
28
|
+
appliedLagMs: number | null;
|
|
29
|
+
fenceFallback: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ExecuteResult {
|
|
33
|
+
result: StatementResult;
|
|
34
|
+
commitCursor: string | null;
|
|
35
|
+
routing: RoutingMetadata;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BatchResult {
|
|
39
|
+
results: StatementResult[];
|
|
40
|
+
commitCursor: string | null;
|
|
41
|
+
routing: RoutingMetadata;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface MigrationInput {
|
|
45
|
+
id: string;
|
|
46
|
+
checksum: string;
|
|
47
|
+
statements: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface MigrationResult {
|
|
51
|
+
applied: boolean;
|
|
52
|
+
commitCursor: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type ReadConsistency = "session" | "strong" | "eventual";
|
|
56
|
+
export type IntMode = "bigint" | "number" | "string";
|
|
57
|
+
export type RetryScope = "request" | "transaction" | "closure" | "never";
|
|
58
|
+
export type TransactionState = "open" | "closed" | "unknown";
|
|
59
|
+
|
|
60
|
+
export interface OperationOptions {
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ExecuteOptions extends OperationOptions {
|
|
65
|
+
consistency?: ReadConsistency;
|
|
66
|
+
sessionCursor?: string | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type BatchOptions = ExecuteOptions;
|
|
70
|
+
|
|
71
|
+
export interface TransactionOptions extends OperationOptions {
|
|
72
|
+
readOnly?: boolean;
|
|
73
|
+
isolation?: "serializable" | "snapshot";
|
|
74
|
+
consistency?: ReadConsistency;
|
|
75
|
+
sessionCursor?: string | null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The optimistic mutation whose effects a mutation-aware SQL transaction commits. `mid` is the
|
|
79
|
+
* requested mutation id; `lmid` is server-owned state and therefore only appears in receipts. */
|
|
80
|
+
export interface MutationIdentity {
|
|
81
|
+
clientId: string;
|
|
82
|
+
mid: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The authoritative outcome of a mutation commit or lmid-only rejection. */
|
|
86
|
+
export interface MutationReceipt {
|
|
87
|
+
applied: boolean;
|
|
88
|
+
lmid: number;
|
|
89
|
+
commitCursor: string | null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Positional rows returned by a read inside an open mutation transaction. */
|
|
93
|
+
export interface MutationRows {
|
|
94
|
+
columns: string[];
|
|
95
|
+
rows: SqlValue[][];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ExecuteMutationInput extends MutationIdentity {
|
|
99
|
+
/** May be empty: an accepted no-op must still advance lmid and retire the prediction. */
|
|
100
|
+
statements: Statement[];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface BeginMutationInput extends MutationIdentity {
|
|
104
|
+
/** A pure-write prefix accumulated before the mutator's first read. */
|
|
105
|
+
statements?: Statement[];
|
|
106
|
+
/** Optional first read, coalesced with begin to preserve the mutation fast path. */
|
|
107
|
+
query?: Statement | string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface RejectMutationInput extends MutationIdentity {
|
|
111
|
+
reason?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface SqlMutationTransaction {
|
|
115
|
+
execute(statement: Statement | string, options?: OperationOptions): Promise<void>;
|
|
116
|
+
batch(statements: Statement[], options?: OperationOptions): Promise<void>;
|
|
117
|
+
query(statement: Statement | string, options?: OperationOptions): Promise<MutationRows>;
|
|
118
|
+
commit(options?: OperationOptions): Promise<MutationReceipt>;
|
|
119
|
+
rollback(options?: OperationOptions): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Begin-time mid dedup can absorb a replay without opening a transaction; callers must skip the
|
|
123
|
+
* mutator body in that branch. */
|
|
124
|
+
export type BeginMutationResult =
|
|
125
|
+
| { absorbed: true; receipt: MutationReceipt }
|
|
126
|
+
| { absorbed: false; transaction: SqlMutationTransaction; read?: MutationRows };
|
|
127
|
+
|
|
128
|
+
export interface RetryOptions extends TransactionOptions {
|
|
129
|
+
/** Total closure attempts, including the first. Defaults to 5. */
|
|
130
|
+
maxAttempts?: number;
|
|
131
|
+
/** Initial closure-retry delay. Defaults to 10ms. */
|
|
132
|
+
baseDelayMs?: number;
|
|
133
|
+
/** Maximum closure-retry delay. Defaults to 250ms. */
|
|
134
|
+
maxDelayMs?: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Portable subset of the WHATWG fetch signature used by the client. Keeping the input to the
|
|
138
|
+
* URL forms we actually emit avoids leaking the DOM-only `RequestInfo` alias into Node/Worker
|
|
139
|
+
* consumers that compile this package from source. */
|
|
140
|
+
export type Fetch = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
141
|
+
|
|
142
|
+
export interface ClientOptions {
|
|
143
|
+
url: string;
|
|
144
|
+
authToken: string;
|
|
145
|
+
consistency?: ReadConsistency;
|
|
146
|
+
sessionCursor?: string | null;
|
|
147
|
+
intMode?: IntMode;
|
|
148
|
+
/** Test/service-binding seam. The default is the runtime's global fetch. */
|
|
149
|
+
fetch?: Fetch;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface SqlTransaction {
|
|
153
|
+
execute(statement: Statement | string, options?: OperationOptions): Promise<StatementResult>;
|
|
154
|
+
batch(statements: Statement[], options?: OperationOptions): Promise<StatementResult[]>;
|
|
155
|
+
commit(options?: OperationOptions): Promise<{ commitCursor: string | null }>;
|
|
156
|
+
rollback(options?: OperationOptions): Promise<void>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface SqlSession {
|
|
160
|
+
execute(statement: Statement | string, options?: ExecuteOptions): Promise<ExecuteResult>;
|
|
161
|
+
batch(statements: Statement[], options?: BatchOptions): Promise<BatchResult>;
|
|
162
|
+
begin(options?: TransactionOptions): Promise<SqlTransaction>;
|
|
163
|
+
withTransaction<T>(fn: (tx: SqlTransaction) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
164
|
+
withTransactionRetry<T>(fn: (tx: SqlTransaction) => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
165
|
+
/** One-round-trip optimistic mutation commit (effects + lmid in the same atomic unit). */
|
|
166
|
+
executeMutation(input: ExecuteMutationInput, options?: OperationOptions): Promise<MutationReceipt>;
|
|
167
|
+
/** Open an interactive optimistic mutation transaction, or absorb a replay before it runs. */
|
|
168
|
+
beginMutation(input: BeginMutationInput, options?: OperationOptions): Promise<BeginMutationResult>;
|
|
169
|
+
/** Process a business/pre-flight rejection as an lmid-only commit. */
|
|
170
|
+
rejectMutation(input: RejectMutationInput, options?: OperationOptions): Promise<MutationReceipt>;
|
|
171
|
+
executeDdl(sql: string, options?: OperationOptions): Promise<ExecuteResult>;
|
|
172
|
+
migrate(input: MigrationInput, options?: OperationOptions): Promise<MigrationResult>;
|
|
173
|
+
executeMultiple(sql: string, options?: OperationOptions): Promise<void>;
|
|
174
|
+
session(cursor?: string | null): SqlSession;
|
|
175
|
+
getSessionCursor(): string | null;
|
|
176
|
+
resetSessionCursor(): void;
|
|
177
|
+
ping(options?: OperationOptions): Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface SqlClient extends SqlSession {
|
|
181
|
+
close(): void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Frozen v1 JSON value and statement DTOs.
|
|
185
|
+
export interface WireI64 {
|
|
186
|
+
$rindle: "i64";
|
|
187
|
+
value: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface WireFloat {
|
|
191
|
+
$rindle: "float";
|
|
192
|
+
value: "Infinity" | "-Infinity";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type WireSqlValue = null | string | number | WireI64 | WireFloat;
|
|
196
|
+
export type WireArgs = WireSqlValue[] | Record<string, WireSqlValue>;
|
|
197
|
+
|
|
198
|
+
export interface WireStatement {
|
|
199
|
+
sql: string;
|
|
200
|
+
args?: WireArgs;
|
|
201
|
+
want_rows?: boolean;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface WireColumn {
|
|
205
|
+
name: string;
|
|
206
|
+
decltype: string | null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface WireStatementResult {
|
|
210
|
+
columns: WireColumn[];
|
|
211
|
+
rows: WireSqlValue[][];
|
|
212
|
+
rows_affected: number;
|
|
213
|
+
last_insert_rowid: string | null;
|
|
214
|
+
rows_read: number | null;
|
|
215
|
+
rows_written: number | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface WireRoutingMetadata {
|
|
219
|
+
served_by: "master" | "follower";
|
|
220
|
+
applied_lag_ms: number | null;
|
|
221
|
+
fence_fallback: boolean;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface WireExecuteResponse {
|
|
225
|
+
result: WireStatementResult;
|
|
226
|
+
commit_cursor: string | null;
|
|
227
|
+
routing: WireRoutingMetadata;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface WireBatchResponse {
|
|
231
|
+
results: WireStatementResult[];
|
|
232
|
+
commit_cursor: string | null;
|
|
233
|
+
routing: WireRoutingMetadata;
|
|
234
|
+
}
|