@clivly/core 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/dist/builder.cjs +599 -0
- package/dist/builder.d.cts +336 -0
- package/dist/builder.d.mts +336 -0
- package/dist/builder.mjs +589 -0
- package/dist/drizzle.d.cts +1 -1
- package/dist/drizzle.d.mts +1 -1
- package/dist/{entity-config-25v_udeY.cjs → entity-config-C5jBmG2C.cjs} +83 -68
- package/dist/{entity-config-buOIVtMM.d.cts → entity-config-DCa785OE.d.cts} +27 -1
- package/dist/{entity-config-buOIVtMM.d.mts → entity-config-DCa785OE.d.mts} +27 -1
- package/dist/{entity-config-DwGXbUo_.mjs → entity-config-DpHPMFIS.mjs} +71 -38
- package/dist/entity-config.cjs +1 -1
- package/dist/entity-config.d.cts +1 -1
- package/dist/entity-config.d.mts +1 -1
- package/dist/entity-config.mjs +1 -1
- package/dist/entity-heuristics.cjs +34 -1
- package/dist/entity-heuristics.d.cts +8 -2
- package/dist/entity-heuristics.d.mts +8 -2
- package/dist/entity-heuristics.mjs +34 -2
- package/dist/index.cjs +136 -199
- package/dist/index.d.cts +135 -89
- package/dist/index.d.mts +135 -89
- package/dist/index.mjs +124 -188
- package/dist/mapping-form.d.cts +1 -1
- package/dist/mapping-form.d.mts +1 -1
- package/dist/mappings-config.cjs +2 -16
- package/dist/mappings-config.d.cts +1 -1
- package/dist/mappings-config.d.mts +1 -1
- package/dist/mappings-config.mjs +1 -15
- package/dist/{projection-OJYCYmS_.cjs → projection-DFgCoyZl.cjs} +0 -6
- package/dist/{projection-BXw_iiw5.mjs → projection-SCNrLJMV.mjs} +1 -1
- package/dist/schema-meta-Ac7938mT.mjs +46 -0
- package/dist/schema-meta-Cgi0MnHh.cjs +69 -0
- package/dist/sync-engine.cjs +1 -1
- package/dist/sync-engine.d.cts +1 -1
- package/dist/sync-engine.d.mts +1 -1
- package/dist/sync-engine.mjs +1 -1
- package/dist/types.cjs +11 -0
- package/dist/types.d.cts +28 -2
- package/dist/types.d.mts +28 -2
- package/dist/types.mjs +9 -1
- package/dist/{view-compiler-u3GGRgnv.mjs → view-compiler-CLvZ0wJW.mjs} +1 -1
- package/dist/{view-compiler-D30HNRnA.cjs → view-compiler-DP4Fbj7-.cjs} +1 -1
- package/dist/view-compiler.cjs +1 -1
- package/dist/view-compiler.d.cts +1 -1
- package/dist/view-compiler.d.mts +1 -1
- package/dist/view-compiler.mjs +1 -1
- package/package.json +11 -1
package/dist/index.mjs
CHANGED
|
@@ -1,217 +1,153 @@
|
|
|
1
1
|
import { authCustom } from "./auth-adapter.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { i as projectionSchema, n as PROJECTION_KEY_PATTERN, r as identityRefs, t as BASE_REF } from "./projection-SCNrLJMV.mjs";
|
|
3
|
+
import { i as primaryKeyColumns, n as coversUniqueKey, r as hasRichMeta, t as columnMetaFor } from "./schema-meta-Ac7938mT.mjs";
|
|
4
|
+
import { _ as joinFansOut, a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, g as fanOutJoins, h as validateProjectionFilter, i as ClivlyConfigError, l as filterSchema, m as validateProjection, n as CRM_CONCEPTS, p as validateEntitiesConfig, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-DpHPMFIS.mjs";
|
|
4
5
|
import { buildFilterFromDiscriminator, buildRelationships, relationshipViaSignature } from "./mapping-form.mjs";
|
|
5
6
|
import { mappingsToEntitiesConfig } from "./mappings-config.mjs";
|
|
6
|
-
import { c as compileProjectionIdentity, d as compileProjectionSelect, f as quoteIdent, i as explainEntitiesConfig, l as compileProjectionJoins, n as compileEntityView, o as PREVIEW_MAX_ROWS, r as compileEntityViews, s as compileProjectionFrom, t as DEFAULT_VIEW_PREFIX, u as compileProjectionPreview } from "./view-compiler-
|
|
7
|
+
import { c as compileProjectionIdentity, d as compileProjectionSelect, f as quoteIdent, i as explainEntitiesConfig, l as compileProjectionJoins, n as compileEntityView, o as PREVIEW_MAX_ROWS, r as compileEntityViews, s as compileProjectionFrom, t as DEFAULT_VIEW_PREFIX, u as compileProjectionPreview } from "./view-compiler-CLvZ0wJW.mjs";
|
|
7
8
|
import { reconcile, runSync } from "./sync-engine.mjs";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
baseTable,
|
|
12
|
-
entityKey: "",
|
|
13
|
-
fieldBindings: {},
|
|
14
|
-
identity: { kind: "base_pk" },
|
|
15
|
-
joins: []
|
|
16
|
-
};
|
|
17
|
-
}
|
|
9
|
+
import { OPEN_STAGE_TYPE, isClosedStageType } from "./types.mjs";
|
|
10
|
+
//#region src/slug.ts
|
|
18
11
|
/**
|
|
19
|
-
* The
|
|
20
|
-
*
|
|
21
|
-
*
|
|
12
|
+
* The one `slugify`.
|
|
13
|
+
*
|
|
14
|
+
* Three byte-identical copies existed before this module — in `@clivly/api`
|
|
15
|
+
* (the custom object-type slug), in `apps/web`'s onboarding route (the
|
|
16
|
+
* organisation slug), and in `@clivly/sdk`. The first two now import this; the
|
|
17
|
+
* SDK's copy stays where it is on purpose, because `@clivly/sdk` cannot depend
|
|
18
|
+
* on this package and its own comment records that its pattern is deliberately
|
|
19
|
+
* kept in step with the server's by hand.
|
|
20
|
+
*
|
|
21
|
+
* Note that the two callers this replaces slug *different things* — an
|
|
22
|
+
* object type and an organisation. They share a transform, not a namespace.
|
|
23
|
+
* Sharing the function stops the transform drifting; it does not make those two
|
|
24
|
+
* identifiers the same identifier.
|
|
22
25
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const conditions = [];
|
|
26
|
-
for (const column of join?.columnsMeta ?? []) if (column.references?.table === baseTable) conditions.push({
|
|
27
|
-
left: {
|
|
28
|
-
table: joinKey,
|
|
29
|
-
column: column.name
|
|
30
|
-
},
|
|
31
|
-
op: "eq",
|
|
32
|
-
right: {
|
|
33
|
-
table: BASE_REF,
|
|
34
|
-
column: column.references.column
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
return conditions;
|
|
38
|
-
}
|
|
26
|
+
const NON_ALNUM = /[^a-z0-9]+/g;
|
|
27
|
+
const EDGE_HYPHENS = /(^-|-$)/g;
|
|
39
28
|
/**
|
|
40
|
-
*
|
|
29
|
+
* Lowercase, trim, collapse every run of non-alphanumerics to one hyphen, and
|
|
30
|
+
* drop leading/trailing hyphens. `"Participant Info"` becomes
|
|
31
|
+
* `"participant-info"`.
|
|
41
32
|
*
|
|
42
|
-
* The
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
33
|
+
* The result is NOT guaranteed to be an acceptable slug. Both
|
|
34
|
+
* `OBJECT_TYPE_SLUG_PATTERN` (server) and `PROJECTION_KEY_PATTERN` (this
|
|
35
|
+
* package) require a leading letter, and this function happily returns
|
|
36
|
+
* `"2024-invoices"` for `"2024 Invoices"`. Callers that need an
|
|
37
|
+
* acceptance-guaranteed identifier want `toObjectTypeSlug`.
|
|
47
38
|
*/
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
let key = joinTable;
|
|
51
|
-
let suffix = 2;
|
|
52
|
-
while (taken.has(key)) {
|
|
53
|
-
key = `${joinTable}_${suffix}`;
|
|
54
|
-
suffix += 1;
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
...draft,
|
|
58
|
-
joins: [...draft.joins, {
|
|
59
|
-
key,
|
|
60
|
-
table: joinTable,
|
|
61
|
-
type: "left",
|
|
62
|
-
on: suggestJoinConditions(draft.baseTable, joinTable, schema, key)
|
|
63
|
-
}]
|
|
64
|
-
};
|
|
39
|
+
function slugify(value) {
|
|
40
|
+
return value.toLowerCase().trim().replace(NON_ALNUM, "-").replace(EDGE_HYPHENS, "");
|
|
65
41
|
}
|
|
66
42
|
/**
|
|
67
|
-
*
|
|
43
|
+
* Prefixed onto a slug that would otherwise not start with a letter.
|
|
68
44
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* to the base primary key, the same default a new draft starts from.
|
|
45
|
+
* Chosen over trimming the offending characters because dropping them loses
|
|
46
|
+
* information the user typed: `"2024 Invoices"` would become `"invoices"`,
|
|
47
|
+
* which collides with a genuinely different object of that name.
|
|
73
48
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
...draft,
|
|
79
|
-
joins: draft.joins.filter((join) => join.key !== joinKey),
|
|
80
|
-
fieldBindings,
|
|
81
|
-
identity: identityDependsOnJoin ? { kind: "base_pk" } : draft.identity
|
|
82
|
-
};
|
|
83
|
-
}
|
|
49
|
+
const LEADING_DIGIT_PREFIX = "obj-";
|
|
50
|
+
/** Mirrors `PROJECTION_KEY_PATTERN` / the server's `OBJECT_TYPE_SLUG_PATTERN`. */
|
|
51
|
+
const MUST_START_WITH_LETTER = /^[a-z]/;
|
|
84
52
|
/**
|
|
85
|
-
*
|
|
86
|
-
* base table first, then each join.
|
|
53
|
+
* A slug the server will accept, derived from a display name.
|
|
87
54
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
55
|
+
* `slugify` alone is not enough: the server's `OBJECT_TYPE_SLUG_PATTERN`
|
|
56
|
+
* (`apps/server/src/index.ts`) demands a leading letter, so a name beginning
|
|
57
|
+
* with a digit slugs to something the platform rejects at push time — long
|
|
58
|
+
* after the object was authored. `@clivly/sdk`'s `custom-slug.ts` already knows
|
|
59
|
+
* this case and deliberately withholds `"2024-invoices"` as a suggestion
|
|
60
|
+
* because it would be rejected too.
|
|
61
|
+
*
|
|
62
|
+
* The prefix is applied ONLY when the derived slug does not already start with
|
|
63
|
+
* a letter, so the overwhelmingly common name is untouched.
|
|
64
|
+
*
|
|
65
|
+
* This value must be shown to whoever authored the name. It is the identifier
|
|
66
|
+
* the host app declares as its `objectType`, so a slug derived silently is a
|
|
67
|
+
* slug the developer cannot copy into their config.
|
|
68
|
+
*
|
|
69
|
+
* Returns an empty string for a name with no alphanumerics at all — the caller
|
|
70
|
+
* decides whether that is "keep typing" or an error, since only it knows
|
|
71
|
+
* whether the field is still being edited.
|
|
91
72
|
*/
|
|
92
|
-
function
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
73
|
+
function toObjectTypeSlug(displayName) {
|
|
74
|
+
const slug = slugify(displayName);
|
|
75
|
+
if (slug === "") return "";
|
|
76
|
+
return MUST_START_WITH_LETTER.test(slug) ? slug : `${LEADING_DIGIT_PREFIX}${slug}`;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/stage-rules.ts
|
|
80
|
+
const CONDITION_OPS = /* @__PURE__ */ new Set([
|
|
81
|
+
"is_empty",
|
|
82
|
+
"is_not_empty",
|
|
83
|
+
"equals",
|
|
84
|
+
"not_equals",
|
|
85
|
+
"greater_than",
|
|
86
|
+
"less_than",
|
|
87
|
+
"before",
|
|
88
|
+
"after"
|
|
89
|
+
]);
|
|
90
|
+
function isCondition(value) {
|
|
91
|
+
if (!value || typeof value !== "object") return false;
|
|
92
|
+
const condition = value;
|
|
93
|
+
return typeof condition.field === "string" && condition.field.length > 0 && typeof condition.op === "string" && CONDITION_OPS.has(condition.op) && (condition.value === void 0 || typeof condition.value === "string");
|
|
110
94
|
}
|
|
111
|
-
/**
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
if (Object.keys(draft.fieldBindings).length === 0) return null;
|
|
115
|
-
return {
|
|
116
|
-
version: 1,
|
|
117
|
-
kind: "projection",
|
|
118
|
-
entityKey: draft.entityKey,
|
|
119
|
-
baseTable: draft.baseTable,
|
|
120
|
-
joins: draft.joins,
|
|
121
|
-
fieldBindings: draft.fieldBindings,
|
|
122
|
-
identity: draft.identity
|
|
123
|
-
};
|
|
95
|
+
/** Host fields arrive as strings; null and "" are both absent. */
|
|
96
|
+
function isEmpty(value) {
|
|
97
|
+
return value === null || value === void 0 || value === "";
|
|
124
98
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
* `entityKey` is stood in for rather than taken from the draft: it is the one
|
|
131
|
-
* field `validateProjection` never looks at, and a half-typed key would
|
|
132
|
-
* otherwise have to be valid before any join or identity could be checked. The
|
|
133
|
-
* missing or malformed key is reported separately, by `draftErrors`.
|
|
134
|
-
*/
|
|
135
|
-
function probeProjection(draft) {
|
|
136
|
-
return {
|
|
137
|
-
version: 1,
|
|
138
|
-
kind: "projection",
|
|
139
|
-
entityKey: PROBE_KEY,
|
|
140
|
-
baseTable: draft.baseTable,
|
|
141
|
-
joins: draft.joins,
|
|
142
|
-
fieldBindings: draft.fieldBindings,
|
|
143
|
-
identity: draft.identity
|
|
144
|
-
};
|
|
99
|
+
function compareNumbers(raw, operand, op) {
|
|
100
|
+
const left = Number(raw);
|
|
101
|
+
const right = Number(operand);
|
|
102
|
+
if (Number.isNaN(left) || Number.isNaN(right)) return false;
|
|
103
|
+
return op === "greater_than" ? left > right : left < right;
|
|
145
104
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
* `projection.projection.identity` — a path no caller could match against, and
|
|
152
|
-
* the reason `draftErrors` was only ever consumed as a flat list of messages.
|
|
153
|
-
*
|
|
154
|
-
* Passing an empty base and dropping the leading separator leaves one
|
|
155
|
-
* convention: every path here starts `projection.` exactly once, whether it came
|
|
156
|
-
* from this module or from the validator.
|
|
157
|
-
*/
|
|
158
|
-
function semanticErrors(draft, schema) {
|
|
159
|
-
return validateProjection(probeProjection(draft), schema, "").map((error) => ({
|
|
160
|
-
...error,
|
|
161
|
-
path: error.path.startsWith(".") ? error.path.slice(1) : error.path
|
|
162
|
-
}));
|
|
105
|
+
function compareDates(raw, operand, op) {
|
|
106
|
+
const left = new Date(raw).getTime();
|
|
107
|
+
const right = new Date(operand ?? "").getTime();
|
|
108
|
+
if (Number.isNaN(left) || Number.isNaN(right)) return false;
|
|
109
|
+
return op === "before" ? left < right : left > right;
|
|
163
110
|
}
|
|
164
111
|
/**
|
|
165
|
-
*
|
|
166
|
-
* the section that owns it.
|
|
112
|
+
* Whether one condition holds for a host row.
|
|
167
113
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
173
|
-
* the user chose the join.
|
|
114
|
+
* `mappedFields` is the set of columns the organization actually exposed. A
|
|
115
|
+
* condition naming anything outside it never matches, whatever the operator: a
|
|
116
|
+
* rule referencing a dropped column must fail safe, because treating the field
|
|
117
|
+
* as null would make `is_empty` match every deal.
|
|
174
118
|
*/
|
|
175
|
-
function
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
path: "projection.fieldBindings",
|
|
187
|
-
message: "Map at least one field."
|
|
188
|
-
});
|
|
189
|
-
if (draft.baseTable === "") {
|
|
190
|
-
errors.push({
|
|
191
|
-
path: "projection.baseTable",
|
|
192
|
-
message: "Pick a base table."
|
|
193
|
-
});
|
|
194
|
-
return errors;
|
|
195
|
-
}
|
|
196
|
-
errors.push(...semanticErrors(draft, schema));
|
|
197
|
-
return errors;
|
|
119
|
+
function matchesCondition(condition, fields, mappedFields) {
|
|
120
|
+
if (!mappedFields.has(condition.field)) return false;
|
|
121
|
+
const raw = fields[condition.field] ?? null;
|
|
122
|
+
if (condition.op === "is_empty") return isEmpty(raw);
|
|
123
|
+
if (condition.op === "is_not_empty") return !isEmpty(raw);
|
|
124
|
+
if (isEmpty(raw)) return false;
|
|
125
|
+
const value = raw;
|
|
126
|
+
if (condition.op === "equals") return value === condition.value;
|
|
127
|
+
if (condition.op === "not_equals") return value !== condition.value;
|
|
128
|
+
if (condition.op === "greater_than" || condition.op === "less_than") return compareNumbers(value, condition.value, condition.op);
|
|
129
|
+
return compareDates(value, condition.value, condition.op);
|
|
198
130
|
}
|
|
199
131
|
/**
|
|
200
|
-
*
|
|
132
|
+
* The id of the first rule whose conditions all hold, or null when none do.
|
|
133
|
+
*
|
|
134
|
+
* Returns a rule id rather than a stage key because the caller needs the
|
|
135
|
+
* matched rule's `allowClose` before it may move a deal into a closed stage.
|
|
201
136
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
137
|
+
* `rules` must arrive already ordered — stage position first, then rule
|
|
138
|
+
* position within a stage. Sorting needs stage rows this module deliberately
|
|
139
|
+
* does not see.
|
|
140
|
+
*
|
|
141
|
+
* A rule with no conditions never matches: an empty list would otherwise
|
|
142
|
+
* vacuously AND to true and capture every deal, which is the opposite of what
|
|
143
|
+
* a half-written rule should do.
|
|
206
144
|
*/
|
|
207
|
-
function
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
fansOut: fanning.has(join.key)
|
|
214
|
-
}));
|
|
145
|
+
function evaluateStage(rules, fields, mappedFields) {
|
|
146
|
+
for (const rule of rules) {
|
|
147
|
+
if (!Array.isArray(rule.conditions) || rule.conditions.length === 0 || !rule.conditions.every(isCondition)) continue;
|
|
148
|
+
if (rule.conditions.every((condition) => matchesCondition(condition, fields, mappedFields))) return rule.id;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
215
151
|
}
|
|
216
152
|
//#endregion
|
|
217
|
-
export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, PREVIEW_MAX_ROWS,
|
|
153
|
+
export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, isClosedStageType, joinFansOut, mappingsToEntitiesConfig, matchesCondition, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
|
package/dist/mapping-form.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as RelationshipVia, h as FilterExpr, x as RelationshipsMap } from "./entity-config-
|
|
1
|
+
import { b as RelationshipVia, h as FilterExpr, x as RelationshipsMap } from "./entity-config-DCa785OE.cjs";
|
|
2
2
|
//#region src/mapping-form.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* A relationship the mapping UI can offer or has configured, in the shape the
|
package/dist/mapping-form.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as RelationshipVia, h as FilterExpr, x as RelationshipsMap } from "./entity-config-
|
|
1
|
+
import { b as RelationshipVia, h as FilterExpr, x as RelationshipsMap } from "./entity-config-DCa785OE.mjs";
|
|
2
2
|
//#region src/mapping-form.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* A relationship the mapping UI can offer or has configured, in the shape the
|
package/dist/mappings-config.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_entity_config = require("./entity-config-
|
|
2
|
+
const require_entity_config = require("./entity-config-C5jBmG2C.cjs");
|
|
3
3
|
//#region src/mappings-config.ts
|
|
4
4
|
const RESERVED_FIELD = "id";
|
|
5
5
|
/**
|
|
@@ -48,26 +48,12 @@ function uniquify(base, used) {
|
|
|
48
48
|
used.add(key);
|
|
49
49
|
return key;
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
* A projection's `fields` map, derived mechanically from its bindings.
|
|
53
|
-
*
|
|
54
|
-
* `ClivlyEntityConfig.fields` is required and read by existing consumers, so a
|
|
55
|
-
* projection entity must populate it. The values are ref *tokens*
|
|
56
|
-
* (`"base.full_name"`), never host column names — the compiler reads
|
|
57
|
-
* `projection.fieldBindings`, and `validateEntitiesConfig` skips the legacy
|
|
58
|
-
* column check for projection entities precisely because of this.
|
|
59
|
-
*/
|
|
60
|
-
function projectionFields(projection) {
|
|
61
|
-
const fields = {};
|
|
62
|
-
for (const [field, binding] of Object.entries(projection.fieldBindings)) fields[field] = `${binding.from.table}.${binding.from.column}`;
|
|
63
|
-
return fields;
|
|
64
|
-
}
|
|
65
51
|
function buildEntity(row, refIndex) {
|
|
66
52
|
const entity = {
|
|
67
53
|
concept: row.targetConcept,
|
|
68
54
|
source: row.projection?.baseTable ?? row.sourceTable,
|
|
69
55
|
role: row.role ?? DEFAULT_ROLE,
|
|
70
|
-
fields: row.projection ? projectionFields(row.projection) : canonicalFields(row.targetConcept, row.fieldMap)
|
|
56
|
+
fields: row.projection ? require_entity_config.projectionFields(row.projection) : canonicalFields(row.targetConcept, row.fieldMap)
|
|
71
57
|
};
|
|
72
58
|
if (row.projection) entity.projection = row.projection;
|
|
73
59
|
if (row.targetConcept === "custom" && row.targetObjectTypeId) entity.targetObjectTypeId = row.targetObjectTypeId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as ProjectionMapping, a as ClivlyEntitiesConfig, h as FilterExpr, x as RelationshipsMap } from "./entity-config-DCa785OE.cjs";
|
|
2
2
|
//#region src/mappings-config.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Mappings → Entity Config bridge.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as ProjectionMapping, a as ClivlyEntitiesConfig, h as FilterExpr, x as RelationshipsMap } from "./entity-config-DCa785OE.mjs";
|
|
2
2
|
//#region src/mappings-config.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Mappings → Entity Config bridge.
|
package/dist/mappings-config.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CANONICAL_FIELDS } from "./entity-config-
|
|
1
|
+
import { t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-DpHPMFIS.mjs";
|
|
2
2
|
//#region src/mappings-config.ts
|
|
3
3
|
const RESERVED_FIELD = "id";
|
|
4
4
|
/**
|
|
@@ -47,20 +47,6 @@ function uniquify(base, used) {
|
|
|
47
47
|
used.add(key);
|
|
48
48
|
return key;
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
* A projection's `fields` map, derived mechanically from its bindings.
|
|
52
|
-
*
|
|
53
|
-
* `ClivlyEntityConfig.fields` is required and read by existing consumers, so a
|
|
54
|
-
* projection entity must populate it. The values are ref *tokens*
|
|
55
|
-
* (`"base.full_name"`), never host column names — the compiler reads
|
|
56
|
-
* `projection.fieldBindings`, and `validateEntitiesConfig` skips the legacy
|
|
57
|
-
* column check for projection entities precisely because of this.
|
|
58
|
-
*/
|
|
59
|
-
function projectionFields(projection) {
|
|
60
|
-
const fields = {};
|
|
61
|
-
for (const [field, binding] of Object.entries(projection.fieldBindings)) fields[field] = `${binding.from.table}.${binding.from.column}`;
|
|
62
|
-
return fields;
|
|
63
|
-
}
|
|
64
50
|
function buildEntity(row, refIndex) {
|
|
65
51
|
const entity = {
|
|
66
52
|
concept: row.targetConcept,
|
|
@@ -99,12 +99,6 @@ Object.defineProperty(exports, "BASE_REF", {
|
|
|
99
99
|
return BASE_REF;
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
|
-
Object.defineProperty(exports, "PROJECTION_KEY_MESSAGE", {
|
|
103
|
-
enumerable: true,
|
|
104
|
-
get: function() {
|
|
105
|
-
return PROJECTION_KEY_MESSAGE;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
102
|
Object.defineProperty(exports, "PROJECTION_KEY_PATTERN", {
|
|
109
103
|
enumerable: true,
|
|
110
104
|
get: function() {
|
|
@@ -93,4 +93,4 @@ function identityRefs(identity, basePkColumn) {
|
|
|
93
93
|
return identity.parts;
|
|
94
94
|
}
|
|
95
95
|
//#endregion
|
|
96
|
-
export { projectionSchema as
|
|
96
|
+
export { projectionSchema as i, PROJECTION_KEY_PATTERN as n, identityRefs as r, BASE_REF as t };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/schema-meta.ts
|
|
2
|
+
/**
|
|
3
|
+
* Accessors over a discovered table's schema-v2 metadata.
|
|
4
|
+
*
|
|
5
|
+
* Every helper treats *absent* metadata as **unknown**, never as a negative
|
|
6
|
+
* fact. `coversUniqueKey` returning false on a v1 payload is what makes
|
|
7
|
+
* projection validation fail closed: we cannot prove a join is many-to-one, so
|
|
8
|
+
* we refuse to compile it rather than guess.
|
|
9
|
+
*/
|
|
10
|
+
function columnMetaFor(table, column) {
|
|
11
|
+
return table.columnsMeta?.find((c) => c.name === column);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Every primary-key column. More than one entry means a composite PK, which is
|
|
15
|
+
* why `base_pk` identity is rejected for such tables.
|
|
16
|
+
*/
|
|
17
|
+
function primaryKeyColumns(table) {
|
|
18
|
+
return (table.columnsMeta ?? []).filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Do `columns` contain every column of at least one unique key on `table`?
|
|
22
|
+
*
|
|
23
|
+
* This is the fan-out test. A join whose ON columns cover a unique key of the
|
|
24
|
+
* joined table matches at most one row per base row; one that does not can
|
|
25
|
+
* match many.
|
|
26
|
+
*/
|
|
27
|
+
function coversUniqueKey(table, columns) {
|
|
28
|
+
const present = new Set(columns);
|
|
29
|
+
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
33
|
+
*
|
|
34
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
35
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
36
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
37
|
+
* rather than a silent walk over `undefined`.
|
|
38
|
+
*
|
|
39
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
40
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
41
|
+
*/
|
|
42
|
+
function hasRichMeta(table) {
|
|
43
|
+
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { primaryKeyColumns as i, coversUniqueKey as n, hasRichMeta as r, columnMetaFor as t };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/schema-meta.ts
|
|
2
|
+
/**
|
|
3
|
+
* Accessors over a discovered table's schema-v2 metadata.
|
|
4
|
+
*
|
|
5
|
+
* Every helper treats *absent* metadata as **unknown**, never as a negative
|
|
6
|
+
* fact. `coversUniqueKey` returning false on a v1 payload is what makes
|
|
7
|
+
* projection validation fail closed: we cannot prove a join is many-to-one, so
|
|
8
|
+
* we refuse to compile it rather than guess.
|
|
9
|
+
*/
|
|
10
|
+
function columnMetaFor(table, column) {
|
|
11
|
+
return table.columnsMeta?.find((c) => c.name === column);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Every primary-key column. More than one entry means a composite PK, which is
|
|
15
|
+
* why `base_pk` identity is rejected for such tables.
|
|
16
|
+
*/
|
|
17
|
+
function primaryKeyColumns(table) {
|
|
18
|
+
return (table.columnsMeta ?? []).filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Do `columns` contain every column of at least one unique key on `table`?
|
|
22
|
+
*
|
|
23
|
+
* This is the fan-out test. A join whose ON columns cover a unique key of the
|
|
24
|
+
* joined table matches at most one row per base row; one that does not can
|
|
25
|
+
* match many.
|
|
26
|
+
*/
|
|
27
|
+
function coversUniqueKey(table, columns) {
|
|
28
|
+
const present = new Set(columns);
|
|
29
|
+
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
33
|
+
*
|
|
34
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
35
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
36
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
37
|
+
* rather than a silent walk over `undefined`.
|
|
38
|
+
*
|
|
39
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
40
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
41
|
+
*/
|
|
42
|
+
function hasRichMeta(table) {
|
|
43
|
+
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
Object.defineProperty(exports, "columnMetaFor", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function() {
|
|
49
|
+
return columnMetaFor;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
Object.defineProperty(exports, "coversUniqueKey", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: function() {
|
|
55
|
+
return coversUniqueKey;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(exports, "hasRichMeta", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
get: function() {
|
|
61
|
+
return hasRichMeta;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(exports, "primaryKeyColumns", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function() {
|
|
67
|
+
return primaryKeyColumns;
|
|
68
|
+
}
|
|
69
|
+
});
|
package/dist/sync-engine.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_view_compiler = require("./view-compiler-
|
|
2
|
+
const require_view_compiler = require("./view-compiler-DP4Fbj7-.cjs");
|
|
3
3
|
//#region src/sync-engine.ts
|
|
4
4
|
/**
|
|
5
5
|
* Diff a view's rows against the current mirror population for one entity.
|
package/dist/sync-engine.d.cts
CHANGED
package/dist/sync-engine.d.mts
CHANGED
package/dist/sync-engine.mjs
CHANGED
package/dist/types.cjs
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/types.ts
|
|
3
|
+
/** Named once so SQL predicates and UI fallbacks cannot drift apart. */
|
|
4
|
+
const OPEN_STAGE_TYPE = "open";
|
|
5
|
+
/** A closed deal is a historical fact: automation never reopens one. */
|
|
6
|
+
function isClosedStageType(type) {
|
|
7
|
+
return type === "won" || type === "lost";
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
exports.OPEN_STAGE_TYPE = OPEN_STAGE_TYPE;
|
|
11
|
+
exports.isClosedStageType = isClosedStageType;
|