@clivly/core 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 +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
|
@@ -1,40 +1,6 @@
|
|
|
1
|
-
const require_projection = require("./projection-
|
|
1
|
+
const require_projection = require("./projection-DFgCoyZl.cjs");
|
|
2
|
+
const require_schema_meta = require("./schema-meta-Cgi0MnHh.cjs");
|
|
2
3
|
let zod = require("zod");
|
|
3
|
-
//#region src/schema-meta.ts
|
|
4
|
-
/**
|
|
5
|
-
* Accessors over a discovered table's schema-v2 metadata.
|
|
6
|
-
*
|
|
7
|
-
* Every helper treats *absent* metadata as **unknown**, never as a negative
|
|
8
|
-
* fact. `coversUniqueKey` returning false on a v1 payload is what makes
|
|
9
|
-
* projection validation fail closed: we cannot prove a join is many-to-one, so
|
|
10
|
-
* we refuse to compile it rather than guess.
|
|
11
|
-
*/
|
|
12
|
-
function columnMetaFor(table, column) {
|
|
13
|
-
return table.columnsMeta?.find((c) => c.name === column);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Every primary-key column. More than one entry means a composite PK, which is
|
|
17
|
-
* why `base_pk` identity is rejected for such tables.
|
|
18
|
-
*/
|
|
19
|
-
function primaryKeyColumns(table) {
|
|
20
|
-
return (table.columnsMeta ?? []).filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Do `columns` contain every column of at least one unique key on `table`?
|
|
24
|
-
*
|
|
25
|
-
* This is the fan-out test. A join whose ON columns cover a unique key of the
|
|
26
|
-
* joined table matches at most one row per base row; one that does not can
|
|
27
|
-
* match many.
|
|
28
|
-
*/
|
|
29
|
-
function coversUniqueKey(table, columns) {
|
|
30
|
-
const present = new Set(columns);
|
|
31
|
-
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
32
|
-
}
|
|
33
|
-
/** Is this table's payload rich enough to validate a projection against? */
|
|
34
|
-
function hasRichMeta(table) {
|
|
35
|
-
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
4
|
//#region src/projection-cardinality.ts
|
|
39
5
|
/**
|
|
40
6
|
* Cardinality analysis for projection joins.
|
|
@@ -55,7 +21,7 @@ function joinSideColumns(join) {
|
|
|
55
21
|
return columns;
|
|
56
22
|
}
|
|
57
23
|
function joinFansOut(join, joinTable) {
|
|
58
|
-
return !coversUniqueKey(joinTable, joinSideColumns(join));
|
|
24
|
+
return !require_schema_meta.coversUniqueKey(joinTable, joinSideColumns(join));
|
|
59
25
|
}
|
|
60
26
|
/** Every join in the projection that can multiply rows. */
|
|
61
27
|
function fanOutJoins(projection, tablesByName) {
|
|
@@ -135,7 +101,7 @@ function checkTablesExist(errors, projection, resolver, base) {
|
|
|
135
101
|
ok = false;
|
|
136
102
|
continue;
|
|
137
103
|
}
|
|
138
|
-
if (!hasRichMeta(table)) {
|
|
104
|
+
if (!require_schema_meta.hasRichMeta(table)) {
|
|
139
105
|
errors.push({
|
|
140
106
|
path,
|
|
141
107
|
message: `table "${name}" was discovered without column types or unique keys, which a projection needs to prove its id is unique. Redeploy or restart the app so it re-reports its schema (or run \`clivly push-schema\`); if the table is declared by hand as \`{ name, columns }\`, use \`discoverFromDrizzle\` so keys and types come with it.`
|
|
@@ -194,14 +160,14 @@ function checkRefs(errors, projection, resolver, base, basePk) {
|
|
|
194
160
|
function checkSingleColumnIdentity(errors, resolver, path, ref) {
|
|
195
161
|
const table = resolver.tableFor(ref.table);
|
|
196
162
|
if (!table) return;
|
|
197
|
-
if (!coversUniqueKey(table, [ref.column])) errors.push({
|
|
163
|
+
if (!require_schema_meta.coversUniqueKey(table, [ref.column])) errors.push({
|
|
198
164
|
path,
|
|
199
165
|
message: `identity column "${ref.table}.${ref.column}" is not covered by a unique key of "${table.name}" — pick a column with a unique constraint, or use a composite identity that is`
|
|
200
166
|
});
|
|
201
167
|
}
|
|
202
168
|
function checkIdentitySafety(errors, projection, resolver, base, baseTable) {
|
|
203
169
|
const path = `${base}.projection.identity`;
|
|
204
|
-
const pk = primaryKeyColumns(baseTable);
|
|
170
|
+
const pk = require_schema_meta.primaryKeyColumns(baseTable);
|
|
205
171
|
const { identity } = projection;
|
|
206
172
|
if (identity.kind === "base_pk" && !identity.column) {
|
|
207
173
|
if (pk.length === 0) {
|
|
@@ -234,7 +200,7 @@ function checkIdentitySafety(errors, projection, resolver, base, baseTable) {
|
|
|
234
200
|
if (identity.kind !== "composite") return;
|
|
235
201
|
for (const part of identity.parts) {
|
|
236
202
|
const table = resolver.tableFor(part.table);
|
|
237
|
-
const meta = table && columnMetaFor(table, part.column);
|
|
203
|
+
const meta = table && require_schema_meta.columnMetaFor(table, part.column);
|
|
238
204
|
if (!meta) continue;
|
|
239
205
|
if (meta.nullable !== false) errors.push({
|
|
240
206
|
path,
|
|
@@ -268,12 +234,70 @@ function checkFanOut(errors, projection, resolver, base, basePk) {
|
|
|
268
234
|
});
|
|
269
235
|
return;
|
|
270
236
|
}
|
|
271
|
-
if (!coversUniqueKey(joinTable, require_projection.identityRefs(projection.identity, basePk).filter((ref) => ref.table === join.key).map((ref) => ref.column))) errors.push({
|
|
237
|
+
if (!require_schema_meta.coversUniqueKey(joinTable, require_projection.identityRefs(projection.identity, basePk).filter((ref) => ref.table === join.key).map((ref) => ref.column))) errors.push({
|
|
272
238
|
path: identityPath,
|
|
273
239
|
message: `identity does not uniquely identify a row of "${joinTable.name}", which fans out — include a unique key of that table`
|
|
274
240
|
});
|
|
275
241
|
}
|
|
276
242
|
/**
|
|
243
|
+
* One error naming a filter column that is not on the projection's base table,
|
|
244
|
+
* and the base table it would have to be on.
|
|
245
|
+
*/
|
|
246
|
+
function columnOnBase(column, baseTable, path) {
|
|
247
|
+
if (baseTable?.columns.includes(column)) return [];
|
|
248
|
+
return [{
|
|
249
|
+
path,
|
|
250
|
+
message: `filter column "${column}" does not exist on "${baseTable?.name ?? "the base table"}" — a derived object is filtered by its base table's columns only, so name one of those, or move the condition into a join's \`on\``
|
|
251
|
+
}];
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
255
|
+
* it can only name columns of the base table.
|
|
256
|
+
*
|
|
257
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
258
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
259
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
260
|
+
*
|
|
261
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
262
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
263
|
+
* syncs UNFILTERED.
|
|
264
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
265
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
266
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
267
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
268
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
269
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
270
|
+
*
|
|
271
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
272
|
+
*/
|
|
273
|
+
function validateProjectionFilter(filter, projection, schema, base) {
|
|
274
|
+
if (!filter) return [];
|
|
275
|
+
const path = `${base}.filter`;
|
|
276
|
+
if ("$raw" in filter) return [{
|
|
277
|
+
path,
|
|
278
|
+
message: "a $raw filter cannot be evaluated against a derived object's rows — it is opaque SQL, so the cloud passes every row through unfiltered. Express it as column conditions on the base table instead"
|
|
279
|
+
}];
|
|
280
|
+
const baseTable = schema.find((t) => t.name === projection.baseTable);
|
|
281
|
+
const errors = [];
|
|
282
|
+
for (const column of Object.keys(filter)) {
|
|
283
|
+
if (column.includes(".")) {
|
|
284
|
+
const [ref, ...rest] = column.split(".");
|
|
285
|
+
const bare = rest.join(".");
|
|
286
|
+
if (ref !== "base") {
|
|
287
|
+
errors.push({
|
|
288
|
+
path,
|
|
289
|
+
message: `filter column "${column}" names the join "${ref}", but a derived object is filtered by its base table ("${projection.baseTable}") only — filter on a base column, or move the condition into that join's \`on\``
|
|
290
|
+
});
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
errors.push(...columnOnBase(bare, baseTable, path));
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
errors.push(...columnOnBase(column, baseTable, path));
|
|
297
|
+
}
|
|
298
|
+
return errors;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
277
301
|
* Validate a projection against the discovered schema. Returns every error
|
|
278
302
|
* found (not just the first) so a builder UI can show them all at once.
|
|
279
303
|
*/
|
|
@@ -284,7 +308,7 @@ function validateProjection(projection, schema, base) {
|
|
|
284
308
|
if (!checkTablesExist(errors, projection, resolver, base)) return errors;
|
|
285
309
|
const baseTable = resolver.tablesByName.get(projection.baseTable);
|
|
286
310
|
if (!baseTable) return errors;
|
|
287
|
-
const basePk = primaryKeyColumns(baseTable)[0] ?? "id";
|
|
311
|
+
const basePk = require_schema_meta.primaryKeyColumns(baseTable)[0] ?? "id";
|
|
288
312
|
checkRefs(errors, projection, resolver, base, basePk);
|
|
289
313
|
checkIdentitySafety(errors, projection, resolver, base, baseTable);
|
|
290
314
|
checkFanOut(errors, projection, resolver, base, basePk);
|
|
@@ -546,6 +570,15 @@ function customObjectDeclarations(config) {
|
|
|
546
570
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
547
571
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
548
572
|
* name cannot tell apart.
|
|
573
|
+
*
|
|
574
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
575
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
576
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
577
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
578
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
579
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
580
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
581
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
549
582
|
*/
|
|
550
583
|
function projectionFields(projection) {
|
|
551
584
|
const fields = {};
|
|
@@ -684,7 +717,7 @@ function validateEntity(v, config, key, entity, schema) {
|
|
|
684
717
|
if (entity.source !== entity.projection.baseTable) v.report(`${base}.source`, `"source" ("${entity.source}") must match "projection.baseTable" ("${entity.projection.baseTable}") — the view compiler reads baseTable and the sync engine reads source, so a mismatch makes them disagree about which table this entity is`);
|
|
685
718
|
for (const error of validateProjection(entity.projection, schema, base)) v.report(error.path, error.message);
|
|
686
719
|
validateReadOnly(v, base, entity);
|
|
687
|
-
|
|
720
|
+
for (const error of validateProjectionFilter(entity.filter, entity.projection, schema, base)) v.report(error.path, error.message);
|
|
688
721
|
return;
|
|
689
722
|
}
|
|
690
723
|
validateFields(v, base, entity);
|
|
@@ -740,18 +773,6 @@ Object.defineProperty(exports, "MATERIALIZING_CONCEPTS", {
|
|
|
740
773
|
return MATERIALIZING_CONCEPTS;
|
|
741
774
|
}
|
|
742
775
|
});
|
|
743
|
-
Object.defineProperty(exports, "columnMetaFor", {
|
|
744
|
-
enumerable: true,
|
|
745
|
-
get: function() {
|
|
746
|
-
return columnMetaFor;
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
Object.defineProperty(exports, "coversUniqueKey", {
|
|
750
|
-
enumerable: true,
|
|
751
|
-
get: function() {
|
|
752
|
-
return coversUniqueKey;
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
776
|
Object.defineProperty(exports, "customObjectDeclarations", {
|
|
756
777
|
enumerable: true,
|
|
757
778
|
get: function() {
|
|
@@ -782,24 +803,12 @@ Object.defineProperty(exports, "filterSchema", {
|
|
|
782
803
|
return filterSchema;
|
|
783
804
|
}
|
|
784
805
|
});
|
|
785
|
-
Object.defineProperty(exports, "hasRichMeta", {
|
|
786
|
-
enumerable: true,
|
|
787
|
-
get: function() {
|
|
788
|
-
return hasRichMeta;
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
806
|
Object.defineProperty(exports, "joinFansOut", {
|
|
792
807
|
enumerable: true,
|
|
793
808
|
get: function() {
|
|
794
809
|
return joinFansOut;
|
|
795
810
|
}
|
|
796
811
|
});
|
|
797
|
-
Object.defineProperty(exports, "primaryKeyColumns", {
|
|
798
|
-
enumerable: true,
|
|
799
|
-
get: function() {
|
|
800
|
-
return primaryKeyColumns;
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
812
|
Object.defineProperty(exports, "projectionFields", {
|
|
804
813
|
enumerable: true,
|
|
805
814
|
get: function() {
|
|
@@ -830,3 +839,9 @@ Object.defineProperty(exports, "validateProjection", {
|
|
|
830
839
|
return validateProjection;
|
|
831
840
|
}
|
|
832
841
|
});
|
|
842
|
+
Object.defineProperty(exports, "validateProjectionFilter", {
|
|
843
|
+
enumerable: true,
|
|
844
|
+
get: function() {
|
|
845
|
+
return validateProjectionFilter;
|
|
846
|
+
}
|
|
847
|
+
});
|
|
@@ -16,6 +16,23 @@ import { z } from "zod";
|
|
|
16
16
|
*/
|
|
17
17
|
/** The reserved ref name for the projection's base table. */
|
|
18
18
|
declare const BASE_REF = "base";
|
|
19
|
+
/**
|
|
20
|
+
* Entity keys become view names (`crm_src_<entityKey>`), so keep them slugs.
|
|
21
|
+
*
|
|
22
|
+
* Accepts hyphens as well as underscores, matching `OBJECT_TYPE_SLUG_PATTERN`
|
|
23
|
+
* on the server. Those two were widened apart: the server learned hyphens
|
|
24
|
+
* because the SDK's own `slugify` collapses non-alphanumerics to them and
|
|
25
|
+
* `clivly status` reports that form as canonical — but this pattern was missed,
|
|
26
|
+
* so `clivly status` would tell you to key an entity `client-accounts` and then
|
|
27
|
+
* `defineClivlyConfig` would reject the config it had just asked for. Same bug,
|
|
28
|
+
* opposite half of the product.
|
|
29
|
+
*
|
|
30
|
+
* Widening is the safe direction: every key that was valid before still is, so
|
|
31
|
+
* nothing existing is renamed. Safe at all because neither use is ever a bare
|
|
32
|
+
* SQL identifier — the view name is quoted (`view-compiler.ts`, `quoteIdent`),
|
|
33
|
+
* and so is a join's `key` when it is emitted as an alias.
|
|
34
|
+
*/
|
|
35
|
+
declare const PROJECTION_KEY_PATTERN: RegExp;
|
|
19
36
|
declare const projectionRefSchema: z.ZodObject<{
|
|
20
37
|
table: z.ZodString;
|
|
21
38
|
column: z.ZodString;
|
|
@@ -517,6 +534,15 @@ declare function customObjectDeclarations(config: ClivlyEntitiesConfig): CustomO
|
|
|
517
534
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
518
535
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
519
536
|
* name cannot tell apart.
|
|
537
|
+
*
|
|
538
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
539
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
540
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
541
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
542
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
543
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
544
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
545
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
520
546
|
*/
|
|
521
547
|
declare function projectionFields(projection: ProjectionMapping): Record<string, string>;
|
|
522
548
|
declare function defineClivlyConfig(config: ClivlyEntitiesConfigInput, options?: {
|
|
@@ -577,4 +603,4 @@ interface ConfigValidationResult {
|
|
|
577
603
|
*/
|
|
578
604
|
declare function validateEntitiesConfig(config: ClivlyEntitiesConfig, schema: DiscoveredSchemaTable[]): ConfigValidationResult;
|
|
579
605
|
//#endregion
|
|
580
|
-
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E,
|
|
606
|
+
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E, ProjectionMapping as F, ProjectionRef as I, identityRefs as L, ProjectionFieldBinding as M, ProjectionIdentity as N, relationshipsSchema as O, ProjectionJoin as P, projectionSchema as R, customObjectDeclarations as S, filterSchema as T, MATERIALIZING_CONCEPTS as _, ClivlyEntitiesConfig as a, RelationshipVia as b, ClivlyEntityConfigInput as c, CrmConcept as d, CustomObjectDeclaration as f, FilterValue as g, FilterExpr as h, ClivlyConfigError as i, PROJECTION_KEY_PATTERN as j, validateEntitiesConfig as k, ConfigValidationError as l, DiscoveredSchemaTable as m, CRM_CONCEPTS as n, ClivlyEntitiesConfigInput as o, DiscoveredColumnMeta as p, CUSTOM_CONCEPT as r, ClivlyEntityConfig as s, CANONICAL_FIELDS as t, ConfigValidationResult as u, MaterializingConcept as v, entitiesConfigSchema as w, RelationshipsMap as x, RelationshipSpec as y };
|
|
@@ -16,6 +16,23 @@ import { z } from "zod";
|
|
|
16
16
|
*/
|
|
17
17
|
/** The reserved ref name for the projection's base table. */
|
|
18
18
|
declare const BASE_REF = "base";
|
|
19
|
+
/**
|
|
20
|
+
* Entity keys become view names (`crm_src_<entityKey>`), so keep them slugs.
|
|
21
|
+
*
|
|
22
|
+
* Accepts hyphens as well as underscores, matching `OBJECT_TYPE_SLUG_PATTERN`
|
|
23
|
+
* on the server. Those two were widened apart: the server learned hyphens
|
|
24
|
+
* because the SDK's own `slugify` collapses non-alphanumerics to them and
|
|
25
|
+
* `clivly status` reports that form as canonical — but this pattern was missed,
|
|
26
|
+
* so `clivly status` would tell you to key an entity `client-accounts` and then
|
|
27
|
+
* `defineClivlyConfig` would reject the config it had just asked for. Same bug,
|
|
28
|
+
* opposite half of the product.
|
|
29
|
+
*
|
|
30
|
+
* Widening is the safe direction: every key that was valid before still is, so
|
|
31
|
+
* nothing existing is renamed. Safe at all because neither use is ever a bare
|
|
32
|
+
* SQL identifier — the view name is quoted (`view-compiler.ts`, `quoteIdent`),
|
|
33
|
+
* and so is a join's `key` when it is emitted as an alias.
|
|
34
|
+
*/
|
|
35
|
+
declare const PROJECTION_KEY_PATTERN: RegExp;
|
|
19
36
|
declare const projectionRefSchema: z.ZodObject<{
|
|
20
37
|
table: z.ZodString;
|
|
21
38
|
column: z.ZodString;
|
|
@@ -517,6 +534,15 @@ declare function customObjectDeclarations(config: ClivlyEntitiesConfig): CustomO
|
|
|
517
534
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
518
535
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
519
536
|
* name cannot tell apart.
|
|
537
|
+
*
|
|
538
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
539
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
540
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
541
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
542
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
543
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
544
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
545
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
520
546
|
*/
|
|
521
547
|
declare function projectionFields(projection: ProjectionMapping): Record<string, string>;
|
|
522
548
|
declare function defineClivlyConfig(config: ClivlyEntitiesConfigInput, options?: {
|
|
@@ -577,4 +603,4 @@ interface ConfigValidationResult {
|
|
|
577
603
|
*/
|
|
578
604
|
declare function validateEntitiesConfig(config: ClivlyEntitiesConfig, schema: DiscoveredSchemaTable[]): ConfigValidationResult;
|
|
579
605
|
//#endregion
|
|
580
|
-
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E,
|
|
606
|
+
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E, ProjectionMapping as F, ProjectionRef as I, identityRefs as L, ProjectionFieldBinding as M, ProjectionIdentity as N, relationshipsSchema as O, ProjectionJoin as P, projectionSchema as R, customObjectDeclarations as S, filterSchema as T, MATERIALIZING_CONCEPTS as _, ClivlyEntitiesConfig as a, RelationshipVia as b, ClivlyEntityConfigInput as c, CrmConcept as d, CustomObjectDeclaration as f, FilterValue as g, FilterExpr as h, ClivlyConfigError as i, PROJECTION_KEY_PATTERN as j, validateEntitiesConfig as k, ConfigValidationError as l, DiscoveredSchemaTable as m, CRM_CONCEPTS as n, ClivlyEntitiesConfigInput as o, DiscoveredColumnMeta as p, CUSTOM_CONCEPT as r, ClivlyEntityConfig as s, CANONICAL_FIELDS as t, ConfigValidationResult as u, MaterializingConcept as v, entitiesConfigSchema as w, RelationshipsMap as x, RelationshipSpec as y };
|
|
@@ -1,40 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as projectionSchema, r as identityRefs, t as BASE_REF } from "./projection-SCNrLJMV.mjs";
|
|
2
|
+
import { i as primaryKeyColumns, n as coversUniqueKey, r as hasRichMeta, t as columnMetaFor } from "./schema-meta-Ac7938mT.mjs";
|
|
2
3
|
import { z } from "zod";
|
|
3
|
-
//#region src/schema-meta.ts
|
|
4
|
-
/**
|
|
5
|
-
* Accessors over a discovered table's schema-v2 metadata.
|
|
6
|
-
*
|
|
7
|
-
* Every helper treats *absent* metadata as **unknown**, never as a negative
|
|
8
|
-
* fact. `coversUniqueKey` returning false on a v1 payload is what makes
|
|
9
|
-
* projection validation fail closed: we cannot prove a join is many-to-one, so
|
|
10
|
-
* we refuse to compile it rather than guess.
|
|
11
|
-
*/
|
|
12
|
-
function columnMetaFor(table, column) {
|
|
13
|
-
return table.columnsMeta?.find((c) => c.name === column);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Every primary-key column. More than one entry means a composite PK, which is
|
|
17
|
-
* why `base_pk` identity is rejected for such tables.
|
|
18
|
-
*/
|
|
19
|
-
function primaryKeyColumns(table) {
|
|
20
|
-
return (table.columnsMeta ?? []).filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Do `columns` contain every column of at least one unique key on `table`?
|
|
24
|
-
*
|
|
25
|
-
* This is the fan-out test. A join whose ON columns cover a unique key of the
|
|
26
|
-
* joined table matches at most one row per base row; one that does not can
|
|
27
|
-
* match many.
|
|
28
|
-
*/
|
|
29
|
-
function coversUniqueKey(table, columns) {
|
|
30
|
-
const present = new Set(columns);
|
|
31
|
-
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
32
|
-
}
|
|
33
|
-
/** Is this table's payload rich enough to validate a projection against? */
|
|
34
|
-
function hasRichMeta(table) {
|
|
35
|
-
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
4
|
//#region src/projection-cardinality.ts
|
|
39
5
|
/**
|
|
40
6
|
* Cardinality analysis for projection joins.
|
|
@@ -274,6 +240,64 @@ function checkFanOut(errors, projection, resolver, base, basePk) {
|
|
|
274
240
|
});
|
|
275
241
|
}
|
|
276
242
|
/**
|
|
243
|
+
* One error naming a filter column that is not on the projection's base table,
|
|
244
|
+
* and the base table it would have to be on.
|
|
245
|
+
*/
|
|
246
|
+
function columnOnBase(column, baseTable, path) {
|
|
247
|
+
if (baseTable?.columns.includes(column)) return [];
|
|
248
|
+
return [{
|
|
249
|
+
path,
|
|
250
|
+
message: `filter column "${column}" does not exist on "${baseTable?.name ?? "the base table"}" — a derived object is filtered by its base table's columns only, so name one of those, or move the condition into a join's \`on\``
|
|
251
|
+
}];
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
255
|
+
* it can only name columns of the base table.
|
|
256
|
+
*
|
|
257
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
258
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
259
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
260
|
+
*
|
|
261
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
262
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
263
|
+
* syncs UNFILTERED.
|
|
264
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
265
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
266
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
267
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
268
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
269
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
270
|
+
*
|
|
271
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
272
|
+
*/
|
|
273
|
+
function validateProjectionFilter(filter, projection, schema, base) {
|
|
274
|
+
if (!filter) return [];
|
|
275
|
+
const path = `${base}.filter`;
|
|
276
|
+
if ("$raw" in filter) return [{
|
|
277
|
+
path,
|
|
278
|
+
message: "a $raw filter cannot be evaluated against a derived object's rows — it is opaque SQL, so the cloud passes every row through unfiltered. Express it as column conditions on the base table instead"
|
|
279
|
+
}];
|
|
280
|
+
const baseTable = schema.find((t) => t.name === projection.baseTable);
|
|
281
|
+
const errors = [];
|
|
282
|
+
for (const column of Object.keys(filter)) {
|
|
283
|
+
if (column.includes(".")) {
|
|
284
|
+
const [ref, ...rest] = column.split(".");
|
|
285
|
+
const bare = rest.join(".");
|
|
286
|
+
if (ref !== "base") {
|
|
287
|
+
errors.push({
|
|
288
|
+
path,
|
|
289
|
+
message: `filter column "${column}" names the join "${ref}", but a derived object is filtered by its base table ("${projection.baseTable}") only — filter on a base column, or move the condition into that join's \`on\``
|
|
290
|
+
});
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
errors.push(...columnOnBase(bare, baseTable, path));
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
errors.push(...columnOnBase(column, baseTable, path));
|
|
297
|
+
}
|
|
298
|
+
return errors;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
277
301
|
* Validate a projection against the discovered schema. Returns every error
|
|
278
302
|
* found (not just the first) so a builder UI can show them all at once.
|
|
279
303
|
*/
|
|
@@ -546,6 +570,15 @@ function customObjectDeclarations(config) {
|
|
|
546
570
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
547
571
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
548
572
|
* name cannot tell apart.
|
|
573
|
+
*
|
|
574
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
575
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
576
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
577
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
578
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
579
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
580
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
581
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
549
582
|
*/
|
|
550
583
|
function projectionFields(projection) {
|
|
551
584
|
const fields = {};
|
|
@@ -684,7 +717,7 @@ function validateEntity(v, config, key, entity, schema) {
|
|
|
684
717
|
if (entity.source !== entity.projection.baseTable) v.report(`${base}.source`, `"source" ("${entity.source}") must match "projection.baseTable" ("${entity.projection.baseTable}") — the view compiler reads baseTable and the sync engine reads source, so a mismatch makes them disagree about which table this entity is`);
|
|
685
718
|
for (const error of validateProjection(entity.projection, schema, base)) v.report(error.path, error.message);
|
|
686
719
|
validateReadOnly(v, base, entity);
|
|
687
|
-
|
|
720
|
+
for (const error of validateProjectionFilter(entity.filter, entity.projection, schema, base)) v.report(error.path, error.message);
|
|
688
721
|
return;
|
|
689
722
|
}
|
|
690
723
|
validateFields(v, base, entity);
|
|
@@ -710,4 +743,4 @@ function validateEntitiesConfig(config, schema) {
|
|
|
710
743
|
};
|
|
711
744
|
}
|
|
712
745
|
//#endregion
|
|
713
|
-
export {
|
|
746
|
+
export { joinFansOut as _, MATERIALIZING_CONCEPTS as a, entitiesConfigSchema as c, relationshipSchema as d, relationshipsSchema as f, fanOutJoins as g, validateProjectionFilter as h, ClivlyConfigError as i, filterSchema as l, validateProjection as m, CRM_CONCEPTS as n, customObjectDeclarations as o, validateEntitiesConfig as p, CUSTOM_CONCEPT as r, defineClivlyConfig as s, CANONICAL_FIELDS as t, projectionFields as u };
|
package/dist/entity-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
|
exports.CANONICAL_FIELDS = require_entity_config.CANONICAL_FIELDS;
|
|
4
4
|
exports.CRM_CONCEPTS = require_entity_config.CRM_CONCEPTS;
|
|
5
5
|
exports.CUSTOM_CONCEPT = require_entity_config.CUSTOM_CONCEPT;
|
package/dist/entity-config.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, O as relationshipsSchema, S as customObjectDeclarations, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, c as ClivlyEntityConfigInput, d as CrmConcept, f as CustomObjectDeclaration, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, o as ClivlyEntitiesConfigInput, p as DiscoveredColumnMeta, r as CUSTOM_CONCEPT, s as ClivlyEntityConfig, t as CANONICAL_FIELDS, u as ConfigValidationResult, v as MaterializingConcept, w as entitiesConfigSchema, x as RelationshipsMap, y as RelationshipSpec } from "./entity-config-
|
|
1
|
+
import { C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, O as relationshipsSchema, S as customObjectDeclarations, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, c as ClivlyEntityConfigInput, d as CrmConcept, f as CustomObjectDeclaration, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, o as ClivlyEntitiesConfigInput, p as DiscoveredColumnMeta, r as CUSTOM_CONCEPT, s as ClivlyEntityConfig, t as CANONICAL_FIELDS, u as ConfigValidationResult, v as MaterializingConcept, w as entitiesConfigSchema, x as RelationshipsMap, y as RelationshipSpec } from "./entity-config-DCa785OE.cjs";
|
|
2
2
|
export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntitiesConfigInput, ClivlyEntityConfig, ClivlyEntityConfigInput, ConfigValidationError, ConfigValidationResult, CrmConcept, CustomObjectDeclaration, DiscoveredColumnMeta, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, customObjectDeclarations, defineClivlyConfig, entitiesConfigSchema, filterSchema, projectionFields, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
|
package/dist/entity-config.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, O as relationshipsSchema, S as customObjectDeclarations, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, c as ClivlyEntityConfigInput, d as CrmConcept, f as CustomObjectDeclaration, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, o as ClivlyEntitiesConfigInput, p as DiscoveredColumnMeta, r as CUSTOM_CONCEPT, s as ClivlyEntityConfig, t as CANONICAL_FIELDS, u as ConfigValidationResult, v as MaterializingConcept, w as entitiesConfigSchema, x as RelationshipsMap, y as RelationshipSpec } from "./entity-config-
|
|
1
|
+
import { C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, O as relationshipsSchema, S as customObjectDeclarations, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, c as ClivlyEntityConfigInput, d as CrmConcept, f as CustomObjectDeclaration, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, o as ClivlyEntitiesConfigInput, p as DiscoveredColumnMeta, r as CUSTOM_CONCEPT, s as ClivlyEntityConfig, t as CANONICAL_FIELDS, u as ConfigValidationResult, v as MaterializingConcept, w as entitiesConfigSchema, x as RelationshipsMap, y as RelationshipSpec } from "./entity-config-DCa785OE.mjs";
|
|
2
2
|
export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntitiesConfigInput, ClivlyEntityConfig, ClivlyEntityConfigInput, ConfigValidationError, ConfigValidationResult, CrmConcept, CustomObjectDeclaration, DiscoveredColumnMeta, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, customObjectDeclarations, defineClivlyConfig, entitiesConfigSchema, filterSchema, projectionFields, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
|
package/dist/entity-config.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-
|
|
1
|
+
import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-DpHPMFIS.mjs";
|
|
2
2
|
export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, MATERIALIZING_CONCEPTS, customObjectDeclarations, defineClivlyConfig, entitiesConfigSchema, filterSchema, projectionFields, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
|
|
@@ -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
|
const require_mapping_score = require("./mapping-score.cjs");
|
|
4
4
|
//#region src/entity-heuristics.ts
|
|
5
5
|
const TARGET_CONCEPTS = [
|
|
@@ -251,6 +251,38 @@ function matchesConcept(tableName, concept) {
|
|
|
251
251
|
if (entry.names.some((name) => candidates.some((candidate) => candidate.includes(name)))) return { exact: false };
|
|
252
252
|
return null;
|
|
253
253
|
}
|
|
254
|
+
const SENSITIVE_COLUMNS = /* @__PURE__ */ new Set([
|
|
255
|
+
"password",
|
|
256
|
+
"password_hash",
|
|
257
|
+
"passwordhash",
|
|
258
|
+
"access_token",
|
|
259
|
+
"accesstoken",
|
|
260
|
+
"api_key",
|
|
261
|
+
"apikey",
|
|
262
|
+
"client_secret",
|
|
263
|
+
"clientsecret",
|
|
264
|
+
"encrypted_password",
|
|
265
|
+
"encryptedpassword",
|
|
266
|
+
"refresh_token",
|
|
267
|
+
"refreshtoken",
|
|
268
|
+
"id_token",
|
|
269
|
+
"idtoken",
|
|
270
|
+
"password_digest",
|
|
271
|
+
"passworddigest",
|
|
272
|
+
"private_key",
|
|
273
|
+
"privatekey",
|
|
274
|
+
"secret",
|
|
275
|
+
"session_token",
|
|
276
|
+
"sessiontoken"
|
|
277
|
+
]);
|
|
278
|
+
/**
|
|
279
|
+
* Does this table carry credentials? Such tables are withheld from suggestions
|
|
280
|
+
* and from bulk apply, so one click can never map an auth table into the CRM.
|
|
281
|
+
* They stay manually selectable — an explicit, informed choice is legitimate.
|
|
282
|
+
*/
|
|
283
|
+
function hasSensitiveColumns(columns) {
|
|
284
|
+
return columns.some((column) => SENSITIVE_COLUMNS.has(column.toLowerCase().replaceAll("-", "_")));
|
|
285
|
+
}
|
|
254
286
|
function metaFor(table, column) {
|
|
255
287
|
return table.columnsMeta?.find((c) => c.name === column);
|
|
256
288
|
}
|
|
@@ -463,6 +495,7 @@ function toFieldMap(scored) {
|
|
|
463
495
|
}
|
|
464
496
|
//#endregion
|
|
465
497
|
exports.TARGET_CONCEPTS = TARGET_CONCEPTS;
|
|
498
|
+
exports.hasSensitiveColumns = hasSensitiveColumns;
|
|
466
499
|
exports.matchesConcept = matchesConcept;
|
|
467
500
|
exports.requiredFieldsFor = requiredFieldsFor;
|
|
468
501
|
exports.scoreConcepts = scoreConcepts;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as DiscoveredColumnMeta } from "./entity-config-
|
|
1
|
+
import { p as DiscoveredColumnMeta } from "./entity-config-DCa785OE.cjs";
|
|
2
2
|
import { Scored } from "./mapping-score.cjs";
|
|
3
3
|
//#region src/entity-heuristics.d.ts
|
|
4
4
|
declare const TARGET_CONCEPTS: readonly ["contact", "company", "deal", "activity", "conversation", "note"];
|
|
@@ -13,6 +13,12 @@ interface ScoredTable {
|
|
|
13
13
|
name: string;
|
|
14
14
|
rowCount?: number;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Does this table carry credentials? Such tables are withheld from suggestions
|
|
18
|
+
* and from bulk apply, so one click can never map an auth table into the CRM.
|
|
19
|
+
* They stay manually selectable — an explicit, informed choice is legitimate.
|
|
20
|
+
*/
|
|
21
|
+
declare function hasSensitiveColumns(columns: string[]): boolean;
|
|
16
22
|
declare function scoreConcepts(table: ScoredTable): Scored<TargetConcept>[];
|
|
17
23
|
declare function topConcept(table: ScoredTable): {
|
|
18
24
|
best: Scored<TargetConcept>;
|
|
@@ -54,4 +60,4 @@ interface FieldSuggestion {
|
|
|
54
60
|
declare function scoreFieldMap(concept: TargetConcept, table: ScoredTable): Scored<FieldSuggestion>[];
|
|
55
61
|
declare function toFieldMap(scored: Scored<FieldSuggestion>[]): Record<string, string>;
|
|
56
62
|
//#endregion
|
|
57
|
-
export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
|
|
63
|
+
export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as DiscoveredColumnMeta } from "./entity-config-
|
|
1
|
+
import { p as DiscoveredColumnMeta } from "./entity-config-DCa785OE.mjs";
|
|
2
2
|
import { Scored } from "./mapping-score.mjs";
|
|
3
3
|
//#region src/entity-heuristics.d.ts
|
|
4
4
|
declare const TARGET_CONCEPTS: readonly ["contact", "company", "deal", "activity", "conversation", "note"];
|
|
@@ -13,6 +13,12 @@ interface ScoredTable {
|
|
|
13
13
|
name: string;
|
|
14
14
|
rowCount?: number;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Does this table carry credentials? Such tables are withheld from suggestions
|
|
18
|
+
* and from bulk apply, so one click can never map an auth table into the CRM.
|
|
19
|
+
* They stay manually selectable — an explicit, informed choice is legitimate.
|
|
20
|
+
*/
|
|
21
|
+
declare function hasSensitiveColumns(columns: string[]): boolean;
|
|
16
22
|
declare function scoreConcepts(table: ScoredTable): Scored<TargetConcept>[];
|
|
17
23
|
declare function topConcept(table: ScoredTable): {
|
|
18
24
|
best: Scored<TargetConcept>;
|
|
@@ -54,4 +60,4 @@ interface FieldSuggestion {
|
|
|
54
60
|
declare function scoreFieldMap(concept: TargetConcept, table: ScoredTable): Scored<FieldSuggestion>[];
|
|
55
61
|
declare function toFieldMap(scored: Scored<FieldSuggestion>[]): Record<string, string>;
|
|
56
62
|
//#endregion
|
|
57
|
-
export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
|
|
63
|
+
export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
|