@travetto/model-sql 6.0.1 → 7.0.0-rc.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-sql",
3
- "version": "6.0.1",
3
+ "version": "7.0.0-rc.0",
4
4
  "description": "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
5
5
  "keywords": [
6
6
  "sql",
@@ -27,14 +27,14 @@
27
27
  "directory": "module/model-sql"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/cli": "^6.0.1",
31
- "@travetto/config": "^6.0.1",
32
- "@travetto/context": "^6.0.1",
33
- "@travetto/model": "^6.0.1",
34
- "@travetto/model-query": "^6.0.1"
30
+ "@travetto/cli": "^7.0.0-rc.0",
31
+ "@travetto/config": "^7.0.0-rc.0",
32
+ "@travetto/context": "^7.0.0-rc.0",
33
+ "@travetto/model": "^7.0.0-rc.0",
34
+ "@travetto/model-query": "^7.0.0-rc.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@travetto/test": "^6.0.2"
37
+ "@travetto/test": "^7.0.0-rc.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@travetto/test": {
@@ -10,6 +10,7 @@ export interface ConnectionAware<C = unknown> {
10
10
 
11
11
  /**
12
12
  * Decorator to ensure a method runs with a valid connection
13
+ * @kind decorator
13
14
  */
14
15
  export function Connected() {
15
16
  return function <T extends { conn?: Connection }>(
@@ -24,6 +25,7 @@ export function Connected() {
24
25
 
25
26
  /**
26
27
  * Decorator to ensure a method runs with a valid connection
28
+ * @kind decorator
27
29
  */
28
30
  export function ConnectedIterator() {
29
31
  return function <T extends { conn?: Connection }>(
@@ -38,6 +40,7 @@ export function ConnectedIterator() {
38
40
 
39
41
  /**
40
42
  * Decorator to ensure a method runs with a valid transaction
43
+ * @kind decorator
41
44
  */
42
45
  export function Transactional(mode: TransactionType = 'required') {
43
46
  return function <T extends { conn?: Connection }>(
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @stylistic/indent */
2
- import { DataUtil, SchemaRegistry, FieldConfig, Schema, type Point } from '@travetto/schema';
2
+ import { DataUtil, SchemaFieldConfig, Schema, SchemaRegistryIndex, type Point } from '@travetto/schema';
3
3
  import { Class, AppError, TypedObject, TimeUtil, castTo, castKey, toConcrete } from '@travetto/runtime';
4
- import { SelectClause, Query, SortClause, WhereClause, RetainFields, ModelQueryUtil } from '@travetto/model-query';
4
+ import { SelectClause, Query, SortClause, WhereClause, RetainQueryPrimitiveFields, ModelQueryUtil } from '@travetto/model-query';
5
5
  import { BulkResponse, IndexConfig, ModelType } from '@travetto/model';
6
6
 
7
7
  import { SQLModelUtil } from '../util.ts';
@@ -21,9 +21,10 @@ class Total {
21
21
  total: number;
22
22
  }
23
23
 
24
- function makeField(name: string, type: Class, required: boolean, extra: Partial<FieldConfig>): FieldConfig {
24
+ function makeField(name: string, type: Class, required: boolean, extra: Partial<SchemaFieldConfig>): SchemaFieldConfig {
25
25
  return {
26
26
  name,
27
+ owner: null!,
27
28
  type,
28
29
  array: false,
29
30
  ...(required ? { required: { active: true } } : {}),
@@ -98,6 +99,8 @@ export abstract class SQLDialect implements DialectState {
98
99
  DECIMAL: (d, p) => `DECIMAL(${d},${p})`
99
100
  };
100
101
 
102
+ ID_AFFIX = '`';
103
+
101
104
  /**
102
105
  * Generate an id field
103
106
  */
@@ -160,7 +163,14 @@ export abstract class SQLDialect implements DialectState {
160
163
  /**
161
164
  * Identify a name or field (escape it)
162
165
  */
163
- abstract ident(name: string | FieldConfig): string;
166
+ ident(field: SchemaFieldConfig | string | symbol): string {
167
+ if (field === '*') {
168
+ return field;
169
+ } else {
170
+ const name = (typeof field === 'symbol' || typeof field === 'string') ? field : field.name;
171
+ return `${this.ID_AFFIX}${name.toString()}${this.ID_AFFIX}`;
172
+ }
173
+ }
164
174
 
165
175
  quote(text: string): string {
166
176
  return `'${text.replace(/[']/g, "''")}'`;
@@ -179,7 +189,7 @@ export abstract class SQLDialect implements DialectState {
179
189
  /**
180
190
  * Convert value to SQL valid representation
181
191
  */
182
- resolveValue(conf: FieldConfig, value: unknown): string {
192
+ resolveValue(conf: SchemaFieldConfig, value: unknown): string {
183
193
  if (value === undefined || value === null) {
184
194
  return 'NULL';
185
195
  } else if (conf.type === String) {
@@ -204,13 +214,13 @@ export abstract class SQLDialect implements DialectState {
204
214
  } else if (conf.type === Object) {
205
215
  return this.quote(JSON.stringify(value).replace(/[']/g, "''"));
206
216
  }
207
- throw new AppError(`Unknown value type for field ${conf.name}, ${value}`, { category: 'data' });
217
+ throw new AppError(`Unknown value type for field ${conf.name.toString()}, ${value}`, { category: 'data' });
208
218
  }
209
219
 
210
220
  /**
211
221
  * Get column type from field config
212
222
  */
213
- getColumnType(conf: FieldConfig): string {
223
+ getColumnType(conf: SchemaFieldConfig): string {
214
224
  let type: string = '';
215
225
 
216
226
  if (conf.type === Number) {
@@ -257,12 +267,12 @@ export abstract class SQLDialect implements DialectState {
257
267
  /**
258
268
  * FieldConfig to Column definition
259
269
  */
260
- getColumnDefinition(conf: FieldConfig): string | undefined {
270
+ getColumnDefinition(conf: SchemaFieldConfig): string | undefined {
261
271
  const type = this.getColumnType(conf);
262
272
  if (!type) {
263
273
  return;
264
274
  }
265
- return `${this.ident(conf)} ${type} ${(conf.required && conf.required.active) ? 'NOT NULL' : 'DEFAULT NULL'}`;
275
+ return `${this.ident(conf)} ${type} ${(conf.required?.active !== false) ? 'NOT NULL' : 'DEFAULT NULL'}`;
266
276
  }
267
277
 
268
278
  /**
@@ -294,7 +304,7 @@ export abstract class SQLDialect implements DialectState {
294
304
  * Add a sql column
295
305
  */
296
306
  getAddColumnSQL(stack: VisitStack[]): string {
297
- const field: FieldConfig = castTo(stack.at(-1));
307
+ const field: SchemaFieldConfig = castTo(stack.at(-1));
298
308
  return `ALTER TABLE ${this.parentTable(stack)} ADD COLUMN ${this.getColumnDefinition(field)};`;
299
309
  }
300
310
 
@@ -341,7 +351,7 @@ export abstract class SQLDialect implements DialectState {
341
351
  /**
342
352
  * Alias a field for usage
343
353
  */
344
- alias(field: string | FieldConfig, alias: string = this.rootAlias): string {
354
+ alias(field: string | symbol | SchemaFieldConfig, alias: string = this.rootAlias): string {
345
355
  return `${alias}.${this.ident(field)}`;
346
356
  }
347
357
 
@@ -358,7 +368,7 @@ export abstract class SQLDialect implements DialectState {
358
368
  const clauses = new Map<string, Alias>();
359
369
  let idx = 0;
360
370
 
361
- SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
371
+ SQLModelUtil.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
362
372
  onRoot: ({ descend, path }) => {
363
373
  const table = resolve(path);
364
374
  clauses.set(table, { alias: this.rootAlias, path });
@@ -366,12 +376,12 @@ export abstract class SQLDialect implements DialectState {
366
376
  },
367
377
  onSub: ({ descend, config, path }) => {
368
378
  const table = resolve(path);
369
- clauses.set(table, { alias: `${config.name.charAt(0)}${idx++}`, path });
379
+ clauses.set(table, { alias: `${config.name.toString().charAt(0)}${idx++}`, path });
370
380
  return descend();
371
381
  },
372
382
  onSimple: ({ config, path }) => {
373
383
  const table = resolve(path);
374
- clauses.set(table, { alias: `${config.name.charAt(0)}${idx++}`, path });
384
+ clauses.set(table, { alias: `${config.name.toString().charAt(0)}${idx++}`, path });
375
385
  }
376
386
  });
377
387
 
@@ -406,10 +416,11 @@ export abstract class SQLDialect implements DialectState {
406
416
  throw new Error(`Unknown field: ${key}`);
407
417
  }
408
418
  const sStack = [...stack, field];
409
- if (key in foreignMap && field.array && !SchemaRegistry.has(field.type)) {
419
+ if (key in foreignMap && field.array && !SchemaRegistryIndex.has(field.type)) {
410
420
  // If dealing with simple external
411
421
  sStack.push({
412
422
  name: field.name,
423
+ owner: null!,
413
424
  type: field.type
414
425
  });
415
426
  }
@@ -633,9 +644,9 @@ ${this.getLimitSQL(cls, query)}`;
633
644
  const config = stack.at(-1)!;
634
645
  const parent = stack.length > 1;
635
646
  const array = parent && config.array;
636
- const fields = SchemaRegistry.has(config.type) ?
647
+ const fields = SchemaRegistryIndex.has(config.type) ?
637
648
  [...SQLModelUtil.getFieldsByLocation(stack).local] :
638
- (array ? [castTo<FieldConfig>(config)] : []);
649
+ (array ? [castTo<SchemaFieldConfig>(config)] : []);
639
650
 
640
651
  if (!parent) {
641
652
  let idField = fields.find(x => x.name === this.idField.name);
@@ -688,7 +699,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
688
699
  */
689
700
  getCreateAllTablesSQL(cls: Class): string[] {
690
701
  const out: string[] = [];
691
- SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
702
+ SQLModelUtil.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
692
703
  onRoot: ({ path, descend }) => { out.push(this.getCreateTableSQL(path)); descend(); },
693
704
  onSub: ({ path, descend }) => { out.push(this.getCreateTableSQL(path)); descend(); },
694
705
  onSimple: ({ path }) => out.push(this.getCreateTableSQL(path))
@@ -727,7 +738,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
727
738
  */
728
739
  getDropAllTablesSQL<T extends ModelType>(cls: Class<T>): string[] {
729
740
  const out: string[] = [];
730
- SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
741
+ SQLModelUtil.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
731
742
  onRoot: ({ path, descend }) => { descend(); out.push(this.getDropTableSQL(path)); },
732
743
  onSub: ({ path, descend }) => { descend(); out.push(this.getDropTableSQL(path)); },
733
744
  onSimple: ({ path }) => out.push(this.getDropTableSQL(path))
@@ -740,7 +751,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
740
751
  */
741
752
  getTruncateAllTablesSQL<T extends ModelType>(cls: Class<T>): string[] {
742
753
  const out: string[] = [];
743
- SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
754
+ SQLModelUtil.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
744
755
  onRoot: ({ path, descend }) => { descend(); out.push(this.getTruncateTableSQL(path)); },
745
756
  onSub: ({ path, descend }) => { descend(); out.push(this.getTruncateTableSQL(path)); },
746
757
  onSimple: ({ path }) => out.push(this.getTruncateTableSQL(path))
@@ -754,8 +765,8 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
754
765
  getInsertSQL(stack: VisitStack[], instances: InsertWrapper['records']): string | undefined {
755
766
  const config = stack.at(-1)!;
756
767
  const columns = SQLModelUtil.getFieldsByLocation(stack).local
757
- .filter(x => !SchemaRegistry.has(x.type))
758
- .toSorted((a, b) => a.name.localeCompare(b.name));
768
+ .filter(x => !SchemaRegistryIndex.has(x.type))
769
+ .toSorted((a, b) => a.name.toString().localeCompare(b.name.toString()));
759
770
  const columnNames = columns.map(c => c.name);
760
771
 
761
772
  const hasParent = stack.length > 1;
@@ -787,7 +798,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
787
798
  return;
788
799
  }
789
800
 
790
- const matrix = instances.map(inst => columns.map(c => this.resolveValue(c, castTo<Record<string, unknown>>(inst.value)[c.name])));
801
+ const matrix = instances.map(inst => columns.map(c => this.resolveValue(c, castTo<Record<string | symbol, unknown>>(inst.value)[c.name])));
791
802
 
792
803
  columnNames.push(this.pathField.name);
793
804
  if (hasParent) {
@@ -859,11 +870,11 @@ ${this.getWhereSQL(type, where)};`;
859
870
  /**
860
871
  * Get elements by ids
861
872
  */
862
- getSelectRowsByIdsSQL(stack: VisitStack[], ids: string[], select: FieldConfig[] = []): string {
873
+ getSelectRowsByIdsSQL(stack: VisitStack[], ids: string[], select: SchemaFieldConfig[] = []): string {
863
874
  const config = stack.at(-1)!;
864
875
  const orderBy = !config.array ?
865
876
  '' :
866
- `ORDER BY ${this.rootAlias}.${this.idxField.name} ASC`;
877
+ `ORDER BY ${this.rootAlias}.${this.idxField.name.toString()} ASC`;
867
878
 
868
879
  const idField = (stack.length > 1 ? this.parentPathField : this.idField);
869
880
 
@@ -888,10 +899,10 @@ ${this.getWhereSQL(cls, where!)}`;
888
899
  const stack: Record<string, unknown>[] = [];
889
900
  const selectStack: (SelectClause<T> | undefined)[] = [];
890
901
 
891
- const buildSet = (children: unknown[], field?: FieldConfig): Record<string, unknown> =>
902
+ const buildSet = (children: unknown[], field?: SchemaFieldConfig): Record<string, unknown> =>
892
903
  SQLModelUtil.collectDependents(this, stack.at(-1)!, children, field);
893
904
 
894
- await SQLModelUtil.visitSchema(SchemaRegistry.get(cls), {
905
+ await SQLModelUtil.visitSchema(SchemaRegistryIndex.getConfig(cls), {
895
906
  onRoot: async (config) => {
896
907
  const res = buildSet(items); // Already filtered by initial select query
897
908
  selectStack.push(select);
@@ -902,11 +913,11 @@ ${this.getWhereSQL(cls, where!)}`;
902
913
  const top = stack.at(-1)!;
903
914
  const ids = Object.keys(top);
904
915
  const selectTop = selectStack.at(-1)!;
905
- const fieldKey = castKey<RetainFields<T>>(config.name);
916
+ const fieldKey = castKey<RetainQueryPrimitiveFields<T>>(config.name);
906
917
  const subSelectTop: SelectClause<T> | undefined = castTo(selectTop?.[fieldKey]);
907
918
 
908
919
  // See if a selection exists at all
909
- const sel: FieldConfig[] = subSelectTop ? fields
920
+ const sel: SchemaFieldConfig[] = subSelectTop ? fields
910
921
  .filter(f => typeof subSelectTop === 'object' && subSelectTop[castTo<typeof fieldKey>(f.name)] === 1)
911
922
  : [];
912
923
 
@@ -990,10 +1001,10 @@ ${this.getWhereSQL(cls, where!)}`;
990
1001
 
991
1002
  await Promise.all([
992
1003
  ...upserts.filter(x => x.stack.length === 1).map(i =>
993
- this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string, string>>(v.value)[idx]))
1004
+ this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string | symbol, string>>(v.value)[idx]))
994
1005
  ),
995
1006
  ...updates.filter(x => x.stack.length === 1).map(i =>
996
- this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string, string>>(v.value)[idx]))
1007
+ this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string | symbol, string>>(v.value)[idx]))
997
1008
  ),
998
1009
  ]);
999
1010
  }
@@ -1,4 +1,4 @@
1
- import type { FieldConfig, ClassConfig } from '@travetto/schema';
1
+ import type { SchemaFieldConfig, SchemaClassConfig } from '@travetto/schema';
2
2
 
3
3
  import { VisitStack } from '../types.ts';
4
4
 
@@ -22,10 +22,10 @@ export interface DeleteWrapper {
22
22
  * Dialect state
23
23
  */
24
24
  export interface DialectState {
25
- pathField: FieldConfig;
26
- parentPathField: FieldConfig;
27
- idField: FieldConfig;
28
- idxField: FieldConfig;
25
+ pathField: SchemaFieldConfig;
26
+ parentPathField: SchemaFieldConfig;
27
+ idField: SchemaFieldConfig;
28
+ idxField: SchemaFieldConfig;
29
29
  }
30
30
 
31
31
  export type VisitState = { path: VisitStack[] };
@@ -35,7 +35,7 @@ export type VisitState = { path: VisitStack[] };
35
35
  */
36
36
  export interface VisitNode<R> {
37
37
  path: VisitStack[];
38
- fields: FieldConfig[];
38
+ fields: SchemaFieldConfig[];
39
39
  descend: () => R;
40
40
  }
41
41
 
@@ -58,7 +58,7 @@ export interface VisitInstanceNode<R> extends VisitNode<R> {
58
58
  * Visit handler
59
59
  */
60
60
  export interface VisitHandler<R, U extends VisitNode<R> = VisitNode<R>> {
61
- onRoot(config: U & { config: ClassConfig }): R;
62
- onSub(config: U & { config: FieldConfig }): R;
63
- onSimple(config: Omit<U, 'descend'> & { config: FieldConfig }): R;
61
+ onRoot(config: U & { config: SchemaClassConfig }): R;
62
+ onSub(config: U & { config: SchemaFieldConfig }): R;
63
+ onSimple(config: Omit<U, 'descend'> & { config: SchemaFieldConfig }): R;
64
64
  }
package/src/service.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ModelType,
3
3
  BulkOp, BulkResponse, ModelCrudSupport, ModelStorageSupport, ModelBulkSupport,
4
- NotFoundError, ModelRegistry, ExistsError, OptionalId, ModelIdSource,
4
+ NotFoundError, ModelRegistryIndex, ExistsError, OptionalId, ModelIdSource,
5
5
  ModelExpiryUtil, ModelCrudUtil, ModelStorageUtil, ModelBulkUtil,
6
6
  } from '@travetto/model';
7
7
  import { castTo, Class } from '@travetto/runtime';
@@ -243,7 +243,7 @@ export class SQLModelService implements
243
243
  async query<T extends ModelType>(cls: Class<T>, query: PageableModelQuery<T>): Promise<T[]> {
244
244
  await QueryVerifier.verify(cls, query);
245
245
  const { records: res } = await this.#exec<T>(this.#dialect.getQuerySQL(cls, query, ModelQueryUtil.getWhereClause(cls, query.where)));
246
- if (ModelRegistry.has(cls)) {
246
+ if (ModelRegistryIndex.has(cls)) {
247
247
  await this.#dialect.fetchDependents(cls, res, query && query.select);
248
248
  }
249
249
 
@@ -1,13 +1,14 @@
1
1
  import { AsyncContext, WithAsyncContext } from '@travetto/context';
2
- import { ModelRegistry } from '@travetto/model';
2
+ import { ModelRegistryIndex } from '@travetto/model';
3
3
  import { Class } from '@travetto/runtime';
4
4
  import { ChangeEvent } from '@travetto/registry';
5
5
  import { SchemaChange } from '@travetto/schema';
6
6
 
7
7
  import { Connected, Transactional } from './connection/decorator.ts';
8
8
  import { SQLDialect } from './dialect/base.ts';
9
- import { SQLModelUtil, VisitStack } from './util.ts';
9
+ import { SQLModelUtil } from './util.ts';
10
10
  import { Connection } from './connection/base.ts';
11
+ import { VisitStack } from './types.ts';
11
12
 
12
13
  /**
13
14
  * Manage creation/updating of all tables
@@ -34,7 +35,7 @@ export class TableManager {
34
35
  for (const op of this.#dialect.getCreateAllTablesSQL(cls)) {
35
36
  out.push(op);
36
37
  }
37
- const indices = ModelRegistry.get(cls).indices;
38
+ const indices = ModelRegistryIndex.getConfig(cls).indices;
38
39
  if (indices) {
39
40
  for (const op of this.#dialect.getCreateAllIndicesSQL(cls, indices)) {
40
41
  out.push(op);
@@ -53,7 +54,7 @@ export class TableManager {
53
54
  for (const op of this.#dialect.getCreateAllTablesSQL(cls)) {
54
55
  await this.#exec(op);
55
56
  }
56
- const indices = ModelRegistry.get(cls).indices;
57
+ const indices = ModelRegistryIndex.getConfig(cls).indices;
57
58
  if (indices) {
58
59
  for (const op of this.#dialect.getCreateAllIndicesSQL(cls, indices)) {
59
60
  try {
package/src/types.ts CHANGED
@@ -6,6 +6,6 @@ export type VisitStack = {
6
6
  [TableSymbol]?: string;
7
7
  array?: boolean;
8
8
  type: Class;
9
- name: string;
9
+ name: string | symbol;
10
10
  index?: number;
11
11
  };
package/src/util.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  import { castKey, castTo, Class, TypedObject } from '@travetto/runtime';
2
2
  import { SelectClause, SortClause } from '@travetto/model-query';
3
- import { ModelRegistry, ModelType, OptionalId } from '@travetto/model';
4
- import { SchemaRegistry, ClassConfig, FieldConfig, DataUtil } from '@travetto/schema';
3
+ import { ModelRegistryIndex, ModelType, OptionalId } from '@travetto/model';
4
+ import { SchemaClassConfig, SchemaFieldConfig, DataUtil, SchemaRegistryIndex } from '@travetto/schema';
5
5
 
6
6
  import { DialectState, InsertWrapper, VisitHandler, VisitState, VisitInstanceNode, OrderBy } from './internal/types.ts';
7
7
  import { TableSymbol, VisitStack } from './types.ts';
8
8
 
9
9
  type FieldCacheEntry = {
10
- local: FieldConfig[];
11
- localMap: Record<string, FieldConfig>;
12
- foreign: FieldConfig[];
13
- foreignMap: Record<string, FieldConfig>;
10
+ local: SchemaFieldConfig[];
11
+ localMap: Record<string | symbol, SchemaFieldConfig>;
12
+ foreign: SchemaFieldConfig[];
13
+ foreignMap: Record<string | symbol, SchemaFieldConfig>;
14
14
  };
15
15
 
16
16
  /**
@@ -54,33 +54,32 @@ export class SQLModelUtil {
54
54
  */
55
55
  static getFieldsByLocation(stack: VisitStack[]): FieldCacheEntry {
56
56
  const top = stack.at(-1)!;
57
- const cls = SchemaRegistry.get(top.type);
57
+ const conf = SchemaRegistryIndex.has(top.type) ? SchemaRegistryIndex.getConfig(top.type) : undefined;
58
58
 
59
- if (cls && this.#schemaFieldsCache.has(cls.class)) {
60
- return this.#schemaFieldsCache.get(cls.class)!;
59
+ if (conf && this.#schemaFieldsCache.has(conf.class)) {
60
+ return this.#schemaFieldsCache.get(conf.class)!;
61
61
  }
62
62
 
63
- if (!cls) { // If a simple type, it is it's own field
64
- const field: FieldConfig = castTo({ ...top });
63
+ if (!conf) { // If a simple type, it is it's own field
64
+ const field: SchemaFieldConfig = castTo({ ...top });
65
65
  return {
66
66
  local: [field], localMap: { [field.name]: field },
67
67
  foreign: [], foreignMap: {}
68
68
  };
69
69
  }
70
70
 
71
- const model = ModelRegistry.get(cls.class)!;
72
- const conf = cls.totalView;
73
- const fields = conf.fields.map(x => ({ ...conf.schema[x] }));
71
+ const hasModel = ModelRegistryIndex.has(conf.class)!;
72
+ const fields = Object.values(conf.fields).map(field => ({ ...field }));
74
73
 
75
74
  // Polymorphic
76
- if (model && (model.baseType ?? model.subType)) {
75
+ if (hasModel && conf.discriminatedBase) {
77
76
  const fieldMap = new Set(fields.map(f => f.name));
78
- for (const type of ModelRegistry.getClassesByBaseType(ModelRegistry.getBaseModel(cls.class))) {
79
- const typeConf = SchemaRegistry.get(type).totalView;
80
- for (const f of typeConf.fields) {
81
- if (!fieldMap.has(f)) {
82
- fieldMap.add(f);
83
- fields.push({ ...typeConf.schema[f], required: { active: false } });
77
+ for (const type of SchemaRegistryIndex.getDiscriminatedClasses(conf.class)) {
78
+ const typeConf = SchemaRegistryIndex.getConfig(type);
79
+ for (const [fieldName, field] of Object.entries<SchemaFieldConfig>(typeConf.fields)) {
80
+ if (!fieldMap.has(fieldName)) {
81
+ fieldMap.add(fieldName);
82
+ fields.push({ ...field, required: { active: false } });
84
83
  }
85
84
  }
86
85
  }
@@ -89,14 +88,14 @@ export class SQLModelUtil {
89
88
  const ret: FieldCacheEntry = {
90
89
  localMap: {},
91
90
  foreignMap: {},
92
- local: fields.filter(x => !SchemaRegistry.has(x.type) && !x.array),
93
- foreign: fields.filter(x => SchemaRegistry.has(x.type) || x.array)
91
+ local: fields.filter(x => !SchemaRegistryIndex.has(x.type) && !x.array),
92
+ foreign: fields.filter(x => SchemaRegistryIndex.has(x.type) || x.array)
94
93
  };
95
94
 
96
95
  ret.local.reduce((acc, f) => (acc[f.name] = f) && acc, ret.localMap);
97
96
  ret.foreign.reduce((acc, f) => (acc[f.name] = f) && acc, ret.foreignMap);
98
97
 
99
- this.#schemaFieldsCache.set(cls.class, ret);
98
+ this.#schemaFieldsCache.set(conf.class, ret);
100
99
 
101
100
  return ret;
102
101
  }
@@ -104,13 +103,13 @@ export class SQLModelUtil {
104
103
  /**
105
104
  * Process a schema structure, synchronously
106
105
  */
107
- static visitSchemaSync(config: ClassConfig | FieldConfig, handler: VisitHandler<void>, state: VisitState = { path: [] }): void {
106
+ static visitSchemaSync(config: SchemaClassConfig | SchemaFieldConfig, handler: VisitHandler<void>, state: VisitState = { path: [] }): void {
108
107
  const path = 'class' in config ? this.classToStack(config.class) : [...state.path, config];
109
108
  const { local: fields, foreign } = this.getFieldsByLocation(path);
110
109
 
111
110
  const descend = (): void => {
112
111
  for (const field of foreign) {
113
- if (SchemaRegistry.has(field.type)) {
112
+ if (SchemaRegistryIndex.has(field.type)) {
114
113
  this.visitSchemaSync(field, handler, { path });
115
114
  } else {
116
115
  handler.onSimple({
@@ -132,13 +131,13 @@ export class SQLModelUtil {
132
131
  /**
133
132
  * Visit a Schema structure
134
133
  */
135
- static async visitSchema(config: ClassConfig | FieldConfig, handler: VisitHandler<Promise<void>>, state: VisitState = { path: [] }): Promise<void> {
134
+ static async visitSchema(config: SchemaClassConfig | SchemaFieldConfig, handler: VisitHandler<Promise<void>>, state: VisitState = { path: [] }): Promise<void> {
136
135
  const path = 'class' in config ? this.classToStack(config.class) : [...state.path, config];
137
136
  const { local: fields, foreign } = this.getFieldsByLocation(path);
138
137
 
139
138
  const descend = async (): Promise<void> => {
140
139
  for (const field of foreign) {
141
- if (SchemaRegistry.has(field.type)) {
140
+ if (SchemaRegistryIndex.has(field.type)) {
142
141
  await this.visitSchema(field, handler, { path });
143
142
  } else {
144
143
  await handler.onSimple({
@@ -162,7 +161,7 @@ export class SQLModelUtil {
162
161
  */
163
162
  static visitSchemaInstance<T extends ModelType>(cls: Class<T>, instance: T | OptionalId<T>, handler: VisitHandler<unknown, VisitInstanceNode<unknown>>): void {
164
163
  const pathObj: unknown[] = [instance];
165
- this.visitSchemaSync(SchemaRegistry.get(cls), {
164
+ this.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
166
165
  onRoot: (config) => {
167
166
  const { path } = config;
168
167
  path[0].name = instance.id!;
@@ -171,7 +170,7 @@ export class SQLModelUtil {
171
170
  },
172
171
  onSub: (config) => {
173
172
  const { config: field } = config;
174
- const topObj: Record<string, unknown> = castTo(pathObj.at(-1));
173
+ const topObj: Record<string | symbol, unknown> = castTo(pathObj.at(-1));
175
174
  const top = config.path.at(-1)!;
176
175
 
177
176
  if (field.name in topObj) {
@@ -199,7 +198,7 @@ export class SQLModelUtil {
199
198
  },
200
199
  onSimple: (config) => {
201
200
  const { config: field } = config;
202
- const topObj: Record<string, unknown> = castTo(pathObj.at(-1));
201
+ const topObj: Record<string | symbol, unknown> = castTo(pathObj.at(-1));
203
202
  const value = topObj[field.name];
204
203
  return handler.onSimple({ ...config, value });
205
204
  }
@@ -209,7 +208,7 @@ export class SQLModelUtil {
209
208
  /**
210
209
  * Get list of selected fields
211
210
  */
212
- static select<T>(cls: Class<T>, select?: SelectClause<T>): FieldConfig[] {
211
+ static select<T>(cls: Class<T>, select?: SelectClause<T>): SchemaFieldConfig[] {
213
212
  if (!select || Object.keys(select).length === 0) {
214
213
  return [{ type: cls, name: '*', owner: cls, array: false }];
215
214
  }
@@ -222,7 +221,7 @@ export class SQLModelUtil {
222
221
  if (typeof k === 'string' && !DataUtil.isPlainObject(select[k]) && localMap[k]) {
223
222
  if (!v) {
224
223
  if (toGet.size === 0) {
225
- toGet = new Set(SchemaRegistry.get(cls).totalView.fields);
224
+ toGet = new Set(Object.keys(SchemaRegistryIndex.getConfig(cls).fields));
226
225
  }
227
226
  toGet.delete(k);
228
227
  } else {
@@ -238,19 +237,19 @@ export class SQLModelUtil {
238
237
  */
239
238
  static orderBy<T>(cls: Class<T>, sort: SortClause<T>[]): OrderBy[] {
240
239
  return sort.map((cl: Record<string, unknown>) => {
241
- let schema: ClassConfig = SchemaRegistry.get(cls);
240
+ let schema: SchemaClassConfig = SchemaRegistryIndex.getConfig(cls);
242
241
  const stack = this.classToStack(cls);
243
242
  let found: OrderBy | undefined;
244
243
  while (!found) {
245
244
  const key = Object.keys(cl)[0];
246
245
  const val = cl[key];
247
- const field = { ...schema.totalView.schema[key] };
246
+ const field = { ...schema.fields[key] };
248
247
  if (DataUtil.isPrimitive(val)) {
249
248
  stack.push(field);
250
249
  found = { stack, asc: val === 1 };
251
250
  } else {
252
251
  stack.push(field);
253
- schema = SchemaRegistry.get(field.type);
252
+ schema = SchemaRegistryIndex.getConfig(field.type);
254
253
  cl = castTo(val);
255
254
  }
256
255
  }
@@ -261,9 +260,9 @@ export class SQLModelUtil {
261
260
  /**
262
261
  * Find all dependent fields via child tables
263
262
  */
264
- static collectDependents<T>(dct: DialectState, parent: unknown, v: T[], field?: FieldConfig): Record<string, T> {
263
+ static collectDependents<T>(dct: DialectState, parent: unknown, v: T[], field?: SchemaFieldConfig): Record<string, T> {
265
264
  if (field) {
266
- const isSimple = SchemaRegistry.has(field.type);
265
+ const isSimple = SchemaRegistryIndex.has(field.type);
267
266
  for (const el of v) {
268
267
  const parentKey: string = castTo(el[castKey<T>(dct.parentPathField.name)]);
269
268
  const root = castTo<Record<string, Record<string, unknown>>>(parent)[parentKey];
@@ -296,7 +295,7 @@ export class SQLModelUtil {
296
295
  static buildTable(list: VisitStack[]): string {
297
296
  const top = list.at(-1)!;
298
297
  if (!top[TableSymbol]) {
299
- top[TableSymbol] = list.map((el, i) => i === 0 ? ModelRegistry.getStore(el.type) : el.name).join('_');
298
+ top[TableSymbol] = list.map((el, i) => i === 0 ? ModelRegistryIndex.getStoreName(el.type) : el.name).join('_');
300
299
  }
301
300
  return top[TableSymbol]!;
302
301
  }
@@ -305,7 +304,7 @@ export class SQLModelUtil {
305
304
  * Build property path for a table/field given the current stack
306
305
  */
307
306
  static buildPath(list: VisitStack[]): string {
308
- return list.map((el) => `${el.name}${el.index ? `[${el.index}]` : ''}`).join('.');
307
+ return list.map((el) => `${el.name.toString()}${el.index ? `[${el.index}]` : ''}`).join('.');
309
308
  }
310
309
 
311
310
  /**
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import { Schema, FieldConfig } from '@travetto/schema';
3
+ import { Schema, SchemaFieldConfig } from '@travetto/schema';
4
4
  import { Suite, Test } from '@travetto/test';
5
5
  import { castTo } from '@travetto/runtime';
6
6
  import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
@@ -67,9 +67,9 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
67
67
 
68
68
  const dct = await this.dialect;
69
69
  dct.resolveName = (stack: VisitStack[]) => {
70
- const field: FieldConfig = castTo(stack.at(-1));
71
- const parent: FieldConfig = castTo(stack.at(-2));
72
- return `${field.owner ? field.owner.name : parent.name}.${field.name}`;
70
+ const field: SchemaFieldConfig = castTo(stack.at(-1));
71
+ const parent: SchemaFieldConfig = castTo(stack.at(-2));
72
+ return `${field.owner ? field.owner.name.toString() : parent.name.toString()}.${field.name.toString()}`;
73
73
  };
74
74
 
75
75
  const qryStr = dct.getWhereGroupingSQL(WhereType, qry);
@@ -80,8 +80,8 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
80
80
  async testRegEx() {
81
81
  const dct = await this.dialect;
82
82
  dct.resolveName = (stack: VisitStack[]) => {
83
- const field: FieldConfig = castTo(stack.at(-1));
84
- return `${field.owner?.name}.${field.name}`;
83
+ const field: SchemaFieldConfig = castTo(stack.at(-1));
84
+ return `${field.owner?.name}.${field.name.toString()}`;
85
85
  };
86
86
 
87
87
  const out = dct.getWhereGroupingSQL(User, {