@terrascale/sdk 0.0.3 → 0.0.5
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 +31 -0
- package/README.md +206 -67
- package/package.json +10 -4
- package/sdk-public-v1-route-manifest.json +85 -0
- package/sdk-v1-route-manifest.json +896 -15
- package/src/http.js +3 -48
- package/src/index.js +80 -1
- package/src/local/environment.js +0 -1
- package/src/ts-auth.js +1 -5
- package/src/v1/backups.js +111 -0
- package/src/v1/catalog.js +240 -0
- package/src/v1/changes.js +225 -0
- package/src/v1/database-schema.js +45 -0
- package/src/v1/decode.js +6 -5
- package/src/v1/errors.js +23 -3
- package/src/v1/export.js +68 -0
- package/src/v1/ids.js +5 -2
- package/src/v1/imports.js +72 -0
- package/src/v1/indexes.js +76 -0
- package/src/v1/json.js +4 -3
- package/src/v1/management-decode.js +24 -0
- package/src/v1/management-routes.js +72 -0
- package/src/v1/management-validation.js +25 -0
- package/src/v1/routes.js +32 -7
- package/src/v1/saved-queries.js +58 -0
- package/src/v1/shape-crypto.js +125 -0
- package/src/v1/shape-manifest.js +31 -0
- package/src/v1/shape-reader.js +214 -0
- package/src/v1/shape-subscription.js +208 -0
- package/src/v1/shapes.js +11 -5
- package/src/v1/sql.js +98 -0
- package/src/v1/system.js +80 -0
- package/src/v1/terrabase-v1.js +231 -30
- package/src/v1/transport.js +117 -33
- package/src/v1/usage.js +101 -0
- package/types/http.d.ts +2 -8
- package/types/index.d.ts +73 -0
- package/types/local/environment.d.ts +1 -1
- package/types/ts-auth.d.ts +0 -2
- package/types/v1/backups.d.ts +128 -0
- package/types/v1/catalog.d.ts +104 -0
- package/types/v1/changes.d.ts +53 -0
- package/types/v1/database-schema.d.ts +82 -0
- package/types/v1/decode.d.ts +20 -4
- package/types/v1/errors.d.ts +16 -1
- package/types/v1/export.d.ts +26 -0
- package/types/v1/ids.d.ts +2 -2
- package/types/v1/imports.d.ts +132 -0
- package/types/v1/indexes.d.ts +75 -0
- package/types/v1/management-decode.d.ts +15 -0
- package/types/v1/management-routes.d.ts +3 -0
- package/types/v1/management-validation.d.ts +8 -0
- package/types/v1/routes.d.ts +5 -2
- package/types/v1/saved-queries.d.ts +33 -0
- package/types/v1/shape-crypto.d.ts +39 -0
- package/types/v1/shape-manifest.d.ts +28 -0
- package/types/v1/shape-reader.d.ts +45 -0
- package/types/v1/shape-subscription.d.ts +35 -0
- package/types/v1/shapes.d.ts +16 -2
- package/types/v1/sql.d.ts +47 -0
- package/types/v1/system.d.ts +85 -0
- package/types/v1/terrabase-v1.d.ts +140 -6
- package/types/v1/transport.d.ts +33 -10
- package/types/v1/usage.d.ts +171 -0
- package/src/ts-auth-discovery.js +0 -207
- package/types/ts-auth-discovery.d.ts +0 -19
package/src/http.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { resolveAccessToken, resolveTsAuthRuntimeConfig } from "./config.js";
|
|
2
2
|
import { err, ok, sanitizeDiagnostic } from "./result.js";
|
|
3
|
-
import {
|
|
4
|
-
regionalIssuerOrigin,
|
|
5
|
-
TsAuthEndpointDiscovery,
|
|
6
|
-
} from "./ts-auth-discovery.js";
|
|
7
3
|
import { parseDatabaseJson } from "./database-codec.js";
|
|
8
4
|
import { validateTsAuthIds } from "./ts-auth-ids.js";
|
|
9
5
|
/** @import { ResolvedTsAuthRuntimeConfig, TsAuthRuntimeConfig, FetchLike } from "./config.js" */
|
|
@@ -22,7 +18,6 @@ import { validateTsAuthIds } from "./ts-auth-ids.js";
|
|
|
22
18
|
* readonly formBody?: URLSearchParams | string | undefined;
|
|
23
19
|
* readonly userinfoAccessToken?: string | undefined;
|
|
24
20
|
* readonly resourceIntrospectionBasic?: string | undefined;
|
|
25
|
-
* readonly issuer?: string | undefined; // Behind the Atlas proxy a regional issuer is called on its own host.
|
|
26
21
|
* readonly signal?: AbortSignal | undefined;
|
|
27
22
|
* }} TsAuthRequest
|
|
28
23
|
*/
|
|
@@ -40,25 +35,14 @@ export class TsAuthHttp {
|
|
|
40
35
|
#fetch;
|
|
41
36
|
/** @readonly @type {TsAuthRuntimeConfig["accessToken"]} */
|
|
42
37
|
#accessToken;
|
|
43
|
-
/** @
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @param {TsAuthRuntimeConfig} config
|
|
48
|
-
* @param {{ readonly authReverseProxy?: boolean }} [options]
|
|
49
|
-
*/
|
|
50
|
-
constructor(config, options) {
|
|
38
|
+
/** @param {TsAuthRuntimeConfig} config */
|
|
39
|
+
constructor(config) {
|
|
51
40
|
this.config = Object.freeze(resolveTsAuthRuntimeConfig(config));
|
|
52
41
|
const fetchHandler = config.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
53
42
|
if (fetchHandler === undefined)
|
|
54
43
|
throw new TypeError("TsAuthClient requires a fetch implementation.");
|
|
55
44
|
this.#fetch = fetchHandler;
|
|
56
45
|
this.#accessToken = config.accessToken;
|
|
57
|
-
this.#discovery =
|
|
58
|
-
options?.authReverseProxy !== true &&
|
|
59
|
-
this.config.apiBaseUrl === "https://auth.terrascale.tech"
|
|
60
|
-
? new TsAuthEndpointDiscovery(fetchHandler)
|
|
61
|
-
: undefined;
|
|
62
46
|
}
|
|
63
47
|
|
|
64
48
|
/**
|
|
@@ -90,18 +74,6 @@ export class TsAuthHttp {
|
|
|
90
74
|
options.body !== undefined
|
|
91
75
|
? JSON.stringify(options.body)
|
|
92
76
|
: options.formBody?.toString();
|
|
93
|
-
const issuerOrigin =
|
|
94
|
-
this.#discovery === undefined || options.issuer === undefined
|
|
95
|
-
? undefined
|
|
96
|
-
: regionalIssuerOrigin(options.issuer);
|
|
97
|
-
const endpoints =
|
|
98
|
-
issuerOrigin === undefined
|
|
99
|
-
? await withCancellation(
|
|
100
|
-
this.#discovery?.endpoints(signal) ?? Promise.resolve(undefined),
|
|
101
|
-
signal,
|
|
102
|
-
)
|
|
103
|
-
: [issuerOrigin];
|
|
104
|
-
let endpointIndex = 0;
|
|
105
77
|
let attempt = 0;
|
|
106
78
|
let refreshed = false;
|
|
107
79
|
/** @type {string | undefined} */
|
|
@@ -163,7 +135,7 @@ export class TsAuthHttp {
|
|
|
163
135
|
);
|
|
164
136
|
const request = new Request(
|
|
165
137
|
buildUrl(
|
|
166
|
-
|
|
138
|
+
this.config.apiBaseUrl,
|
|
167
139
|
options.path,
|
|
168
140
|
options.query,
|
|
169
141
|
),
|
|
@@ -189,16 +161,6 @@ export class TsAuthHttp {
|
|
|
189
161
|
signal.throwIfAborted();
|
|
190
162
|
}
|
|
191
163
|
responseReceived = true;
|
|
192
|
-
if (
|
|
193
|
-
options.method === "GET" &&
|
|
194
|
-
endpoints !== undefined &&
|
|
195
|
-
endpointIndex < endpoints.length - 1 &&
|
|
196
|
-
[502, 503, 504].includes(response.status)
|
|
197
|
-
) {
|
|
198
|
-
void response.body?.cancel().catch(() => undefined);
|
|
199
|
-
endpointIndex += 1;
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
164
|
if (
|
|
203
165
|
response.type === "opaqueredirect" ||
|
|
204
166
|
(response.status >= 300 && response.status < 400)
|
|
@@ -293,13 +255,6 @@ export class TsAuthHttp {
|
|
|
293
255
|
!signal?.aborted &&
|
|
294
256
|
options.method === "GET"
|
|
295
257
|
) {
|
|
296
|
-
if (
|
|
297
|
-
endpoints !== undefined &&
|
|
298
|
-
endpointIndex < endpoints.length - 1
|
|
299
|
-
) {
|
|
300
|
-
endpointIndex += 1;
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
258
|
if (attempt < this.config.maxRetryAttempts) {
|
|
304
259
|
await delayForRetry(
|
|
305
260
|
retryDelay(undefined, this.config.retryDelayMs, attempt),
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createTerraBaseV1Client as createTerraBaseClient, TerraBaseV1Client as TerraBaseClient } from "./v1/terrabase-v1.js";
|
|
2
2
|
export { terraScaleReleaseBlockers, TerraScaleCapabilityUnavailableError } from "./management-contracts.js";
|
|
3
|
-
// TerraBase
|
|
3
|
+
// Project-scoped TerraBase API; shape support is retained for the historical release.
|
|
4
4
|
export {
|
|
5
5
|
createTerraBaseV1Client,
|
|
6
6
|
TerraBaseV1Client,
|
|
@@ -19,11 +19,16 @@ export {
|
|
|
19
19
|
isTerraBaseV1Error,
|
|
20
20
|
} from "./v1/errors.js";
|
|
21
21
|
export { v1Filter } from "./v1/filter.js";
|
|
22
|
+
export { V1ShapeSubscription } from "./v1/shape-subscription.js";
|
|
23
|
+
/** @typedef {import("./v1/shape-subscription.js").V1ShapeSubscribeOptions} V1ShapeSubscribeOptions */
|
|
24
|
+
/** @typedef {import("./v1/shape-subscription.js").V1ShapeDocument} V1ShapeDocument */
|
|
25
|
+
/** @typedef {import("./v1/shape-reader.js").V1SealedShapePosition} V1SealedShapePosition */
|
|
22
26
|
export { generateId, isValidId, requireId } from "./ids.js";
|
|
23
27
|
/** @typedef {import("./ids.js").EntityId} EntityId */
|
|
24
28
|
export { ExactDecimal } from "./v1/json.js";
|
|
25
29
|
|
|
26
30
|
/** @typedef {import("./v1/transport.js").TerraBaseV1ClientConfig} TerraBaseV1ClientConfig */
|
|
31
|
+
/** @typedef {import("./v1/transport.js").V1TokenProvider} V1TokenProvider */
|
|
27
32
|
/** @typedef {import("./v1/terrabase-v1.js").V1RequestOptions} V1RequestOptions */
|
|
28
33
|
/** @typedef {import("./v1/terrabase-v1.js").V1ReadOptions} V1ReadOptions */
|
|
29
34
|
/** @typedef {import("./v1/terrabase-v1.js").V1WriteOptions} V1WriteOptions */
|
|
@@ -32,6 +37,7 @@ export { ExactDecimal } from "./v1/json.js";
|
|
|
32
37
|
/** @typedef {import("./v1/terrabase-v1.js").V1PatchOp} V1PatchOp */
|
|
33
38
|
/** @typedef {import("./v1/terrabase-v1.js").V1Patch} V1Patch */
|
|
34
39
|
/** @typedef {import("./v1/terrabase-v1.js").V1SortKey} V1SortKey */
|
|
40
|
+
/** @typedef {import("./v1/terrabase-v1.js").V1NearSortKey} V1NearSortKey */
|
|
35
41
|
/** @typedef {import("./v1/terrabase-v1.js").V1Query} V1Query */
|
|
36
42
|
/** @typedef {import("./v1/terrabase-v1.js").V1Count} V1Count */
|
|
37
43
|
/** @typedef {import("./v1/terrabase-v1.js").V1QueryPage} V1QueryPage */
|
|
@@ -54,6 +60,11 @@ export { ExactDecimal } from "./v1/json.js";
|
|
|
54
60
|
/** @typedef {import("./v1/shapes.js").V1ShapeOp} V1ShapeOp */
|
|
55
61
|
|
|
56
62
|
export { staticBaseUrlResolver } from "./v1/transport.js";
|
|
63
|
+
export { V1Databases } from "./v1/catalog.js";
|
|
64
|
+
export { V1SavedQueries } from "./v1/saved-queries.js";
|
|
65
|
+
/** @typedef {import("./v1/saved-queries.js").V1SavedQuery} V1SavedQuery */
|
|
66
|
+
/** @typedef {import("./v1/saved-queries.js").V1SavedQueryRequest} V1SavedQueryRequest */
|
|
67
|
+
/** @typedef {import("./v1/saved-queries.js").V1SavedQueryPage} V1SavedQueryPage */
|
|
57
68
|
/** @typedef {import("./v1/transport.js").TerraBaseV1ClientConfig} TerraBaseClientConfig */
|
|
58
69
|
/** @typedef {import("./v1/transport.js").V1EndpointContext} V1EndpointContext */
|
|
59
70
|
/** @typedef {import("./v1/transport.js").V1EndpointResolver} V1EndpointResolver */
|
|
@@ -61,3 +72,71 @@ export { staticBaseUrlResolver } from "./v1/transport.js";
|
|
|
61
72
|
/** @typedef {import("./v1/terrabase-v1.js").V1CollectionPage} V1CollectionPage */
|
|
62
73
|
/** @typedef {import("./v1/terrabase-v1.js").V1CollectionListOptions} V1CollectionListOptions */
|
|
63
74
|
/** @typedef {import("./v1/shapes.js").V1ShapePage} V1ShapePage */
|
|
75
|
+
/** @typedef {import("./v1/sql.js").V1SqlRow} V1SqlRow */
|
|
76
|
+
/** @typedef {import("./v1/sql.js").V1SqlResult} V1SqlResult */
|
|
77
|
+
/** @typedef {import("./v1/sql.js").V1SqlIndexSuggestion} V1SqlIndexSuggestion */
|
|
78
|
+
/** @typedef {import("./v1/sql.js").V1SqlCheck} V1SqlCheck */
|
|
79
|
+
/** @typedef {import("./v1/changes.js").V1ChangeStreamRequest} V1ChangeStreamRequest */
|
|
80
|
+
/** @typedef {import("./v1/changes.js").V1DatabaseChange} V1DatabaseChange */
|
|
81
|
+
/** @typedef {import("./v1/changes.js").V1DatabaseChangeCursor} V1DatabaseChangeCursor */
|
|
82
|
+
/** @typedef {import("./v1/export.js").V1ExportOptions} V1ExportOptions */
|
|
83
|
+
/** @typedef {import("./v1/export.js").V1ExportedDocument} V1ExportedDocument */
|
|
84
|
+
/** @typedef {import("./v1/catalog.js").V1ManagedDatabase} V1ManagedDatabase */
|
|
85
|
+
/** @typedef {import("./v1/catalog.js").V1DatabaseCreateRequest} V1DatabaseCreateRequest */
|
|
86
|
+
/** @typedef {import("./v1/catalog.js").V1DatabaseUpdateRequest} V1DatabaseUpdateRequest */
|
|
87
|
+
/** @typedef {import("./v1/catalog.js").V1DatabaseBranchRequest} V1DatabaseBranchRequest */
|
|
88
|
+
/** @typedef {import("./v1/catalog.js").V1DatabaseListRequest} V1DatabaseListRequest */
|
|
89
|
+
/** @typedef {import("./v1/catalog.js").V1DatabasePage} V1DatabasePage */
|
|
90
|
+
/** @typedef {import("./v1/catalog.js").V1CollectionSchema} V1CollectionSchema */
|
|
91
|
+
/** @typedef {import("./v1/catalog.js").V1CollectionTtl} V1CollectionTtl */
|
|
92
|
+
|
|
93
|
+
export { V1DatabaseSchemaClient } from "./v1/database-schema.js";
|
|
94
|
+
/** @typedef {import("./v1/database-schema.js").V1InferredSchema} V1InferredSchema */
|
|
95
|
+
/** @typedef {import("./v1/database-schema.js").V1DatabaseSchema} V1DatabaseSchema */
|
|
96
|
+
/** @typedef {import("./v1/database-schema.js").V1UniqueSchema} V1UniqueSchema */
|
|
97
|
+
/** @typedef {import("./v1/database-schema.js").V1UniqueSchemaRequest} V1UniqueSchemaRequest */
|
|
98
|
+
/** @typedef {import("./v1/database-schema.js").V1QueryShapes} V1QueryShapes */
|
|
99
|
+
|
|
100
|
+
export { V1Indexes } from "./v1/indexes.js";
|
|
101
|
+
/** @typedef {import("./v1/indexes.js").V1Index} V1Index */
|
|
102
|
+
/** @typedef {import("./v1/indexes.js").V1IndexList} V1IndexList */
|
|
103
|
+
/** @typedef {import("./v1/indexes.js").V1AutoIndexPolicy} V1AutoIndexPolicy */
|
|
104
|
+
/** @typedef {import("./v1/indexes.js").V1IndexRecommendations} V1IndexRecommendations */
|
|
105
|
+
/** @typedef {import("./v1/indexes.js").V1AppliedIndexRecommendation} V1AppliedIndexRecommendation */
|
|
106
|
+
/** @typedef {import("./v1/indexes.js").V1CreateIndexRequest} V1CreateIndexRequest */
|
|
107
|
+
|
|
108
|
+
export { V1ProjectUsageClient, V1DatabaseUsageClient } from "./v1/usage.js";
|
|
109
|
+
/** @typedef {import("./v1/usage.js").V1DatabaseUsage} V1DatabaseUsage */
|
|
110
|
+
/** @typedef {import("./v1/usage.js").V1ProjectUsage} V1ProjectUsage */
|
|
111
|
+
/** @typedef {import("./v1/usage.js").V1Analytics} V1Analytics */
|
|
112
|
+
/** @typedef {import("./v1/usage.js").V1Replication} V1Replication */
|
|
113
|
+
/** @typedef {import("./v1/usage.js").V1UsageRequest} V1UsageRequest */
|
|
114
|
+
/** @typedef {import("./v1/usage.js").V1AnalyticsRequest} V1AnalyticsRequest */
|
|
115
|
+
|
|
116
|
+
export { V1Backups, V1BackupDestinationClient } from "./v1/backups.js";
|
|
117
|
+
/** @typedef {import("./v1/backups.js").V1Backup} V1Backup */
|
|
118
|
+
/** @typedef {import("./v1/backups.js").V1BackupPage} V1BackupPage */
|
|
119
|
+
/** @typedef {import("./v1/backups.js").V1BackupPolicy} V1BackupPolicy */
|
|
120
|
+
/** @typedef {import("./v1/backups.js").V1RestoreWindow} V1RestoreWindow */
|
|
121
|
+
/** @typedef {import("./v1/backups.js").V1PointInTimePlan} V1PointInTimePlan */
|
|
122
|
+
/** @typedef {import("./v1/backups.js").V1RestoredDatabase} V1RestoredDatabase */
|
|
123
|
+
/** @typedef {import("./v1/backups.js").V1BackupDestination} V1BackupDestination */
|
|
124
|
+
/** @typedef {import("./v1/backups.js").V1BackupPolicyRequest} V1BackupPolicyRequest */
|
|
125
|
+
/** @typedef {import("./v1/backups.js").V1PointInTimeRequest} V1PointInTimeRequest */
|
|
126
|
+
/** @typedef {import("./v1/backups.js").V1PointInTimeRestoreRequest} V1PointInTimeRestoreRequest */
|
|
127
|
+
/** @typedef {import("./v1/backups.js").V1BackupDestinationRequest} V1BackupDestinationRequest */
|
|
128
|
+
|
|
129
|
+
export { V1Imports } from "./v1/imports.js";
|
|
130
|
+
/** @typedef {import("./v1/imports.js").V1ImportJob} V1ImportJob */
|
|
131
|
+
/** @typedef {import("./v1/imports.js").V1ImportOptions} V1ImportOptions */
|
|
132
|
+
/** @typedef {import("./v1/imports.js").V1CreateImportRequest} V1CreateImportRequest */
|
|
133
|
+
|
|
134
|
+
export { createTerraBaseSystemClient, V1Health, V1Platform } from "./v1/system.js";
|
|
135
|
+
/** @typedef {import("./v1/system.js").TerraBaseSystemClientConfig} TerraBaseSystemClientConfig */
|
|
136
|
+
/** @typedef {import("./v1/system.js").V1ProbeStatus} V1ProbeStatus */
|
|
137
|
+
/** @typedef {import("./v1/system.js").V1ReplicationHealth} V1ReplicationHealth */
|
|
138
|
+
/** @typedef {import("./v1/system.js").V1PlatformDatabases} V1PlatformDatabases */
|
|
139
|
+
/** @typedef {import("./v1/shape-manifest.js").V1ShapeManifest} V1ShapeManifest */
|
|
140
|
+
/** @typedef {import("./v1/shape-manifest.js").V1ShapeManifestRequest} V1ShapeManifestRequest */
|
|
141
|
+
/** @typedef {import("./v1/shape-reader.js").V1ShapeKeyRequest} V1ShapeKeyRequest */
|
|
142
|
+
/** @typedef {import("./v1/shape-reader.js").V1ShapeKey} V1ShapeKey */
|
package/src/local/environment.js
CHANGED
package/src/ts-auth.js
CHANGED
|
@@ -15,7 +15,6 @@ import { requireDeadLetterId } from "./ts-auth-ids.js";
|
|
|
15
15
|
* @typedef {Omit<TsAuthRuntimeConfig, "apiBaseUrl"> & {
|
|
16
16
|
* readonly apiBaseUrl: string; // The ts-auth deployment's public origin; there is no default.
|
|
17
17
|
* readonly instanceSlug: string; // The instance the `sk_` key belongs to; it prefixes every route.
|
|
18
|
-
* readonly useReverseProxy?: boolean; // Route public auth calls through the reverse proxy instead of regional discovery.
|
|
19
18
|
* }} TsAuthClientConfig
|
|
20
19
|
*/
|
|
21
20
|
|
|
@@ -2360,7 +2359,6 @@ export class TsAuthOidcClient {
|
|
|
2360
2359
|
routeName: "tsauth.oidc.resourceIntrospect",
|
|
2361
2360
|
formBody: form,
|
|
2362
2361
|
resourceIntrospectionBasic: basic,
|
|
2363
|
-
issuer: expectedIssuer,
|
|
2364
2362
|
}),
|
|
2365
2363
|
/** @returns {value is TsAuthOidcResourceIntrospection} */
|
|
2366
2364
|
(value) => {
|
|
@@ -2520,9 +2518,7 @@ export class TsAuthClient {
|
|
|
2520
2518
|
);
|
|
2521
2519
|
}
|
|
2522
2520
|
const slug = validateInstanceSlug(config?.instanceSlug);
|
|
2523
|
-
this.#http = new TsAuthHttp(config
|
|
2524
|
-
authReverseProxy: config.useReverseProxy === true,
|
|
2525
|
-
});
|
|
2521
|
+
this.#http = new TsAuthHttp(config);
|
|
2526
2522
|
requireBareOrigin(this.#http.config.apiBaseUrl);
|
|
2527
2523
|
this.users = new TsAuthUsersClient(this.#http, slug);
|
|
2528
2524
|
this.sessions = new TsAuthSessionsClient(this.#http, slug);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { requireId } from "../ids.js";
|
|
2
|
+
import { requireName } from "./ids.js";
|
|
3
|
+
import { flag, list, nullable, numeric, optional, record, text } from "./management-decode.js";
|
|
4
|
+
import { members, mutationOptions, pageQuery } from "./management-validation.js";
|
|
5
|
+
import { malformed, object } from "./decode.js";
|
|
6
|
+
import { stringifyExactJson } from "./json.js";
|
|
7
|
+
/** @import { V1Transport } from "./transport.js" */
|
|
8
|
+
/** @import { V1RequestOptions } from "./terrabase-v1.js" */
|
|
9
|
+
const backup = record({ id: requireId, databaseId: requireId, status: text, trigger: text, sizeBytes: numeric, createdAt: text });
|
|
10
|
+
const backupPage = record({ items: list(backup), next: nullable(text) });
|
|
11
|
+
const policy = record({ fullBackupCron: nullable(text), retentionDays: numeric, disabled: flag, updatedAt: nullable(text) });
|
|
12
|
+
const window = record({ earliest: text, latest: text });
|
|
13
|
+
const plan = record({ window, pointInTime: text, snapshotAt: text, latestSegmentAt: nullable(text), segmentCount: numeric, historyCheck: text });
|
|
14
|
+
const restored = record({ id: requireId, projectId: requireId, name: text, branchedFrom: nullable(requireId), createdAt: text,
|
|
15
|
+
homeRegion: optional(nullable(text)), replicaRegions: optional(nullable(list(text))), placementVersion: optional(numeric), replicas: list(text) });
|
|
16
|
+
const destination = record({ endpoint: text, region: text, bucket: text, mode: text, updatedAt: text });
|
|
17
|
+
/** @typedef {ReturnType<typeof backup>} V1Backup */
|
|
18
|
+
/** @typedef {ReturnType<typeof backupPage>} V1BackupPage */
|
|
19
|
+
/** @typedef {ReturnType<typeof policy>} V1BackupPolicy */
|
|
20
|
+
/** @typedef {ReturnType<typeof window>} V1RestoreWindow */
|
|
21
|
+
/** @typedef {ReturnType<typeof plan>} V1PointInTimePlan */
|
|
22
|
+
/** @typedef {ReturnType<typeof restored>} V1RestoredDatabase */
|
|
23
|
+
/** @typedef {ReturnType<typeof destination>} V1BackupDestination */
|
|
24
|
+
/** @typedef {{ readonly fullBackupCron?: string | null; readonly retentionDays?: number | null; readonly disabled?: boolean | null }} V1BackupPolicyRequest */
|
|
25
|
+
/** @typedef {{ readonly pointInTime: string; readonly destination?: "platform" | "customer" | null }} V1PointInTimeRequest */
|
|
26
|
+
/** @typedef {V1PointInTimeRequest & { readonly name?: string | null }} V1PointInTimeRestoreRequest */
|
|
27
|
+
/** @typedef {{ readonly endpoint: string; readonly region: string; readonly bucket: string; readonly accessKeyId: string; readonly secretKey: string; readonly mode: "both" | "customer" }} V1BackupDestinationRequest */
|
|
28
|
+
/** @param {unknown} value @returns {"platform" | "customer"} */
|
|
29
|
+
function source(value) {
|
|
30
|
+
if (value !== "platform" && value !== "customer") throw new TypeError("destination must be platform or customer.");
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
/** @param {V1PointInTimeRequest} request */
|
|
34
|
+
function pointInTime(request) {
|
|
35
|
+
if (typeof request.pointInTime !== "string" || !Number.isFinite(Date.parse(request.pointInTime))) throw new TypeError("pointInTime must be an RFC 3339 instant.");
|
|
36
|
+
if (request.destination !== undefined && request.destination !== null) source(request.destination);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Backups and explicit point-in-time restore sources for one database. */
|
|
40
|
+
export class V1Backups {
|
|
41
|
+
/** @type {V1Transport} */
|
|
42
|
+
#transport;
|
|
43
|
+
/** @type {{database: string}} */
|
|
44
|
+
#params;
|
|
45
|
+
/** @param {V1Transport} transport @param {string} database */
|
|
46
|
+
constructor(transport, database) { this.#transport = transport; this.#params = { database: requireName("database", database) }; Object.freeze(this); }
|
|
47
|
+
/** @param {V1RequestOptions} [options] @returns {Promise<V1Backup>} */
|
|
48
|
+
async create(options = {}) {
|
|
49
|
+
mutationOptions(options);
|
|
50
|
+
return backup((await this.#transport.call("createBackup", this.#params, { signal: options.signal })).value);
|
|
51
|
+
}
|
|
52
|
+
/** @param {import("./catalog.js").V1DatabaseListRequest} [request] @param {V1RequestOptions} [options] @returns {Promise<V1BackupPage>} */
|
|
53
|
+
async list(request = {}, options = {}) { return backupPage((await this.#transport.call("listBackups", this.#params, { query: pageQuery(request), signal: options.signal })).value); }
|
|
54
|
+
/** @param {string} id @param {{readonly name?: string | null}} [request] @param {V1RequestOptions} [options] @returns {Promise<V1RestoredDatabase>} */
|
|
55
|
+
async restore(id, request = {}, options = {}) {
|
|
56
|
+
members(request, ["name"]);
|
|
57
|
+
if (request.name !== undefined && request.name !== null) requireName("database", request.name);
|
|
58
|
+
mutationOptions(options);
|
|
59
|
+
return restored((await this.#transport.call("restoreBackup", { ...this.#params, backup: requireId(id) }, {
|
|
60
|
+
body: stringifyExactJson(request, "restore"), signal: options.signal,
|
|
61
|
+
})).value);
|
|
62
|
+
}
|
|
63
|
+
/** @param {V1RequestOptions} [options] @returns {Promise<V1BackupPolicy>} */
|
|
64
|
+
async getPolicy(options = {}) { return policy((await this.#transport.call("getBackupPolicy", this.#params, { signal: options.signal })).value); }
|
|
65
|
+
/** @param {V1BackupPolicyRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1BackupPolicy>} */
|
|
66
|
+
async setPolicy(request, options = {}) {
|
|
67
|
+
members(request, ["fullBackupCron", "retentionDays", "disabled"]);
|
|
68
|
+
mutationOptions(options);
|
|
69
|
+
return policy((await this.#transport.call("setBackupPolicy", this.#params, { body: stringifyExactJson(request, "backup policy"), signal: options.signal })).value);
|
|
70
|
+
}
|
|
71
|
+
/** @param {{readonly destination?: "platform" | "customer"}} [request] @param {V1RequestOptions} [options] @returns {Promise<V1RestoreWindow>} */
|
|
72
|
+
async window(request = {}, options = {}) {
|
|
73
|
+
members(request, ["destination"]);
|
|
74
|
+
const query = new URLSearchParams();
|
|
75
|
+
if (request.destination !== undefined) query.set("destination", source(request.destination));
|
|
76
|
+
return window((await this.#transport.call("getRestoreWindow", this.#params, { query, signal: options.signal })).value);
|
|
77
|
+
}
|
|
78
|
+
/** @param {V1PointInTimeRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1PointInTimePlan>} */
|
|
79
|
+
async plan(request, options = {}) {
|
|
80
|
+
members(request, ["pointInTime", "destination"]);
|
|
81
|
+
pointInTime(request);
|
|
82
|
+
return plan((await this.#transport.call("planPointInTime", this.#params, { body: stringifyExactJson(request, "restore plan"), signal: options.signal })).value);
|
|
83
|
+
}
|
|
84
|
+
/** @param {V1PointInTimeRestoreRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1RestoredDatabase>} */
|
|
85
|
+
async restorePointInTime(request, options = {}) {
|
|
86
|
+
members(request, ["pointInTime", "destination", "name"]);
|
|
87
|
+
pointInTime(request);
|
|
88
|
+
if (request.name !== undefined && request.name !== null) requireName("database", request.name);
|
|
89
|
+
mutationOptions(options);
|
|
90
|
+
return restored((await this.#transport.call("restorePointInTime", this.#params, { body: stringifyExactJson(request, "point-in-time restore"), signal: options.signal })).value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Write-only destination credentials are sent only to the configured API, never retained in the client. */
|
|
94
|
+
export class V1BackupDestinationClient {
|
|
95
|
+
/** @type {V1Transport} */
|
|
96
|
+
#transport;
|
|
97
|
+
/** @param {V1Transport} transport */
|
|
98
|
+
constructor(transport) { this.#transport = transport; Object.freeze(this); }
|
|
99
|
+
/** @param {V1RequestOptions} [options] @returns {Promise<V1BackupDestination>} */
|
|
100
|
+
async get(options = {}) { return destination((await this.#transport.call("getBackupDestination", {}, { signal: options.signal })).value); }
|
|
101
|
+
/** @param {V1BackupDestinationRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1BackupDestination>} */
|
|
102
|
+
async set(request, options = {}) {
|
|
103
|
+
members(request, ["endpoint", "region", "bucket", "accessKeyId", "secretKey", "mode"]);
|
|
104
|
+
mutationOptions(options);
|
|
105
|
+
const raw = object((await this.#transport.call("setBackupDestination", {}, { body: stringifyExactJson(request, "backup destination"), signal: options.signal })).value, "backup destination");
|
|
106
|
+
if (raw.accessKeyId !== undefined || raw.secretKey !== undefined) malformed("write-only destination credentials");
|
|
107
|
+
return destination(raw);
|
|
108
|
+
}
|
|
109
|
+
/** @param {V1RequestOptions} [options] @returns {Promise<void>} */
|
|
110
|
+
async delete(options = {}) { mutationOptions(options); await this.#transport.call("deleteBackupDestination", {}, { signal: options.signal }); }
|
|
111
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { requireId } from "../ids.js";
|
|
2
|
+
import { array, malformed, object, string } from "./decode.js";
|
|
3
|
+
import { requireName } from "./ids.js";
|
|
4
|
+
import { isJsonPointer } from "./filter.js";
|
|
5
|
+
import { isJsonObject, stringifyExactJson } from "./json.js";
|
|
6
|
+
/**
|
|
7
|
+
* @import { JsonInputObject, JsonObject, JsonValue } from "./json.js"
|
|
8
|
+
* @import { V1Transport } from "./transport.js"
|
|
9
|
+
* @import { V1RequestOptions } from "./terrabase-v1.js"
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {object} V1ManagedDatabase
|
|
14
|
+
* @property {string} id Canonical database ULID.
|
|
15
|
+
* @property {string} projectId Canonical owning-project ULID.
|
|
16
|
+
* @property {string} name Database name used in data routes.
|
|
17
|
+
* @property {string | null} branchedFrom Source database ULID, or null.
|
|
18
|
+
* @property {string} createdAt ISO date-time returned by the producer.
|
|
19
|
+
* @property {string} region Serving region.
|
|
20
|
+
* @property {string} homeRegion Management region.
|
|
21
|
+
* @property {readonly string[]} replicaRegions Other serving regions; empty when allRegions is true.
|
|
22
|
+
* @property {boolean} allRegions True when a full copy is served in every serving region, including later ones.
|
|
23
|
+
*/
|
|
24
|
+
/** @typedef {{ readonly name: string; readonly homeRegion?: string | null | undefined; readonly replicaRegions?: readonly string[] | null | undefined; readonly allRegions?: boolean | undefined }} V1DatabaseCreateRequest */
|
|
25
|
+
/** @typedef {{ readonly replicaRegions?: readonly string[] | undefined; readonly allRegions?: true | undefined; readonly homeRegion?: string | null | undefined }} V1DatabaseUpdateRequest */
|
|
26
|
+
/** @typedef {{ readonly name: string }} V1DatabaseBranchRequest */
|
|
27
|
+
/** @typedef {{ readonly after?: string | undefined; readonly limit?: number | undefined }} V1DatabaseListRequest */
|
|
28
|
+
/** @typedef {{ readonly items: readonly V1ManagedDatabase[]; readonly next: string | null }} V1DatabasePage */
|
|
29
|
+
/** @typedef {{ readonly schema: JsonObject | boolean | null; readonly dialect?: string | null | undefined }} V1CollectionSchema */
|
|
30
|
+
/** The expiry member, or null when documents never expire. @typedef {{ readonly path: string | null }} V1CollectionTtl */
|
|
31
|
+
|
|
32
|
+
/** @param {unknown} value @param {readonly string[]} allowed @returns {void} */
|
|
33
|
+
function members(value, allowed) {
|
|
34
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) ||
|
|
35
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
36
|
+
throw new TypeError("A catalog request must be a plain object.");
|
|
37
|
+
for (const key of Object.keys(value))
|
|
38
|
+
if (!allowed.includes(key)) throw new TypeError(`Unknown catalog request member ${key}.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @param {object} options @returns {void} */
|
|
42
|
+
function mutationOptions(options) {
|
|
43
|
+
if ("idempotencyKey" in options)
|
|
44
|
+
throw new TypeError("The current producer does not support Idempotency-Key; mutations are never automatically retried.");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @param {unknown} value @returns {string} */
|
|
48
|
+
function region(value) {
|
|
49
|
+
if (typeof value !== "string" || !/^[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/u.test(value))
|
|
50
|
+
throw new TypeError("A region is 1-63 lowercase letters, digits or hyphens, starts with a letter and cannot end with a hyphen.");
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @param {unknown} value @param {string | null | undefined} home @returns {readonly string[]} */
|
|
55
|
+
function replicas(value, home) {
|
|
56
|
+
if (!Array.isArray(value) || value.length > 8) throw new TypeError("replicaRegions is an array of at most 8 regions.");
|
|
57
|
+
const result = value.map(region);
|
|
58
|
+
if (new Set(result).size !== result.length) throw new TypeError("replicaRegions must not repeat a region.");
|
|
59
|
+
if (home !== undefined && home !== null && result.includes(home))
|
|
60
|
+
throw new TypeError("replicaRegions must not include homeRegion.");
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @param {JsonValue | undefined} value @returns {V1ManagedDatabase} */
|
|
65
|
+
function database(value) {
|
|
66
|
+
const raw = object(value, "managed database");
|
|
67
|
+
const createdAt = string(raw.createdAt, "database createdAt");
|
|
68
|
+
if (!/^\d{4}-\d{2}-\d{2}T/u.test(createdAt) || !Number.isFinite(Date.parse(createdAt))) malformed("database createdAt");
|
|
69
|
+
if (typeof raw.allRegions !== "boolean") malformed("database allRegions");
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
id: requireId(raw.id, "Database ID"), projectId: requireId(raw.projectId, "Project ID"),
|
|
72
|
+
name: requireName("database", raw.name),
|
|
73
|
+
branchedFrom: raw.branchedFrom === null ? null : requireId(raw.branchedFrom, "Source database ID"),
|
|
74
|
+
createdAt, region: region(raw.region), homeRegion: region(raw.homeRegion),
|
|
75
|
+
replicaRegions: Object.freeze(array(raw.replicaRegions, "database replicaRegions").map(region)),
|
|
76
|
+
allRegions: raw.allRegions,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Public database catalog operations, bound to the transport's authorized project. */
|
|
81
|
+
export class V1Databases {
|
|
82
|
+
/** @type {V1Transport} */
|
|
83
|
+
#transport;
|
|
84
|
+
/** @param {V1Transport} transport */
|
|
85
|
+
constructor(transport) { this.#transport = transport; Object.freeze(this); }
|
|
86
|
+
|
|
87
|
+
/** @param {V1DatabaseCreateRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1ManagedDatabase>} */
|
|
88
|
+
async create(request, options = {}) {
|
|
89
|
+
members(request, ["name", "homeRegion", "replicaRegions", "allRegions"]);
|
|
90
|
+
mutationOptions(options);
|
|
91
|
+
const name = requireName("database", request.name);
|
|
92
|
+
if (request.homeRegion !== undefined && request.homeRegion !== null) region(request.homeRegion);
|
|
93
|
+
if (request.replicaRegions !== undefined && request.replicaRegions !== null) replicas(request.replicaRegions, request.homeRegion);
|
|
94
|
+
if (request.allRegions !== undefined && typeof request.allRegions !== "boolean") throw new TypeError("allRegions must be a boolean.");
|
|
95
|
+
if (request.allRegions === true && request.replicaRegions !== undefined && request.replicaRegions !== null)
|
|
96
|
+
throw new TypeError("allRegions cannot be combined with replicaRegions.");
|
|
97
|
+
const response = await this.#transport.call("createDatabase", { database: name }, {
|
|
98
|
+
body: stringifyExactJson({ name,
|
|
99
|
+
...(request.homeRegion === undefined ? {} : { homeRegion: request.homeRegion }),
|
|
100
|
+
...(request.replicaRegions === undefined ? {} : { replicaRegions: request.replicaRegions }),
|
|
101
|
+
...(request.allRegions === undefined ? {} : { allRegions: request.allRegions }),
|
|
102
|
+
}, "database create"), signal: options.signal,
|
|
103
|
+
});
|
|
104
|
+
return database(response.value);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @param {V1DatabaseListRequest} [request] @param {V1RequestOptions} [options] @returns {Promise<V1DatabasePage>} */
|
|
108
|
+
async list(request = {}, options = {}) {
|
|
109
|
+
members(request, ["after", "limit"]);
|
|
110
|
+
const query = new URLSearchParams();
|
|
111
|
+
if (request.after !== undefined) {
|
|
112
|
+
if (typeof request.after !== "string") throw new TypeError("Database after must be a string.");
|
|
113
|
+
query.set("after", request.after);
|
|
114
|
+
}
|
|
115
|
+
if (request.limit !== undefined) {
|
|
116
|
+
if (!Number.isInteger(request.limit) || request.limit < 1 || request.limit > 100) throw new TypeError("Database list limit is 1-100.");
|
|
117
|
+
query.set("limit", String(request.limit));
|
|
118
|
+
}
|
|
119
|
+
const response = await this.#transport.call("listDatabases", {}, { query, signal: options.signal });
|
|
120
|
+
const raw = object(response.value, "database page");
|
|
121
|
+
if (raw.next !== null && (typeof raw.next !== "string" || raw.next === "")) malformed("database next");
|
|
122
|
+
return Object.freeze({ items: Object.freeze(array(raw.items, "databases").map(database)), next: raw.next });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @param {string} name @param {V1RequestOptions} [options] @returns {Promise<V1ManagedDatabase>} */
|
|
126
|
+
async get(name, options = {}) {
|
|
127
|
+
const response = await this.#transport.call("getDatabase", { database: requireName("database", name) }, { signal: options.signal });
|
|
128
|
+
return database(response.value);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @param {string} name @param {V1DatabaseUpdateRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1ManagedDatabase>} */
|
|
132
|
+
async update(name, request, options = {}) {
|
|
133
|
+
members(request, ["homeRegion", "replicaRegions", "allRegions"]);
|
|
134
|
+
mutationOptions(options);
|
|
135
|
+
if (request.homeRegion !== undefined && request.homeRegion !== null) region(request.homeRegion);
|
|
136
|
+
if (request.allRegions === true) {
|
|
137
|
+
if (request.replicaRegions !== undefined) throw new TypeError("allRegions cannot be combined with replicaRegions.");
|
|
138
|
+
} else {
|
|
139
|
+
if (request.allRegions !== undefined) throw new TypeError("allRegions must be true; send replicaRegions to turn it off.");
|
|
140
|
+
replicas(request.replicaRegions, request.homeRegion);
|
|
141
|
+
}
|
|
142
|
+
const response = await this.#transport.call("updateDatabase", { database: requireName("database", name) }, {
|
|
143
|
+
body: stringifyExactJson({
|
|
144
|
+
...(request.allRegions === true ? { allRegions: true } : { replicaRegions: request.replicaRegions }),
|
|
145
|
+
...(request.homeRegion === undefined ? {} : { homeRegion: request.homeRegion }),
|
|
146
|
+
}, "database update"), signal: options.signal,
|
|
147
|
+
});
|
|
148
|
+
return database(response.value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @param {string} name @param {V1RequestOptions} [options] @returns {Promise<void>} */
|
|
152
|
+
async delete(name, options = {}) {
|
|
153
|
+
mutationOptions(options);
|
|
154
|
+
await this.#transport.call("deleteDatabase", { database: requireName("database", name) }, { signal: options.signal });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @param {string} name @param {V1DatabaseBranchRequest} request @param {V1RequestOptions} [options] @returns {Promise<V1ManagedDatabase>} */
|
|
158
|
+
async branch(name, request, options = {}) {
|
|
159
|
+
members(request, ["name"]);
|
|
160
|
+
mutationOptions(options);
|
|
161
|
+
const response = await this.#transport.call("branchDatabase", { database: requireName("database", name) }, {
|
|
162
|
+
body: stringifyExactJson({ name: requireName("database", request.name) }, "database branch"), signal: options.signal,
|
|
163
|
+
});
|
|
164
|
+
return database(response.value);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** @param {JsonValue | undefined} value @returns {V1CollectionSchema} */
|
|
169
|
+
function collectionSchema(value) {
|
|
170
|
+
const raw = object(value, "collection schema");
|
|
171
|
+
const schema = raw.schema;
|
|
172
|
+
if (schema !== null && typeof schema !== "boolean" && !isJsonObject(schema)) malformed("declared schema");
|
|
173
|
+
const dialect = raw.dialect;
|
|
174
|
+
if (dialect !== undefined && dialect !== null && typeof dialect !== "string") malformed("schema dialect");
|
|
175
|
+
return Object.freeze({ schema, ...(dialect === undefined ? {} : { dialect }) });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @param {boolean | JsonInputObject} schema @returns {string} */
|
|
179
|
+
function schemaText(schema) {
|
|
180
|
+
if (typeof schema !== "boolean" && !isJsonObject(schema)) throw new TypeError("A declared schema must be a JSON object or boolean.");
|
|
181
|
+
const body = stringifyExactJson(schema, "declared schema");
|
|
182
|
+
if (new TextEncoder().encode(body).byteLength > 64 * 1024) throw new TypeError("A declared schema is at most 64 KiB.");
|
|
183
|
+
let nodes = 0;
|
|
184
|
+
/** @param {unknown} value @param {number} depth */
|
|
185
|
+
const visit = (value, depth) => {
|
|
186
|
+
if (++nodes > 4093) throw new TypeError("A declared schema holds at most 4093 values.");
|
|
187
|
+
if (!Array.isArray(value) && !isJsonObject(value)) return;
|
|
188
|
+
if (depth >= 15) throw new TypeError("A declared schema nests at most 15 levels.");
|
|
189
|
+
for (const child of Object.values(value)) visit(child, depth + 1);
|
|
190
|
+
};
|
|
191
|
+
visit(schema, 0);
|
|
192
|
+
return body;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionSchema>} */
|
|
196
|
+
export async function getCollectionSchema(transport, params, options = {}) {
|
|
197
|
+
return collectionSchema((await transport.call("getCollectionSchema", params, { signal: options.signal })).value);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {boolean | JsonInputObject} schema @param {V1RequestOptions} [options] @returns {Promise<V1CollectionSchema>} */
|
|
201
|
+
export async function putCollectionSchema(transport, params, schema, options = {}) {
|
|
202
|
+
mutationOptions(options);
|
|
203
|
+
return collectionSchema((await transport.call("putCollectionSchema", params, {
|
|
204
|
+
body: schemaText(schema), signal: options.signal,
|
|
205
|
+
})).value);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionSchema>} */
|
|
209
|
+
export async function deleteCollectionSchema(transport, params, options = {}) {
|
|
210
|
+
mutationOptions(options);
|
|
211
|
+
return collectionSchema((await transport.call("deleteCollectionSchema", params, { signal: options.signal })).value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** @param {JsonValue | undefined} value @returns {V1CollectionTtl} */
|
|
215
|
+
function collectionTtl(value) {
|
|
216
|
+
const path = object(value, "collection TTL").path;
|
|
217
|
+
if (path !== null && typeof path !== "string") malformed("TTL path");
|
|
218
|
+
return Object.freeze({ path });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
222
|
+
export async function getCollectionTtl(transport, params, options = {}) {
|
|
223
|
+
return collectionTtl((await transport.call("getCollectionTtl", params, { signal: options.signal })).value);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {string} path @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
227
|
+
export async function putCollectionTtl(transport, params, path, options = {}) {
|
|
228
|
+
mutationOptions(options);
|
|
229
|
+
if (!isJsonPointer(path))
|
|
230
|
+
throw new TypeError("A TTL path is a JSON Pointer of 1 to 16 object members.");
|
|
231
|
+
return collectionTtl((await transport.call("putCollectionTtl", params, {
|
|
232
|
+
body: stringifyExactJson({ path }, "collection TTL"), signal: options.signal,
|
|
233
|
+
})).value);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** @param {V1Transport} transport @param {Readonly<Record<string, string>>} params @param {V1RequestOptions} [options] @returns {Promise<V1CollectionTtl>} */
|
|
237
|
+
export async function deleteCollectionTtl(transport, params, options = {}) {
|
|
238
|
+
mutationOptions(options);
|
|
239
|
+
return collectionTtl((await transport.call("deleteCollectionTtl", params, { signal: options.signal })).value);
|
|
240
|
+
}
|