@fougere/adapter-sql 0.2.0-alpha.2
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 +21 -0
- package/README.md +18 -0
- package/dist/check.d.ts +41 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +67 -0
- package/dist/check.js.map +1 -0
- package/dist/crud.d.ts +153 -0
- package/dist/crud.d.ts.map +1 -0
- package/dist/crud.js +458 -0
- package/dist/crud.js.map +1 -0
- package/dist/ddl.d.ts +87 -0
- package/dist/ddl.d.ts.map +1 -0
- package/dist/ddl.js +194 -0
- package/dist/ddl.js.map +1 -0
- package/dist/dialect.d.ts +54 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +109 -0
- package/dist/dialect.js.map +1 -0
- package/dist/diff.d.ts +86 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +168 -0
- package/dist/diff.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/setup.d.ts +37 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +40 -0
- package/dist/setup.js.map +1 -0
- package/dist/table.d.ts +169 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +292 -0
- package/dist/table.js.map +1 -0
- package/dist/values.d.ts +32 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +70 -0
- package/dist/values.js.map +1 -0
- package/package.json +56 -0
package/dist/crud.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SqlEntityOrm — per-entity ORM over Kysely, one implementation for every engine.
|
|
3
|
+
*
|
|
4
|
+
* Structurally matches @fougere/core's EntityOrm (duck typed, no dep). There is
|
|
5
|
+
* no generated table object: Kysely addresses tables and columns by name, so the
|
|
6
|
+
* entity stays the only description. The field↔column mapping is explicit rather
|
|
7
|
+
* than a global plugin — auth tables carry their own naming and must not be
|
|
8
|
+
* rewritten behind the caller's back.
|
|
9
|
+
*
|
|
10
|
+
* `create` and `update` re-read the row instead of using `RETURNING`: the
|
|
11
|
+
* contract is to hand back the COMPLETE row, including defaults realised by SQL.
|
|
12
|
+
* That also makes the code identical on MySQL and SQL Server, which have no
|
|
13
|
+
* `RETURNING` clause.
|
|
14
|
+
*/
|
|
15
|
+
import { sql } from 'kysely';
|
|
16
|
+
import { applyCreate, applyUpdate, schemaOf } from '@fougere/schema';
|
|
17
|
+
import { toTable, toTableName } from './table.js';
|
|
18
|
+
import { resolveDialect } from './dialect.js';
|
|
19
|
+
import { codecsOf } from './values.js';
|
|
20
|
+
/**
|
|
21
|
+
* The primary key, read off the role axis.
|
|
22
|
+
*
|
|
23
|
+
* Used to answer "what identifies a row" — where to point a WHERE, what a cursor
|
|
24
|
+
* carries. The generated ids and managed timestamps that used to be computed here
|
|
25
|
+
* moved to `applyCreate`/`applyUpdate` (`@fougere/schema`): nothing in them was about
|
|
26
|
+
* SQL, and every other storage was re-deriving them from scratch.
|
|
27
|
+
*/
|
|
28
|
+
function analyzeFields(entity) {
|
|
29
|
+
const pkNames = Object.entries(entity.getFields())
|
|
30
|
+
.filter(([, field]) => field.role?.primary)
|
|
31
|
+
.map(([name]) => name);
|
|
32
|
+
return { pk: { names: pkNames, isComposite: pkNames.length > 1 } };
|
|
33
|
+
}
|
|
34
|
+
/** Slice a key set into what one statement may bind. One slice when it already fits. */
|
|
35
|
+
function chunks(values, size) {
|
|
36
|
+
if (values.length <= size)
|
|
37
|
+
return [values];
|
|
38
|
+
const out = [];
|
|
39
|
+
for (let i = 0; i < values.length; i += size)
|
|
40
|
+
out.push(values.slice(i, i + size));
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/** Stand-in for "no upper bound", where the engine still demands a LIMIT. */
|
|
44
|
+
const UNBOUNDED = 1_000_000_000;
|
|
45
|
+
function pick(obj, keys) {
|
|
46
|
+
const result = {};
|
|
47
|
+
for (const key of keys)
|
|
48
|
+
if (key in obj)
|
|
49
|
+
result[key] = obj[key];
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
function pickList(list, keys) {
|
|
53
|
+
const result = list.map((item) => pick(item, keys));
|
|
54
|
+
result.total = list.total;
|
|
55
|
+
result.endCursor = list.endCursor;
|
|
56
|
+
result.hasMore = list.hasMore;
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
export class SqlEntityOrm {
|
|
60
|
+
db;
|
|
61
|
+
table;
|
|
62
|
+
pk;
|
|
63
|
+
/** The axes `applyCreate`/`applyUpdate` read — held once, they are asked per write. */
|
|
64
|
+
fields;
|
|
65
|
+
selectFields;
|
|
66
|
+
/** field → column and back; the entity names travel, the SQL names stay inside. */
|
|
67
|
+
toColumn = new Map();
|
|
68
|
+
toField = new Map();
|
|
69
|
+
/** field → the value pair a driver needs; only for the shapes a driver can't bind. */
|
|
70
|
+
codecs;
|
|
71
|
+
/** How many keys one statement may carry here — see `Dialect.maxBindings`. */
|
|
72
|
+
maxBindings;
|
|
73
|
+
/** How this engine spells an upsert, or `false` when it cannot — see `Dialect.upsert`. */
|
|
74
|
+
upsertClause;
|
|
75
|
+
constructor(db, source, tableName, selectFields, dialect = 'sqlite') {
|
|
76
|
+
this.db = db;
|
|
77
|
+
const resolved = resolveDialect(dialect);
|
|
78
|
+
this.maxBindings = resolved.maxBindings;
|
|
79
|
+
this.upsertClause = resolved.upsert;
|
|
80
|
+
// Normalized once: the table projection and the axis analysis below both read the
|
|
81
|
+
// schema, and a card handed to each separately would be rebuilt twice into two
|
|
82
|
+
// unrelated field objects. Past this line nothing knows which form arrived.
|
|
83
|
+
const entity = schemaOf(source);
|
|
84
|
+
this.table = toTable(tableName, entity);
|
|
85
|
+
for (const column of this.table.columns) {
|
|
86
|
+
this.toColumn.set(column.field, column.name);
|
|
87
|
+
this.toField.set(column.name, column.field);
|
|
88
|
+
}
|
|
89
|
+
this.codecs = codecsOf(this.table.columns);
|
|
90
|
+
this.pk = analyzeFields(entity).pk;
|
|
91
|
+
this.fields = entity.getFields();
|
|
92
|
+
this.selectFields = selectFields;
|
|
93
|
+
}
|
|
94
|
+
/** The Kysely instance this ORM wraps — no judge sits behind it. See EntityOrm.client. */
|
|
95
|
+
get client() {
|
|
96
|
+
return this.db;
|
|
97
|
+
}
|
|
98
|
+
/** Returns a scoped ORM that restricts all read results to the fields of the given schema. */
|
|
99
|
+
output(schema) {
|
|
100
|
+
const scoped = Object.create(this);
|
|
101
|
+
scoped.selectFields = new Set(Object.keys(schema.getFields()));
|
|
102
|
+
return scoped;
|
|
103
|
+
}
|
|
104
|
+
resolveSelect(options) {
|
|
105
|
+
if (options?.select)
|
|
106
|
+
return new Set(Object.keys(options.select.getFields()));
|
|
107
|
+
return this.selectFields;
|
|
108
|
+
}
|
|
109
|
+
column(field) {
|
|
110
|
+
return this.toColumn.get(field) ?? field;
|
|
111
|
+
}
|
|
112
|
+
/** The value a driver can bind — `true` becomes 1, a Date becomes its ISO string. */
|
|
113
|
+
write(field, value) {
|
|
114
|
+
return this.codecs.get(field)?.write(value) ?? value;
|
|
115
|
+
}
|
|
116
|
+
/** Entity keys → column keys, and entity values → bindable values. */
|
|
117
|
+
toRow(data) {
|
|
118
|
+
const row = {};
|
|
119
|
+
for (const [key, value] of Object.entries(data))
|
|
120
|
+
row[this.column(key)] = this.write(key, value);
|
|
121
|
+
return row;
|
|
122
|
+
}
|
|
123
|
+
/** Column keys → entity keys, and column values → the values the entity declares. */
|
|
124
|
+
fromRow(row) {
|
|
125
|
+
const data = {};
|
|
126
|
+
for (const [key, value] of Object.entries(row)) {
|
|
127
|
+
const field = this.toField.get(key) ?? key;
|
|
128
|
+
data[field] = this.codecs.get(field)?.read(value) ?? value;
|
|
129
|
+
}
|
|
130
|
+
return data;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Apply a primary-key filter (simple or composite).
|
|
134
|
+
*
|
|
135
|
+
* The key crosses to the column exactly like every other value — `whereAll` states
|
|
136
|
+
* the rule two lines below and this did not follow it. It cost nothing while every
|
|
137
|
+
* generated key was a string; a key that holds a Date (`primary(created())`) inserted
|
|
138
|
+
* fine and then failed its own re-read, with the row already persisted.
|
|
139
|
+
*/
|
|
140
|
+
wherePk(query, id) {
|
|
141
|
+
if (this.pk.isComposite) {
|
|
142
|
+
const obj = id;
|
|
143
|
+
return this.pk.names.reduce((q, name) => q.where(this.column(name), '=', this.write(name, obj[name])), query);
|
|
144
|
+
}
|
|
145
|
+
const name = this.pk.names[0];
|
|
146
|
+
return query.where(this.column(name), '=', this.write(name, id));
|
|
147
|
+
}
|
|
148
|
+
// A filter compares against the COLUMN, so its value crosses the same way a write
|
|
149
|
+
// does: `findBy({ done: true })` has to look for 1, not for `true`.
|
|
150
|
+
//
|
|
151
|
+
// A criterion may name a SET — `where: { id: [a, b, c] }` is `IN`, one query for a
|
|
152
|
+
// whole page. Without it a relation had no batch form at all: the GraphQL `one`
|
|
153
|
+
// resolver read row by row (50 calls for a page of 50, measured), while its `many`
|
|
154
|
+
// dual already went through this same door. An empty set matches nothing, said in
|
|
155
|
+
// SQL rather than by returning the whole table.
|
|
156
|
+
whereAll(query, criteria) {
|
|
157
|
+
return Object.entries(criteria).reduce((q, [key, value]) => Array.isArray(value)
|
|
158
|
+
? q.where(this.column(key), 'in', [...new Set(value)].map((v) => this.write(key, v)))
|
|
159
|
+
: q.where(this.column(key), '=', this.write(key, value)), query);
|
|
160
|
+
}
|
|
161
|
+
async list(options) {
|
|
162
|
+
let query = this.db.selectFrom(this.table.name).selectAll();
|
|
163
|
+
// The criteria a caller states — `list({ where: { orderId } })`, and the whole of
|
|
164
|
+
// `listBy`. Named `where` rather than spread across the options so an unknown key
|
|
165
|
+
// stays what it always was (ignored) instead of silently becoming a filter.
|
|
166
|
+
if (options?.where) {
|
|
167
|
+
// `list` is the ONE read that cannot be split: a limit and an order do not
|
|
168
|
+
// recompose across slices, so an oversized set here is refused rather than
|
|
169
|
+
// truncated — and the gesture that does handle it is named.
|
|
170
|
+
this.refuseOversized(options.where, 'list');
|
|
171
|
+
query = this.whereAll(query, options.where);
|
|
172
|
+
}
|
|
173
|
+
// Cursor-based: fetch after a given id (uses the first PK field).
|
|
174
|
+
if (options?.after) {
|
|
175
|
+
query = query.where(this.column(this.pk.names[0]), '>', options.after);
|
|
176
|
+
}
|
|
177
|
+
if (options?.orderBy && this.toColumn.has(options.orderBy)) {
|
|
178
|
+
query = query.orderBy(this.column(options.orderBy), options.order === 'desc' ? 'desc' : 'asc');
|
|
179
|
+
}
|
|
180
|
+
const limit = options?.limit;
|
|
181
|
+
// Fetch one extra to determine hasMore.
|
|
182
|
+
if (limit !== undefined)
|
|
183
|
+
query = query.limit(limit + 1);
|
|
184
|
+
const offset = options?.page !== undefined && limit !== undefined
|
|
185
|
+
? (options.page - 1) * limit
|
|
186
|
+
: options?.offset;
|
|
187
|
+
if (offset !== undefined && offset > 0) {
|
|
188
|
+
// SQLite and MySQL reject OFFSET without a preceding LIMIT — an offset on
|
|
189
|
+
// its own needs an upper bound that means "everything after".
|
|
190
|
+
if (limit === undefined)
|
|
191
|
+
query = query.limit(UNBOUNDED);
|
|
192
|
+
query = query.offset(offset);
|
|
193
|
+
}
|
|
194
|
+
const rows = (await query.execute()).map((row) => this.fromRow(row));
|
|
195
|
+
const hasMore = limit !== undefined && rows.length > limit;
|
|
196
|
+
const data = hasMore ? rows.slice(0, limit) : rows;
|
|
197
|
+
const result = data;
|
|
198
|
+
result.hasMore = hasMore;
|
|
199
|
+
if (data.length > 0) {
|
|
200
|
+
result.endCursor = String(data[data.length - 1][this.pk.names[0]] ?? '');
|
|
201
|
+
}
|
|
202
|
+
// Count is opt-in — a separate query, over the same FILTER.
|
|
203
|
+
//
|
|
204
|
+
// It used to count the whole table: `list({ where: { authorId }, count: true })`
|
|
205
|
+
// returned this author's page beside everybody's total, so a paginator computed the
|
|
206
|
+
// wrong number of pages and a tenant learned how many rows the other tenants have.
|
|
207
|
+
// `where` is the filter and belongs here; `after`, `limit` and `offset` are the page
|
|
208
|
+
// and do not — `total` is what the query matches, not what this page holds.
|
|
209
|
+
if (options?.count) {
|
|
210
|
+
let counting = this.db.selectFrom(this.table.name).select((eb) => eb.fn.countAll().as('count'));
|
|
211
|
+
if (options.where)
|
|
212
|
+
counting = this.whereAll(counting, options.where);
|
|
213
|
+
const row = await counting.executeTakeFirst();
|
|
214
|
+
result.total = Number(row?.count ?? 0);
|
|
215
|
+
}
|
|
216
|
+
const sel = this.resolveSelect(options);
|
|
217
|
+
return sel ? pickList(result, sel) : result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* One query for N keys, never N queries — what every page-level read stands on:
|
|
221
|
+
* a computed field, a relation, a resolver on the other side of a wire.
|
|
222
|
+
*
|
|
223
|
+
* A composite key has no list form: it is refused by name rather than answering a
|
|
224
|
+
* partial result that reads as complete.
|
|
225
|
+
*/
|
|
226
|
+
async findByKeys(ids, options) {
|
|
227
|
+
if (this.pk.isComposite) {
|
|
228
|
+
throw new Error(`${this.table.name}.findByKeys: the primary key is composite (${this.pk.names.join(', ')}) — read them one by one, or filter with \`findAllBy\`.`);
|
|
229
|
+
}
|
|
230
|
+
if (ids.length === 0)
|
|
231
|
+
return new Map();
|
|
232
|
+
const name = this.pk.names[0];
|
|
233
|
+
const sel = this.resolveSelect(options);
|
|
234
|
+
const found = new Map();
|
|
235
|
+
// Split, because a key set comes from a page and a page has no ceiling. One
|
|
236
|
+
// statement per slice, merged here — the caller never learns there were several.
|
|
237
|
+
for (const slice of chunks([...new Set(ids)], this.maxBindings)) {
|
|
238
|
+
const rows = await this.db
|
|
239
|
+
.selectFrom(this.table.name)
|
|
240
|
+
.selectAll()
|
|
241
|
+
.where(this.column(name), 'in', slice.map((id) => this.write(name, id)))
|
|
242
|
+
.execute();
|
|
243
|
+
for (const row of rows) {
|
|
244
|
+
const data = this.fromRow(row);
|
|
245
|
+
found.set(String(data[name]), sel ? pick(data, sel) : data);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return found;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The other direction of a relation, in one query — see the port's `findAllByKeys`.
|
|
252
|
+
*
|
|
253
|
+
* The grouping key is read off the ROW rather than trusted from the request: a codec
|
|
254
|
+
* may write a value one way and read it back another, and a group keyed on the
|
|
255
|
+
* request's spelling would then be empty while the rows sit there.
|
|
256
|
+
*/
|
|
257
|
+
async findAllByKeys(field, keys, options) {
|
|
258
|
+
const grouped = new Map();
|
|
259
|
+
if (keys.length === 0)
|
|
260
|
+
return grouped;
|
|
261
|
+
const rows = await this.findAllBy({ [field]: [...keys] }, options);
|
|
262
|
+
for (const row of rows) {
|
|
263
|
+
const key = String(row[field]);
|
|
264
|
+
const held = grouped.get(key);
|
|
265
|
+
if (held)
|
|
266
|
+
held.push(row);
|
|
267
|
+
else
|
|
268
|
+
grouped.set(key, [row]);
|
|
269
|
+
}
|
|
270
|
+
return grouped;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Write the row, or make the existing one look like this — one statement.
|
|
274
|
+
*
|
|
275
|
+
* The gesture an import needs and the port did not have: `create` throws on the
|
|
276
|
+
* second run (`UNIQUE constraint failed`), so re-reading anything meant deleting
|
|
277
|
+
* first. Measured pulling 500 rows from an API twice.
|
|
278
|
+
*
|
|
279
|
+
* Both lifecycles are realized, each on the side it belongs to: `applyCreate` fills
|
|
280
|
+
* what a first write owes (a generated key, `created()`, a declared default) and
|
|
281
|
+
* `applyUpdate` stamps what every write owes (`updated()`). On conflict the key and
|
|
282
|
+
* the creation stamps are left alone — a row keeps the moment it appeared, whatever
|
|
283
|
+
* later overwrites say.
|
|
284
|
+
*/
|
|
285
|
+
async upsert(input, options) {
|
|
286
|
+
if (this.upsertClause === false) {
|
|
287
|
+
throw new Error(`${this.table.name}.upsert(): this engine has no upsert clause — read with findById ` +
|
|
288
|
+
`and call create or update, and know that the pair is not atomic.`);
|
|
289
|
+
}
|
|
290
|
+
// `applyUpdate` FIRST: `updated()` declares both `create: 'now'` and
|
|
291
|
+
// `update: 'now'`, so filling the creation side first leaves nothing for the
|
|
292
|
+
// update side to stamp — the row would carry the moment it was inserted forever.
|
|
293
|
+
const data = applyCreate(this.fields, applyUpdate(this.fields, input));
|
|
294
|
+
// Never overwritten by a later write: the key identifies the row, and a stamp that
|
|
295
|
+
// is create-ONLY records when it appeared. One that is also `update: 'now'` is the
|
|
296
|
+
// opposite — it exists to move.
|
|
297
|
+
const frozen = this.frozenColumns();
|
|
298
|
+
const row = this.toRow(data);
|
|
299
|
+
const replaced = Object.fromEntries(Object.entries(row).filter(([column]) => !frozen.has(column)));
|
|
300
|
+
const insert = this.db.insertInto(this.table.name).values(row);
|
|
301
|
+
await (this.upsertClause === 'on conflict'
|
|
302
|
+
? insert.onConflict((oc) => oc.columns(this.pk.names.map((n) => this.column(n))).doUpdateSet(replaced))
|
|
303
|
+
: insert.onDuplicateKeyUpdate(replaced)).execute();
|
|
304
|
+
const id = this.pk.isComposite
|
|
305
|
+
? Object.fromEntries(this.pk.names.map((n) => [n, data[n]]))
|
|
306
|
+
: data[this.pk.names[0]];
|
|
307
|
+
return (await this.findById(id, options));
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Upsert a whole page in one statement — what an import writes through.
|
|
311
|
+
*
|
|
312
|
+
* Row by row, 500 rows were 500 statements (measured pulling an API); the shape of
|
|
313
|
+
* an import is a page, so the write should be one too. Sliced like every other batch,
|
|
314
|
+
* but by rows × COLUMNS: a statement binds values, not rows, so the ceiling divides.
|
|
315
|
+
*
|
|
316
|
+
* Answers how many rows were written and not the rows themselves. `create` hands back
|
|
317
|
+
* the complete row because a caller acts on it; an import acts on none of them, and
|
|
318
|
+
* re-reading a page to satisfy a symmetry nobody uses would double the work.
|
|
319
|
+
*/
|
|
320
|
+
async upsertAll(inputs, _options) {
|
|
321
|
+
if (this.upsertClause === false) {
|
|
322
|
+
throw new Error(`${this.table.name}.upsertAll(): this engine has no upsert clause — write the rows ` +
|
|
323
|
+
`one by one with create or update, and know that the pair is not atomic.`);
|
|
324
|
+
}
|
|
325
|
+
if (inputs.length === 0)
|
|
326
|
+
return 0;
|
|
327
|
+
const rows = inputs.map((input) => this.toRow(applyCreate(this.fields, applyUpdate(this.fields, input))));
|
|
328
|
+
const frozen = this.frozenColumns();
|
|
329
|
+
const columns = new Set(rows.flatMap((row) => Object.keys(row)));
|
|
330
|
+
const replaced = Object.fromEntries([...columns].filter((column) => !frozen.has(column)).map((column) => [column, sql.ref(`excluded.${column}`)]));
|
|
331
|
+
// A statement binds VALUES: one row costs as many as it has columns.
|
|
332
|
+
const perStatement = Math.max(1, Math.floor(this.maxBindings / Math.max(1, columns.size)));
|
|
333
|
+
let written = 0;
|
|
334
|
+
for (const slice of chunks([...rows], perStatement)) {
|
|
335
|
+
const insert = this.db.insertInto(this.table.name).values(slice);
|
|
336
|
+
await (this.upsertClause === 'on conflict'
|
|
337
|
+
? insert.onConflict((oc) => oc.columns(this.pk.names.map((n) => this.column(n))).doUpdateSet(replaced))
|
|
338
|
+
: insert.onDuplicateKeyUpdate(replaced)).execute();
|
|
339
|
+
written += slice.length;
|
|
340
|
+
}
|
|
341
|
+
return written;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* The COLUMNS a later write must not touch: the key, and a stamp that is create-only.
|
|
345
|
+
* One that is also `update: 'now'` is the opposite — it exists to move.
|
|
346
|
+
*/
|
|
347
|
+
frozenColumns() {
|
|
348
|
+
return new Set([
|
|
349
|
+
...this.pk.names.map((name) => this.column(name)),
|
|
350
|
+
...Object.entries(this.fields)
|
|
351
|
+
.filter(([, field]) => field.lifecycle?.create === 'now' && field.lifecycle?.update !== 'now')
|
|
352
|
+
.map(([name]) => this.column(name)),
|
|
353
|
+
]);
|
|
354
|
+
}
|
|
355
|
+
async findById(id, options) {
|
|
356
|
+
const row = await this.wherePk(this.db.selectFrom(this.table.name).selectAll(), id).executeTakeFirst();
|
|
357
|
+
if (!row)
|
|
358
|
+
return undefined;
|
|
359
|
+
const data = this.fromRow(row);
|
|
360
|
+
const sel = this.resolveSelect(options);
|
|
361
|
+
return sel ? pick(data, sel) : data;
|
|
362
|
+
}
|
|
363
|
+
async findBy(criteria, options) {
|
|
364
|
+
const row = await this.whereAll(this.db.selectFrom(this.table.name).selectAll(), criteria)
|
|
365
|
+
.limit(1)
|
|
366
|
+
.executeTakeFirst();
|
|
367
|
+
if (!row)
|
|
368
|
+
return undefined;
|
|
369
|
+
const data = this.fromRow(row);
|
|
370
|
+
const sel = this.resolveSelect(options);
|
|
371
|
+
return sel ? pick(data, sel) : data;
|
|
372
|
+
}
|
|
373
|
+
async findAllBy(criteria, options) {
|
|
374
|
+
const sel = this.resolveSelect(options);
|
|
375
|
+
const out = [];
|
|
376
|
+
// A set criterion may hold more values than the engine binds. Splitting is safe
|
|
377
|
+
// HERE because this read has no page and no order: the slices simply concatenate.
|
|
378
|
+
for (const slice of this.splitCriteria(criteria)) {
|
|
379
|
+
const rows = await this.whereAll(this.db.selectFrom(this.table.name).selectAll(), slice).execute();
|
|
380
|
+
for (const row of rows) {
|
|
381
|
+
const data = this.fromRow(row);
|
|
382
|
+
out.push(sel ? pick(data, sel) : data);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* One criteria object per statement — the oversized set is the one that splits.
|
|
389
|
+
*
|
|
390
|
+
* Only ONE criterion may be split: two split sets would need their cross product,
|
|
391
|
+
* which is a different query, so the second is refused rather than silently wrong.
|
|
392
|
+
*/
|
|
393
|
+
refuseOversized(criteria, op) {
|
|
394
|
+
for (const [key, value] of Object.entries(criteria)) {
|
|
395
|
+
if (!Array.isArray(value) || new Set(value).size <= this.maxBindings)
|
|
396
|
+
continue;
|
|
397
|
+
throw new Error(`${this.table.name}.${op}(): \`${key}\` holds ${new Set(value).size} values and this engine ` +
|
|
398
|
+
`binds ${this.maxBindings} — a page and an order cannot be split across statements. ` +
|
|
399
|
+
`Use \`findAllByKeys('${key}', keys)\`, which reads them in slices and groups the answer.`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
splitCriteria(criteria) {
|
|
403
|
+
const oversized = Object.entries(criteria)
|
|
404
|
+
.filter(([, value]) => Array.isArray(value) && new Set(value).size > this.maxBindings);
|
|
405
|
+
if (oversized.length === 0)
|
|
406
|
+
return [criteria];
|
|
407
|
+
if (oversized.length > 1) {
|
|
408
|
+
throw new Error(`${this.table.name}: ${oversized.map(([key]) => `\`${key}\``).join(' and ')} each hold more than ` +
|
|
409
|
+
`${this.maxBindings} values — split one of them at the call site, they cannot both be sliced.`);
|
|
410
|
+
}
|
|
411
|
+
const [key, values] = oversized[0];
|
|
412
|
+
return chunks([...new Set(values)], this.maxBindings)
|
|
413
|
+
.map((slice) => ({ ...criteria, [key]: slice }));
|
|
414
|
+
}
|
|
415
|
+
async create(input, options) {
|
|
416
|
+
// The lifecycle axis, realized where it is declared — a generated id, a stamped
|
|
417
|
+
// `createdAt`, a declared default. The column DEFAULT below still holds for a
|
|
418
|
+
// writer that is not us, the way a CHECK does; nothing depends on it any more.
|
|
419
|
+
const data = applyCreate(this.fields, input);
|
|
420
|
+
await this.db.insertInto(this.table.name).values(this.toRow(data)).execute();
|
|
421
|
+
// Contract: create returns the COMPLETE row (validation judges absence, it
|
|
422
|
+
// never fills) — re-read so SQL-realised defaults appear. Same move as update().
|
|
423
|
+
const id = this.pk.isComposite
|
|
424
|
+
? Object.fromEntries(this.pk.names.map((n) => [n, data[n]]))
|
|
425
|
+
: data[this.pk.names[0]];
|
|
426
|
+
const created = id !== undefined ? await this.findById(id) : undefined;
|
|
427
|
+
const result = created ?? data;
|
|
428
|
+
const sel = this.resolveSelect(options);
|
|
429
|
+
return sel ? pick(result, sel) : result;
|
|
430
|
+
}
|
|
431
|
+
async update(id, input, options) {
|
|
432
|
+
const data = applyUpdate(this.fields, input);
|
|
433
|
+
await this.wherePk(this.db.updateTable(this.table.name).set(this.toRow(data)), id).execute();
|
|
434
|
+
const updated = await this.findById(id);
|
|
435
|
+
const result = updated ?? (typeof id === 'string' ? { id, ...data } : { ...id, ...data });
|
|
436
|
+
const sel = this.resolveSelect(options);
|
|
437
|
+
return sel ? pick(result, sel) : result;
|
|
438
|
+
}
|
|
439
|
+
async delete(id) {
|
|
440
|
+
const before = await this.findById(id);
|
|
441
|
+
if (!before)
|
|
442
|
+
return false;
|
|
443
|
+
await this.wherePk(this.db.deleteFrom(this.table.name), id).execute();
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Create an OrmFactory backed by Kysely — same call shape on every engine.
|
|
449
|
+
*
|
|
450
|
+
* ```ts
|
|
451
|
+
* const app = await createApp({ createContainer, ormFactory: createOrmFactory(db) });
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
export function createOrmFactory(db, options, dialect = 'sqlite') {
|
|
455
|
+
const resolve = options?.tableName ?? toTableName;
|
|
456
|
+
return (entity, name) => new SqlEntityOrm(db, entity, resolve(name), undefined, dialect);
|
|
457
|
+
}
|
|
458
|
+
//# sourceMappingURL=crud.js.map
|
package/dist/crud.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.js","sourceRoot":"","sources":["../src/crud.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,GAAG,EAAe,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAmD,MAAM,iBAAiB,CAAC;AACtH,OAAO,EAAE,OAAO,EAAE,WAAW,EAAiB,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,cAAc,EAAoB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAmB,MAAM,aAAa,CAAC;AA4BxD;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,MAAkB;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;SAC/C,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAEzB,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC;AACrE,CAAC;AAED,wFAAwF;AACxF,SAAS,MAAM,CAAI,MAAW,EAAE,IAAY;IAC1C,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAU,EAAE,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAClF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6EAA6E;AAC7E,MAAM,SAAS,GAAG,aAAa,CAAC;AAEhC,SAAS,IAAI,CAAoC,GAAM,EAAE,IAAiB;IACxE,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,IAAI,GAAG,IAAI,GAAG;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/D,OAAO,MAAW,CAAC;AACrB,CAAC;AAED,SAAS,QAAQ,CAAoC,IAAmB,EAAE,IAAiB;IACzF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAkB,CAAC;IACrE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAC1B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAClC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,OAAO,YAAY;IAkBb,EAAE;IAjBJ,KAAK,CAAW;IAChB,EAAE,CAAiB;IAC3B,uFAAuF;IAC/E,MAAM,CAAS;IACf,YAAY,CAAe;IACnC,mFAAmF;IAC3E,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,sFAAsF;IAC9E,MAAM,CAA0B;IAExC,8EAA8E;IACtE,WAAW,CAAS;IAC5B,0FAA0F;IAClF,YAAY,CAA6C;IAEjE,YACU,EAAe,EACvB,MAAoB,EACpB,SAAiB,EACjB,YAA0B,EAC1B,OAAO,GAAgB,QAAQ;kBAJvB,EAAE;QAMV,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;QACxC,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC;QACpC,kFAAkF;QAClF,+EAA+E;QAC/E,4EAA4E;QAC5E,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACxC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,0FAA0F;IAC1F,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,8FAA8F;IAC9F,MAAM,CAAC,MAAkB;QACvB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAiB,CAAC;QAClD,MAAc,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,aAAa,CAAC,OAAsB;QAC1C,IAAI,OAAO,EAAE,MAAM;YAAE,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAEO,MAAM,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;IAC3C,CAAC;IAED,qFAAqF;IAC7E,KAAK,CAAC,KAAa,EAAE,KAAc;QACzC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;IACvD,CAAC;IAED,sEAAsE;IAC9D,KAAK,CAAC,IAA6B;QACzC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChG,OAAO,GAAG,CAAC;IACb,CAAC;IAED,qFAAqF;IAC7E,OAAO,CAAC,GAA4B;QAC1C,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;YAC3C,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACK,OAAO,CAAiD,KAAQ,EAAE,EAAoC;QAC5G,IAAI,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,EAA6B,CAAC;YAC1C,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAChH,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,kFAAkF;IAClF,oEAAoE;IACpE,EAAE;IACF,mFAAmF;IACnF,gFAAgF;IAChF,mFAAmF;IACnF,kFAAkF;IAClF,gDAAgD;IACxC,QAAQ,CAAiD,KAAQ,EAAE,QAAiC;QAC1G,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACvC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YACrF,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,EAC1D,KAAK,CACN,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA0E;QACnF,IAAI,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;QAE5D,kFAAkF;QAClF,kFAAkF;QAClF,4EAA4E;QAC5E,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,2EAA2E;YAC3E,2EAA2E;YAC3E,4DAA4D;YAC5D,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC5C,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAY,EAAE,OAAO,CAAC,KAAK,CAAQ,CAAC;QAC5D,CAAC;QAED,kEAAkE;QAClE,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;QAC7B,wCAAwC;QACxC,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,OAAO,EAAE,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;YAC/D,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK;YAC5B,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC;QACpB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACvC,0EAA0E;YAC1E,8DAA8D;YAC9D,IAAI,KAAK,KAAK,SAAS;gBAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACxD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAE1E,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,MAAM,MAAM,GAAG,IAA2C,CAAC;QAC3D,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;QAEzB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,4DAA4D;QAC5D,EAAE;QACF,iFAAiF;QACjF,oFAAoF;QACpF,mFAAmF;QACnF,qFAAqF;QACrF,4EAA4E;QAC5E,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,IAAI,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YACrG,IAAI,OAAO,CAAC,KAAK;gBAAE,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAe,EAAE,OAAO,CAAC,KAAK,CAAQ,CAAC;YACnF,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,CAAC;YAC9C,MAAM,CAAC,KAAK,GAAG,MAAM,CAAE,GAAW,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,GAAsB,EAAE,OAAsB;QAC7D,IAAI,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,8CAA8C,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACrK,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,GAAG,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;QACzD,4EAA4E;QAC5E,iFAAiF;QACjF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE;iBACvB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;iBAC3B,SAAS,EAAE;iBACX,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;iBACvE,OAAO,EAAE,CAAC;YACb,KAAK,MAAM,GAAG,IAAI,IAAa,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC/B,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CACjB,KAAa,EACb,IAAuB,EACvB,OAAsB;QAEtB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqC,CAAC;QAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;gBAAM,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,MAAM,CAAC,KAA8B,EAAE,OAAsB;QACjE,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,mEAAmE;gBACrF,kEAAkE,CACnE,CAAC;QACJ,CAAC;QACD,qEAAqE;QACrE,6EAA6E;QAC7E,iFAAiF;QACjF,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QACvE,mFAAmF;QACnF,mFAAmF;QACnF,gCAAgC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAEnG,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,YAAY,KAAK,aAAa;YACxC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC5G,CAAC,CAAE,MAAc,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CACjD,CAAC,OAAO,EAAE,CAAC;QAEZ,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW;YAC5B,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAY,CAAC;QACxC,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAW,EAAE,OAAO,CAAC,CAAE,CAAC;IACtD,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,SAAS,CAAC,MAA0C,EAAE,QAAuB;QACjF,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,kEAAkE;gBACpF,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAElC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1G,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CACjC,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC,CAC9G,CAAC;QACF,qEAAqE;QACrE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE3F,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC,YAAY,KAAK,aAAa;gBACxC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC5G,CAAC,CAAE,MAAc,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CACjD,CAAC,OAAO,EAAE,CAAC;YACZ,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;QAC1B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACK,aAAa;QACnB,OAAO,IAAI,GAAG,CAAC;YACb,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACjD,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;iBAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK,CAAC;iBAC7F,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SACtC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAoC,EAAE,OAAsB;QACzE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAS,EAAE,EAAE,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC9G,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAiC,EAAE,OAAsB;QACpE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAS,EAAE,QAAQ,CAAC;aAC9F,KAAK,CAAC,CAAC,CAAC;aACR,gBAAgB,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAiC,EAAE,OAAsB;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,GAAG,GAA8B,EAAE,CAAC;QAC1C,gFAAgF;QAChF,kFAAkF;QAClF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAS,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1G,KAAK,MAAM,GAAG,IAAI,IAAa,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC/B,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACK,eAAe,CAAC,QAAiC,EAAE,EAAU;QACnE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW;gBAAE,SAAS;YAC/E,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,SAAS,GAAG,YAAY,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,0BAA0B;gBAC7F,SAAS,IAAI,CAAC,WAAW,4DAA4D;gBACrF,wBAAwB,GAAG,+DAA+D,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,QAAiC;QACrD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;aACvC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;QACzF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,uBAAuB;gBAClG,GAAG,IAAI,CAAC,WAAW,2EAA2E,CAC/F,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAE,CAAC;QACpC,OAAO,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,MAAmB,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC;aAC/D,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAA8B,EAAE,OAAsB;QACjE,gFAAgF;QAChF,8EAA8E;QAC9E,+EAA+E;QAC/E,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAE7C,MAAM,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAE7E,2EAA2E;QAC3E,iFAAiF;QACjF,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW;YAC5B,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAwB,CAAC;QACnD,MAAM,OAAO,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,MAAM,MAAM,GAAG,OAAO,IAAI,IAAI,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAoC,EAAE,KAA8B,EAAE,OAAsB;QACvG,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAE7C,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAEpG,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAoC;QAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAe,EAAE,OAA2B,EAAE,OAAO,GAAgB,QAAQ;IAC5G,MAAM,OAAO,GAAG,OAAO,EAAE,SAAS,IAAI,WAAW,CAAC;IAClD,OAAO,CAAC,MAAoB,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACjH,CAAC"}
|
package/dist/ddl.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DDL — the table description, rendered as SQL.
|
|
3
|
+
*
|
|
4
|
+
* Kysely's schema builder is what makes this dialect-agnostic: it owns the
|
|
5
|
+
* identifier quoting and the per-engine syntax, so this module only decides
|
|
6
|
+
* *what* to emit. Compilation needs no connection — a `DummyDriver` paired with
|
|
7
|
+
* a real query compiler renders the statement for any engine, which is why the
|
|
8
|
+
* whole surface is testable without a database.
|
|
9
|
+
*/
|
|
10
|
+
import { Kysely } from 'kysely';
|
|
11
|
+
import { type AppLike, type ColumnDef, type TableDef } from './table.js';
|
|
12
|
+
import { type DialectName } from './dialect.js';
|
|
13
|
+
/** A Kysely bound to a dialect's compiler but to no connection — renders SQL only. */
|
|
14
|
+
export declare function compiler(name: DialectName): Kysely<any>;
|
|
15
|
+
/**
|
|
16
|
+
* Render `CREATE TABLE` for one described table.
|
|
17
|
+
*
|
|
18
|
+
* `IF NOT EXISTS` is emitted everywhere it exists — SQL Server has no such
|
|
19
|
+
* clause, so there the statement is bare and the caller must not replay it
|
|
20
|
+
* blindly (the diff pass, once it lands, answers that properly).
|
|
21
|
+
*
|
|
22
|
+
* `skipReferences` names the columns whose FK is rendered WITHOUT the inline
|
|
23
|
+
* `references()` — the column itself still gets created; `orderTables` sends a
|
|
24
|
+
* column here when its target is part of a cycle, so the constraint reaches the
|
|
25
|
+
* table separately, once every table involved exists (`addForeignKeyConstraintSQL`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function createTableSQL(table: TableDef, dialectName: DialectName, options?: {
|
|
28
|
+
skipReferences?: Set<string>;
|
|
29
|
+
}): string;
|
|
30
|
+
/**
|
|
31
|
+
* `CREATE INDEX` for every column that asked for one.
|
|
32
|
+
*
|
|
33
|
+
* Separate statements, never part of `CREATE TABLE`: an index is not a constraint, it
|
|
34
|
+
* changes no answer — only what a read costs. `IF NOT EXISTS` everywhere it exists, so
|
|
35
|
+
* replaying the batch is safe (SQL Server has no such clause, same rule as the tables).
|
|
36
|
+
*/
|
|
37
|
+
export declare function indexSQL(table: TableDef, column: ColumnDef, dialectName: DialectName): string;
|
|
38
|
+
/** Every index one table asks for — one statement each. */
|
|
39
|
+
export declare function createIndexSQL(table: TableDef, dialectName: DialectName): string[];
|
|
40
|
+
/**
|
|
41
|
+
* `ALTER TABLE ADD CONSTRAINT` for one FK `orderTables` deferred — closes a
|
|
42
|
+
* relation cycle once every table in it exists. Not available on SQLite (its
|
|
43
|
+
* `ALTER TABLE` is limited to RENAME/ADD COLUMN/RENAME COLUMN/DROP COLUMN) — a
|
|
44
|
+
* caller on that dialect never produces a deferred edge to render here.
|
|
45
|
+
*/
|
|
46
|
+
export declare function addForeignKeyConstraintSQL(table: TableDef, column: ColumnDef, dialectName: DialectName): string;
|
|
47
|
+
export interface GenerateOptions {
|
|
48
|
+
/** Override table name resolution. Default: camelCase → snake_case + 's'. */
|
|
49
|
+
tableName?: (entityName: string) => string;
|
|
50
|
+
/** Target engine. Default: sqlite. */
|
|
51
|
+
dialect?: DialectName;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `CREATE TABLE` for every entity the app hosts — scanned frond entities plus
|
|
55
|
+
* auth runtime entities when present.
|
|
56
|
+
*
|
|
57
|
+
* SQLite resolves FK targets lazily and accepts any order, and it has no
|
|
58
|
+
* `ALTER TABLE ADD CONSTRAINT` to close a cycle with — every FK stays inline,
|
|
59
|
+
* unordered. Every other engine needs a referenced table to exist first:
|
|
60
|
+
* `orderTables` sorts the batch and reports the edges a cycle forces to defer,
|
|
61
|
+
* rendered as `ALTER TABLE ADD CONSTRAINT` after every `CREATE TABLE`.
|
|
62
|
+
*
|
|
63
|
+
* Caveat for a repeat call (`autoMigrate`): `CREATE TABLE IF NOT EXISTS` is
|
|
64
|
+
* idempotent, `ADD CONSTRAINT` is not — on pg/mysql/mssql, calling this twice
|
|
65
|
+
* for an app with a relation cycle re-issues the same constraint and errors.
|
|
66
|
+
* The introspection-based `migrate()` (`diff.ts`) doesn't have this problem: it
|
|
67
|
+
* only ever emits a table's constraints once, the run that creates it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function generateSQL(app: AppLike, options?: GenerateOptions): string[];
|
|
70
|
+
/**
|
|
71
|
+
* Anything that can run a statement. `exec` is accepted alongside `execute` so a
|
|
72
|
+
* raw better-sqlite3 handle drops in unchanged.
|
|
73
|
+
*/
|
|
74
|
+
export type SqlSink = {
|
|
75
|
+
execute(sql: string): unknown;
|
|
76
|
+
} | {
|
|
77
|
+
exec(sql: string): unknown;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Create every missing table. Additive only — an existing table is left alone.
|
|
81
|
+
*
|
|
82
|
+
* Stays SYNCHRONOUS when the sink is (a raw better-sqlite3 handle), so a caller
|
|
83
|
+
* that doesn't await still gets its tables before the next statement. Returns a
|
|
84
|
+
* promise only when the sink actually returns one.
|
|
85
|
+
*/
|
|
86
|
+
export declare function autoMigrate(app: AppLike, sink: SqlSink, options?: GenerateOptions): void | Promise<void>;
|
|
87
|
+
//# sourceMappingURL=ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../src/ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,MAAM,EAOP,MAAM,QAAQ,CAAC;AAChB,OAAO,EAKL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAchE,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAcvD;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,WAAW,EAAE,WAAW,EACxB,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACzC,MAAM,CA6CR;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAO7F;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,EAAE,CAIlF;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAQ/G;AAID,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CA4B7E;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf;IAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GACjC;IAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAMnC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxG"}
|