@atscript/moost-db 0.1.53 → 0.1.55
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 +639 -124
- package/dist/index.d.cts +237 -24
- package/dist/index.d.mts +237 -24
- package/dist/index.mjs +623 -126
- package/package.json +10 -10
package/dist/index.d.cts
CHANGED
|
@@ -6,29 +6,66 @@ import { HttpError } from "@moostjs/event-http";
|
|
|
6
6
|
import * as moost from "moost";
|
|
7
7
|
import { Moost, TConsoleBase } from "moost";
|
|
8
8
|
|
|
9
|
-
//#region src/as-
|
|
9
|
+
//#region src/as-readable.controller.d.ts
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Optional gate configuration for a single request. Each present entry enables
|
|
12
|
+
* the corresponding check; omitted entries skip that gate entirely.
|
|
13
|
+
*/
|
|
14
|
+
interface ReadableGates {
|
|
15
|
+
filter?: {
|
|
16
|
+
predicate: (field: string) => boolean;
|
|
17
|
+
annotation: string;
|
|
18
|
+
};
|
|
19
|
+
sort?: {
|
|
20
|
+
predicate: (field: string) => boolean;
|
|
21
|
+
annotation: string;
|
|
22
|
+
};
|
|
23
|
+
search?: {
|
|
24
|
+
allowed: boolean;
|
|
25
|
+
rejectionMessage: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Abstract base class for read-only HTTP controllers over an Atscript interface.
|
|
13
30
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
31
|
+
* Shared responsibilities (implemented here):
|
|
32
|
+
* - Stamps `@db.http.path` on the bound interface's metadata at registration
|
|
33
|
+
* with the final public path (leading slash + Moost `globalPrefix`).
|
|
34
|
+
* - Lazily serializes the bound interface for the `/meta` endpoint
|
|
35
|
+
* (see {@link getSerializeOptions}).
|
|
36
|
+
* - Provides DTO-backed validators for the Uniquery controls DTOs and the
|
|
37
|
+
* helpers (`parseQueryString`, `returnOne`, `validateParsed`, etc.) that
|
|
38
|
+
* subclasses share.
|
|
39
|
+
* - Registers the `/meta` route. Subclasses override {@link buildMetaResponse}
|
|
40
|
+
* to shape the payload; DB-backed readables add relations/searchable flags,
|
|
41
|
+
* value-help controllers add their capability hints.
|
|
42
|
+
*
|
|
43
|
+
* Subclass responsibilities:
|
|
44
|
+
* - Pass the bound interface + logical name + (optional) kind tag through super().
|
|
45
|
+
* - Implement {@link hasField} so insights validation can reject unknown keys.
|
|
46
|
+
* - Register the `/query`, `/pages`, `/one(/:id)` routes with the concrete
|
|
47
|
+
* handlers that match the data source's contract (DB readables route into
|
|
48
|
+
* aggregate/vector/search; value-help controllers just filter/sort/paginate).
|
|
16
49
|
*/
|
|
17
|
-
declare class
|
|
18
|
-
/**
|
|
19
|
-
protected
|
|
50
|
+
declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
|
|
51
|
+
/** The Atscript interface this controller serves. */
|
|
52
|
+
protected readonly boundType: T;
|
|
53
|
+
/** Short human-readable name for logging (usually the table/source name). */
|
|
54
|
+
protected readonly controllerName: string;
|
|
20
55
|
/** Application-scoped logger. */
|
|
21
56
|
protected logger: TConsoleBase;
|
|
22
|
-
/** Cached serialized type definition (lazy, computed on first access). */
|
|
23
|
-
private _serializedType?;
|
|
24
57
|
/** Moost application instance. */
|
|
25
58
|
protected app: Moost;
|
|
59
|
+
/** Cached serialized type definition (lazy, computed on first access). */
|
|
60
|
+
private _serializedType?;
|
|
26
61
|
/** Cached full meta response (computed lazily on first meta() call). */
|
|
27
62
|
private _metaResponse?;
|
|
28
|
-
constructor(
|
|
63
|
+
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
64
|
+
/** Subclass contract: return `true` if `path` addresses a valid field on the bound source. */
|
|
65
|
+
protected abstract hasField(path: string): boolean;
|
|
29
66
|
/** Sets @db.http.path on the type metadata from the controller's computed prefix. */
|
|
30
67
|
private _resolveHttpPath;
|
|
31
|
-
/** Lazily serializes the type (after all controllers have set
|
|
68
|
+
/** Lazily serializes the bound type (after all controllers have set @db.http.path). */
|
|
32
69
|
protected getSerializedType(): _atscript_typescript_utils0.TSerializedAnnotatedType;
|
|
33
70
|
/**
|
|
34
71
|
* One-time initialization hook. Override to seed data, register watchers, etc.
|
|
@@ -36,14 +73,24 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
36
73
|
protected init(): void | Promise<void>;
|
|
37
74
|
/**
|
|
38
75
|
* Returns serialization options for the `/meta` endpoint's type field.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
76
|
+
*
|
|
77
|
+
* `refDepth: 0.5` is intentionally static — independent of `@db.depth.limit`
|
|
78
|
+
* (which is a security guard on nested writes, not a serialization policy).
|
|
79
|
+
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
80
|
+
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
81
|
+
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
82
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) are not `.ref` nodes
|
|
83
|
+
* and always expand fully regardless of `refDepth` — the write-payload shape
|
|
84
|
+
* clients need is unaffected.
|
|
85
|
+
*
|
|
86
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, and `db.rel.*`; strips
|
|
87
|
+
* other `db.*` (table, column, index, default, etc.). Override in subclass
|
|
88
|
+
* to customise.
|
|
42
89
|
*/
|
|
43
90
|
protected getSerializeOptions(): TSerializeOptions;
|
|
44
91
|
/**
|
|
45
92
|
* Whether this controller is read-only (no write endpoints).
|
|
46
|
-
* Returns `true`
|
|
93
|
+
* Returns `true` by default; {@link AsDbController} overrides to `false`.
|
|
47
94
|
*/
|
|
48
95
|
protected _isReadOnly(): boolean;
|
|
49
96
|
private _queryControlsValidator?;
|
|
@@ -55,6 +102,49 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
55
102
|
protected validateControls(controls: Record<string, unknown>, type: "query" | "pages" | "getOne"): string | undefined;
|
|
56
103
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
57
104
|
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Shared filter/sort/search gate check. Subclasses assemble a {@link ReadableGates}
|
|
107
|
+
* config per request (or once in the constructor when static) and call this to
|
|
108
|
+
* get a uniform HTTP 400 response for any offending field/control.
|
|
109
|
+
*/
|
|
110
|
+
protected checkGates(filter: FilterExpr | undefined, controls: Record<string, unknown>, gates: ReadableGates): HttpError | undefined;
|
|
111
|
+
protected parseQueryString(url: string): _uniqu_url0.UrlQuery;
|
|
112
|
+
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
113
|
+
/**
|
|
114
|
+
* **GET /meta** — returns the bound interface's metadata envelope.
|
|
115
|
+
*
|
|
116
|
+
* Base implementation delegates to {@link buildMetaResponse}, which subclasses
|
|
117
|
+
* override to add source-specific fields (relations, searchable flags, etc.).
|
|
118
|
+
* The response is cached on the instance; async overrides must cache any
|
|
119
|
+
* extra enrichment themselves.
|
|
120
|
+
*/
|
|
121
|
+
meta(): Promise<TMetaResponse>;
|
|
122
|
+
/**
|
|
123
|
+
* Builds the `/meta` payload. Override in subclasses to populate source-specific
|
|
124
|
+
* fields. Defaults return a minimal envelope with the serialized type and the
|
|
125
|
+
* read-only flag; value-help dicts populate their capability hints here.
|
|
126
|
+
*/
|
|
127
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/as-db-readable.controller.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* Read-only database controller for Moost that works with any `AtscriptDbReadable`
|
|
133
|
+
* (tables or views). Provides query, pages, getOne, and meta endpoints.
|
|
134
|
+
*
|
|
135
|
+
* For write operations (insert, replace, update, delete), use {@link AsDbController}.
|
|
136
|
+
* For views, use {@link AsDbViewController}.
|
|
137
|
+
*/
|
|
138
|
+
declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsReadableController<T, DataType> {
|
|
139
|
+
/** Reference to the underlying readable (table or view). */
|
|
140
|
+
protected readable: AtscriptDbReadable<T>;
|
|
141
|
+
private readonly _gates;
|
|
142
|
+
constructor(readable: AtscriptDbReadable<T>, app: Moost);
|
|
143
|
+
private _buildGates;
|
|
144
|
+
private _collectAnnotated;
|
|
145
|
+
protected hasField(path: string): boolean;
|
|
146
|
+
/** Validates $with relations against the readable. */
|
|
147
|
+
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
58
148
|
/**
|
|
59
149
|
* Compute an embedding vector from a search term.
|
|
60
150
|
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
@@ -71,8 +161,6 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
71
161
|
* May return a Promise for async lookups.
|
|
72
162
|
*/
|
|
73
163
|
protected transformProjection(projection?: UniqueryControls["$select"]): UniqueryControls["$select"] | undefined | Promise<UniqueryControls["$select"] | undefined>;
|
|
74
|
-
protected parseQueryString(url: string): _uniqu_url0.UrlQuery;
|
|
75
|
-
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
76
164
|
/**
|
|
77
165
|
* Extracts a composite identifier object from query params.
|
|
78
166
|
* Tries composite primary key first, then compound unique indexes.
|
|
@@ -104,12 +192,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
104
192
|
/**
|
|
105
193
|
* **GET /meta** — returns table/view metadata for UI.
|
|
106
194
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* their own enrichment if needed.
|
|
195
|
+
* Overrides the base's minimal envelope to add relations, searchable flags,
|
|
196
|
+
* vector-searchable flags, field-descriptor-derived filter/sort hints, and
|
|
197
|
+
* the configured primary keys.
|
|
111
198
|
*/
|
|
112
|
-
|
|
199
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
113
200
|
}
|
|
114
201
|
//#endregion
|
|
115
202
|
//#region src/as-db.controller.d.ts
|
|
@@ -161,6 +248,132 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
161
248
|
removeComposite(query: Record<string, string>): Promise<unknown>;
|
|
162
249
|
}
|
|
163
250
|
//#endregion
|
|
251
|
+
//#region src/as-value-help.controller.d.ts
|
|
252
|
+
/**
|
|
253
|
+
* Parsed Uniquery controls with the `$search` field carved out for value-help
|
|
254
|
+
* use (the core DTO includes it but we narrow the type here so implementations
|
|
255
|
+
* can rely on the concrete shape).
|
|
256
|
+
*/
|
|
257
|
+
interface ValueHelpQuery<T> {
|
|
258
|
+
filter: FilterExpr;
|
|
259
|
+
controls: {
|
|
260
|
+
$skip?: number;
|
|
261
|
+
$limit?: number;
|
|
262
|
+
$search?: string;
|
|
263
|
+
$select?: (keyof T | string)[];
|
|
264
|
+
$sort?: unknown;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Abstract base class for read-only HTTP controllers serving a **value-help**
|
|
270
|
+
* source — an interface bound to a simple `/query` / `/pages` / `/one(/:id)` /
|
|
271
|
+
* `/meta` surface, not a full DB table. Value-help controllers drive the
|
|
272
|
+
* client-side picker UI on fields annotated `@db.rel.FK`.
|
|
273
|
+
*
|
|
274
|
+
* Subclass responsibilities:
|
|
275
|
+
* - Pass the bound interface + rows/backing-source through super().
|
|
276
|
+
* - Implement the abstract {@link query} and {@link getOne} methods.
|
|
277
|
+
*
|
|
278
|
+
* The bound interface's `@ui.dict.*` annotations are **client-side hints**
|
|
279
|
+
* consumed by the picker UI; the server does not gate filter / sort / search
|
|
280
|
+
* requests against them. Subclasses that need a backend gate should compose
|
|
281
|
+
* one of their own (see {@link AsDbReadableController} for the
|
|
282
|
+
* `@db.column.filterable` / `@db.column.sortable` pattern).
|
|
283
|
+
*/
|
|
284
|
+
declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsReadableController<T, DataType> {
|
|
285
|
+
/** Per-prop metadata map of the bound interface; eagerly built once. */
|
|
286
|
+
protected readonly fieldMeta: Map<string, Map<string, unknown>>;
|
|
287
|
+
/**
|
|
288
|
+
* Fields that participate in `$search` by default. Populated from
|
|
289
|
+
* `@ui.dict.searchable`:
|
|
290
|
+
* - If any prop carries `@ui.dict.searchable`, only those props are here.
|
|
291
|
+
* - Else if the interface carries `@ui.dict.searchable`, every `string`-typed prop is here.
|
|
292
|
+
* - Else every `string`-typed prop is here (hint is absent — default to all strings).
|
|
293
|
+
*/
|
|
294
|
+
protected readonly searchableFields: readonly string[];
|
|
295
|
+
/** The `@meta.id` field name on the bound interface, if any. */
|
|
296
|
+
protected readonly primaryKey: string | undefined;
|
|
297
|
+
constructor(boundType: T, controllerName: string, app: Moost);
|
|
298
|
+
/** Executes a value-help query against the backing source. */
|
|
299
|
+
protected abstract query(controls: ValueHelpQuery<DataType>): Promise<{
|
|
300
|
+
data: DataType[];
|
|
301
|
+
count: number;
|
|
302
|
+
}>;
|
|
303
|
+
/** Returns the row whose primary key matches `id`, or `null` on miss. */
|
|
304
|
+
protected abstract getOne(id: string | number): Promise<DataType | null>;
|
|
305
|
+
protected hasField(path: string): boolean;
|
|
306
|
+
/**
|
|
307
|
+
* **GET /query** — returns an array of matched rows (up to `$limit`).
|
|
308
|
+
*/
|
|
309
|
+
runQuery(url: string): Promise<DataType[] | HttpError>;
|
|
310
|
+
/**
|
|
311
|
+
* **GET /pages** — paginated row window plus total count.
|
|
312
|
+
*/
|
|
313
|
+
runPages(url: string): Promise<{
|
|
314
|
+
data: DataType[];
|
|
315
|
+
page: number;
|
|
316
|
+
itemsPerPage: number;
|
|
317
|
+
pages: number;
|
|
318
|
+
count: number;
|
|
319
|
+
} | HttpError>;
|
|
320
|
+
/**
|
|
321
|
+
* **GET /one/:id** — retrieves a single row by primary key.
|
|
322
|
+
*/
|
|
323
|
+
runGetOne(id: string): Promise<DataType | HttpError>;
|
|
324
|
+
/**
|
|
325
|
+
* **GET /one?<pk>=<val>** — retrieves a single row by PK query param (fallback).
|
|
326
|
+
*/
|
|
327
|
+
runGetOneComposite(query: Record<string, string>): Promise<DataType | HttpError>;
|
|
328
|
+
/**
|
|
329
|
+
* Meta response surfaces `@ui.dict.*` annotations as **hints** for the
|
|
330
|
+
* client picker UI (which controls to render); the server does not enforce
|
|
331
|
+
* these flags at request time.
|
|
332
|
+
*/
|
|
333
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/as-json-value-help.controller.d.ts
|
|
337
|
+
/**
|
|
338
|
+
* Concrete value-help controller backed by a static in-memory array. Provides
|
|
339
|
+
* filter/sort/search/paginate over the provided rows, respecting the
|
|
340
|
+
* `@ui.dict.*` capability annotations on the bound interface.
|
|
341
|
+
*
|
|
342
|
+
* **Semantics:**
|
|
343
|
+
* - Filter is interpreted as a subset of MongoDB-style comparison operators
|
|
344
|
+
* (`$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`, `$regex`) and
|
|
345
|
+
* logical combinators (`$and`, `$or`, `$not`, `$nor`). Unknown operators
|
|
346
|
+
* fall through to strict equality.
|
|
347
|
+
* - Sort is stable, multi-key, lexicographic. Direction via `-` prefix on the
|
|
348
|
+
* field name or `{ [field]: 'asc' | 'desc' }`.
|
|
349
|
+
* - Search is case-insensitive substring matching across every field listed in
|
|
350
|
+
* {@link searchableFields}.
|
|
351
|
+
* - Type coercion: comparisons compare raw JS values (no implicit string-to-
|
|
352
|
+
* number coercion); strings are compared case-insensitively only for
|
|
353
|
+
* `$search`, not for filter operators.
|
|
354
|
+
*
|
|
355
|
+
* **Constructor:**
|
|
356
|
+
* ```ts
|
|
357
|
+
* new AsJsonValueHelpController(StatusDict, [
|
|
358
|
+
* { id: 'active', label: 'Active' },
|
|
359
|
+
* { id: 'archived', label: 'Archived' },
|
|
360
|
+
* ], app);
|
|
361
|
+
* ```
|
|
362
|
+
*
|
|
363
|
+
* Register under a Moost path with `@Controller('/api/dicts/status')` or the
|
|
364
|
+
* equivalent composition decorator.
|
|
365
|
+
*/
|
|
366
|
+
declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsValueHelpController<T, DataType> {
|
|
367
|
+
protected rows: DataType[];
|
|
368
|
+
private _pkIndex?;
|
|
369
|
+
constructor(boundType: T, rows: DataType[], app: Moost, controllerName?: string);
|
|
370
|
+
protected query(controls: ValueHelpQuery<DataType>): Promise<{
|
|
371
|
+
data: DataType[];
|
|
372
|
+
count: number;
|
|
373
|
+
}>;
|
|
374
|
+
protected getOne(id: string | number): Promise<DataType | null>;
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
164
377
|
//#region src/decorators.d.ts
|
|
165
378
|
/**
|
|
166
379
|
* DI token under which the {@link AtscriptDbReadable} instance
|
|
@@ -223,4 +436,4 @@ declare const ViewController: (readable: AtscriptDbReadable, prefix?: string) =>
|
|
|
223
436
|
declare const validationErrorTransform: () => moost.TInterceptorDef;
|
|
224
437
|
declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorator;
|
|
225
438
|
//#endregion
|
|
226
|
-
export { AsDbController, AsDbReadableController, READABLE_DEF, ReadableController, TABLE_DEF, TableController, UseValidationErrorTransform, ViewController, validationErrorTransform };
|
|
439
|
+
export { AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, READABLE_DEF, ReadableController, ReadableGates, TABLE_DEF, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, validationErrorTransform };
|
package/dist/index.d.mts
CHANGED
|
@@ -6,29 +6,66 @@ import { Moost, TConsoleBase } from "moost";
|
|
|
6
6
|
import * as _uniqu_url0 from "@uniqu/url";
|
|
7
7
|
import { AtscriptDbReadable, AtscriptDbTable, FilterExpr, TMetaResponse, Uniquery, UniqueryControls } from "@atscript/db";
|
|
8
8
|
|
|
9
|
-
//#region src/as-
|
|
9
|
+
//#region src/as-readable.controller.d.ts
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Optional gate configuration for a single request. Each present entry enables
|
|
12
|
+
* the corresponding check; omitted entries skip that gate entirely.
|
|
13
|
+
*/
|
|
14
|
+
interface ReadableGates {
|
|
15
|
+
filter?: {
|
|
16
|
+
predicate: (field: string) => boolean;
|
|
17
|
+
annotation: string;
|
|
18
|
+
};
|
|
19
|
+
sort?: {
|
|
20
|
+
predicate: (field: string) => boolean;
|
|
21
|
+
annotation: string;
|
|
22
|
+
};
|
|
23
|
+
search?: {
|
|
24
|
+
allowed: boolean;
|
|
25
|
+
rejectionMessage: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Abstract base class for read-only HTTP controllers over an Atscript interface.
|
|
13
30
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
31
|
+
* Shared responsibilities (implemented here):
|
|
32
|
+
* - Stamps `@db.http.path` on the bound interface's metadata at registration
|
|
33
|
+
* with the final public path (leading slash + Moost `globalPrefix`).
|
|
34
|
+
* - Lazily serializes the bound interface for the `/meta` endpoint
|
|
35
|
+
* (see {@link getSerializeOptions}).
|
|
36
|
+
* - Provides DTO-backed validators for the Uniquery controls DTOs and the
|
|
37
|
+
* helpers (`parseQueryString`, `returnOne`, `validateParsed`, etc.) that
|
|
38
|
+
* subclasses share.
|
|
39
|
+
* - Registers the `/meta` route. Subclasses override {@link buildMetaResponse}
|
|
40
|
+
* to shape the payload; DB-backed readables add relations/searchable flags,
|
|
41
|
+
* value-help controllers add their capability hints.
|
|
42
|
+
*
|
|
43
|
+
* Subclass responsibilities:
|
|
44
|
+
* - Pass the bound interface + logical name + (optional) kind tag through super().
|
|
45
|
+
* - Implement {@link hasField} so insights validation can reject unknown keys.
|
|
46
|
+
* - Register the `/query`, `/pages`, `/one(/:id)` routes with the concrete
|
|
47
|
+
* handlers that match the data source's contract (DB readables route into
|
|
48
|
+
* aggregate/vector/search; value-help controllers just filter/sort/paginate).
|
|
16
49
|
*/
|
|
17
|
-
declare class
|
|
18
|
-
/**
|
|
19
|
-
protected
|
|
50
|
+
declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
|
|
51
|
+
/** The Atscript interface this controller serves. */
|
|
52
|
+
protected readonly boundType: T;
|
|
53
|
+
/** Short human-readable name for logging (usually the table/source name). */
|
|
54
|
+
protected readonly controllerName: string;
|
|
20
55
|
/** Application-scoped logger. */
|
|
21
56
|
protected logger: TConsoleBase;
|
|
22
|
-
/** Cached serialized type definition (lazy, computed on first access). */
|
|
23
|
-
private _serializedType?;
|
|
24
57
|
/** Moost application instance. */
|
|
25
58
|
protected app: Moost;
|
|
59
|
+
/** Cached serialized type definition (lazy, computed on first access). */
|
|
60
|
+
private _serializedType?;
|
|
26
61
|
/** Cached full meta response (computed lazily on first meta() call). */
|
|
27
62
|
private _metaResponse?;
|
|
28
|
-
constructor(
|
|
63
|
+
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
64
|
+
/** Subclass contract: return `true` if `path` addresses a valid field on the bound source. */
|
|
65
|
+
protected abstract hasField(path: string): boolean;
|
|
29
66
|
/** Sets @db.http.path on the type metadata from the controller's computed prefix. */
|
|
30
67
|
private _resolveHttpPath;
|
|
31
|
-
/** Lazily serializes the type (after all controllers have set
|
|
68
|
+
/** Lazily serializes the bound type (after all controllers have set @db.http.path). */
|
|
32
69
|
protected getSerializedType(): _atscript_typescript_utils0.TSerializedAnnotatedType;
|
|
33
70
|
/**
|
|
34
71
|
* One-time initialization hook. Override to seed data, register watchers, etc.
|
|
@@ -36,14 +73,24 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
36
73
|
protected init(): void | Promise<void>;
|
|
37
74
|
/**
|
|
38
75
|
* Returns serialization options for the `/meta` endpoint's type field.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
76
|
+
*
|
|
77
|
+
* `refDepth: 0.5` is intentionally static — independent of `@db.depth.limit`
|
|
78
|
+
* (which is a security guard on nested writes, not a serialization policy).
|
|
79
|
+
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
80
|
+
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
81
|
+
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
82
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) are not `.ref` nodes
|
|
83
|
+
* and always expand fully regardless of `refDepth` — the write-payload shape
|
|
84
|
+
* clients need is unaffected.
|
|
85
|
+
*
|
|
86
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, and `db.rel.*`; strips
|
|
87
|
+
* other `db.*` (table, column, index, default, etc.). Override in subclass
|
|
88
|
+
* to customise.
|
|
42
89
|
*/
|
|
43
90
|
protected getSerializeOptions(): TSerializeOptions;
|
|
44
91
|
/**
|
|
45
92
|
* Whether this controller is read-only (no write endpoints).
|
|
46
|
-
* Returns `true`
|
|
93
|
+
* Returns `true` by default; {@link AsDbController} overrides to `false`.
|
|
47
94
|
*/
|
|
48
95
|
protected _isReadOnly(): boolean;
|
|
49
96
|
private _queryControlsValidator?;
|
|
@@ -55,6 +102,49 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
55
102
|
protected validateControls(controls: Record<string, unknown>, type: "query" | "pages" | "getOne"): string | undefined;
|
|
56
103
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
57
104
|
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Shared filter/sort/search gate check. Subclasses assemble a {@link ReadableGates}
|
|
107
|
+
* config per request (or once in the constructor when static) and call this to
|
|
108
|
+
* get a uniform HTTP 400 response for any offending field/control.
|
|
109
|
+
*/
|
|
110
|
+
protected checkGates(filter: FilterExpr | undefined, controls: Record<string, unknown>, gates: ReadableGates): HttpError | undefined;
|
|
111
|
+
protected parseQueryString(url: string): _uniqu_url0.UrlQuery;
|
|
112
|
+
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
113
|
+
/**
|
|
114
|
+
* **GET /meta** — returns the bound interface's metadata envelope.
|
|
115
|
+
*
|
|
116
|
+
* Base implementation delegates to {@link buildMetaResponse}, which subclasses
|
|
117
|
+
* override to add source-specific fields (relations, searchable flags, etc.).
|
|
118
|
+
* The response is cached on the instance; async overrides must cache any
|
|
119
|
+
* extra enrichment themselves.
|
|
120
|
+
*/
|
|
121
|
+
meta(): Promise<TMetaResponse>;
|
|
122
|
+
/**
|
|
123
|
+
* Builds the `/meta` payload. Override in subclasses to populate source-specific
|
|
124
|
+
* fields. Defaults return a minimal envelope with the serialized type and the
|
|
125
|
+
* read-only flag; value-help dicts populate their capability hints here.
|
|
126
|
+
*/
|
|
127
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/as-db-readable.controller.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* Read-only database controller for Moost that works with any `AtscriptDbReadable`
|
|
133
|
+
* (tables or views). Provides query, pages, getOne, and meta endpoints.
|
|
134
|
+
*
|
|
135
|
+
* For write operations (insert, replace, update, delete), use {@link AsDbController}.
|
|
136
|
+
* For views, use {@link AsDbViewController}.
|
|
137
|
+
*/
|
|
138
|
+
declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsReadableController<T, DataType> {
|
|
139
|
+
/** Reference to the underlying readable (table or view). */
|
|
140
|
+
protected readable: AtscriptDbReadable<T>;
|
|
141
|
+
private readonly _gates;
|
|
142
|
+
constructor(readable: AtscriptDbReadable<T>, app: Moost);
|
|
143
|
+
private _buildGates;
|
|
144
|
+
private _collectAnnotated;
|
|
145
|
+
protected hasField(path: string): boolean;
|
|
146
|
+
/** Validates $with relations against the readable. */
|
|
147
|
+
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
58
148
|
/**
|
|
59
149
|
* Compute an embedding vector from a search term.
|
|
60
150
|
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
@@ -71,8 +161,6 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
71
161
|
* May return a Promise for async lookups.
|
|
72
162
|
*/
|
|
73
163
|
protected transformProjection(projection?: UniqueryControls["$select"]): UniqueryControls["$select"] | undefined | Promise<UniqueryControls["$select"] | undefined>;
|
|
74
|
-
protected parseQueryString(url: string): _uniqu_url0.UrlQuery;
|
|
75
|
-
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
76
164
|
/**
|
|
77
165
|
* Extracts a composite identifier object from query params.
|
|
78
166
|
* Tries composite primary key first, then compound unique indexes.
|
|
@@ -104,12 +192,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
104
192
|
/**
|
|
105
193
|
* **GET /meta** — returns table/view metadata for UI.
|
|
106
194
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* their own enrichment if needed.
|
|
195
|
+
* Overrides the base's minimal envelope to add relations, searchable flags,
|
|
196
|
+
* vector-searchable flags, field-descriptor-derived filter/sort hints, and
|
|
197
|
+
* the configured primary keys.
|
|
111
198
|
*/
|
|
112
|
-
|
|
199
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
113
200
|
}
|
|
114
201
|
//#endregion
|
|
115
202
|
//#region src/as-db.controller.d.ts
|
|
@@ -161,6 +248,132 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
161
248
|
removeComposite(query: Record<string, string>): Promise<unknown>;
|
|
162
249
|
}
|
|
163
250
|
//#endregion
|
|
251
|
+
//#region src/as-value-help.controller.d.ts
|
|
252
|
+
/**
|
|
253
|
+
* Parsed Uniquery controls with the `$search` field carved out for value-help
|
|
254
|
+
* use (the core DTO includes it but we narrow the type here so implementations
|
|
255
|
+
* can rely on the concrete shape).
|
|
256
|
+
*/
|
|
257
|
+
interface ValueHelpQuery<T> {
|
|
258
|
+
filter: FilterExpr;
|
|
259
|
+
controls: {
|
|
260
|
+
$skip?: number;
|
|
261
|
+
$limit?: number;
|
|
262
|
+
$search?: string;
|
|
263
|
+
$select?: (keyof T | string)[];
|
|
264
|
+
$sort?: unknown;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Abstract base class for read-only HTTP controllers serving a **value-help**
|
|
270
|
+
* source — an interface bound to a simple `/query` / `/pages` / `/one(/:id)` /
|
|
271
|
+
* `/meta` surface, not a full DB table. Value-help controllers drive the
|
|
272
|
+
* client-side picker UI on fields annotated `@db.rel.FK`.
|
|
273
|
+
*
|
|
274
|
+
* Subclass responsibilities:
|
|
275
|
+
* - Pass the bound interface + rows/backing-source through super().
|
|
276
|
+
* - Implement the abstract {@link query} and {@link getOne} methods.
|
|
277
|
+
*
|
|
278
|
+
* The bound interface's `@ui.dict.*` annotations are **client-side hints**
|
|
279
|
+
* consumed by the picker UI; the server does not gate filter / sort / search
|
|
280
|
+
* requests against them. Subclasses that need a backend gate should compose
|
|
281
|
+
* one of their own (see {@link AsDbReadableController} for the
|
|
282
|
+
* `@db.column.filterable` / `@db.column.sortable` pattern).
|
|
283
|
+
*/
|
|
284
|
+
declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsReadableController<T, DataType> {
|
|
285
|
+
/** Per-prop metadata map of the bound interface; eagerly built once. */
|
|
286
|
+
protected readonly fieldMeta: Map<string, Map<string, unknown>>;
|
|
287
|
+
/**
|
|
288
|
+
* Fields that participate in `$search` by default. Populated from
|
|
289
|
+
* `@ui.dict.searchable`:
|
|
290
|
+
* - If any prop carries `@ui.dict.searchable`, only those props are here.
|
|
291
|
+
* - Else if the interface carries `@ui.dict.searchable`, every `string`-typed prop is here.
|
|
292
|
+
* - Else every `string`-typed prop is here (hint is absent — default to all strings).
|
|
293
|
+
*/
|
|
294
|
+
protected readonly searchableFields: readonly string[];
|
|
295
|
+
/** The `@meta.id` field name on the bound interface, if any. */
|
|
296
|
+
protected readonly primaryKey: string | undefined;
|
|
297
|
+
constructor(boundType: T, controllerName: string, app: Moost);
|
|
298
|
+
/** Executes a value-help query against the backing source. */
|
|
299
|
+
protected abstract query(controls: ValueHelpQuery<DataType>): Promise<{
|
|
300
|
+
data: DataType[];
|
|
301
|
+
count: number;
|
|
302
|
+
}>;
|
|
303
|
+
/** Returns the row whose primary key matches `id`, or `null` on miss. */
|
|
304
|
+
protected abstract getOne(id: string | number): Promise<DataType | null>;
|
|
305
|
+
protected hasField(path: string): boolean;
|
|
306
|
+
/**
|
|
307
|
+
* **GET /query** — returns an array of matched rows (up to `$limit`).
|
|
308
|
+
*/
|
|
309
|
+
runQuery(url: string): Promise<DataType[] | HttpError>;
|
|
310
|
+
/**
|
|
311
|
+
* **GET /pages** — paginated row window plus total count.
|
|
312
|
+
*/
|
|
313
|
+
runPages(url: string): Promise<{
|
|
314
|
+
data: DataType[];
|
|
315
|
+
page: number;
|
|
316
|
+
itemsPerPage: number;
|
|
317
|
+
pages: number;
|
|
318
|
+
count: number;
|
|
319
|
+
} | HttpError>;
|
|
320
|
+
/**
|
|
321
|
+
* **GET /one/:id** — retrieves a single row by primary key.
|
|
322
|
+
*/
|
|
323
|
+
runGetOne(id: string): Promise<DataType | HttpError>;
|
|
324
|
+
/**
|
|
325
|
+
* **GET /one?<pk>=<val>** — retrieves a single row by PK query param (fallback).
|
|
326
|
+
*/
|
|
327
|
+
runGetOneComposite(query: Record<string, string>): Promise<DataType | HttpError>;
|
|
328
|
+
/**
|
|
329
|
+
* Meta response surfaces `@ui.dict.*` annotations as **hints** for the
|
|
330
|
+
* client picker UI (which controls to render); the server does not enforce
|
|
331
|
+
* these flags at request time.
|
|
332
|
+
*/
|
|
333
|
+
protected buildMetaResponse(): Promise<TMetaResponse>;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/as-json-value-help.controller.d.ts
|
|
337
|
+
/**
|
|
338
|
+
* Concrete value-help controller backed by a static in-memory array. Provides
|
|
339
|
+
* filter/sort/search/paginate over the provided rows, respecting the
|
|
340
|
+
* `@ui.dict.*` capability annotations on the bound interface.
|
|
341
|
+
*
|
|
342
|
+
* **Semantics:**
|
|
343
|
+
* - Filter is interpreted as a subset of MongoDB-style comparison operators
|
|
344
|
+
* (`$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`, `$regex`) and
|
|
345
|
+
* logical combinators (`$and`, `$or`, `$not`, `$nor`). Unknown operators
|
|
346
|
+
* fall through to strict equality.
|
|
347
|
+
* - Sort is stable, multi-key, lexicographic. Direction via `-` prefix on the
|
|
348
|
+
* field name or `{ [field]: 'asc' | 'desc' }`.
|
|
349
|
+
* - Search is case-insensitive substring matching across every field listed in
|
|
350
|
+
* {@link searchableFields}.
|
|
351
|
+
* - Type coercion: comparisons compare raw JS values (no implicit string-to-
|
|
352
|
+
* number coercion); strings are compared case-insensitively only for
|
|
353
|
+
* `$search`, not for filter operators.
|
|
354
|
+
*
|
|
355
|
+
* **Constructor:**
|
|
356
|
+
* ```ts
|
|
357
|
+
* new AsJsonValueHelpController(StatusDict, [
|
|
358
|
+
* { id: 'active', label: 'Active' },
|
|
359
|
+
* { id: 'archived', label: 'Archived' },
|
|
360
|
+
* ], app);
|
|
361
|
+
* ```
|
|
362
|
+
*
|
|
363
|
+
* Register under a Moost path with `@Controller('/api/dicts/status')` or the
|
|
364
|
+
* equivalent composition decorator.
|
|
365
|
+
*/
|
|
366
|
+
declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsValueHelpController<T, DataType> {
|
|
367
|
+
protected rows: DataType[];
|
|
368
|
+
private _pkIndex?;
|
|
369
|
+
constructor(boundType: T, rows: DataType[], app: Moost, controllerName?: string);
|
|
370
|
+
protected query(controls: ValueHelpQuery<DataType>): Promise<{
|
|
371
|
+
data: DataType[];
|
|
372
|
+
count: number;
|
|
373
|
+
}>;
|
|
374
|
+
protected getOne(id: string | number): Promise<DataType | null>;
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
164
377
|
//#region src/decorators.d.ts
|
|
165
378
|
/**
|
|
166
379
|
* DI token under which the {@link AtscriptDbReadable} instance
|
|
@@ -223,4 +436,4 @@ declare const ViewController: (readable: AtscriptDbReadable, prefix?: string) =>
|
|
|
223
436
|
declare const validationErrorTransform: () => moost.TInterceptorDef;
|
|
224
437
|
declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorator;
|
|
225
438
|
//#endregion
|
|
226
|
-
export { AsDbController, AsDbReadableController, READABLE_DEF, ReadableController, TABLE_DEF, TableController, UseValidationErrorTransform, ViewController, validationErrorTransform };
|
|
439
|
+
export { AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, READABLE_DEF, ReadableController, ReadableGates, TABLE_DEF, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, validationErrorTransform };
|