@atscript/db-client 0.1.163 → 0.1.165
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/dist/client-error-C9Q5xCuW.cjs +222 -0
- package/dist/client-error-Dn4WeNft.mjs +175 -0
- package/dist/index.cjs +54 -194
- package/dist/index.d.cts +17 -182
- package/dist/index.d.mts +17 -182
- package/dist/index.mjs +34 -175
- package/dist/{validator-CI8qxCps.d.cts → validator-DrJYT-pz.d.cts} +235 -22
- package/dist/{validator-zwjB-484.d.mts → validator-tvR4Sl3l.d.mts} +235 -22
- package/dist/validator.cjs +5 -17
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.mjs +1 -13
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -1,149 +1,7 @@
|
|
|
1
|
+
import { a as ClientError, c as VersionMismatchError, i as ActionUnsupportedError, n as ActionNotFoundError, o as ClientValidationError, r as ActionTargetError, s as TransportError, t as ActionDisabledError } from "./client-error-Dn4WeNft.mjs";
|
|
1
2
|
import { buildUrl } from "@uniqu/url/builder";
|
|
2
3
|
import { deserializeAnnotatedType } from "@atscript/typescript/utils";
|
|
3
4
|
import { bucketSeries, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
|
|
4
|
-
//#region src/client-error.ts
|
|
5
|
-
/**
|
|
6
|
-
* Error thrown by `Client` when the server responds with a non-2xx status code.
|
|
7
|
-
* Captures the HTTP status and the structured error body from moost-db.
|
|
8
|
-
*/
|
|
9
|
-
var ClientError = class extends Error {
|
|
10
|
-
status;
|
|
11
|
-
body;
|
|
12
|
-
name = "ClientError";
|
|
13
|
-
constructor(status, body) {
|
|
14
|
-
super(body.message || `HTTP ${status}`);
|
|
15
|
-
this.status = status;
|
|
16
|
-
this.body = body;
|
|
17
|
-
}
|
|
18
|
-
/** Shortcut to structured validation/DB errors from the server. */
|
|
19
|
-
get errors() {
|
|
20
|
-
return this.body.errors ?? [];
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Typed marker thrown by `Client._send` when the server response body's
|
|
25
|
-
* `name === 'ActionDisabledError'`. The transport / status / base body are
|
|
26
|
-
* identical to a generic `ClientError`; this subclass adds typed accessors
|
|
27
|
-
* so consumers can write `catch (e) { if (e instanceof ActionDisabledError) … }`
|
|
28
|
-
* to access `action` / `id` / `ids` / `reason` / `reasons` without indexing
|
|
29
|
-
* into `body`.
|
|
30
|
-
*/
|
|
31
|
-
var ActionDisabledError = class extends ClientError {
|
|
32
|
-
name = "ActionDisabledError";
|
|
33
|
-
/** The `@DbAction` name that rejected the request. */
|
|
34
|
-
get action() {
|
|
35
|
-
return this.body.action;
|
|
36
|
-
}
|
|
37
|
-
/** Present only for `'row'`-level rejections. */
|
|
38
|
-
get id() {
|
|
39
|
-
return this.body.id;
|
|
40
|
-
}
|
|
41
|
-
/** Present only for `'rows'`-level rejections (full list of failing IDs). */
|
|
42
|
-
get ids() {
|
|
43
|
-
return this.body.ids;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Human-readable reason shared by every rejected row — present only when
|
|
47
|
-
* the server's `disabled` predicate returned one. `message` already
|
|
48
|
-
* carries it.
|
|
49
|
-
*
|
|
50
|
-
* @since 0.1.141
|
|
51
|
-
*/
|
|
52
|
-
get reason() {
|
|
53
|
-
return this.body.reason;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* `'rows'`-level only: per-row reasons index-aligned with {@link ids}
|
|
57
|
-
* (`null` where that row has none). Present only when at least one
|
|
58
|
-
* rejected row carries a reason.
|
|
59
|
-
*
|
|
60
|
-
* @since 0.1.141
|
|
61
|
-
*/
|
|
62
|
-
get reasons() {
|
|
63
|
-
return this.body.reasons;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* Typed marker thrown by `Client` when a query-targeted action request is
|
|
68
|
-
* refused (body `name === 'ActionTargetError'`): `TARGET_INVALID` (400),
|
|
69
|
-
* `TARGET_TOO_LARGE` (400, `cap`) or `TARGET_CHANGED` (409, `matched` — the
|
|
70
|
-
* rows the query matches now; re-confirm and retry with that count).
|
|
71
|
-
*
|
|
72
|
-
* @since 0.1.147
|
|
73
|
-
*/
|
|
74
|
-
var ActionTargetError = class extends ClientError {
|
|
75
|
-
name = "ActionTargetError";
|
|
76
|
-
get code() {
|
|
77
|
-
return this.body.code;
|
|
78
|
-
}
|
|
79
|
-
get action() {
|
|
80
|
-
return this.body.action;
|
|
81
|
-
}
|
|
82
|
-
get matched() {
|
|
83
|
-
return this.body.matched;
|
|
84
|
-
}
|
|
85
|
-
get cap() {
|
|
86
|
-
return this.body.cap;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
90
|
-
* Typed marker thrown by `Client._send` when the server response body's
|
|
91
|
-
* `kind === 'version_mismatch'`. The transport / status / base body are
|
|
92
|
-
* identical to a generic `ClientError`; this subclass adds a typed
|
|
93
|
-
* `currentVersion` accessor so consumers can write
|
|
94
|
-
* `catch (e) { if (e instanceof VersionMismatchError) … }` without casting.
|
|
95
|
-
*/
|
|
96
|
-
var VersionMismatchError = class extends ClientError {
|
|
97
|
-
name = "VersionMismatchError";
|
|
98
|
-
/** Row's current server-side version — clients refresh + retry against it. */
|
|
99
|
-
get currentVersion() {
|
|
100
|
-
return this.body.currentVersion;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
/**
|
|
104
|
-
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
105
|
-
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
106
|
-
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
107
|
-
* server answered and rejected — the request's outcome is unknown: a write
|
|
108
|
-
* may have committed. Reload before retrying a non-idempotent write.
|
|
109
|
-
* The underlying error is the standard `cause`.
|
|
110
|
-
*/
|
|
111
|
-
var TransportError = class extends Error {
|
|
112
|
-
method;
|
|
113
|
-
url;
|
|
114
|
-
name = "TransportError";
|
|
115
|
-
constructor(method, url, detail, cause) {
|
|
116
|
-
super(`${method} ${url}: ${detail}`, { cause });
|
|
117
|
-
this.method = method;
|
|
118
|
-
this.url = url;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
122
|
-
var ActionNotFoundError = class extends Error {
|
|
123
|
-
action;
|
|
124
|
-
name = "ActionNotFoundError";
|
|
125
|
-
constructor(action) {
|
|
126
|
-
super(`Action "${action}" is not declared on this controller`);
|
|
127
|
-
this.action = action;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
132
|
-
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
133
|
-
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
134
|
-
* when no browser environment and no `navigate` option are configured.
|
|
135
|
-
*/
|
|
136
|
-
var ActionUnsupportedError = class extends Error {
|
|
137
|
-
action;
|
|
138
|
-
processor;
|
|
139
|
-
name = "ActionUnsupportedError";
|
|
140
|
-
constructor(action, processor, message) {
|
|
141
|
-
super(message);
|
|
142
|
-
this.action = action;
|
|
143
|
-
this.processor = processor;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
//#endregion
|
|
147
5
|
//#region src/meta-store.ts
|
|
148
6
|
/** ETags remembered per store key (most recent first). */
|
|
149
7
|
const CANDIDATES_PER_KEY = 4;
|
|
@@ -335,17 +193,17 @@ var Client = class {
|
|
|
335
193
|
* The response type narrows by the literal `$with` array in `query` —
|
|
336
194
|
* relations not listed in `$with` are stripped from the row type.
|
|
337
195
|
*/
|
|
338
|
-
async query(query) {
|
|
339
|
-
return this._get("query", query);
|
|
196
|
+
async query(query, opts) {
|
|
197
|
+
return this._get("query", query, opts?.signal);
|
|
340
198
|
}
|
|
341
199
|
/**
|
|
342
200
|
* `GET /query` with `$count: true` — returns record count.
|
|
343
201
|
*/
|
|
344
|
-
async count(query) {
|
|
202
|
+
async count(query, opts) {
|
|
345
203
|
return this._get("query", {
|
|
346
204
|
...query,
|
|
347
205
|
controls: { $count: true }
|
|
348
|
-
});
|
|
206
|
+
}, opts?.signal);
|
|
349
207
|
}
|
|
350
208
|
/**
|
|
351
209
|
* `GET /query` with `$groupBy` — aggregate query with typed dimension/measure fields.
|
|
@@ -364,8 +222,8 @@ var Client = class {
|
|
|
364
222
|
* read a representative row ordered by `$rowOrder`. The URL forms are `expr(a/b):x`,
|
|
365
223
|
* `sum(a*b):x`, `first(f):x` and `$rowOrder=f,-id`.
|
|
366
224
|
*/
|
|
367
|
-
async aggregate(query) {
|
|
368
|
-
return this._get("query", query);
|
|
225
|
+
async aggregate(query, opts) {
|
|
226
|
+
return this._get("query", query, opts?.signal);
|
|
369
227
|
}
|
|
370
228
|
/**
|
|
371
229
|
* `GET /pages` — paginated query with typed filter and relations.
|
|
@@ -373,7 +231,7 @@ var Client = class {
|
|
|
373
231
|
* Response rows narrow by the literal `$with` array — same algebra as
|
|
374
232
|
* {@link query}.
|
|
375
233
|
*/
|
|
376
|
-
async pages(query, page = 1, size = 10) {
|
|
234
|
+
async pages(query, page = 1, size = 10, opts) {
|
|
377
235
|
return this._get("pages", {
|
|
378
236
|
...query,
|
|
379
237
|
controls: {
|
|
@@ -381,7 +239,7 @@ var Client = class {
|
|
|
381
239
|
$page: page,
|
|
382
240
|
$size: size
|
|
383
241
|
}
|
|
384
|
-
});
|
|
242
|
+
}, opts?.signal);
|
|
385
243
|
}
|
|
386
244
|
/**
|
|
387
245
|
* `GET /geo` — distance-ranked geospatial search (tables with `@db.index.geo`).
|
|
@@ -392,19 +250,19 @@ var Client = class {
|
|
|
392
250
|
* in `query.controls`; filter / `$select` / `$with` / `$skip` / `$limit`
|
|
393
251
|
* compose as usual.
|
|
394
252
|
*/
|
|
395
|
-
async geoSearch(point, query) {
|
|
253
|
+
async geoSearch(point, query, opts) {
|
|
396
254
|
return this._get("geo", {
|
|
397
255
|
...query,
|
|
398
256
|
controls: {
|
|
399
257
|
...query?.controls,
|
|
400
258
|
$center: point.join(",")
|
|
401
259
|
}
|
|
402
|
-
});
|
|
260
|
+
}, opts?.signal);
|
|
403
261
|
}
|
|
404
262
|
/**
|
|
405
263
|
* `GET /geo` with `$page` / `$size` — paginated distance-ranked search.
|
|
406
264
|
*/
|
|
407
|
-
async geoPages(point, query, page = 1, size = 10) {
|
|
265
|
+
async geoPages(point, query, page = 1, size = 10, opts) {
|
|
408
266
|
return this._get("geo", {
|
|
409
267
|
...query,
|
|
410
268
|
controls: {
|
|
@@ -413,7 +271,7 @@ var Client = class {
|
|
|
413
271
|
$page: page,
|
|
414
272
|
$size: size
|
|
415
273
|
}
|
|
416
|
-
});
|
|
274
|
+
}, opts?.signal);
|
|
417
275
|
}
|
|
418
276
|
/**
|
|
419
277
|
* `GET /one/:id` or `GET /one?k1=v1&k2=v2` — single record by primary key.
|
|
@@ -421,13 +279,13 @@ var Client = class {
|
|
|
421
279
|
* Returns `null` on 404. Response narrows by the literal `$with` array in
|
|
422
280
|
* `query.controls` — same algebra as {@link query}.
|
|
423
281
|
*/
|
|
424
|
-
async one(id, query) {
|
|
282
|
+
async one(id, query, opts) {
|
|
425
283
|
const controlStr = query?.controls ? buildUrl({ controls: query.controls }) : "";
|
|
426
|
-
return this._getOrNull(this._idUrl("one", id, controlStr));
|
|
284
|
+
return this._getOrNull(this._idUrl("one", id, controlStr), opts?.signal);
|
|
427
285
|
}
|
|
428
286
|
async insert(data, opts) {
|
|
429
287
|
const query = opts?.onConflict === "ignore" ? "?$onConflict=ignore" : "";
|
|
430
|
-
return this._request("POST", query, await this._prepareWrite(data, "insert"));
|
|
288
|
+
return this._request("POST", query, await this._prepareWrite(data, "insert"), opts);
|
|
431
289
|
}
|
|
432
290
|
/**
|
|
433
291
|
* `PATCH /` — partial update one or many records by primary key.
|
|
@@ -438,21 +296,21 @@ var Client = class {
|
|
|
438
296
|
* {@link VersionMismatchError}; a PK-only body with a version is a real
|
|
439
297
|
* write (the server bumps the version).
|
|
440
298
|
*/
|
|
441
|
-
async update(data) {
|
|
442
|
-
return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
|
|
299
|
+
async update(data, opts) {
|
|
300
|
+
return this._request("PATCH", "", await this._prepareWrite(data, "patch"), opts);
|
|
443
301
|
}
|
|
444
302
|
/**
|
|
445
303
|
* `PUT /` — full replace one or many records by primary key. Same OCC
|
|
446
304
|
* shapes as {@link update}; the version column itself is optional.
|
|
447
305
|
*/
|
|
448
|
-
async replace(data) {
|
|
449
|
-
return this._request("PUT", "", await this._prepareWrite(data, "replace"));
|
|
306
|
+
async replace(data, opts) {
|
|
307
|
+
return this._request("PUT", "", await this._prepareWrite(data, "replace"), opts);
|
|
450
308
|
}
|
|
451
309
|
/**
|
|
452
310
|
* `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
|
|
453
311
|
*/
|
|
454
|
-
async remove(id) {
|
|
455
|
-
return this._request("DELETE", this._idUrl("", id));
|
|
312
|
+
async remove(id, opts) {
|
|
313
|
+
return this._request("DELETE", this._idUrl("", id), void 0, opts);
|
|
456
314
|
}
|
|
457
315
|
/**
|
|
458
316
|
* `GET /meta` — table/view metadata (cached after first call).
|
|
@@ -626,7 +484,7 @@ var Client = class {
|
|
|
626
484
|
}
|
|
627
485
|
_postQueryTarget(action, body) {
|
|
628
486
|
const path = action.queryTarget?.url ?? action.value;
|
|
629
|
-
return this._requestUrl("POST", `${this._baseUrl}${path}`, body, true);
|
|
487
|
+
return this._requestUrl("POST", `${this._baseUrl}${path}`, body, { allowEmpty: true });
|
|
630
488
|
}
|
|
631
489
|
/**
|
|
632
490
|
* `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
|
|
@@ -732,7 +590,7 @@ var Client = class {
|
|
|
732
590
|
throw new ActionUnsupportedError(action.name, "navigate", `Action "${action.name}" is processor: 'navigate' but no browser is available and no \`navigate\` option was provided to Client.`);
|
|
733
591
|
}
|
|
734
592
|
_postAction(action, body) {
|
|
735
|
-
return this._requestUrl("POST", `${this._baseUrl}${action.value}`, body, true);
|
|
593
|
+
return this._requestUrl("POST", `${this._baseUrl}${action.value}`, body, { allowEmpty: true });
|
|
736
594
|
}
|
|
737
595
|
_idToParams(id) {
|
|
738
596
|
const params = new URLSearchParams();
|
|
@@ -754,17 +612,17 @@ var Client = class {
|
|
|
754
612
|
const path = `${base ? `${base}/` : ""}${encodeURIComponent(String(id))}`;
|
|
755
613
|
return query ? `${path}?${query}` : path;
|
|
756
614
|
}
|
|
757
|
-
async _getOrNull(endpoint) {
|
|
615
|
+
async _getOrNull(endpoint, signal) {
|
|
758
616
|
try {
|
|
759
|
-
return await this._request("GET", endpoint);
|
|
617
|
+
return await this._request("GET", endpoint, void 0, { signal });
|
|
760
618
|
} catch (e) {
|
|
761
619
|
if (e instanceof ClientError && e.status === 404) return null;
|
|
762
620
|
throw e;
|
|
763
621
|
}
|
|
764
622
|
}
|
|
765
|
-
async _get(endpoint, query) {
|
|
623
|
+
async _get(endpoint, query, signal) {
|
|
766
624
|
const qs = query ? buildUrl(query) : "";
|
|
767
|
-
return this._request("GET", `${endpoint}${qs ? `?${qs}` : ""}
|
|
625
|
+
return this._request("GET", `${endpoint}${qs ? `?${qs}` : ""}`, void 0, { signal });
|
|
768
626
|
}
|
|
769
627
|
async _resolveHeaders() {
|
|
770
628
|
if (!this._headers) return {};
|
|
@@ -848,8 +706,8 @@ var Client = class {
|
|
|
848
706
|
etag
|
|
849
707
|
};
|
|
850
708
|
}
|
|
851
|
-
_request(method, endpoint, body) {
|
|
852
|
-
return this._requestUrl(method, this._endpointUrl(endpoint), body);
|
|
709
|
+
_request(method, endpoint, body, opts) {
|
|
710
|
+
return this._requestUrl(method, this._endpointUrl(endpoint), body, opts);
|
|
853
711
|
}
|
|
854
712
|
/** `baseUrl + path` joined with an endpoint relative to this controller (`""`, `meta`, `?q`…). */
|
|
855
713
|
_endpointUrl(endpoint) {
|
|
@@ -857,17 +715,18 @@ var Client = class {
|
|
|
857
715
|
return `${this._baseUrl}${this._path}${sep}${endpoint}`;
|
|
858
716
|
}
|
|
859
717
|
/** Sends one request to an absolute URL with the client's headers and a JSON body. */
|
|
860
|
-
async _requestUrl(method, url, body,
|
|
718
|
+
async _requestUrl(method, url, body, opts) {
|
|
861
719
|
const headers = { ...await this._resolveHeaders() };
|
|
862
720
|
const init = {
|
|
863
721
|
method,
|
|
864
722
|
headers
|
|
865
723
|
};
|
|
724
|
+
if (opts?.signal) init.signal = opts.signal;
|
|
866
725
|
if (body !== void 0) {
|
|
867
726
|
headers["Content-Type"] = "application/json";
|
|
868
727
|
init.body = JSON.stringify(body);
|
|
869
728
|
}
|
|
870
|
-
return this._send(url, init, allowEmpty);
|
|
729
|
+
return this._send(url, init, opts?.allowEmpty === true);
|
|
871
730
|
}
|
|
872
731
|
async _send(url, init, allowEmpty) {
|
|
873
732
|
const res = await this._fetchUrl(url, init);
|
|
@@ -1013,4 +872,4 @@ function describeShape(value) {
|
|
|
1013
872
|
return typeof value;
|
|
1014
873
|
}
|
|
1015
874
|
//#endregion
|
|
1016
|
-
export { ActionDisabledError, ActionNotFoundError, ActionTargetError, ActionUnsupportedError, Client, ClientError, MetaStore, TransportError, VersionMismatchError, actionIdentifier, bucketSeries, bucketStartInstant, clearMetaStore, defaultMetaStore, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
|
|
875
|
+
export { ActionDisabledError, ActionNotFoundError, ActionTargetError, ActionUnsupportedError, Client, ClientError, ClientValidationError, MetaStore, TransportError, VersionMismatchError, actionIdentifier, bucketSeries, bucketStartInstant, clearMetaStore, defaultMetaStore, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
|
|
@@ -172,15 +172,38 @@ interface PageResult<T> {
|
|
|
172
172
|
pages: number;
|
|
173
173
|
count: number;
|
|
174
174
|
}
|
|
175
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* Per-call options of the `Client` read / write methods (since 0.1.165).
|
|
177
|
+
*/
|
|
178
|
+
interface TClientCallOptions {
|
|
179
|
+
/**
|
|
180
|
+
* Cancels the request. An aborted request rejects with a `TransportError`
|
|
181
|
+
* whose `aborted` is `true` (the `AbortError` is its `cause`).
|
|
182
|
+
*/
|
|
183
|
+
signal?: AbortSignal;
|
|
184
|
+
}
|
|
185
|
+
/** One entry of a server error's `errors` list. */
|
|
186
|
+
interface ServerErrorEntry {
|
|
187
|
+
path: string;
|
|
188
|
+
message: string;
|
|
189
|
+
/** Machine-readable reason (`UNIQUE`, `FK_MISSING`, `FK_RESTRICT`, …). @since 0.1.165 */
|
|
190
|
+
code?: string;
|
|
191
|
+
/** Values the message names, for translating it. @since 0.1.165 */
|
|
192
|
+
params?: Record<string, unknown>;
|
|
193
|
+
details?: unknown[];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Server error response shape (matches moost-db error transform). Open to
|
|
197
|
+
* extra fields (since 0.1.165): a handler's own error body (`quota`, `tier`,
|
|
198
|
+
* …) reads without a cast — type it with `ClientError<B>`.
|
|
199
|
+
*/
|
|
176
200
|
interface ServerError {
|
|
177
201
|
message: string;
|
|
178
202
|
statusCode: number;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}>;
|
|
203
|
+
/** The `DbError` code (`CONFLICT`, `FK_VIOLATION`, …) or a handler's own. @since 0.1.165 */
|
|
204
|
+
code?: string;
|
|
205
|
+
errors?: ServerErrorEntry[];
|
|
206
|
+
[key: string]: unknown;
|
|
184
207
|
}
|
|
185
208
|
/**
|
|
186
209
|
* Minimal brand shape every `Client<T>` generic must satisfy. All fields are
|
|
@@ -203,6 +226,16 @@ type DataOf<T> = T extends {
|
|
|
203
226
|
__dataType?: infer D;
|
|
204
227
|
};
|
|
205
228
|
} ? unknown extends D ? T extends (new (...a: any[]) => infer I) ? I : Record<string, unknown> : D & Record<string, unknown> : Record<string, unknown>;
|
|
229
|
+
/**
|
|
230
|
+
* The row type a read answers with: {@link DataOf} without its
|
|
231
|
+
* `Record<string, unknown>` widening, which would make `DbResponse`'s
|
|
232
|
+
* `Omit` collapse every field to `unknown` (since 0.1.165).
|
|
233
|
+
*/
|
|
234
|
+
type ResponseDataOf<T> = T extends {
|
|
235
|
+
type: {
|
|
236
|
+
__dataType?: infer D;
|
|
237
|
+
};
|
|
238
|
+
} ? unknown extends D ? T extends (new (...a: any[]) => infer I) ? I : Record<string, unknown> : D : Record<string, unknown>;
|
|
206
239
|
/**
|
|
207
240
|
* `$select` controls of a read over `T` whose controller declares the display-only
|
|
208
241
|
* fields `D` (`@DbDecorations`): `$select` also accepts `keyof D`; filter, sort
|
|
@@ -235,7 +268,7 @@ type IdOf<T> = T extends {
|
|
|
235
268
|
* short-circuits to the data type. `$actions` / `$disabledReasons` are always
|
|
236
269
|
* optional — the server emits them only when the request set `?$actions=true`.
|
|
237
270
|
*/
|
|
238
|
-
type ClientResponse<T, Q> = DbResponse<
|
|
271
|
+
type ClientResponse<T, Q> = DbResponse<ResponseDataOf<T>, NavOf<T>, Q> & {
|
|
239
272
|
/**
|
|
240
273
|
* Server-evaluated per-row availability for `'row'` and `'rows'`-level
|
|
241
274
|
* actions. Each entry is the `name` of an action that is NOT disabled for
|
|
@@ -274,6 +307,200 @@ interface TDbQueryTarget<T = AtscriptClientShape> {
|
|
|
274
307
|
maxRows?: number;
|
|
275
308
|
}
|
|
276
309
|
//#endregion
|
|
310
|
+
//#region src/client-error.d.ts
|
|
311
|
+
/**
|
|
312
|
+
* Error thrown by `Client` when the server responds with a non-2xx status code.
|
|
313
|
+
* Captures the HTTP status and the structured error body from moost-db.
|
|
314
|
+
*/
|
|
315
|
+
declare class ClientError<B extends ServerError = ServerError> extends Error {
|
|
316
|
+
/** HTTP status code (e.g. 400, 404, 409, 500). */
|
|
317
|
+
readonly status: number;
|
|
318
|
+
/**
|
|
319
|
+
* Structured error response from the server. `B` (since 0.1.165) types a
|
|
320
|
+
* body that carries fields of its own: `new ClientError<QuotaBody>(…)`,
|
|
321
|
+
* `(e as ClientError<QuotaBody>).body.quota`.
|
|
322
|
+
*/
|
|
323
|
+
readonly body: B;
|
|
324
|
+
name: string;
|
|
325
|
+
constructor(/** HTTP status code (e.g. 400, 404, 409, 500). */
|
|
326
|
+
|
|
327
|
+
status: number,
|
|
328
|
+
/**
|
|
329
|
+
* Structured error response from the server. `B` (since 0.1.165) types a
|
|
330
|
+
* body that carries fields of its own: `new ClientError<QuotaBody>(…)`,
|
|
331
|
+
* `(e as ClientError<QuotaBody>).body.quota`.
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
body: B);
|
|
335
|
+
/** Shortcut to structured validation/DB errors from the server. */
|
|
336
|
+
get errors(): ServerErrorEntry[];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Thrown before the request is sent when the client-side preflight
|
|
340
|
+
* (`validator` option, `ClientValidator`) rejects a write. A
|
|
341
|
+
* {@link ClientError} since 0.1.165 — status `400`, `body` shaped like the
|
|
342
|
+
* server's validation envelope (`{ message, statusCode: 400, errors }`) — so
|
|
343
|
+
* one `instanceof ClientError` branch handles a preflight failure and the
|
|
344
|
+
* server's 400 alike; `preflight` (`true`) tells them apart. The same class
|
|
345
|
+
* is exported from `@atscript/db-client` and `@atscript/db-client/validator`.
|
|
346
|
+
*/
|
|
347
|
+
declare class ClientValidationError extends ClientError {
|
|
348
|
+
name: string;
|
|
349
|
+
/** `true`: the request never left the client. */
|
|
350
|
+
readonly preflight: true;
|
|
351
|
+
constructor(errors: Array<{
|
|
352
|
+
path: string;
|
|
353
|
+
message: string;
|
|
354
|
+
}>);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Wire-body shape for `ActionDisabledError` responses (HTTP 409). Extends
|
|
358
|
+
* the base `ServerError` envelope with a `name` discriminator, the action
|
|
359
|
+
* name, and the offending identifier(s). The bridge between `@atscript/moost-db`'s
|
|
360
|
+
* server-side error and this typed client-side subclass is the wire JSON
|
|
361
|
+
* body — neither package depends on the other.
|
|
362
|
+
*/
|
|
363
|
+
interface ActionDisabledErrorBody extends ServerError {
|
|
364
|
+
name: "ActionDisabledError";
|
|
365
|
+
action: string;
|
|
366
|
+
id?: Record<string, unknown>;
|
|
367
|
+
ids?: Record<string, unknown>[];
|
|
368
|
+
/** Reason shared by every rejected row, when the server's predicate gave one. @since 0.1.141 */
|
|
369
|
+
reason?: string;
|
|
370
|
+
/** `'rows'` level: index-aligned with `ids`, `null` = no reason for that row. @since 0.1.141 */
|
|
371
|
+
reasons?: (string | null)[];
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Typed marker thrown by `Client._send` when the server response body's
|
|
375
|
+
* `name === 'ActionDisabledError'`. The transport / status / base body are
|
|
376
|
+
* identical to a generic `ClientError`; this subclass adds typed accessors
|
|
377
|
+
* so consumers can write `catch (e) { if (e instanceof ActionDisabledError) … }`
|
|
378
|
+
* to access `action` / `id` / `ids` / `reason` / `reasons` without indexing
|
|
379
|
+
* into `body`.
|
|
380
|
+
*/
|
|
381
|
+
declare class ActionDisabledError extends ClientError {
|
|
382
|
+
name: string;
|
|
383
|
+
/** The `@DbAction` name that rejected the request. */
|
|
384
|
+
get action(): string;
|
|
385
|
+
/** Present only for `'row'`-level rejections. */
|
|
386
|
+
get id(): Record<string, unknown> | undefined;
|
|
387
|
+
/** Present only for `'rows'`-level rejections (full list of failing IDs). */
|
|
388
|
+
get ids(): Record<string, unknown>[] | undefined;
|
|
389
|
+
/**
|
|
390
|
+
* Human-readable reason shared by every rejected row — present only when
|
|
391
|
+
* the server's `disabled` predicate returned one. `message` already
|
|
392
|
+
* carries it.
|
|
393
|
+
*
|
|
394
|
+
* @since 0.1.141
|
|
395
|
+
*/
|
|
396
|
+
get reason(): string | undefined;
|
|
397
|
+
/**
|
|
398
|
+
* `'rows'`-level only: per-row reasons index-aligned with {@link ids}
|
|
399
|
+
* (`null` where that row has none). Present only when at least one
|
|
400
|
+
* rejected row carries a reason.
|
|
401
|
+
*
|
|
402
|
+
* @since 0.1.141
|
|
403
|
+
*/
|
|
404
|
+
get reasons(): (string | null)[] | undefined;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Wire-body shape for `ActionTargetError` responses (since 0.1.147): a
|
|
408
|
+
* query target the server refused — `code` says why.
|
|
409
|
+
*/
|
|
410
|
+
interface ActionTargetErrorBody extends ServerError {
|
|
411
|
+
name: "ActionTargetError";
|
|
412
|
+
code: "TARGET_INVALID" | "TARGET_TOO_LARGE" | "TARGET_CHANGED";
|
|
413
|
+
action: string;
|
|
414
|
+
/** `TARGET_CHANGED`: the current match count. */
|
|
415
|
+
matched?: number;
|
|
416
|
+
/** `TARGET_TOO_LARGE`: the most rows one request may target. */
|
|
417
|
+
cap?: number;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Typed marker thrown by `Client` when a query-targeted action request is
|
|
421
|
+
* refused (body `name === 'ActionTargetError'`): `TARGET_INVALID` (400),
|
|
422
|
+
* `TARGET_TOO_LARGE` (400, `cap`) or `TARGET_CHANGED` (409, `matched` — the
|
|
423
|
+
* rows the query matches now; re-confirm and retry with that count).
|
|
424
|
+
*
|
|
425
|
+
* @since 0.1.147
|
|
426
|
+
*/
|
|
427
|
+
declare class ActionTargetError extends ClientError {
|
|
428
|
+
name: string;
|
|
429
|
+
get code(): ActionTargetErrorBody["code"];
|
|
430
|
+
get action(): string;
|
|
431
|
+
get matched(): number | undefined;
|
|
432
|
+
get cap(): number | undefined;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Wire-body shape for 409 OCC `version_mismatch` responses. Extends the base
|
|
436
|
+
* `ServerError` envelope with a `kind` discriminator and the row's current
|
|
437
|
+
* server-side version. The bridge between `@atscript/moost-db`'s server-side
|
|
438
|
+
* `_disambiguateMismatch` and this typed client-side subclass is the wire
|
|
439
|
+
* JSON body — neither package depends on the other.
|
|
440
|
+
*/
|
|
441
|
+
interface VersionMismatchErrorBody extends ServerError {
|
|
442
|
+
kind: "version_mismatch";
|
|
443
|
+
currentVersion: number;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Typed marker thrown by `Client._send` when the server response body's
|
|
447
|
+
* `kind === 'version_mismatch'`. The transport / status / base body are
|
|
448
|
+
* identical to a generic `ClientError`; this subclass adds a typed
|
|
449
|
+
* `currentVersion` accessor so consumers can write
|
|
450
|
+
* `catch (e) { if (e instanceof VersionMismatchError) … }` without casting.
|
|
451
|
+
*/
|
|
452
|
+
declare class VersionMismatchError extends ClientError {
|
|
453
|
+
name: string;
|
|
454
|
+
/** Row's current server-side version — clients refresh + retry against it. */
|
|
455
|
+
get currentVersion(): number;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
459
|
+
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
460
|
+
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
461
|
+
* server answered and rejected — the request's outcome is unknown: a write
|
|
462
|
+
* may have committed. Reload before retrying a non-idempotent write.
|
|
463
|
+
* The underlying error is the standard `cause`.
|
|
464
|
+
*/
|
|
465
|
+
declare class TransportError extends Error {
|
|
466
|
+
/** HTTP method of the failed request. */
|
|
467
|
+
readonly method: string;
|
|
468
|
+
/** Full request URL. */
|
|
469
|
+
readonly url: string;
|
|
470
|
+
name: string;
|
|
471
|
+
constructor(/** HTTP method of the failed request. */
|
|
472
|
+
|
|
473
|
+
method: string, /** Full request URL. */
|
|
474
|
+
|
|
475
|
+
url: string, /** What went wrong; the message is `<method> <url>: <detail>`. */
|
|
476
|
+
|
|
477
|
+
detail: string, cause?: unknown);
|
|
478
|
+
/**
|
|
479
|
+
* `true` when the request was cancelled — its `cause` is an `AbortError`
|
|
480
|
+
* (an aborted `signal`) or a `TimeoutError` (`AbortSignal.timeout`) —
|
|
481
|
+
* rather than a network failure. @since 0.1.165
|
|
482
|
+
*/
|
|
483
|
+
get aborted(): boolean;
|
|
484
|
+
}
|
|
485
|
+
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
486
|
+
declare class ActionNotFoundError extends Error {
|
|
487
|
+
readonly action: string;
|
|
488
|
+
name: string;
|
|
489
|
+
constructor(action: string);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
493
|
+
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
494
|
+
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
495
|
+
* when no browser environment and no `navigate` option are configured.
|
|
496
|
+
*/
|
|
497
|
+
declare class ActionUnsupportedError extends Error {
|
|
498
|
+
readonly action: string;
|
|
499
|
+
readonly processor: string;
|
|
500
|
+
name: string;
|
|
501
|
+
constructor(action: string, processor: string, message: string);
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
277
504
|
//#region src/validator.d.ts
|
|
278
505
|
/** Options for {@link ClientValidator} / {@link createClientValidator}. */
|
|
279
506
|
interface ClientValidatorOptions {
|
|
@@ -329,20 +556,6 @@ declare class ClientValidator {
|
|
|
329
556
|
validate(data: unknown, mode: ValidatorMode): void;
|
|
330
557
|
private _getValidator;
|
|
331
558
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Structured validation error thrown before HTTP requests when client-side
|
|
334
|
-
* validation fails.
|
|
335
|
-
*/
|
|
336
|
-
declare class ClientValidationError extends Error {
|
|
337
|
-
readonly errors: Array<{
|
|
338
|
-
path: string;
|
|
339
|
-
message: string;
|
|
340
|
-
}>;
|
|
341
|
-
constructor(errors: Array<{
|
|
342
|
-
path: string;
|
|
343
|
-
message: string;
|
|
344
|
-
}>);
|
|
345
|
-
}
|
|
346
559
|
/**
|
|
347
560
|
* Create a {@link ClientValidator} from a meta response (or promise).
|
|
348
561
|
*
|
|
@@ -355,4 +568,4 @@ declare class ClientValidationError extends Error {
|
|
|
355
568
|
*/
|
|
356
569
|
declare function createClientValidator(meta: MetaResponse, opts?: ClientValidatorOptions): ClientValidator;
|
|
357
570
|
//#endregion
|
|
358
|
-
export {
|
|
571
|
+
export { UniqueryControls as $, FieldMeta as A, RowOf as B, ClientOptions as C, DbRow as D, DbPatch as E, OwnOf as F, TDbInsertIgnoreResult$1 as G, ServerError as H, PageResult as I, TDbInsertResult$1 as J, TDbInsertManyIgnoreResult$1 as K, PatchOf as L, IdOf as M, MetaResponse as N, DecoratedControls as O, NavOf as P, Uniquery$1 as Q, RelationInfo as R, CalendarBucketLabel as S, DataOf as T, TClientCallOptions as U, SearchIndexInfo as V, TDbDeleteResult$1 as W, TDbUpdateResult$1 as X, TDbQueryTarget as Y, TypedWithRelation as Z, AggregateQuery$1 as _, createClientValidator as a, defaultMetaStore as at, BucketExpr$1 as b, ActionNotFoundError as c, ActionUnsupportedError as d, ValidGroupBy$1 as et, ClientError as f, VersionMismatchErrorBody as g, VersionMismatchError as h, ValidatorMode$1 as i, clearMetaStore as it, FilterExpr as j, DecoratedQuery as k, ActionTargetError as l, TransportError as m, ClientValidatorOptions as n, MetaStore as nt, ActionDisabledError as o, ClientValidationError as p, TDbInsertManyResult$1 as q, DbValidationContext as r, MetaStoreOptions as rt, ActionDisabledErrorBody as s, ClientValidator as t, WeekStart as tt, ActionTargetErrorBody as u, AggregateResult$1 as v, ClientResponse as w, BucketUnit as x, AtscriptClientShape as y, ResponseDataOf as z };
|