@assemora/data 0.1.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/LICENSE +202 -0
- package/README.md +13 -0
- package/dist/columns.d.ts +121 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +101 -0
- package/dist/columns.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/instance.d.ts +46 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +158 -0
- package/dist/instance.js.map +1 -0
- package/dist/model.d.ts +73 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +272 -0
- package/dist/model.js.map +1 -0
- package/dist/module.d.ts +19 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +29 -0
- package/dist/module.js.map +1 -0
- package/dist/pivot.d.ts +86 -0
- package/dist/pivot.d.ts.map +1 -0
- package/dist/pivot.js +145 -0
- package/dist/pivot.js.map +1 -0
- package/dist/query.d.ts +170 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +307 -0
- package/dist/query.js.map +1 -0
- package/dist/relations.d.ts +64 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +26 -0
- package/dist/relations.js.map +1 -0
- package/dist/runtime.d.ts +28 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +149 -0
- package/dist/runtime.js.map +1 -0
- package/dist/slow-queries.d.ts +55 -0
- package/dist/slow-queries.d.ts.map +1 -0
- package/dist/slow-queries.js +96 -0
- package/dist/slow-queries.js.map +1 -0
- package/package.json +40 -0
package/dist/instance.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model instances (SPEC.md §22, §26, §28, §29).
|
|
3
|
+
*
|
|
4
|
+
* An instance is the record itself plus the verbs that act on it, so a caller writes
|
|
5
|
+
* `user.name = 'John'` and `await user.save()` rather than assembling an update.
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { NotFoundError } from '@assemora/core';
|
|
9
|
+
import { comparison, emptyQuery } from '@assemora/database';
|
|
10
|
+
import { definePivot } from './pivot.js';
|
|
11
|
+
import { execute } from './runtime.js';
|
|
12
|
+
const ORIGINAL = Symbol('assemora.original');
|
|
13
|
+
const columnNames = (context) => Object.keys(context.columns);
|
|
14
|
+
/** Values a write should carry: everything the model declares as a column. */
|
|
15
|
+
const persistable = (context, row) => {
|
|
16
|
+
const values = {};
|
|
17
|
+
for (const name of columnNames(context)) {
|
|
18
|
+
if (name in row)
|
|
19
|
+
values[name] = row[name];
|
|
20
|
+
}
|
|
21
|
+
return values;
|
|
22
|
+
};
|
|
23
|
+
const touchTimestamps = (context, row, moment) => {
|
|
24
|
+
for (const [name, column] of Object.entries(context.columns)) {
|
|
25
|
+
if (column.timestampRole === 'updated' ||
|
|
26
|
+
(moment === 'create' && column.timestampRole === 'created')) {
|
|
27
|
+
row[name] = new Date();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const fillDefaults = (context, row) => {
|
|
32
|
+
for (const [name, column] of Object.entries(context.columns)) {
|
|
33
|
+
if (row[name] !== undefined)
|
|
34
|
+
continue;
|
|
35
|
+
if (column.usesRandomDefault)
|
|
36
|
+
row[name] = randomUUID();
|
|
37
|
+
else if (column.hasDefault && column.defaultValue !== undefined)
|
|
38
|
+
row[name] = column.defaultValue;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
export const createInstance = (context, row, origin = { persisted: true }) => {
|
|
42
|
+
const instance = { ...row };
|
|
43
|
+
let persisted = origin.persisted;
|
|
44
|
+
Object.defineProperty(instance, ORIGINAL, {
|
|
45
|
+
enumerable: false,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: { ...row },
|
|
48
|
+
});
|
|
49
|
+
const original = () => instance[ORIGINAL] ?? {};
|
|
50
|
+
const key = context.table.primaryKey;
|
|
51
|
+
const identify = () => {
|
|
52
|
+
const id = instance[key];
|
|
53
|
+
if (id === undefined || id === null) {
|
|
54
|
+
throw new NotFoundError(context.table.name);
|
|
55
|
+
}
|
|
56
|
+
return comparison(key, '=', id);
|
|
57
|
+
};
|
|
58
|
+
const write = async (operation, data) => execute({
|
|
59
|
+
...emptyQuery(context.table.name, operation),
|
|
60
|
+
where: operation === 'insert' ? [] : [identify()],
|
|
61
|
+
...(data === undefined ? {} : { data }),
|
|
62
|
+
}, { table: context.table });
|
|
63
|
+
const methods = {
|
|
64
|
+
async save() {
|
|
65
|
+
if (persisted) {
|
|
66
|
+
touchTimestamps(context, instance, 'update');
|
|
67
|
+
const changed = {};
|
|
68
|
+
for (const name of columnNames(context)) {
|
|
69
|
+
if (instance[name] !== original()[name])
|
|
70
|
+
changed[name] = instance[name];
|
|
71
|
+
}
|
|
72
|
+
if (Object.keys(changed).length > 0)
|
|
73
|
+
await write('update', changed);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
fillDefaults(context, instance);
|
|
77
|
+
touchTimestamps(context, instance, 'create');
|
|
78
|
+
await write('insert', persistable(context, instance));
|
|
79
|
+
}
|
|
80
|
+
instance[ORIGINAL] = { ...persistable(context, instance) };
|
|
81
|
+
persisted = true;
|
|
82
|
+
},
|
|
83
|
+
async update(values) {
|
|
84
|
+
Object.assign(instance, values);
|
|
85
|
+
await methods.save();
|
|
86
|
+
},
|
|
87
|
+
async delete() {
|
|
88
|
+
const softColumn = context.table.softDeleteColumn;
|
|
89
|
+
if (softColumn === undefined) {
|
|
90
|
+
await write('delete');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
instance[softColumn] = new Date();
|
|
94
|
+
await write('update', { [softColumn]: instance[softColumn] });
|
|
95
|
+
},
|
|
96
|
+
async restore() {
|
|
97
|
+
const softColumn = context.table.softDeleteColumn;
|
|
98
|
+
if (softColumn === undefined)
|
|
99
|
+
return;
|
|
100
|
+
instance[softColumn] = null;
|
|
101
|
+
await write('update', { [softColumn]: null });
|
|
102
|
+
},
|
|
103
|
+
async refresh() {
|
|
104
|
+
const rows = await execute({ ...emptyQuery(context.table.name), where: [identify()], limit: 1 }, { table: context.table });
|
|
105
|
+
const fresh = rows[0];
|
|
106
|
+
if (fresh === undefined)
|
|
107
|
+
throw new NotFoundError(context.table.name, String(instance[key]));
|
|
108
|
+
for (const name of columnNames(context))
|
|
109
|
+
delete instance[name];
|
|
110
|
+
Object.assign(instance, fresh);
|
|
111
|
+
instance[ORIGINAL] = { ...fresh };
|
|
112
|
+
persisted = true;
|
|
113
|
+
},
|
|
114
|
+
isDirty(field) {
|
|
115
|
+
if (field !== undefined)
|
|
116
|
+
return instance[field] !== original()[field];
|
|
117
|
+
return columnNames(context).some((name) => instance[name] !== original()[name]);
|
|
118
|
+
},
|
|
119
|
+
getOriginal(field) {
|
|
120
|
+
return original()[field];
|
|
121
|
+
},
|
|
122
|
+
toJSON() {
|
|
123
|
+
const output = {};
|
|
124
|
+
for (const [name, column] of Object.entries(context.columns)) {
|
|
125
|
+
// A hidden column never reaches serialized output (SPEC.md §28).
|
|
126
|
+
if (column.isHidden)
|
|
127
|
+
continue;
|
|
128
|
+
if (name in instance)
|
|
129
|
+
output[name] = instance[name];
|
|
130
|
+
}
|
|
131
|
+
for (const [name, compute] of Object.entries(context.computed)) {
|
|
132
|
+
output[name] = compute(instance);
|
|
133
|
+
}
|
|
134
|
+
return output;
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
Object.assign(instance, methods);
|
|
138
|
+
for (const [name, compute] of Object.entries(context.computed)) {
|
|
139
|
+
Object.defineProperty(instance, name, {
|
|
140
|
+
enumerable: true,
|
|
141
|
+
get: () => compute(instance),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// `user.roles` exists whether or not anybody loaded roles: the verbs of SPEC.md §24
|
|
145
|
+
// act on the join table, and the row alone is enough to address it.
|
|
146
|
+
for (const relation of context.table.relations) {
|
|
147
|
+
if (relation.kind !== 'belongsToMany')
|
|
148
|
+
continue;
|
|
149
|
+
definePivot(instance, {
|
|
150
|
+
owner: context.table,
|
|
151
|
+
relation,
|
|
152
|
+
row: instance,
|
|
153
|
+
related: context.related,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return instance;
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=instance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { TableDescriptor } from '@assemora/database';
|
|
2
|
+
import { type ComputedValues, type Instance, type NoComputed } from './instance.js';
|
|
3
|
+
import { type Fields, type InferRecord, type Query, type ScopedQuery } from './query.js';
|
|
4
|
+
export type ScopeMap<F extends Fields> = Readonly<Record<string, (query: Query<F>) => Query<F>>>;
|
|
5
|
+
export type NoScopes = Readonly<Record<never, never>>;
|
|
6
|
+
/**
|
|
7
|
+
* Scopes and computed fields are declared through mapped types so that TypeScript
|
|
8
|
+
* types the callback arguments from `fields` alone, and infers the scope names and
|
|
9
|
+
* the computed value types from the object literal.
|
|
10
|
+
*/
|
|
11
|
+
export type ModelOptions<F extends Fields, S, C extends ComputedValues> = {
|
|
12
|
+
readonly scopes?: {
|
|
13
|
+
readonly [K in keyof S]: (query: Query<F>) => Query<F>;
|
|
14
|
+
};
|
|
15
|
+
readonly computed?: {
|
|
16
|
+
readonly [K in keyof C]: (record: InferRecord<F>) => C[K];
|
|
17
|
+
};
|
|
18
|
+
/** Marks the model soft-deleting and names the column (SPEC.md §29). */
|
|
19
|
+
readonly softDeletes?: boolean | string;
|
|
20
|
+
};
|
|
21
|
+
export type Model<F extends Fields, SN extends string = never, C extends ComputedValues = NoComputed> = ScopedQuery<F, SN, Instance<F, C>> & {
|
|
22
|
+
readonly table: string;
|
|
23
|
+
readonly primaryKey: string;
|
|
24
|
+
readonly fields: F;
|
|
25
|
+
readonly descriptor: TableDescriptor;
|
|
26
|
+
/** The record type this model produces. A type-level marker (SPEC.md §18). */
|
|
27
|
+
readonly $infer: InferRecord<F>;
|
|
28
|
+
find(id: unknown): Promise<Instance<F, C> | null>;
|
|
29
|
+
findOrFail(id: unknown): Promise<Instance<F, C>>;
|
|
30
|
+
all(): Promise<Instance<F, C>[]>;
|
|
31
|
+
create(values: Partial<InferRecord<F>>): Promise<Instance<F, C>>;
|
|
32
|
+
/**
|
|
33
|
+
* The row of this entry in the language being read (SPEC.md §131).
|
|
34
|
+
*
|
|
35
|
+
* `find()` answers the row whose id was asked for, in whatever language it happens to
|
|
36
|
+
* be written — which is what a policy check and an edit want. This answers the *entry*
|
|
37
|
+
* that row belongs to, read in the language of the operation, with the same fallback
|
|
38
|
+
* as any other read. It is what a relation needs: a translation's foreign keys point
|
|
39
|
+
* at originals, so following one and showing its name means asking for the entry
|
|
40
|
+
* rather than for the row.
|
|
41
|
+
*
|
|
42
|
+
* Identical to `find()` on a model that is not translatable.
|
|
43
|
+
*/
|
|
44
|
+
translated(id: unknown): Promise<Instance<F, C> | null>;
|
|
45
|
+
/**
|
|
46
|
+
* One row per language, and the row keeps its own shape (SPEC.md §131).
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* export const Article = model('articles', {
|
|
50
|
+
* id: uuid().primary(),
|
|
51
|
+
* title: string(),
|
|
52
|
+
* }).translatable()
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* The model gains `locale` and `translationOf` and nothing else changes: `title` is
|
|
56
|
+
* still a `string`, `Article.$infer` still has the shape it had, and every scope,
|
|
57
|
+
* relation and query written against it still means what it meant. What changes is
|
|
58
|
+
* that a read is scoped to the language of the operation without a caller asking.
|
|
59
|
+
*/
|
|
60
|
+
translatable(): Model<F & TranslationFields, SN, C>;
|
|
61
|
+
};
|
|
62
|
+
export declare const TRANSLATION_FIELDS: {
|
|
63
|
+
/** The language this row is written in. */
|
|
64
|
+
readonly locale: import("./columns.js").ColumnBuilder<string>;
|
|
65
|
+
/** The row this one translates, or null for the original. */
|
|
66
|
+
readonly translationOf: import("./columns.js").ColumnBuilder<string | null>;
|
|
67
|
+
};
|
|
68
|
+
export type TranslationFields = typeof TRANSLATION_FIELDS;
|
|
69
|
+
/** Every declared model, for resolving the other side of a relation. */
|
|
70
|
+
export declare const registeredModels: () => Readonly<Record<string, TableDescriptor>>;
|
|
71
|
+
export declare const clearModelRegistry: () => void;
|
|
72
|
+
export declare const model: <F extends Fields, S extends Readonly<Record<string, unknown>> = NoScopes, C extends ComputedValues = NoComputed>(table: string, fields: F, options?: ModelOptions<F, S, C>) => Model<F, keyof S & string, C>;
|
|
73
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `model()` (SPEC.md §9, §17, §21, §25, §29).
|
|
3
|
+
*
|
|
4
|
+
* One declaration produces the record type, the column metadata the database needs,
|
|
5
|
+
* the query entry point and the model's scopes.
|
|
6
|
+
*/
|
|
7
|
+
import { ValidationError } from '@assemora/core';
|
|
8
|
+
import { comparison, emptyQuery } from '@assemora/database';
|
|
9
|
+
import { string, uuid } from './columns.js';
|
|
10
|
+
import { createInstance, } from './instance.js';
|
|
11
|
+
import { createQuery, } from './query.js';
|
|
12
|
+
import { isRelation } from './relations.js';
|
|
13
|
+
import { execute } from './runtime.js';
|
|
14
|
+
/**
|
|
15
|
+
* The two columns a translatable model gains (SPEC.md §131).
|
|
16
|
+
*
|
|
17
|
+
* A row per language, and the row keeps its own shape. `title` stays a `string`: the
|
|
18
|
+
* moment a field becomes a map keyed by language, one declaration stops feeding the
|
|
19
|
+
* record type, the column, the form, the OpenAPI schema and the SDK at once, which is
|
|
20
|
+
* §2 and §128 given up for a storage convenience. `Article.where('locale', 'ru')` is an
|
|
21
|
+
* ordinary query and everything already built keeps working.
|
|
22
|
+
*
|
|
23
|
+
* Both are indexed. Every read of a translatable model filters on `locale`, and the
|
|
24
|
+
* fallback groups by `translationOf` — an index each is what the feature is, not a
|
|
25
|
+
* tuning decision somebody makes later.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* How `translatable()` tells the `model()` call it re-enters what it is doing.
|
|
29
|
+
*
|
|
30
|
+
* A symbol rather than a field on `ModelOptions`, because it is not something an
|
|
31
|
+
* application says: writing `{ translatable: true }` beside `softDeletes` would be a
|
|
32
|
+
* second way to declare it, and the two would eventually be given different answers.
|
|
33
|
+
*/
|
|
34
|
+
const TRANSLATABLE = Symbol.for('assemora.model.translatable');
|
|
35
|
+
export const TRANSLATION_FIELDS = {
|
|
36
|
+
/** The language this row is written in. */
|
|
37
|
+
locale: string().index(),
|
|
38
|
+
/** The row this one translates, or null for the original. */
|
|
39
|
+
translationOf: uuid().nullable().index(),
|
|
40
|
+
};
|
|
41
|
+
const registry = new Map();
|
|
42
|
+
/** Every declared model, for resolving the other side of a relation. */
|
|
43
|
+
export const registeredModels = () => {
|
|
44
|
+
const descriptors = {};
|
|
45
|
+
for (const [table, model] of registry)
|
|
46
|
+
descriptors[table] = model.descriptor;
|
|
47
|
+
return descriptors;
|
|
48
|
+
};
|
|
49
|
+
export const clearModelRegistry = () => {
|
|
50
|
+
registry.clear();
|
|
51
|
+
};
|
|
52
|
+
/** `users` → `userId`. Crude on purpose; pass `foreignKey` when it does not fit. */
|
|
53
|
+
const foreignKeyFor = (table) => `${table.endsWith('s') ? table.slice(0, -1) : table}Id`;
|
|
54
|
+
const describeColumn = (name, column) => ({
|
|
55
|
+
name,
|
|
56
|
+
type: column.type,
|
|
57
|
+
isPrimary: column.isPrimary,
|
|
58
|
+
isNullable: column.isNullable,
|
|
59
|
+
isUnique: column.isUnique,
|
|
60
|
+
isIndexed: column.isIndexed,
|
|
61
|
+
hasDefault: column.hasDefault,
|
|
62
|
+
...(column.enumValues === undefined ? {} : { enumValues: column.enumValues }),
|
|
63
|
+
});
|
|
64
|
+
const describeRelation = (name, relation, owner) => {
|
|
65
|
+
const target = relation.target();
|
|
66
|
+
return {
|
|
67
|
+
name,
|
|
68
|
+
kind: relation.kind,
|
|
69
|
+
target: target.table,
|
|
70
|
+
foreignKey: relation.foreignKey ??
|
|
71
|
+
(relation.kind === 'belongsTo' ? `${name}Id` : foreignKeyFor(owner.table)),
|
|
72
|
+
ownerKey: relation.ownerKey ?? (relation.kind === 'belongsTo' ? target.primaryKey : owner.primaryKey),
|
|
73
|
+
// Carried rather than resolved: what a join table is called and which of its
|
|
74
|
+
// columns points where is derived once, in `@assemora/database`, so the DDL, the
|
|
75
|
+
// diff and the pivot verbs cannot form three opinions about the same table.
|
|
76
|
+
...(relation.through === undefined ? {} : { through: relation.through }),
|
|
77
|
+
...(relation.foreignPivotKey === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { foreignPivotKey: relation.foreignPivotKey }),
|
|
80
|
+
...(relation.relatedPivotKey === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { relatedPivotKey: relation.relatedPivotKey }),
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
export const model = (table, fields, options = {}) => {
|
|
86
|
+
const columns = {};
|
|
87
|
+
const relations = {};
|
|
88
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
89
|
+
if (isRelation(field))
|
|
90
|
+
relations[name] = field;
|
|
91
|
+
else
|
|
92
|
+
columns[name] = field;
|
|
93
|
+
}
|
|
94
|
+
const primaryKey = Object.entries(columns).find(([, column]) => column.isPrimary)?.[0] ?? 'id';
|
|
95
|
+
const translates = options[TRANSLATABLE] === true;
|
|
96
|
+
const updatedAtColumn = Object.entries(columns).find(([, column]) => column.timestampRole === 'updated')?.[0];
|
|
97
|
+
const softDeleteColumn = options.softDeletes === true
|
|
98
|
+
? 'deletedAt'
|
|
99
|
+
: typeof options.softDeletes === 'string'
|
|
100
|
+
? options.softDeletes
|
|
101
|
+
: undefined;
|
|
102
|
+
let cached;
|
|
103
|
+
const descriptor = () => {
|
|
104
|
+
if (cached !== undefined)
|
|
105
|
+
return cached;
|
|
106
|
+
const described = Object.entries(columns).map(([name, column]) => describeColumn(name, column));
|
|
107
|
+
/**
|
|
108
|
+
* On a translatable model, a unique column is unique *within a language*.
|
|
109
|
+
*
|
|
110
|
+
* `slug: string().unique()` on a model with one row per language is a model with no
|
|
111
|
+
* translations: the Russian row would carry the same slug as the Ukrainian one and
|
|
112
|
+
* the constraint would refuse it. Globally unique is never what was meant — two
|
|
113
|
+
* rows sharing a slug *are* the same entry, in two languages, which is exactly what
|
|
114
|
+
* §131 stores.
|
|
115
|
+
*
|
|
116
|
+
* Done here rather than asked of the application, because it is not a choice: an
|
|
117
|
+
* application that had to remember would be one where forgetting is a table that
|
|
118
|
+
* cannot hold a translation, discovered at the first one.
|
|
119
|
+
*/
|
|
120
|
+
const perLocale = translates ? described.filter((column) => column.isUnique) : [];
|
|
121
|
+
cached = {
|
|
122
|
+
name: table,
|
|
123
|
+
primaryKey,
|
|
124
|
+
columns: described.map((column) => perLocale.includes(column) ? { ...column, isUnique: false } : column),
|
|
125
|
+
relations: Object.entries(relations).map(([name, relation]) => describeRelation(name, relation, { table, primaryKey })),
|
|
126
|
+
...(softDeleteColumn === undefined ? {} : { softDeleteColumn }),
|
|
127
|
+
...(translates ? { translatable: true } : {}),
|
|
128
|
+
...(updatedAtColumn === undefined ? {} : { updatedAtColumn }),
|
|
129
|
+
...(perLocale.length === 0
|
|
130
|
+
? {}
|
|
131
|
+
: { uniqueTogether: perLocale.map((column) => [column.name, 'locale']) }),
|
|
132
|
+
};
|
|
133
|
+
return cached;
|
|
134
|
+
};
|
|
135
|
+
const instanceContext = {
|
|
136
|
+
get table() {
|
|
137
|
+
return descriptor();
|
|
138
|
+
},
|
|
139
|
+
columns,
|
|
140
|
+
computed: (options.computed ?? {}),
|
|
141
|
+
related: registeredModels,
|
|
142
|
+
};
|
|
143
|
+
/** A row that came out of storage already exists there. */
|
|
144
|
+
const hydrate = (row) => createInstance(instanceContext, row, { persisted: true });
|
|
145
|
+
const baseState = () => ({
|
|
146
|
+
table: descriptor,
|
|
147
|
+
where: [],
|
|
148
|
+
order: [],
|
|
149
|
+
load: [],
|
|
150
|
+
limit: undefined,
|
|
151
|
+
offset: undefined,
|
|
152
|
+
trashed: 'without',
|
|
153
|
+
// The language of the operation, and the fallback on: both are what a read means
|
|
154
|
+
// without a caller saying anything (SPEC.md §131).
|
|
155
|
+
locale: 'context',
|
|
156
|
+
fallback: true,
|
|
157
|
+
});
|
|
158
|
+
const query = () => createQuery(baseState(), {
|
|
159
|
+
related: registeredModels,
|
|
160
|
+
hydrate,
|
|
161
|
+
scopes: (options.scopes ?? {}),
|
|
162
|
+
});
|
|
163
|
+
/** Validates a value against the column that will hold it. */
|
|
164
|
+
const validate = (values) => {
|
|
165
|
+
const checked = {};
|
|
166
|
+
const issues = [];
|
|
167
|
+
for (const [name, column] of Object.entries(columns)) {
|
|
168
|
+
const raw = values[name];
|
|
169
|
+
if (raw === undefined) {
|
|
170
|
+
if (!column.hasDefault && !column.isNullable && column.timestampRole === undefined) {
|
|
171
|
+
issues.push({ path: [name], code: 'required', message: 'This field is required' });
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const result = column.schema.parse(raw);
|
|
176
|
+
if (result.ok) {
|
|
177
|
+
const transform = column.transform;
|
|
178
|
+
checked[name] = transform === undefined ? result.value : transform(result.value);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
issues.push(...result.issues.map((issue) => ({ ...issue, path: [name, ...issue.path] })));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (issues.length > 0)
|
|
185
|
+
throw new ValidationError(issues);
|
|
186
|
+
return checked;
|
|
187
|
+
};
|
|
188
|
+
const statics = {
|
|
189
|
+
table,
|
|
190
|
+
primaryKey,
|
|
191
|
+
fields,
|
|
192
|
+
// A type-level marker: the value is never read, the type is the point.
|
|
193
|
+
$infer: undefined,
|
|
194
|
+
async find(id) {
|
|
195
|
+
const rows = await execute({
|
|
196
|
+
...emptyQuery(table),
|
|
197
|
+
where: [
|
|
198
|
+
comparison(primaryKey, '=', id),
|
|
199
|
+
...(softDeleteColumn === undefined ? [] : [comparison(softDeleteColumn, 'is null')]),
|
|
200
|
+
],
|
|
201
|
+
limit: 1,
|
|
202
|
+
}, { table: descriptor() });
|
|
203
|
+
const row = rows[0];
|
|
204
|
+
return row === undefined ? null : hydrate(row);
|
|
205
|
+
},
|
|
206
|
+
async findOrFail(id) {
|
|
207
|
+
const found = await statics.find(id);
|
|
208
|
+
if (found === null) {
|
|
209
|
+
const { NotFoundError } = await import('@assemora/core');
|
|
210
|
+
throw new NotFoundError(table, String(id));
|
|
211
|
+
}
|
|
212
|
+
return found;
|
|
213
|
+
},
|
|
214
|
+
all() {
|
|
215
|
+
return query().get();
|
|
216
|
+
},
|
|
217
|
+
async translated(id) {
|
|
218
|
+
if (descriptor().translatable !== true)
|
|
219
|
+
return statics.find(id);
|
|
220
|
+
// The row first, unscoped, because the id may name a translation: asking for the
|
|
221
|
+
// group of `id` without knowing which row it is would miss the original.
|
|
222
|
+
const named = await statics.find(id);
|
|
223
|
+
if (named === null)
|
|
224
|
+
return null;
|
|
225
|
+
const row = named;
|
|
226
|
+
const entry = row.translationOf ?? row[primaryKey];
|
|
227
|
+
/**
|
|
228
|
+
* `translationOf` is a column of a translatable model and not a member of `F`,
|
|
229
|
+
* which is what makes this the one place that has to say so. Everything else —
|
|
230
|
+
* the language filter, the fallback — is the ordinary read.
|
|
231
|
+
*/
|
|
232
|
+
const grouped = query();
|
|
233
|
+
const rows = await grouped
|
|
234
|
+
.where((nested) => {
|
|
235
|
+
const scope = nested;
|
|
236
|
+
return scope.where(primaryKey, entry).orWhere('translationOf', entry);
|
|
237
|
+
})
|
|
238
|
+
.get();
|
|
239
|
+
return rows[0] ?? null;
|
|
240
|
+
},
|
|
241
|
+
async create(values) {
|
|
242
|
+
// Not `hydrate`: this row does not exist yet, whether or not it was handed a
|
|
243
|
+
// primary key of its own.
|
|
244
|
+
const instance = createInstance(instanceContext, validate(values), {
|
|
245
|
+
persisted: false,
|
|
246
|
+
});
|
|
247
|
+
await instance.save();
|
|
248
|
+
return instance;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
const translatable = () =>
|
|
252
|
+
// Declared again with the two extra columns rather than patched in place: a model is
|
|
253
|
+
// its fields, and everything derived from them — the record type, the descriptor,
|
|
254
|
+
// the validator, the instance — is built once from that one list. Re-entering here
|
|
255
|
+
// is what keeps a translatable model from being a second kind of model.
|
|
256
|
+
model(table, { ...fields, ...TRANSLATION_FIELDS },
|
|
257
|
+
// The symbol rides along beside the declared options. It is not part of
|
|
258
|
+
// `ModelOptions` and must not become part of it, so it is attached rather than
|
|
259
|
+
// written into the literal.
|
|
260
|
+
Object.assign({ [TRANSLATABLE]: true }, options));
|
|
261
|
+
const built = Object.assign(query(), statics, { translatable });
|
|
262
|
+
// A getter, not a value: Object.assign would resolve it, and resolving a relation
|
|
263
|
+
// while the models are still being declared reaches a target that does not exist yet.
|
|
264
|
+
Object.defineProperty(built, 'descriptor', { enumerable: true, get: descriptor });
|
|
265
|
+
registry.set(table, {
|
|
266
|
+
get descriptor() {
|
|
267
|
+
return descriptor();
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
return built;
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TableDescriptor } from '@assemora/database';
|
|
2
|
+
declare module '@assemora/core' {
|
|
3
|
+
interface RegistrySections {
|
|
4
|
+
models: ModelDescriptor;
|
|
5
|
+
}
|
|
6
|
+
interface ModuleBuilder {
|
|
7
|
+
models(...models: {
|
|
8
|
+
readonly table: string;
|
|
9
|
+
readonly descriptor: TableDescriptor;
|
|
10
|
+
}[]): ModuleBuilder;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export type ModelDescriptor = {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly table: TableDescriptor;
|
|
16
|
+
readonly module?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const defineModelFacet: () => void;
|
|
19
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEzD,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,gBAAgB;QACxB,MAAM,EAAE,eAAe,CAAA;KACxB;IAED,UAAU,aAAa;QACrB,MAAM,CACJ,GAAG,MAAM,EAAE;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAA;SAAE,EAAE,GAC5E,aAAa,CAAA;KACjB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAID,eAAO,MAAM,gBAAgB,QAAO,IAoBnC,CAAA"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.models()` facet of `module()` (SPEC.md §13, §42, ADR-0009).
|
|
3
|
+
*
|
|
4
|
+
* Registering a model describes its table in the Schema Registry, which is where
|
|
5
|
+
* migrations, introspection and `assemora.describe` read it from.
|
|
6
|
+
*/
|
|
7
|
+
import { defineModuleFacet } from '@assemora/core';
|
|
8
|
+
let defined = false;
|
|
9
|
+
export const defineModelFacet = () => {
|
|
10
|
+
if (defined)
|
|
11
|
+
return;
|
|
12
|
+
defineModuleFacet('models', (internals, args) => {
|
|
13
|
+
internals.addRegistration((context) => {
|
|
14
|
+
for (const candidate of args) {
|
|
15
|
+
const declared = candidate;
|
|
16
|
+
if (typeof declared?.table !== 'string')
|
|
17
|
+
continue;
|
|
18
|
+
context.registry.register('models', {
|
|
19
|
+
name: declared.table,
|
|
20
|
+
table: declared.descriptor,
|
|
21
|
+
module: internals.name,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
defined = true;
|
|
27
|
+
};
|
|
28
|
+
defineModelFacet();
|
|
29
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAqBlD,IAAI,OAAO,GAAG,KAAK,CAAA;AAEnB,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAS,EAAE;IACzC,IAAI,OAAO;QAAE,OAAM;IAEnB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE;QAC9C,SAAS,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE;YACpC,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;gBAC7B,MAAM,QAAQ,GAAG,SAA2D,CAAA;gBAE5E,IAAI,OAAO,QAAQ,EAAE,KAAK,KAAK,QAAQ;oBAAE,SAAQ;gBAEjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE;oBAClC,IAAI,EAAE,QAAQ,CAAC,KAAK;oBACpB,KAAK,EAAE,QAAQ,CAAC,UAAU;oBAC1B,MAAM,EAAE,SAAS,CAAC,IAAI;iBACvB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,IAAI,CAAA;AAChB,CAAC,CAAA;AAED,gBAAgB,EAAE,CAAA"}
|