@atscript/db-client 0.1.145 → 0.1.147

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
@@ -65,6 +65,29 @@ var ActionDisabledError = class extends ClientError {
65
65
  }
66
66
  };
67
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
+ /**
68
91
  * Typed marker thrown by `Client._send` when the server response body's
69
92
  * `kind === 'version_mismatch'`. The transport / status / base body are
70
93
  * identical to a generic `ClientError`; this subclass adds a typed
@@ -186,7 +209,8 @@ var Client = class {
186
209
  *
187
210
  * `$select` may carry calendar buckets (`{ $bucket, $field, $tz?, $weekStart?, $as? }`);
188
211
  * a `$groupBy` entry must be a dimension or a bucket alias (`ValidGroupBy`), and a
189
- * bucket's value is typed as its `YYYY-MM-DD` label (`| null` for an optional source).
212
+ * bucket's value is typed as its `YYYY-MM-DD` (hour: `YYYY-MM-DDTHH:00`) label
213
+ * (`| null` for an optional source).
190
214
  * Gap-fill between labels with `nextBucketLabel` (re-exported here).
191
215
  */
192
216
  async aggregate(query) {
@@ -323,6 +347,13 @@ var Client = class {
323
347
  * `{ ids?, input? }` — `ids` carries `id` (object or array per level),
324
348
  * `input` carries the form payload.
325
349
  *
350
+ * **Delegated actions** (since 0.1.147). An action another controller owns
351
+ * (`owner`, e.g. a view listing its source table's actions) may carry
352
+ * `idMap`: each `id` is then a row (or identifier) of THIS controller and
353
+ * is mapped to the owner's identification — `{ [ownerField]:
354
+ * row[path] }` — before it is sent to `value` (the owner's route). A
355
+ * missing path throws `TypeError`. See {@link actionIdentifier}.
356
+ *
326
357
  * @typeParam R Caller-asserted return shape from the action handler. The
327
358
  * server returns whatever the handler emits (commonly
328
359
  * `{ message?: string, ... }`); the client cannot validate.
@@ -332,15 +363,65 @@ var Client = class {
332
363
  const action = meta.actions.find((a) => a.name === name);
333
364
  if (!action) throw new ActionNotFoundError(name);
334
365
  if (action.processor === "custom") throw new ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
366
+ const mapped = action.idMap ? mapDelegatedIds(action, id) : id;
335
367
  if (action.processor === "navigate") {
336
- const url = this._interpolateNavigateUrl(action, id, meta.preferredId);
368
+ const order = action.idMap ? Object.keys(action.idMap) : meta.preferredId;
369
+ const url = this._interpolateNavigateUrl(action, mapped, order);
337
370
  await this._dispatchNavigate(action, url);
338
371
  return;
339
372
  }
340
- const body = this._buildActionBody(action, id, input);
373
+ const body = this._buildActionBody(action, mapped, input);
341
374
  return this._postAction(action, body);
342
375
  }
343
376
  /**
377
+ * Runs a `'rows'` action on every row matching a query (since 0.1.147) —
378
+ * the action's `/meta` entry must carry `queryTarget`. POSTs
379
+ * `{ query: { q, exclude?, expectCount?, maxRows? }, input? }` to
380
+ * `queryTarget.url` (a delegated action: the view resolves the rows and
381
+ * runs the owner's action in batches) or `value`. `q` is the `/query`
382
+ * string of `target.filter` / `search` / `index`.
383
+ *
384
+ * The server answers what the handler returns — for a delegated action
385
+ * (and a handler returning `target.summary()`) a
386
+ * {@link TDbActionTargetSummary}. Refusals arrive as
387
+ * {@link ActionTargetError}: `TARGET_TOO_LARGE` (`cap`), `TARGET_CHANGED`
388
+ * (`matched` differs from `expectCount`), `TARGET_INVALID`. Throws
389
+ * {@link ActionUnsupportedError} when the action takes no query target.
390
+ *
391
+ * @since 0.1.147
392
+ */
393
+ async actionOnQuery(name, target, input) {
394
+ const action = await this._queryTargetAction(name);
395
+ const body = { query: queryTargetBody(target) };
396
+ if (input !== void 0) body.input = input;
397
+ return this._postQueryTarget(action, body);
398
+ }
399
+ /**
400
+ * How many rows {@link actionOnQuery} would target right now (a dry run:
401
+ * the handler does not run) — pass the answer as `expectCount` to make the
402
+ * real run fail with `TARGET_CHANGED` if the set changed in between.
403
+ *
404
+ * @since 0.1.147
405
+ */
406
+ async countActionTarget(name, target) {
407
+ const action = await this._queryTargetAction(name);
408
+ const query = {
409
+ ...queryTargetBody(target),
410
+ dryRun: true
411
+ };
412
+ return this._postQueryTarget(action, { query });
413
+ }
414
+ async _queryTargetAction(name) {
415
+ const action = (await this.meta()).actions.find((a) => a.name === name);
416
+ if (!action) throw new ActionNotFoundError(name);
417
+ if (!action.queryTarget || action.processor !== "backend") throw new ActionUnsupportedError(name, action.processor, `Action "${name}" does not accept a query target (no \`queryTarget\` in /meta).`);
418
+ return action;
419
+ }
420
+ _postQueryTarget(action, body) {
421
+ const path = action.queryTarget?.url ?? action.value;
422
+ return this._requestUrl("POST", `${this._baseUrl}${path}`, body, true);
423
+ }
424
+ /**
344
425
  * `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
345
426
  * row-level actions the caller may run on one row right now, and the
346
427
  * reasons of those disabled with one: `{ actions, disabledReasons? }`.
@@ -519,6 +600,7 @@ var Client = class {
519
600
  };
520
601
  }
521
602
  if (errorBody.name === "ActionDisabledError") throw new ActionDisabledError(res.status, errorBody);
603
+ if (errorBody.name === "ActionTargetError") throw new ActionTargetError(res.status, errorBody);
522
604
  if (errorBody.kind === "version_mismatch") throw new VersionMismatchError(res.status, errorBody);
523
605
  throw new ClientError(res.status, errorBody);
524
606
  }
@@ -540,6 +622,60 @@ function describeCause(cause) {
540
622
  if (cause instanceof Error) return cause.message || cause.name;
541
623
  return String(cause);
542
624
  }
625
+ /** The value at a dot `path` of `row`. */
626
+ function valueAt(row, path) {
627
+ if (!path.includes(".") || Object.hasOwn(row, path)) return row[path];
628
+ let v = row;
629
+ for (const part of path.split(".")) v = v?.[part];
630
+ return v;
631
+ }
632
+ /**
633
+ * The identifier `action` takes for one row (or identifier) of the
634
+ * controller whose `/meta` listed it (since 0.1.147):
635
+ *
636
+ * - a delegated action with `idMap` → the owner's identification,
637
+ * `{ [ownerField]: rowOrId[path] }` (dot paths allowed); a missing value
638
+ * throws `TypeError` naming the action and the path;
639
+ * - any other action → `rowOrId`'s `preferredId` fields when it carries them
640
+ * all, else `rowOrId` itself (already an identifier).
641
+ *
642
+ * UIs build `ids` from loaded rows with it; `Client.action()` applies the
643
+ * `idMap` mapping itself.
644
+ *
645
+ * @since 0.1.147
646
+ */
647
+ function actionIdentifier(action, rowOrId, preferredId) {
648
+ if (action.idMap) {
649
+ const out = {};
650
+ for (const [field, path] of Object.entries(action.idMap)) {
651
+ const value = valueAt(rowOrId, path);
652
+ if (value === void 0 || value === null) throw new TypeError(`client.action("${action.name}"): the identifier has no "${path}" — needed for the owner's "${field}".`);
653
+ out[field] = value;
654
+ }
655
+ return out;
656
+ }
657
+ if (preferredId.length > 0 && preferredId.every((f) => rowOrId[f] !== void 0)) return Object.fromEntries(preferredId.map((f) => [f, rowOrId[f]]));
658
+ return rowOrId;
659
+ }
660
+ /** `action()`'s `id` argument through a delegated action's `idMap` (shape errors are left to the body builder). */
661
+ function mapDelegatedIds(action, id) {
662
+ const map = (one) => one !== null && typeof one === "object" && !Array.isArray(one) ? actionIdentifier(action, one, []) : one;
663
+ return Array.isArray(id) ? id.map(map) : map(id);
664
+ }
665
+ /** The wire `query` of a {@link TDbQueryTarget}. */
666
+ function queryTargetBody(target) {
667
+ const controls = {};
668
+ if (target.search !== void 0) controls.$search = target.search;
669
+ if (target.index !== void 0) controls.$index = target.index;
670
+ const out = { q: (0, _uniqu_url_builder.buildUrl)({
671
+ filter: target.filter ?? {},
672
+ controls
673
+ }) };
674
+ if (target.exclude?.length) out.exclude = target.exclude;
675
+ if (target.expectCount !== void 0) out.expectCount = target.expectCount;
676
+ if (target.maxRows !== void 0) out.maxRows = target.maxRows;
677
+ return out;
678
+ }
543
679
  /**
544
680
  * Render a single identifier field for substitution into a navigate-URL
545
681
  * template or human-readable string. `null` / `undefined` collapse to `""`
@@ -578,11 +714,19 @@ function describeShape(value) {
578
714
  //#endregion
579
715
  exports.ActionDisabledError = ActionDisabledError;
580
716
  exports.ActionNotFoundError = ActionNotFoundError;
717
+ exports.ActionTargetError = ActionTargetError;
581
718
  exports.ActionUnsupportedError = ActionUnsupportedError;
582
719
  exports.Client = Client;
583
720
  exports.ClientError = ClientError;
584
721
  exports.TransportError = TransportError;
585
722
  exports.VersionMismatchError = VersionMismatchError;
723
+ exports.actionIdentifier = actionIdentifier;
724
+ Object.defineProperty(exports, "bucketSeries", {
725
+ enumerable: true,
726
+ get: function() {
727
+ return _uniqu_core.bucketSeries;
728
+ }
729
+ });
586
730
  Object.defineProperty(exports, "bucketStartInstant", {
587
731
  enumerable: true,
588
732
  get: function() {
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as TDbDeleteResult, C as OwnOf, D as RowOf, E as RelationInfo, F as Uniquery, I as UniqueryControls, L as ValidGroupBy, M as TDbInsertResult, N as TDbUpdateResult, O as SearchIndexInfo, P as TypedWithRelation, R as WeekStart, S as NavOf, T as PatchOf, _ as DbRow, a as ValidatorMode, b as IdOf, c as AggregateResult, d as BucketUnit, f as CalendarBucketLabel, g as DbPatch, h as DataOf, j as TDbInsertManyResult, k as ServerError, l as AtscriptClientShape, m as ClientResponse, n as ClientValidator, p as ClientOptions, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as BucketExpr, v as FieldMeta, w as PageResult, x as MetaResponse, y as FilterExpr } from "./validator-810-rhnQ.cjs";
2
- import { AggregateExpr, AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, BucketExpr as BucketExpr$1, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1, ValidGroupBy as ValidGroupBy$1, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
1
+ import { A as TDbDeleteResult, C as OwnOf, D as RowOf, E as RelationInfo, F as TypedWithRelation, I as Uniquery, L as UniqueryControls, M as TDbInsertResult, N as TDbQueryTarget, O as SearchIndexInfo, P as TDbUpdateResult, R as ValidGroupBy, S as NavOf, T as PatchOf, _ as DbRow, a as ValidatorMode, b as IdOf, c as AggregateResult, d as BucketUnit, f as CalendarBucketLabel, g as DbPatch, h as DataOf, j as TDbInsertManyResult, k as ServerError, l as AtscriptClientShape, m as ClientResponse, n as ClientValidator, p as ClientOptions, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as BucketExpr, v as FieldMeta, w as PageResult, x as MetaResponse, y as FilterExpr, z as WeekStart } from "./validator-DVha6LOQ.cjs";
2
+ import { AggregateExpr, AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, BucketExpr as BucketExpr$1, BucketSeriesOptions, NextBucketOptions, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1, ValidGroupBy as ValidGroupBy$1, bucketSeries, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
3
3
  import { TAtscriptAnnotatedType, TSerializedAnnotatedType } from "@atscript/typescript/utils";
4
- import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
4
+ import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbActionTargetSummary, TDbActionTargetSummary as TDbActionTargetSummary$1, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
5
5
 
6
6
  //#region src/client.d.ts
7
7
  type Own<T> = OwnOf<T>;
@@ -59,7 +59,8 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
59
59
  *
60
60
  * `$select` may carry calendar buckets (`{ $bucket, $field, $tz?, $weekStart?, $as? }`);
61
61
  * a `$groupBy` entry must be a dimension or a bucket alias (`ValidGroupBy`), and a
62
- * bucket's value is typed as its `YYYY-MM-DD` label (`| null` for an optional source).
62
+ * bucket's value is typed as its `YYYY-MM-DD` (hour: `YYYY-MM-DDTHH:00`) label
63
+ * (`| null` for an optional source).
63
64
  * Gap-fill between labels with `nextBucketLabel` (re-exported here).
64
65
  */
65
66
  aggregate<const Q extends AggregateQuery$1<Own<T>>>(query: Q & ValidGroupBy$1<Own<T>, Q>): Promise<Q["controls"]["$select"] extends readonly (string | AggregateExpr | BucketExpr$1)[] ? AggregateResult$1<Own<T>, Q["controls"]["$select"]>[] : Record<string, unknown>[]>;
@@ -166,11 +167,48 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
166
167
  * `{ ids?, input? }` — `ids` carries `id` (object or array per level),
167
168
  * `input` carries the form payload.
168
169
  *
170
+ * **Delegated actions** (since 0.1.147). An action another controller owns
171
+ * (`owner`, e.g. a view listing its source table's actions) may carry
172
+ * `idMap`: each `id` is then a row (or identifier) of THIS controller and
173
+ * is mapped to the owner's identification — `{ [ownerField]:
174
+ * row[path] }` — before it is sent to `value` (the owner's route). A
175
+ * missing path throws `TypeError`. See {@link actionIdentifier}.
176
+ *
169
177
  * @typeParam R Caller-asserted return shape from the action handler. The
170
178
  * server returns whatever the handler emits (commonly
171
179
  * `{ message?: string, ... }`); the client cannot validate.
172
180
  */
173
181
  action<R = unknown>(name: string, id?: Partial<Own<T>> | Partial<Own<T>>[], input?: unknown): Promise<R>;
182
+ /**
183
+ * Runs a `'rows'` action on every row matching a query (since 0.1.147) —
184
+ * the action's `/meta` entry must carry `queryTarget`. POSTs
185
+ * `{ query: { q, exclude?, expectCount?, maxRows? }, input? }` to
186
+ * `queryTarget.url` (a delegated action: the view resolves the rows and
187
+ * runs the owner's action in batches) or `value`. `q` is the `/query`
188
+ * string of `target.filter` / `search` / `index`.
189
+ *
190
+ * The server answers what the handler returns — for a delegated action
191
+ * (and a handler returning `target.summary()`) a
192
+ * {@link TDbActionTargetSummary}. Refusals arrive as
193
+ * {@link ActionTargetError}: `TARGET_TOO_LARGE` (`cap`), `TARGET_CHANGED`
194
+ * (`matched` differs from `expectCount`), `TARGET_INVALID`. Throws
195
+ * {@link ActionUnsupportedError} when the action takes no query target.
196
+ *
197
+ * @since 0.1.147
198
+ */
199
+ actionOnQuery<R = TDbActionTargetSummary$1>(name: string, target: TDbQueryTarget<T>, input?: unknown): Promise<R>;
200
+ /**
201
+ * How many rows {@link actionOnQuery} would target right now (a dry run:
202
+ * the handler does not run) — pass the answer as `expectCount` to make the
203
+ * real run fail with `TARGET_CHANGED` if the set changed in between.
204
+ *
205
+ * @since 0.1.147
206
+ */
207
+ countActionTarget(name: string, target: TDbQueryTarget<T>): Promise<{
208
+ matched: number;
209
+ }>;
210
+ private _queryTargetAction;
211
+ private _postQueryTarget;
174
212
  /**
175
213
  * `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
176
214
  * row-level actions the caller may run on one row right now, and the
@@ -224,6 +262,22 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
224
262
  private _requestUrl;
225
263
  private _send;
226
264
  }
265
+ /**
266
+ * The identifier `action` takes for one row (or identifier) of the
267
+ * controller whose `/meta` listed it (since 0.1.147):
268
+ *
269
+ * - a delegated action with `idMap` → the owner's identification,
270
+ * `{ [ownerField]: rowOrId[path] }` (dot paths allowed); a missing value
271
+ * throws `TypeError` naming the action and the path;
272
+ * - any other action → `rowOrId`'s `preferredId` fields when it carries them
273
+ * all, else `rowOrId` itself (already an identifier).
274
+ *
275
+ * UIs build `ids` from loaded rows with it; `Client.action()` applies the
276
+ * `idMap` mapping itself.
277
+ *
278
+ * @since 0.1.147
279
+ */
280
+ declare function actionIdentifier(action: Pick<TDbActionInfo$1, "name" | "idMap">, rowOrId: Record<string, unknown>, preferredId: readonly string[]): Record<string, unknown>;
227
281
  /**
228
282
  * Render a single identifier field for substitution into a navigate-URL
229
283
  * template or human-readable string. `null` / `undefined` collapse to `""`
@@ -318,6 +372,34 @@ declare class ActionDisabledError extends ClientError {
318
372
  */
319
373
  get reasons(): (string | null)[] | undefined;
320
374
  }
375
+ /**
376
+ * Wire-body shape for `ActionTargetError` responses (since 0.1.147): a
377
+ * query target the server refused — `code` says why.
378
+ */
379
+ interface ActionTargetErrorBody extends ServerError {
380
+ name: "ActionTargetError";
381
+ code: "TARGET_INVALID" | "TARGET_TOO_LARGE" | "TARGET_CHANGED";
382
+ action: string;
383
+ /** `TARGET_CHANGED`: the current match count. */
384
+ matched?: number;
385
+ /** `TARGET_TOO_LARGE`: the most rows one request may target. */
386
+ cap?: number;
387
+ }
388
+ /**
389
+ * Typed marker thrown by `Client` when a query-targeted action request is
390
+ * refused (body `name === 'ActionTargetError'`): `TARGET_INVALID` (400),
391
+ * `TARGET_TOO_LARGE` (400, `cap`) or `TARGET_CHANGED` (409, `matched` — the
392
+ * rows the query matches now; re-confirm and retry with that count).
393
+ *
394
+ * @since 0.1.147
395
+ */
396
+ declare class ActionTargetError extends ClientError {
397
+ name: string;
398
+ get code(): ActionTargetErrorBody["code"];
399
+ get action(): string;
400
+ get matched(): number | undefined;
401
+ get cap(): number | undefined;
402
+ }
321
403
  /**
322
404
  * Wire-body shape for 409 OCC `version_mismatch` responses. Extends the base
323
405
  * `ServerError` envelope with a `kind` discriminator and the row's current
@@ -382,4 +464,4 @@ declare class ActionUnsupportedError extends Error {
382
464
  constructor(action: string, processor: string, message: string);
383
465
  }
384
466
  //#endregion
385
- export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, type BucketExpr, type BucketUnit, type CalendarBucketLabel, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type DbPatch, type DbRow, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type PatchOf, type RelationInfo, type RowOf, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbAvailableActions, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, TransportError, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidGroupBy, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, type WeekStart, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
467
+ export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionTargetError, type ActionTargetErrorBody, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, type BucketExpr, type BucketSeriesOptions, type BucketUnit, type CalendarBucketLabel, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type DbPatch, type DbRow, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type NextBucketOptions, type OwnOf, type PageResult, type PatchOf, type RelationInfo, type RowOf, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbActionTargetSummary, type TDbAvailableActions, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbQueryTarget, type TDbUpdateResult, type TSerializedAnnotatedType, TransportError, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidGroupBy, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, type WeekStart, actionIdentifier, bucketSeries, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as TDbDeleteResult, C as OwnOf, D as RowOf, E as RelationInfo, F as Uniquery, I as UniqueryControls, L as ValidGroupBy, M as TDbInsertResult, N as TDbUpdateResult, O as SearchIndexInfo, P as TypedWithRelation, R as WeekStart, S as NavOf, T as PatchOf, _ as DbRow, a as ValidatorMode, b as IdOf, c as AggregateResult, d as BucketUnit, f as CalendarBucketLabel, g as DbPatch, h as DataOf, j as TDbInsertManyResult, k as ServerError, l as AtscriptClientShape, m as ClientResponse, n as ClientValidator, p as ClientOptions, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as BucketExpr, v as FieldMeta, w as PageResult, x as MetaResponse, y as FilterExpr } from "./validator-CIm1ZCS5.mjs";
1
+ import { A as TDbDeleteResult, C as OwnOf, D as RowOf, E as RelationInfo, F as TypedWithRelation, I as Uniquery, L as UniqueryControls, M as TDbInsertResult, N as TDbQueryTarget, O as SearchIndexInfo, P as TDbUpdateResult, R as ValidGroupBy, S as NavOf, T as PatchOf, _ as DbRow, a as ValidatorMode, b as IdOf, c as AggregateResult, d as BucketUnit, f as CalendarBucketLabel, g as DbPatch, h as DataOf, j as TDbInsertManyResult, k as ServerError, l as AtscriptClientShape, m as ClientResponse, n as ClientValidator, p as ClientOptions, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as BucketExpr, v as FieldMeta, w as PageResult, x as MetaResponse, y as FilterExpr, z as WeekStart } from "./validator-xkEq4h0X.mjs";
2
2
  import { TAtscriptAnnotatedType, TSerializedAnnotatedType } from "@atscript/typescript/utils";
3
- import { AggregateExpr, AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, BucketExpr as BucketExpr$1, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1, ValidGroupBy as ValidGroupBy$1, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
4
- import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
3
+ import { AggregateExpr, AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, BucketExpr as BucketExpr$1, BucketSeriesOptions, NextBucketOptions, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1, ValidGroupBy as ValidGroupBy$1, bucketSeries, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
4
+ import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbActionTargetSummary, TDbActionTargetSummary as TDbActionTargetSummary$1, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
5
5
 
6
6
  //#region src/client.d.ts
7
7
  type Own<T> = OwnOf<T>;
@@ -59,7 +59,8 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
59
59
  *
60
60
  * `$select` may carry calendar buckets (`{ $bucket, $field, $tz?, $weekStart?, $as? }`);
61
61
  * a `$groupBy` entry must be a dimension or a bucket alias (`ValidGroupBy`), and a
62
- * bucket's value is typed as its `YYYY-MM-DD` label (`| null` for an optional source).
62
+ * bucket's value is typed as its `YYYY-MM-DD` (hour: `YYYY-MM-DDTHH:00`) label
63
+ * (`| null` for an optional source).
63
64
  * Gap-fill between labels with `nextBucketLabel` (re-exported here).
64
65
  */
65
66
  aggregate<const Q extends AggregateQuery$1<Own<T>>>(query: Q & ValidGroupBy$1<Own<T>, Q>): Promise<Q["controls"]["$select"] extends readonly (string | AggregateExpr | BucketExpr$1)[] ? AggregateResult$1<Own<T>, Q["controls"]["$select"]>[] : Record<string, unknown>[]>;
@@ -166,11 +167,48 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
166
167
  * `{ ids?, input? }` — `ids` carries `id` (object or array per level),
167
168
  * `input` carries the form payload.
168
169
  *
170
+ * **Delegated actions** (since 0.1.147). An action another controller owns
171
+ * (`owner`, e.g. a view listing its source table's actions) may carry
172
+ * `idMap`: each `id` is then a row (or identifier) of THIS controller and
173
+ * is mapped to the owner's identification — `{ [ownerField]:
174
+ * row[path] }` — before it is sent to `value` (the owner's route). A
175
+ * missing path throws `TypeError`. See {@link actionIdentifier}.
176
+ *
169
177
  * @typeParam R Caller-asserted return shape from the action handler. The
170
178
  * server returns whatever the handler emits (commonly
171
179
  * `{ message?: string, ... }`); the client cannot validate.
172
180
  */
173
181
  action<R = unknown>(name: string, id?: Partial<Own<T>> | Partial<Own<T>>[], input?: unknown): Promise<R>;
182
+ /**
183
+ * Runs a `'rows'` action on every row matching a query (since 0.1.147) —
184
+ * the action's `/meta` entry must carry `queryTarget`. POSTs
185
+ * `{ query: { q, exclude?, expectCount?, maxRows? }, input? }` to
186
+ * `queryTarget.url` (a delegated action: the view resolves the rows and
187
+ * runs the owner's action in batches) or `value`. `q` is the `/query`
188
+ * string of `target.filter` / `search` / `index`.
189
+ *
190
+ * The server answers what the handler returns — for a delegated action
191
+ * (and a handler returning `target.summary()`) a
192
+ * {@link TDbActionTargetSummary}. Refusals arrive as
193
+ * {@link ActionTargetError}: `TARGET_TOO_LARGE` (`cap`), `TARGET_CHANGED`
194
+ * (`matched` differs from `expectCount`), `TARGET_INVALID`. Throws
195
+ * {@link ActionUnsupportedError} when the action takes no query target.
196
+ *
197
+ * @since 0.1.147
198
+ */
199
+ actionOnQuery<R = TDbActionTargetSummary$1>(name: string, target: TDbQueryTarget<T>, input?: unknown): Promise<R>;
200
+ /**
201
+ * How many rows {@link actionOnQuery} would target right now (a dry run:
202
+ * the handler does not run) — pass the answer as `expectCount` to make the
203
+ * real run fail with `TARGET_CHANGED` if the set changed in between.
204
+ *
205
+ * @since 0.1.147
206
+ */
207
+ countActionTarget(name: string, target: TDbQueryTarget<T>): Promise<{
208
+ matched: number;
209
+ }>;
210
+ private _queryTargetAction;
211
+ private _postQueryTarget;
174
212
  /**
175
213
  * `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
176
214
  * row-level actions the caller may run on one row right now, and the
@@ -224,6 +262,22 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
224
262
  private _requestUrl;
225
263
  private _send;
226
264
  }
265
+ /**
266
+ * The identifier `action` takes for one row (or identifier) of the
267
+ * controller whose `/meta` listed it (since 0.1.147):
268
+ *
269
+ * - a delegated action with `idMap` → the owner's identification,
270
+ * `{ [ownerField]: rowOrId[path] }` (dot paths allowed); a missing value
271
+ * throws `TypeError` naming the action and the path;
272
+ * - any other action → `rowOrId`'s `preferredId` fields when it carries them
273
+ * all, else `rowOrId` itself (already an identifier).
274
+ *
275
+ * UIs build `ids` from loaded rows with it; `Client.action()` applies the
276
+ * `idMap` mapping itself.
277
+ *
278
+ * @since 0.1.147
279
+ */
280
+ declare function actionIdentifier(action: Pick<TDbActionInfo$1, "name" | "idMap">, rowOrId: Record<string, unknown>, preferredId: readonly string[]): Record<string, unknown>;
227
281
  /**
228
282
  * Render a single identifier field for substitution into a navigate-URL
229
283
  * template or human-readable string. `null` / `undefined` collapse to `""`
@@ -318,6 +372,34 @@ declare class ActionDisabledError extends ClientError {
318
372
  */
319
373
  get reasons(): (string | null)[] | undefined;
320
374
  }
375
+ /**
376
+ * Wire-body shape for `ActionTargetError` responses (since 0.1.147): a
377
+ * query target the server refused — `code` says why.
378
+ */
379
+ interface ActionTargetErrorBody extends ServerError {
380
+ name: "ActionTargetError";
381
+ code: "TARGET_INVALID" | "TARGET_TOO_LARGE" | "TARGET_CHANGED";
382
+ action: string;
383
+ /** `TARGET_CHANGED`: the current match count. */
384
+ matched?: number;
385
+ /** `TARGET_TOO_LARGE`: the most rows one request may target. */
386
+ cap?: number;
387
+ }
388
+ /**
389
+ * Typed marker thrown by `Client` when a query-targeted action request is
390
+ * refused (body `name === 'ActionTargetError'`): `TARGET_INVALID` (400),
391
+ * `TARGET_TOO_LARGE` (400, `cap`) or `TARGET_CHANGED` (409, `matched` — the
392
+ * rows the query matches now; re-confirm and retry with that count).
393
+ *
394
+ * @since 0.1.147
395
+ */
396
+ declare class ActionTargetError extends ClientError {
397
+ name: string;
398
+ get code(): ActionTargetErrorBody["code"];
399
+ get action(): string;
400
+ get matched(): number | undefined;
401
+ get cap(): number | undefined;
402
+ }
321
403
  /**
322
404
  * Wire-body shape for 409 OCC `version_mismatch` responses. Extends the base
323
405
  * `ServerError` envelope with a `kind` discriminator and the row's current
@@ -382,4 +464,4 @@ declare class ActionUnsupportedError extends Error {
382
464
  constructor(action: string, processor: string, message: string);
383
465
  }
384
466
  //#endregion
385
- export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, type BucketExpr, type BucketUnit, type CalendarBucketLabel, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type DbPatch, type DbRow, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type PatchOf, type RelationInfo, type RowOf, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbAvailableActions, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, TransportError, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidGroupBy, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, type WeekStart, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
467
+ export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionTargetError, type ActionTargetErrorBody, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, type BucketExpr, type BucketSeriesOptions, type BucketUnit, type CalendarBucketLabel, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type DbPatch, type DbRow, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type NextBucketOptions, type OwnOf, type PageResult, type PatchOf, type RelationInfo, type RowOf, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbActionTargetSummary, type TDbAvailableActions, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbQueryTarget, type TDbUpdateResult, type TSerializedAnnotatedType, TransportError, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidGroupBy, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, type WeekStart, actionIdentifier, bucketSeries, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { buildUrl } from "@uniqu/url/builder";
2
2
  import { deserializeAnnotatedType } from "@atscript/typescript/utils";
3
- import { bucketStartInstant, nextBucketLabel } from "@uniqu/core";
3
+ import { bucketSeries, bucketStartInstant, nextBucketLabel } from "@uniqu/core";
4
4
  //#region src/client-error.ts
5
5
  /**
6
6
  * Error thrown by `Client` when the server responds with a non-2xx status code.
@@ -64,6 +64,29 @@ var ActionDisabledError = class extends ClientError {
64
64
  }
65
65
  };
66
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
+ /**
67
90
  * Typed marker thrown by `Client._send` when the server response body's
68
91
  * `kind === 'version_mismatch'`. The transport / status / base body are
69
92
  * identical to a generic `ClientError`; this subclass adds a typed
@@ -185,7 +208,8 @@ var Client = class {
185
208
  *
186
209
  * `$select` may carry calendar buckets (`{ $bucket, $field, $tz?, $weekStart?, $as? }`);
187
210
  * a `$groupBy` entry must be a dimension or a bucket alias (`ValidGroupBy`), and a
188
- * bucket's value is typed as its `YYYY-MM-DD` label (`| null` for an optional source).
211
+ * bucket's value is typed as its `YYYY-MM-DD` (hour: `YYYY-MM-DDTHH:00`) label
212
+ * (`| null` for an optional source).
189
213
  * Gap-fill between labels with `nextBucketLabel` (re-exported here).
190
214
  */
191
215
  async aggregate(query) {
@@ -322,6 +346,13 @@ var Client = class {
322
346
  * `{ ids?, input? }` — `ids` carries `id` (object or array per level),
323
347
  * `input` carries the form payload.
324
348
  *
349
+ * **Delegated actions** (since 0.1.147). An action another controller owns
350
+ * (`owner`, e.g. a view listing its source table's actions) may carry
351
+ * `idMap`: each `id` is then a row (or identifier) of THIS controller and
352
+ * is mapped to the owner's identification — `{ [ownerField]:
353
+ * row[path] }` — before it is sent to `value` (the owner's route). A
354
+ * missing path throws `TypeError`. See {@link actionIdentifier}.
355
+ *
325
356
  * @typeParam R Caller-asserted return shape from the action handler. The
326
357
  * server returns whatever the handler emits (commonly
327
358
  * `{ message?: string, ... }`); the client cannot validate.
@@ -331,15 +362,65 @@ var Client = class {
331
362
  const action = meta.actions.find((a) => a.name === name);
332
363
  if (!action) throw new ActionNotFoundError(name);
333
364
  if (action.processor === "custom") throw new ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
365
+ const mapped = action.idMap ? mapDelegatedIds(action, id) : id;
334
366
  if (action.processor === "navigate") {
335
- const url = this._interpolateNavigateUrl(action, id, meta.preferredId);
367
+ const order = action.idMap ? Object.keys(action.idMap) : meta.preferredId;
368
+ const url = this._interpolateNavigateUrl(action, mapped, order);
336
369
  await this._dispatchNavigate(action, url);
337
370
  return;
338
371
  }
339
- const body = this._buildActionBody(action, id, input);
372
+ const body = this._buildActionBody(action, mapped, input);
340
373
  return this._postAction(action, body);
341
374
  }
342
375
  /**
376
+ * Runs a `'rows'` action on every row matching a query (since 0.1.147) —
377
+ * the action's `/meta` entry must carry `queryTarget`. POSTs
378
+ * `{ query: { q, exclude?, expectCount?, maxRows? }, input? }` to
379
+ * `queryTarget.url` (a delegated action: the view resolves the rows and
380
+ * runs the owner's action in batches) or `value`. `q` is the `/query`
381
+ * string of `target.filter` / `search` / `index`.
382
+ *
383
+ * The server answers what the handler returns — for a delegated action
384
+ * (and a handler returning `target.summary()`) a
385
+ * {@link TDbActionTargetSummary}. Refusals arrive as
386
+ * {@link ActionTargetError}: `TARGET_TOO_LARGE` (`cap`), `TARGET_CHANGED`
387
+ * (`matched` differs from `expectCount`), `TARGET_INVALID`. Throws
388
+ * {@link ActionUnsupportedError} when the action takes no query target.
389
+ *
390
+ * @since 0.1.147
391
+ */
392
+ async actionOnQuery(name, target, input) {
393
+ const action = await this._queryTargetAction(name);
394
+ const body = { query: queryTargetBody(target) };
395
+ if (input !== void 0) body.input = input;
396
+ return this._postQueryTarget(action, body);
397
+ }
398
+ /**
399
+ * How many rows {@link actionOnQuery} would target right now (a dry run:
400
+ * the handler does not run) — pass the answer as `expectCount` to make the
401
+ * real run fail with `TARGET_CHANGED` if the set changed in between.
402
+ *
403
+ * @since 0.1.147
404
+ */
405
+ async countActionTarget(name, target) {
406
+ const action = await this._queryTargetAction(name);
407
+ const query = {
408
+ ...queryTargetBody(target),
409
+ dryRun: true
410
+ };
411
+ return this._postQueryTarget(action, { query });
412
+ }
413
+ async _queryTargetAction(name) {
414
+ const action = (await this.meta()).actions.find((a) => a.name === name);
415
+ if (!action) throw new ActionNotFoundError(name);
416
+ if (!action.queryTarget || action.processor !== "backend") throw new ActionUnsupportedError(name, action.processor, `Action "${name}" does not accept a query target (no \`queryTarget\` in /meta).`);
417
+ return action;
418
+ }
419
+ _postQueryTarget(action, body) {
420
+ const path = action.queryTarget?.url ?? action.value;
421
+ return this._requestUrl("POST", `${this._baseUrl}${path}`, body, true);
422
+ }
423
+ /**
343
424
  * `GET /meta/actions/:id` or `GET /meta/actions?k1=v1&k2=v2` — the
344
425
  * row-level actions the caller may run on one row right now, and the
345
426
  * reasons of those disabled with one: `{ actions, disabledReasons? }`.
@@ -518,6 +599,7 @@ var Client = class {
518
599
  };
519
600
  }
520
601
  if (errorBody.name === "ActionDisabledError") throw new ActionDisabledError(res.status, errorBody);
602
+ if (errorBody.name === "ActionTargetError") throw new ActionTargetError(res.status, errorBody);
521
603
  if (errorBody.kind === "version_mismatch") throw new VersionMismatchError(res.status, errorBody);
522
604
  throw new ClientError(res.status, errorBody);
523
605
  }
@@ -539,6 +621,60 @@ function describeCause(cause) {
539
621
  if (cause instanceof Error) return cause.message || cause.name;
540
622
  return String(cause);
541
623
  }
624
+ /** The value at a dot `path` of `row`. */
625
+ function valueAt(row, path) {
626
+ if (!path.includes(".") || Object.hasOwn(row, path)) return row[path];
627
+ let v = row;
628
+ for (const part of path.split(".")) v = v?.[part];
629
+ return v;
630
+ }
631
+ /**
632
+ * The identifier `action` takes for one row (or identifier) of the
633
+ * controller whose `/meta` listed it (since 0.1.147):
634
+ *
635
+ * - a delegated action with `idMap` → the owner's identification,
636
+ * `{ [ownerField]: rowOrId[path] }` (dot paths allowed); a missing value
637
+ * throws `TypeError` naming the action and the path;
638
+ * - any other action → `rowOrId`'s `preferredId` fields when it carries them
639
+ * all, else `rowOrId` itself (already an identifier).
640
+ *
641
+ * UIs build `ids` from loaded rows with it; `Client.action()` applies the
642
+ * `idMap` mapping itself.
643
+ *
644
+ * @since 0.1.147
645
+ */
646
+ function actionIdentifier(action, rowOrId, preferredId) {
647
+ if (action.idMap) {
648
+ const out = {};
649
+ for (const [field, path] of Object.entries(action.idMap)) {
650
+ const value = valueAt(rowOrId, path);
651
+ if (value === void 0 || value === null) throw new TypeError(`client.action("${action.name}"): the identifier has no "${path}" — needed for the owner's "${field}".`);
652
+ out[field] = value;
653
+ }
654
+ return out;
655
+ }
656
+ if (preferredId.length > 0 && preferredId.every((f) => rowOrId[f] !== void 0)) return Object.fromEntries(preferredId.map((f) => [f, rowOrId[f]]));
657
+ return rowOrId;
658
+ }
659
+ /** `action()`'s `id` argument through a delegated action's `idMap` (shape errors are left to the body builder). */
660
+ function mapDelegatedIds(action, id) {
661
+ const map = (one) => one !== null && typeof one === "object" && !Array.isArray(one) ? actionIdentifier(action, one, []) : one;
662
+ return Array.isArray(id) ? id.map(map) : map(id);
663
+ }
664
+ /** The wire `query` of a {@link TDbQueryTarget}. */
665
+ function queryTargetBody(target) {
666
+ const controls = {};
667
+ if (target.search !== void 0) controls.$search = target.search;
668
+ if (target.index !== void 0) controls.$index = target.index;
669
+ const out = { q: buildUrl({
670
+ filter: target.filter ?? {},
671
+ controls
672
+ }) };
673
+ if (target.exclude?.length) out.exclude = target.exclude;
674
+ if (target.expectCount !== void 0) out.expectCount = target.expectCount;
675
+ if (target.maxRows !== void 0) out.maxRows = target.maxRows;
676
+ return out;
677
+ }
542
678
  /**
543
679
  * Render a single identifier field for substitution into a navigate-URL
544
680
  * template or human-readable string. `null` / `undefined` collapse to `""`
@@ -575,4 +711,4 @@ function describeShape(value) {
575
711
  return typeof value;
576
712
  }
577
713
  //#endregion
578
- export { ActionDisabledError, ActionNotFoundError, ActionUnsupportedError, Client, ClientError, TransportError, VersionMismatchError, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
714
+ export { ActionDisabledError, ActionNotFoundError, ActionTargetError, ActionUnsupportedError, Client, ClientError, TransportError, VersionMismatchError, actionIdentifier, bucketSeries, bucketStartInstant, encodeNavigateId, formatIdentifier, formatIdentifierField, nextBucketLabel };
@@ -132,6 +132,28 @@ type ClientResponse<T, Q> = DbResponse<DataOf<T>, NavOf<T>, Q> & {
132
132
  */
133
133
  $disabledReasons?: Record<string, string>;
134
134
  };
135
+ /**
136
+ * "Every row matching this query" — the target of
137
+ * `Client.actionOnQuery()` / `countActionTarget()`, for a `'rows'` action
138
+ * whose `/meta` entry carries `queryTarget`. The same filter / search /
139
+ * index the user's `/query` used; the server resolves it under the caller's
140
+ * read scope and the action's own gate.
141
+ *
142
+ * @since 0.1.147
143
+ */
144
+ interface TDbQueryTarget<T = AtscriptClientShape> {
145
+ filter?: Uniquery$1<OwnOf<T>, NavOf<T>>["filter"];
146
+ /** `$search` term. */
147
+ search?: string;
148
+ /** `$index` — the search index `search` uses. */
149
+ index?: string;
150
+ /** Identifiers to leave out (any identification of the controller the request goes to). */
151
+ exclude?: Record<string, unknown>[];
152
+ /** Fail with 409 `TARGET_CHANGED` when the target no longer matches exactly this many rows. */
153
+ expectCount?: number;
154
+ /** Client-side cap (never above the action's `queryTarget.maxRows`). */
155
+ maxRows?: number;
156
+ }
135
157
  //#endregion
136
158
  //#region src/validator.d.ts
137
159
  /** Options for {@link ClientValidator} / {@link createClientValidator}. */
@@ -214,4 +236,4 @@ declare class ClientValidationError extends Error {
214
236
  */
215
237
  declare function createClientValidator(meta: MetaResponse, opts?: ClientValidatorOptions): ClientValidator;
216
238
  //#endregion
217
- export { TDbDeleteResult$1 as A, OwnOf as C, RowOf as D, RelationInfo as E, Uniquery$1 as F, UniqueryControls$1 as I, ValidGroupBy$1 as L, TDbInsertResult$1 as M, TDbUpdateResult$1 as N, SearchIndexInfo as O, TypedWithRelation as P, WeekStart as R, NavOf as S, PatchOf as T, DbRow as _, ValidatorMode$1 as a, IdOf as b, AggregateResult$1 as c, BucketUnit as d, CalendarBucketLabel as f, DbPatch as g, DataOf as h, DbValidationContext as i, TDbInsertManyResult$1 as j, ServerError as k, AtscriptClientShape as l, ClientResponse as m, ClientValidator as n, createClientValidator as o, ClientOptions as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, BucketExpr$1 as u, FieldMeta as v, PageResult as w, MetaResponse as x, FilterExpr as y };
239
+ export { TDbDeleteResult$1 as A, OwnOf as C, RowOf as D, RelationInfo as E, TypedWithRelation as F, Uniquery$1 as I, UniqueryControls$1 as L, TDbInsertResult$1 as M, TDbQueryTarget as N, SearchIndexInfo as O, TDbUpdateResult$1 as P, ValidGroupBy$1 as R, NavOf as S, PatchOf as T, DbRow as _, ValidatorMode$1 as a, IdOf as b, AggregateResult$1 as c, BucketUnit as d, CalendarBucketLabel as f, DbPatch as g, DataOf as h, DbValidationContext as i, TDbInsertManyResult$1 as j, ServerError as k, AtscriptClientShape as l, ClientResponse as m, ClientValidator as n, createClientValidator as o, ClientOptions as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, BucketExpr$1 as u, FieldMeta as v, PageResult as w, MetaResponse as x, FilterExpr as y, WeekStart as z };
@@ -132,6 +132,28 @@ type ClientResponse<T, Q> = DbResponse<DataOf<T>, NavOf<T>, Q> & {
132
132
  */
133
133
  $disabledReasons?: Record<string, string>;
134
134
  };
135
+ /**
136
+ * "Every row matching this query" — the target of
137
+ * `Client.actionOnQuery()` / `countActionTarget()`, for a `'rows'` action
138
+ * whose `/meta` entry carries `queryTarget`. The same filter / search /
139
+ * index the user's `/query` used; the server resolves it under the caller's
140
+ * read scope and the action's own gate.
141
+ *
142
+ * @since 0.1.147
143
+ */
144
+ interface TDbQueryTarget<T = AtscriptClientShape> {
145
+ filter?: Uniquery$1<OwnOf<T>, NavOf<T>>["filter"];
146
+ /** `$search` term. */
147
+ search?: string;
148
+ /** `$index` — the search index `search` uses. */
149
+ index?: string;
150
+ /** Identifiers to leave out (any identification of the controller the request goes to). */
151
+ exclude?: Record<string, unknown>[];
152
+ /** Fail with 409 `TARGET_CHANGED` when the target no longer matches exactly this many rows. */
153
+ expectCount?: number;
154
+ /** Client-side cap (never above the action's `queryTarget.maxRows`). */
155
+ maxRows?: number;
156
+ }
135
157
  //#endregion
136
158
  //#region src/validator.d.ts
137
159
  /** Options for {@link ClientValidator} / {@link createClientValidator}. */
@@ -214,4 +236,4 @@ declare class ClientValidationError extends Error {
214
236
  */
215
237
  declare function createClientValidator(meta: MetaResponse, opts?: ClientValidatorOptions): ClientValidator;
216
238
  //#endregion
217
- export { TDbDeleteResult$1 as A, OwnOf as C, RowOf as D, RelationInfo as E, Uniquery$1 as F, UniqueryControls$1 as I, ValidGroupBy$1 as L, TDbInsertResult$1 as M, TDbUpdateResult$1 as N, SearchIndexInfo as O, TypedWithRelation as P, WeekStart as R, NavOf as S, PatchOf as T, DbRow as _, ValidatorMode$1 as a, IdOf as b, AggregateResult$1 as c, BucketUnit as d, CalendarBucketLabel as f, DbPatch as g, DataOf as h, DbValidationContext as i, TDbInsertManyResult$1 as j, ServerError as k, AtscriptClientShape as l, ClientResponse as m, ClientValidator as n, createClientValidator as o, ClientOptions as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, BucketExpr$1 as u, FieldMeta as v, PageResult as w, MetaResponse as x, FilterExpr as y };
239
+ export { TDbDeleteResult$1 as A, OwnOf as C, RowOf as D, RelationInfo as E, TypedWithRelation as F, Uniquery$1 as I, UniqueryControls$1 as L, TDbInsertResult$1 as M, TDbQueryTarget as N, SearchIndexInfo as O, TDbUpdateResult$1 as P, ValidGroupBy$1 as R, NavOf as S, PatchOf as T, DbRow as _, ValidatorMode$1 as a, IdOf as b, AggregateResult$1 as c, BucketUnit as d, CalendarBucketLabel as f, DbPatch as g, DataOf as h, DbValidationContext as i, TDbInsertManyResult$1 as j, ServerError as k, AtscriptClientShape as l, ClientResponse as m, ClientValidator as n, createClientValidator as o, ClientOptions as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, BucketExpr$1 as u, FieldMeta as v, PageResult as w, MetaResponse as x, FilterExpr as y, WeekStart as z };
@@ -1,2 +1,2 @@
1
- import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-810-rhnQ.cjs";
1
+ import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-DVha6LOQ.cjs";
2
2
  export { ClientValidationError, ClientValidator, ClientValidatorOptions, type DbValidationContext, type ValidatorMode, createClientValidator };
@@ -1,2 +1,2 @@
1
- import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-CIm1ZCS5.mjs";
1
+ import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-xkEq4h0X.mjs";
2
2
  export { ClientValidationError, ClientValidator, ClientValidatorOptions, type DbValidationContext, type ValidatorMode, createClientValidator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-client",
3
- "version": "0.1.145",
3
+ "version": "0.1.147",
4
4
  "description": "Browser-compatible HTTP client for @atscript/moost-db REST endpoints.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -43,18 +43,18 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@uniqu/core": "^0.1.11",
47
- "@uniqu/url": "^0.1.11"
46
+ "@uniqu/core": "^0.1.12",
47
+ "@uniqu/url": "^0.1.12"
48
48
  },
49
49
  "devDependencies": {
50
- "@atscript/core": "^0.1.97",
51
- "@atscript/typescript": "^0.1.97",
52
- "unplugin-atscript": "^0.1.97",
53
- "@atscript/db": "0.1.145"
50
+ "@atscript/core": "^0.1.99",
51
+ "@atscript/typescript": "^0.1.99",
52
+ "unplugin-atscript": "^0.1.99",
53
+ "@atscript/db": "0.1.147"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@atscript/db": "^0.1.44",
57
- "@atscript/typescript": "^0.1.97"
57
+ "@atscript/typescript": "^0.1.99"
58
58
  },
59
59
  "scripts": {
60
60
  "postinstall": "asc -f dts",