@atscript/moost-db 0.1.34 → 0.1.35

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Atscript
3
+ Copyright (c) 2025-present Artem Maltsev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.cjs CHANGED
@@ -28,8 +28,11 @@ const moost = __toESM(require("moost"));
28
28
  const __uniqu_url = __toESM(require("@uniqu/url"));
29
29
 
30
30
  //#region packages/moost-db/src/decorators.ts
31
- const TABLE_DEF = "__atscript_db_table_def";
31
+ const READABLE_DEF = "__atscript_db_readable_def";
32
+ const TABLE_DEF = READABLE_DEF;
32
33
  const TableController = (table, prefix) => (0, moost.ApplyDecorators)((0, moost.Provide)(TABLE_DEF, () => table), (0, moost.Controller)(prefix || table.tableName), (0, moost.Inherit)());
34
+ const ReadableController = (readable, prefix) => (0, moost.ApplyDecorators)((0, moost.Provide)(READABLE_DEF, () => readable), (0, moost.Controller)(prefix || readable.tableName), (0, moost.Inherit)());
35
+ const ViewController = ReadableController;
33
36
 
34
37
  //#endregion
35
38
  //#region packages/moost-db/src/validation-interceptor.ts
@@ -145,7 +148,7 @@ _define_property$1(SelectControlDto, "id", "SelectControlDto");
145
148
  (0, __atscript_typescript_utils.defineAnnotatedType)("object", SelectControlDto).propPattern(/./, (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").value(1).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").value(0).$type).$type);
146
149
 
147
150
  //#endregion
148
- //#region packages/moost-db/src/as-db.controller.ts
151
+ //#region packages/moost-db/src/as-db-readable.controller.ts
149
152
  function _define_property(obj, key, value) {
150
153
  if (key in obj) Object.defineProperty(obj, key, {
151
154
  value,
@@ -156,21 +159,21 @@ function _define_property(obj, key, value) {
156
159
  else obj[key] = value;
157
160
  return obj;
158
161
  }
159
- function _ts_decorate(decorators, target, key, desc) {
162
+ function _ts_decorate$1(decorators, target, key, desc) {
160
163
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
161
164
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
162
165
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
163
166
  return c > 3 && r && Object.defineProperty(target, key, r), r;
164
167
  }
165
- function _ts_metadata(k, v) {
168
+ function _ts_metadata$1(k, v) {
166
169
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
167
170
  }
168
- function _ts_param(paramIndex, decorator) {
171
+ function _ts_param$1(paramIndex, decorator) {
169
172
  return function(target, key) {
170
173
  decorator(target, key, paramIndex);
171
174
  };
172
175
  }
173
- var AsDbController = class {
176
+ var AsDbReadableController = class {
174
177
  /**
175
178
  * One-time initialization hook. Override to seed data, register watchers, etc.
176
179
  */ init() {}
@@ -192,7 +195,7 @@ var AsDbController = class {
192
195
  return undefined;
193
196
  }
194
197
  validateInsights(insights) {
195
- for (const key of insights.keys()) if (!this.table.flatMap.has(key)) return `Unknown field "${key}"`;
198
+ for (const key of insights.keys()) if (!this.readable.flatMap.has(key)) return `Unknown field "${key}"`;
196
199
  return undefined;
197
200
  }
198
201
  validateParsed(parsed, type) {
@@ -214,16 +217,6 @@ var AsDbController = class {
214
217
  */ transformProjection(projection) {
215
218
  return projection;
216
219
  }
217
- /**
218
- * Intercepts write operations. Return `undefined` to abort.
219
- */ onWrite(action, data) {
220
- return data;
221
- }
222
- /**
223
- * Intercepts delete operations. Return `undefined` to abort.
224
- */ onRemove(id) {
225
- return id;
226
- }
227
220
  parseQueryString(url) {
228
221
  const idx = url.indexOf("?");
229
222
  return (0, __uniqu_url.parseUrl)(idx >= 0 ? url.slice(idx + 1) : "");
@@ -242,7 +235,7 @@ var AsDbController = class {
242
235
  const controls = parsed.controls;
243
236
  const filter = this.transformFilter(parsed.filter);
244
237
  const select = this.transformProjection(controls.$select);
245
- if (controls.$count) return this.table.count({
238
+ if (controls.$count) return this.readable.count({
246
239
  filter,
247
240
  controls: {
248
241
  ...controls,
@@ -251,7 +244,7 @@ var AsDbController = class {
251
244
  });
252
245
  const searchTerm = controls.$search;
253
246
  const indexName = controls.$index;
254
- if (searchTerm && this.table.isSearchable()) return this.table.search(searchTerm, {
247
+ if (searchTerm && this.readable.isSearchable()) return this.readable.search(searchTerm, {
255
248
  filter,
256
249
  controls: {
257
250
  ...controls,
@@ -259,7 +252,7 @@ var AsDbController = class {
259
252
  $limit: controls.$limit || 1e3
260
253
  }
261
254
  }, indexName);
262
- return this.table.findMany({
255
+ return this.readable.findMany({
263
256
  filter,
264
257
  controls: {
265
258
  ...controls,
@@ -292,8 +285,8 @@ var AsDbController = class {
292
285
  }
293
286
  };
294
287
  let result;
295
- if (searchTerm && this.table.isSearchable()) result = await this.table.searchWithCount(searchTerm, query, indexName);
296
- else result = await this.table.findManyWithCount(query);
288
+ if (searchTerm && this.readable.isSearchable()) result = await this.readable.searchWithCount(searchTerm, query, indexName);
289
+ else result = await this.readable.findManyWithCount(query);
297
290
  return {
298
291
  data: result.data,
299
292
  page,
@@ -310,7 +303,109 @@ else result = await this.table.findManyWithCount(query);
310
303
  const error = this.validateParsed(parsed, "getOne");
311
304
  if (error) return error;
312
305
  const select = this.transformProjection(parsed.controls.$select);
313
- return this.returnOne(this.table.findById(id, { $select: select }));
306
+ const controls = {
307
+ ...parsed.controls,
308
+ $select: select
309
+ };
310
+ return this.returnOne(this.readable.findById(id, { controls }));
311
+ }
312
+ /**
313
+ * **GET /meta** — returns table/view metadata for UI.
314
+ */ meta() {
315
+ return {
316
+ searchable: this.readable.isSearchable(),
317
+ searchIndexes: this._searchIndexes,
318
+ type: this._serializedType
319
+ };
320
+ }
321
+ constructor(readable, app) {
322
+ /** Reference to the underlying readable (table or view). */ _define_property(this, "readable", void 0);
323
+ /** Application-scoped logger. */ _define_property(this, "logger", void 0);
324
+ /** Cached serialized type definition (static, computed once). */ _define_property(this, "_serializedType", void 0);
325
+ /** Cached search index list (static, computed once). */ _define_property(this, "_searchIndexes", void 0);
326
+ _define_property(this, "_queryControlsValidator", void 0);
327
+ _define_property(this, "_pagesControlsValidator", void 0);
328
+ _define_property(this, "_getOneControlsValidator", void 0);
329
+ this.readable = readable;
330
+ this._serializedType = (0, __atscript_typescript_utils.serializeAnnotatedType)(readable.type);
331
+ this._searchIndexes = readable.getSearchIndexes();
332
+ this.logger = app.getLogger(`db [${readable.tableName}]`);
333
+ this.logger.info(`Initializing ${readable.isView ? "view" : "table"} controller`);
334
+ try {
335
+ const p = this.init();
336
+ if (p instanceof Promise) p.catch((error) => {
337
+ this.logger.error(error);
338
+ });
339
+ } catch (error) {
340
+ this.logger.error(error);
341
+ throw error;
342
+ }
343
+ }
344
+ };
345
+ _ts_decorate$1([
346
+ (0, __moostjs_event_http.Get)("query"),
347
+ _ts_param$1(0, (0, __moostjs_event_http.Url)()),
348
+ _ts_metadata$1("design:type", Function),
349
+ _ts_metadata$1("design:paramtypes", [String]),
350
+ _ts_metadata$1("design:returntype", Promise)
351
+ ], AsDbReadableController.prototype, "query", null);
352
+ _ts_decorate$1([
353
+ (0, __moostjs_event_http.Get)("pages"),
354
+ _ts_param$1(0, (0, __moostjs_event_http.Url)()),
355
+ _ts_metadata$1("design:type", Function),
356
+ _ts_metadata$1("design:paramtypes", [String]),
357
+ _ts_metadata$1("design:returntype", Promise)
358
+ ], AsDbReadableController.prototype, "pages", null);
359
+ _ts_decorate$1([
360
+ (0, __moostjs_event_http.Get)("one/:id"),
361
+ _ts_param$1(0, (0, moost.Param)("id")),
362
+ _ts_param$1(1, (0, __moostjs_event_http.Url)()),
363
+ _ts_metadata$1("design:type", Function),
364
+ _ts_metadata$1("design:paramtypes", [String, String]),
365
+ _ts_metadata$1("design:returntype", Promise)
366
+ ], AsDbReadableController.prototype, "getOne", null);
367
+ _ts_decorate$1([
368
+ (0, __moostjs_event_http.Get)("meta"),
369
+ _ts_metadata$1("design:type", Function),
370
+ _ts_metadata$1("design:paramtypes", []),
371
+ _ts_metadata$1("design:returntype", void 0)
372
+ ], AsDbReadableController.prototype, "meta", null);
373
+ AsDbReadableController = _ts_decorate$1([
374
+ UseValidationErrorTransform(),
375
+ _ts_param$1(0, (0, moost.Inject)(READABLE_DEF)),
376
+ _ts_metadata$1("design:type", Function),
377
+ _ts_metadata$1("design:paramtypes", [typeof AtscriptDbReadable === "undefined" ? Object : AtscriptDbReadable, typeof moost.Moost === "undefined" ? Object : moost.Moost])
378
+ ], AsDbReadableController);
379
+
380
+ //#endregion
381
+ //#region packages/moost-db/src/as-db.controller.ts
382
+ function _ts_decorate(decorators, target, key, desc) {
383
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
384
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
385
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
386
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
387
+ }
388
+ function _ts_metadata(k, v) {
389
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
390
+ }
391
+ function _ts_param(paramIndex, decorator) {
392
+ return function(target, key) {
393
+ decorator(target, key, paramIndex);
394
+ };
395
+ }
396
+ var AsDbController = class extends AsDbReadableController {
397
+ /** Reference to the underlying table (typed for write access). */ get table() {
398
+ return this.readable;
399
+ }
400
+ /**
401
+ * Intercepts write operations. Return `undefined` to abort.
402
+ */ onWrite(action, data) {
403
+ return data;
404
+ }
405
+ /**
406
+ * Intercepts delete operations. Return `undefined` to abort.
407
+ */ onRemove(id) {
408
+ return id;
314
409
  }
315
410
  /**
316
411
  * **POST /** — inserts one or many records.
@@ -348,61 +443,10 @@ else result = await this.table.findManyWithCount(query);
348
443
  if (result.deletedCount < 1) return new __moostjs_event_http.HttpError(404);
349
444
  return result;
350
445
  }
351
- /**
352
- * **GET /meta** — returns table metadata for UI.
353
- */ meta() {
354
- return {
355
- searchable: this.table.isSearchable(),
356
- searchIndexes: this._searchIndexes,
357
- type: this._serializedType
358
- };
359
- }
360
446
  constructor(table, app) {
361
- /** Reference to the underlying table. */ _define_property(this, "table", void 0);
362
- /** Application-scoped logger. */ _define_property(this, "logger", void 0);
363
- /** Cached serialized type definition (static, computed once). */ _define_property(this, "_serializedType", void 0);
364
- /** Cached search index list (static, computed once). */ _define_property(this, "_searchIndexes", void 0);
365
- _define_property(this, "_queryControlsValidator", void 0);
366
- _define_property(this, "_pagesControlsValidator", void 0);
367
- _define_property(this, "_getOneControlsValidator", void 0);
368
- this.table = table;
369
- this._serializedType = (0, __atscript_typescript_utils.serializeAnnotatedType)(table.type);
370
- this._searchIndexes = table.getSearchIndexes();
371
- this.logger = app.getLogger(`db [${table.tableName}]`);
372
- this.logger.info("Initializing table controller");
373
- try {
374
- const p = this.init();
375
- if (p instanceof Promise) p.catch((error) => {
376
- this.logger.error(error);
377
- });
378
- } catch (error) {
379
- this.logger.error(error);
380
- throw error;
381
- }
447
+ super(table, app);
382
448
  }
383
449
  };
384
- _ts_decorate([
385
- (0, __moostjs_event_http.Get)("query"),
386
- _ts_param(0, (0, __moostjs_event_http.Url)()),
387
- _ts_metadata("design:type", Function),
388
- _ts_metadata("design:paramtypes", [String]),
389
- _ts_metadata("design:returntype", Promise)
390
- ], AsDbController.prototype, "query", null);
391
- _ts_decorate([
392
- (0, __moostjs_event_http.Get)("pages"),
393
- _ts_param(0, (0, __moostjs_event_http.Url)()),
394
- _ts_metadata("design:type", Function),
395
- _ts_metadata("design:paramtypes", [String]),
396
- _ts_metadata("design:returntype", Promise)
397
- ], AsDbController.prototype, "pages", null);
398
- _ts_decorate([
399
- (0, __moostjs_event_http.Get)("one/:id"),
400
- _ts_param(0, (0, moost.Param)("id")),
401
- _ts_param(1, (0, __moostjs_event_http.Url)()),
402
- _ts_metadata("design:type", Function),
403
- _ts_metadata("design:paramtypes", [String, String]),
404
- _ts_metadata("design:returntype", Promise)
405
- ], AsDbController.prototype, "getOne", null);
406
450
  _ts_decorate([
407
451
  (0, __moostjs_event_http.Post)(""),
408
452
  _ts_param(0, (0, __moostjs_event_http.Body)()),
@@ -431,14 +475,8 @@ _ts_decorate([
431
475
  _ts_metadata("design:paramtypes", [String]),
432
476
  _ts_metadata("design:returntype", Promise)
433
477
  ], AsDbController.prototype, "remove", null);
434
- _ts_decorate([
435
- (0, __moostjs_event_http.Get)("meta"),
436
- _ts_metadata("design:type", Function),
437
- _ts_metadata("design:paramtypes", []),
438
- _ts_metadata("design:returntype", void 0)
439
- ], AsDbController.prototype, "meta", null);
440
478
  AsDbController = _ts_decorate([
441
- UseValidationErrorTransform(),
479
+ (0, moost.Inherit)(),
442
480
  _ts_param(0, (0, moost.Inject)(TABLE_DEF)),
443
481
  _ts_metadata("design:type", Function),
444
482
  _ts_metadata("design:paramtypes", [typeof AtscriptDbTable === "undefined" ? Object : AtscriptDbTable, typeof moost.Moost === "undefined" ? Object : moost.Moost])
@@ -451,7 +489,16 @@ Object.defineProperty(exports, 'AsDbController', {
451
489
  return AsDbController;
452
490
  }
453
491
  });
492
+ Object.defineProperty(exports, 'AsDbReadableController', {
493
+ enumerable: true,
494
+ get: function () {
495
+ return AsDbReadableController;
496
+ }
497
+ });
498
+ exports.READABLE_DEF = READABLE_DEF
499
+ exports.ReadableController = ReadableController
454
500
  exports.TABLE_DEF = TABLE_DEF
455
501
  exports.TableController = TableController
456
502
  exports.UseValidationErrorTransform = UseValidationErrorTransform
503
+ exports.ViewController = ViewController
457
504
  exports.validationErrorTransform = validationErrorTransform
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _atscript_typescript_serialize from '@atscript/typescript/serialize';
2
2
  import * as _atscript_utils_db from '@atscript/utils-db';
3
- import { AtscriptDbTable, Uniquery, FilterExpr, UniqueryControls } from '@atscript/utils-db';
3
+ import { AtscriptDbReadable, Uniquery, FilterExpr, UniqueryControls, AtscriptDbTable } from '@atscript/utils-db';
4
4
  import * as _uniqu_url from '@uniqu/url';
5
5
  import { TAtscriptAnnotatedType, TAtscriptDataType, Validator } from '@atscript/typescript/utils';
6
6
  import { HttpError } from '@moostjs/event-http';
@@ -8,27 +8,22 @@ import * as moost from 'moost';
8
8
  import { TConsoleBase, Moost } from 'moost';
9
9
 
10
10
  /**
11
- * Generic database controller for Moost that works with any `AtscriptDbTable` +
12
- * `BaseDbAdapter`. All CRUD routes through the generic table layer — no
13
- * adapter-specific imports.
11
+ * Read-only database controller for Moost that works with any `AtscriptDbReadable`
12
+ * (tables or views). Provides query, pages, getOne, and meta endpoints.
14
13
  *
15
- * Subclass and provide the table via DI:
16
- * ```ts
17
- * ‎@Provide(TABLE_DEF, () => driver.getTable(MyType))
18
- * ‎@TableController(MyType)
19
- * export class MyController extends AsDbController<typeof MyType> {}
20
- * ```
14
+ * For write operations (insert, replace, update, delete), use {@link AsDbController}.
15
+ * For views, use {@link AsDbViewController}.
21
16
  */
22
- declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
23
- /** Reference to the underlying table. */
24
- protected table: AtscriptDbTable<T>;
17
+ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
18
+ /** Reference to the underlying readable (table or view). */
19
+ protected readable: AtscriptDbReadable<T>;
25
20
  /** Application-scoped logger. */
26
21
  protected logger: TConsoleBase;
27
22
  /** Cached serialized type definition (static, computed once). */
28
23
  private _serializedType;
29
24
  /** Cached search index list (static, computed once). */
30
25
  private _searchIndexes;
31
- constructor(table: AtscriptDbTable<T>, app: Moost);
26
+ constructor(readable: AtscriptDbReadable<T>, app: Moost);
32
27
  /**
33
28
  * One-time initialization hook. Override to seed data, register watchers, etc.
34
29
  */
@@ -50,14 +45,6 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
50
45
  * Transform projection before querying.
51
46
  */
52
47
  protected transformProjection(projection?: UniqueryControls['$select']): UniqueryControls['$select'] | undefined;
53
- /**
54
- * Intercepts write operations. Return `undefined` to abort.
55
- */
56
- protected onWrite(action: 'insert' | 'insertMany' | 'replace' | 'update', data: unknown): unknown | Promise<unknown | undefined>;
57
- /**
58
- * Intercepts delete operations. Return `undefined` to abort.
59
- */
60
- protected onRemove(id: unknown): unknown | Promise<unknown | undefined>;
61
48
  protected parseQueryString(url: string): _uniqu_url.UrlQuery;
62
49
  protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
63
50
  /**
@@ -78,6 +65,38 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
78
65
  * **GET /one/:id** — retrieves a single record by ID or unique property.
79
66
  */
80
67
  getOne(id: string, url: string): Promise<DataType | HttpError>;
68
+ /**
69
+ * **GET /meta** — returns table/view metadata for UI.
70
+ */
71
+ meta(): {
72
+ searchable: boolean;
73
+ searchIndexes: _atscript_utils_db.TSearchIndexInfo[];
74
+ type: _atscript_typescript_serialize.TSerializedAnnotatedType;
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Full CRUD database controller for Moost that works with any `AtscriptDbTable` +
80
+ * `BaseDbAdapter`. Extends {@link AsDbReadableController} with write operations.
81
+ *
82
+ * Subclass and provide the table via DI:
83
+ * ```ts
84
+ * ‎@TableController(usersTable)
85
+ * export class UsersController extends AsDbController<typeof UserModel> {}
86
+ * ```
87
+ */
88
+ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsDbReadableController<T, DataType> {
89
+ /** Reference to the underlying table (typed for write access). */
90
+ protected get table(): AtscriptDbTable<T>;
91
+ constructor(table: AtscriptDbTable<T>, app: Moost);
92
+ /**
93
+ * Intercepts write operations. Return `undefined` to abort.
94
+ */
95
+ protected onWrite(action: 'insert' | 'insertMany' | 'replace' | 'update', data: unknown): unknown | Promise<unknown | undefined>;
96
+ /**
97
+ * Intercepts delete operations. Return `undefined` to abort.
98
+ */
99
+ protected onRemove(id: unknown): unknown | Promise<unknown | undefined>;
81
100
  /**
82
101
  * **POST /** — inserts one or many records.
83
102
  */
@@ -94,21 +113,19 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
94
113
  * **DELETE /:id** — removes a single record by primary key.
95
114
  */
96
115
  remove(id: string): Promise<HttpError | unknown>;
97
- /**
98
- * **GET /meta** — returns table metadata for UI.
99
- */
100
- meta(): {
101
- searchable: boolean;
102
- searchIndexes: _atscript_utils_db.TSearchIndexInfo[];
103
- type: _atscript_typescript_serialize.TSerializedAnnotatedType;
104
- };
105
116
  }
106
117
 
118
+ /**
119
+ * DI token under which the {@link AtscriptDbReadable} instance
120
+ * is exposed to the readable controller's constructor via `@Inject`.
121
+ */
122
+ declare const READABLE_DEF = "__atscript_db_readable_def";
107
123
  /**
108
124
  * DI token under which the {@link AtscriptDbTable} instance
109
125
  * is exposed to the controller's constructor via `@Inject`.
126
+ * Points to the same token as READABLE_DEF for backward compatibility.
110
127
  */
111
- declare const TABLE_DEF = "__atscript_db_table_def";
128
+ declare const TABLE_DEF = "__atscript_db_readable_def";
112
129
  /**
113
130
  * Combines the boilerplate needed to turn an {@link AsDbController}
114
131
  * subclass into a fully wired HTTP controller for a given `@db.table` model.
@@ -130,8 +147,32 @@ declare const TABLE_DEF = "__atscript_db_table_def";
130
147
  * ```
131
148
  */
132
149
  declare const TableController: (table: AtscriptDbTable, prefix?: string) => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
150
+ /**
151
+ * Combines the boilerplate needed to turn an {@link AsDbReadableController}
152
+ * subclass into a fully wired HTTP controller for a given `@db.view` or `@db.table` model.
153
+ *
154
+ * @param readable The {@link AtscriptDbReadable} instance (table or view).
155
+ * @param prefix Optional route prefix. Defaults to `readable.tableName`.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * ‎@ReadableController(activeTasksView)
160
+ * export class ActiveTasksController extends AsDbReadableController<typeof ActiveTasks> {}
161
+ * ```
162
+ */
163
+ declare const ReadableController: (readable: AtscriptDbReadable, prefix?: string) => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
164
+ /**
165
+ * Alias for {@link ReadableController} — use with view-backed controllers.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * ‎@ViewController(activeTasksView)
170
+ * export class ActiveTasksController extends AsDbReadableController<typeof ActiveTasks> {}
171
+ * ```
172
+ */
173
+ declare const ViewController: (readable: AtscriptDbReadable, prefix?: string) => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
133
174
 
134
175
  declare const validationErrorTransform: () => moost.TInterceptorDef;
135
176
  declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorator;
136
177
 
137
- export { AsDbController, TABLE_DEF, TableController, UseValidationErrorTransform, validationErrorTransform };
178
+ export { AsDbController, AsDbReadableController, READABLE_DEF, ReadableController, TABLE_DEF, TableController, UseValidationErrorTransform, ViewController, validationErrorTransform };
package/dist/index.mjs CHANGED
@@ -4,8 +4,11 @@ import { ApplyDecorators, Controller, Inherit, Inject, Intercept, Moost, Param,
4
4
  import { parseUrl } from "@uniqu/url";
5
5
 
6
6
  //#region packages/moost-db/src/decorators.ts
7
- const TABLE_DEF = "__atscript_db_table_def";
7
+ const READABLE_DEF = "__atscript_db_readable_def";
8
+ const TABLE_DEF = READABLE_DEF;
8
9
  const TableController = (table, prefix) => ApplyDecorators(Provide(TABLE_DEF, () => table), Controller(prefix || table.tableName), Inherit());
10
+ const ReadableController = (readable, prefix) => ApplyDecorators(Provide(READABLE_DEF, () => readable), Controller(prefix || readable.tableName), Inherit());
11
+ const ViewController = ReadableController;
9
12
 
10
13
  //#endregion
11
14
  //#region packages/moost-db/src/validation-interceptor.ts
@@ -121,7 +124,7 @@ defineAnnotatedType("object", SortControlDto).propPattern(/./, defineAnnotatedTy
121
124
  defineAnnotatedType("object", SelectControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(0).$type).$type);
122
125
 
123
126
  //#endregion
124
- //#region packages/moost-db/src/as-db.controller.ts
127
+ //#region packages/moost-db/src/as-db-readable.controller.ts
125
128
  function _define_property(obj, key, value) {
126
129
  if (key in obj) Object.defineProperty(obj, key, {
127
130
  value,
@@ -132,21 +135,21 @@ function _define_property(obj, key, value) {
132
135
  else obj[key] = value;
133
136
  return obj;
134
137
  }
135
- function _ts_decorate(decorators, target, key, desc) {
138
+ function _ts_decorate$1(decorators, target, key, desc) {
136
139
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
137
140
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
138
141
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
139
142
  return c > 3 && r && Object.defineProperty(target, key, r), r;
140
143
  }
141
- function _ts_metadata(k, v) {
144
+ function _ts_metadata$1(k, v) {
142
145
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
143
146
  }
144
- function _ts_param(paramIndex, decorator) {
147
+ function _ts_param$1(paramIndex, decorator) {
145
148
  return function(target, key) {
146
149
  decorator(target, key, paramIndex);
147
150
  };
148
151
  }
149
- var AsDbController = class {
152
+ var AsDbReadableController = class {
150
153
  /**
151
154
  * One-time initialization hook. Override to seed data, register watchers, etc.
152
155
  */ init() {}
@@ -168,7 +171,7 @@ var AsDbController = class {
168
171
  return undefined;
169
172
  }
170
173
  validateInsights(insights) {
171
- for (const key of insights.keys()) if (!this.table.flatMap.has(key)) return `Unknown field "${key}"`;
174
+ for (const key of insights.keys()) if (!this.readable.flatMap.has(key)) return `Unknown field "${key}"`;
172
175
  return undefined;
173
176
  }
174
177
  validateParsed(parsed, type) {
@@ -190,16 +193,6 @@ var AsDbController = class {
190
193
  */ transformProjection(projection) {
191
194
  return projection;
192
195
  }
193
- /**
194
- * Intercepts write operations. Return `undefined` to abort.
195
- */ onWrite(action, data) {
196
- return data;
197
- }
198
- /**
199
- * Intercepts delete operations. Return `undefined` to abort.
200
- */ onRemove(id) {
201
- return id;
202
- }
203
196
  parseQueryString(url) {
204
197
  const idx = url.indexOf("?");
205
198
  return parseUrl(idx >= 0 ? url.slice(idx + 1) : "");
@@ -218,7 +211,7 @@ var AsDbController = class {
218
211
  const controls = parsed.controls;
219
212
  const filter = this.transformFilter(parsed.filter);
220
213
  const select = this.transformProjection(controls.$select);
221
- if (controls.$count) return this.table.count({
214
+ if (controls.$count) return this.readable.count({
222
215
  filter,
223
216
  controls: {
224
217
  ...controls,
@@ -227,7 +220,7 @@ var AsDbController = class {
227
220
  });
228
221
  const searchTerm = controls.$search;
229
222
  const indexName = controls.$index;
230
- if (searchTerm && this.table.isSearchable()) return this.table.search(searchTerm, {
223
+ if (searchTerm && this.readable.isSearchable()) return this.readable.search(searchTerm, {
231
224
  filter,
232
225
  controls: {
233
226
  ...controls,
@@ -235,7 +228,7 @@ var AsDbController = class {
235
228
  $limit: controls.$limit || 1e3
236
229
  }
237
230
  }, indexName);
238
- return this.table.findMany({
231
+ return this.readable.findMany({
239
232
  filter,
240
233
  controls: {
241
234
  ...controls,
@@ -268,8 +261,8 @@ var AsDbController = class {
268
261
  }
269
262
  };
270
263
  let result;
271
- if (searchTerm && this.table.isSearchable()) result = await this.table.searchWithCount(searchTerm, query, indexName);
272
- else result = await this.table.findManyWithCount(query);
264
+ if (searchTerm && this.readable.isSearchable()) result = await this.readable.searchWithCount(searchTerm, query, indexName);
265
+ else result = await this.readable.findManyWithCount(query);
273
266
  return {
274
267
  data: result.data,
275
268
  page,
@@ -286,7 +279,109 @@ else result = await this.table.findManyWithCount(query);
286
279
  const error = this.validateParsed(parsed, "getOne");
287
280
  if (error) return error;
288
281
  const select = this.transformProjection(parsed.controls.$select);
289
- return this.returnOne(this.table.findById(id, { $select: select }));
282
+ const controls = {
283
+ ...parsed.controls,
284
+ $select: select
285
+ };
286
+ return this.returnOne(this.readable.findById(id, { controls }));
287
+ }
288
+ /**
289
+ * **GET /meta** — returns table/view metadata for UI.
290
+ */ meta() {
291
+ return {
292
+ searchable: this.readable.isSearchable(),
293
+ searchIndexes: this._searchIndexes,
294
+ type: this._serializedType
295
+ };
296
+ }
297
+ constructor(readable, app) {
298
+ /** Reference to the underlying readable (table or view). */ _define_property(this, "readable", void 0);
299
+ /** Application-scoped logger. */ _define_property(this, "logger", void 0);
300
+ /** Cached serialized type definition (static, computed once). */ _define_property(this, "_serializedType", void 0);
301
+ /** Cached search index list (static, computed once). */ _define_property(this, "_searchIndexes", void 0);
302
+ _define_property(this, "_queryControlsValidator", void 0);
303
+ _define_property(this, "_pagesControlsValidator", void 0);
304
+ _define_property(this, "_getOneControlsValidator", void 0);
305
+ this.readable = readable;
306
+ this._serializedType = serializeAnnotatedType(readable.type);
307
+ this._searchIndexes = readable.getSearchIndexes();
308
+ this.logger = app.getLogger(`db [${readable.tableName}]`);
309
+ this.logger.info(`Initializing ${readable.isView ? "view" : "table"} controller`);
310
+ try {
311
+ const p = this.init();
312
+ if (p instanceof Promise) p.catch((error) => {
313
+ this.logger.error(error);
314
+ });
315
+ } catch (error) {
316
+ this.logger.error(error);
317
+ throw error;
318
+ }
319
+ }
320
+ };
321
+ _ts_decorate$1([
322
+ Get("query"),
323
+ _ts_param$1(0, Url()),
324
+ _ts_metadata$1("design:type", Function),
325
+ _ts_metadata$1("design:paramtypes", [String]),
326
+ _ts_metadata$1("design:returntype", Promise)
327
+ ], AsDbReadableController.prototype, "query", null);
328
+ _ts_decorate$1([
329
+ Get("pages"),
330
+ _ts_param$1(0, Url()),
331
+ _ts_metadata$1("design:type", Function),
332
+ _ts_metadata$1("design:paramtypes", [String]),
333
+ _ts_metadata$1("design:returntype", Promise)
334
+ ], AsDbReadableController.prototype, "pages", null);
335
+ _ts_decorate$1([
336
+ Get("one/:id"),
337
+ _ts_param$1(0, Param("id")),
338
+ _ts_param$1(1, Url()),
339
+ _ts_metadata$1("design:type", Function),
340
+ _ts_metadata$1("design:paramtypes", [String, String]),
341
+ _ts_metadata$1("design:returntype", Promise)
342
+ ], AsDbReadableController.prototype, "getOne", null);
343
+ _ts_decorate$1([
344
+ Get("meta"),
345
+ _ts_metadata$1("design:type", Function),
346
+ _ts_metadata$1("design:paramtypes", []),
347
+ _ts_metadata$1("design:returntype", void 0)
348
+ ], AsDbReadableController.prototype, "meta", null);
349
+ AsDbReadableController = _ts_decorate$1([
350
+ UseValidationErrorTransform(),
351
+ _ts_param$1(0, Inject(READABLE_DEF)),
352
+ _ts_metadata$1("design:type", Function),
353
+ _ts_metadata$1("design:paramtypes", [typeof AtscriptDbReadable === "undefined" ? Object : AtscriptDbReadable, typeof Moost === "undefined" ? Object : Moost])
354
+ ], AsDbReadableController);
355
+
356
+ //#endregion
357
+ //#region packages/moost-db/src/as-db.controller.ts
358
+ function _ts_decorate(decorators, target, key, desc) {
359
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
360
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
361
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
362
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
363
+ }
364
+ function _ts_metadata(k, v) {
365
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
366
+ }
367
+ function _ts_param(paramIndex, decorator) {
368
+ return function(target, key) {
369
+ decorator(target, key, paramIndex);
370
+ };
371
+ }
372
+ var AsDbController = class extends AsDbReadableController {
373
+ /** Reference to the underlying table (typed for write access). */ get table() {
374
+ return this.readable;
375
+ }
376
+ /**
377
+ * Intercepts write operations. Return `undefined` to abort.
378
+ */ onWrite(action, data) {
379
+ return data;
380
+ }
381
+ /**
382
+ * Intercepts delete operations. Return `undefined` to abort.
383
+ */ onRemove(id) {
384
+ return id;
290
385
  }
291
386
  /**
292
387
  * **POST /** — inserts one or many records.
@@ -324,61 +419,10 @@ else result = await this.table.findManyWithCount(query);
324
419
  if (result.deletedCount < 1) return new HttpError(404);
325
420
  return result;
326
421
  }
327
- /**
328
- * **GET /meta** — returns table metadata for UI.
329
- */ meta() {
330
- return {
331
- searchable: this.table.isSearchable(),
332
- searchIndexes: this._searchIndexes,
333
- type: this._serializedType
334
- };
335
- }
336
422
  constructor(table, app) {
337
- /** Reference to the underlying table. */ _define_property(this, "table", void 0);
338
- /** Application-scoped logger. */ _define_property(this, "logger", void 0);
339
- /** Cached serialized type definition (static, computed once). */ _define_property(this, "_serializedType", void 0);
340
- /** Cached search index list (static, computed once). */ _define_property(this, "_searchIndexes", void 0);
341
- _define_property(this, "_queryControlsValidator", void 0);
342
- _define_property(this, "_pagesControlsValidator", void 0);
343
- _define_property(this, "_getOneControlsValidator", void 0);
344
- this.table = table;
345
- this._serializedType = serializeAnnotatedType(table.type);
346
- this._searchIndexes = table.getSearchIndexes();
347
- this.logger = app.getLogger(`db [${table.tableName}]`);
348
- this.logger.info("Initializing table controller");
349
- try {
350
- const p = this.init();
351
- if (p instanceof Promise) p.catch((error) => {
352
- this.logger.error(error);
353
- });
354
- } catch (error) {
355
- this.logger.error(error);
356
- throw error;
357
- }
423
+ super(table, app);
358
424
  }
359
425
  };
360
- _ts_decorate([
361
- Get("query"),
362
- _ts_param(0, Url()),
363
- _ts_metadata("design:type", Function),
364
- _ts_metadata("design:paramtypes", [String]),
365
- _ts_metadata("design:returntype", Promise)
366
- ], AsDbController.prototype, "query", null);
367
- _ts_decorate([
368
- Get("pages"),
369
- _ts_param(0, Url()),
370
- _ts_metadata("design:type", Function),
371
- _ts_metadata("design:paramtypes", [String]),
372
- _ts_metadata("design:returntype", Promise)
373
- ], AsDbController.prototype, "pages", null);
374
- _ts_decorate([
375
- Get("one/:id"),
376
- _ts_param(0, Param("id")),
377
- _ts_param(1, Url()),
378
- _ts_metadata("design:type", Function),
379
- _ts_metadata("design:paramtypes", [String, String]),
380
- _ts_metadata("design:returntype", Promise)
381
- ], AsDbController.prototype, "getOne", null);
382
426
  _ts_decorate([
383
427
  Post(""),
384
428
  _ts_param(0, Body()),
@@ -407,18 +451,12 @@ _ts_decorate([
407
451
  _ts_metadata("design:paramtypes", [String]),
408
452
  _ts_metadata("design:returntype", Promise)
409
453
  ], AsDbController.prototype, "remove", null);
410
- _ts_decorate([
411
- Get("meta"),
412
- _ts_metadata("design:type", Function),
413
- _ts_metadata("design:paramtypes", []),
414
- _ts_metadata("design:returntype", void 0)
415
- ], AsDbController.prototype, "meta", null);
416
454
  AsDbController = _ts_decorate([
417
- UseValidationErrorTransform(),
455
+ Inherit(),
418
456
  _ts_param(0, Inject(TABLE_DEF)),
419
457
  _ts_metadata("design:type", Function),
420
458
  _ts_metadata("design:paramtypes", [typeof AtscriptDbTable === "undefined" ? Object : AtscriptDbTable, typeof Moost === "undefined" ? Object : Moost])
421
459
  ], AsDbController);
422
460
 
423
461
  //#endregion
424
- export { AsDbController, TABLE_DEF, TableController, UseValidationErrorTransform, validationErrorTransform };
462
+ export { AsDbController, AsDbReadableController, READABLE_DEF, ReadableController, TABLE_DEF, TableController, UseValidationErrorTransform, ViewController, validationErrorTransform };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/moost-db",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Generic database controller for Moost with Atscript.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -13,7 +13,7 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/moostjs/atscript/issues"
15
15
  },
16
- "license": "ISC",
16
+ "license": "MIT",
17
17
  "author": "Artem Maltsev",
18
18
  "repository": {
19
19
  "type": "git",
@@ -35,20 +35,20 @@
35
35
  "./package.json": "./package.json"
36
36
  },
37
37
  "dependencies": {
38
- "@uniqu/url": "^0.0.4"
38
+ "@uniqu/url": "^0.0.5"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@moostjs/event-http": "^0.6.2",
42
42
  "moost": "^0.6.2",
43
43
  "vitest": "3.2.4",
44
- "@atscript/core": "^0.1.34"
44
+ "@atscript/core": "^0.1.35"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@moostjs/event-http": "^0.6.2",
48
48
  "moost": "^0.6.2",
49
- "@uniqu/core": "^0.0.4",
50
- "@atscript/utils-db": "^0.1.34",
51
- "@atscript/typescript": "^0.1.34"
49
+ "@uniqu/core": "^0.0.5",
50
+ "@atscript/utils-db": "^0.1.35",
51
+ "@atscript/typescript": "^0.1.35"
52
52
  },
53
53
  "scripts": {
54
54
  "pub": "pnpm publish --access public",