clivly 0.9.7 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/{adapter-C2eRbBfo.d.cts → adapter-1TN0zohk.d.cts} +1 -1
- package/{adapter-D5LRa-NA.d.mts → adapter-uxirB88i.d.mts} +1 -1
- package/{auth-adapter-Bjgo8LhN.d.mts → auth-adapter-CXTiW3Mb.d.cts} +1 -1
- package/{auth-adapter-DWzgl5Yq.d.cts → auth-adapter-DSuYwv1c.d.mts} +1 -1
- package/auth-better-auth.d.cts +1 -1
- package/auth-better-auth.d.mts +1 -1
- package/auth-clerk.d.cts +1 -1
- package/auth-clerk.d.mts +1 -1
- package/auth-workos.d.cts +1 -1
- package/auth-workos.d.mts +1 -1
- package/core-adapter.d.cts +1 -1
- package/core-adapter.d.mts +1 -1
- package/core-auth-adapter.d.cts +1 -1
- package/core-auth-adapter.d.mts +1 -1
- package/core-drizzle.d.cts +1 -1
- package/core-drizzle.d.mts +1 -1
- package/core-entity-config.cjs +1 -1
- package/core-entity-config.d.cts +1 -1
- package/core-entity-config.d.mts +1 -1
- package/core-entity-config.mjs +1 -1
- package/core-entity-heuristics.cjs +1 -0
- package/core-entity-heuristics.d.cts +3 -3
- package/core-entity-heuristics.d.mts +3 -3
- package/core-entity-heuristics.mjs +2 -2
- package/core-mapping-form.d.cts +1 -1
- package/core-mapping-form.d.mts +1 -1
- package/core-mappings-config.cjs +1 -1
- package/core-mappings-config.d.cts +1 -1
- package/core-mappings-config.d.mts +1 -1
- package/core-mappings-config.mjs +1 -1
- package/core-sync-engine.cjs +1 -1
- package/core-sync-engine.d.cts +1 -1
- package/core-sync-engine.d.mts +1 -1
- package/core-sync-engine.mjs +1 -1
- package/core-types.cjs +4 -0
- package/core-types.d.cts +2 -2
- package/core-types.d.mts +2 -2
- package/core-types.mjs +2 -1
- package/core-view-compiler.cjs +1 -1
- package/core-view-compiler.d.cts +1 -1
- package/core-view-compiler.d.mts +1 -1
- package/core-view-compiler.mjs +1 -1
- package/core.cjs +17 -15
- package/core.d.cts +10 -10
- package/core.d.mts +10 -10
- package/core.mjs +9 -8
- package/{drizzle-BmDDgAIF.d.mts → drizzle-DTOuGXEn.d.cts} +1 -1
- package/{drizzle-ixVV4pPN.d.cts → drizzle-SGapoly2.d.mts} +1 -1
- package/drizzle.cjs +2 -2
- package/drizzle.d.cts +3 -3
- package/drizzle.d.mts +3 -3
- package/drizzle.mjs +2 -2
- package/{entity-config-C8IIkrmX.mjs → entity-config-C4d3K9CP.mjs} +81 -4
- package/{entity-config-B2RA53nw.d.cts → entity-config-DNQAMbMS.d.cts} +27 -1
- package/{entity-config-B2RA53nw.d.mts → entity-config-DNQAMbMS.d.mts} +27 -1
- package/{entity-config-CPM-Tp-d.cjs → entity-config-DgAe0_YD.cjs} +86 -3
- package/index.cjs +24 -14
- package/index.mjs +24 -14
- package/internal-BAOG5eE4.d.mts +226 -0
- package/internal-BHJ0X3fC.d.cts +226 -0
- package/internal-Bgiimp5D.mjs +144 -0
- package/internal-Cz59xMj8.cjs +167 -0
- package/internal-adapter.d.cts +1 -1
- package/internal-adapter.d.mts +1 -1
- package/internal-auth-adapter.d.cts +1 -1
- package/internal-auth-adapter.d.mts +1 -1
- package/internal-drizzle.d.cts +1 -1
- package/internal-drizzle.d.mts +1 -1
- package/internal-entity-config.cjs +1 -1
- package/internal-entity-config.d.cts +1 -1
- package/internal-entity-config.d.mts +1 -1
- package/internal-entity-config.mjs +1 -1
- package/internal-entity-heuristics.cjs +34 -1
- package/internal-entity-heuristics.d.cts +8 -2
- package/internal-entity-heuristics.d.mts +8 -2
- package/internal-entity-heuristics.mjs +34 -2
- package/internal-mapping-form.d.cts +1 -1
- package/internal-mapping-form.d.mts +1 -1
- package/internal-mappings-config.cjs +1 -1
- package/internal-mappings-config.d.cts +1 -1
- package/internal-mappings-config.d.mts +1 -1
- package/internal-mappings-config.mjs +1 -1
- package/internal-sync-engine.cjs +1 -1
- package/internal-sync-engine.d.cts +1 -1
- package/internal-sync-engine.d.mts +1 -1
- package/internal-sync-engine.mjs +1 -1
- package/internal-types.cjs +4 -0
- package/internal-types.d.cts +2 -2
- package/internal-types.d.mts +2 -2
- package/internal-types.mjs +2 -1
- package/internal-view-compiler.cjs +1 -1
- package/internal-view-compiler.d.cts +1 -1
- package/internal-view-compiler.d.mts +1 -1
- package/internal-view-compiler.mjs +1 -1
- package/internal.cjs +17 -15
- package/internal.d.cts +10 -10
- package/internal.d.mts +10 -10
- package/internal.mjs +9 -8
- package/{mapping-form-DH77BXLv.d.mts → mapping-form-Cc7Ewv8c.d.mts} +1 -1
- package/{mapping-form-J0aU6O7l.d.cts → mapping-form-DwdbYAfR.d.cts} +1 -1
- package/{mappings-config-D9dsonoi.d.mts → mappings-config-Be9D43Np.d.mts} +1 -1
- package/{mappings-config-RQLGx8ub.d.cts → mappings-config-DUt4-9iO.d.cts} +1 -1
- package/{mappings-config-CBwL3FVh.cjs → mappings-config-Dx9ldgdf.cjs} +2 -16
- package/{mappings-config-DUKBe6OS.mjs → mappings-config-MoXBeI8p.mjs} +1 -15
- package/{namespace-probe-BQ4imnV7.d.mts → namespace-probe-9ScMDR0t.d.mts} +1 -1
- package/{namespace-probe-DZIGdvlm.d.cts → namespace-probe-DC4tbmLj.d.cts} +1 -1
- package/package.json +1 -1
- package/{projection-hnPSgH6G.mjs → projection-CSGJbZlZ.mjs} +1 -1
- package/{projection-DEwL-D8R.cjs → projection-Dz6_i1-w.cjs} +0 -6
- package/{projection-sql-iHy-Cx6V.cjs → projection-sql-CbXCKt3s.cjs} +1 -1
- package/{projection-sql-9zzTVGvf.mjs → projection-sql-iHoiYr1R.mjs} +1 -1
- package/{sdk-BL9BCzyD.cjs → sdk-B1vX4kUJ.cjs} +26 -6
- package/{sdk-CubnsyD9.mjs → sdk-D6gX0TXS.mjs} +26 -6
- package/sdk.cjs +2 -2
- package/sdk.d.cts +2 -2
- package/sdk.d.mts +2 -2
- package/sdk.mjs +2 -2
- package/{sync-engine-BLgwLpR6.cjs → sync-engine-B0FT1ax6.cjs} +1 -1
- package/{sync-engine-B_IzoUSx.mjs → sync-engine-CTSIcLlZ.mjs} +1 -1
- package/{sync-engine-BG2O1an2.d.cts → sync-engine-Df0kHAoJ.d.cts} +2 -2
- package/{sync-engine-DgQbrJON.d.mts → sync-engine-ugFacJ6G.d.mts} +2 -2
- package/{types-B1oRXdNP.d.mts → types-Cq1uHUKp.d.cts} +28 -2
- package/{types-B1oRXdNP.d.cts → types-Cq1uHUKp.d.mts} +28 -2
- package/types-Cq5gHCTq.cjs +20 -0
- package/types-YJTkXKzU.mjs +9 -0
- package/{view-compiler-D_6gfXFr.mjs → view-compiler-BzKk8nY3.mjs} +2 -2
- package/{view-compiler-Bo7pqXW3.d.mts → view-compiler-D3zp9S3K.d.cts} +1 -1
- package/{view-compiler-CmEdAMwj.d.cts → view-compiler-TpzDTu7z.d.mts} +1 -1
- package/{view-compiler-BHI5E4QI.cjs → view-compiler-ZKZbd3SK.cjs} +2 -2
- package/vite.d.cts +1 -1
- package/vite.d.mts +1 -1
- package/internal-BLKMnIIZ.cjs +0 -259
- package/internal-BpVh_J77.mjs +0 -212
- package/internal-CvlFk3KC.d.mts +0 -180
- package/internal-DGUOLQAq.d.cts +0 -180
package/internal-BLKMnIIZ.cjs
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
const require_projection = require("./projection-DEwL-D8R.cjs");
|
|
2
|
-
const require_entity_config = require("./entity-config-CPM-Tp-d.cjs");
|
|
3
|
-
//#region ../core/src/projection-draft.ts
|
|
4
|
-
function emptyProjectionDraft(baseTable) {
|
|
5
|
-
return {
|
|
6
|
-
baseTable,
|
|
7
|
-
entityKey: "",
|
|
8
|
-
fieldBindings: {},
|
|
9
|
-
identity: { kind: "base_pk" },
|
|
10
|
-
joins: []
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* The ON conditions implied by a foreign key between the two tables, so the
|
|
15
|
-
* builder can pre-fill a join instead of making the user hand-pick columns.
|
|
16
|
-
* Returns [] when no FK relates them — the user then picks manually.
|
|
17
|
-
*/
|
|
18
|
-
function suggestJoinConditions(baseTable, joinTable, schema, joinKey) {
|
|
19
|
-
const join = schema.find((t) => t.name === joinTable);
|
|
20
|
-
const conditions = [];
|
|
21
|
-
for (const column of join?.columnsMeta ?? []) if (column.references?.table === baseTable) conditions.push({
|
|
22
|
-
left: {
|
|
23
|
-
table: joinKey,
|
|
24
|
-
column: column.name
|
|
25
|
-
},
|
|
26
|
-
op: "eq",
|
|
27
|
-
right: {
|
|
28
|
-
table: require_projection.BASE_REF,
|
|
29
|
-
column: column.references.column
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return conditions;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Add a join on `joinTable`, keyed and pre-filled.
|
|
36
|
-
*
|
|
37
|
-
* The key is the table's own name where that is free, because it is what the
|
|
38
|
-
* user already calls the table; a collision falls back to `_2`, `_3` and so on.
|
|
39
|
-
* Uniqueness is not cosmetic — the key is the SQL alias and the name every ref
|
|
40
|
-
* addresses the table by, so two joins sharing one would make every ref
|
|
41
|
-
* ambiguous. `base` is reserved for the base table and can never be taken.
|
|
42
|
-
*/
|
|
43
|
-
function addDraftJoin(draft, joinTable, schema) {
|
|
44
|
-
const taken = /* @__PURE__ */ new Set([require_projection.BASE_REF, ...draft.joins.map((j) => j.key)]);
|
|
45
|
-
let key = joinTable;
|
|
46
|
-
let suffix = 2;
|
|
47
|
-
while (taken.has(key)) {
|
|
48
|
-
key = `${joinTable}_${suffix}`;
|
|
49
|
-
suffix += 1;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
...draft,
|
|
53
|
-
joins: [...draft.joins, {
|
|
54
|
-
key,
|
|
55
|
-
table: joinTable,
|
|
56
|
-
type: "left",
|
|
57
|
-
on: suggestJoinConditions(draft.baseTable, joinTable, schema, key)
|
|
58
|
-
}]
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Remove a join, and everything that addressed it.
|
|
63
|
-
*
|
|
64
|
-
* Refs to a removed join are refs to a table the projection no longer declares.
|
|
65
|
-
* Left in place they fail validation as "unknown ref table", which blames the
|
|
66
|
-
* field — or, worse, the identity — for the join's removal. Identity falls back
|
|
67
|
-
* to the base primary key, the same default a new draft starts from.
|
|
68
|
-
*/
|
|
69
|
-
function removeDraftJoin(draft, joinKey) {
|
|
70
|
-
const fieldBindings = Object.fromEntries(Object.entries(draft.fieldBindings).filter(([, binding]) => binding.from.table !== joinKey));
|
|
71
|
-
const identityDependsOnJoin = require_projection.identityRefs(draft.identity, "").some((ref) => ref.table === joinKey);
|
|
72
|
-
return {
|
|
73
|
-
...draft,
|
|
74
|
-
joins: draft.joins.filter((join) => join.key !== joinKey),
|
|
75
|
-
fieldBindings,
|
|
76
|
-
identity: identityDependsOnJoin ? { kind: "base_pk" } : draft.identity
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Every table a field or identity may currently read from, in ref order: the
|
|
81
|
-
* base table first, then each join.
|
|
82
|
-
*
|
|
83
|
-
* Returns [] when the base table is not in the discovered schema — there is
|
|
84
|
-
* nothing truthful to offer, and inventing columns would let someone build a
|
|
85
|
-
* projection that cannot compile.
|
|
86
|
-
*/
|
|
87
|
-
function draftRefOptions(draft, schema) {
|
|
88
|
-
const byName = new Map(schema.map((table) => [table.name, table]));
|
|
89
|
-
const base = byName.get(draft.baseTable);
|
|
90
|
-
if (!base) return [];
|
|
91
|
-
const options = [{
|
|
92
|
-
ref: require_projection.BASE_REF,
|
|
93
|
-
label: `${base.name} (${require_projection.BASE_REF})`,
|
|
94
|
-
columns: base.columns
|
|
95
|
-
}];
|
|
96
|
-
for (const join of draft.joins) {
|
|
97
|
-
const table = byName.get(join.table);
|
|
98
|
-
if (table) options.push({
|
|
99
|
-
ref: join.key,
|
|
100
|
-
label: `${table.name} (${join.key})`,
|
|
101
|
-
columns: table.columns
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return options;
|
|
105
|
-
}
|
|
106
|
-
/** A draft becomes a projection once it has a valid key and ≥1 binding. */
|
|
107
|
-
function draftToProjection(draft) {
|
|
108
|
-
if (!require_projection.PROJECTION_KEY_PATTERN.test(draft.entityKey)) return null;
|
|
109
|
-
if (Object.keys(draft.fieldBindings).length === 0) return null;
|
|
110
|
-
return {
|
|
111
|
-
version: 1,
|
|
112
|
-
kind: "projection",
|
|
113
|
-
entityKey: draft.entityKey,
|
|
114
|
-
baseTable: draft.baseTable,
|
|
115
|
-
joins: draft.joins,
|
|
116
|
-
fieldBindings: draft.fieldBindings,
|
|
117
|
-
identity: draft.identity
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const PROBE_KEY = "draft";
|
|
121
|
-
/**
|
|
122
|
-
* A draft rendered as a projection so the semantic checks can run against it
|
|
123
|
-
* while it is being written, not only once it is finished.
|
|
124
|
-
*
|
|
125
|
-
* `entityKey` is stood in for rather than taken from the draft: it is the one
|
|
126
|
-
* field `validateProjection` never looks at, and a half-typed key would
|
|
127
|
-
* otherwise have to be valid before any join or identity could be checked. The
|
|
128
|
-
* missing or malformed key is reported separately, by `draftErrors`.
|
|
129
|
-
*/
|
|
130
|
-
function probeProjection(draft) {
|
|
131
|
-
return {
|
|
132
|
-
version: 1,
|
|
133
|
-
kind: "projection",
|
|
134
|
-
entityKey: PROBE_KEY,
|
|
135
|
-
baseTable: draft.baseTable,
|
|
136
|
-
joins: draft.joins,
|
|
137
|
-
fieldBindings: draft.fieldBindings,
|
|
138
|
-
identity: draft.identity
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* `validateProjection` appends its own `projection.` segment to the base path it
|
|
143
|
-
* is given, because in a config the base is the entity that owns the projection
|
|
144
|
-
* (`entities.accounts` → `entities.accounts.projection.identity`). A draft has
|
|
145
|
-
* no owning entity, so passing "projection" as the base produced
|
|
146
|
-
* `projection.projection.identity` — a path no caller could match against, and
|
|
147
|
-
* the reason `draftErrors` was only ever consumed as a flat list of messages.
|
|
148
|
-
*
|
|
149
|
-
* Passing an empty base and dropping the leading separator leaves one
|
|
150
|
-
* convention: every path here starts `projection.` exactly once, whether it came
|
|
151
|
-
* from this module or from the validator.
|
|
152
|
-
*/
|
|
153
|
-
function semanticErrors(draft, schema) {
|
|
154
|
-
return require_entity_config.validateProjection(probeProjection(draft), schema, "").map((error) => ({
|
|
155
|
-
...error,
|
|
156
|
-
path: error.path.startsWith(".") ? error.path.slice(1) : error.path
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Every reason this draft cannot be saved, one per cause and each anchored to
|
|
161
|
-
* the section that owns it.
|
|
162
|
-
*
|
|
163
|
-
* One lumped "Name the object and map a field." was all this used to return for
|
|
164
|
-
* an incomplete draft, at the bare path "projection". Two consequences, both
|
|
165
|
-
* bad: a builder had nowhere to attach the message, and — because it returned
|
|
166
|
-
* before validating — a fanning join under a `base_pk` identity stayed silent
|
|
167
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
168
|
-
* the user chose the join.
|
|
169
|
-
*/
|
|
170
|
-
function draftErrors(draft, schema) {
|
|
171
|
-
const errors = [];
|
|
172
|
-
if (draft.entityKey === "") errors.push({
|
|
173
|
-
path: "projection.entityKey",
|
|
174
|
-
message: "Key is required."
|
|
175
|
-
});
|
|
176
|
-
else if (!require_projection.PROJECTION_KEY_PATTERN.test(draft.entityKey)) errors.push({
|
|
177
|
-
path: "projection.entityKey",
|
|
178
|
-
message: `Key ${require_projection.PROJECTION_KEY_MESSAGE}.`
|
|
179
|
-
});
|
|
180
|
-
if (Object.keys(draft.fieldBindings).length === 0) errors.push({
|
|
181
|
-
path: "projection.fieldBindings",
|
|
182
|
-
message: "Map at least one field."
|
|
183
|
-
});
|
|
184
|
-
if (draft.baseTable === "") {
|
|
185
|
-
errors.push({
|
|
186
|
-
path: "projection.baseTable",
|
|
187
|
-
message: "Pick a base table."
|
|
188
|
-
});
|
|
189
|
-
return errors;
|
|
190
|
-
}
|
|
191
|
-
errors.push(...semanticErrors(draft, schema));
|
|
192
|
-
return errors;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Per-join fan-out, for the builder's inline cardinality hints.
|
|
196
|
-
*
|
|
197
|
-
* Answered from the joins alone. Joins are chosen before the key and the fields
|
|
198
|
-
* are filled in, and a fan-out warning is precisely what should steer the
|
|
199
|
-
* identity choice while it is being made — requiring a complete draft put the
|
|
200
|
-
* hint after the decision it exists to inform.
|
|
201
|
-
*/
|
|
202
|
-
function describeCardinality(draft, schema) {
|
|
203
|
-
const projection = probeProjection(draft);
|
|
204
|
-
const tablesByName = new Map(schema.map((t) => [t.name, t]));
|
|
205
|
-
const fanning = new Set(require_entity_config.fanOutJoins(projection, tablesByName).map((j) => j.key));
|
|
206
|
-
return projection.joins.map((join) => ({
|
|
207
|
-
joinKey: join.key,
|
|
208
|
-
fansOut: fanning.has(join.key)
|
|
209
|
-
}));
|
|
210
|
-
}
|
|
211
|
-
//#endregion
|
|
212
|
-
Object.defineProperty(exports, "addDraftJoin", {
|
|
213
|
-
enumerable: true,
|
|
214
|
-
get: function() {
|
|
215
|
-
return addDraftJoin;
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
Object.defineProperty(exports, "describeCardinality", {
|
|
219
|
-
enumerable: true,
|
|
220
|
-
get: function() {
|
|
221
|
-
return describeCardinality;
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
Object.defineProperty(exports, "draftErrors", {
|
|
225
|
-
enumerable: true,
|
|
226
|
-
get: function() {
|
|
227
|
-
return draftErrors;
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
Object.defineProperty(exports, "draftRefOptions", {
|
|
231
|
-
enumerable: true,
|
|
232
|
-
get: function() {
|
|
233
|
-
return draftRefOptions;
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
Object.defineProperty(exports, "draftToProjection", {
|
|
237
|
-
enumerable: true,
|
|
238
|
-
get: function() {
|
|
239
|
-
return draftToProjection;
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
Object.defineProperty(exports, "emptyProjectionDraft", {
|
|
243
|
-
enumerable: true,
|
|
244
|
-
get: function() {
|
|
245
|
-
return emptyProjectionDraft;
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
Object.defineProperty(exports, "removeDraftJoin", {
|
|
249
|
-
enumerable: true,
|
|
250
|
-
get: function() {
|
|
251
|
-
return removeDraftJoin;
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
Object.defineProperty(exports, "suggestJoinConditions", {
|
|
255
|
-
enumerable: true,
|
|
256
|
-
get: function() {
|
|
257
|
-
return suggestJoinConditions;
|
|
258
|
-
}
|
|
259
|
-
});
|
package/internal-BpVh_J77.mjs
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { i as identityRefs, n as PROJECTION_KEY_MESSAGE, r as PROJECTION_KEY_PATTERN, t as BASE_REF } from "./projection-hnPSgH6G.mjs";
|
|
2
|
-
import { h as fanOutJoins, m as validateProjection } from "./entity-config-C8IIkrmX.mjs";
|
|
3
|
-
//#region ../core/src/projection-draft.ts
|
|
4
|
-
function emptyProjectionDraft(baseTable) {
|
|
5
|
-
return {
|
|
6
|
-
baseTable,
|
|
7
|
-
entityKey: "",
|
|
8
|
-
fieldBindings: {},
|
|
9
|
-
identity: { kind: "base_pk" },
|
|
10
|
-
joins: []
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* The ON conditions implied by a foreign key between the two tables, so the
|
|
15
|
-
* builder can pre-fill a join instead of making the user hand-pick columns.
|
|
16
|
-
* Returns [] when no FK relates them — the user then picks manually.
|
|
17
|
-
*/
|
|
18
|
-
function suggestJoinConditions(baseTable, joinTable, schema, joinKey) {
|
|
19
|
-
const join = schema.find((t) => t.name === joinTable);
|
|
20
|
-
const conditions = [];
|
|
21
|
-
for (const column of join?.columnsMeta ?? []) if (column.references?.table === baseTable) conditions.push({
|
|
22
|
-
left: {
|
|
23
|
-
table: joinKey,
|
|
24
|
-
column: column.name
|
|
25
|
-
},
|
|
26
|
-
op: "eq",
|
|
27
|
-
right: {
|
|
28
|
-
table: BASE_REF,
|
|
29
|
-
column: column.references.column
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return conditions;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Add a join on `joinTable`, keyed and pre-filled.
|
|
36
|
-
*
|
|
37
|
-
* The key is the table's own name where that is free, because it is what the
|
|
38
|
-
* user already calls the table; a collision falls back to `_2`, `_3` and so on.
|
|
39
|
-
* Uniqueness is not cosmetic — the key is the SQL alias and the name every ref
|
|
40
|
-
* addresses the table by, so two joins sharing one would make every ref
|
|
41
|
-
* ambiguous. `base` is reserved for the base table and can never be taken.
|
|
42
|
-
*/
|
|
43
|
-
function addDraftJoin(draft, joinTable, schema) {
|
|
44
|
-
const taken = /* @__PURE__ */ new Set([BASE_REF, ...draft.joins.map((j) => j.key)]);
|
|
45
|
-
let key = joinTable;
|
|
46
|
-
let suffix = 2;
|
|
47
|
-
while (taken.has(key)) {
|
|
48
|
-
key = `${joinTable}_${suffix}`;
|
|
49
|
-
suffix += 1;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
...draft,
|
|
53
|
-
joins: [...draft.joins, {
|
|
54
|
-
key,
|
|
55
|
-
table: joinTable,
|
|
56
|
-
type: "left",
|
|
57
|
-
on: suggestJoinConditions(draft.baseTable, joinTable, schema, key)
|
|
58
|
-
}]
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Remove a join, and everything that addressed it.
|
|
63
|
-
*
|
|
64
|
-
* Refs to a removed join are refs to a table the projection no longer declares.
|
|
65
|
-
* Left in place they fail validation as "unknown ref table", which blames the
|
|
66
|
-
* field — or, worse, the identity — for the join's removal. Identity falls back
|
|
67
|
-
* to the base primary key, the same default a new draft starts from.
|
|
68
|
-
*/
|
|
69
|
-
function removeDraftJoin(draft, joinKey) {
|
|
70
|
-
const fieldBindings = Object.fromEntries(Object.entries(draft.fieldBindings).filter(([, binding]) => binding.from.table !== joinKey));
|
|
71
|
-
const identityDependsOnJoin = identityRefs(draft.identity, "").some((ref) => ref.table === joinKey);
|
|
72
|
-
return {
|
|
73
|
-
...draft,
|
|
74
|
-
joins: draft.joins.filter((join) => join.key !== joinKey),
|
|
75
|
-
fieldBindings,
|
|
76
|
-
identity: identityDependsOnJoin ? { kind: "base_pk" } : draft.identity
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Every table a field or identity may currently read from, in ref order: the
|
|
81
|
-
* base table first, then each join.
|
|
82
|
-
*
|
|
83
|
-
* Returns [] when the base table is not in the discovered schema — there is
|
|
84
|
-
* nothing truthful to offer, and inventing columns would let someone build a
|
|
85
|
-
* projection that cannot compile.
|
|
86
|
-
*/
|
|
87
|
-
function draftRefOptions(draft, schema) {
|
|
88
|
-
const byName = new Map(schema.map((table) => [table.name, table]));
|
|
89
|
-
const base = byName.get(draft.baseTable);
|
|
90
|
-
if (!base) return [];
|
|
91
|
-
const options = [{
|
|
92
|
-
ref: BASE_REF,
|
|
93
|
-
label: `${base.name} (${BASE_REF})`,
|
|
94
|
-
columns: base.columns
|
|
95
|
-
}];
|
|
96
|
-
for (const join of draft.joins) {
|
|
97
|
-
const table = byName.get(join.table);
|
|
98
|
-
if (table) options.push({
|
|
99
|
-
ref: join.key,
|
|
100
|
-
label: `${table.name} (${join.key})`,
|
|
101
|
-
columns: table.columns
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return options;
|
|
105
|
-
}
|
|
106
|
-
/** A draft becomes a projection once it has a valid key and ≥1 binding. */
|
|
107
|
-
function draftToProjection(draft) {
|
|
108
|
-
if (!PROJECTION_KEY_PATTERN.test(draft.entityKey)) return null;
|
|
109
|
-
if (Object.keys(draft.fieldBindings).length === 0) return null;
|
|
110
|
-
return {
|
|
111
|
-
version: 1,
|
|
112
|
-
kind: "projection",
|
|
113
|
-
entityKey: draft.entityKey,
|
|
114
|
-
baseTable: draft.baseTable,
|
|
115
|
-
joins: draft.joins,
|
|
116
|
-
fieldBindings: draft.fieldBindings,
|
|
117
|
-
identity: draft.identity
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const PROBE_KEY = "draft";
|
|
121
|
-
/**
|
|
122
|
-
* A draft rendered as a projection so the semantic checks can run against it
|
|
123
|
-
* while it is being written, not only once it is finished.
|
|
124
|
-
*
|
|
125
|
-
* `entityKey` is stood in for rather than taken from the draft: it is the one
|
|
126
|
-
* field `validateProjection` never looks at, and a half-typed key would
|
|
127
|
-
* otherwise have to be valid before any join or identity could be checked. The
|
|
128
|
-
* missing or malformed key is reported separately, by `draftErrors`.
|
|
129
|
-
*/
|
|
130
|
-
function probeProjection(draft) {
|
|
131
|
-
return {
|
|
132
|
-
version: 1,
|
|
133
|
-
kind: "projection",
|
|
134
|
-
entityKey: PROBE_KEY,
|
|
135
|
-
baseTable: draft.baseTable,
|
|
136
|
-
joins: draft.joins,
|
|
137
|
-
fieldBindings: draft.fieldBindings,
|
|
138
|
-
identity: draft.identity
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* `validateProjection` appends its own `projection.` segment to the base path it
|
|
143
|
-
* is given, because in a config the base is the entity that owns the projection
|
|
144
|
-
* (`entities.accounts` → `entities.accounts.projection.identity`). A draft has
|
|
145
|
-
* no owning entity, so passing "projection" as the base produced
|
|
146
|
-
* `projection.projection.identity` — a path no caller could match against, and
|
|
147
|
-
* the reason `draftErrors` was only ever consumed as a flat list of messages.
|
|
148
|
-
*
|
|
149
|
-
* Passing an empty base and dropping the leading separator leaves one
|
|
150
|
-
* convention: every path here starts `projection.` exactly once, whether it came
|
|
151
|
-
* from this module or from the validator.
|
|
152
|
-
*/
|
|
153
|
-
function semanticErrors(draft, schema) {
|
|
154
|
-
return validateProjection(probeProjection(draft), schema, "").map((error) => ({
|
|
155
|
-
...error,
|
|
156
|
-
path: error.path.startsWith(".") ? error.path.slice(1) : error.path
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Every reason this draft cannot be saved, one per cause and each anchored to
|
|
161
|
-
* the section that owns it.
|
|
162
|
-
*
|
|
163
|
-
* One lumped "Name the object and map a field." was all this used to return for
|
|
164
|
-
* an incomplete draft, at the bare path "projection". Two consequences, both
|
|
165
|
-
* bad: a builder had nowhere to attach the message, and — because it returned
|
|
166
|
-
* before validating — a fanning join under a `base_pk` identity stayed silent
|
|
167
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
168
|
-
* the user chose the join.
|
|
169
|
-
*/
|
|
170
|
-
function draftErrors(draft, schema) {
|
|
171
|
-
const errors = [];
|
|
172
|
-
if (draft.entityKey === "") errors.push({
|
|
173
|
-
path: "projection.entityKey",
|
|
174
|
-
message: "Key is required."
|
|
175
|
-
});
|
|
176
|
-
else if (!PROJECTION_KEY_PATTERN.test(draft.entityKey)) errors.push({
|
|
177
|
-
path: "projection.entityKey",
|
|
178
|
-
message: `Key ${PROJECTION_KEY_MESSAGE}.`
|
|
179
|
-
});
|
|
180
|
-
if (Object.keys(draft.fieldBindings).length === 0) errors.push({
|
|
181
|
-
path: "projection.fieldBindings",
|
|
182
|
-
message: "Map at least one field."
|
|
183
|
-
});
|
|
184
|
-
if (draft.baseTable === "") {
|
|
185
|
-
errors.push({
|
|
186
|
-
path: "projection.baseTable",
|
|
187
|
-
message: "Pick a base table."
|
|
188
|
-
});
|
|
189
|
-
return errors;
|
|
190
|
-
}
|
|
191
|
-
errors.push(...semanticErrors(draft, schema));
|
|
192
|
-
return errors;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Per-join fan-out, for the builder's inline cardinality hints.
|
|
196
|
-
*
|
|
197
|
-
* Answered from the joins alone. Joins are chosen before the key and the fields
|
|
198
|
-
* are filled in, and a fan-out warning is precisely what should steer the
|
|
199
|
-
* identity choice while it is being made — requiring a complete draft put the
|
|
200
|
-
* hint after the decision it exists to inform.
|
|
201
|
-
*/
|
|
202
|
-
function describeCardinality(draft, schema) {
|
|
203
|
-
const projection = probeProjection(draft);
|
|
204
|
-
const tablesByName = new Map(schema.map((t) => [t.name, t]));
|
|
205
|
-
const fanning = new Set(fanOutJoins(projection, tablesByName).map((j) => j.key));
|
|
206
|
-
return projection.joins.map((join) => ({
|
|
207
|
-
joinKey: join.key,
|
|
208
|
-
fansOut: fanning.has(join.key)
|
|
209
|
-
}));
|
|
210
|
-
}
|
|
211
|
-
//#endregion
|
|
212
|
-
export { draftToProjection as a, suggestJoinConditions as c, draftRefOptions as i, describeCardinality as n, emptyProjectionDraft as o, draftErrors as r, removeDraftJoin as s, addDraftJoin as t };
|
package/internal-CvlFk3KC.d.mts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { M as ProjectionIdentity, N as ProjectionJoin, P as ProjectionMapping, j as ProjectionFieldBinding, l as ConfigValidationError, m as DiscoveredSchemaTable, p as DiscoveredColumnMeta } from "./entity-config-B2RA53nw.mjs";
|
|
2
|
-
//#region ../core/src/projection-cardinality.d.ts
|
|
3
|
-
declare function joinFansOut(join: ProjectionJoin, joinTable: DiscoveredSchemaTable): boolean;
|
|
4
|
-
/** Every join in the projection that can multiply rows. */
|
|
5
|
-
declare function fanOutJoins(projection: ProjectionMapping, tablesByName: Map<string, DiscoveredSchemaTable>): ProjectionJoin[];
|
|
6
|
-
//#endregion
|
|
7
|
-
//#region ../core/src/projection-draft.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* The builder's in-progress shape.
|
|
10
|
-
*
|
|
11
|
-
* A draft is deliberately looser than a `ProjectionMapping`: it exists while
|
|
12
|
-
* the user is still choosing things, so every field can be empty. All of the
|
|
13
|
-
* builder's rules live here as pure functions, which keeps the React components
|
|
14
|
-
* presentational and lets the rules be tested without a DOM.
|
|
15
|
-
*/
|
|
16
|
-
interface ProjectionDraft {
|
|
17
|
-
baseTable: string;
|
|
18
|
-
entityKey: string;
|
|
19
|
-
fieldBindings: Record<string, ProjectionFieldBinding>;
|
|
20
|
-
identity: ProjectionIdentity;
|
|
21
|
-
joins: ProjectionJoin[];
|
|
22
|
-
}
|
|
23
|
-
declare function emptyProjectionDraft(baseTable: string): ProjectionDraft;
|
|
24
|
-
/**
|
|
25
|
-
* The ON conditions implied by a foreign key between the two tables, so the
|
|
26
|
-
* builder can pre-fill a join instead of making the user hand-pick columns.
|
|
27
|
-
* Returns [] when no FK relates them — the user then picks manually.
|
|
28
|
-
*/
|
|
29
|
-
declare function suggestJoinConditions(baseTable: string, joinTable: string, schema: DiscoveredSchemaTable[], joinKey: string): ProjectionJoin["on"];
|
|
30
|
-
/**
|
|
31
|
-
* Add a join on `joinTable`, keyed and pre-filled.
|
|
32
|
-
*
|
|
33
|
-
* The key is the table's own name where that is free, because it is what the
|
|
34
|
-
* user already calls the table; a collision falls back to `_2`, `_3` and so on.
|
|
35
|
-
* Uniqueness is not cosmetic — the key is the SQL alias and the name every ref
|
|
36
|
-
* addresses the table by, so two joins sharing one would make every ref
|
|
37
|
-
* ambiguous. `base` is reserved for the base table and can never be taken.
|
|
38
|
-
*/
|
|
39
|
-
declare function addDraftJoin(draft: ProjectionDraft, joinTable: string, schema: DiscoveredSchemaTable[]): ProjectionDraft;
|
|
40
|
-
/**
|
|
41
|
-
* Remove a join, and everything that addressed it.
|
|
42
|
-
*
|
|
43
|
-
* Refs to a removed join are refs to a table the projection no longer declares.
|
|
44
|
-
* Left in place they fail validation as "unknown ref table", which blames the
|
|
45
|
-
* field — or, worse, the identity — for the join's removal. Identity falls back
|
|
46
|
-
* to the base primary key, the same default a new draft starts from.
|
|
47
|
-
*/
|
|
48
|
-
declare function removeDraftJoin(draft: ProjectionDraft, joinKey: string): ProjectionDraft;
|
|
49
|
-
/** One selectable table in the builder's column pickers. */
|
|
50
|
-
interface DraftRefOption {
|
|
51
|
-
columns: string[];
|
|
52
|
-
label: string;
|
|
53
|
-
ref: string;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Every table a field or identity may currently read from, in ref order: the
|
|
57
|
-
* base table first, then each join.
|
|
58
|
-
*
|
|
59
|
-
* Returns [] when the base table is not in the discovered schema — there is
|
|
60
|
-
* nothing truthful to offer, and inventing columns would let someone build a
|
|
61
|
-
* projection that cannot compile.
|
|
62
|
-
*/
|
|
63
|
-
declare function draftRefOptions(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): DraftRefOption[];
|
|
64
|
-
/** A draft becomes a projection once it has a valid key and ≥1 binding. */
|
|
65
|
-
declare function draftToProjection(draft: ProjectionDraft): ProjectionMapping | null;
|
|
66
|
-
/**
|
|
67
|
-
* Every reason this draft cannot be saved, one per cause and each anchored to
|
|
68
|
-
* the section that owns it.
|
|
69
|
-
*
|
|
70
|
-
* One lumped "Name the object and map a field." was all this used to return for
|
|
71
|
-
* an incomplete draft, at the bare path "projection". Two consequences, both
|
|
72
|
-
* bad: a builder had nowhere to attach the message, and — because it returned
|
|
73
|
-
* before validating — a fanning join under a `base_pk` identity stayed silent
|
|
74
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
75
|
-
* the user chose the join.
|
|
76
|
-
*/
|
|
77
|
-
declare function draftErrors(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): ConfigValidationError[];
|
|
78
|
-
/**
|
|
79
|
-
* Per-join fan-out, for the builder's inline cardinality hints.
|
|
80
|
-
*
|
|
81
|
-
* Answered from the joins alone. Joins are chosen before the key and the fields
|
|
82
|
-
* are filled in, and a fan-out warning is precisely what should steer the
|
|
83
|
-
* identity choice while it is being made — requiring a complete draft put the
|
|
84
|
-
* hint after the decision it exists to inform.
|
|
85
|
-
*/
|
|
86
|
-
declare function describeCardinality(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): Array<{
|
|
87
|
-
fansOut: boolean;
|
|
88
|
-
joinKey: string;
|
|
89
|
-
}>;
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region ../core/src/projection-sql.d.ts
|
|
92
|
-
/**
|
|
93
|
-
* SQL generation for projection-backed entities.
|
|
94
|
-
*
|
|
95
|
-
* Base and joins are aliased by their ref key (`base`, then each join's `key`),
|
|
96
|
-
* which is what allows the same table to be joined twice and keeps generated
|
|
97
|
-
* SQL independent of host table names.
|
|
98
|
-
*
|
|
99
|
-
* Pure string generation — no DB access. Identifiers are escaped so output is
|
|
100
|
-
* deterministic and injection-safe.
|
|
101
|
-
*/
|
|
102
|
-
/** Quote a single identifier part, escaping embedded double-quotes. */
|
|
103
|
-
declare function quoteIdent(name: string): string;
|
|
104
|
-
/**
|
|
105
|
-
* The identity expression. Composite identities concatenate their parts with
|
|
106
|
-
* `::`; validation guarantees every part is NOT NULL and non-text, which is
|
|
107
|
-
* what makes the concatenation collision-free.
|
|
108
|
-
*
|
|
109
|
-
* Exported unaliased because two paths need the same expression under different
|
|
110
|
-
* names: the view compiler aliases it `id`, and the SDK's projection push source
|
|
111
|
-
* also orders and keyset-paginates by it. Sharing one definition is what keeps
|
|
112
|
-
* a pushed row's `id` identical to the one the view would have produced.
|
|
113
|
-
*/
|
|
114
|
-
declare function compileProjectionIdentity(projection: ProjectionMapping, basePkColumn: string): string;
|
|
115
|
-
declare function compileProjectionSelect(projection: ProjectionMapping, basePkColumn: string): {
|
|
116
|
-
columns: string[];
|
|
117
|
-
selectItems: string[];
|
|
118
|
-
};
|
|
119
|
-
declare function compileProjectionJoins(projection: ProjectionMapping): string[];
|
|
120
|
-
/** `FROM <baseTable> AS "base"` — the alias every ref resolves against. */
|
|
121
|
-
declare function compileProjectionFrom(projection: ProjectionMapping): string;
|
|
122
|
-
/**
|
|
123
|
-
* Hard ceiling on preview rows, regardless of what a caller asks for.
|
|
124
|
-
*
|
|
125
|
-
* A preview runs against a production database from a button a user can click
|
|
126
|
-
* as often as they like, so the cap belongs beside the query rather than in
|
|
127
|
-
* whichever caller happens to be passing a limit today.
|
|
128
|
-
*/
|
|
129
|
-
declare const PREVIEW_MAX_ROWS = 50;
|
|
130
|
-
/**
|
|
131
|
-
* One SELECT reading a few rows through an UNSAVED projection, for the derived
|
|
132
|
-
* object builder's preview.
|
|
133
|
-
*
|
|
134
|
-
* Lives here, beside the view compiler, because both sides of the round trip
|
|
135
|
-
* need the identical statement: an embedded org runs it against its own
|
|
136
|
-
* database from `@clivly.com/api`, and a cloud org's host app runs it from
|
|
137
|
-
* `@clivly/sdk`. Two copies would be free to disagree about what the user is
|
|
138
|
-
* being shown, which is the whole value of a preview.
|
|
139
|
-
*
|
|
140
|
-
* Columns are aliased by FIELD NAME, not by ref token. That is the deliberate
|
|
141
|
-
* difference from the push source, which aliases by token because cloud ingest
|
|
142
|
-
* reads `row[token]`; a preview is read by a person, in a table whose headers
|
|
143
|
-
* are the field names they just typed.
|
|
144
|
-
*/
|
|
145
|
-
declare function compileProjectionPreview(projection: ProjectionMapping, limit: number): string;
|
|
146
|
-
//#endregion
|
|
147
|
-
//#region ../core/src/projection-validate.d.ts
|
|
148
|
-
/**
|
|
149
|
-
* Validate a projection against the discovered schema. Returns every error
|
|
150
|
-
* found (not just the first) so a builder UI can show them all at once.
|
|
151
|
-
*/
|
|
152
|
-
declare function validateProjection(projection: ProjectionMapping, schema: DiscoveredSchemaTable[], base: string): ConfigValidationError[];
|
|
153
|
-
//#endregion
|
|
154
|
-
//#region ../core/src/schema-meta.d.ts
|
|
155
|
-
/**
|
|
156
|
-
* Accessors over a discovered table's schema-v2 metadata.
|
|
157
|
-
*
|
|
158
|
-
* Every helper treats *absent* metadata as **unknown**, never as a negative
|
|
159
|
-
* fact. `coversUniqueKey` returning false on a v1 payload is what makes
|
|
160
|
-
* projection validation fail closed: we cannot prove a join is many-to-one, so
|
|
161
|
-
* we refuse to compile it rather than guess.
|
|
162
|
-
*/
|
|
163
|
-
declare function columnMetaFor(table: DiscoveredSchemaTable, column: string): DiscoveredColumnMeta | undefined;
|
|
164
|
-
/**
|
|
165
|
-
* Every primary-key column. More than one entry means a composite PK, which is
|
|
166
|
-
* why `base_pk` identity is rejected for such tables.
|
|
167
|
-
*/
|
|
168
|
-
declare function primaryKeyColumns(table: DiscoveredSchemaTable): string[];
|
|
169
|
-
/**
|
|
170
|
-
* Do `columns` contain every column of at least one unique key on `table`?
|
|
171
|
-
*
|
|
172
|
-
* This is the fan-out test. A join whose ON columns cover a unique key of the
|
|
173
|
-
* joined table matches at most one row per base row; one that does not can
|
|
174
|
-
* match many.
|
|
175
|
-
*/
|
|
176
|
-
declare function coversUniqueKey(table: DiscoveredSchemaTable, columns: string[]): boolean;
|
|
177
|
-
/** Is this table's payload rich enough to validate a projection against? */
|
|
178
|
-
declare function hasRichMeta(table: DiscoveredSchemaTable): boolean;
|
|
179
|
-
//#endregion
|
|
180
|
-
export { fanOutJoins as C, suggestJoinConditions as S, draftErrors as _, validateProjection as a, emptyProjectionDraft as b, compileProjectionIdentity as c, compileProjectionSelect as d, quoteIdent as f, describeCardinality as g, addDraftJoin as h, primaryKeyColumns as i, compileProjectionJoins as l, ProjectionDraft as m, coversUniqueKey as n, PREVIEW_MAX_ROWS as o, DraftRefOption as p, hasRichMeta as r, compileProjectionFrom as s, columnMetaFor as t, compileProjectionPreview as u, draftRefOptions as v, joinFansOut as w, removeDraftJoin as x, draftToProjection as y };
|