@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.cjs CHANGED
@@ -1,150 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_client_error = require("./client-error-C9Q5xCuW.cjs");
2
3
  let _uniqu_url_builder = require("@uniqu/url/builder");
3
4
  let _atscript_typescript_utils = require("@atscript/typescript/utils");
4
5
  let _uniqu_core = require("@uniqu/core");
5
- //#region src/client-error.ts
6
- /**
7
- * Error thrown by `Client` when the server responds with a non-2xx status code.
8
- * Captures the HTTP status and the structured error body from moost-db.
9
- */
10
- var ClientError = class extends Error {
11
- status;
12
- body;
13
- name = "ClientError";
14
- constructor(status, body) {
15
- super(body.message || `HTTP ${status}`);
16
- this.status = status;
17
- this.body = body;
18
- }
19
- /** Shortcut to structured validation/DB errors from the server. */
20
- get errors() {
21
- return this.body.errors ?? [];
22
- }
23
- };
24
- /**
25
- * Typed marker thrown by `Client._send` when the server response body's
26
- * `name === 'ActionDisabledError'`. The transport / status / base body are
27
- * identical to a generic `ClientError`; this subclass adds typed accessors
28
- * so consumers can write `catch (e) { if (e instanceof ActionDisabledError) … }`
29
- * to access `action` / `id` / `ids` / `reason` / `reasons` without indexing
30
- * into `body`.
31
- */
32
- var ActionDisabledError = class extends ClientError {
33
- name = "ActionDisabledError";
34
- /** The `@DbAction` name that rejected the request. */
35
- get action() {
36
- return this.body.action;
37
- }
38
- /** Present only for `'row'`-level rejections. */
39
- get id() {
40
- return this.body.id;
41
- }
42
- /** Present only for `'rows'`-level rejections (full list of failing IDs). */
43
- get ids() {
44
- return this.body.ids;
45
- }
46
- /**
47
- * Human-readable reason shared by every rejected row — present only when
48
- * the server's `disabled` predicate returned one. `message` already
49
- * carries it.
50
- *
51
- * @since 0.1.141
52
- */
53
- get reason() {
54
- return this.body.reason;
55
- }
56
- /**
57
- * `'rows'`-level only: per-row reasons index-aligned with {@link ids}
58
- * (`null` where that row has none). Present only when at least one
59
- * rejected row carries a reason.
60
- *
61
- * @since 0.1.141
62
- */
63
- get reasons() {
64
- return this.body.reasons;
65
- }
66
- };
67
- /**
68
- * Typed marker thrown by `Client` when a query-targeted action request is
69
- * refused (body `name === 'ActionTargetError'`): `TARGET_INVALID` (400),
70
- * `TARGET_TOO_LARGE` (400, `cap`) or `TARGET_CHANGED` (409, `matched` — the
71
- * rows the query matches now; re-confirm and retry with that count).
72
- *
73
- * @since 0.1.147
74
- */
75
- var ActionTargetError = class extends ClientError {
76
- name = "ActionTargetError";
77
- get code() {
78
- return this.body.code;
79
- }
80
- get action() {
81
- return this.body.action;
82
- }
83
- get matched() {
84
- return this.body.matched;
85
- }
86
- get cap() {
87
- return this.body.cap;
88
- }
89
- };
90
- /**
91
- * Typed marker thrown by `Client._send` when the server response body's
92
- * `kind === 'version_mismatch'`. The transport / status / base body are
93
- * identical to a generic `ClientError`; this subclass adds a typed
94
- * `currentVersion` accessor so consumers can write
95
- * `catch (e) { if (e instanceof VersionMismatchError) … }` without casting.
96
- */
97
- var VersionMismatchError = class extends ClientError {
98
- name = "VersionMismatchError";
99
- /** Row's current server-side version — clients refresh + retry against it. */
100
- get currentVersion() {
101
- return this.body.currentVersion;
102
- }
103
- };
104
- /**
105
- * Thrown by `Client` when NO server verdict was obtained: `fetch` itself
106
- * rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
107
- * carried a body that is not JSON. Unlike {@link ClientError} — where the
108
- * server answered and rejected — the request's outcome is unknown: a write
109
- * may have committed. Reload before retrying a non-idempotent write.
110
- * The underlying error is the standard `cause`.
111
- */
112
- var TransportError = class extends Error {
113
- method;
114
- url;
115
- name = "TransportError";
116
- constructor(method, url, detail, cause) {
117
- super(`${method} ${url}: ${detail}`, { cause });
118
- this.method = method;
119
- this.url = url;
120
- }
121
- };
122
- /** Thrown by `Client.action()` when the action name is not present in `/meta`. */
123
- var ActionNotFoundError = class extends Error {
124
- action;
125
- name = "ActionNotFoundError";
126
- constructor(action) {
127
- super(`Action "${action}" is not declared on this controller`);
128
- this.action = action;
129
- }
130
- };
131
- /**
132
- * Thrown by `Client.action()` for actions that cannot be invoked through
133
- * the client — currently `processor: 'custom'` (UI-dispatched events,
134
- * which the application is responsible for handling) and `processor: 'navigate'`
135
- * when no browser environment and no `navigate` option are configured.
136
- */
137
- var ActionUnsupportedError = class extends Error {
138
- action;
139
- processor;
140
- name = "ActionUnsupportedError";
141
- constructor(action, processor, message) {
142
- super(message);
143
- this.action = action;
144
- this.processor = processor;
145
- }
146
- };
147
- //#endregion
148
6
  //#region src/meta-store.ts
149
7
  /** ETags remembered per store key (most recent first). */
150
8
  const CANDIDATES_PER_KEY = 4;
@@ -336,17 +194,17 @@ var Client = class {
336
194
  * The response type narrows by the literal `$with` array in `query` —
337
195
  * relations not listed in `$with` are stripped from the row type.
338
196
  */
339
- async query(query) {
340
- return this._get("query", query);
197
+ async query(query, opts) {
198
+ return this._get("query", query, opts?.signal);
341
199
  }
342
200
  /**
343
201
  * `GET /query` with `$count: true` — returns record count.
344
202
  */
345
- async count(query) {
203
+ async count(query, opts) {
346
204
  return this._get("query", {
347
205
  ...query,
348
206
  controls: { $count: true }
349
- });
207
+ }, opts?.signal);
350
208
  }
351
209
  /**
352
210
  * `GET /query` with `$groupBy` — aggregate query with typed dimension/measure fields.
@@ -365,8 +223,8 @@ var Client = class {
365
223
  * read a representative row ordered by `$rowOrder`. The URL forms are `expr(a/b):x`,
366
224
  * `sum(a*b):x`, `first(f):x` and `$rowOrder=f,-id`.
367
225
  */
368
- async aggregate(query) {
369
- return this._get("query", query);
226
+ async aggregate(query, opts) {
227
+ return this._get("query", query, opts?.signal);
370
228
  }
371
229
  /**
372
230
  * `GET /pages` — paginated query with typed filter and relations.
@@ -374,7 +232,7 @@ var Client = class {
374
232
  * Response rows narrow by the literal `$with` array — same algebra as
375
233
  * {@link query}.
376
234
  */
377
- async pages(query, page = 1, size = 10) {
235
+ async pages(query, page = 1, size = 10, opts) {
378
236
  return this._get("pages", {
379
237
  ...query,
380
238
  controls: {
@@ -382,7 +240,7 @@ var Client = class {
382
240
  $page: page,
383
241
  $size: size
384
242
  }
385
- });
243
+ }, opts?.signal);
386
244
  }
387
245
  /**
388
246
  * `GET /geo` — distance-ranked geospatial search (tables with `@db.index.geo`).
@@ -393,19 +251,19 @@ var Client = class {
393
251
  * in `query.controls`; filter / `$select` / `$with` / `$skip` / `$limit`
394
252
  * compose as usual.
395
253
  */
396
- async geoSearch(point, query) {
254
+ async geoSearch(point, query, opts) {
397
255
  return this._get("geo", {
398
256
  ...query,
399
257
  controls: {
400
258
  ...query?.controls,
401
259
  $center: point.join(",")
402
260
  }
403
- });
261
+ }, opts?.signal);
404
262
  }
405
263
  /**
406
264
  * `GET /geo` with `$page` / `$size` — paginated distance-ranked search.
407
265
  */
408
- async geoPages(point, query, page = 1, size = 10) {
266
+ async geoPages(point, query, page = 1, size = 10, opts) {
409
267
  return this._get("geo", {
410
268
  ...query,
411
269
  controls: {
@@ -414,7 +272,7 @@ var Client = class {
414
272
  $page: page,
415
273
  $size: size
416
274
  }
417
- });
275
+ }, opts?.signal);
418
276
  }
419
277
  /**
420
278
  * `GET /one/:id` or `GET /one?k1=v1&k2=v2` — single record by primary key.
@@ -422,13 +280,13 @@ var Client = class {
422
280
  * Returns `null` on 404. Response narrows by the literal `$with` array in
423
281
  * `query.controls` — same algebra as {@link query}.
424
282
  */
425
- async one(id, query) {
283
+ async one(id, query, opts) {
426
284
  const controlStr = query?.controls ? (0, _uniqu_url_builder.buildUrl)({ controls: query.controls }) : "";
427
- return this._getOrNull(this._idUrl("one", id, controlStr));
285
+ return this._getOrNull(this._idUrl("one", id, controlStr), opts?.signal);
428
286
  }
429
287
  async insert(data, opts) {
430
288
  const query = opts?.onConflict === "ignore" ? "?$onConflict=ignore" : "";
431
- return this._request("POST", query, await this._prepareWrite(data, "insert"));
289
+ return this._request("POST", query, await this._prepareWrite(data, "insert"), opts);
432
290
  }
433
291
  /**
434
292
  * `PATCH /` — partial update one or many records by primary key.
@@ -439,21 +297,21 @@ var Client = class {
439
297
  * {@link VersionMismatchError}; a PK-only body with a version is a real
440
298
  * write (the server bumps the version).
441
299
  */
442
- async update(data) {
443
- return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
300
+ async update(data, opts) {
301
+ return this._request("PATCH", "", await this._prepareWrite(data, "patch"), opts);
444
302
  }
445
303
  /**
446
304
  * `PUT /` — full replace one or many records by primary key. Same OCC
447
305
  * shapes as {@link update}; the version column itself is optional.
448
306
  */
449
- async replace(data) {
450
- return this._request("PUT", "", await this._prepareWrite(data, "replace"));
307
+ async replace(data, opts) {
308
+ return this._request("PUT", "", await this._prepareWrite(data, "replace"), opts);
451
309
  }
452
310
  /**
453
311
  * `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
454
312
  */
455
- async remove(id) {
456
- return this._request("DELETE", this._idUrl("", id));
313
+ async remove(id, opts) {
314
+ return this._request("DELETE", this._idUrl("", id), void 0, opts);
457
315
  }
458
316
  /**
459
317
  * `GET /meta` — table/view metadata (cached after first call).
@@ -569,8 +427,8 @@ var Client = class {
569
427
  async action(name, id, input) {
570
428
  const meta = await this.meta();
571
429
  const action = meta.actions.find((a) => a.name === name);
572
- if (!action) throw new ActionNotFoundError(name);
573
- if (action.processor === "custom") throw new ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
430
+ if (!action) throw new require_client_error.ActionNotFoundError(name);
431
+ if (action.processor === "custom") throw new require_client_error.ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
574
432
  const mapped = action.idMap ? mapDelegatedIds(action, id) : id;
575
433
  if (action.processor === "navigate") {
576
434
  const order = action.idMap ? Object.keys(action.idMap) : meta.preferredId;
@@ -621,13 +479,13 @@ var Client = class {
621
479
  }
622
480
  async _queryTargetAction(name) {
623
481
  const action = (await this.meta()).actions.find((a) => a.name === name);
624
- if (!action) throw new ActionNotFoundError(name);
625
- if (!action.queryTarget || action.processor !== "backend") throw new ActionUnsupportedError(name, action.processor, `Action "${name}" does not accept a query target (no \`queryTarget\` in /meta).`);
482
+ if (!action) throw new require_client_error.ActionNotFoundError(name);
483
+ if (!action.queryTarget || action.processor !== "backend") throw new require_client_error.ActionUnsupportedError(name, action.processor, `Action "${name}" does not accept a query target (no \`queryTarget\` in /meta).`);
626
484
  return action;
627
485
  }
628
486
  _postQueryTarget(action, body) {
629
487
  const path = action.queryTarget?.url ?? action.value;
630
- return this._requestUrl("POST", `${this._baseUrl}${path}`, body, true);
488
+ return this._requestUrl("POST", `${this._baseUrl}${path}`, body, { allowEmpty: true });
631
489
  }
632
490
  /**
633
491
  * `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
@@ -730,10 +588,10 @@ var Client = class {
730
588
  loc.assign(url);
731
589
  return;
732
590
  }
733
- 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.`);
591
+ throw new require_client_error.ActionUnsupportedError(action.name, "navigate", `Action "${action.name}" is processor: 'navigate' but no browser is available and no \`navigate\` option was provided to Client.`);
734
592
  }
735
593
  _postAction(action, body) {
736
- return this._requestUrl("POST", `${this._baseUrl}${action.value}`, body, true);
594
+ return this._requestUrl("POST", `${this._baseUrl}${action.value}`, body, { allowEmpty: true });
737
595
  }
738
596
  _idToParams(id) {
739
597
  const params = new URLSearchParams();
@@ -755,17 +613,17 @@ var Client = class {
755
613
  const path = `${base ? `${base}/` : ""}${encodeURIComponent(String(id))}`;
756
614
  return query ? `${path}?${query}` : path;
757
615
  }
758
- async _getOrNull(endpoint) {
616
+ async _getOrNull(endpoint, signal) {
759
617
  try {
760
- return await this._request("GET", endpoint);
618
+ return await this._request("GET", endpoint, void 0, { signal });
761
619
  } catch (e) {
762
- if (e instanceof ClientError && e.status === 404) return null;
620
+ if (e instanceof require_client_error.ClientError && e.status === 404) return null;
763
621
  throw e;
764
622
  }
765
623
  }
766
- async _get(endpoint, query) {
624
+ async _get(endpoint, query, signal) {
767
625
  const qs = query ? (0, _uniqu_url_builder.buildUrl)(query) : "";
768
- return this._request("GET", `${endpoint}${qs ? `?${qs}` : ""}`);
626
+ return this._request("GET", `${endpoint}${qs ? `?${qs}` : ""}`, void 0, { signal });
769
627
  }
770
628
  async _resolveHeaders() {
771
629
  if (!this._headers) return {};
@@ -841,7 +699,7 @@ var Client = class {
841
699
  text = await res.text();
842
700
  body = JSON.parse(text);
843
701
  } catch (cause) {
844
- throw new TransportError("GET", url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
702
+ throw new require_client_error.TransportError("GET", url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
845
703
  }
846
704
  store._put(key, etag, text);
847
705
  return {
@@ -849,8 +707,8 @@ var Client = class {
849
707
  etag
850
708
  };
851
709
  }
852
- _request(method, endpoint, body) {
853
- return this._requestUrl(method, this._endpointUrl(endpoint), body);
710
+ _request(method, endpoint, body, opts) {
711
+ return this._requestUrl(method, this._endpointUrl(endpoint), body, opts);
854
712
  }
855
713
  /** `baseUrl + path` joined with an endpoint relative to this controller (`""`, `meta`, `?q`…). */
856
714
  _endpointUrl(endpoint) {
@@ -858,17 +716,18 @@ var Client = class {
858
716
  return `${this._baseUrl}${this._path}${sep}${endpoint}`;
859
717
  }
860
718
  /** Sends one request to an absolute URL with the client's headers and a JSON body. */
861
- async _requestUrl(method, url, body, allowEmpty = false) {
719
+ async _requestUrl(method, url, body, opts) {
862
720
  const headers = { ...await this._resolveHeaders() };
863
721
  const init = {
864
722
  method,
865
723
  headers
866
724
  };
725
+ if (opts?.signal) init.signal = opts.signal;
867
726
  if (body !== void 0) {
868
727
  headers["Content-Type"] = "application/json";
869
728
  init.body = JSON.stringify(body);
870
729
  }
871
- return this._send(url, init, allowEmpty);
730
+ return this._send(url, init, opts?.allowEmpty === true);
872
731
  }
873
732
  async _send(url, init, allowEmpty) {
874
733
  const res = await this._fetchUrl(url, init);
@@ -879,7 +738,7 @@ var Client = class {
879
738
  try {
880
739
  return await this._fetch(url, init);
881
740
  } catch (cause) {
882
- throw new TransportError(init.method ?? "GET", url, describeCause(cause), cause);
741
+ throw new require_client_error.TransportError(init.method ?? "GET", url, describeCause(cause), cause);
883
742
  }
884
743
  }
885
744
  /** A response's JSON body, or the matching error for a non-2xx status. */
@@ -894,15 +753,15 @@ var Client = class {
894
753
  statusCode: res.status
895
754
  };
896
755
  }
897
- if (errorBody.name === "ActionDisabledError") throw new ActionDisabledError(res.status, errorBody);
898
- if (errorBody.name === "ActionTargetError") throw new ActionTargetError(res.status, errorBody);
899
- if (errorBody.kind === "version_mismatch") throw new VersionMismatchError(res.status, errorBody);
900
- throw new ClientError(res.status, errorBody);
756
+ if (errorBody.name === "ActionDisabledError") throw new require_client_error.ActionDisabledError(res.status, errorBody);
757
+ if (errorBody.name === "ActionTargetError") throw new require_client_error.ActionTargetError(res.status, errorBody);
758
+ if (errorBody.kind === "version_mismatch") throw new require_client_error.VersionMismatchError(res.status, errorBody);
759
+ throw new require_client_error.ClientError(res.status, errorBody);
901
760
  }
902
761
  if (!allowEmpty) try {
903
762
  return await res.json();
904
763
  } catch (cause) {
905
- throw new TransportError(method, url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
764
+ throw new require_client_error.TransportError(method, url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
906
765
  }
907
766
  if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
908
767
  try {
@@ -1014,15 +873,16 @@ function describeShape(value) {
1014
873
  return typeof value;
1015
874
  }
1016
875
  //#endregion
1017
- exports.ActionDisabledError = ActionDisabledError;
1018
- exports.ActionNotFoundError = ActionNotFoundError;
1019
- exports.ActionTargetError = ActionTargetError;
1020
- exports.ActionUnsupportedError = ActionUnsupportedError;
876
+ exports.ActionDisabledError = require_client_error.ActionDisabledError;
877
+ exports.ActionNotFoundError = require_client_error.ActionNotFoundError;
878
+ exports.ActionTargetError = require_client_error.ActionTargetError;
879
+ exports.ActionUnsupportedError = require_client_error.ActionUnsupportedError;
1021
880
  exports.Client = Client;
1022
- exports.ClientError = ClientError;
881
+ exports.ClientError = require_client_error.ClientError;
882
+ exports.ClientValidationError = require_client_error.ClientValidationError;
1023
883
  exports.MetaStore = MetaStore;
1024
- exports.TransportError = TransportError;
1025
- exports.VersionMismatchError = VersionMismatchError;
884
+ exports.TransportError = require_client_error.TransportError;
885
+ exports.VersionMismatchError = require_client_error.VersionMismatchError;
1026
886
  exports.actionIdentifier = actionIdentifier;
1027
887
  Object.defineProperty(exports, "bucketSeries", {
1028
888
  enumerable: true,