@terrascale/sdk 0.0.4 → 0.0.6
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/CHANGELOG.md +23 -0
- package/README.md +62 -1
- package/package.json +1 -1
- package/sdk-public-v1-route-manifest.json +10 -1
- package/src/index.js +17 -0
- package/src/v1/catalog.js +30 -0
- package/src/v1/change-webhooks.js +78 -0
- package/src/v1/decode.js +1 -0
- package/src/v1/errors.js +26 -0
- package/src/v1/export.js +68 -0
- package/src/v1/management-routes.js +18 -5
- package/src/v1/shape-reader.js +7 -5
- package/src/v1/sql-execute.js +132 -0
- package/src/v1/terrabase-v1.js +92 -4
- package/src/v1/transport.js +48 -8
- package/types/index.d.ts +17 -1
- package/types/v1/catalog.d.ts +12 -0
- package/types/v1/change-webhooks.d.ts +59 -0
- package/types/v1/decode.d.ts +5 -0
- package/types/v1/errors.d.ts +18 -0
- package/types/v1/export.d.ts +26 -0
- package/types/v1/management-routes.d.ts +1 -1
- package/types/v1/shape-reader.d.ts +1 -1
- package/types/v1/sql-execute.d.ts +61 -0
- package/types/v1/terrabase-v1.d.ts +53 -2
- package/types/v1/transport.d.ts +10 -4
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { array, malformed, object, string } from "./decode.js";
|
|
2
|
+
import { requireName } from "./ids.js";
|
|
3
|
+
import { ExactDecimal, stringifyExactJson } from "./json.js";
|
|
4
|
+
/**
|
|
5
|
+
* @import { JsonValue } from "./json.js"
|
|
6
|
+
* @import { V1RequestOptions } from "./terrabase-v1.js"
|
|
7
|
+
* @import { V1Transport } from "./transport.js"
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An argument: null, a finite number, an int64 `bigint` or ExactDecimal, a boolean (stored as 1 or 0) or a string.
|
|
12
|
+
* @typedef {null | boolean | number | bigint | ExactDecimal | string} V1SqlArgument
|
|
13
|
+
*/
|
|
14
|
+
/** @typedef {readonly V1SqlArgument[] | Readonly<Record<string, V1SqlArgument>>} V1SqlArguments Positional array, or named values keyed without their `:`, `@` or `$` prefix. */
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} V1SqlStatement
|
|
17
|
+
* @property {string} sql
|
|
18
|
+
* @property {readonly V1SqlArgument[] | undefined} [args] Positional arguments.
|
|
19
|
+
* @property {Readonly<Record<string, V1SqlArgument>> | undefined} [named] Named arguments, keys without their prefix.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* A result value. Integers beyond 2^53 decode as ExactDecimal, keeping every digit; `BigInt(String(value))` converts one.
|
|
23
|
+
* @typedef {null | number | ExactDecimal | string} V1SqlValue
|
|
24
|
+
*/
|
|
25
|
+
/** @typedef {{ readonly name: string; readonly decltype: string | null }} V1SqlColumn A column; `decltype` is null for an expression. */
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} V1SqlExecuteResult
|
|
28
|
+
* @property {readonly V1SqlColumn[]} columns
|
|
29
|
+
* @property {readonly (readonly V1SqlValue[])[]} rows One value per column.
|
|
30
|
+
* @property {number} rowsAffected
|
|
31
|
+
* @property {number | ExactDecimal | null} lastInsertRowid
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const maximumStatements = 256;
|
|
35
|
+
const maximumSqlLength = 16_384;
|
|
36
|
+
|
|
37
|
+
/** @param {unknown} value @param {string} where */
|
|
38
|
+
function requireArgument(value, where) {
|
|
39
|
+
if (value === null || typeof value === "boolean" || typeof value === "string" || value instanceof ExactDecimal) return;
|
|
40
|
+
if (typeof value === "number" && Number.isFinite(value)) return;
|
|
41
|
+
if (typeof value === "bigint" && BigInt.asIntN(64, value) === value) return;
|
|
42
|
+
throw new TypeError(`SQL ${where} is null, a finite number, an int64 bigint, a boolean or a string.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @param {unknown} statement @param {number} index */
|
|
46
|
+
function encodeStatement(statement, index) {
|
|
47
|
+
if (typeof statement !== "string" && (typeof statement !== "object" || statement === null || Array.isArray(statement)))
|
|
48
|
+
throw new TypeError(`SQL statement ${index} is a string or { sql, args, named }.`);
|
|
49
|
+
const { sql, args, named, ...extra } = /** @type {V1SqlStatement} */ (typeof statement === "string" ? { sql: statement } : statement);
|
|
50
|
+
for (const member of Object.keys(extra)) throw new TypeError(`Unknown SQL statement member ${member}.`);
|
|
51
|
+
if (typeof sql !== "string" || sql.length === 0 || sql.length > maximumSqlLength)
|
|
52
|
+
throw new TypeError(`SQL statement ${index} is a non-empty string of at most ${maximumSqlLength} UTF-16 code units.`);
|
|
53
|
+
/** @type {{ sql: string; args?: V1SqlArgument[]; named?: Record<string, V1SqlArgument> }} */
|
|
54
|
+
const encoded = { sql };
|
|
55
|
+
if (args !== undefined) {
|
|
56
|
+
if (!Array.isArray(args)) throw new TypeError(`SQL statement ${index} args is an array.`);
|
|
57
|
+
args.forEach((arg, position) => requireArgument(arg, `argument ${position}`));
|
|
58
|
+
encoded.args = [...args];
|
|
59
|
+
}
|
|
60
|
+
if (named !== undefined) {
|
|
61
|
+
if (named === null || typeof named !== "object" || Array.isArray(named)) throw new TypeError(`SQL statement ${index} named is an object.`);
|
|
62
|
+
for (const [name, value] of Object.entries(named)) {
|
|
63
|
+
if (/^[:@$]/u.test(name)) throw new TypeError(`SQL named argument ${name} is keyed without its prefix.`);
|
|
64
|
+
requireArgument(value, `named argument ${name}`);
|
|
65
|
+
}
|
|
66
|
+
encoded.named = { ...named };
|
|
67
|
+
}
|
|
68
|
+
return encoded;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** @param {string} sql @param {V1SqlArguments | undefined} args @returns {V1SqlStatement} */
|
|
72
|
+
export function sqlStatement(sql, args) {
|
|
73
|
+
if (args === undefined) return { sql };
|
|
74
|
+
return Array.isArray(args) ? { sql, args } : { sql, named: /** @type {Readonly<Record<string, V1SqlArgument>>} */ (args) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @param {JsonValue | undefined} value @returns {V1SqlValue} */
|
|
78
|
+
function decodeValue(value) {
|
|
79
|
+
if (value === null || typeof value === "number" || typeof value === "string" || value instanceof ExactDecimal) return value;
|
|
80
|
+
return malformed("SQL value");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** @param {JsonValue | undefined} value @returns {V1SqlExecuteResult} */
|
|
84
|
+
function decodeResult(value) {
|
|
85
|
+
const raw = object(value, "SQL statement result");
|
|
86
|
+
const columns = array(raw.columns, "SQL columns").map(entry => {
|
|
87
|
+
const column = object(entry, "SQL column");
|
|
88
|
+
const decltype = column.decltype === null || column.decltype === undefined ? null : string(column.decltype, "SQL column decltype");
|
|
89
|
+
return Object.freeze({ name: string(column.name, "SQL column name"), decltype });
|
|
90
|
+
});
|
|
91
|
+
const rows = array(raw.rows, "SQL rows").map(entry => {
|
|
92
|
+
const row = array(entry, "SQL row").map(decodeValue);
|
|
93
|
+
if (row.length !== columns.length) malformed("SQL row width");
|
|
94
|
+
return Object.freeze(row);
|
|
95
|
+
});
|
|
96
|
+
const affected = raw.rows_affected;
|
|
97
|
+
if (typeof affected !== "number" || !Number.isSafeInteger(affected) || affected < 0) malformed("SQL rows_affected");
|
|
98
|
+
const rowid = raw.last_insert_rowid ?? null;
|
|
99
|
+
if (rowid !== null && !(rowid instanceof ExactDecimal) && !(typeof rowid === "number" && Number.isInteger(rowid))) malformed("SQL last_insert_rowid");
|
|
100
|
+
return Object.freeze({ columns: Object.freeze(columns), rows: Object.freeze(rows), rowsAffected: affected, lastInsertRowid: rowid });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Runs 1 to 256 statements as one transaction: all commit or none do. BEGIN, COMMIT and ROLLBACK are refused.
|
|
105
|
+
* A refused statement throws SqlError with its index.
|
|
106
|
+
* @param {V1Transport} transport
|
|
107
|
+
* @param {string} database
|
|
108
|
+
* @param {readonly (string | V1SqlStatement)[]} statements
|
|
109
|
+
* @param {V1RequestOptions} [options]
|
|
110
|
+
* @returns {Promise<readonly V1SqlExecuteResult[]>}
|
|
111
|
+
*/
|
|
112
|
+
export async function executeSql(transport, database, statements, options = {}) {
|
|
113
|
+
if (!Array.isArray(statements) || statements.length === 0 || statements.length > maximumStatements)
|
|
114
|
+
throw new TypeError(`SQL batches hold 1 to ${maximumStatements} statements.`);
|
|
115
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) throw new TypeError("SQL request options must be an object.");
|
|
116
|
+
for (const member of Object.keys(options)) if (member !== "signal") throw new TypeError(`Unknown SQL request option ${member}.`);
|
|
117
|
+
const body = stringifyExactJson({ statements: statements.map(encodeStatement) }, "SQL statements");
|
|
118
|
+
const response = await transport.call("executeSql", { database: requireName("database", database) }, { body, signal: options.signal });
|
|
119
|
+
const results = array(object(response.value, "SQL execute response").results, "SQL results").map(decodeResult);
|
|
120
|
+
if (results.length !== statements.length) malformed("SQL result count");
|
|
121
|
+
return Object.freeze(results);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Rows as objects keyed by column name; a later duplicate column name wins.
|
|
126
|
+
* @template [T=Record<string, V1SqlValue>]
|
|
127
|
+
* @param {V1SqlExecuteResult} result
|
|
128
|
+
* @returns {T[]}
|
|
129
|
+
*/
|
|
130
|
+
export function sqlRowObjects(result) {
|
|
131
|
+
return result.rows.map(row => /** @type {T} */ (Object.fromEntries(result.columns.map((column, index) => [column.name, row[index] ?? null]))));
|
|
132
|
+
}
|
package/src/v1/terrabase-v1.js
CHANGED
|
@@ -11,8 +11,10 @@ import { readShapeManifest } from "./shape-manifest.js";
|
|
|
11
11
|
import { V1ShapeSubscription } from "./shape-subscription.js";
|
|
12
12
|
import { decryptSealedShapePage, readShapePage, readShapeKey } from "./shape-reader.js";
|
|
13
13
|
import { runSql, checkSql } from "./sql.js";
|
|
14
|
+
import { executeSql, sqlRowObjects, sqlStatement } from "./sql-execute.js";
|
|
14
15
|
import { streamChanges } from "./changes.js";
|
|
15
|
-
import {
|
|
16
|
+
import { exportDocuments } from "./export.js";
|
|
17
|
+
import { V1Databases, getCollectionSchema, putCollectionSchema, deleteCollectionSchema, getCollectionTtl, putCollectionTtl, deleteCollectionTtl } from "./catalog.js";
|
|
16
18
|
import { V1Transport } from "./transport.js";
|
|
17
19
|
import { V1Health, V1Platform } from "./system.js";
|
|
18
20
|
import { V1Imports } from "./imports.js";
|
|
@@ -21,6 +23,7 @@ import { V1ProjectUsageClient, V1DatabaseUsageClient } from "./usage.js";
|
|
|
21
23
|
import { V1Indexes } from "./indexes.js";
|
|
22
24
|
import { V1DatabaseSchemaClient } from "./database-schema.js";
|
|
23
25
|
import { V1SavedQueries } from "./saved-queries.js";
|
|
26
|
+
import { V1ChangeWebhooks } from "./change-webhooks.js";
|
|
24
27
|
/**
|
|
25
28
|
* @import { V1Document, V1Durability, V1WriteResult } from "./decode.js"
|
|
26
29
|
* @import { V1Filter, V1Path } from "./filter.js"
|
|
@@ -58,12 +61,17 @@ import { V1SavedQueries } from "./saved-queries.js";
|
|
|
58
61
|
* @typedef {{ readonly merge: JsonInputObject } | { readonly ops: readonly V1PatchOp[] }} V1Patch
|
|
59
62
|
*/
|
|
60
63
|
|
|
61
|
-
/** @typedef {{ readonly path: V1Path; readonly order?: "asc" | "desc" | undefined }}
|
|
64
|
+
/** @typedef {{ readonly path: V1Path; readonly order?: "asc" | "desc" | undefined }} V1FieldSortKey */
|
|
65
|
+
/**
|
|
66
|
+
* Vector similarity sort, nearest first. It must be the only sort key; limit is at most 100 and there is no cursor.
|
|
67
|
+
* @typedef {{ readonly near: { readonly path: V1Path; readonly vector: readonly number[]; readonly metric?: "cosine" | "l2" | undefined } }} V1NearSortKey
|
|
68
|
+
*/
|
|
69
|
+
/** @typedef {V1FieldSortKey | V1NearSortKey} V1SortKey */
|
|
62
70
|
/**
|
|
63
71
|
* A query-v1 request. Every member is optional.
|
|
64
72
|
* @typedef {object} V1Query
|
|
65
73
|
* @property {V1Filter | null | undefined} [filter]
|
|
66
|
-
* @property {readonly V1SortKey[] | undefined} [sort] At most 4 keys; results are finally ordered by `$id`.
|
|
74
|
+
* @property {readonly V1SortKey[] | undefined} [sort] At most 4 keys, or one `near` key; results are finally ordered by `$id`.
|
|
67
75
|
* @property {readonly string[] | undefined} [project] At most 64 paths.
|
|
68
76
|
* @property {number | undefined} [limit] 1-1000 (server default 50).
|
|
69
77
|
* @property {string | null | undefined} [cursor] A previous page's `nextCursor`.
|
|
@@ -174,6 +182,26 @@ function normalizePatch(patch) {
|
|
|
174
182
|
return { ops: ops.map(patchOp) };
|
|
175
183
|
}
|
|
176
184
|
|
|
185
|
+
/**
|
|
186
|
+
* @param {V1NearSortKey} key
|
|
187
|
+
* @param {V1Query} query
|
|
188
|
+
* @returns {Record<string, unknown>}
|
|
189
|
+
*/
|
|
190
|
+
function nearSort(key, query) {
|
|
191
|
+
if (Object.keys(key).length !== 1 || query.sort?.length !== 1) throw new TypeError("near is the only sort key and has no path or order beside it.");
|
|
192
|
+
if (query.cursor != null) throw new TypeError("A near query has one page and takes no cursor.");
|
|
193
|
+
if (query.limit !== undefined && query.limit > 100) throw new TypeError("A near query limit is at most 100.");
|
|
194
|
+
const near = key.near;
|
|
195
|
+
if (near === null || typeof near !== "object") throw new TypeError("near is an object.");
|
|
196
|
+
for (const member of Object.keys(near)) if (!["path", "vector", "metric"].includes(member)) throw new TypeError(`Unknown near member ${member}.`);
|
|
197
|
+
if (near.metric !== undefined && near.metric !== "cosine" && near.metric !== "l2") throw new TypeError('near metric is "cosine" or "l2".');
|
|
198
|
+
const vector = near.vector;
|
|
199
|
+
if (!Array.isArray(vector) || vector.length < 1 || vector.length > 4096) throw new TypeError("A near vector has 1-4096 numbers.");
|
|
200
|
+
if (!vector.every(value => typeof value === "number" && Math.abs(value) <= 3.4028234663852886e38)) throw new TypeError("near vector values are finite float32 numbers.");
|
|
201
|
+
if (near.metric !== "l2" && vector.every(value => value === 0)) throw new TypeError("A cosine near vector is not all zeros.");
|
|
202
|
+
return { near: { path: requirePath(near.path), vector: [...vector], ...(near.metric === undefined ? {} : { metric: near.metric }) } };
|
|
203
|
+
}
|
|
204
|
+
|
|
177
205
|
/**
|
|
178
206
|
* @param {V1Query} query
|
|
179
207
|
* @returns {string}
|
|
@@ -189,6 +217,7 @@ function encodeQuery(query) {
|
|
|
189
217
|
if (!Array.isArray(query.sort) || query.sort.length > 4) throw new TypeError("sort has at most 4 keys.");
|
|
190
218
|
body.sort = query.sort.map((key) => {
|
|
191
219
|
if (key === null || typeof key !== "object") throw new TypeError("Sort keys are objects.");
|
|
220
|
+
if ("near" in key) return nearSort(key, query);
|
|
192
221
|
for (const member of Object.keys(key)) if (member !== "path" && member !== "order") throw new TypeError(`Unknown sort member ${member}.`);
|
|
193
222
|
if (key.order !== undefined && key.order !== "asc" && key.order !== "desc") throw new TypeError('Sort order is "asc" or "desc".');
|
|
194
223
|
return { path: requirePath(key.path), ...(key.order === undefined ? {} : { order: key.order }) };
|
|
@@ -228,7 +257,13 @@ export function decodeQueryPage(value) {
|
|
|
228
257
|
count = Object.freeze({ value: u64(rawCount.value, "count value"), exact: boolean(rawCount.exact, "count exact flag") });
|
|
229
258
|
}
|
|
230
259
|
return Object.freeze({
|
|
231
|
-
items: Object.freeze(array(raw.items, "query items").map(
|
|
260
|
+
items: Object.freeze(array(raw.items, "query items").map(item => {
|
|
261
|
+
const distance = object(item, "document").distance;
|
|
262
|
+
if (distance === undefined) return decodeDocument(item);
|
|
263
|
+
const value = distance instanceof ExactDecimal ? distance.toNumber() : distance;
|
|
264
|
+
if (typeof value !== "number" || !Number.isFinite(value)) malformed("near distance");
|
|
265
|
+
return Object.freeze({ ...decodeDocument(item), distance: value });
|
|
266
|
+
})),
|
|
232
267
|
nextCursor: typeof next === "string" && next !== "" ? next : null,
|
|
233
268
|
count,
|
|
234
269
|
});
|
|
@@ -268,6 +303,8 @@ function encodeDocument(document) {
|
|
|
268
303
|
|
|
269
304
|
/** A collection handle by name. It performs no I/O until an operation runs. */
|
|
270
305
|
export class V1Collection {
|
|
306
|
+
/** @type {V1ChangeWebhooks} */
|
|
307
|
+
webhooks;
|
|
271
308
|
/** @type {V1Transport} */
|
|
272
309
|
#transport;
|
|
273
310
|
/** @type {Readonly<Record<string, string>>} */
|
|
@@ -286,6 +323,7 @@ export class V1Collection {
|
|
|
286
323
|
this.#transport = transport;
|
|
287
324
|
this.database = requireName("database", database);
|
|
288
325
|
this.name = requireName("collection", name);
|
|
326
|
+
this.webhooks = new V1ChangeWebhooks(transport, this.database, this.name);
|
|
289
327
|
this.#params = Object.freeze({ database: this.database, collection: this.name });
|
|
290
328
|
Object.freeze(this);
|
|
291
329
|
}
|
|
@@ -407,6 +445,15 @@ export class V1Collection {
|
|
|
407
445
|
putSchema(schema, options = {}) { return putCollectionSchema(this.#transport, this.#params, schema, options); }
|
|
408
446
|
/** @param {V1RequestOptions} [options] */
|
|
409
447
|
deleteSchema(options = {}) { return deleteCollectionSchema(this.#transport, this.#params, options); }
|
|
448
|
+
/** @param {V1RequestOptions} [options] */
|
|
449
|
+
getTtl(options = {}) { return getCollectionTtl(this.#transport, this.#params, options); }
|
|
450
|
+
/**
|
|
451
|
+
* Documents whose member at `path` holds a past RFC 3339 UTC time are deleted by an hourly sweep (up to about an hour late).
|
|
452
|
+
* @param {string} path JSON Pointer such as `/expiresAt`. @param {V1RequestOptions} [options]
|
|
453
|
+
*/
|
|
454
|
+
putTtl(path, options = {}) { return putCollectionTtl(this.#transport, this.#params, path, options); }
|
|
455
|
+
/** @param {V1RequestOptions} [options] */
|
|
456
|
+
deleteTtl(options = {}) { return deleteCollectionTtl(this.#transport, this.#params, options); }
|
|
410
457
|
|
|
411
458
|
/**
|
|
412
459
|
* Iterates over every page, following `nextCursor`. `count`
|
|
@@ -810,10 +857,51 @@ export class V1Database {
|
|
|
810
857
|
return checkSql(this.#transport, this.name, statement, options, "explainSql");
|
|
811
858
|
}
|
|
812
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Runs one relational SQL statement as its own transaction. Reads need documents:read; writes and DDL need documents:write.
|
|
862
|
+
* @param {string} sql
|
|
863
|
+
* @param {import("./sql-execute.js").V1SqlArguments} [args] Positional array, or named values keyed without their prefix.
|
|
864
|
+
* @param {V1RequestOptions} [options]
|
|
865
|
+
* @returns {Promise<import("./sql-execute.js").V1SqlExecuteResult>}
|
|
866
|
+
*/
|
|
867
|
+
async execute(sql, args, options = {}) {
|
|
868
|
+
const [result] = await executeSql(this.#transport, this.name, [sqlStatement(sql, args)], options);
|
|
869
|
+
return /** @type {import("./sql-execute.js").V1SqlExecuteResult} */ (result);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Runs one statement and returns its rows as objects keyed by column name.
|
|
874
|
+
* @template [T=Record<string, import("./sql-execute.js").V1SqlValue>]
|
|
875
|
+
* @param {string} sql
|
|
876
|
+
* @param {import("./sql-execute.js").V1SqlArguments} [args]
|
|
877
|
+
* @param {V1RequestOptions} [options]
|
|
878
|
+
* @returns {Promise<T[]>}
|
|
879
|
+
*/
|
|
880
|
+
async query(sql, args, options = {}) {
|
|
881
|
+
return sqlRowObjects(await this.execute(sql, args, options));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Runs 1 to 256 statements as one transaction: all commit or none do. A refused statement throws SqlError.
|
|
886
|
+
* @param {readonly (string | import("./sql-execute.js").V1SqlStatement)[]} statements
|
|
887
|
+
* @param {V1RequestOptions} [options]
|
|
888
|
+
*/
|
|
889
|
+
batch(statements, options = {}) {
|
|
890
|
+
return executeSql(this.#transport, this.name, statements, options);
|
|
891
|
+
}
|
|
892
|
+
|
|
813
893
|
/** @param {import("./changes.js").V1ChangeStreamRequest} [request] @param {V1RequestOptions} [options] */
|
|
814
894
|
changes(request = {}, options = {}) {
|
|
815
895
|
return streamChanges(this.#transport, this.name, request, options.signal);
|
|
816
896
|
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Streams every live document as `{ collection, id, document }`, in collection then id order, from one snapshot.
|
|
900
|
+
* @param {import("./export.js").V1ExportOptions} [options]
|
|
901
|
+
*/
|
|
902
|
+
export(options = {}) {
|
|
903
|
+
return exportDocuments(this.#transport, this.name, options);
|
|
904
|
+
}
|
|
817
905
|
}
|
|
818
906
|
|
|
819
907
|
/**
|
package/src/v1/transport.js
CHANGED
|
@@ -23,12 +23,13 @@ const tokenPattern = /^(?!tbk1\.)(?:[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-
|
|
|
23
23
|
* @typedef {{ readonly project: string | undefined; readonly database?: string | undefined; readonly signal?: AbortSignal | undefined }} V1EndpointContext
|
|
24
24
|
*/
|
|
25
25
|
/** @typedef {(context: V1EndpointContext) => string | Promise<string>} V1EndpointResolver */
|
|
26
|
+
/** A fresh access token for each authenticated request, including stream reconnects. @typedef {() => string | Promise<string>} V1TokenProvider */
|
|
26
27
|
/**
|
|
27
28
|
* Project-scoped C# Documents API client. Supply exactly one of baseUrl or endpointResolver.
|
|
28
29
|
* URLs must use HTTPS or loopback HTTP. Mutations are never automatically replayed.
|
|
29
30
|
* @typedef {object} TerraBaseV1ClientConfig
|
|
30
31
|
* @property {string} project
|
|
31
|
-
* @property {string} token
|
|
32
|
+
* @property {string | V1TokenProvider} token An access JWT (or a legacy C# tb_ key), or a provider resolved per authenticated request; held privately. Browser callers must use end-user tokens, never project credentials.
|
|
32
33
|
* @property {string | undefined} [baseUrl]
|
|
33
34
|
* @property {V1EndpointResolver | undefined} [endpointResolver]
|
|
34
35
|
* @property {FetchLike | undefined} [fetch]
|
|
@@ -117,6 +118,13 @@ function canonicalBaseUrl(raw) {
|
|
|
117
118
|
return `${url.origin}${url.pathname.replace(/\/+$/u, "")}`;
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
/** @param {unknown} token @returns {string} */
|
|
122
|
+
function requireToken(token) {
|
|
123
|
+
if (typeof token !== "string" || token.trim() !== token || !tokenPattern.test(token))
|
|
124
|
+
throw new TypeError("token must be a compact access JWT or a legacy C# tb_ API key.");
|
|
125
|
+
return token;
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
/** A response body that failed in transit after its headers arrived. */
|
|
121
129
|
export class BodyTransportError extends Error {
|
|
122
130
|
/** @param {unknown} cause */
|
|
@@ -134,7 +142,7 @@ export class V1Transport {
|
|
|
134
142
|
#resolver;
|
|
135
143
|
/** @type {Map<string, { promise: Promise<string>; timer: ReturnType<typeof setTimeout>; waiters: number }>} */
|
|
136
144
|
#bases = new Map();
|
|
137
|
-
/** @type {string | undefined} */
|
|
145
|
+
/** @type {string | V1TokenProvider | undefined} */
|
|
138
146
|
#token;
|
|
139
147
|
/** @type {FetchLike} */
|
|
140
148
|
#fetch;
|
|
@@ -150,8 +158,8 @@ export class V1Transport {
|
|
|
150
158
|
if (config.endpointResolver !== undefined && typeof config.endpointResolver !== "function")
|
|
151
159
|
throw new TypeError("endpointResolver must be a function.");
|
|
152
160
|
this.#resolver = config.endpointResolver ?? staticBaseUrlResolver(/** @type {string} */ (config.baseUrl));
|
|
153
|
-
if ((config.project !== undefined || config.token !== undefined) &&
|
|
154
|
-
|
|
161
|
+
if ((config.project !== undefined || config.token !== undefined) && typeof config.token !== "function")
|
|
162
|
+
requireToken(config.token);
|
|
155
163
|
if ("autoIdempotencyKey" in config || "maxRetries" in config || "resolver" in config)
|
|
156
164
|
throw new TypeError("Unsupported client configuration member.");
|
|
157
165
|
const fetchHandler = config.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
@@ -164,6 +172,35 @@ export class V1Transport {
|
|
|
164
172
|
this.#maxResponseBytes = maxResponseBytes;
|
|
165
173
|
}
|
|
166
174
|
|
|
175
|
+
/** Internal credential lifetime metadata; never exposes the bearer value. */
|
|
176
|
+
get hasTokenProvider() { return typeof this.#token === "function"; }
|
|
177
|
+
|
|
178
|
+
/** @param {AbortSignal | undefined} signal @returns {Promise<string>} */
|
|
179
|
+
async #accessToken(signal) {
|
|
180
|
+
signal?.throwIfAborted();
|
|
181
|
+
const provider = this.#token;
|
|
182
|
+
if (typeof provider !== "function") return requireToken(provider);
|
|
183
|
+
/** @type {() => void} */
|
|
184
|
+
let abort = () => {};
|
|
185
|
+
try {
|
|
186
|
+
const token = await new Promise((resolve, reject) => {
|
|
187
|
+
abort = () => reject(signal?.reason);
|
|
188
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
189
|
+
void Promise.resolve().then(() => {
|
|
190
|
+
signal?.throwIfAborted();
|
|
191
|
+
return provider();
|
|
192
|
+
}).then(resolve, () => {
|
|
193
|
+
// Provider errors may contain credentials; do not retain their message or cause.
|
|
194
|
+
reject(new Error("TerraBase token provider failed."));
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
signal?.throwIfAborted();
|
|
198
|
+
return requireToken(token);
|
|
199
|
+
} finally {
|
|
200
|
+
signal?.removeEventListener("abort", abort);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
167
204
|
/**
|
|
168
205
|
* @param {V1Operation} operation
|
|
169
206
|
* @param {Readonly<Record<string, string>>} params
|
|
@@ -320,7 +357,7 @@ export class V1Transport {
|
|
|
320
357
|
}
|
|
321
358
|
|
|
322
359
|
/**
|
|
323
|
-
* @param {"streamDatabaseChanges"} operation
|
|
360
|
+
* @param {"streamDatabaseChanges" | "exportDatabase"} operation
|
|
324
361
|
* @param {Readonly<Record<string, string>>} params
|
|
325
362
|
* @param {V1CallOptions} [options]
|
|
326
363
|
* @returns {Promise<Response>}
|
|
@@ -328,9 +365,10 @@ export class V1Transport {
|
|
|
328
365
|
async openStream(operation, params, options = {}) {
|
|
329
366
|
const response = await this.#send(operation, params, options);
|
|
330
367
|
const mediaType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
331
|
-
|
|
368
|
+
const expected = v1Route(operation).responseMediaType;
|
|
369
|
+
if (mediaType !== expected || response.body === null) {
|
|
332
370
|
void response.body?.cancel().catch(() => undefined);
|
|
333
|
-
throw new TypeError(
|
|
371
|
+
throw new TypeError(`TerraBase ${operation} requires a ${expected} response body.`);
|
|
334
372
|
}
|
|
335
373
|
return response;
|
|
336
374
|
}
|
|
@@ -344,7 +382,8 @@ export class V1Transport {
|
|
|
344
382
|
let url = await this.url(operation, params, options.signal);
|
|
345
383
|
if (options.query !== undefined && options.query.size > 0) url += `?${options.query}`;
|
|
346
384
|
const headers = new Headers(options.headers);
|
|
347
|
-
if (route.permission !== "anonymous" && this.#token !== undefined)
|
|
385
|
+
if (route.permission !== "anonymous" && this.#token !== undefined)
|
|
386
|
+
headers.set("authorization", `Bearer ${await this.#accessToken(options.signal)}`);
|
|
348
387
|
if (route.responseMediaType !== "") headers.set("accept", route.responseMediaType);
|
|
349
388
|
if (options.body !== undefined) headers.set("content-type", options.contentType ?? "application/json");
|
|
350
389
|
/** @type {RequestInit & { duplex?: "half" }} */
|
|
@@ -357,6 +396,7 @@ export class V1Transport {
|
|
|
357
396
|
init.duplex = "half";
|
|
358
397
|
} else if (options.body !== undefined) init.body = typeof options.body === "string" ? options.body : options.body.slice().buffer;
|
|
359
398
|
if (options.signal !== undefined) init.signal = options.signal;
|
|
399
|
+
options.signal?.throwIfAborted();
|
|
360
400
|
const response = await this.#fetch(url, init);
|
|
361
401
|
if (options.signal?.aborted) {
|
|
362
402
|
void response.body?.cancel().catch(() => undefined);
|
package/types/index.d.ts
CHANGED
|
@@ -6,12 +6,14 @@ export { V1Databases } from "./v1/catalog.js";
|
|
|
6
6
|
export { V1SavedQueries } from "./v1/saved-queries.js";
|
|
7
7
|
export { V1DatabaseSchemaClient } from "./v1/database-schema.js";
|
|
8
8
|
export { V1Indexes } from "./v1/indexes.js";
|
|
9
|
+
export { V1ChangeWebhooks } from "./v1/change-webhooks.js";
|
|
9
10
|
export { V1Imports } from "./v1/imports.js";
|
|
10
11
|
export type V1ShapeSubscribeOptions = import("./v1/shape-subscription.js").V1ShapeSubscribeOptions;
|
|
11
12
|
export type V1ShapeDocument = import("./v1/shape-subscription.js").V1ShapeDocument;
|
|
12
13
|
export type V1SealedShapePosition = import("./v1/shape-reader.js").V1SealedShapePosition;
|
|
13
14
|
export type EntityId = import("./ids.js").EntityId;
|
|
14
15
|
export type TerraBaseV1ClientConfig = import("./v1/transport.js").TerraBaseV1ClientConfig;
|
|
16
|
+
export type V1TokenProvider = import("./v1/transport.js").V1TokenProvider;
|
|
15
17
|
export type V1RequestOptions = import("./v1/terrabase-v1.js").V1RequestOptions;
|
|
16
18
|
export type V1ReadOptions = import("./v1/terrabase-v1.js").V1ReadOptions;
|
|
17
19
|
export type V1WriteOptions = import("./v1/terrabase-v1.js").V1WriteOptions;
|
|
@@ -20,6 +22,7 @@ export type V1ConditionalWriteOptions = import("./v1/terrabase-v1.js").V1Conditi
|
|
|
20
22
|
export type V1PatchOp = import("./v1/terrabase-v1.js").V1PatchOp;
|
|
21
23
|
export type V1Patch = import("./v1/terrabase-v1.js").V1Patch;
|
|
22
24
|
export type V1SortKey = import("./v1/terrabase-v1.js").V1SortKey;
|
|
25
|
+
export type V1NearSortKey = import("./v1/terrabase-v1.js").V1NearSortKey;
|
|
23
26
|
export type V1Query = import("./v1/terrabase-v1.js").V1Query;
|
|
24
27
|
export type V1Count = import("./v1/terrabase-v1.js").V1Count;
|
|
25
28
|
export type V1QueryPage = import("./v1/terrabase-v1.js").V1QueryPage;
|
|
@@ -54,9 +57,17 @@ export type V1SqlRow = import("./v1/sql.js").V1SqlRow;
|
|
|
54
57
|
export type V1SqlResult = import("./v1/sql.js").V1SqlResult;
|
|
55
58
|
export type V1SqlIndexSuggestion = import("./v1/sql.js").V1SqlIndexSuggestion;
|
|
56
59
|
export type V1SqlCheck = import("./v1/sql.js").V1SqlCheck;
|
|
60
|
+
export type V1SqlArgument = import("./v1/sql-execute.js").V1SqlArgument;
|
|
61
|
+
export type V1SqlArguments = import("./v1/sql-execute.js").V1SqlArguments;
|
|
62
|
+
export type V1SqlStatement = import("./v1/sql-execute.js").V1SqlStatement;
|
|
63
|
+
export type V1SqlValue = import("./v1/sql-execute.js").V1SqlValue;
|
|
64
|
+
export type V1SqlColumn = import("./v1/sql-execute.js").V1SqlColumn;
|
|
65
|
+
export type V1SqlExecuteResult = import("./v1/sql-execute.js").V1SqlExecuteResult;
|
|
57
66
|
export type V1ChangeStreamRequest = import("./v1/changes.js").V1ChangeStreamRequest;
|
|
58
67
|
export type V1DatabaseChange = import("./v1/changes.js").V1DatabaseChange;
|
|
59
68
|
export type V1DatabaseChangeCursor = import("./v1/changes.js").V1DatabaseChangeCursor;
|
|
69
|
+
export type V1ExportOptions = import("./v1/export.js").V1ExportOptions;
|
|
70
|
+
export type V1ExportedDocument = import("./v1/export.js").V1ExportedDocument;
|
|
60
71
|
export type V1ManagedDatabase = import("./v1/catalog.js").V1ManagedDatabase;
|
|
61
72
|
export type V1DatabaseCreateRequest = import("./v1/catalog.js").V1DatabaseCreateRequest;
|
|
62
73
|
export type V1DatabaseUpdateRequest = import("./v1/catalog.js").V1DatabaseUpdateRequest;
|
|
@@ -64,6 +75,7 @@ export type V1DatabaseBranchRequest = import("./v1/catalog.js").V1DatabaseBranch
|
|
|
64
75
|
export type V1DatabaseListRequest = import("./v1/catalog.js").V1DatabaseListRequest;
|
|
65
76
|
export type V1DatabasePage = import("./v1/catalog.js").V1DatabasePage;
|
|
66
77
|
export type V1CollectionSchema = import("./v1/catalog.js").V1CollectionSchema;
|
|
78
|
+
export type V1CollectionTtl = import("./v1/catalog.js").V1CollectionTtl;
|
|
67
79
|
export type V1InferredSchema = import("./v1/database-schema.js").V1InferredSchema;
|
|
68
80
|
export type V1DatabaseSchema = import("./v1/database-schema.js").V1DatabaseSchema;
|
|
69
81
|
export type V1UniqueSchema = import("./v1/database-schema.js").V1UniqueSchema;
|
|
@@ -81,6 +93,10 @@ export type V1Analytics = import("./v1/usage.js").V1Analytics;
|
|
|
81
93
|
export type V1Replication = import("./v1/usage.js").V1Replication;
|
|
82
94
|
export type V1UsageRequest = import("./v1/usage.js").V1UsageRequest;
|
|
83
95
|
export type V1AnalyticsRequest = import("./v1/usage.js").V1AnalyticsRequest;
|
|
96
|
+
export type V1ChangeWebhook = import("./v1/change-webhooks.js").V1ChangeWebhook;
|
|
97
|
+
export type V1CreatedChangeWebhook = import("./v1/change-webhooks.js").V1CreatedChangeWebhook;
|
|
98
|
+
export type V1ChangeWebhookDelivery = import("./v1/change-webhooks.js").V1ChangeWebhookDelivery;
|
|
99
|
+
export type V1ChangeWebhookListRequest = import("./v1/change-webhooks.js").V1ChangeWebhookListRequest;
|
|
84
100
|
export type V1Backup = import("./v1/backups.js").V1Backup;
|
|
85
101
|
export type V1BackupPage = import("./v1/backups.js").V1BackupPage;
|
|
86
102
|
export type V1BackupPolicy = import("./v1/backups.js").V1BackupPolicy;
|
|
@@ -105,7 +121,7 @@ export type V1ShapeKeyRequest = import("./v1/shape-reader.js").V1ShapeKeyRequest
|
|
|
105
121
|
export type V1ShapeKey = import("./v1/shape-reader.js").V1ShapeKey;
|
|
106
122
|
export { createTerraBaseV1Client as createTerraBaseClient, TerraBaseV1Client as TerraBaseClient, createTerraBaseV1Client, TerraBaseV1Client, V1Database, V1Collection, V1Collections, V1Transaction } from "./v1/terrabase-v1.js";
|
|
107
123
|
export { terraScaleReleaseBlockers, TerraScaleCapabilityUnavailableError } from "./management-contracts.js";
|
|
108
|
-
export { TerraBaseV1Error, V1PreconditionFailedError, V1InvalidKeyError, V1ForbiddenScopeError, V1NotFoundError, V1ErrorCode, isTerraBaseV1Error } from "./v1/errors.js";
|
|
124
|
+
export { TerraBaseV1Error, V1PreconditionFailedError, V1InvalidKeyError, V1ForbiddenScopeError, V1NotFoundError, SqlError, V1ErrorCode, isTerraBaseV1Error } from "./v1/errors.js";
|
|
109
125
|
export { generateId, isValidId, requireId } from "./ids.js";
|
|
110
126
|
export { V1ProjectUsageClient, V1DatabaseUsageClient } from "./v1/usage.js";
|
|
111
127
|
export { V1Backups, V1BackupDestinationClient } from "./v1/backups.js";
|
package/types/v1/catalog.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ export function getCollectionSchema(transport: V1Transport, params: Readonly<Rec
|
|
|
4
4
|
export function putCollectionSchema(transport: V1Transport, params: Readonly<Record<string, string>>, schema: boolean | JsonInputObject, options?: V1RequestOptions): Promise<V1CollectionSchema>;
|
|
5
5
|
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionSchema>} */
|
|
6
6
|
export function deleteCollectionSchema(transport: V1Transport, params: Readonly<Record<string, string>>, options?: V1RequestOptions): Promise<V1CollectionSchema>;
|
|
7
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
8
|
+
export function getCollectionTtl(transport: V1Transport, params: Readonly<Record<string, string>>, options?: V1RequestOptions): Promise<V1CollectionTtl>;
|
|
9
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {string} path @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
10
|
+
export function putCollectionTtl(transport: V1Transport, params: Readonly<Record<string, string>>, path: string, options?: V1RequestOptions): Promise<V1CollectionTtl>;
|
|
11
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
12
|
+
export function deleteCollectionTtl(transport: V1Transport, params: Readonly<Record<string, string>>, options?: V1RequestOptions): Promise<V1CollectionTtl>;
|
|
7
13
|
/** Public database catalog operations, bound to the transport's authorized project. */
|
|
8
14
|
export class V1Databases {
|
|
9
15
|
/** @param {V1Transport} transport */
|
|
@@ -86,6 +92,12 @@ export type V1CollectionSchema = {
|
|
|
86
92
|
readonly schema: JsonObject | boolean | null;
|
|
87
93
|
readonly dialect?: string | null | undefined;
|
|
88
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* The expiry member, or null when documents never expire.
|
|
97
|
+
*/
|
|
98
|
+
export type V1CollectionTtl = {
|
|
99
|
+
readonly path: string | null;
|
|
100
|
+
};
|
|
89
101
|
import type { V1Transport } from "./transport.js";
|
|
90
102
|
import type { V1RequestOptions } from "./terrabase-v1.js";
|
|
91
103
|
import type { JsonInputObject } from "./json.js";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Collection change webhook registration and sanitized delivery history. Requests are never replayed. */
|
|
2
|
+
export class V1ChangeWebhooks {
|
|
3
|
+
/** @param {V1Transport} transport @param {string} database @param {string} collection */
|
|
4
|
+
constructor(transport: V1Transport, database: string, collection: string);
|
|
5
|
+
/**
|
|
6
|
+
* Registers from the current feed head. Requires documents:read and documents:write.
|
|
7
|
+
* Store secret immediately; it is non-enumerable to keep it out of JSON and ordinary diagnostics.
|
|
8
|
+
* @param {{ readonly url: string }} request @param {V1RequestOptions} [options] @returns {Promise<V1CreatedChangeWebhook>}
|
|
9
|
+
*/
|
|
10
|
+
create(request: {
|
|
11
|
+
readonly url: string;
|
|
12
|
+
}, options?: V1RequestOptions): Promise<V1CreatedChangeWebhook>;
|
|
13
|
+
/**
|
|
14
|
+
* Requires documents:read. Ascending IDs; continue with the final ID until an empty array. No secrets.
|
|
15
|
+
* @param {V1ChangeWebhookListRequest} [request] @param {V1RequestOptions} [options] @returns {Promise<readonly V1ChangeWebhook[]>}
|
|
16
|
+
*/
|
|
17
|
+
list(request?: V1ChangeWebhookListRequest, options?: V1RequestOptions): Promise<readonly V1ChangeWebhook[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Requires documents:read and documents:write. An in-flight delivery may still complete. Unknown ID returns 404.
|
|
20
|
+
* @param {string} id @param {V1RequestOptions} [options] @returns {Promise<void>}
|
|
21
|
+
*/
|
|
22
|
+
delete(id: string, options?: V1RequestOptions): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Requires documents:read. Latest 100 retained records; paginate with the final ID until an empty array.
|
|
25
|
+
* @param {string} id @param {V1ChangeWebhookListRequest} [request] @param {V1RequestOptions} [options] @returns {Promise<readonly V1ChangeWebhookDelivery[]>}
|
|
26
|
+
*/
|
|
27
|
+
deliveries(id: string, request?: V1ChangeWebhookListRequest, options?: V1RequestOptions): Promise<readonly V1ChangeWebhookDelivery[]>;
|
|
28
|
+
#private;
|
|
29
|
+
}
|
|
30
|
+
export type V1ChangeWebhook = ReturnType<typeof webhook>;
|
|
31
|
+
export type V1CreatedChangeWebhook = V1ChangeWebhook & {
|
|
32
|
+
readonly secret: string;
|
|
33
|
+
};
|
|
34
|
+
export type V1ChangeWebhookDelivery = ReturnType<typeof deliveries>[number];
|
|
35
|
+
export type V1ChangeWebhookListRequest = {
|
|
36
|
+
readonly after?: string;
|
|
37
|
+
readonly limit?: number;
|
|
38
|
+
};
|
|
39
|
+
import type { V1RequestOptions } from "./terrabase-v1.js";
|
|
40
|
+
import type { V1Transport } from "./transport.js";
|
|
41
|
+
/** @import { V1Transport } from "./transport.js" */
|
|
42
|
+
/** @import { V1RequestOptions } from "./terrabase-v1.js" */
|
|
43
|
+
declare const webhook: import("./management-decode.js").Decoder<Readonly<{
|
|
44
|
+
id: string;
|
|
45
|
+
url: string;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
status: string;
|
|
48
|
+
}>>;
|
|
49
|
+
declare const deliveries: import("./management-decode.js").Decoder<readonly Readonly<{
|
|
50
|
+
id: string;
|
|
51
|
+
createdAt: string;
|
|
52
|
+
status: string;
|
|
53
|
+
attempts: number | import("./json.js").ExactDecimal;
|
|
54
|
+
lastAttemptAt: string | null;
|
|
55
|
+
nextAttemptAt: string | null;
|
|
56
|
+
httpStatus: number | import("./json.js").ExactDecimal | null;
|
|
57
|
+
errorCode: string | null;
|
|
58
|
+
}>[]>;
|
|
59
|
+
export {};
|
package/types/v1/decode.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @property {"value" | "conflict" | (string & {})} state
|
|
10
10
|
* @property {JsonObject} document
|
|
11
11
|
* @property {readonly JsonValue[]} [siblings] Live versions and tombstones on a causal conflict.
|
|
12
|
+
* @property {number} [distance] Distance to the query vector; present only on `near` query results.
|
|
12
13
|
*/
|
|
13
14
|
/**
|
|
14
15
|
* The result of a document write.
|
|
@@ -97,6 +98,10 @@ export type V1Document = {
|
|
|
97
98
|
* Live versions and tombstones on a causal conflict.
|
|
98
99
|
*/
|
|
99
100
|
siblings?: readonly JsonValue[];
|
|
101
|
+
/**
|
|
102
|
+
* Distance to the query vector; present only on `near` query results.
|
|
103
|
+
*/
|
|
104
|
+
distance?: number;
|
|
100
105
|
};
|
|
101
106
|
/**
|
|
102
107
|
* The result of a document write.
|
package/types/v1/errors.d.ts
CHANGED
|
@@ -92,6 +92,9 @@ export const V1ErrorCode: Readonly<{
|
|
|
92
92
|
internalError: "internal_error";
|
|
93
93
|
capacity: "capacity";
|
|
94
94
|
notModified: "not_modified";
|
|
95
|
+
sqlError: "sql_error";
|
|
96
|
+
sqlConflict: "sql_conflict";
|
|
97
|
+
sqlTooLarge: "sql_too_large";
|
|
95
98
|
}>;
|
|
96
99
|
/** `412 precondition_failed`: an If-Match, If-None-Match, assert or assert_count failed. */
|
|
97
100
|
export class V1PreconditionFailedError extends TerraBaseV1Error {
|
|
@@ -105,6 +108,21 @@ export class V1ForbiddenScopeError extends TerraBaseV1Error {
|
|
|
105
108
|
/** `404 not_found`: absent or deleted; `etag` carries a tombstone version. */
|
|
106
109
|
export class V1NotFoundError extends TerraBaseV1Error {
|
|
107
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* A refused relational SQL statement: `sql_error` (400), `forbidden_scope` (403, writes and DDL need
|
|
113
|
+
* documents:write), `sql_conflict` (409) or `sql_too_large` (413). The whole request rolled back.
|
|
114
|
+
*/
|
|
115
|
+
export class SqlError extends TerraBaseV1Error {
|
|
116
|
+
/** @param {TerraBaseV1ErrorInit & { statement: number; sqliteCode?: string | undefined }} init */
|
|
117
|
+
constructor(init: TerraBaseV1ErrorInit & {
|
|
118
|
+
statement: number;
|
|
119
|
+
sqliteCode?: string | undefined;
|
|
120
|
+
});
|
|
121
|
+
/** @type {number} Zero-based index of the failing statement. */
|
|
122
|
+
statement: number;
|
|
123
|
+
/** @type {string | undefined} The SQLite error code, such as `SQLITE_CONSTRAINT_PRIMARYKEY`. */
|
|
124
|
+
sqliteCode: string | undefined;
|
|
125
|
+
}
|
|
108
126
|
export type TerraBaseV1ErrorInit = {
|
|
109
127
|
status: number;
|
|
110
128
|
code: string;
|