@atscript/db-client 0.1.127 → 0.1.129
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 +54 -12
- package/dist/index.d.cts +40 -10
- package/dist/index.d.mts +40 -10
- package/dist/index.mjs +54 -13
- package/dist/{validator-3I12BVi4.d.cts → validator-C3uaaZkU.d.cts} +30 -2
- package/dist/{validator-DBEmk_s8.d.mts → validator-CfPFKDL_.d.mts} +30 -2
- package/dist/validator.cjs +41 -1
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.mjs +42 -2
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,24 @@ var VersionMismatchError = class extends ClientError {
|
|
|
56
56
|
return this.body.currentVersion;
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
61
|
+
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
62
|
+
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
63
|
+
* server answered and rejected — the request's outcome is unknown: a write
|
|
64
|
+
* may have committed. Reload before retrying a non-idempotent write.
|
|
65
|
+
* The underlying error is the standard `cause`.
|
|
66
|
+
*/
|
|
67
|
+
var TransportError = class extends Error {
|
|
68
|
+
method;
|
|
69
|
+
url;
|
|
70
|
+
name = "TransportError";
|
|
71
|
+
constructor(method, url, detail, cause) {
|
|
72
|
+
super(`${method} ${url}: ${detail}`, { cause });
|
|
73
|
+
this.method = method;
|
|
74
|
+
this.url = url;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
59
77
|
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
60
78
|
var ActionNotFoundError = class extends Error {
|
|
61
79
|
action;
|
|
@@ -212,22 +230,26 @@ var Client = class {
|
|
|
212
230
|
return this._getOrNull(`one/${encodeURIComponent(String(id))}${controlStr ? `?${controlStr}` : ""}`);
|
|
213
231
|
}
|
|
214
232
|
async insert(data) {
|
|
215
|
-
await this.
|
|
216
|
-
return this._request("POST", "", data);
|
|
233
|
+
return this._request("POST", "", await this._prepareWrite(data, "insert"));
|
|
217
234
|
}
|
|
218
235
|
/**
|
|
219
236
|
* `PATCH /` — partial update one or many records by primary key.
|
|
237
|
+
*
|
|
238
|
+
* Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
|
|
239
|
+
* contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
|
|
240
|
+
* before preflight and sending. A stale version surfaces as
|
|
241
|
+
* {@link VersionMismatchError}; a PK-only body with a version is a real
|
|
242
|
+
* write (the server bumps the version).
|
|
220
243
|
*/
|
|
221
244
|
async update(data) {
|
|
222
|
-
await this.
|
|
223
|
-
return this._request("PATCH", "", data);
|
|
245
|
+
return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
|
|
224
246
|
}
|
|
225
247
|
/**
|
|
226
|
-
* `PUT /` — full replace one or many records by primary key.
|
|
248
|
+
* `PUT /` — full replace one or many records by primary key. Same OCC
|
|
249
|
+
* shapes as {@link update}; the version column itself is optional.
|
|
227
250
|
*/
|
|
228
251
|
async replace(data) {
|
|
229
|
-
await this.
|
|
230
|
-
return this._request("PUT", "", data);
|
|
252
|
+
return this._request("PUT", "", await this._prepareWrite(data, "replace"));
|
|
231
253
|
}
|
|
232
254
|
/**
|
|
233
255
|
* `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
|
|
@@ -329,8 +351,12 @@ var Client = class {
|
|
|
329
351
|
getValidator() {
|
|
330
352
|
return this._getValidator();
|
|
331
353
|
}
|
|
332
|
-
|
|
333
|
-
|
|
354
|
+
/** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
|
|
355
|
+
async _prepareWrite(data, mode) {
|
|
356
|
+
const validator = await this._getValidator();
|
|
357
|
+
const normalized = validator.liftCas(data, mode);
|
|
358
|
+
validator.validate(normalized, mode);
|
|
359
|
+
return normalized;
|
|
334
360
|
}
|
|
335
361
|
_getValidator() {
|
|
336
362
|
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) => {
|
|
@@ -422,14 +448,20 @@ var Client = class {
|
|
|
422
448
|
return init;
|
|
423
449
|
}
|
|
424
450
|
async _send(url, init, allowEmpty) {
|
|
425
|
-
const
|
|
451
|
+
const method = init.method ?? "GET";
|
|
452
|
+
let res;
|
|
453
|
+
try {
|
|
454
|
+
res = await this._fetch(url, init);
|
|
455
|
+
} catch (cause) {
|
|
456
|
+
throw new TransportError(method, url, describeCause(cause), cause);
|
|
457
|
+
}
|
|
426
458
|
if (!res.ok) {
|
|
427
459
|
let errorBody;
|
|
428
460
|
try {
|
|
429
461
|
errorBody = await res.json();
|
|
430
462
|
} catch {
|
|
431
463
|
errorBody = {
|
|
432
|
-
message: res.statusText
|
|
464
|
+
message: res.statusText || `HTTP ${res.status}`,
|
|
433
465
|
statusCode: res.status
|
|
434
466
|
};
|
|
435
467
|
}
|
|
@@ -437,7 +469,11 @@ var Client = class {
|
|
|
437
469
|
if (errorBody.kind === "version_mismatch") throw new VersionMismatchError(res.status, errorBody);
|
|
438
470
|
throw new ClientError(res.status, errorBody);
|
|
439
471
|
}
|
|
440
|
-
if (!allowEmpty)
|
|
472
|
+
if (!allowEmpty) try {
|
|
473
|
+
return await res.json();
|
|
474
|
+
} catch (cause) {
|
|
475
|
+
throw new TransportError(method, url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
|
|
476
|
+
}
|
|
441
477
|
if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
|
|
442
478
|
try {
|
|
443
479
|
return await res.json();
|
|
@@ -446,6 +482,11 @@ var Client = class {
|
|
|
446
482
|
}
|
|
447
483
|
}
|
|
448
484
|
};
|
|
485
|
+
/** One-line rendering of a thrown value for a `TransportError` message. */
|
|
486
|
+
function describeCause(cause) {
|
|
487
|
+
if (cause instanceof Error) return cause.message || cause.name;
|
|
488
|
+
return String(cause);
|
|
489
|
+
}
|
|
449
490
|
/**
|
|
450
491
|
* Render a single identifier field for substitution into a navigate-URL
|
|
451
492
|
* template or human-readable string. `null` / `undefined` collapse to `""`
|
|
@@ -487,6 +528,7 @@ exports.ActionNotFoundError = ActionNotFoundError;
|
|
|
487
528
|
exports.ActionUnsupportedError = ActionUnsupportedError;
|
|
488
529
|
exports.Client = Client;
|
|
489
530
|
exports.ClientError = ClientError;
|
|
531
|
+
exports.TransportError = TransportError;
|
|
490
532
|
exports.VersionMismatchError = VersionMismatchError;
|
|
491
533
|
exports.encodeNavigateId = encodeNavigateId;
|
|
492
534
|
exports.formatIdentifier = formatIdentifier;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
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:
|
|
104
|
+
insert(data: PatchOf<T>): Promise<TDbInsertResult$1>;
|
|
105
105
|
/**
|
|
106
106
|
* `POST /` — insert many records.
|
|
107
107
|
*/
|
|
108
|
-
insert(data:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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;
|
|
@@ -287,6 +295,28 @@ declare class VersionMismatchError extends ClientError {
|
|
|
287
295
|
/** Row's current server-side version — clients refresh + retry against it. */
|
|
288
296
|
get currentVersion(): number;
|
|
289
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
300
|
+
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
301
|
+
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
302
|
+
* server answered and rejected — the request's outcome is unknown: a write
|
|
303
|
+
* may have committed. Reload before retrying a non-idempotent write.
|
|
304
|
+
* The underlying error is the standard `cause`.
|
|
305
|
+
*/
|
|
306
|
+
declare class TransportError extends Error {
|
|
307
|
+
/** HTTP method of the failed request. */
|
|
308
|
+
readonly method: string;
|
|
309
|
+
/** Full request URL. */
|
|
310
|
+
readonly url: string;
|
|
311
|
+
name: string;
|
|
312
|
+
constructor(/** HTTP method of the failed request. */
|
|
313
|
+
|
|
314
|
+
method: string, /** Full request URL. */
|
|
315
|
+
|
|
316
|
+
url: string, /** What went wrong; the message is `<method> <url>: <detail>`. */
|
|
317
|
+
|
|
318
|
+
detail: string, cause?: unknown);
|
|
319
|
+
}
|
|
290
320
|
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
291
321
|
declare class ActionNotFoundError extends Error {
|
|
292
322
|
readonly action: string;
|
|
@@ -306,4 +336,4 @@ declare class ActionUnsupportedError extends Error {
|
|
|
306
336
|
constructor(action: string, processor: string, message: string);
|
|
307
337
|
}
|
|
308
338
|
//#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 };
|
|
339
|
+
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, TransportError, 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
|
|
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:
|
|
104
|
+
insert(data: PatchOf<T>): Promise<TDbInsertResult$1>;
|
|
105
105
|
/**
|
|
106
106
|
* `POST /` — insert many records.
|
|
107
107
|
*/
|
|
108
|
-
insert(data:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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;
|
|
@@ -287,6 +295,28 @@ declare class VersionMismatchError extends ClientError {
|
|
|
287
295
|
/** Row's current server-side version — clients refresh + retry against it. */
|
|
288
296
|
get currentVersion(): number;
|
|
289
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
300
|
+
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
301
|
+
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
302
|
+
* server answered and rejected — the request's outcome is unknown: a write
|
|
303
|
+
* may have committed. Reload before retrying a non-idempotent write.
|
|
304
|
+
* The underlying error is the standard `cause`.
|
|
305
|
+
*/
|
|
306
|
+
declare class TransportError extends Error {
|
|
307
|
+
/** HTTP method of the failed request. */
|
|
308
|
+
readonly method: string;
|
|
309
|
+
/** Full request URL. */
|
|
310
|
+
readonly url: string;
|
|
311
|
+
name: string;
|
|
312
|
+
constructor(/** HTTP method of the failed request. */
|
|
313
|
+
|
|
314
|
+
method: string, /** Full request URL. */
|
|
315
|
+
|
|
316
|
+
url: string, /** What went wrong; the message is `<method> <url>: <detail>`. */
|
|
317
|
+
|
|
318
|
+
detail: string, cause?: unknown);
|
|
319
|
+
}
|
|
290
320
|
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
291
321
|
declare class ActionNotFoundError extends Error {
|
|
292
322
|
readonly action: string;
|
|
@@ -306,4 +336,4 @@ declare class ActionUnsupportedError extends Error {
|
|
|
306
336
|
constructor(action: string, processor: string, message: string);
|
|
307
337
|
}
|
|
308
338
|
//#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 };
|
|
339
|
+
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, TransportError, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode, VersionMismatchError, type VersionMismatchErrorBody, encodeNavigateId, formatIdentifier, formatIdentifierField };
|
package/dist/index.mjs
CHANGED
|
@@ -55,6 +55,24 @@ var VersionMismatchError = class extends ClientError {
|
|
|
55
55
|
return this.body.currentVersion;
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Thrown by `Client` when NO server verdict was obtained: `fetch` itself
|
|
60
|
+
* rejected (network down, DNS, CORS, `AbortError`, …) or a 2xx response
|
|
61
|
+
* carried a body that is not JSON. Unlike {@link ClientError} — where the
|
|
62
|
+
* server answered and rejected — the request's outcome is unknown: a write
|
|
63
|
+
* may have committed. Reload before retrying a non-idempotent write.
|
|
64
|
+
* The underlying error is the standard `cause`.
|
|
65
|
+
*/
|
|
66
|
+
var TransportError = class extends Error {
|
|
67
|
+
method;
|
|
68
|
+
url;
|
|
69
|
+
name = "TransportError";
|
|
70
|
+
constructor(method, url, detail, cause) {
|
|
71
|
+
super(`${method} ${url}: ${detail}`, { cause });
|
|
72
|
+
this.method = method;
|
|
73
|
+
this.url = url;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
58
76
|
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
59
77
|
var ActionNotFoundError = class extends Error {
|
|
60
78
|
action;
|
|
@@ -211,22 +229,26 @@ var Client = class {
|
|
|
211
229
|
return this._getOrNull(`one/${encodeURIComponent(String(id))}${controlStr ? `?${controlStr}` : ""}`);
|
|
212
230
|
}
|
|
213
231
|
async insert(data) {
|
|
214
|
-
await this.
|
|
215
|
-
return this._request("POST", "", data);
|
|
232
|
+
return this._request("POST", "", await this._prepareWrite(data, "insert"));
|
|
216
233
|
}
|
|
217
234
|
/**
|
|
218
235
|
* `PATCH /` — partial update one or many records by primary key.
|
|
236
|
+
*
|
|
237
|
+
* Accepts both OCC shapes (since 0.1.128): a bare `version: N` (the wire
|
|
238
|
+
* contract) or the SDK `$cas: { version: N }`, which is lifted to `version`
|
|
239
|
+
* before preflight and sending. A stale version surfaces as
|
|
240
|
+
* {@link VersionMismatchError}; a PK-only body with a version is a real
|
|
241
|
+
* write (the server bumps the version).
|
|
219
242
|
*/
|
|
220
243
|
async update(data) {
|
|
221
|
-
await this.
|
|
222
|
-
return this._request("PATCH", "", data);
|
|
244
|
+
return this._request("PATCH", "", await this._prepareWrite(data, "patch"));
|
|
223
245
|
}
|
|
224
246
|
/**
|
|
225
|
-
* `PUT /` — full replace one or many records by primary key.
|
|
247
|
+
* `PUT /` — full replace one or many records by primary key. Same OCC
|
|
248
|
+
* shapes as {@link update}; the version column itself is optional.
|
|
226
249
|
*/
|
|
227
250
|
async replace(data) {
|
|
228
|
-
await this.
|
|
229
|
-
return this._request("PUT", "", data);
|
|
251
|
+
return this._request("PUT", "", await this._prepareWrite(data, "replace"));
|
|
230
252
|
}
|
|
231
253
|
/**
|
|
232
254
|
* `DELETE /:id` or `DELETE /?k1=v1&k2=v2` — remove a record by primary key.
|
|
@@ -328,8 +350,12 @@ var Client = class {
|
|
|
328
350
|
getValidator() {
|
|
329
351
|
return this._getValidator();
|
|
330
352
|
}
|
|
331
|
-
|
|
332
|
-
|
|
353
|
+
/** Lifts `$cas` to the wire shape, then runs the preflight validator on the result. */
|
|
354
|
+
async _prepareWrite(data, mode) {
|
|
355
|
+
const validator = await this._getValidator();
|
|
356
|
+
const normalized = validator.liftCas(data, mode);
|
|
357
|
+
validator.validate(normalized, mode);
|
|
358
|
+
return normalized;
|
|
333
359
|
}
|
|
334
360
|
_getValidator() {
|
|
335
361
|
if (!this._validatorPromise) this._validatorPromise = Promise.all([this.meta(), import("./validator.mjs")]).then(([m, { createClientValidator }]) => createClientValidator(m, { lenientWrites: this._lenientWrites })).catch((err) => {
|
|
@@ -421,14 +447,20 @@ var Client = class {
|
|
|
421
447
|
return init;
|
|
422
448
|
}
|
|
423
449
|
async _send(url, init, allowEmpty) {
|
|
424
|
-
const
|
|
450
|
+
const method = init.method ?? "GET";
|
|
451
|
+
let res;
|
|
452
|
+
try {
|
|
453
|
+
res = await this._fetch(url, init);
|
|
454
|
+
} catch (cause) {
|
|
455
|
+
throw new TransportError(method, url, describeCause(cause), cause);
|
|
456
|
+
}
|
|
425
457
|
if (!res.ok) {
|
|
426
458
|
let errorBody;
|
|
427
459
|
try {
|
|
428
460
|
errorBody = await res.json();
|
|
429
461
|
} catch {
|
|
430
462
|
errorBody = {
|
|
431
|
-
message: res.statusText
|
|
463
|
+
message: res.statusText || `HTTP ${res.status}`,
|
|
432
464
|
statusCode: res.status
|
|
433
465
|
};
|
|
434
466
|
}
|
|
@@ -436,7 +468,11 @@ var Client = class {
|
|
|
436
468
|
if (errorBody.kind === "version_mismatch") throw new VersionMismatchError(res.status, errorBody);
|
|
437
469
|
throw new ClientError(res.status, errorBody);
|
|
438
470
|
}
|
|
439
|
-
if (!allowEmpty)
|
|
471
|
+
if (!allowEmpty) try {
|
|
472
|
+
return await res.json();
|
|
473
|
+
} catch (cause) {
|
|
474
|
+
throw new TransportError(method, url, `HTTP ${res.status} body is not JSON (${describeCause(cause)})`, cause);
|
|
475
|
+
}
|
|
440
476
|
if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
|
|
441
477
|
try {
|
|
442
478
|
return await res.json();
|
|
@@ -445,6 +481,11 @@ var Client = class {
|
|
|
445
481
|
}
|
|
446
482
|
}
|
|
447
483
|
};
|
|
484
|
+
/** One-line rendering of a thrown value for a `TransportError` message. */
|
|
485
|
+
function describeCause(cause) {
|
|
486
|
+
if (cause instanceof Error) return cause.message || cause.name;
|
|
487
|
+
return String(cause);
|
|
488
|
+
}
|
|
448
489
|
/**
|
|
449
490
|
* Render a single identifier field for substitution into a navigate-URL
|
|
450
491
|
* template or human-readable string. `null` / `undefined` collapse to `""`
|
|
@@ -481,4 +522,4 @@ function describeShape(value) {
|
|
|
481
522
|
return typeof value;
|
|
482
523
|
}
|
|
483
524
|
//#endregion
|
|
484
|
-
export { ActionDisabledError, ActionNotFoundError, ActionUnsupportedError, Client, ClientError, VersionMismatchError, encodeNavigateId, formatIdentifier, formatIdentifierField };
|
|
525
|
+
export { ActionDisabledError, ActionNotFoundError, ActionUnsupportedError, Client, ClientError, TransportError, VersionMismatchError, encodeNavigateId, formatIdentifier, formatIdentifierField };
|
|
@@ -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 {
|
|
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 {
|
|
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 };
|
package/dist/validator.cjs
CHANGED
|
@@ -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),
|
|
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;
|
package/dist/validator.d.cts
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/validator.d.mts
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/validator.mjs
CHANGED
|
@@ -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),
|
|
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.
|
|
3
|
+
"version": "0.1.129",
|
|
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.
|
|
46
|
+
"@uniqu/url": "^0.1.8"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@atscript/core": "^0.1.
|
|
50
|
-
"@atscript/typescript": "^0.1.
|
|
51
|
-
"@uniqu/core": "^0.1.
|
|
52
|
-
"unplugin-atscript": "^0.1.
|
|
53
|
-
"@atscript/db": "0.1.
|
|
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.129"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@atscript/db": "^0.1.44",
|
|
57
|
-
"@atscript/typescript": "^0.1.
|
|
57
|
+
"@atscript/typescript": "^0.1.92"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"postinstall": "asc -f dts",
|