@adminiumjs/engine 0.2.1 → 0.2.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/dist/adapter-registry.d.ts.map +1 -1
- package/dist/adapter-registry.js.map +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -0
- package/dist/adapter.js.map +1 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/classify/columns.d.ts.map +1 -1
- package/dist/classify/columns.js.map +1 -1
- package/dist/classify/index.d.ts +1 -1
- package/dist/classify/index.d.ts.map +1 -1
- package/dist/classify/index.js +1 -1
- package/dist/classify/index.js.map +1 -1
- package/dist/classify/names.d.ts.map +1 -1
- package/dist/classify/names.js +1 -0
- package/dist/classify/names.js.map +1 -1
- package/dist/classify/pii.d.ts.map +1 -1
- package/dist/classify/pii.js.map +1 -1
- package/dist/classify/tables.d.ts +24 -0
- package/dist/classify/tables.d.ts.map +1 -1
- package/dist/classify/tables.js +49 -2
- package/dist/classify/tables.js.map +1 -1
- package/dist/config-schema/envelope.d.ts +20 -0
- package/dist/config-schema/envelope.d.ts.map +1 -1
- package/dist/config-schema/envelope.js +24 -0
- package/dist/config-schema/envelope.js.map +1 -1
- package/dist/config-schema/index.d.ts +4 -2
- package/dist/config-schema/index.d.ts.map +1 -1
- package/dist/config-schema/index.js +10 -2
- package/dist/config-schema/index.js.map +1 -1
- package/dist/config-schema/migrations.d.ts.map +1 -1
- package/dist/config-schema/migrations.js +1 -0
- package/dist/config-schema/migrations.js.map +1 -1
- package/dist/config-schema/table-bound.d.ts +20 -0
- package/dist/config-schema/table-bound.d.ts.map +1 -0
- package/dist/config-schema/table-bound.js +34 -0
- package/dist/config-schema/table-bound.js.map +1 -0
- package/dist/generate/archetype.d.ts +17 -0
- package/dist/generate/archetype.d.ts.map +1 -1
- package/dist/generate/archetype.js +9 -2
- package/dist/generate/archetype.js.map +1 -1
- package/dist/generate/crud.d.ts.map +1 -1
- package/dist/generate/crud.js +1 -0
- package/dist/generate/crud.js.map +1 -1
- package/dist/generate/dashboard.d.ts.map +1 -1
- package/dist/generate/dashboard.js +1 -0
- package/dist/generate/dashboard.js.map +1 -1
- package/dist/generate/domains.d.ts.map +1 -1
- package/dist/generate/domains.js +1 -0
- package/dist/generate/domains.js.map +1 -1
- package/dist/generate/index.d.ts +25 -0
- package/dist/generate/index.d.ts.map +1 -1
- package/dist/generate/index.js +27 -3
- package/dist/generate/index.js.map +1 -1
- package/dist/generate/recompose.d.ts +51 -0
- package/dist/generate/recompose.d.ts.map +1 -0
- package/dist/generate/recompose.js +100 -0
- package/dist/generate/recompose.js.map +1 -0
- package/dist/generate/util.d.ts.map +1 -1
- package/dist/generate/util.js +1 -0
- package/dist/generate/util.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/infer/index.d.ts +10 -0
- package/dist/infer/index.d.ts.map +1 -0
- package/dist/infer/index.js +11 -0
- package/dist/infer/index.js.map +1 -0
- package/dist/infer/relations.d.ts +41 -0
- package/dist/infer/relations.d.ts.map +1 -0
- package/dist/infer/relations.js +473 -0
- package/dist/infer/relations.js.map +1 -0
- package/dist/ir-json-schema.d.ts +18 -0
- package/dist/ir-json-schema.d.ts.map +1 -0
- package/dist/ir-json-schema.js +65 -0
- package/dist/ir-json-schema.js.map +1 -0
- package/dist/schema-model.d.ts.map +1 -1
- package/dist/schema-model.js +1 -0
- package/dist/schema-model.js.map +1 -1
- package/dist/snapshot/diff.d.ts.map +1 -1
- package/dist/snapshot/diff.js +1 -0
- package/dist/snapshot/diff.js.map +1 -1
- package/dist/snapshot/hash.d.ts.map +1 -1
- package/dist/snapshot/hash.js.map +1 -1
- package/dist/snapshot/index.d.ts.map +1 -1
- package/dist/snapshot/index.js.map +1 -1
- package/dist/snapshot/sha256.d.ts.map +1 -1
- package/dist/snapshot/sha256.js +1 -0
- package/dist/snapshot/sha256.js.map +1 -1
- package/ir-v1.schema.json +1148 -0
- package/package.json +6 -4
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Relation inference — 05-introspection-engine.md §6 rules 1 (name
|
|
4
|
+
* convention) and 2 (join table).
|
|
5
|
+
*
|
|
6
|
+
* `RELATION_KINDS` has always declared `inferred-name` and
|
|
7
|
+
* `inferred-join-table`, and five consumers branch on them, but nothing ever
|
|
8
|
+
* WROTE one: `model.relations` came exclusively from declared foreign keys.
|
|
9
|
+
* On a schema that declares none — MyISAM, legacy SQLite, most
|
|
10
|
+
* ORM-generated MySQL — that emptiness cascades: `detectDomains` shatters
|
|
11
|
+
* into singletons so every table lands in "General", dashboards are skipped
|
|
12
|
+
* for want of a time axis, and every `*_id` column falls through §7.1 to
|
|
13
|
+
* `external-id` (a mono string) instead of an FK entity chip.
|
|
14
|
+
*
|
|
15
|
+
* Pure and deterministic like the rest of the engine: same model in, same
|
|
16
|
+
* relations out, no mutation of the input.
|
|
17
|
+
*
|
|
18
|
+
* **Ordering is load-bearing.** Rule 2 reads `semantics.primary === 'fk'`,
|
|
19
|
+
* which `classifyColumn` derives from `model.relations` — so rule 1 must run
|
|
20
|
+
* first and seed the graph before rule 2 can see a join table at all.
|
|
21
|
+
* {@link applyInference} enforces that order and is deliberately NOT folded
|
|
22
|
+
* into `classifyModel`: `applyClassification` spreads `...model` and rebuilds
|
|
23
|
+
* only `tables`, so relations added inside it would vanish on the next call.
|
|
24
|
+
*
|
|
25
|
+
* **Confidence banding.** 05 §6 accepts an inferred relation at ≥ 0.8;
|
|
26
|
+
* everything a rule here is less than sure of is emitted in the 0.5–0.79
|
|
27
|
+
* band instead of being dropped. All four 0.8 gates exclude that band, so
|
|
28
|
+
* such a relation is visible to the Studio remap editor as "suggested,
|
|
29
|
+
* unchecked" without acting on anything — which is how a suggestion ships
|
|
30
|
+
* without a schema change. Nothing here ever emits 1.0: that value belongs
|
|
31
|
+
* to declared FKs and accepted overrides.
|
|
32
|
+
*/
|
|
33
|
+
import { classifyColumn, buildColumnClassifyContext } from '../classify/columns.js';
|
|
34
|
+
import { normalizeName } from '../classify/names.js';
|
|
35
|
+
import { detectJoinTable, joinDetectionColumns } from '../classify/tables.js';
|
|
36
|
+
/**
|
|
37
|
+
* Accepted-relation threshold (§6). The SAME number lives in
|
|
38
|
+
* classify/columns.ts (`ACCEPTED_RELATION_CONFIDENCE`), classify/tables.ts
|
|
39
|
+
* (`detectHierarchy`) and generate/domains.ts (`EDGE_CONFIDENCE`); all four
|
|
40
|
+
* move together or the model means different things to different readers.
|
|
41
|
+
*/
|
|
42
|
+
const ACCEPTED_RELATION_CONFIDENCE = 0.8;
|
|
43
|
+
/**
|
|
44
|
+
* Evidence is scored in integer hundredths (as `detectJoinTable` does) so
|
|
45
|
+
* 0.7 + 0.1 lands on 0.8 exactly rather than 0.7999999999999999. Below the
|
|
46
|
+
* floor a name match is noise, not a suggestion, and nothing is emitted; the
|
|
47
|
+
* ceiling keeps inference short of the 1.0 that means declared or accepted.
|
|
48
|
+
*/
|
|
49
|
+
const MIN_SUGGESTION_HUNDREDTHS = 50;
|
|
50
|
+
const MAX_INFERRED_HUNDREDTHS = 95;
|
|
51
|
+
/** Types an id column can plausibly have. */
|
|
52
|
+
const ID_TYPES = new Set(['integer', 'bigint', 'uuid', 'varchar', 'text']);
|
|
53
|
+
const INTEGER_FAMILY = new Set(['integer', 'bigint']);
|
|
54
|
+
const TEXTISH = new Set(['text', 'varchar']);
|
|
55
|
+
/**
|
|
56
|
+
* §6 rule 3 self-FK vocabulary — the same list classify/tables.ts matches in
|
|
57
|
+
* `detectHierarchy`. `parent_id` names no table, so the name rule can only
|
|
58
|
+
* resolve it by falling back to the column's own table; without this the
|
|
59
|
+
* tree/org-chart trigger never fires on an FK-less schema.
|
|
60
|
+
*/
|
|
61
|
+
const HIERARCHY_COLUMN_RE = /^(parent|manager|supervisor|reports_to|superior)(_id)?$/;
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Name shapes
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/** Crude inverse of generate/util.ts's `pluralizeWord` — 'categories' → 'category'. */
|
|
66
|
+
function singularizeWord(word) {
|
|
67
|
+
if (/ies$/.test(word))
|
|
68
|
+
return `${word.slice(0, -3)}y`;
|
|
69
|
+
if (/(x|z|ch|sh|s)es$/.test(word))
|
|
70
|
+
return word.slice(0, -2);
|
|
71
|
+
if (/[^s]s$/.test(word))
|
|
72
|
+
return word.slice(0, -1);
|
|
73
|
+
return word;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The table name a column claims to reference: `customer_id` → 'customer',
|
|
77
|
+
* `billing_customer_uuid` → 'billing_customer', `customerid` → 'customer'.
|
|
78
|
+
* Null for bare keys (`id`/`uuid`/`guid`) and anything not id-suffixed.
|
|
79
|
+
*/
|
|
80
|
+
function referenceBase(name) {
|
|
81
|
+
if (/^(id|uuid|guid)$/.test(name))
|
|
82
|
+
return null;
|
|
83
|
+
const suffixed = /^(?<base>.+?)_(id|uuid)$/.exec(name);
|
|
84
|
+
if (suffixed?.groups !== undefined)
|
|
85
|
+
return suffixed.groups['base'];
|
|
86
|
+
const glued = /^(?<base>.{3,}?)id$/.exec(name);
|
|
87
|
+
if (glued?.groups !== undefined)
|
|
88
|
+
return glued.groups['base'];
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Role-prefixed FKs (`shipping_address_id`, `created_by_user_id`) are the
|
|
93
|
+
* common case in real schemas, so the full base is tried first and then
|
|
94
|
+
* progressively shorter tails. The index of the winning candidate is the
|
|
95
|
+
* number of tokens dropped, which costs confidence.
|
|
96
|
+
*/
|
|
97
|
+
function baseCandidates(base) {
|
|
98
|
+
const tokens = base.split('_').filter(Boolean);
|
|
99
|
+
return tokens.map((_, i) => tokens.slice(i).join('_'));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Legacy schemas often carry one prefix across every table (`wp_posts`,
|
|
103
|
+
* `wp_users`) while their FK columns do not (`post_id`). A prefix shared by
|
|
104
|
+
* at least three tables and covering 80% of them is decoration, and its
|
|
105
|
+
* stripped form is registered as a second (non-exact) lookup key.
|
|
106
|
+
*/
|
|
107
|
+
function commonTablePrefix(tables) {
|
|
108
|
+
if (tables.length < 3)
|
|
109
|
+
return null;
|
|
110
|
+
const counts = new Map();
|
|
111
|
+
for (const table of tables) {
|
|
112
|
+
const tokens = normalizeName(table.name).split('_').filter(Boolean);
|
|
113
|
+
const head = tokens[0];
|
|
114
|
+
if (head === undefined || tokens.length < 2 || head.length < 2)
|
|
115
|
+
continue;
|
|
116
|
+
counts.set(head, (counts.get(head) ?? 0) + 1);
|
|
117
|
+
}
|
|
118
|
+
// Two prefixes cannot both reach 80%, so the first hit is the only hit.
|
|
119
|
+
for (const [prefix, count] of counts) {
|
|
120
|
+
if (count >= 3 && count / tables.length >= 0.8)
|
|
121
|
+
return prefix;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* What a `<table>_id` column would land on. A composite primary key has no
|
|
127
|
+
* single-column target, so those tables are simply not referenceable by
|
|
128
|
+
* name. A table with NO declared key (routine on MyISAM/legacy SQLite) falls
|
|
129
|
+
* back to an id-shaped column, which scores lower for it.
|
|
130
|
+
*/
|
|
131
|
+
function keyColumnOf(table) {
|
|
132
|
+
if (table.primaryKey.length > 1)
|
|
133
|
+
return null;
|
|
134
|
+
if (table.primaryKey.length === 1) {
|
|
135
|
+
const column = table.columns.find((c) => c.name === table.primaryKey[0]);
|
|
136
|
+
return column === undefined ? null : { column, declaredPk: true };
|
|
137
|
+
}
|
|
138
|
+
const singular = singularizeWord(normalizeName(table.name));
|
|
139
|
+
const column = table.columns.find((c) => {
|
|
140
|
+
const name = normalizeName(c.name);
|
|
141
|
+
return /^(id|uuid|guid)$/.test(name) || name === `${singular}_id`;
|
|
142
|
+
});
|
|
143
|
+
return column === undefined ? null : { column, declaredPk: false };
|
|
144
|
+
}
|
|
145
|
+
function buildTargetIndex(model) {
|
|
146
|
+
const index = new Map();
|
|
147
|
+
const prefix = commonTablePrefix(model.tables);
|
|
148
|
+
const register = (key, target) => {
|
|
149
|
+
if (key.length < 3)
|
|
150
|
+
return;
|
|
151
|
+
const bucket = index.get(key) ?? [];
|
|
152
|
+
// A table registers under several spellings; keep one entry per table so
|
|
153
|
+
// "matched two tables" always means genuine ambiguity.
|
|
154
|
+
if (!bucket.some((t) => t.table.id === target.table.id))
|
|
155
|
+
bucket.push(target);
|
|
156
|
+
index.set(key, bucket);
|
|
157
|
+
};
|
|
158
|
+
for (const table of [...model.tables].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
159
|
+
if (table.system)
|
|
160
|
+
continue;
|
|
161
|
+
const key = keyColumnOf(table);
|
|
162
|
+
if (key === null)
|
|
163
|
+
continue;
|
|
164
|
+
const name = normalizeName(table.name);
|
|
165
|
+
register(name, { table, key, exact: true });
|
|
166
|
+
register(singularizeWord(name), { table, key, exact: true });
|
|
167
|
+
if (prefix !== null && name.startsWith(`${prefix}_`)) {
|
|
168
|
+
const stripped = name.slice(prefix.length + 1);
|
|
169
|
+
register(stripped, { table, key, exact: false });
|
|
170
|
+
register(singularizeWord(stripped), { table, key, exact: false });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return index;
|
|
174
|
+
}
|
|
175
|
+
function resolveByName(index, base, source) {
|
|
176
|
+
const candidates = baseCandidates(base);
|
|
177
|
+
for (const [droppedTokens, key] of candidates.entries()) {
|
|
178
|
+
const hits = index.get(key);
|
|
179
|
+
if (hits === undefined || hits.length === 0)
|
|
180
|
+
continue;
|
|
181
|
+
// Schema qualification: a same-schema table always beats a sibling's.
|
|
182
|
+
const sameSchema = hits.filter((t) => t.table.schema === source.schema);
|
|
183
|
+
const pool = sameSchema.length > 0 ? sameSchema : hits;
|
|
184
|
+
const target = [...pool].sort((a, b) => a.table.id.localeCompare(b.table.id))[0];
|
|
185
|
+
return {
|
|
186
|
+
target,
|
|
187
|
+
droppedTokens,
|
|
188
|
+
ambiguous: pool.length > 1,
|
|
189
|
+
crossSchema: sameSchema.length === 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
/** FK column sets already spoken for — a second relation would just duplicate. */
|
|
195
|
+
function claimedColumns(model, table) {
|
|
196
|
+
const claimed = new Set();
|
|
197
|
+
for (const relation of model.relations) {
|
|
198
|
+
if (relation.from.tableId !== table.id)
|
|
199
|
+
continue;
|
|
200
|
+
// An M2M relation's `from.columns` are the TARGET's key columns, not FKs.
|
|
201
|
+
if (relation.through !== null)
|
|
202
|
+
continue;
|
|
203
|
+
for (const column of relation.from.columns)
|
|
204
|
+
claimed.add(column);
|
|
205
|
+
}
|
|
206
|
+
return claimed;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* §6 rule 4 — a `<base>_type` / `<base>_id` pair is polymorphic, and no
|
|
210
|
+
* relation is ever fabricated for one. `detectPolymorphic` flags it on the
|
|
211
|
+
* table; this is the same test at the point where a relation would be born.
|
|
212
|
+
*/
|
|
213
|
+
function isPolymorphicPartner(table, base) {
|
|
214
|
+
return table.columns.some((c) => {
|
|
215
|
+
if (normalizeName(c.name) !== `${base}_type`)
|
|
216
|
+
return false;
|
|
217
|
+
return TEXTISH.has(c.logicalType) || c.logicalType === 'enum';
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/** 'one-to-one' when the FK column is itself unique on the referencing side. */
|
|
221
|
+
function cardinalityOf(table, column) {
|
|
222
|
+
const soleUnique = column.isUnique ||
|
|
223
|
+
(table.primaryKey.length === 1 && table.primaryKey[0] === column.name) ||
|
|
224
|
+
table.uniques.some((u) => u.columns.length === 1 && u.columns[0] === column.name);
|
|
225
|
+
return soleUnique ? 'one-to-one' : 'one-to-many';
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Rule 1: `customer_id` → `customers.id`.
|
|
229
|
+
*
|
|
230
|
+
* The canonical case — exact singular/plural name match, agreeing types, a
|
|
231
|
+
* declared single-column PK on the far side — reaches 0.90 and behaves like
|
|
232
|
+
* a declared FK everywhere. Every weakening (a role prefix dropped, a
|
|
233
|
+
* cross-schema hop, an ambiguous name, a non-unique target column, types
|
|
234
|
+
* that merely rhyme) costs enough to fall into the 0.5–0.79 suggestion band.
|
|
235
|
+
*/
|
|
236
|
+
export function inferNameRelations(model) {
|
|
237
|
+
const index = buildTargetIndex(model);
|
|
238
|
+
const existingIds = new Set(model.relations.map((r) => r.id));
|
|
239
|
+
const inferred = [];
|
|
240
|
+
for (const table of [...model.tables].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
241
|
+
if (table.system)
|
|
242
|
+
continue;
|
|
243
|
+
const claimed = claimedColumns(model, table);
|
|
244
|
+
const solePk = table.primaryKey.length === 1 ? table.primaryKey[0] : null;
|
|
245
|
+
for (const column of table.columns) {
|
|
246
|
+
// A declared FK is the truth this rule is guessing at — never restate it.
|
|
247
|
+
if (column.references !== null)
|
|
248
|
+
continue;
|
|
249
|
+
if (claimed.has(column.name))
|
|
250
|
+
continue;
|
|
251
|
+
if (column.name === solePk)
|
|
252
|
+
continue;
|
|
253
|
+
if (!ID_TYPES.has(column.logicalType))
|
|
254
|
+
continue;
|
|
255
|
+
const name = normalizeName(column.name);
|
|
256
|
+
const base = referenceBase(name);
|
|
257
|
+
if (base !== null && isPolymorphicPartner(table, base))
|
|
258
|
+
continue;
|
|
259
|
+
let match = base === null ? null : resolveByName(index, base, table);
|
|
260
|
+
let hierarchy = false;
|
|
261
|
+
if (match === null && HIERARCHY_COLUMN_RE.test(name)) {
|
|
262
|
+
// `parent_id` / `reports_to` name no table; they mean "this one".
|
|
263
|
+
const key = keyColumnOf(table);
|
|
264
|
+
if (key === null)
|
|
265
|
+
continue;
|
|
266
|
+
match = {
|
|
267
|
+
target: { table, key, exact: true },
|
|
268
|
+
droppedTokens: 0,
|
|
269
|
+
ambiguous: false,
|
|
270
|
+
crossSchema: false,
|
|
271
|
+
};
|
|
272
|
+
hierarchy = true;
|
|
273
|
+
}
|
|
274
|
+
if (match === null)
|
|
275
|
+
continue;
|
|
276
|
+
const { target } = match;
|
|
277
|
+
const selfReferential = target.table.id === table.id;
|
|
278
|
+
const reasons = [];
|
|
279
|
+
let hundredths = 60;
|
|
280
|
+
reasons.push(hierarchy
|
|
281
|
+
? `"${name}" is §6 rule 3 hierarchy vocabulary — resolved to its own table`
|
|
282
|
+
: `name matches table "${target.table.name}" (§6 rule 1)`);
|
|
283
|
+
if (target.exact)
|
|
284
|
+
hundredths += 5;
|
|
285
|
+
else
|
|
286
|
+
reasons.push('matched only after stripping the shared table-name prefix (+0)');
|
|
287
|
+
if (match.droppedTokens > 0) {
|
|
288
|
+
hundredths -= 10;
|
|
289
|
+
reasons.push(`role-prefixed name — ${match.droppedTokens} leading token(s) ignored (-0.1)`);
|
|
290
|
+
}
|
|
291
|
+
if (match.crossSchema) {
|
|
292
|
+
hundredths -= 5;
|
|
293
|
+
reasons.push(`target lives in schema "${target.table.schema}", not "${table.schema}" (-0.05)`);
|
|
294
|
+
}
|
|
295
|
+
if (match.ambiguous) {
|
|
296
|
+
hundredths -= 15;
|
|
297
|
+
reasons.push('more than one table answers to this name (-0.15)');
|
|
298
|
+
}
|
|
299
|
+
if (target.key.declaredPk) {
|
|
300
|
+
hundredths += 15;
|
|
301
|
+
reasons.push(`target "${target.key.column.name}" is the declared primary key (+0.15)`);
|
|
302
|
+
}
|
|
303
|
+
else if (target.key.column.isUnique) {
|
|
304
|
+
hundredths += 5;
|
|
305
|
+
reasons.push(`target "${target.key.column.name}" is unique but not a declared key (+0.05)`);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
reasons.push(`target "${target.key.column.name}" is neither a key nor unique (+0)`);
|
|
309
|
+
}
|
|
310
|
+
const targetType = target.key.column.logicalType;
|
|
311
|
+
if (targetType === column.logicalType) {
|
|
312
|
+
hundredths += 10;
|
|
313
|
+
reasons.push(`both sides are ${column.logicalType} (+0.1)`);
|
|
314
|
+
}
|
|
315
|
+
else if (INTEGER_FAMILY.has(targetType) && INTEGER_FAMILY.has(column.logicalType)) {
|
|
316
|
+
hundredths += 5;
|
|
317
|
+
reasons.push(`${column.logicalType} vs ${targetType} — same integer family (+0.05)`);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
hundredths -= 15;
|
|
321
|
+
reasons.push(`${column.logicalType} does not agree with ${targetType} (-0.15)`);
|
|
322
|
+
}
|
|
323
|
+
if (selfReferential && !hierarchy) {
|
|
324
|
+
// `products.product_id` on a table whose PK is `id` is far more often
|
|
325
|
+
// a legacy/external key than a self-join — suggested, never accepted.
|
|
326
|
+
hundredths -= 20;
|
|
327
|
+
reasons.push('self-reference on non-hierarchy vocabulary (-0.2)');
|
|
328
|
+
}
|
|
329
|
+
if (hundredths < MIN_SUGGESTION_HUNDREDTHS)
|
|
330
|
+
continue;
|
|
331
|
+
const confidence = Math.min(MAX_INFERRED_HUNDREDTHS, hundredths) / 100;
|
|
332
|
+
const id = `inferred-name:${table.id}(${column.name})->${target.table.id}(${target.key.column.name})`;
|
|
333
|
+
if (existingIds.has(id))
|
|
334
|
+
continue;
|
|
335
|
+
existingIds.add(id);
|
|
336
|
+
inferred.push({
|
|
337
|
+
relation: {
|
|
338
|
+
id,
|
|
339
|
+
kind: 'inferred-name',
|
|
340
|
+
cardinality: cardinalityOf(table, column),
|
|
341
|
+
from: { tableId: table.id, columns: [column.name] },
|
|
342
|
+
to: { tableId: target.table.id, columns: [target.key.column.name] },
|
|
343
|
+
through: null,
|
|
344
|
+
onDelete: null,
|
|
345
|
+
onUpdate: null,
|
|
346
|
+
selfReferential,
|
|
347
|
+
confidence,
|
|
348
|
+
},
|
|
349
|
+
reasons,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return inferred;
|
|
354
|
+
}
|
|
355
|
+
function resolveJoinSide(model, table, columnName) {
|
|
356
|
+
const declared = table.columns.find((c) => c.name === columnName)?.references ?? null;
|
|
357
|
+
if (declared !== null) {
|
|
358
|
+
return { column: columnName, tableId: declared.tableId, toColumn: declared.column, confidence: 1 };
|
|
359
|
+
}
|
|
360
|
+
for (const relation of model.relations) {
|
|
361
|
+
if (relation.through !== null)
|
|
362
|
+
continue;
|
|
363
|
+
if (relation.from.tableId !== table.id)
|
|
364
|
+
continue;
|
|
365
|
+
if (relation.from.columns.length !== 1 || relation.from.columns[0] !== columnName)
|
|
366
|
+
continue;
|
|
367
|
+
if (relation.confidence < ACCEPTED_RELATION_CONFIDENCE)
|
|
368
|
+
continue;
|
|
369
|
+
return {
|
|
370
|
+
column: columnName,
|
|
371
|
+
tableId: relation.to.tableId,
|
|
372
|
+
toColumn: relation.to.columns[0],
|
|
373
|
+
confidence: relation.confidence,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The join detector needs classified columns, and `classifyColumn` is the
|
|
380
|
+
* only thing that produces them. `joinDetectionColumns` (classify/tables.ts)
|
|
381
|
+
* then applies the accepted-relation → `fk` restatement, which is shared
|
|
382
|
+
* verbatim with `classifyTable` — the two MUST agree, or rule 2 emits an M2M
|
|
383
|
+
* for a table the table classifier still calls an `entity`.
|
|
384
|
+
*/
|
|
385
|
+
function detectionColumnsFor(model, table) {
|
|
386
|
+
const ctx = buildColumnClassifyContext(model, table);
|
|
387
|
+
return joinDetectionColumns(model, table, table.columns.map((column) => classifyColumn(column, ctx)));
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Rule 2: a table that is nothing but two FKs is an M2M edge, not an entity.
|
|
391
|
+
*
|
|
392
|
+
* Idempotence matters here beyond re-runs: `@adminium/schema-import`'s Django
|
|
393
|
+
* parser already emits `inferred-join-table` relations for every
|
|
394
|
+
* `ManyToManyField`, so an imported model arrives with M2M relations a live
|
|
395
|
+
* introspection of the same database does not have. A through table already
|
|
396
|
+
* covered by ANY relation is left alone, which keeps the two paths from
|
|
397
|
+
* disagreeing and keeps `databaseModelSchema.superRefine` (which throws on a
|
|
398
|
+
* duplicate relation id) satisfied.
|
|
399
|
+
*/
|
|
400
|
+
export function inferJoinTableRelations(model) {
|
|
401
|
+
const covered = new Set(model.relations
|
|
402
|
+
.map((r) => r.through?.tableId)
|
|
403
|
+
.filter((id) => id !== undefined && id !== null));
|
|
404
|
+
const existingIds = new Set(model.relations.map((r) => r.id));
|
|
405
|
+
const inferred = [];
|
|
406
|
+
for (const table of [...model.tables].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
407
|
+
if (table.system)
|
|
408
|
+
continue;
|
|
409
|
+
if (covered.has(table.id))
|
|
410
|
+
continue;
|
|
411
|
+
const join = detectJoinTable(table, detectionColumnsFor(model, table));
|
|
412
|
+
if (!join.isJoin)
|
|
413
|
+
continue;
|
|
414
|
+
const sides = join.fkColumns.map((name) => resolveJoinSide(model, table, name));
|
|
415
|
+
const [from, to] = sides;
|
|
416
|
+
if (from === undefined || from === null || to === undefined || to === null)
|
|
417
|
+
continue;
|
|
418
|
+
// Column order fixes the direction: deterministic, and it mirrors the
|
|
419
|
+
// order the schema itself declares the pair in.
|
|
420
|
+
const id = `inferred-m2m:${table.id}(${from.column}+${to.column})`;
|
|
421
|
+
if (existingIds.has(id))
|
|
422
|
+
continue;
|
|
423
|
+
existingIds.add(id);
|
|
424
|
+
inferred.push({
|
|
425
|
+
relation: {
|
|
426
|
+
id,
|
|
427
|
+
kind: 'inferred-join-table',
|
|
428
|
+
cardinality: 'many-to-many',
|
|
429
|
+
from: { tableId: from.tableId, columns: [from.toColumn] },
|
|
430
|
+
to: { tableId: to.tableId, columns: [to.toColumn] },
|
|
431
|
+
through: {
|
|
432
|
+
tableId: table.id,
|
|
433
|
+
fromColumns: [from.column],
|
|
434
|
+
toColumns: [to.column],
|
|
435
|
+
},
|
|
436
|
+
onDelete: null,
|
|
437
|
+
onUpdate: null,
|
|
438
|
+
selfReferential: from.tableId === to.tableId,
|
|
439
|
+
// A join is never surer than the weaker of the two FKs holding it up.
|
|
440
|
+
confidence: Math.min(join.confidence, from.confidence, to.confidence),
|
|
441
|
+
},
|
|
442
|
+
reasons: [...join.reasons, `join-table confidence ${join.confidence.toFixed(2)}`],
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return inferred;
|
|
446
|
+
}
|
|
447
|
+
// ---------------------------------------------------------------------------
|
|
448
|
+
// Entry point
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
/** Both rules, in the only order that works. Rule 2 sees rule 1's output. */
|
|
451
|
+
export function inferRelations(model) {
|
|
452
|
+
const named = inferNameRelations(model);
|
|
453
|
+
const seeded = named.length === 0
|
|
454
|
+
? model
|
|
455
|
+
: { ...model, relations: [...model.relations, ...named.map((i) => i.relation)] };
|
|
456
|
+
return [...named, ...inferJoinTableRelations(seeded)];
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Return a NEW model with §6 rules 1–2 relations appended. Call it BEFORE
|
|
460
|
+
* `applyClassification` — the column and table classifiers both read
|
|
461
|
+
* `model.relations`, and that is the entire point of inferring them.
|
|
462
|
+
*
|
|
463
|
+
* Idempotent: a second pass finds every column already claimed and every
|
|
464
|
+
* join table already covered, so it returns the model untouched.
|
|
465
|
+
*/
|
|
466
|
+
export function applyInference(model) {
|
|
467
|
+
const inferred = inferRelations(model);
|
|
468
|
+
if (inferred.length === 0)
|
|
469
|
+
return model;
|
|
470
|
+
const relations = [...model.relations, ...inferred.map((i) => i.relation)];
|
|
471
|
+
return { ...model, relations, stats: { ...model.stats, relationCount: relations.length } };
|
|
472
|
+
}
|
|
473
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/infer/relations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Where the docs site serves this document (`apps/docs/src/pages/schemas/`). */
|
|
2
|
+
export declare const IR_SCHEMA_URL = "https://docs.adminium.dev/schemas/ir-v1.json";
|
|
3
|
+
/**
|
|
4
|
+
* The schema document, serialized exactly as `ir-v1.schema.json` holds it.
|
|
5
|
+
*
|
|
6
|
+
* `io: 'input'` is deliberate: the model defaults nearly everything, and an
|
|
7
|
+
* author needs to know what they MAY omit. The output schema would mark every
|
|
8
|
+
* defaulted field required and reject the minimal IR §2.3 promises works.
|
|
9
|
+
*
|
|
10
|
+
* `$schema` is added to the root as an allowed property. The generated document
|
|
11
|
+
* is `additionalProperties: false` throughout (every IR object is a Zod
|
|
12
|
+
* `strictObject`), so without this the very pointer that makes an editor
|
|
13
|
+
* validate the file would be the first thing the editor flagged.
|
|
14
|
+
* `schema-import`'s `parsers/json.ts` strips it on the way in for the same
|
|
15
|
+
* reason.
|
|
16
|
+
*/
|
|
17
|
+
export declare function irJsonSchemaDocument(): string;
|
|
18
|
+
//# sourceMappingURL=ir-json-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ir-json-schema.d.ts","sourceRoot":"","sources":["../src/ir-json-schema.ts"],"names":[],"mappings":"AAkBA,iFAAiF;AACjF,eAAO,MAAM,aAAa,iDAAiD,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA0C7C"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* The published JSON Schema for the IR — `docs.adminium.dev/schemas/ir-v1.json`.
|
|
4
|
+
*
|
|
5
|
+
* The JSON IR guide has told readers to point `$schema` at that URL since the
|
|
6
|
+
* page was written, and no such document was ever generated: the URL 404'd. A
|
|
7
|
+
* hand-written one would have drifted from the model within a release, so this
|
|
8
|
+
* derives it from `databaseModelSchema` — the same schema that validates every
|
|
9
|
+
* import.
|
|
10
|
+
*
|
|
11
|
+
* Lives in `src` rather than in the emitting script because both the script
|
|
12
|
+
* (via `dist`) and the drift test (via `src`) must produce byte-identical
|
|
13
|
+
* output; two copies would be the drift the artifact exists to prevent.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { IR_VERSION, databaseModelSchema } from './schema-model.js';
|
|
17
|
+
/** Where the docs site serves this document (`apps/docs/src/pages/schemas/`). */
|
|
18
|
+
export const IR_SCHEMA_URL = 'https://docs.adminium.dev/schemas/ir-v1.json';
|
|
19
|
+
/**
|
|
20
|
+
* The schema document, serialized exactly as `ir-v1.schema.json` holds it.
|
|
21
|
+
*
|
|
22
|
+
* `io: 'input'` is deliberate: the model defaults nearly everything, and an
|
|
23
|
+
* author needs to know what they MAY omit. The output schema would mark every
|
|
24
|
+
* defaulted field required and reject the minimal IR §2.3 promises works.
|
|
25
|
+
*
|
|
26
|
+
* `$schema` is added to the root as an allowed property. The generated document
|
|
27
|
+
* is `additionalProperties: false` throughout (every IR object is a Zod
|
|
28
|
+
* `strictObject`), so without this the very pointer that makes an editor
|
|
29
|
+
* validate the file would be the first thing the editor flagged.
|
|
30
|
+
* `schema-import`'s `parsers/json.ts` strips it on the way in for the same
|
|
31
|
+
* reason.
|
|
32
|
+
*/
|
|
33
|
+
export function irJsonSchemaDocument() {
|
|
34
|
+
const generated = z.toJSONSchema(databaseModelSchema, { io: 'input' });
|
|
35
|
+
// `introspectedAt` defaults to `new Date().toISOString()`, so the raw output
|
|
36
|
+
// embeds the moment of generation and the artifact differs from itself on
|
|
37
|
+
// every run — a `--check` gate that fails on a clean tree teaches people to
|
|
38
|
+
// ignore it. The default is not expressible as a constant anyway: it means
|
|
39
|
+
// "when this was imported", which is what the description now says.
|
|
40
|
+
const introspectedAt = generated.properties?.introspectedAt;
|
|
41
|
+
if (introspectedAt !== undefined) {
|
|
42
|
+
const stable = { ...introspectedAt };
|
|
43
|
+
delete stable.default;
|
|
44
|
+
generated.properties.introspectedAt = {
|
|
45
|
+
...stable,
|
|
46
|
+
description: 'ISO 8601. Defaults to the moment of import when omitted.',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return `${JSON.stringify({
|
|
50
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
51
|
+
$id: IR_SCHEMA_URL,
|
|
52
|
+
title: `Adminium schema IR v${String(IR_VERSION)}`,
|
|
53
|
+
description: 'The intermediate representation every Adminium schema import converges on. ' +
|
|
54
|
+
'Generated from packages/engine/src/schema-model.ts — do not edit by hand.',
|
|
55
|
+
...generated,
|
|
56
|
+
properties: {
|
|
57
|
+
$schema: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'Optional pointer to this schema. Adminium ignores it on import.',
|
|
60
|
+
},
|
|
61
|
+
...generated.properties,
|
|
62
|
+
},
|
|
63
|
+
}, null, 2)}\n`;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ir-json-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ir-json-schema.js","sourceRoot":"","sources":["../src/ir-json-schema.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEpE,iFAAiF;AACjF,MAAM,CAAC,MAAM,aAAa,GAAG,8CAA8C,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,SAAS,GAAG,CAAC,CAAC,YAAY,CAAC,mBAAmB,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAGpE,CAAC;IAEF,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,oEAAoE;IACpE,MAAM,cAAc,GAAG,SAAS,CAAC,UAAU,EAAE,cAEhC,CAAC;IACd,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QACrC,OAAO,MAAM,CAAC,OAAO,CAAC;QACrB,SAAS,CAAC,UAAsC,CAAC,cAAc,GAAG;YACjE,GAAG,MAAM;YACT,WAAW,EAAE,0DAA0D;SACxE,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,SAAS,CACtB;QACE,OAAO,EAAE,8CAA8C;QACvD,GAAG,EAAE,aAAa;QAClB,KAAK,EAAE,uBAAuB,MAAM,CAAC,UAAU,CAAC,EAAE;QAClD,WAAW,EACT,6EAA6E;YAC7E,2EAA2E;QAC7E,GAAG,SAAS;QACZ,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,iEAAiE;aAC/E;YACD,GAAG,SAAS,CAAC,UAAU;SACxB;KACF,EACD,IAAI,EACJ,CAAC,CACF,IAAI,CAAC;AACR,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-model.d.ts","sourceRoot":"","sources":["../src/schema-model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema-model.d.ts","sourceRoot":"","sources":["../src/schema-model.ts"],"names":[],"mappings":"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"}
|
package/dist/schema-model.js
CHANGED