@atscript/db-client 0.1.164 → 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/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, allowEmpty = false) {
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
- /** Server error response shape (matches moost-db error transform). */
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
- errors?: Array<{
180
- path: string;
181
- message: string;
182
- details?: unknown[];
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<DataOf<T>, NavOf<T>, Q> & {
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 { SearchIndexInfo as A, Uniquery$1 as B, MetaResponse as C, PatchOf as D, PageResult as E, TDbInsertManyResult$1 as F, MetaStoreOptions as G, ValidGroupBy$1 as H, TDbInsertResult$1 as I, clearMetaStore as K, TDbQueryTarget as L, TDbDeleteResult$1 as M, TDbInsertIgnoreResult$1 as N, RelationInfo as O, TDbInsertManyIgnoreResult$1 as P, TDbUpdateResult$1 as R, IdOf as S, OwnOf as T, WeekStart as U, UniqueryControls as V, MetaStore as W, DbRow as _, ValidatorMode$1 as a, FieldMeta as b, AggregateResult$1 as c, BucketUnit as d, CalendarBucketLabel as f, DbPatch as g, DataOf as h, DbValidationContext as i, ServerError as j, RowOf as k, AtscriptClientShape as l, ClientResponse as m, ClientValidator as n, createClientValidator as o, ClientOptions as p, defaultMetaStore as q, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, BucketExpr$1 as u, DecoratedControls as v, NavOf as w, FilterExpr as x, DecoratedQuery as y, TypedWithRelation as z };
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 };