@atscript/db-client 0.1.55 → 0.1.56
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 +111 -1
- package/dist/index.d.cts +50 -3
- package/dist/index.d.mts +50 -3
- package/dist/index.mjs +110 -2
- package/dist/{validator-DfU6oJir.d.cts → validator-DNm9kCoq.d.cts} +11 -0
- package/dist/{validator-wyKVvc-q.d.mts → validator-DfNMCEKa.d.mts} +11 -0
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -17,6 +17,28 @@ var ClientError = class extends Error {
|
|
|
17
17
|
return this.body.errors ?? [];
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
+
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
21
|
+
var ActionNotFoundError = class extends Error {
|
|
22
|
+
name = "ActionNotFoundError";
|
|
23
|
+
constructor(action) {
|
|
24
|
+
super(`Action "${action}" is not declared on this controller`);
|
|
25
|
+
this.action = action;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
30
|
+
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
31
|
+
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
32
|
+
* when no browser environment and no `navigate` option are configured.
|
|
33
|
+
*/
|
|
34
|
+
var ActionUnsupportedError = class extends Error {
|
|
35
|
+
name = "ActionUnsupportedError";
|
|
36
|
+
constructor(action, processor, message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.action = action;
|
|
39
|
+
this.processor = processor;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
20
42
|
//#endregion
|
|
21
43
|
//#region src/client.ts
|
|
22
44
|
/**
|
|
@@ -45,6 +67,7 @@ var Client = class {
|
|
|
45
67
|
_baseUrl;
|
|
46
68
|
_fetch;
|
|
47
69
|
_headers;
|
|
70
|
+
_navigate;
|
|
48
71
|
_metaPromise;
|
|
49
72
|
_validatorPromise;
|
|
50
73
|
constructor(path, opts) {
|
|
@@ -52,6 +75,7 @@ var Client = class {
|
|
|
52
75
|
this._baseUrl = opts?.baseUrl ?? "";
|
|
53
76
|
this._fetch = opts?.fetch ?? globalThis.fetch.bind(globalThis);
|
|
54
77
|
this._headers = opts?.headers;
|
|
78
|
+
this._navigate = opts?.navigate;
|
|
55
79
|
}
|
|
56
80
|
/**
|
|
57
81
|
* `GET /query` — query records with typed filter, sort, select, and relations.
|
|
@@ -138,6 +162,39 @@ var Client = class {
|
|
|
138
162
|
return this._metaPromise;
|
|
139
163
|
}
|
|
140
164
|
/**
|
|
165
|
+
* Invoke a declared action by name. Resolves the action descriptor from the
|
|
166
|
+
* cached `/meta` response, then dispatches based on `processor`:
|
|
167
|
+
*
|
|
168
|
+
* - `'backend'` → POST `pk` (or `pks`) as a JSON body to the action's path
|
|
169
|
+
* and return the parsed server response. The HTTP method is always POST.
|
|
170
|
+
* - `'navigate'` → for `level: 'row'`, substitute `$1` in `value` with the
|
|
171
|
+
* PK (URL-encoded; composite PKs are URL-encoded per field and joined
|
|
172
|
+
* with `/`); for `level: 'rows'` or `'table'`, navigate to `value`
|
|
173
|
+
* verbatim. The default navigator (browser only) calls
|
|
174
|
+
* `window.location.assign(url)`. Provide `ClientOptions.navigate` to
|
|
175
|
+
* integrate with a SPA router.
|
|
176
|
+
* - `'custom'` → throw {@link ActionUnsupportedError}; UI-dispatched events
|
|
177
|
+
* are the application's responsibility, not the client's.
|
|
178
|
+
*
|
|
179
|
+
* Throws {@link ActionNotFoundError} when the action is not present in `/meta`.
|
|
180
|
+
*
|
|
181
|
+
* For `level: 'rows'`, `pk` must be an array. If a non-array is supplied
|
|
182
|
+
* for a `'rows'` action it is wrapped into a single-element array — the
|
|
183
|
+
* server-side `@DbActionPKs()` resolver requires an array body.
|
|
184
|
+
*/
|
|
185
|
+
async action(name, pk) {
|
|
186
|
+
const action = (await this.meta()).actions.find((a) => a.name === name);
|
|
187
|
+
if (!action) throw new ActionNotFoundError(name);
|
|
188
|
+
if (action.processor === "custom") throw new ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
|
|
189
|
+
if (action.processor === "navigate") {
|
|
190
|
+
const url = this._interpolateNavigateUrl(action, pk);
|
|
191
|
+
await this._dispatchNavigate(action, url);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const body = this._buildActionBody(action, pk);
|
|
195
|
+
return this._postAction(action, body);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
141
198
|
* Returns a lazily-initialized {@link ClientValidator} backed by the `/meta` type.
|
|
142
199
|
* Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
|
|
143
200
|
*/
|
|
@@ -154,6 +211,34 @@ var Client = class {
|
|
|
154
211
|
});
|
|
155
212
|
return this._validatorPromise;
|
|
156
213
|
}
|
|
214
|
+
_buildActionBody(action, pk) {
|
|
215
|
+
if (action.level === "table") return void 0;
|
|
216
|
+
if (action.level !== "rows") return pk;
|
|
217
|
+
if (Array.isArray(pk)) return pk;
|
|
218
|
+
return pk === void 0 ? [] : [pk];
|
|
219
|
+
}
|
|
220
|
+
_interpolateNavigateUrl(action, pk) {
|
|
221
|
+
if (action.level !== "row") return action.value;
|
|
222
|
+
if (pk === void 0) return action.value;
|
|
223
|
+
return action.value.replace(/\$1/g, encodeNavigatePk(pk));
|
|
224
|
+
}
|
|
225
|
+
async _dispatchNavigate(action, url) {
|
|
226
|
+
if (this._navigate) {
|
|
227
|
+
await this._navigate(url);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const loc = globalThis.location;
|
|
231
|
+
if (loc?.assign) {
|
|
232
|
+
loc.assign(url);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
throw new ActionUnsupportedError(action.name, "navigate", `Action "${action.name}" is processor: 'navigate' but no browser is available and no \`navigate\` option was provided to Client.`);
|
|
236
|
+
}
|
|
237
|
+
async _postAction(action, body) {
|
|
238
|
+
const url = `${this._baseUrl}${action.value}`;
|
|
239
|
+
const init = await this._buildInit("POST", body);
|
|
240
|
+
return this._send(url, init, true);
|
|
241
|
+
}
|
|
157
242
|
_idToParams(id) {
|
|
158
243
|
const params = new URLSearchParams();
|
|
159
244
|
for (const [k, v] of Object.entries(id)) params.set(k, String(v));
|
|
@@ -179,6 +264,10 @@ var Client = class {
|
|
|
179
264
|
async _request(method, endpoint, body) {
|
|
180
265
|
const sep = endpoint && !endpoint.startsWith("?") ? "/" : "";
|
|
181
266
|
const url = `${this._baseUrl}${this._path}${sep}${endpoint}`;
|
|
267
|
+
const init = await this._buildInit(method, body);
|
|
268
|
+
return this._send(url, init, false);
|
|
269
|
+
}
|
|
270
|
+
async _buildInit(method, body) {
|
|
182
271
|
const headers = { ...await this._resolveHeaders() };
|
|
183
272
|
const init = {
|
|
184
273
|
method,
|
|
@@ -188,6 +277,9 @@ var Client = class {
|
|
|
188
277
|
headers["Content-Type"] = "application/json";
|
|
189
278
|
init.body = JSON.stringify(body);
|
|
190
279
|
}
|
|
280
|
+
return init;
|
|
281
|
+
}
|
|
282
|
+
async _send(url, init, allowEmpty) {
|
|
191
283
|
const res = await this._fetch(url, init);
|
|
192
284
|
if (!res.ok) {
|
|
193
285
|
let errorBody;
|
|
@@ -201,9 +293,27 @@ var Client = class {
|
|
|
201
293
|
}
|
|
202
294
|
throw new ClientError(res.status, errorBody);
|
|
203
295
|
}
|
|
204
|
-
return res.json();
|
|
296
|
+
if (!allowEmpty) return res.json();
|
|
297
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
|
|
298
|
+
try {
|
|
299
|
+
return await res.json();
|
|
300
|
+
} catch {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
205
303
|
}
|
|
206
304
|
};
|
|
305
|
+
/**
|
|
306
|
+
* Encode a row PK for substitution into a `processor: 'navigate'` URL template.
|
|
307
|
+
* Scalars are URL-encoded directly; composite PK objects have each value
|
|
308
|
+
* URL-encoded and joined with `/` in object-key order (which mirrors
|
|
309
|
+
* `primaryKeys` for the table).
|
|
310
|
+
*/
|
|
311
|
+
function encodeNavigatePk(pk) {
|
|
312
|
+
if (pk === null || pk === void 0) return "";
|
|
313
|
+
return (typeof pk === "object" ? Object.values(pk) : [pk]).map((v) => encodeURIComponent(String(v))).join("/");
|
|
314
|
+
}
|
|
207
315
|
//#endregion
|
|
316
|
+
exports.ActionNotFoundError = ActionNotFoundError;
|
|
317
|
+
exports.ActionUnsupportedError = ActionUnsupportedError;
|
|
208
318
|
exports.Client = Client;
|
|
209
319
|
exports.ClientError = ClientError;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as TDbUpdateResult, E as UniqueryControls, S as TDbInsertResult, T as Uniquery, _ as RelationInfo, b as TDbDeleteResult, c as ClientOptions, d as FilterExpr, f as IdOf, g as PageResult, h as OwnOf, i as ValidatorMode, l as DataOf, m as NavOf, n as ClientValidator, o as AggregateQuery, p as MetaResponse, s as AggregateResult, t as ClientValidationError, u as FieldMeta, v as SearchIndexInfo, w as TypedWithRelation, x as TDbInsertManyResult, y as ServerError } from "./validator-
|
|
1
|
+
import { C as TDbUpdateResult, E as UniqueryControls, S as TDbInsertResult, T as Uniquery, _ as RelationInfo, b as TDbDeleteResult, c as ClientOptions, d as FilterExpr, f as IdOf, g as PageResult, h as OwnOf, i as ValidatorMode, l as DataOf, m as NavOf, n as ClientValidator, o as AggregateQuery, p as MetaResponse, s as AggregateResult, t as ClientValidationError, u as FieldMeta, v as SearchIndexInfo, w as TypedWithRelation, x as TDbInsertManyResult, y as ServerError } from "./validator-DNm9kCoq.cjs";
|
|
2
2
|
import { AggregateQuery as AggregateQuery$1, AggregateResult as AggregateResult$1, Uniquery as Uniquery$1, UniqueryControls as UniqueryControls$1 } from "@uniqu/core";
|
|
3
|
-
import { TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
|
|
3
|
+
import { TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
|
|
4
4
|
import { TSerializedAnnotatedType } from "@atscript/typescript/utils";
|
|
5
5
|
|
|
6
6
|
//#region src/client.d.ts
|
|
@@ -34,6 +34,7 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
34
34
|
private readonly _baseUrl;
|
|
35
35
|
private readonly _fetch;
|
|
36
36
|
private readonly _headers?;
|
|
37
|
+
private readonly _navigate?;
|
|
37
38
|
private _metaPromise?;
|
|
38
39
|
private _validatorPromise?;
|
|
39
40
|
constructor(path: string, opts?: ClientOptions);
|
|
@@ -90,6 +91,28 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
90
91
|
* `GET /meta` — table/view metadata (cached after first call).
|
|
91
92
|
*/
|
|
92
93
|
meta(): Promise<MetaResponse>;
|
|
94
|
+
/**
|
|
95
|
+
* Invoke a declared action by name. Resolves the action descriptor from the
|
|
96
|
+
* cached `/meta` response, then dispatches based on `processor`:
|
|
97
|
+
*
|
|
98
|
+
* - `'backend'` → POST `pk` (or `pks`) as a JSON body to the action's path
|
|
99
|
+
* and return the parsed server response. The HTTP method is always POST.
|
|
100
|
+
* - `'navigate'` → for `level: 'row'`, substitute `$1` in `value` with the
|
|
101
|
+
* PK (URL-encoded; composite PKs are URL-encoded per field and joined
|
|
102
|
+
* with `/`); for `level: 'rows'` or `'table'`, navigate to `value`
|
|
103
|
+
* verbatim. The default navigator (browser only) calls
|
|
104
|
+
* `window.location.assign(url)`. Provide `ClientOptions.navigate` to
|
|
105
|
+
* integrate with a SPA router.
|
|
106
|
+
* - `'custom'` → throw {@link ActionUnsupportedError}; UI-dispatched events
|
|
107
|
+
* are the application's responsibility, not the client's.
|
|
108
|
+
*
|
|
109
|
+
* Throws {@link ActionNotFoundError} when the action is not present in `/meta`.
|
|
110
|
+
*
|
|
111
|
+
* For `level: 'rows'`, `pk` must be an array. If a non-array is supplied
|
|
112
|
+
* for a `'rows'` action it is wrapped into a single-element array — the
|
|
113
|
+
* server-side `@DbActionPKs()` resolver requires an array body.
|
|
114
|
+
*/
|
|
115
|
+
action(name: string, pk?: unknown): Promise<unknown>;
|
|
93
116
|
/**
|
|
94
117
|
* Returns a lazily-initialized {@link ClientValidator} backed by the `/meta` type.
|
|
95
118
|
* Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
|
|
@@ -97,11 +120,17 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
97
120
|
getValidator(): Promise<ClientValidator>;
|
|
98
121
|
private _validateData;
|
|
99
122
|
private _getValidator;
|
|
123
|
+
private _buildActionBody;
|
|
124
|
+
private _interpolateNavigateUrl;
|
|
125
|
+
private _dispatchNavigate;
|
|
126
|
+
private _postAction;
|
|
100
127
|
private _idToParams;
|
|
101
128
|
private _getOrNull;
|
|
102
129
|
private _get;
|
|
103
130
|
private _resolveHeaders;
|
|
104
131
|
private _request;
|
|
132
|
+
private _buildInit;
|
|
133
|
+
private _send;
|
|
105
134
|
}
|
|
106
135
|
//#endregion
|
|
107
136
|
//#region src/client-error.d.ts
|
|
@@ -127,5 +156,23 @@ declare class ClientError extends Error {
|
|
|
127
156
|
details?: unknown[];
|
|
128
157
|
}[];
|
|
129
158
|
}
|
|
159
|
+
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
160
|
+
declare class ActionNotFoundError extends Error {
|
|
161
|
+
readonly action: string;
|
|
162
|
+
name: string;
|
|
163
|
+
constructor(action: string);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
167
|
+
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
168
|
+
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
169
|
+
* when no browser environment and no `navigate` option are configured.
|
|
170
|
+
*/
|
|
171
|
+
declare class ActionUnsupportedError extends Error {
|
|
172
|
+
readonly action: string;
|
|
173
|
+
readonly processor: string;
|
|
174
|
+
name: string;
|
|
175
|
+
constructor(action: string, processor: string, message: string);
|
|
176
|
+
}
|
|
130
177
|
//#endregion
|
|
131
|
-
export { type AggregateQuery, type AggregateResult, Client, ClientError, type ClientOptions, type ClientValidationError, type ClientValidator, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode };
|
|
178
|
+
export { ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, Client, ClientError, type ClientOptions, type ClientValidationError, type ClientValidator, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, 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 };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { C as TDbUpdateResult, E as UniqueryControls, S as TDbInsertResult, T as Uniquery, _ as RelationInfo, b as TDbDeleteResult, c as ClientOptions, d as FilterExpr, f as IdOf, g as PageResult, h as OwnOf, i as ValidatorMode, l as DataOf, m as NavOf, n as ClientValidator, o as AggregateQuery, p as MetaResponse, s as AggregateResult, t as ClientValidationError, u as FieldMeta, v as SearchIndexInfo, w as TypedWithRelation, x as TDbInsertManyResult, y as ServerError } from "./validator-
|
|
1
|
+
import { C as TDbUpdateResult, E as UniqueryControls, S as TDbInsertResult, T as Uniquery, _ as RelationInfo, b as TDbDeleteResult, c as ClientOptions, d as FilterExpr, f as IdOf, g as PageResult, h as OwnOf, i as ValidatorMode, l as DataOf, m as NavOf, n as ClientValidator, o as AggregateQuery, p as MetaResponse, s as AggregateResult, t as ClientValidationError, u as FieldMeta, v as SearchIndexInfo, w as TypedWithRelation, x as TDbInsertManyResult, y as ServerError } from "./validator-DfNMCEKa.mjs";
|
|
2
2
|
import { 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
|
-
import { TDbDeleteResult as TDbDeleteResult$1, TDbInsertManyResult as TDbInsertManyResult$1, TDbInsertResult as TDbInsertResult$1, TDbUpdateResult as TDbUpdateResult$1 } from "@atscript/db";
|
|
4
|
+
import { TDbActionInfo, TDbActionIntent, TDbActionLevel, TDbActionProcessor, 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>;
|
|
@@ -34,6 +34,7 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
34
34
|
private readonly _baseUrl;
|
|
35
35
|
private readonly _fetch;
|
|
36
36
|
private readonly _headers?;
|
|
37
|
+
private readonly _navigate?;
|
|
37
38
|
private _metaPromise?;
|
|
38
39
|
private _validatorPromise?;
|
|
39
40
|
constructor(path: string, opts?: ClientOptions);
|
|
@@ -90,6 +91,28 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
90
91
|
* `GET /meta` — table/view metadata (cached after first call).
|
|
91
92
|
*/
|
|
92
93
|
meta(): Promise<MetaResponse>;
|
|
94
|
+
/**
|
|
95
|
+
* Invoke a declared action by name. Resolves the action descriptor from the
|
|
96
|
+
* cached `/meta` response, then dispatches based on `processor`:
|
|
97
|
+
*
|
|
98
|
+
* - `'backend'` → POST `pk` (or `pks`) as a JSON body to the action's path
|
|
99
|
+
* and return the parsed server response. The HTTP method is always POST.
|
|
100
|
+
* - `'navigate'` → for `level: 'row'`, substitute `$1` in `value` with the
|
|
101
|
+
* PK (URL-encoded; composite PKs are URL-encoded per field and joined
|
|
102
|
+
* with `/`); for `level: 'rows'` or `'table'`, navigate to `value`
|
|
103
|
+
* verbatim. The default navigator (browser only) calls
|
|
104
|
+
* `window.location.assign(url)`. Provide `ClientOptions.navigate` to
|
|
105
|
+
* integrate with a SPA router.
|
|
106
|
+
* - `'custom'` → throw {@link ActionUnsupportedError}; UI-dispatched events
|
|
107
|
+
* are the application's responsibility, not the client's.
|
|
108
|
+
*
|
|
109
|
+
* Throws {@link ActionNotFoundError} when the action is not present in `/meta`.
|
|
110
|
+
*
|
|
111
|
+
* For `level: 'rows'`, `pk` must be an array. If a non-array is supplied
|
|
112
|
+
* for a `'rows'` action it is wrapped into a single-element array — the
|
|
113
|
+
* server-side `@DbActionPKs()` resolver requires an array body.
|
|
114
|
+
*/
|
|
115
|
+
action(name: string, pk?: unknown): Promise<unknown>;
|
|
93
116
|
/**
|
|
94
117
|
* Returns a lazily-initialized {@link ClientValidator} backed by the `/meta` type.
|
|
95
118
|
* Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
|
|
@@ -97,11 +120,17 @@ declare class Client<T = Record<string, unknown>> {
|
|
|
97
120
|
getValidator(): Promise<ClientValidator>;
|
|
98
121
|
private _validateData;
|
|
99
122
|
private _getValidator;
|
|
123
|
+
private _buildActionBody;
|
|
124
|
+
private _interpolateNavigateUrl;
|
|
125
|
+
private _dispatchNavigate;
|
|
126
|
+
private _postAction;
|
|
100
127
|
private _idToParams;
|
|
101
128
|
private _getOrNull;
|
|
102
129
|
private _get;
|
|
103
130
|
private _resolveHeaders;
|
|
104
131
|
private _request;
|
|
132
|
+
private _buildInit;
|
|
133
|
+
private _send;
|
|
105
134
|
}
|
|
106
135
|
//#endregion
|
|
107
136
|
//#region src/client-error.d.ts
|
|
@@ -127,5 +156,23 @@ declare class ClientError extends Error {
|
|
|
127
156
|
details?: unknown[];
|
|
128
157
|
}[];
|
|
129
158
|
}
|
|
159
|
+
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
160
|
+
declare class ActionNotFoundError extends Error {
|
|
161
|
+
readonly action: string;
|
|
162
|
+
name: string;
|
|
163
|
+
constructor(action: string);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
167
|
+
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
168
|
+
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
169
|
+
* when no browser environment and no `navigate` option are configured.
|
|
170
|
+
*/
|
|
171
|
+
declare class ActionUnsupportedError extends Error {
|
|
172
|
+
readonly action: string;
|
|
173
|
+
readonly processor: string;
|
|
174
|
+
name: string;
|
|
175
|
+
constructor(action: string, processor: string, message: string);
|
|
176
|
+
}
|
|
130
177
|
//#endregion
|
|
131
|
-
export { type AggregateQuery, type AggregateResult, Client, ClientError, type ClientOptions, type ClientValidationError, type ClientValidator, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, type TDbDeleteResult, type TDbInsertManyResult, type TDbInsertResult, type TDbUpdateResult, type TSerializedAnnotatedType, type TypedWithRelation, type Uniquery, type UniqueryControls, type ValidatorMode };
|
|
178
|
+
export { ActionNotFoundError, ActionUnsupportedError, type AggregateQuery, type AggregateResult, Client, ClientError, type ClientOptions, type ClientValidationError, type ClientValidator, type DataOf, type FieldMeta, type FilterExpr, type IdOf, type MetaResponse, type NavOf, type OwnOf, type PageResult, type RelationInfo, type SearchIndexInfo, type ServerError, 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 };
|
package/dist/index.mjs
CHANGED
|
@@ -16,6 +16,28 @@ var ClientError = class extends Error {
|
|
|
16
16
|
return this.body.errors ?? [];
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
+
/** Thrown by `Client.action()` when the action name is not present in `/meta`. */
|
|
20
|
+
var ActionNotFoundError = class extends Error {
|
|
21
|
+
name = "ActionNotFoundError";
|
|
22
|
+
constructor(action) {
|
|
23
|
+
super(`Action "${action}" is not declared on this controller`);
|
|
24
|
+
this.action = action;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Thrown by `Client.action()` for actions that cannot be invoked through
|
|
29
|
+
* the client — currently `processor: 'custom'` (UI-dispatched events,
|
|
30
|
+
* which the application is responsible for handling) and `processor: 'navigate'`
|
|
31
|
+
* when no browser environment and no `navigate` option are configured.
|
|
32
|
+
*/
|
|
33
|
+
var ActionUnsupportedError = class extends Error {
|
|
34
|
+
name = "ActionUnsupportedError";
|
|
35
|
+
constructor(action, processor, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.action = action;
|
|
38
|
+
this.processor = processor;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
19
41
|
//#endregion
|
|
20
42
|
//#region src/client.ts
|
|
21
43
|
/**
|
|
@@ -44,6 +66,7 @@ var Client = class {
|
|
|
44
66
|
_baseUrl;
|
|
45
67
|
_fetch;
|
|
46
68
|
_headers;
|
|
69
|
+
_navigate;
|
|
47
70
|
_metaPromise;
|
|
48
71
|
_validatorPromise;
|
|
49
72
|
constructor(path, opts) {
|
|
@@ -51,6 +74,7 @@ var Client = class {
|
|
|
51
74
|
this._baseUrl = opts?.baseUrl ?? "";
|
|
52
75
|
this._fetch = opts?.fetch ?? globalThis.fetch.bind(globalThis);
|
|
53
76
|
this._headers = opts?.headers;
|
|
77
|
+
this._navigate = opts?.navigate;
|
|
54
78
|
}
|
|
55
79
|
/**
|
|
56
80
|
* `GET /query` — query records with typed filter, sort, select, and relations.
|
|
@@ -137,6 +161,39 @@ var Client = class {
|
|
|
137
161
|
return this._metaPromise;
|
|
138
162
|
}
|
|
139
163
|
/**
|
|
164
|
+
* Invoke a declared action by name. Resolves the action descriptor from the
|
|
165
|
+
* cached `/meta` response, then dispatches based on `processor`:
|
|
166
|
+
*
|
|
167
|
+
* - `'backend'` → POST `pk` (or `pks`) as a JSON body to the action's path
|
|
168
|
+
* and return the parsed server response. The HTTP method is always POST.
|
|
169
|
+
* - `'navigate'` → for `level: 'row'`, substitute `$1` in `value` with the
|
|
170
|
+
* PK (URL-encoded; composite PKs are URL-encoded per field and joined
|
|
171
|
+
* with `/`); for `level: 'rows'` or `'table'`, navigate to `value`
|
|
172
|
+
* verbatim. The default navigator (browser only) calls
|
|
173
|
+
* `window.location.assign(url)`. Provide `ClientOptions.navigate` to
|
|
174
|
+
* integrate with a SPA router.
|
|
175
|
+
* - `'custom'` → throw {@link ActionUnsupportedError}; UI-dispatched events
|
|
176
|
+
* are the application's responsibility, not the client's.
|
|
177
|
+
*
|
|
178
|
+
* Throws {@link ActionNotFoundError} when the action is not present in `/meta`.
|
|
179
|
+
*
|
|
180
|
+
* For `level: 'rows'`, `pk` must be an array. If a non-array is supplied
|
|
181
|
+
* for a `'rows'` action it is wrapped into a single-element array — the
|
|
182
|
+
* server-side `@DbActionPKs()` resolver requires an array body.
|
|
183
|
+
*/
|
|
184
|
+
async action(name, pk) {
|
|
185
|
+
const action = (await this.meta()).actions.find((a) => a.name === name);
|
|
186
|
+
if (!action) throw new ActionNotFoundError(name);
|
|
187
|
+
if (action.processor === "custom") throw new ActionUnsupportedError(name, "custom", `Action "${name}" has processor "custom" — applications must dispatch custom actions themselves; the client cannot.`);
|
|
188
|
+
if (action.processor === "navigate") {
|
|
189
|
+
const url = this._interpolateNavigateUrl(action, pk);
|
|
190
|
+
await this._dispatchNavigate(action, url);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const body = this._buildActionBody(action, pk);
|
|
194
|
+
return this._postAction(action, body);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
140
197
|
* Returns a lazily-initialized {@link ClientValidator} backed by the `/meta` type.
|
|
141
198
|
* Useful for accessing `flatMap` and `navFields` (e.g. for form generation).
|
|
142
199
|
*/
|
|
@@ -153,6 +210,34 @@ var Client = class {
|
|
|
153
210
|
});
|
|
154
211
|
return this._validatorPromise;
|
|
155
212
|
}
|
|
213
|
+
_buildActionBody(action, pk) {
|
|
214
|
+
if (action.level === "table") return void 0;
|
|
215
|
+
if (action.level !== "rows") return pk;
|
|
216
|
+
if (Array.isArray(pk)) return pk;
|
|
217
|
+
return pk === void 0 ? [] : [pk];
|
|
218
|
+
}
|
|
219
|
+
_interpolateNavigateUrl(action, pk) {
|
|
220
|
+
if (action.level !== "row") return action.value;
|
|
221
|
+
if (pk === void 0) return action.value;
|
|
222
|
+
return action.value.replace(/\$1/g, encodeNavigatePk(pk));
|
|
223
|
+
}
|
|
224
|
+
async _dispatchNavigate(action, url) {
|
|
225
|
+
if (this._navigate) {
|
|
226
|
+
await this._navigate(url);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const loc = globalThis.location;
|
|
230
|
+
if (loc?.assign) {
|
|
231
|
+
loc.assign(url);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
throw new ActionUnsupportedError(action.name, "navigate", `Action "${action.name}" is processor: 'navigate' but no browser is available and no \`navigate\` option was provided to Client.`);
|
|
235
|
+
}
|
|
236
|
+
async _postAction(action, body) {
|
|
237
|
+
const url = `${this._baseUrl}${action.value}`;
|
|
238
|
+
const init = await this._buildInit("POST", body);
|
|
239
|
+
return this._send(url, init, true);
|
|
240
|
+
}
|
|
156
241
|
_idToParams(id) {
|
|
157
242
|
const params = new URLSearchParams();
|
|
158
243
|
for (const [k, v] of Object.entries(id)) params.set(k, String(v));
|
|
@@ -178,6 +263,10 @@ var Client = class {
|
|
|
178
263
|
async _request(method, endpoint, body) {
|
|
179
264
|
const sep = endpoint && !endpoint.startsWith("?") ? "/" : "";
|
|
180
265
|
const url = `${this._baseUrl}${this._path}${sep}${endpoint}`;
|
|
266
|
+
const init = await this._buildInit(method, body);
|
|
267
|
+
return this._send(url, init, false);
|
|
268
|
+
}
|
|
269
|
+
async _buildInit(method, body) {
|
|
181
270
|
const headers = { ...await this._resolveHeaders() };
|
|
182
271
|
const init = {
|
|
183
272
|
method,
|
|
@@ -187,6 +276,9 @@ var Client = class {
|
|
|
187
276
|
headers["Content-Type"] = "application/json";
|
|
188
277
|
init.body = JSON.stringify(body);
|
|
189
278
|
}
|
|
279
|
+
return init;
|
|
280
|
+
}
|
|
281
|
+
async _send(url, init, allowEmpty) {
|
|
190
282
|
const res = await this._fetch(url, init);
|
|
191
283
|
if (!res.ok) {
|
|
192
284
|
let errorBody;
|
|
@@ -200,8 +292,24 @@ var Client = class {
|
|
|
200
292
|
}
|
|
201
293
|
throw new ClientError(res.status, errorBody);
|
|
202
294
|
}
|
|
203
|
-
return res.json();
|
|
295
|
+
if (!allowEmpty) return res.json();
|
|
296
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
|
|
297
|
+
try {
|
|
298
|
+
return await res.json();
|
|
299
|
+
} catch {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
204
302
|
}
|
|
205
303
|
};
|
|
304
|
+
/**
|
|
305
|
+
* Encode a row PK for substitution into a `processor: 'navigate'` URL template.
|
|
306
|
+
* Scalars are URL-encoded directly; composite PK objects have each value
|
|
307
|
+
* URL-encoded and joined with `/` in object-key order (which mirrors
|
|
308
|
+
* `primaryKeys` for the table).
|
|
309
|
+
*/
|
|
310
|
+
function encodeNavigatePk(pk) {
|
|
311
|
+
if (pk === null || pk === void 0) return "";
|
|
312
|
+
return (typeof pk === "object" ? Object.values(pk) : [pk]).map((v) => encodeURIComponent(String(v))).join("/");
|
|
313
|
+
}
|
|
206
314
|
//#endregion
|
|
207
|
-
export { Client, ClientError };
|
|
315
|
+
export { ActionNotFoundError, ActionUnsupportedError, Client, ClientError };
|
|
@@ -21,6 +21,17 @@ interface ClientOptions {
|
|
|
21
21
|
* @example "https://api.example.com"
|
|
22
22
|
*/
|
|
23
23
|
baseUrl?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Override for `processor: 'navigate'` action dispatch. When `Client.action()`
|
|
26
|
+
* resolves a navigate action, this hook is invoked with the interpolated
|
|
27
|
+
* URL. Default behaviour (browser only) calls `window.location.assign(url)`.
|
|
28
|
+
*
|
|
29
|
+
* Provide a custom navigator to integrate with a SPA router:
|
|
30
|
+
* ```typescript
|
|
31
|
+
* new Client('/api/users', { navigate: (url) => router.push(url) })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
navigate?: (url: string) => void | Promise<void>;
|
|
24
35
|
}
|
|
25
36
|
/** Search index metadata from the server. */
|
|
26
37
|
type SearchIndexInfo = TSearchIndexInfo;
|
|
@@ -21,6 +21,17 @@ interface ClientOptions {
|
|
|
21
21
|
* @example "https://api.example.com"
|
|
22
22
|
*/
|
|
23
23
|
baseUrl?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Override for `processor: 'navigate'` action dispatch. When `Client.action()`
|
|
26
|
+
* resolves a navigate action, this hook is invoked with the interpolated
|
|
27
|
+
* URL. Default behaviour (browser only) calls `window.location.assign(url)`.
|
|
28
|
+
*
|
|
29
|
+
* Provide a custom navigator to integrate with a SPA router:
|
|
30
|
+
* ```typescript
|
|
31
|
+
* new Client('/api/users', { navigate: (url) => router.push(url) })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
navigate?: (url: string) => void | Promise<void>;
|
|
24
35
|
}
|
|
25
36
|
/** Search index metadata from the server. */
|
|
26
37
|
type SearchIndexInfo = TSearchIndexInfo;
|
package/dist/validator.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createClientValidator, i as ValidatorMode, n as ClientValidator, r as DbValidationContext, t as ClientValidationError } from "./validator-
|
|
1
|
+
import { a as createClientValidator, i as ValidatorMode, n as ClientValidator, r as DbValidationContext, t as ClientValidationError } from "./validator-DNm9kCoq.cjs";
|
|
2
2
|
export { ClientValidationError, ClientValidator, DbValidationContext, ValidatorMode, createClientValidator };
|
package/dist/validator.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createClientValidator, i as ValidatorMode, n as ClientValidator, r as DbValidationContext, t as ClientValidationError } from "./validator-
|
|
1
|
+
import { a as createClientValidator, i as ValidatorMode, n as ClientValidator, r as DbValidationContext, t as ClientValidationError } from "./validator-DfNMCEKa.mjs";
|
|
2
2
|
export { ClientValidationError, ClientValidator, DbValidationContext, ValidatorMode, 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.56",
|
|
4
4
|
"description": "Browser-compatible HTTP client for @atscript/moost-db REST endpoints.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@atscript/typescript": "^0.1.50",
|
|
51
51
|
"@uniqu/core": "^0.1.5",
|
|
52
52
|
"unplugin-atscript": "^0.1.50",
|
|
53
|
-
"@atscript/db": "0.1.
|
|
53
|
+
"@atscript/db": "0.1.56"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@atscript/db": "^0.1.44",
|