@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/query.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query builder (SPEC.md §19, §20, §30).
|
|
3
|
+
*
|
|
4
|
+
* Every call returns a new builder, so a query is safe to share and to derive from.
|
|
5
|
+
* The builder produces a Query AST and never touches an adapter's own query API.
|
|
6
|
+
*/
|
|
7
|
+
import { AssemoraError, currentContext } from '@assemora/core';
|
|
8
|
+
import { comparison, group, jsonContains, jsonEquals, jsonLike, orComparison, UNSPECIFIED_LOCALE, } from '@assemora/database';
|
|
9
|
+
import { execute } from './runtime.js';
|
|
10
|
+
/**
|
|
11
|
+
* A pattern match needs a key to match against: matching a whole document as text is
|
|
12
|
+
* meaningless, and the adapters would disagree about what it even means.
|
|
13
|
+
*/
|
|
14
|
+
const jsonPath = (field, path) => {
|
|
15
|
+
if (path === '') {
|
|
16
|
+
throw new AssemoraError('INVALID_QUERY', `A pattern match on "${field}" needs a key inside the document`, { status: 400 });
|
|
17
|
+
}
|
|
18
|
+
return path.split('.');
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A row count has to be a whole number that is not negative.
|
|
22
|
+
*
|
|
23
|
+
* Passed through unchecked, a `NaN` reaches the adapter, which drops the clause
|
|
24
|
+
* entirely — the query then returns every row instead of the page that was asked for.
|
|
25
|
+
*/
|
|
26
|
+
const countable = (method, count) => {
|
|
27
|
+
if (!Number.isInteger(count) || count < 0) {
|
|
28
|
+
throw new AssemoraError('INVALID_QUERY', `${method}() needs a whole number of rows`, {
|
|
29
|
+
status: 400,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return count;
|
|
33
|
+
};
|
|
34
|
+
const parsePath = (path) => {
|
|
35
|
+
const [head, ...rest] = path.split('.');
|
|
36
|
+
return {
|
|
37
|
+
relation: head ?? path,
|
|
38
|
+
nested: rest.length > 0 ? [parsePath(rest.join('.'))] : [],
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const mergeLoads = (existing, addition) => {
|
|
42
|
+
const found = existing.find((load) => load.relation === addition.relation);
|
|
43
|
+
if (found === undefined)
|
|
44
|
+
return [...existing, addition];
|
|
45
|
+
return existing.map((load) => load.relation === addition.relation
|
|
46
|
+
? {
|
|
47
|
+
relation: load.relation,
|
|
48
|
+
nested: addition.nested.reduce(mergeLoads, load.nested),
|
|
49
|
+
}
|
|
50
|
+
: load);
|
|
51
|
+
};
|
|
52
|
+
export const createQuery = (state, runtime) => {
|
|
53
|
+
const derive = (next) => createQuery({ ...state, ...next }, runtime);
|
|
54
|
+
const withCondition = (condition) => derive({ where: [...state.where, condition] });
|
|
55
|
+
/**
|
|
56
|
+
* The language this query reads in, or `undefined` for "every language".
|
|
57
|
+
*
|
|
58
|
+
* `undefined` is also what an application in one language answers, and what a query
|
|
59
|
+
* outside any context answers — a migration, a script, a test. Both mean the same
|
|
60
|
+
* thing here: no language filter, which is exactly what those reads did before §131.
|
|
61
|
+
*/
|
|
62
|
+
const readsIn = () => {
|
|
63
|
+
if (!state.table().translatable)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (state.locale === 'all')
|
|
66
|
+
return undefined;
|
|
67
|
+
if (state.locale !== 'context')
|
|
68
|
+
return state.locale.code;
|
|
69
|
+
return currentContext()?.locale;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Reading one language, and what counts as being written in it.
|
|
73
|
+
*
|
|
74
|
+
* The default language matches rows marked with it *and* rows marked with nothing:
|
|
75
|
+
* a row written before the deployment named any languages is in the language it was
|
|
76
|
+
* written in, which is the default. Without that, adding `locales` to a project that
|
|
77
|
+
* already had content would make all of it vanish (`UNSPECIFIED_LOCALE`).
|
|
78
|
+
*/
|
|
79
|
+
const localeCondition = () => {
|
|
80
|
+
const code = readsIn();
|
|
81
|
+
if (code === undefined)
|
|
82
|
+
return [];
|
|
83
|
+
return code === currentContext()?.locales?.defaultLocale
|
|
84
|
+
? [comparison('locale', 'in', [code, UNSPECIFIED_LOCALE])]
|
|
85
|
+
: [comparison('locale', '=', code)];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The state a `where(query => …)` group is built in.
|
|
89
|
+
*
|
|
90
|
+
* Empty conditions, and neither of the two the query adds by itself: a group is the
|
|
91
|
+
* caller's own parentheses and nothing else. `toAst()` is what the group is taken
|
|
92
|
+
* from, and it puts the soft-delete and language filters at the front — so a nested
|
|
93
|
+
* `orWhere` produced `(deletedAt is null AND a) OR b`, which is not the parentheses
|
|
94
|
+
* anybody wrote. It was invisible while the outer query repeated both conditions
|
|
95
|
+
* anyway; the fallback rewrites the language one, and then it is not invisible.
|
|
96
|
+
*
|
|
97
|
+
* Expressed as `trashed: 'with'` and `locale: 'all'` rather than as a third flag,
|
|
98
|
+
* because those are exactly what "add neither" already means.
|
|
99
|
+
*/
|
|
100
|
+
const nestedState = () => ({
|
|
101
|
+
...state,
|
|
102
|
+
where: [],
|
|
103
|
+
trashed: 'with',
|
|
104
|
+
locale: 'all',
|
|
105
|
+
});
|
|
106
|
+
const softDeleteCondition = () => {
|
|
107
|
+
const column = state.table().softDeleteColumn;
|
|
108
|
+
if (column === undefined || state.trashed === 'with')
|
|
109
|
+
return [];
|
|
110
|
+
if (state.trashed === 'only')
|
|
111
|
+
return [comparison(column, 'is not null')];
|
|
112
|
+
return [comparison(column, 'is null')];
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* The shorthand's default column, or a message that names the problem.
|
|
116
|
+
*
|
|
117
|
+
* Without this the query reached the adapter and came back as "No column is mapped
|
|
118
|
+
* for createdAt", which says nothing about `latest()` having filled it in.
|
|
119
|
+
*/
|
|
120
|
+
const timeColumn = (field) => {
|
|
121
|
+
if (field !== undefined)
|
|
122
|
+
return field;
|
|
123
|
+
const table = state.table();
|
|
124
|
+
if (!table.columns.some((column) => column.name === 'createdAt')) {
|
|
125
|
+
throw new AssemoraError('INVALID_QUERY', `"${table.name}" has no createdAt column, so latest() and oldest() need one named: latest('publishedAt')`, { status: 400 });
|
|
126
|
+
}
|
|
127
|
+
return 'createdAt';
|
|
128
|
+
};
|
|
129
|
+
const toAst = () => ({
|
|
130
|
+
model: state.table().name,
|
|
131
|
+
operation: 'select',
|
|
132
|
+
where: [...softDeleteCondition(), ...localeCondition(), ...state.where],
|
|
133
|
+
order: state.order,
|
|
134
|
+
with: state.load,
|
|
135
|
+
...(state.limit === undefined ? {} : { limit: state.limit }),
|
|
136
|
+
...(state.offset === undefined ? {} : { offset: state.offset }),
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* The query that actually runs, with the fallback folded into it (SPEC.md §131).
|
|
140
|
+
*
|
|
141
|
+
* One query and not two, because `order`, `limit` and `offset` decide what a page
|
|
142
|
+
* *is*: two result sets appended would put every untranslated row after every
|
|
143
|
+
* translated one, and page two of a menu would be a different menu.
|
|
144
|
+
*
|
|
145
|
+
* It costs one extra read to build. The rows already written in this language are
|
|
146
|
+
* fetched first — under the caller's own `where`, with no order and no limit — and
|
|
147
|
+
* what comes back is the set of *groups* that need no fallback. The query then asks
|
|
148
|
+
* for those rows, plus the default-language row of every group not among them.
|
|
149
|
+
*
|
|
150
|
+
* The alternative is `distinct on` or a window function, which is a change to the
|
|
151
|
+
* Query AST every adapter would have to implement, and ADR-0013 is why that is not a
|
|
152
|
+
* thing to do lightly. This uses `in` and `not in`, which every adapter already
|
|
153
|
+
* agrees about.
|
|
154
|
+
*/
|
|
155
|
+
const astToRun = async () => {
|
|
156
|
+
const base = toAst();
|
|
157
|
+
const code = readsIn();
|
|
158
|
+
const fallbackTo = currentContext()?.locales?.defaultLocale;
|
|
159
|
+
if (!state.fallback ||
|
|
160
|
+
code === undefined ||
|
|
161
|
+
fallbackTo === undefined ||
|
|
162
|
+
// The default language is where a fallback would come *from*. Nothing to do.
|
|
163
|
+
code === fallbackTo) {
|
|
164
|
+
return base;
|
|
165
|
+
}
|
|
166
|
+
const key = state.table().primaryKey;
|
|
167
|
+
// Deliberately without the caller's `order`, `limit` and `offset`: this asks which
|
|
168
|
+
// groups are already written in this language, and a page of that answer would
|
|
169
|
+
// leave the rest of them falling back when they should not.
|
|
170
|
+
const { limit: _limit, offset: _offset, ...whole } = base;
|
|
171
|
+
const written = await execute({ ...whole, order: [], with: [] }, { table: state.table(), related: runtime.related() });
|
|
172
|
+
/**
|
|
173
|
+
* What each row translates, or itself where it translates nothing.
|
|
174
|
+
*
|
|
175
|
+
* A row written directly in a language, with no original behind it, is its own
|
|
176
|
+
* group — otherwise an article first written in Russian would be answered twice.
|
|
177
|
+
*/
|
|
178
|
+
const covered = written.map((row) => row.translationOf ?? row[key]);
|
|
179
|
+
return {
|
|
180
|
+
...base,
|
|
181
|
+
where: [
|
|
182
|
+
...softDeleteCondition(),
|
|
183
|
+
...state.where,
|
|
184
|
+
/**
|
|
185
|
+
* A combinator says how a condition joins the one before it, not how its own
|
|
186
|
+
* children join each other — which is why `orComparison` exists at all. So the
|
|
187
|
+
* outer group joins the caller's `where` with `and`, and the inner group joins
|
|
188
|
+
* its sibling with `or`.
|
|
189
|
+
*/
|
|
190
|
+
group([
|
|
191
|
+
comparison('locale', '=', code),
|
|
192
|
+
group([
|
|
193
|
+
// The same rule as `localeCondition`: the default language is also where
|
|
194
|
+
// the rows that name no language belong.
|
|
195
|
+
comparison('locale', 'in', [fallbackTo, UNSPECIFIED_LOCALE]),
|
|
196
|
+
...(covered.length === 0 ? [] : [comparison(key, 'not in', covered)]),
|
|
197
|
+
], 'or'),
|
|
198
|
+
], 'and'),
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
const run = async () => {
|
|
203
|
+
const rows = await execute(await astToRun(), {
|
|
204
|
+
table: state.table(),
|
|
205
|
+
related: runtime.related(),
|
|
206
|
+
});
|
|
207
|
+
return rows.map(runtime.hydrate);
|
|
208
|
+
};
|
|
209
|
+
const query = {
|
|
210
|
+
where(first, second, third) {
|
|
211
|
+
if (typeof first === 'function') {
|
|
212
|
+
const nested = first(createQuery(nestedState(), runtime));
|
|
213
|
+
return withCondition(group(nested.toAst().where));
|
|
214
|
+
}
|
|
215
|
+
if (typeof first === 'object' && first !== null) {
|
|
216
|
+
return Object.entries(first).reduce((accumulated, [field, value]) => accumulated.where(field, value), createQuery(state, runtime));
|
|
217
|
+
}
|
|
218
|
+
return third === undefined
|
|
219
|
+
? withCondition(comparison(String(first), '=', second))
|
|
220
|
+
: withCondition(comparison(String(first), second, third));
|
|
221
|
+
},
|
|
222
|
+
orWhere(first, second, third) {
|
|
223
|
+
if (typeof first === 'function') {
|
|
224
|
+
const nested = first(createQuery(nestedState(), runtime));
|
|
225
|
+
return withCondition(group(nested.toAst().where, 'or'));
|
|
226
|
+
}
|
|
227
|
+
return third === undefined
|
|
228
|
+
? withCondition(orComparison(String(first), '=', second))
|
|
229
|
+
: withCondition(orComparison(String(first), second, third));
|
|
230
|
+
},
|
|
231
|
+
whereIn: (field, values) => withCondition(comparison(field, 'in', values)),
|
|
232
|
+
whereNotIn: (field, values) => withCondition(comparison(field, 'not in', values)),
|
|
233
|
+
whereNull: (field) => withCondition(comparison(field, 'is null')),
|
|
234
|
+
whereNotNull: (field) => withCondition(comparison(field, 'is not null')),
|
|
235
|
+
whereBetween: (field, range) => withCondition(comparison(field, 'between', range)),
|
|
236
|
+
whereLike: (field, pattern) => withCondition(comparison(field, 'like', pattern)),
|
|
237
|
+
whereJson: (field, path, value) => withCondition(jsonEquals(field, path === '' ? [] : path.split('.'), value)),
|
|
238
|
+
whereJsonContains: (field, fragment) => withCondition(jsonContains(field, fragment)),
|
|
239
|
+
whereJsonLike: (field, path, pattern) => withCondition(jsonLike(field, jsonPath(field, path), pattern)),
|
|
240
|
+
orWhereJsonLike: (field, path, pattern) => withCondition(jsonLike(field, jsonPath(field, path), pattern, 'or')),
|
|
241
|
+
orderBy: (field, direction = 'asc') => derive({ order: [...state.order, { field, direction }] }),
|
|
242
|
+
latest: (field) => derive({ order: [...state.order, { field: timeColumn(field), direction: 'desc' }] }),
|
|
243
|
+
oldest: (field) => derive({ order: [...state.order, { field: timeColumn(field), direction: 'asc' }] }),
|
|
244
|
+
limit: (count) => derive({ limit: countable('limit', count) }),
|
|
245
|
+
offset: (count) => derive({ offset: countable('offset', count) }),
|
|
246
|
+
take: (count) => derive({ limit: countable('take', count) }),
|
|
247
|
+
with: (...relations) => derive({ load: relations.map(parsePath).reduce(mergeLoads, state.load) }),
|
|
248
|
+
withTrashed: () => derive({ trashed: 'with' }),
|
|
249
|
+
onlyTrashed: () => derive({ trashed: 'only' }),
|
|
250
|
+
inLocale: (code) => derive({ locale: { code } }),
|
|
251
|
+
allLocales: () => derive({ locale: 'all' }),
|
|
252
|
+
withoutFallback: () => derive({ fallback: false }),
|
|
253
|
+
get: run,
|
|
254
|
+
async first() {
|
|
255
|
+
const rows = await createQuery({ ...state, limit: 1 }, runtime).get();
|
|
256
|
+
return rows[0] ?? null;
|
|
257
|
+
},
|
|
258
|
+
async firstOrFail() {
|
|
259
|
+
const row = await query.first();
|
|
260
|
+
if (row === null) {
|
|
261
|
+
const { NotFoundError } = await import('@assemora/core');
|
|
262
|
+
throw new NotFoundError(state.table().name);
|
|
263
|
+
}
|
|
264
|
+
return row;
|
|
265
|
+
},
|
|
266
|
+
async count() {
|
|
267
|
+
return execute({ ...(await astToRun()), operation: 'count', order: [], with: [] }, { table: state.table() });
|
|
268
|
+
},
|
|
269
|
+
async exists() {
|
|
270
|
+
return (await query.count()) > 0;
|
|
271
|
+
},
|
|
272
|
+
async paginate(page = 1, perPage = 20) {
|
|
273
|
+
const total = await query.count();
|
|
274
|
+
const data = await createQuery({ ...state, limit: perPage, offset: (page - 1) * perPage }, runtime).get();
|
|
275
|
+
return { data, total, page, perPage, lastPage: Math.max(1, Math.ceil(total / perPage)) };
|
|
276
|
+
},
|
|
277
|
+
async cursorPaginate(perPage = 20, after) {
|
|
278
|
+
const key = state.table().primaryKey;
|
|
279
|
+
const page = createQuery({
|
|
280
|
+
...state,
|
|
281
|
+
where: after === undefined ? state.where : [...state.where, comparison(key, '>', after)],
|
|
282
|
+
order: [{ field: key, direction: 'asc' }],
|
|
283
|
+
limit: perPage + 1,
|
|
284
|
+
}, runtime);
|
|
285
|
+
const rows = await page.get();
|
|
286
|
+
const data = rows.slice(0, perPage);
|
|
287
|
+
const last = data.at(-1);
|
|
288
|
+
return {
|
|
289
|
+
data,
|
|
290
|
+
nextCursor: rows.length > perPage && last !== undefined ? last[key] : null,
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
toAst,
|
|
294
|
+
// biome-ignore lint/suspicious/noThenProperty: SPEC.md §20 asks a query to be awaitable without a terminal method — that is what PromiseLike means
|
|
295
|
+
then(onFulfilled, onRejected) {
|
|
296
|
+
return run().then(onFulfilled, onRejected);
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
for (const [name, scope] of Object.entries(runtime.scopes)) {
|
|
300
|
+
Object.defineProperty(query, name, {
|
|
301
|
+
enumerable: false,
|
|
302
|
+
value: () => scope(query),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return query;
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relations (SPEC.md §23, §24).
|
|
3
|
+
*
|
|
4
|
+
* A relation names the other side through a thunk, so two models may reference each
|
|
5
|
+
* other without either declaration depending on the other's type.
|
|
6
|
+
*/
|
|
7
|
+
import type { RelationKind } from '@assemora/database';
|
|
8
|
+
/** The minimum a relation needs to know about its target. */
|
|
9
|
+
export type RelatedModel = {
|
|
10
|
+
readonly table: string;
|
|
11
|
+
readonly primaryKey: string;
|
|
12
|
+
};
|
|
13
|
+
export type RelationOptions = {
|
|
14
|
+
/** The column that holds the reference. Derived from the names when omitted. */
|
|
15
|
+
readonly foreignKey?: string;
|
|
16
|
+
/** The column it points at. The target's primary key when omitted. */
|
|
17
|
+
readonly ownerKey?: string;
|
|
18
|
+
/** The join table, for `belongsToMany` only. Named after both tables when omitted. */
|
|
19
|
+
readonly through?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The join table column holding this model's key, for `belongsToMany` only.
|
|
22
|
+
* Derived from this model's table name when omitted — `users` gives `userId`.
|
|
23
|
+
*
|
|
24
|
+
* A model linked to itself has to name both: `userId` twice is not a link.
|
|
25
|
+
*/
|
|
26
|
+
readonly foreignPivotKey?: string;
|
|
27
|
+
/** The join table column holding the target's key, for `belongsToMany` only. */
|
|
28
|
+
readonly relatedPivotKey?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The kind is carried as a literal so a declaration can be told apart by it: only a
|
|
32
|
+
* `belongsToMany` has pivot verbs, and `PivotFields` decides that from the type alone
|
|
33
|
+
* (SPEC.md §24). Defaulted, so `Relation` still names any of them.
|
|
34
|
+
*/
|
|
35
|
+
export type Relation<K extends RelationKind = RelationKind> = RelationOptions & {
|
|
36
|
+
readonly node: 'relation';
|
|
37
|
+
readonly kind: K;
|
|
38
|
+
readonly target: () => RelatedModel;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A thunk returning the model on the other side: `() => Post`.
|
|
42
|
+
*
|
|
43
|
+
* Declared as `unknown` on purpose. Two models normally reference each other, and
|
|
44
|
+
* any structural parameter type — `() => RelatedModel`, even `() => unknown` —
|
|
45
|
+
* makes TypeScript resolve the target while the declaration that needs it is still
|
|
46
|
+
* being computed, which is the circular-reference error that would force every
|
|
47
|
+
* mutual relation to be annotated by hand. Erasing the type at this one boundary
|
|
48
|
+
* keeps the API of SPEC.md §9 and §23 working; what the thunk returns is checked at
|
|
49
|
+
* runtime instead, with a clear error (see ADR-0010).
|
|
50
|
+
*/
|
|
51
|
+
export type RelationTarget = unknown;
|
|
52
|
+
/** This model holds the foreign key. */
|
|
53
|
+
export declare const belongsTo: (target: RelationTarget, options?: RelationOptions) => Relation<'belongsTo'>;
|
|
54
|
+
/** The other model holds the foreign key, and there is at most one row. */
|
|
55
|
+
export declare const hasOne: (target: RelationTarget, options?: RelationOptions) => Relation<'hasOne'>;
|
|
56
|
+
/** The other model holds the foreign key, and there may be many rows. */
|
|
57
|
+
export declare const hasMany: (target: RelationTarget, options?: RelationOptions) => Relation<'hasMany'>;
|
|
58
|
+
/**
|
|
59
|
+
* Both sides are linked through a join table, and the instance carries the verbs that
|
|
60
|
+
* write to it: `user.roles.attach(id)` (SPEC.md §24).
|
|
61
|
+
*/
|
|
62
|
+
export declare const belongsToMany: (target: RelationTarget, options?: RelationOptions) => Relation<'belongsToMany'>;
|
|
63
|
+
export declare const isRelation: (value: unknown) => value is Relation;
|
|
64
|
+
//# sourceMappingURL=relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,gFAAgF;IAChF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI,eAAe,GAAG;IAC9E,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,YAAY,CAAA;CACpC,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAA;AA0BpC,wCAAwC;AACxC,eAAO,MAAM,SAAS,WACZ,cAAc,YACZ,eAAe,KACxB,QAAQ,CAAC,WAAW,CAA2C,CAAA;AAElE,2EAA2E;AAC3E,eAAO,MAAM,MAAM,WAAY,cAAc,YAAY,eAAe,KAAG,QAAQ,CAAC,QAAQ,CACvD,CAAA;AAErC,yEAAyE;AACzE,eAAO,MAAM,OAAO,WAAY,cAAc,YAAY,eAAe,KAAG,QAAQ,CAAC,SAAS,CACxD,CAAA;AAEtC;;;GAGG;AACH,eAAO,MAAM,aAAa,WAChB,cAAc,YACZ,eAAe,KACxB,QAAQ,CAAC,eAAe,CAA+C,CAAA;AAE1E,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,KAAK,IAAI,QAC4C,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const asRelatedModel = (target) => {
|
|
2
|
+
if (typeof target !== 'function') {
|
|
3
|
+
throw new TypeError('A relation target must be a function returning the related model');
|
|
4
|
+
}
|
|
5
|
+
const resolved = target();
|
|
6
|
+
if (typeof resolved !== 'object' ||
|
|
7
|
+
resolved === null ||
|
|
8
|
+
typeof resolved.table !== 'string') {
|
|
9
|
+
throw new TypeError('A relation target must be a function returning the related model');
|
|
10
|
+
}
|
|
11
|
+
return resolved;
|
|
12
|
+
};
|
|
13
|
+
const relation = (kind, target, options = {}) => ({ node: 'relation', kind, target: () => asRelatedModel(target), ...options });
|
|
14
|
+
/** This model holds the foreign key. */
|
|
15
|
+
export const belongsTo = (target, options) => relation('belongsTo', target, options);
|
|
16
|
+
/** The other model holds the foreign key, and there is at most one row. */
|
|
17
|
+
export const hasOne = (target, options) => relation('hasOne', target, options);
|
|
18
|
+
/** The other model holds the foreign key, and there may be many rows. */
|
|
19
|
+
export const hasMany = (target, options) => relation('hasMany', target, options);
|
|
20
|
+
/**
|
|
21
|
+
* Both sides are linked through a join table, and the instance carries the verbs that
|
|
22
|
+
* write to it: `user.roles.attach(id)` (SPEC.md §24).
|
|
23
|
+
*/
|
|
24
|
+
export const belongsToMany = (target, options) => relation('belongsToMany', target, options);
|
|
25
|
+
export const isRelation = (value) => typeof value === 'object' && value !== null && value.node === 'relation';
|
|
26
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAwDA,MAAM,cAAc,GAAG,CAAC,MAAsB,EAAgB,EAAE;IAC9D,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CAAC,kEAAkE,CAAC,CAAA;IACzF,CAAC;IAED,MAAM,QAAQ,GAAI,MAAwB,EAAE,CAAA;IAE5C,IACE,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,IAAI;QACjB,OAAQ,QAAyB,CAAC,KAAK,KAAK,QAAQ,EACpD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,kEAAkE,CAAC,CAAA;IACzF,CAAC;IAED,OAAO,QAAwB,CAAA;AACjC,CAAC,CAAA;AAED,MAAM,QAAQ,GAAG,CACf,IAAO,EACP,MAAsB,EACtB,OAAO,GAAoB,EAAE,EAChB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAA;AAEhG,wCAAwC;AACxC,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,MAAsB,EACtB,OAAyB,EACF,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAElE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAAsB,EAAE,OAAyB,EAAsB,EAAE,CAC9F,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAErC,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAsB,EAAE,OAAyB,EAAuB,EAAE,CAChG,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAEtC;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAsB,EACtB,OAAyB,EACE,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAE1E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAc,EAAqB,EAAE,CAC9D,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA2B,CAAC,IAAI,KAAK,UAAU,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type TransactionPort } from '@assemora/core';
|
|
2
|
+
import type { DatabaseAdapter, DatabaseContext, QueryAst } from '@assemora/database';
|
|
3
|
+
/** Binds the adapter every model will use. Called once, when the application boots. */
|
|
4
|
+
export declare const useAdapter: (adapter: DatabaseAdapter) => void;
|
|
5
|
+
export declare const clearAdapter: () => void;
|
|
6
|
+
export declare const currentAdapter: () => DatabaseAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Every statement the data layer runs (SPEC.md §88).
|
|
9
|
+
*
|
|
10
|
+
* The query builder, a model's `find()`, an instance's writes and the pivot verbs all
|
|
11
|
+
* reach the adapter through here rather than through `currentAdapter().execute`, so
|
|
12
|
+
* there is one place that knows how long a query took — and one place that would have
|
|
13
|
+
* to be edited again for anything else the data layer ever wants to measure.
|
|
14
|
+
*
|
|
15
|
+
* A function, and deliberately not a wrapped adapter. `currentAdapter()` hands the
|
|
16
|
+
* real adapter to the CLI and to `applySchema()`, which reach for methods only the
|
|
17
|
+
* PostgreSQL one has; a wrapper would either lose them, taking `db:migrate` with them,
|
|
18
|
+
* or forward them, and be a second definition of a contract that already has one
|
|
19
|
+
* (SPEC.md §31, ADR-0013).
|
|
20
|
+
*/
|
|
21
|
+
export declare const execute: <T>(query: QueryAst, context: DatabaseContext) => Promise<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Runs an operation inside a transaction. Everything the operation awaits sees the
|
|
24
|
+
* transactional adapter without being told about it.
|
|
25
|
+
*/
|
|
26
|
+
export declare const transaction: <T>(operation: () => Promise<T>) => Promise<T>;
|
|
27
|
+
export declare const dataTransactions: () => TransactionPort;
|
|
28
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAQpF,uFAAuF;AACvF,eAAO,MAAM,UAAU,YAAa,eAAe,KAAG,IAErD,CAAA;AAED,eAAO,MAAM,YAAY,QAAO,IAE/B,CAAA;AAED,eAAO,MAAM,cAAc,QAAO,eAUjC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,GAAU,CAAC,SAAS,QAAQ,WAAW,eAAe,KAAG,OAAO,CAAC,CAAC,CAiBrF,CAAA;AAyBD;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAkBrE,CAAA;AAwDD,eAAO,MAAM,gBAAgB,QAAO,eAIlC,CAAA"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the data layer finds its adapter, and how it reaches it (SPEC.md §33, §88).
|
|
3
|
+
*
|
|
4
|
+
* The current adapter and the current transaction travel through AsyncLocalStorage,
|
|
5
|
+
* so a developer never passes `tx` by hand. `execute()` below is the one seam every
|
|
6
|
+
* query in this package runs through, which is what makes a query measurable without
|
|
7
|
+
* anything having to wrap the adapter itself.
|
|
8
|
+
*/
|
|
9
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
10
|
+
import { ConfigurationError } from '@assemora/core';
|
|
11
|
+
import { recordQuery } from './slow-queries.js';
|
|
12
|
+
let ambient;
|
|
13
|
+
const scoped = new AsyncLocalStorage();
|
|
14
|
+
/** Binds the adapter every model will use. Called once, when the application boots. */
|
|
15
|
+
export const useAdapter = (adapter) => {
|
|
16
|
+
ambient = adapter;
|
|
17
|
+
};
|
|
18
|
+
export const clearAdapter = () => {
|
|
19
|
+
ambient = undefined;
|
|
20
|
+
};
|
|
21
|
+
export const currentAdapter = () => {
|
|
22
|
+
const adapter = scoped.getStore() ?? ambient;
|
|
23
|
+
if (adapter === undefined) {
|
|
24
|
+
throw new ConfigurationError('No database adapter is registered. Call useAdapter() before querying.');
|
|
25
|
+
}
|
|
26
|
+
return adapter;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Every statement the data layer runs (SPEC.md §88).
|
|
30
|
+
*
|
|
31
|
+
* The query builder, a model's `find()`, an instance's writes and the pivot verbs all
|
|
32
|
+
* reach the adapter through here rather than through `currentAdapter().execute`, so
|
|
33
|
+
* there is one place that knows how long a query took — and one place that would have
|
|
34
|
+
* to be edited again for anything else the data layer ever wants to measure.
|
|
35
|
+
*
|
|
36
|
+
* A function, and deliberately not a wrapped adapter. `currentAdapter()` hands the
|
|
37
|
+
* real adapter to the CLI and to `applySchema()`, which reach for methods only the
|
|
38
|
+
* PostgreSQL one has; a wrapper would either lose them, taking `db:migrate` with them,
|
|
39
|
+
* or forward them, and be a second definition of a contract that already has one
|
|
40
|
+
* (SPEC.md §31, ADR-0013).
|
|
41
|
+
*/
|
|
42
|
+
export const execute = async (query, context) => {
|
|
43
|
+
const startedAt = performance.now();
|
|
44
|
+
try {
|
|
45
|
+
const answer = await currentAdapter().execute(query, context);
|
|
46
|
+
recordQuery(query, performance.now() - startedAt, answer);
|
|
47
|
+
return answer;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
// Timed either way. A query that ran for four seconds and then failed is the one
|
|
51
|
+
// most worth knowing about, and a log that goes quiet exactly when the database
|
|
52
|
+
// starts refusing is the wrong log.
|
|
53
|
+
recordQuery(query, performance.now() - startedAt);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Work waiting for the OUTERMOST transaction to commit (ADR-0023).
|
|
59
|
+
*
|
|
60
|
+
* The store is the list itself, and a nested `transaction()` never replaces it — so
|
|
61
|
+
* every layer inside registers against the one commit that actually makes rows
|
|
62
|
+
* durable. A savepoint is not that commit: everything written under it is still the
|
|
63
|
+
* caller's to undo.
|
|
64
|
+
*/
|
|
65
|
+
const waiting = new AsyncLocalStorage();
|
|
66
|
+
const drain = async (work) => {
|
|
67
|
+
for (const item of work) {
|
|
68
|
+
try {
|
|
69
|
+
await item();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Swallowed on purpose, and documented on `TransactionPort.afterCommit`: this
|
|
73
|
+
// runs after the commit, so there is no caller left to reject to, and one
|
|
74
|
+
// registration's failure must not cancel the next one's. Whoever registers
|
|
75
|
+
// reports its own failure — the command bus logs the job it could not queue.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Runs an operation inside a transaction. Everything the operation awaits sees the
|
|
81
|
+
* transactional adapter without being told about it.
|
|
82
|
+
*/
|
|
83
|
+
export const transaction = (operation) => {
|
|
84
|
+
const adapter = currentAdapter();
|
|
85
|
+
const run = () => adapter.transaction(() => scoped.run(adapter, operation));
|
|
86
|
+
// Nested: the caller owns the commit, so anything registered inside keeps waiting
|
|
87
|
+
// for theirs.
|
|
88
|
+
if (waiting.getStore() !== undefined)
|
|
89
|
+
return run();
|
|
90
|
+
const work = [];
|
|
91
|
+
// `.then` is attached outside `waiting.run`, so the work drains with no transaction
|
|
92
|
+
// in scope — a job dispatched by after-commit work has nothing left to wait for,
|
|
93
|
+
// and would otherwise be appended to a list that is already being read.
|
|
94
|
+
return waiting.run(work, run).then(async (value) => {
|
|
95
|
+
await drain(work);
|
|
96
|
+
return value;
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Holds `work` until the outermost transaction commits, and drops it if that
|
|
101
|
+
* transaction is undone. With none open, "after commit" is "now".
|
|
102
|
+
*/
|
|
103
|
+
const afterCommit = (work) => {
|
|
104
|
+
const pending = waiting.getStore();
|
|
105
|
+
if (pending === undefined)
|
|
106
|
+
return work();
|
|
107
|
+
pending.push(work);
|
|
108
|
+
return Promise.resolve();
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* The transaction stage of the command pipeline (SPEC.md §14, ADR-0008).
|
|
112
|
+
*
|
|
113
|
+
* `core` declares the port and cannot reach a database; this is the implementation
|
|
114
|
+
* the data layer registers, so a handler that writes several rows either commits all
|
|
115
|
+
* of them or none.
|
|
116
|
+
*/
|
|
117
|
+
/** Nothing else can be thrown by accident, so nothing else can be mistaken for it. */
|
|
118
|
+
const ROLLBACK = Symbol('assemora.rollback');
|
|
119
|
+
/**
|
|
120
|
+
* Runs the operation inside a transaction that is always undone, and still answers
|
|
121
|
+
* with what it returned (SPEC.md §73).
|
|
122
|
+
*
|
|
123
|
+
* Rejecting is the only way to make an adapter roll back — that is how both of them
|
|
124
|
+
* implement it — so the value is carried out past the rejection rather than through
|
|
125
|
+
* it. A real failure inside the operation is rethrown untouched.
|
|
126
|
+
*/
|
|
127
|
+
const rollingBack = async (operation) => {
|
|
128
|
+
const carried = [];
|
|
129
|
+
try {
|
|
130
|
+
await transaction(async () => {
|
|
131
|
+
carried.push(await operation());
|
|
132
|
+
throw ROLLBACK;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error !== ROLLBACK)
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
const [value] = carried;
|
|
140
|
+
if (carried.length === 0) {
|
|
141
|
+
throw new ConfigurationError('The transaction was undone before the operation answered');
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
};
|
|
145
|
+
export const dataTransactions = () => ({
|
|
146
|
+
run: (operation, options) => options?.rollback === true ? rollingBack(operation) : transaction(operation),
|
|
147
|
+
afterCommit,
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|