@atscript/db-client 0.1.126 → 0.1.128

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
@@ -212,22 +212,26 @@ var Client = class {
212
212
  return this._getOrNull(`one/${encodeURIComponent(String(id))}${controlStr ? `?${controlStr}` : ""}`);
213
213
  }
214
214
  async insert(data) {
215
- await this._validateData(data, "insert");
216
- return this._request("POST", "", data);
215
+ return this._request("POST", "", await this._prepareWrite(data, "insert"));
217
216
  }
218
217
  /**
219
218
  * `PATCH /` — partial update one or many records by primary key.
219
+ *
220
+ * Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
221
+ * contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
222
+ * before preflight and sending. A stale version surfaces as
223
+ * {@link VersionMismatchError}; a PK-only body with a version is a real
224
+ * write (the server bumps the version).
220
225
  */
221
226
  async update(data) {
222
- await this._validateData(data, "patch");
223
- return this._request("PATCH", "", data);
227
+ return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
224
228
  }
225
229
  /**
226
- * `PUT /` — full replace one or many records by primary key.
230
+ * `PUT /` — full replace one or many records by primary key. Same OCC
231
+ * shapes as {@link update}; the version column itself is optional.
227
232
  */
228
233
  async replace(data) {
229
- await this._validateData(data, "replace");
230
- return this._request("PUT", "", data);
234
+ return this._request("PUT", "", await this._prepareWrite(data, "replace"));
231
235
  }
232
236
  /**
233
237
  * `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
@@ -329,8 +333,12 @@ var Client = class {
329
333
  getValidator() {
330
334
  return this._getValidator();
331
335
  }
332
- async _validateData(data, mode) {
333
- (await this._getValidator()).validate(data, mode);
336
+ /** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
337
+ async _prepareWrite(data, mode) {
338
+ const validator = await this._getValidator();
339
+ const normalized = validator.liftCas(data, mode);
340
+ validator.validate(normalized, mode);
341
+ return normalized;
334
342
  }
335
343
  _getValidator() {
336
344
  if (!this._validatorPromise) this._validatorPromise = Promise.all([this.meta(), Promise.resolve().then(() => require("./validator.cjs"))]).then(([m, { createClientValidator }]) => createClientValidator(m, { lenientWrites: this._lenientWrites })).catch((err) => {
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TDbDeleteResult, D as TypedWithRelation, E as TDbUpdateResult, O as Uniquery, S as ServerError, T as TDbInsertResult, _ as NavOf, a as ValidatorMode, b as RelationInfo, c as AggregateResult, d as ClientResponse, f as DataOf, g as MetaResponse, h as IdOf, k as UniqueryControls, l as AtscriptClientShape, m as FilterExpr, n as ClientValidator, p as FieldMeta, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as ClientOptions, v as OwnOf, w as TDbInsertManyResult, x as SearchIndexInfo, y as PageResult } from "./validator-3I12BVi4.cjs";
1
+ import { A as TDbUpdateResult, C as RelationInfo, D as TDbDeleteResult, E as ServerError, M as Uniquery, N as UniqueryControls, O as TDbInsertManyResult, S as PatchOf, T as SearchIndexInfo, _ as IdOf, a as ValidatorMode, b as OwnOf, c as AggregateResult, d as ClientResponse, f as DataOf, g as FilterExpr, h as FieldMeta, j as TypedWithRelation, k as TDbInsertResult, l as AtscriptClientShape, m as DbRow, n as ClientValidator, p as DbPatch, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as ClientOptions, v as MetaResponse, w as RowOf, x as PageResult, y as NavOf } from "./validator-C3uaaZkU.cjs";
2
2
  import { AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1 } from "@uniqu/core";
3
3
  import { TAtscriptAnnotatedType, TSerializedAnnotatedType } from "@atscript/typescript/utils";
4
4
  import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
@@ -6,7 +6,6 @@ import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLev
6
6
  //#region src/client.d.ts
7
7
  type Own<T> = OwnOf<T>;
8
8
  type Nav<T> = NavOf<T>;
9
- type Data<T> = DataOf<T>;
10
9
  type Id<T> = IdOf<T>;
11
10
  type Response<T, Q> = ClientResponse<T, Q>;
12
11
  /**
@@ -99,21 +98,29 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
99
98
  controls?: UniqueryControls$1<Own<T>, Nav<T>>;
100
99
  }>(id: Id<T>, query?: Q): Promise<Response<T, Q> | null>;
101
100
  /**
102
- * `POST /` — insert one record.
101
+ * `POST /` — insert one record. The version column (if any) is optional —
102
+ * the server initialises it; `$cas` is rejected (no meaning on insert).
103
103
  */
104
- insert(data: Partial<Data<T>>): Promise<TDbInsertResult$1>;
104
+ insert(data: PatchOf<T>): Promise<TDbInsertResult$1>;
105
105
  /**
106
106
  * `POST /` — insert many records.
107
107
  */
108
- insert(data: Partial<Data<T>>[]): Promise<TDbInsertManyResult$1>;
108
+ insert(data: PatchOf<T>[]): Promise<TDbInsertManyResult$1>;
109
109
  /**
110
110
  * `PATCH /` — partial update one or many records by primary key.
111
+ *
112
+ * Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
113
+ * contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
114
+ * before preflight and sending. A stale version surfaces as
115
+ * {@link VersionMismatchError}; a PK-only body with a version is a real
116
+ * write (the server bumps the version).
111
117
  */
112
- update(data: Partial<Data<T>> | Partial<Data<T>>[]): Promise<TDbUpdateResult$1>;
118
+ update(data: PatchOf<T> | PatchOf<T>[]): Promise<TDbUpdateResult$1>;
113
119
  /**
114
- * `PUT /` — full replace one or many records by primary key.
120
+ * `PUT /` — full replace one or many records by primary key. Same OCC
121
+ * shapes as {@link update}; the version column itself is optional.
115
122
  */
116
- replace(data: Data<T> | Data<T>[]): Promise<TDbUpdateResult$1>;
123
+ replace(data: RowOf<T> | RowOf<T>[]): Promise<TDbUpdateResult$1>;
117
124
  /**
118
125
  * `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
119
126
  */
@@ -177,7 +184,8 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
177
184
  * Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
178
185
  */
179
186
  getValidator(): Promise<ClientValidator>;
180
- private _validateData;
187
+ /** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
188
+ private _prepareWrite;
181
189
  private _getValidator;
182
190
  private _buildActionBody;
183
191
  private _interpolateNavigateUrl;
@@ -306,4 +314,4 @@ declare class ActionUnsupportedError extends Error {
306
314
  constructor(action: string, processor: string, message: string);
307
315
  }
308
316
  //#endregion
309
- export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, encodeNavigateId, formatIdentifier, formatIdentifierField };
317
+ export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, 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 TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, encodeNavigateId, formatIdentifier, formatIdentifierField };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TDbDeleteResult, D as TypedWithRelation, E as TDbUpdateResult, O as Uniquery, S as ServerError, T as TDbInsertResult, _ as NavOf, a as ValidatorMode, b as RelationInfo, c as AggregateResult, d as ClientResponse, f as DataOf, g as MetaResponse, h as IdOf, k as UniqueryControls, l as AtscriptClientShape, m as FilterExpr, n as ClientValidator, p as FieldMeta, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as ClientOptions, v as OwnOf, w as TDbInsertManyResult, x as SearchIndexInfo, y as PageResult } from "./validator-DBEmk_s8.mjs";
1
+ import { A as TDbUpdateResult, C as RelationInfo, D as TDbDeleteResult, E as ServerError, M as Uniquery, N as UniqueryControls, O as TDbInsertManyResult, S as PatchOf, T as SearchIndexInfo, _ as IdOf, a as ValidatorMode, b as OwnOf, c as AggregateResult, d as ClientResponse, f as DataOf, g as FilterExpr, h as FieldMeta, j as TypedWithRelation, k as TDbInsertResult, l as AtscriptClientShape, m as DbRow, n as ClientValidator, p as DbPatch, r as ClientValidatorOptions, s as AggregateQuery, t as ClientValidationError, u as ClientOptions, v as MetaResponse, w as RowOf, x as PageResult, y as NavOf } from "./validator-CfPFKDL_.mjs";
2
2
  import { TAtscriptAnnotatedType, TSerializedAnnotatedType } from "@atscript/typescript/utils";
3
3
  import { AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1 } from "@uniqu/core";
4
4
  import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
@@ -6,7 +6,6 @@ import { TCrudOp, TCrudPermissions, TDbActionInfo, TDbActionIntent, TDbActionLev
6
6
  //#region src/client.d.ts
7
7
  type Own<T> = OwnOf<T>;
8
8
  type Nav<T> = NavOf<T>;
9
- type Data<T> = DataOf<T>;
10
9
  type Id<T> = IdOf<T>;
11
10
  type Response<T, Q> = ClientResponse<T, Q>;
12
11
  /**
@@ -99,21 +98,29 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
99
98
  controls?: UniqueryControls$1<Own<T>, Nav<T>>;
100
99
  }>(id: Id<T>, query?: Q): Promise<Response<T, Q> | null>;
101
100
  /**
102
- * `POST /` — insert one record.
101
+ * `POST /` — insert one record. The version column (if any) is optional —
102
+ * the server initialises it; `$cas` is rejected (no meaning on insert).
103
103
  */
104
- insert(data: Partial<Data<T>>): Promise<TDbInsertResult$1>;
104
+ insert(data: PatchOf<T>): Promise<TDbInsertResult$1>;
105
105
  /**
106
106
  * `POST /` — insert many records.
107
107
  */
108
- insert(data: Partial<Data<T>>[]): Promise<TDbInsertManyResult$1>;
108
+ insert(data: PatchOf<T>[]): Promise<TDbInsertManyResult$1>;
109
109
  /**
110
110
  * `PATCH /` — partial update one or many records by primary key.
111
+ *
112
+ * Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
113
+ * contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
114
+ * before preflight and sending. A stale version surfaces as
115
+ * {@link VersionMismatchError}; a PK-only body with a version is a real
116
+ * write (the server bumps the version).
111
117
  */
112
- update(data: Partial<Data<T>> | Partial<Data<T>>[]): Promise<TDbUpdateResult$1>;
118
+ update(data: PatchOf<T> | PatchOf<T>[]): Promise<TDbUpdateResult$1>;
113
119
  /**
114
- * `PUT /` — full replace one or many records by primary key.
120
+ * `PUT /` — full replace one or many records by primary key. Same OCC
121
+ * shapes as {@link update}; the version column itself is optional.
115
122
  */
116
- replace(data: Data<T> | Data<T>[]): Promise<TDbUpdateResult$1>;
123
+ replace(data: RowOf<T> | RowOf<T>[]): Promise<TDbUpdateResult$1>;
117
124
  /**
118
125
  * `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
119
126
  */
@@ -177,7 +184,8 @@ declare class Client<T extends AtscriptClientShape = AtscriptClientShape> {
177
184
  * Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
178
185
  */
179
186
  getValidator(): Promise<ClientValidator>;
180
- private _validateData;
187
+ /** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
188
+ private _prepareWrite;
181
189
  private _getValidator;
182
190
  private _buildActionBody;
183
191
  private _interpolateNavigateUrl;
@@ -306,4 +314,4 @@ declare class ActionUnsupportedError extends Error {
306
314
  constructor(action: string, processor: string, message: string);
307
315
  }
308
316
  //#endregion
309
- export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, Client, ClientError, type ClientOptions, type ClientResponse, type ClientValidationError, type ClientValidator, type ClientValidatorOptions, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, encodeNavigateId, formatIdentifier, formatIdentifierField };
317
+ export { ActionDisabledError, type ActionDisabledErrorBody, ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, type AtscriptClientShape, 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 TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, encodeNavigateId, formatIdentifier, formatIdentifierField };
package/dist/index.mjs CHANGED
@@ -211,22 +211,26 @@ var Client = class {
211
211
  return this._getOrNull(`one/${encodeURIComponent(String(id))}${controlStr ? `?${controlStr}` : ""}`);
212
212
  }
213
213
  async insert(data) {
214
- await this._validateData(data, "insert");
215
- return this._request("POST", "", data);
214
+ return this._request("POST", "", await this._prepareWrite(data, "insert"));
216
215
  }
217
216
  /**
218
217
  * `PATCH /` — partial update one or many records by primary key.
218
+ *
219
+ * Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
220
+ * contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
221
+ * before preflight and sending. A stale version surfaces as
222
+ * {@link VersionMismatchError}; a PK-only body with a version is a real
223
+ * write (the server bumps the version).
219
224
  */
220
225
  async update(data) {
221
- await this._validateData(data, "patch");
222
- return this._request("PATCH", "", data);
226
+ return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
223
227
  }
224
228
  /**
225
- * `PUT /` — full replace one or many records by primary key.
229
+ * `PUT /` — full replace one or many records by primary key. Same OCC
230
+ * shapes as {@link update}; the version column itself is optional.
226
231
  */
227
232
  async replace(data) {
228
- await this._validateData(data, "replace");
229
- return this._request("PUT", "", data);
233
+ return this._request("PUT", "", await this._prepareWrite(data, "replace"));
230
234
  }
231
235
  /**
232
236
  * `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
@@ -328,8 +332,12 @@ var Client = class {
328
332
  getValidator() {
329
333
  return this._getValidator();
330
334
  }
331
- async _validateData(data, mode) {
332
- (await this._getValidator()).validate(data, mode);
335
+ /** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
336
+ async _prepareWrite(data, mode) {
337
+ const validator = await this._getValidator();
338
+ const normalized = validator.liftCas(data, mode);
339
+ validator.validate(normalized, mode);
340
+ return normalized;
333
341
  }
334
342
  _getValidator() {
335
343
  if (!this._validatorPromise) this._validatorPromise = Promise.all([this.meta(), import("./validator.mjs")]).then(([m, { createClientValidator }]) => createClientValidator(m, { lenientWrites: this._lenientWrites })).catch((err) => {
@@ -1,9 +1,17 @@
1
1
  import { AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, FilterExpr, TypedWithRelation, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1 } from "@uniqu/core";
2
2
  import { TAtscriptAnnotatedType, TAtscriptTypeObject } from "@atscript/typescript/utils";
3
- import { DbResponse, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1, TFieldMeta, TMetaResponse, TRelationInfo, TSearchIndexInfo } from "@atscript/db";
3
+ import { DbPatch, DbResponse, DbRow, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1, TFieldMeta, TMetaResponse, TRelationInfo, TSearchIndexInfo } from "@atscript/db";
4
4
  import { DbValidationContext, ValidatorMode, ValidatorMode as ValidatorMode$1 } from "@atscript/db/validator";
5
5
 
6
6
  //#region src/types.d.ts
7
+ /**
8
+ * Write payload for `insert()` / `update()`: every key optional, optional
9
+ * columns additionally accept `null` (explicit NULL). `$cas` is accepted on
10
+ * `update()` / `replace()` through the open index signature.
11
+ */
12
+ type PatchOf<T> = DbPatch<DataOf<T>>;
13
+ /** Write payload for `replace()`: full row; optional columns additionally accept `null`. */
14
+ type RowOf<T> = DbRow<DataOf<T>>;
7
15
  /** Options for creating a Client instance. */
8
16
  interface ClientOptions {
9
17
  /**
@@ -120,6 +128,12 @@ type ClientResponse<T, Q> = DbResponse<DataOf<T>, NavOf<T>, Q> & {
120
128
  //#region src/validator.d.ts
121
129
  /** Options for {@link ClientValidator} / {@link createClientValidator}. */
122
130
  interface ClientValidatorOptions {
131
+ /**
132
+ * The table's `@db.column.version` column (from `/meta`'s `versionColumn`).
133
+ * Enables the `$cas` lift in {@link ClientValidator.liftCas}; without it a
134
+ * `$cas` payload is rejected (the table is not versioned).
135
+ */
136
+ versionColumn?: string;
123
137
  /**
124
138
  * Tolerate unknown properties in write payloads. Enable when the served
125
139
  * `/meta` type is a PROJECTION of the full server-side type (e.g. an ARBAC
@@ -144,7 +158,21 @@ declare class ClientValidator {
144
158
  readonly flatMap: Map<string, TAtscriptAnnotatedType>;
145
159
  /** Set of field paths that are navigation relations (TO/FROM/VIA). */
146
160
  readonly navFields: ReadonlySet<string>;
161
+ /** The table's version column when it opts into OCC (from `/meta`). */
162
+ readonly versionColumn?: string;
147
163
  constructor(type: TAtscriptAnnotatedType<TAtscriptTypeObject>, opts?: ClientValidatorOptions);
164
+ /**
165
+ * Normalises the SDK `$cas` shape to the wire shape (since 0.1.128):
166
+ * `{ id, $cas: { version: 4 } }` → `{ id, version: 4 }` (the server lifts
167
+ * `version` back to `$cas`). Returns a shallow clone per item; items
168
+ * without `$cas` (a `$cas: undefined` counts as absent) are returned
169
+ * as-is. Throws `ClientValidationError` when the table is not versioned,
170
+ * the `$cas` shape is invalid, or `version` and `$cas` disagree — the same
171
+ * messages the server produces, from the shared `reconcileCas` — or when
172
+ * `$cas` appears on an insert.
173
+ */
174
+ liftCas(data: unknown, mode: ValidatorMode): unknown;
175
+ private _liftCasItem;
148
176
  /**
149
177
  * Validate data for a given write mode.
150
178
  * Throws `ClientValidationError` if validation fails.
@@ -178,4 +206,4 @@ declare class ClientValidationError extends Error {
178
206
  */
179
207
  declare function createClientValidator(meta: MetaResponse, opts?: ClientValidatorOptions): ClientValidator;
180
208
  //#endregion
181
- export { TDbDeleteResult$1 as C, TypedWithRelation as D, TDbUpdateResult$1 as E, Uniquery$1 as O, ServerError as S, TDbInsertResult$1 as T, NavOf as _, ValidatorMode$1 as a, RelationInfo as b, AggregateResult$1 as c, ClientResponse as d, DataOf as f, MetaResponse as g, IdOf as h, DbValidationContext as i, UniqueryControls$1 as k, AtscriptClientShape as l, FilterExpr as m, ClientValidator as n, createClientValidator as o, FieldMeta as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, ClientOptions as u, OwnOf as v, TDbInsertManyResult$1 as w, SearchIndexInfo as x, PageResult as y };
209
+ export { TDbUpdateResult$1 as A, RelationInfo as C, TDbDeleteResult$1 as D, ServerError as E, Uniquery$1 as M, UniqueryControls$1 as N, TDbInsertManyResult$1 as O, PatchOf as S, SearchIndexInfo as T, IdOf as _, ValidatorMode$1 as a, OwnOf as b, AggregateResult$1 as c, ClientResponse as d, DataOf as f, FilterExpr as g, FieldMeta as h, DbValidationContext as i, TypedWithRelation as j, TDbInsertResult$1 as k, AtscriptClientShape as l, DbRow as m, ClientValidator as n, createClientValidator as o, DbPatch as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, ClientOptions as u, MetaResponse as v, RowOf as w, PageResult as x, NavOf as y };
@@ -1,9 +1,17 @@
1
1
  import { TAtscriptAnnotatedType, TAtscriptTypeObject } from "@atscript/typescript/utils";
2
2
  import { DbValidationContext, ValidatorMode, ValidatorMode as ValidatorMode$1 } from "@atscript/db/validator";
3
3
  import { AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, FilterExpr, TypedWithRelation, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1 } from "@uniqu/core";
4
- import { DbResponse, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1, TFieldMeta, TMetaResponse, TRelationInfo, TSearchIndexInfo } from "@atscript/db";
4
+ import { DbPatch, DbResponse, DbRow, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1, TFieldMeta, TMetaResponse, TRelationInfo, TSearchIndexInfo } from "@atscript/db";
5
5
 
6
6
  //#region src/types.d.ts
7
+ /**
8
+ * Write payload for `insert()` / `update()`: every key optional, optional
9
+ * columns additionally accept `null` (explicit NULL). `$cas` is accepted on
10
+ * `update()` / `replace()` through the open index signature.
11
+ */
12
+ type PatchOf<T> = DbPatch<DataOf<T>>;
13
+ /** Write payload for `replace()`: full row; optional columns additionally accept `null`. */
14
+ type RowOf<T> = DbRow<DataOf<T>>;
7
15
  /** Options for creating a Client instance. */
8
16
  interface ClientOptions {
9
17
  /**
@@ -120,6 +128,12 @@ type ClientResponse<T, Q> = DbResponse<DataOf<T>, NavOf<T>, Q> & {
120
128
  //#region src/validator.d.ts
121
129
  /** Options for {@link ClientValidator} / {@link createClientValidator}. */
122
130
  interface ClientValidatorOptions {
131
+ /**
132
+ * The table's `@db.column.version` column (from `/meta`'s `versionColumn`).
133
+ * Enables the `$cas` lift in {@link ClientValidator.liftCas}; without it a
134
+ * `$cas` payload is rejected (the table is not versioned).
135
+ */
136
+ versionColumn?: string;
123
137
  /**
124
138
  * Tolerate unknown properties in write payloads. Enable when the served
125
139
  * `/meta` type is a PROJECTION of the full server-side type (e.g. an ARBAC
@@ -144,7 +158,21 @@ declare class ClientValidator {
144
158
  readonly flatMap: Map<string, TAtscriptAnnotatedType>;
145
159
  /** Set of field paths that are navigation relations (TO/FROM/VIA). */
146
160
  readonly navFields: ReadonlySet<string>;
161
+ /** The table's version column when it opts into OCC (from `/meta`). */
162
+ readonly versionColumn?: string;
147
163
  constructor(type: TAtscriptAnnotatedType<TAtscriptTypeObject>, opts?: ClientValidatorOptions);
164
+ /**
165
+ * Normalises the SDK `$cas` shape to the wire shape (since 0.1.128):
166
+ * `{ id, $cas: { version: 4 } }` → `{ id, version: 4 }` (the server lifts
167
+ * `version` back to `$cas`). Returns a shallow clone per item; items
168
+ * without `$cas` (a `$cas: undefined` counts as absent) are returned
169
+ * as-is. Throws `ClientValidationError` when the table is not versioned,
170
+ * the `$cas` shape is invalid, or `version` and `$cas` disagree — the same
171
+ * messages the server produces, from the shared `reconcileCas` — or when
172
+ * `$cas` appears on an insert.
173
+ */
174
+ liftCas(data: unknown, mode: ValidatorMode): unknown;
175
+ private _liftCasItem;
148
176
  /**
149
177
  * Validate data for a given write mode.
150
178
  * Throws `ClientValidationError` if validation fails.
@@ -178,4 +206,4 @@ declare class ClientValidationError extends Error {
178
206
  */
179
207
  declare function createClientValidator(meta: MetaResponse, opts?: ClientValidatorOptions): ClientValidator;
180
208
  //#endregion
181
- export { TDbDeleteResult$1 as C, TypedWithRelation as D, TDbUpdateResult$1 as E, Uniquery$1 as O, ServerError as S, TDbInsertResult$1 as T, NavOf as _, ValidatorMode$1 as a, RelationInfo as b, AggregateResult$1 as c, ClientResponse as d, DataOf as f, MetaResponse as g, IdOf as h, DbValidationContext as i, UniqueryControls$1 as k, AtscriptClientShape as l, FilterExpr as m, ClientValidator as n, createClientValidator as o, FieldMeta as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, ClientOptions as u, OwnOf as v, TDbInsertManyResult$1 as w, SearchIndexInfo as x, PageResult as y };
209
+ export { TDbUpdateResult$1 as A, RelationInfo as C, TDbDeleteResult$1 as D, ServerError as E, Uniquery$1 as M, UniqueryControls$1 as N, TDbInsertManyResult$1 as O, PatchOf as S, SearchIndexInfo as T, IdOf as _, ValidatorMode$1 as a, OwnOf as b, AggregateResult$1 as c, ClientResponse as d, DataOf as f, FilterExpr as g, FieldMeta as h, DbValidationContext as i, TypedWithRelation as j, TDbInsertResult$1 as k, AtscriptClientShape as l, DbRow as m, ClientValidator as n, createClientValidator as o, DbPatch as p, ClientValidatorOptions as r, AggregateQuery$1 as s, ClientValidationError as t, ClientOptions as u, MetaResponse as v, RowOf as w, PageResult as x, NavOf as y };
@@ -15,14 +15,51 @@ var ClientValidator = class {
15
15
  flatMap;
16
16
  /** Set of field paths that are navigation relations (TO/FROM/VIA). */
17
17
  navFields;
18
+ /** The table's version column when it opts into OCC (from `/meta`). */
19
+ versionColumn;
18
20
  constructor(type, opts) {
19
21
  this._type = type;
20
22
  this._lenientWrites = opts?.lenientWrites === true;
23
+ this.versionColumn = opts?.versionColumn;
21
24
  const ctx = (0, _atscript_db_validator.buildValidationContext)(type);
22
25
  this.flatMap = ctx.flatMap;
23
26
  this.navFields = ctx.navFields;
24
27
  }
25
28
  /**
29
+ * Normalises the SDK `$cas` shape to the wire shape (since 0.1.128):
30
+ * `{ id, $cas: { version: 4 } }` → `{ id, version: 4 }` (the server lifts
31
+ * `version` back to `$cas`). Returns a shallow clone per item; items
32
+ * without `$cas` (a `$cas: undefined` counts as absent) are returned
33
+ * as-is. Throws `ClientValidationError` when the table is not versioned,
34
+ * the `$cas` shape is invalid, or `version` and `$cas` disagree — the same
35
+ * messages the server produces, from the shared `reconcileCas` — or when
36
+ * `$cas` appears on an insert.
37
+ */
38
+ liftCas(data, mode) {
39
+ if (Array.isArray(data)) return data.map((item, i) => this._liftCasItem(item, mode, `[${i}].`));
40
+ return this._liftCasItem(data, mode, "");
41
+ }
42
+ _liftCasItem(item, mode, prefix) {
43
+ if (item === null || typeof item !== "object" || Array.isArray(item)) return item;
44
+ if (item.$cas === void 0) return item;
45
+ const path = `${prefix}$cas`;
46
+ if (mode === "insert") throw new ClientValidationError([{
47
+ path,
48
+ message: "$cas is not allowed on insert"
49
+ }]);
50
+ const clone = { ...item };
51
+ try {
52
+ (0, _atscript_db_validator.reconcileCas)(clone, this.versionColumn, "version");
53
+ } catch (e) {
54
+ if (e instanceof _atscript_db_validator.DbError) throw new ClientValidationError(e.errors.map((err) => ({
55
+ path: `${prefix}${err.path}`,
56
+ message: err.message
57
+ })));
58
+ throw e;
59
+ }
60
+ return clone;
61
+ }
62
+ /**
26
63
  * Validate data for a given write mode.
27
64
  * Throws `ClientValidationError` if validation fails.
28
65
  */
@@ -76,7 +113,10 @@ var ClientValidationError = class extends Error {
76
113
  * ```
77
114
  */
78
115
  function createClientValidator(meta, opts) {
79
- return new ClientValidator((0, _atscript_typescript_utils.deserializeAnnotatedType)(meta.type), opts);
116
+ return new ClientValidator((0, _atscript_typescript_utils.deserializeAnnotatedType)(meta.type), {
117
+ versionColumn: meta.versionColumn,
118
+ ...opts
119
+ });
80
120
  }
81
121
  //#endregion
82
122
  exports.ClientValidationError = ClientValidationError;
@@ -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-3I12BVi4.cjs";
1
+ import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-C3uaaZkU.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-DBEmk_s8.mjs";
1
+ import { a as ValidatorMode, i as DbValidationContext, n as ClientValidator, o as createClientValidator, r as ClientValidatorOptions, t as ClientValidationError } from "./validator-CfPFKDL_.mjs";
2
2
  export { ClientValidationError, ClientValidator, ClientValidatorOptions, type DbValidationContext, type ValidatorMode, createClientValidator };
@@ -1,5 +1,5 @@
1
1
  import { deserializeAnnotatedType } from "@atscript/typescript/utils";
2
- import { buildDbValidator, buildValidationContext } from "@atscript/db/validator";
2
+ import { DbError, buildDbValidator, buildValidationContext, reconcileCas } from "@atscript/db/validator";
3
3
  //#region src/validator.ts
4
4
  /**
5
5
  * Client-side validator backed by an Atscript type from the `/meta` endpoint.
@@ -14,14 +14,51 @@ var ClientValidator = class {
14
14
  flatMap;
15
15
  /** Set of field paths that are navigation relations (TO/FROM/VIA). */
16
16
  navFields;
17
+ /** The table's version column when it opts into OCC (from `/meta`). */
18
+ versionColumn;
17
19
  constructor(type, opts) {
18
20
  this._type = type;
19
21
  this._lenientWrites = opts?.lenientWrites === true;
22
+ this.versionColumn = opts?.versionColumn;
20
23
  const ctx = buildValidationContext(type);
21
24
  this.flatMap = ctx.flatMap;
22
25
  this.navFields = ctx.navFields;
23
26
  }
24
27
  /**
28
+ * Normalises the SDK `$cas` shape to the wire shape (since 0.1.128):
29
+ * `{ id, $cas: { version: 4 } }` → `{ id, version: 4 }` (the server lifts
30
+ * `version` back to `$cas`). Returns a shallow clone per item; items
31
+ * without `$cas` (a `$cas: undefined` counts as absent) are returned
32
+ * as-is. Throws `ClientValidationError` when the table is not versioned,
33
+ * the `$cas` shape is invalid, or `version` and `$cas` disagree — the same
34
+ * messages the server produces, from the shared `reconcileCas` — or when
35
+ * `$cas` appears on an insert.
36
+ */
37
+ liftCas(data, mode) {
38
+ if (Array.isArray(data)) return data.map((item, i) => this._liftCasItem(item, mode, `[${i}].`));
39
+ return this._liftCasItem(data, mode, "");
40
+ }
41
+ _liftCasItem(item, mode, prefix) {
42
+ if (item === null || typeof item !== "object" || Array.isArray(item)) return item;
43
+ if (item.$cas === void 0) return item;
44
+ const path = `${prefix}$cas`;
45
+ if (mode === "insert") throw new ClientValidationError([{
46
+ path,
47
+ message: "$cas is not allowed on insert"
48
+ }]);
49
+ const clone = { ...item };
50
+ try {
51
+ reconcileCas(clone, this.versionColumn, "version");
52
+ } catch (e) {
53
+ if (e instanceof DbError) throw new ClientValidationError(e.errors.map((err) => ({
54
+ path: `${prefix}${err.path}`,
55
+ message: err.message
56
+ })));
57
+ throw e;
58
+ }
59
+ return clone;
60
+ }
61
+ /**
25
62
  * Validate data for a given write mode.
26
63
  * Throws `ClientValidationError` if validation fails.
27
64
  */
@@ -75,7 +112,10 @@ var ClientValidationError = class extends Error {
75
112
  * ```
76
113
  */
77
114
  function createClientValidator(meta, opts) {
78
- return new ClientValidator(deserializeAnnotatedType(meta.type), opts);
115
+ return new ClientValidator(deserializeAnnotatedType(meta.type), {
116
+ versionColumn: meta.versionColumn,
117
+ ...opts
118
+ });
79
119
  }
80
120
  //#endregion
81
121
  export { ClientValidationError, ClientValidator, createClientValidator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-client",
3
- "version": "0.1.126",
3
+ "version": "0.1.128",
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/url": "^0.1.7"
46
+ "@uniqu/url": "^0.1.8"
47
47
  },
48
48
  "devDependencies": {
49
- "@atscript/core": "^0.1.88",
50
- "@atscript/typescript": "^0.1.88",
51
- "@uniqu/core": "^0.1.7",
52
- "unplugin-atscript": "^0.1.88",
53
- "@atscript/db": "0.1.126"
49
+ "@atscript/core": "^0.1.92",
50
+ "@atscript/typescript": "^0.1.92",
51
+ "@uniqu/core": "^0.1.8",
52
+ "unplugin-atscript": "^0.1.92",
53
+ "@atscript/db": "0.1.128"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@atscript/db": "^0.1.44",
57
- "@atscript/typescript": "^0.1.88"
57
+ "@atscript/typescript": "^0.1.92"
58
58
  },
59
59
  "scripts": {
60
60
  "postinstall": "asc -f dts",