metal-orm 1.1.10 → 1.1.16
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/README.md +758 -769
- package/dist/index.cjs +106 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +106 -43
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/render.mjs +21 -12
- package/scripts/generate-entities/schema.mjs +209 -195
- package/scripts/generate-entities/tree-detection.mjs +159 -153
- package/scripts/inflection/pt-br.mjs +468 -468
- package/scripts/run-eslint.mjs +33 -34
- package/scripts/show-sql-seed.sql +102 -0
- package/scripts/show-sql.mjs +321 -325
- package/src/bulk/bulk-delete-executor.ts +3 -5
- package/src/bulk/bulk-insert-executor.ts +7 -7
- package/src/bulk/bulk-update-executor.ts +2 -2
- package/src/bulk/bulk-upsert-executor.ts +5 -7
- package/src/codegen/naming-strategy.ts +54 -54
- package/src/codegen/typescript.ts +482 -482
- package/src/core/ast/adapters.ts +1 -1
- package/src/core/ast/builders.ts +123 -123
- package/src/core/ast/expression-builders.ts +745 -745
- package/src/core/ast/expression-nodes.ts +304 -304
- package/src/core/ast/expression-visitor.ts +212 -212
- package/src/core/ast/expression.ts +20 -20
- package/src/core/ast/helpers.ts +14 -14
- package/src/core/ast/join.ts +18 -18
- package/src/core/ast/query.ts +200 -200
- package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/render-reference.test.ts +70 -70
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
- package/src/core/ddl/introspect/catalogs/index.ts +5 -5
- package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
- package/src/core/ddl/introspect/context.ts +15 -15
- package/src/core/ddl/introspect/functions/postgres.ts +35 -35
- package/src/core/ddl/introspect/mysql.ts +113 -36
- package/src/core/ddl/introspect/registry.ts +40 -40
- package/src/core/ddl/introspect/run-select.ts +35 -35
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/ddl/naming-strategy.ts +16 -16
- package/src/core/ddl/schema-dialect.ts +56 -56
- package/src/core/ddl/schema-diff.ts +234 -234
- package/src/core/ddl/schema-generator.ts +1 -1
- package/src/core/ddl/schema-introspect.ts +25 -25
- package/src/core/ddl/sql-writing.ts +171 -171
- package/src/core/dialect/abstract.ts +541 -541
- package/src/core/dialect/base/cte-compiler.ts +33 -33
- package/src/core/dialect/base/function-table-formatter.ts +103 -103
- package/src/core/dialect/base/groupby-compiler.ts +21 -21
- package/src/core/dialect/base/join-compiler.ts +25 -25
- package/src/core/dialect/base/orderby-compiler.ts +35 -35
- package/src/core/dialect/base/pagination-strategy.ts +32 -32
- package/src/core/dialect/base/returning-strategy.ts +57 -57
- package/src/core/dialect/base/sql-dialect.ts +294 -294
- package/src/core/dialect/dialect-factory.ts +91 -91
- package/src/core/dialect/mssql/functions.ts +120 -120
- package/src/core/dialect/mssql/index.ts +317 -317
- package/src/core/dialect/mysql/functions.ts +103 -103
- package/src/core/dialect/mysql/index.ts +105 -105
- package/src/core/dialect/postgres/functions.ts +108 -108
- package/src/core/dialect/postgres/index.ts +91 -91
- package/src/core/dialect/sqlite/functions.ts +129 -129
- package/src/core/dialect/sqlite/index.ts +67 -67
- package/src/core/driver/database-driver.ts +19 -19
- package/src/core/driver/mssql-driver.ts +23 -23
- package/src/core/driver/mysql-driver.ts +23 -23
- package/src/core/driver/postgres-driver.ts +23 -23
- package/src/core/driver/sqlite-driver.ts +23 -23
- package/src/core/execution/db-executor.ts +92 -92
- package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
- package/src/core/execution/executors/mssql-executor.ts +184 -184
- package/src/core/execution/executors/mysql-executor.ts +88 -88
- package/src/core/execution/executors/postgres-executor.ts +26 -26
- package/src/core/execution/executors/sqlite-executor.ts +42 -42
- package/src/core/execution/pooling/pool-types.ts +30 -30
- package/src/core/execution/pooling/pool.ts +275 -275
- package/src/core/functions/array.ts +35 -35
- package/src/core/functions/control-flow.ts +79 -79
- package/src/core/functions/datetime.ts +237 -237
- package/src/core/functions/json.ts +70 -70
- package/src/core/functions/numeric.ts +297 -297
- package/src/core/functions/text.ts +334 -334
- package/src/core/functions/types.ts +33 -33
- package/src/core/hydration/types.ts +51 -51
- package/src/core/sql/sql-operator-config.ts +39 -39
- package/src/core/sql/sql.ts +152 -152
- package/src/decorators/column-decorator.ts +87 -87
- package/src/decorators/decorator-metadata.ts +143 -143
- package/src/decorators/entity.ts +93 -93
- package/src/decorators/index.ts +39 -39
- package/src/decorators/relations.ts +231 -231
- package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
- package/src/decorators/transformers/transformer-decorators.ts +134 -134
- package/src/decorators/transformers/transformer-executor.ts +171 -171
- package/src/decorators/transformers/transformer-metadata.ts +73 -73
- package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
- package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
- package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
- package/src/decorators/validators/country-validator-registry.ts +64 -64
- package/src/decorators/validators/country-validators-decorators.ts +203 -203
- package/src/decorators/validators/country-validators.ts +105 -105
- package/src/decorators/validators/validator-metadata.ts +59 -59
- package/src/dto/dto-types.ts +226 -226
- package/src/dto/filter-types.ts +141 -141
- package/src/dto/index.ts +95 -95
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -37
- package/src/dto/openapi/generators/dto.ts +91 -91
- package/src/dto/openapi/generators/filter.ts +75 -75
- package/src/dto/openapi/generators/nested-dto.ts +618 -618
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -228
- package/src/dto/openapi/index.ts +12 -12
- package/src/dto/openapi/types.ts +101 -101
- package/src/dto/openapi/utilities.ts +95 -95
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -197
- package/src/index.ts +86 -86
- package/src/orm/als.ts +58 -58
- package/src/orm/domain-event-bus.ts +119 -119
- package/src/orm/entity-context.ts +86 -86
- package/src/orm/entity-materializer.ts +159 -159
- package/src/orm/entity-meta.ts +97 -97
- package/src/orm/entity-registry.ts +39 -39
- package/src/orm/entity-relation-cache.ts +39 -39
- package/src/orm/execute.ts +194 -194
- package/src/orm/execution-context.ts +18 -18
- package/src/orm/hydration-context.ts +26 -26
- package/src/orm/hydration.ts +112 -112
- package/src/orm/identity-map.ts +61 -61
- package/src/orm/interceptor-pipeline.ts +16 -16
- package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/orm-session.ts +620 -620
- package/src/orm/orm.ts +34 -34
- package/src/orm/pooled-executor-factory.ts +121 -121
- package/src/orm/query-logger.ts +40 -40
- package/src/orm/relation-change-processor.ts +420 -420
- package/src/orm/relations/belongs-to.ts +143 -143
- package/src/orm/relations/has-many.ts +204 -204
- package/src/orm/relations/has-one.ts +174 -174
- package/src/orm/relations/many-to-many.ts +288 -288
- package/src/orm/relations/morph-many.ts +156 -156
- package/src/orm/relations/morph-one.ts +151 -151
- package/src/orm/relations/morph-to.ts +162 -162
- package/src/orm/runtime-types.ts +92 -92
- package/src/orm/save-graph.ts +561 -561
- package/src/orm/transaction-runner.ts +24 -24
- package/src/orm/unit-of-work.ts +426 -426
- package/src/query/index.ts +69 -69
- package/src/query/target.ts +46 -46
- package/src/query-builder/column-selector.ts +81 -81
- package/src/query-builder/delete.ts +141 -141
- package/src/query-builder/insert-query-state.ts +149 -149
- package/src/query-builder/insert.ts +117 -117
- package/src/query-builder/query-ast-service.ts +287 -287
- package/src/query-builder/query-resolution.ts +78 -78
- package/src/query-builder/raw-column-parser.ts +38 -38
- package/src/query-builder/relation-filter-utils.ts +153 -153
- package/src/query-builder/relation-join-planner.ts +10 -10
- package/src/query-builder/relation-manager.ts +87 -87
- package/src/query-builder/relation-projection-helper.ts +102 -102
- package/src/query-builder/relation-types.ts +9 -9
- package/src/query-builder/relation-utils.ts +15 -15
- package/src/query-builder/select/cte-facet.ts +40 -40
- package/src/query-builder/select/from-facet.ts +80 -80
- package/src/query-builder/select/join-facet.ts +62 -62
- package/src/query-builder/select/predicate-facet.ts +104 -104
- package/src/query-builder/select/projection-facet.ts +70 -70
- package/src/query-builder/select/relation-facet.ts +81 -81
- package/src/query-builder/select/setop-facet.ts +36 -36
- package/src/query-builder/select-helpers.ts +1 -1
- package/src/query-builder/select-query-builder-deps.ts +130 -130
- package/src/query-builder/select-query-state.ts +207 -207
- package/src/query-builder/update.ts +151 -151
- package/src/schema/column-types.ts +322 -322
- package/src/schema/table-guards.ts +37 -37
- package/src/schema/table.ts +281 -281
- package/src/tree/index.ts +13 -13
- package/src/tree/nested-set-strategy.ts +545 -545
- package/src/tree/tree-decorator.ts +397 -397
- package/src/tree/tree-manager.ts +754 -754
- package/src/tree/tree-query.ts +427 -427
- package/src/tree/tree-types.ts +299 -299
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
import { HasManyCollection } from '../../schema/types.js';
|
|
2
|
-
import { EntityContext } from '../entity-context.js';
|
|
3
|
-
import { RelationKey } from '../runtime-types.js';
|
|
4
|
-
import { MorphManyRelation } from '../../schema/relation.js';
|
|
5
|
-
import { TableDef } from '../../schema/table.js';
|
|
6
|
-
import { EntityMeta, getHydrationRows } from '../entity-meta.js';
|
|
7
|
-
|
|
8
|
-
type Rows = Record<string, unknown>[];
|
|
9
|
-
|
|
10
|
-
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
|
-
|
|
12
|
-
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
|
-
for (const key of keys) {
|
|
14
|
-
Object.defineProperty(obj, key, {
|
|
15
|
-
value: obj[key],
|
|
16
|
-
writable: false,
|
|
17
|
-
configurable: false,
|
|
18
|
-
enumerable: false
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const hideWritable = (obj: object, keys: string[]): void => {
|
|
24
|
-
for (const key of keys) {
|
|
25
|
-
const value = obj[key as keyof typeof obj];
|
|
26
|
-
Object.defineProperty(obj, key, {
|
|
27
|
-
value,
|
|
28
|
-
writable: true,
|
|
29
|
-
configurable: true,
|
|
30
|
-
enumerable: false
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export class DefaultMorphManyCollection<TChild> implements HasManyCollection<TChild> {
|
|
36
|
-
private loaded = false;
|
|
37
|
-
private items: TChild[] = [];
|
|
38
|
-
private readonly added = new Set<TChild>();
|
|
39
|
-
private readonly removed = new Set<TChild>();
|
|
40
|
-
|
|
41
|
-
constructor(
|
|
42
|
-
private readonly ctx: EntityContext,
|
|
43
|
-
private readonly meta: EntityMeta<TableDef>,
|
|
44
|
-
private readonly root: unknown,
|
|
45
|
-
private readonly relationName: string,
|
|
46
|
-
private readonly relation: MorphManyRelation,
|
|
47
|
-
private readonly rootTable: TableDef,
|
|
48
|
-
private readonly loader: () => Promise<Map<string, Rows>>,
|
|
49
|
-
private readonly createEntity: (row: Record<string, unknown>) => TChild,
|
|
50
|
-
private readonly localKey: string
|
|
51
|
-
) {
|
|
52
|
-
hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
|
|
53
|
-
hideWritable(this, ['loaded', 'items', 'added', 'removed']);
|
|
54
|
-
this.hydrateFromCache();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async load(): Promise<TChild[]> {
|
|
58
|
-
if (this.loaded) return this.items;
|
|
59
|
-
const map = await this.loader();
|
|
60
|
-
const key = toKey((this.root as Record<string, unknown>)[this.localKey]);
|
|
61
|
-
const rows = map.get(key) ?? [];
|
|
62
|
-
this.items = rows.map(row => this.createEntity(row));
|
|
63
|
-
this.loaded = true;
|
|
64
|
-
return this.items;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getItems(): TChild[] {
|
|
68
|
-
return this.items;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get length(): number {
|
|
72
|
-
return this.items.length;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
[Symbol.iterator](): Iterator<TChild> {
|
|
76
|
-
return this.items[Symbol.iterator]();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
add(data: Partial<TChild>): TChild {
|
|
80
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
81
|
-
const childRow: Record<string, unknown> = {
|
|
82
|
-
...data,
|
|
83
|
-
[this.relation.idField]: keyValue,
|
|
84
|
-
[this.relation.typeField]: this.relation.typeValue
|
|
85
|
-
};
|
|
86
|
-
const entity = this.createEntity(childRow);
|
|
87
|
-
this.added.add(entity);
|
|
88
|
-
this.items.push(entity);
|
|
89
|
-
this.ctx.registerRelationChange(
|
|
90
|
-
this.root,
|
|
91
|
-
this.relationKey,
|
|
92
|
-
this.rootTable,
|
|
93
|
-
this.relationName,
|
|
94
|
-
this.relation,
|
|
95
|
-
{ kind: 'add', entity }
|
|
96
|
-
);
|
|
97
|
-
return entity;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
attach(entity: TChild): void {
|
|
101
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
102
|
-
(entity as Record<string, unknown>)[this.relation.idField] = keyValue;
|
|
103
|
-
(entity as Record<string, unknown>)[this.relation.typeField] = this.relation.typeValue;
|
|
104
|
-
this.ctx.markDirty(entity as object);
|
|
105
|
-
this.items.push(entity);
|
|
106
|
-
this.ctx.registerRelationChange(
|
|
107
|
-
this.root,
|
|
108
|
-
this.relationKey,
|
|
109
|
-
this.rootTable,
|
|
110
|
-
this.relationName,
|
|
111
|
-
this.relation,
|
|
112
|
-
{ kind: 'attach', entity }
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
remove(entity: TChild): void {
|
|
117
|
-
this.items = this.items.filter(item => item !== entity);
|
|
118
|
-
this.removed.add(entity);
|
|
119
|
-
this.ctx.registerRelationChange(
|
|
120
|
-
this.root,
|
|
121
|
-
this.relationKey,
|
|
122
|
-
this.rootTable,
|
|
123
|
-
this.relationName,
|
|
124
|
-
this.relation,
|
|
125
|
-
{ kind: 'remove', entity }
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
clear(): void {
|
|
130
|
-
for (const entity of [...this.items]) {
|
|
131
|
-
this.remove(entity);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private get relationKey(): RelationKey {
|
|
136
|
-
return `${this.rootTable.name}.${this.relationName}`;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private hydrateFromCache(): void {
|
|
140
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
141
|
-
if (keyValue === undefined || keyValue === null) return;
|
|
142
|
-
const rows = getHydrationRows(this.meta, this.relationName, keyValue);
|
|
143
|
-
if (!rows?.length) return;
|
|
144
|
-
this.items = rows.map(row => this.createEntity(row));
|
|
145
|
-
this.loaded = true;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
toJSON(): unknown[] {
|
|
149
|
-
return this.items.map(item => {
|
|
150
|
-
const entityWithToJSON = item as { toJSON?: () => unknown };
|
|
151
|
-
return typeof entityWithToJSON.toJSON === 'function'
|
|
152
|
-
? entityWithToJSON.toJSON()
|
|
153
|
-
: item;
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
1
|
+
import { HasManyCollection } from '../../schema/types.js';
|
|
2
|
+
import { EntityContext } from '../entity-context.js';
|
|
3
|
+
import { RelationKey } from '../runtime-types.js';
|
|
4
|
+
import { MorphManyRelation } from '../../schema/relation.js';
|
|
5
|
+
import { TableDef } from '../../schema/table.js';
|
|
6
|
+
import { EntityMeta, getHydrationRows } from '../entity-meta.js';
|
|
7
|
+
|
|
8
|
+
type Rows = Record<string, unknown>[];
|
|
9
|
+
|
|
10
|
+
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
|
+
|
|
12
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
|
+
for (const key of keys) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: obj[key],
|
|
16
|
+
writable: false,
|
|
17
|
+
configurable: false,
|
|
18
|
+
enumerable: false
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const hideWritable = (obj: object, keys: string[]): void => {
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const value = obj[key as keyof typeof obj];
|
|
26
|
+
Object.defineProperty(obj, key, {
|
|
27
|
+
value,
|
|
28
|
+
writable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
enumerable: false
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class DefaultMorphManyCollection<TChild> implements HasManyCollection<TChild> {
|
|
36
|
+
private loaded = false;
|
|
37
|
+
private items: TChild[] = [];
|
|
38
|
+
private readonly added = new Set<TChild>();
|
|
39
|
+
private readonly removed = new Set<TChild>();
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
private readonly ctx: EntityContext,
|
|
43
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
44
|
+
private readonly root: unknown,
|
|
45
|
+
private readonly relationName: string,
|
|
46
|
+
private readonly relation: MorphManyRelation,
|
|
47
|
+
private readonly rootTable: TableDef,
|
|
48
|
+
private readonly loader: () => Promise<Map<string, Rows>>,
|
|
49
|
+
private readonly createEntity: (row: Record<string, unknown>) => TChild,
|
|
50
|
+
private readonly localKey: string
|
|
51
|
+
) {
|
|
52
|
+
hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
|
|
53
|
+
hideWritable(this, ['loaded', 'items', 'added', 'removed']);
|
|
54
|
+
this.hydrateFromCache();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async load(): Promise<TChild[]> {
|
|
58
|
+
if (this.loaded) return this.items;
|
|
59
|
+
const map = await this.loader();
|
|
60
|
+
const key = toKey((this.root as Record<string, unknown>)[this.localKey]);
|
|
61
|
+
const rows = map.get(key) ?? [];
|
|
62
|
+
this.items = rows.map(row => this.createEntity(row));
|
|
63
|
+
this.loaded = true;
|
|
64
|
+
return this.items;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getItems(): TChild[] {
|
|
68
|
+
return this.items;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get length(): number {
|
|
72
|
+
return this.items.length;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[Symbol.iterator](): Iterator<TChild> {
|
|
76
|
+
return this.items[Symbol.iterator]();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
add(data: Partial<TChild>): TChild {
|
|
80
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
81
|
+
const childRow: Record<string, unknown> = {
|
|
82
|
+
...data,
|
|
83
|
+
[this.relation.idField]: keyValue,
|
|
84
|
+
[this.relation.typeField]: this.relation.typeValue
|
|
85
|
+
};
|
|
86
|
+
const entity = this.createEntity(childRow);
|
|
87
|
+
this.added.add(entity);
|
|
88
|
+
this.items.push(entity);
|
|
89
|
+
this.ctx.registerRelationChange(
|
|
90
|
+
this.root,
|
|
91
|
+
this.relationKey,
|
|
92
|
+
this.rootTable,
|
|
93
|
+
this.relationName,
|
|
94
|
+
this.relation,
|
|
95
|
+
{ kind: 'add', entity }
|
|
96
|
+
);
|
|
97
|
+
return entity;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
attach(entity: TChild): void {
|
|
101
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
102
|
+
(entity as Record<string, unknown>)[this.relation.idField] = keyValue;
|
|
103
|
+
(entity as Record<string, unknown>)[this.relation.typeField] = this.relation.typeValue;
|
|
104
|
+
this.ctx.markDirty(entity as object);
|
|
105
|
+
this.items.push(entity);
|
|
106
|
+
this.ctx.registerRelationChange(
|
|
107
|
+
this.root,
|
|
108
|
+
this.relationKey,
|
|
109
|
+
this.rootTable,
|
|
110
|
+
this.relationName,
|
|
111
|
+
this.relation,
|
|
112
|
+
{ kind: 'attach', entity }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
remove(entity: TChild): void {
|
|
117
|
+
this.items = this.items.filter(item => item !== entity);
|
|
118
|
+
this.removed.add(entity);
|
|
119
|
+
this.ctx.registerRelationChange(
|
|
120
|
+
this.root,
|
|
121
|
+
this.relationKey,
|
|
122
|
+
this.rootTable,
|
|
123
|
+
this.relationName,
|
|
124
|
+
this.relation,
|
|
125
|
+
{ kind: 'remove', entity }
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
clear(): void {
|
|
130
|
+
for (const entity of [...this.items]) {
|
|
131
|
+
this.remove(entity);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private get relationKey(): RelationKey {
|
|
136
|
+
return `${this.rootTable.name}.${this.relationName}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private hydrateFromCache(): void {
|
|
140
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
141
|
+
if (keyValue === undefined || keyValue === null) return;
|
|
142
|
+
const rows = getHydrationRows(this.meta, this.relationName, keyValue);
|
|
143
|
+
if (!rows?.length) return;
|
|
144
|
+
this.items = rows.map(row => this.createEntity(row));
|
|
145
|
+
this.loaded = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toJSON(): unknown[] {
|
|
149
|
+
return this.items.map(item => {
|
|
150
|
+
const entityWithToJSON = item as { toJSON?: () => unknown };
|
|
151
|
+
return typeof entityWithToJSON.toJSON === 'function'
|
|
152
|
+
? entityWithToJSON.toJSON()
|
|
153
|
+
: item;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
import { HasOneReferenceApi } from '../../schema/types.js';
|
|
2
|
-
import { EntityContext } from '../entity-context.js';
|
|
3
|
-
import { RelationKey } from '../runtime-types.js';
|
|
4
|
-
import { MorphOneRelation } from '../../schema/relation.js';
|
|
5
|
-
import { TableDef } from '../../schema/table.js';
|
|
6
|
-
import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
|
|
7
|
-
|
|
8
|
-
type Row = Record<string, unknown>;
|
|
9
|
-
|
|
10
|
-
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
|
-
|
|
12
|
-
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
|
-
for (const key of keys) {
|
|
14
|
-
Object.defineProperty(obj, key, {
|
|
15
|
-
value: obj[key],
|
|
16
|
-
writable: false,
|
|
17
|
-
configurable: false,
|
|
18
|
-
enumerable: false
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const hideWritable = (obj: object, keys: string[]): void => {
|
|
24
|
-
for (const key of keys) {
|
|
25
|
-
const value = obj[key as keyof typeof obj];
|
|
26
|
-
Object.defineProperty(obj, key, {
|
|
27
|
-
value,
|
|
28
|
-
writable: true,
|
|
29
|
-
configurable: true,
|
|
30
|
-
enumerable: false
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export class DefaultMorphOneReference<TChild extends object> implements HasOneReferenceApi<TChild> {
|
|
36
|
-
private loaded = false;
|
|
37
|
-
private current: TChild | null = null;
|
|
38
|
-
|
|
39
|
-
constructor(
|
|
40
|
-
private readonly ctx: EntityContext,
|
|
41
|
-
private readonly meta: EntityMeta<TableDef>,
|
|
42
|
-
private readonly root: unknown,
|
|
43
|
-
private readonly relationName: string,
|
|
44
|
-
private readonly relation: MorphOneRelation,
|
|
45
|
-
private readonly rootTable: TableDef,
|
|
46
|
-
private readonly loader: () => Promise<Map<string, Row>>,
|
|
47
|
-
private readonly createEntity: (row: Row) => TChild,
|
|
48
|
-
private readonly localKey: string
|
|
49
|
-
) {
|
|
50
|
-
hideInternal(this, [
|
|
51
|
-
'ctx', 'meta', 'root', 'relationName', 'relation',
|
|
52
|
-
'rootTable', 'loader', 'createEntity', 'localKey'
|
|
53
|
-
]);
|
|
54
|
-
hideWritable(this, ['loaded', 'current']);
|
|
55
|
-
this.populateFromHydrationCache();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async load(): Promise<TChild | null> {
|
|
59
|
-
if (this.loaded) return this.current;
|
|
60
|
-
const map = await this.loader();
|
|
61
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
62
|
-
if (keyValue === undefined || keyValue === null) {
|
|
63
|
-
this.loaded = true;
|
|
64
|
-
return this.current;
|
|
65
|
-
}
|
|
66
|
-
const row = map.get(toKey(keyValue));
|
|
67
|
-
this.current = row ? this.createEntity(row) : null;
|
|
68
|
-
this.loaded = true;
|
|
69
|
-
return this.current;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get(): TChild | null {
|
|
73
|
-
return this.current;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
set(data: Partial<TChild> | TChild | null): TChild | null {
|
|
77
|
-
if (data === null) {
|
|
78
|
-
return this.detachCurrent();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const entity = hasEntityMeta(data) ? (data as TChild) : this.createEntity(data as Row);
|
|
82
|
-
if (this.current && this.current !== entity) {
|
|
83
|
-
this.ctx.registerRelationChange(
|
|
84
|
-
this.root,
|
|
85
|
-
this.relationKey,
|
|
86
|
-
this.rootTable,
|
|
87
|
-
this.relationName,
|
|
88
|
-
this.relation,
|
|
89
|
-
{ kind: 'remove', entity: this.current }
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
this.assignMorphKeys(entity);
|
|
94
|
-
this.current = entity;
|
|
95
|
-
this.loaded = true;
|
|
96
|
-
|
|
97
|
-
this.ctx.registerRelationChange(
|
|
98
|
-
this.root,
|
|
99
|
-
this.relationKey,
|
|
100
|
-
this.rootTable,
|
|
101
|
-
this.relationName,
|
|
102
|
-
this.relation,
|
|
103
|
-
{ kind: 'attach', entity }
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
return entity;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
toJSON(): unknown {
|
|
110
|
-
if (!this.current) return null;
|
|
111
|
-
const entityWithToJSON = this.current as { toJSON?: () => unknown };
|
|
112
|
-
return typeof entityWithToJSON.toJSON === 'function'
|
|
113
|
-
? entityWithToJSON.toJSON()
|
|
114
|
-
: this.current;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private detachCurrent(): TChild | null {
|
|
118
|
-
const previous = this.current;
|
|
119
|
-
if (!previous) return null;
|
|
120
|
-
this.current = null;
|
|
121
|
-
this.loaded = true;
|
|
122
|
-
this.ctx.registerRelationChange(
|
|
123
|
-
this.root,
|
|
124
|
-
this.relationKey,
|
|
125
|
-
this.rootTable,
|
|
126
|
-
this.relationName,
|
|
127
|
-
this.relation,
|
|
128
|
-
{ kind: 'remove', entity: previous }
|
|
129
|
-
);
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private assignMorphKeys(entity: TChild): void {
|
|
134
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
135
|
-
(entity as Row)[this.relation.idField] = keyValue;
|
|
136
|
-
(entity as Row)[this.relation.typeField] = this.relation.typeValue;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private get relationKey(): RelationKey {
|
|
140
|
-
return `${this.rootTable.name}.${this.relationName}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private populateFromHydrationCache(): void {
|
|
144
|
-
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
145
|
-
if (keyValue === undefined || keyValue === null) return;
|
|
146
|
-
const row = getHydrationRecord(this.meta, this.relationName, keyValue);
|
|
147
|
-
if (!row) return;
|
|
148
|
-
this.current = this.createEntity(row);
|
|
149
|
-
this.loaded = true;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
1
|
+
import { HasOneReferenceApi } from '../../schema/types.js';
|
|
2
|
+
import { EntityContext } from '../entity-context.js';
|
|
3
|
+
import { RelationKey } from '../runtime-types.js';
|
|
4
|
+
import { MorphOneRelation } from '../../schema/relation.js';
|
|
5
|
+
import { TableDef } from '../../schema/table.js';
|
|
6
|
+
import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
|
|
7
|
+
|
|
8
|
+
type Row = Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
|
+
|
|
12
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
|
+
for (const key of keys) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: obj[key],
|
|
16
|
+
writable: false,
|
|
17
|
+
configurable: false,
|
|
18
|
+
enumerable: false
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const hideWritable = (obj: object, keys: string[]): void => {
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const value = obj[key as keyof typeof obj];
|
|
26
|
+
Object.defineProperty(obj, key, {
|
|
27
|
+
value,
|
|
28
|
+
writable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
enumerable: false
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class DefaultMorphOneReference<TChild extends object> implements HasOneReferenceApi<TChild> {
|
|
36
|
+
private loaded = false;
|
|
37
|
+
private current: TChild | null = null;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private readonly ctx: EntityContext,
|
|
41
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
42
|
+
private readonly root: unknown,
|
|
43
|
+
private readonly relationName: string,
|
|
44
|
+
private readonly relation: MorphOneRelation,
|
|
45
|
+
private readonly rootTable: TableDef,
|
|
46
|
+
private readonly loader: () => Promise<Map<string, Row>>,
|
|
47
|
+
private readonly createEntity: (row: Row) => TChild,
|
|
48
|
+
private readonly localKey: string
|
|
49
|
+
) {
|
|
50
|
+
hideInternal(this, [
|
|
51
|
+
'ctx', 'meta', 'root', 'relationName', 'relation',
|
|
52
|
+
'rootTable', 'loader', 'createEntity', 'localKey'
|
|
53
|
+
]);
|
|
54
|
+
hideWritable(this, ['loaded', 'current']);
|
|
55
|
+
this.populateFromHydrationCache();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async load(): Promise<TChild | null> {
|
|
59
|
+
if (this.loaded) return this.current;
|
|
60
|
+
const map = await this.loader();
|
|
61
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
62
|
+
if (keyValue === undefined || keyValue === null) {
|
|
63
|
+
this.loaded = true;
|
|
64
|
+
return this.current;
|
|
65
|
+
}
|
|
66
|
+
const row = map.get(toKey(keyValue));
|
|
67
|
+
this.current = row ? this.createEntity(row) : null;
|
|
68
|
+
this.loaded = true;
|
|
69
|
+
return this.current;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get(): TChild | null {
|
|
73
|
+
return this.current;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
set(data: Partial<TChild> | TChild | null): TChild | null {
|
|
77
|
+
if (data === null) {
|
|
78
|
+
return this.detachCurrent();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const entity = hasEntityMeta(data) ? (data as TChild) : this.createEntity(data as Row);
|
|
82
|
+
if (this.current && this.current !== entity) {
|
|
83
|
+
this.ctx.registerRelationChange(
|
|
84
|
+
this.root,
|
|
85
|
+
this.relationKey,
|
|
86
|
+
this.rootTable,
|
|
87
|
+
this.relationName,
|
|
88
|
+
this.relation,
|
|
89
|
+
{ kind: 'remove', entity: this.current }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.assignMorphKeys(entity);
|
|
94
|
+
this.current = entity;
|
|
95
|
+
this.loaded = true;
|
|
96
|
+
|
|
97
|
+
this.ctx.registerRelationChange(
|
|
98
|
+
this.root,
|
|
99
|
+
this.relationKey,
|
|
100
|
+
this.rootTable,
|
|
101
|
+
this.relationName,
|
|
102
|
+
this.relation,
|
|
103
|
+
{ kind: 'attach', entity }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return entity;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
toJSON(): unknown {
|
|
110
|
+
if (!this.current) return null;
|
|
111
|
+
const entityWithToJSON = this.current as { toJSON?: () => unknown };
|
|
112
|
+
return typeof entityWithToJSON.toJSON === 'function'
|
|
113
|
+
? entityWithToJSON.toJSON()
|
|
114
|
+
: this.current;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private detachCurrent(): TChild | null {
|
|
118
|
+
const previous = this.current;
|
|
119
|
+
if (!previous) return null;
|
|
120
|
+
this.current = null;
|
|
121
|
+
this.loaded = true;
|
|
122
|
+
this.ctx.registerRelationChange(
|
|
123
|
+
this.root,
|
|
124
|
+
this.relationKey,
|
|
125
|
+
this.rootTable,
|
|
126
|
+
this.relationName,
|
|
127
|
+
this.relation,
|
|
128
|
+
{ kind: 'remove', entity: previous }
|
|
129
|
+
);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private assignMorphKeys(entity: TChild): void {
|
|
134
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
135
|
+
(entity as Row)[this.relation.idField] = keyValue;
|
|
136
|
+
(entity as Row)[this.relation.typeField] = this.relation.typeValue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private get relationKey(): RelationKey {
|
|
140
|
+
return `${this.rootTable.name}.${this.relationName}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private populateFromHydrationCache(): void {
|
|
144
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
145
|
+
if (keyValue === undefined || keyValue === null) return;
|
|
146
|
+
const row = getHydrationRecord(this.meta, this.relationName, keyValue);
|
|
147
|
+
if (!row) return;
|
|
148
|
+
this.current = this.createEntity(row);
|
|
149
|
+
this.loaded = true;
|
|
150
|
+
}
|
|
151
|
+
}
|