@clivly/core 0.7.4 → 0.9.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/dist/index.d.mts CHANGED
@@ -1,9 +1,79 @@
1
1
  import { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, RecordSource, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput } from "./types.mjs";
2
2
  import { CRMAdapter } from "./adapter.mjs";
3
3
  import { ClivlyAuthAdapter, ClivlyUser, CrmMembership, MembershipResolver, authCustom } from "./auth-adapter.mjs";
4
- import { CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntityConfig, ConfigValidationError, ConfigValidationResult, CrmConcept, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig } from "./entity-config.mjs";
4
+ import { CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntityConfig, ConfigValidationError, ConfigValidationResult, CrmConcept, DiscoveredColumnMeta, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig } from "./entity-config.mjs";
5
5
  import { RelationshipCandidate, buildFilterFromDiscriminator, buildRelationships, relationshipViaSignature } from "./mapping-form.mjs";
6
- import { EntityMappingRow, mappingsToEntitiesConfig } from "./mappings-config.mjs";
6
+ import { a as ProjectionIdentity, c as ProjectionRef, i as ProjectionFieldBinding, l as identityRefs, n as mappingsToEntitiesConfig, o as ProjectionJoin, r as BASE_REF, s as ProjectionMapping, t as EntityMappingRow, u as projectionSchema } from "./mappings-config-B0W5bVZj.mjs";
7
7
  import { CompileOptions, CompiledView, DEFAULT_VIEW_PREFIX, ExplainResult, compileEntityView, compileEntityViews, explainEntitiesConfig } from "./view-compiler.mjs";
8
8
  import { EntitySyncResult, MirrorRecord, PersistAction, SourceRow, SyncAction, SyncResult, SyncStore, reconcile, runSync } from "./sync-engine.mjs";
9
- export { type Activity, type ActivityType, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, mappingsToEntitiesConfig, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, validateEntitiesConfig };
9
+ //#region src/projection-cardinality.d.ts
10
+ declare function joinFansOut(join: ProjectionJoin, joinTable: DiscoveredSchemaTable): boolean;
11
+ /** Every join in the projection that can multiply rows. */
12
+ declare function fanOutJoins(projection: ProjectionMapping, tablesByName: Map<string, DiscoveredSchemaTable>): ProjectionJoin[];
13
+ //#endregion
14
+ //#region src/projection-draft.d.ts
15
+ /**
16
+ * The builder's in-progress shape.
17
+ *
18
+ * A draft is deliberately looser than a `ProjectionMapping`: it exists while
19
+ * the user is still choosing things, so every field can be empty. All of the
20
+ * builder's rules live here as pure functions, which keeps the React components
21
+ * presentational and lets the rules be tested without a DOM.
22
+ */
23
+ interface ProjectionDraft {
24
+ baseTable: string;
25
+ entityKey: string;
26
+ fieldBindings: Record<string, ProjectionFieldBinding>;
27
+ identity: ProjectionIdentity;
28
+ joins: ProjectionJoin[];
29
+ }
30
+ declare function emptyProjectionDraft(baseTable: string): ProjectionDraft;
31
+ /**
32
+ * The ON conditions implied by a foreign key between the two tables, so the
33
+ * builder can pre-fill a join instead of making the user hand-pick columns.
34
+ * Returns [] when no FK relates them — the user then picks manually.
35
+ */
36
+ declare function suggestJoinConditions(baseTable: string, joinTable: string, schema: DiscoveredSchemaTable[], joinKey: string): ProjectionJoin["on"];
37
+ /** A draft becomes a projection once it has a valid key and ≥1 binding. */
38
+ declare function draftToProjection(draft: ProjectionDraft): ProjectionMapping | null;
39
+ declare function draftErrors(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): ConfigValidationError[];
40
+ /** Per-join fan-out, for the builder's inline cardinality hints. */
41
+ declare function describeCardinality(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): Array<{
42
+ fansOut: boolean;
43
+ joinKey: string;
44
+ }>;
45
+ //#endregion
46
+ //#region src/projection-validate.d.ts
47
+ /**
48
+ * Validate a projection against the discovered schema. Returns every error
49
+ * found (not just the first) so a builder UI can show them all at once.
50
+ */
51
+ declare function validateProjection(projection: ProjectionMapping, schema: DiscoveredSchemaTable[], base: string): ConfigValidationError[];
52
+ //#endregion
53
+ //#region src/schema-meta.d.ts
54
+ /**
55
+ * Accessors over a discovered table's schema-v2 metadata.
56
+ *
57
+ * Every helper treats *absent* metadata as **unknown**, never as a negative
58
+ * fact. `coversUniqueKey` returning false on a v1 payload is what makes
59
+ * projection validation fail closed: we cannot prove a join is many-to-one, so
60
+ * we refuse to compile it rather than guess.
61
+ */
62
+ declare function columnMetaFor(table: DiscoveredSchemaTable, column: string): DiscoveredColumnMeta | undefined;
63
+ /**
64
+ * Every primary-key column. More than one entry means a composite PK, which is
65
+ * why `base_pk` identity is rejected for such tables.
66
+ */
67
+ declare function primaryKeyColumns(table: DiscoveredSchemaTable): string[];
68
+ /**
69
+ * Do `columns` contain every column of at least one unique key on `table`?
70
+ *
71
+ * This is the fan-out test. A join whose ON columns cover a unique key of the
72
+ * joined table matches at most one row per base row; one that does not can
73
+ * match many.
74
+ */
75
+ declare function coversUniqueKey(table: DiscoveredSchemaTable, columns: string[]): boolean;
76
+ /** Is this table's payload rich enough to validate a projection against? */
77
+ declare function hasRichMeta(table: DiscoveredSchemaTable): boolean;
78
+ //#endregion
79
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/dist/index.mjs CHANGED
@@ -1,7 +1,73 @@
1
1
  import { authCustom } from "./auth-adapter.mjs";
2
- import { CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, MATERIALIZING_CONCEPTS, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig } from "./entity-config.mjs";
2
+ import { i as projectionSchema, n as PROJECTION_KEY_PATTERN, r as identityRefs, t as BASE_REF } from "./projection-C_EMRmZ8.mjs";
3
+ import { _ as hasRichMeta, a as MATERIALIZING_CONCEPTS, c as filterSchema, d as validateEntitiesConfig, f as validateProjection, g as coversUniqueKey, h as columnMetaFor, i as ClivlyConfigError, l as relationshipSchema, m as joinFansOut, n as CRM_CONCEPTS, o as defineClivlyConfig, p as fanOutJoins, s as entitiesConfigSchema, t as CANONICAL_FIELDS, u as relationshipsSchema, v as primaryKeyColumns } from "./entity-config-Tb5bbKLj.mjs";
3
4
  import { buildFilterFromDiscriminator, buildRelationships, relationshipViaSignature } from "./mapping-form.mjs";
4
5
  import { mappingsToEntitiesConfig } from "./mappings-config.mjs";
5
- import { DEFAULT_VIEW_PREFIX, compileEntityView, compileEntityViews, explainEntitiesConfig } from "./view-compiler.mjs";
6
+ import { i as explainEntitiesConfig, n as compileEntityView, r as compileEntityViews, t as DEFAULT_VIEW_PREFIX } from "./view-compiler-BKrBA_II.mjs";
6
7
  import { reconcile, runSync } from "./sync-engine.mjs";
7
- export { CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, mappingsToEntitiesConfig, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, validateEntitiesConfig };
8
+ //#region src/projection-draft.ts
9
+ function emptyProjectionDraft(baseTable) {
10
+ return {
11
+ baseTable,
12
+ entityKey: "",
13
+ fieldBindings: {},
14
+ identity: { kind: "base_pk" },
15
+ joins: []
16
+ };
17
+ }
18
+ /**
19
+ * The ON conditions implied by a foreign key between the two tables, so the
20
+ * builder can pre-fill a join instead of making the user hand-pick columns.
21
+ * Returns [] when no FK relates them — the user then picks manually.
22
+ */
23
+ function suggestJoinConditions(baseTable, joinTable, schema, joinKey) {
24
+ const join = schema.find((t) => t.name === joinTable);
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
+ }
39
+ /** A draft becomes a projection once it has a valid key and ≥1 binding. */
40
+ function draftToProjection(draft) {
41
+ if (!PROJECTION_KEY_PATTERN.test(draft.entityKey)) return null;
42
+ if (Object.keys(draft.fieldBindings).length === 0) return null;
43
+ return {
44
+ version: 1,
45
+ kind: "projection",
46
+ entityKey: draft.entityKey,
47
+ baseTable: draft.baseTable,
48
+ joins: draft.joins,
49
+ fieldBindings: draft.fieldBindings,
50
+ identity: draft.identity
51
+ };
52
+ }
53
+ function draftErrors(draft, schema) {
54
+ const projection = draftToProjection(draft);
55
+ if (!projection) return [{
56
+ path: "projection",
57
+ message: "Name the object and map a field."
58
+ }];
59
+ return validateProjection(projection, schema, "projection");
60
+ }
61
+ /** Per-join fan-out, for the builder's inline cardinality hints. */
62
+ function describeCardinality(draft, schema) {
63
+ const projection = draftToProjection(draft);
64
+ if (!projection) return [];
65
+ const tablesByName = new Map(schema.map((t) => [t.name, t]));
66
+ const fanning = new Set(fanOutJoins(projection, tablesByName).map((j) => j.key));
67
+ return projection.joins.map((join) => ({
68
+ joinKey: join.key,
69
+ fansOut: fanning.has(join.key)
70
+ }));
71
+ }
72
+ //#endregion
73
+ export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
@@ -0,0 +1,168 @@
1
+ import { ClivlyEntitiesConfig } from "./entity-config.mjs";
2
+ import { z } from "zod";
3
+ //#region src/projection.d.ts
4
+ /**
5
+ * Projection-backed custom mappings — one custom object derived from a base
6
+ * table plus explicit joins.
7
+ *
8
+ * A projection is deliberately narrow: equality joins only, direct column
9
+ * bindings only, no expressions and no aggregation. Everything a user can
10
+ * express here compiles to a single read-only view whose `id` column is
11
+ * provably unique per row, which is what lets the sync engine consume it
12
+ * unchanged.
13
+ *
14
+ * Refs address tables symbolically: `"base"` for the base table, or a join's
15
+ * `key`. Raw table names are never accepted in a ref, so a projection can
16
+ * never reach a table it did not declare.
17
+ */
18
+ /** The reserved ref name for the projection's base table. */
19
+ declare const BASE_REF = "base";
20
+ declare const projectionRefSchema: z.ZodObject<{
21
+ table: z.ZodString;
22
+ column: z.ZodString;
23
+ }, z.core.$strict>;
24
+ type ProjectionRef = z.infer<typeof projectionRefSchema>;
25
+ declare const projectionJoinSchema: z.ZodObject<{
26
+ key: z.ZodString;
27
+ table: z.ZodString;
28
+ type: z.ZodEnum<{
29
+ inner: "inner";
30
+ left: "left";
31
+ }>;
32
+ on: z.ZodArray<z.ZodObject<{
33
+ left: z.ZodObject<{
34
+ table: z.ZodString;
35
+ column: z.ZodString;
36
+ }, z.core.$strict>;
37
+ op: z.ZodLiteral<"eq">;
38
+ right: z.ZodObject<{
39
+ table: z.ZodString;
40
+ column: z.ZodString;
41
+ }, z.core.$strict>;
42
+ }, z.core.$strict>>;
43
+ }, z.core.$strict>;
44
+ type ProjectionJoin = z.infer<typeof projectionJoinSchema>;
45
+ declare const projectionFieldBindingSchema: z.ZodObject<{
46
+ from: z.ZodObject<{
47
+ table: z.ZodString;
48
+ column: z.ZodString;
49
+ }, z.core.$strict>;
50
+ }, z.core.$strict>;
51
+ type ProjectionFieldBinding = z.infer<typeof projectionFieldBindingSchema>;
52
+ declare const projectionIdentitySchema: z.ZodUnion<readonly [z.ZodObject<{
53
+ kind: z.ZodLiteral<"base_pk">;
54
+ column: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strict>, z.ZodObject<{
56
+ kind: z.ZodLiteral<"column">;
57
+ from: z.ZodObject<{
58
+ table: z.ZodString;
59
+ column: z.ZodString;
60
+ }, z.core.$strict>;
61
+ }, z.core.$strict>, z.ZodObject<{
62
+ kind: z.ZodLiteral<"composite">;
63
+ parts: z.ZodArray<z.ZodObject<{
64
+ table: z.ZodString;
65
+ column: z.ZodString;
66
+ }, z.core.$strict>>;
67
+ }, z.core.$strict>]>;
68
+ type ProjectionIdentity = z.infer<typeof projectionIdentitySchema>;
69
+ declare const projectionSchema: z.ZodObject<{
70
+ version: z.ZodLiteral<1>;
71
+ kind: z.ZodLiteral<"projection">;
72
+ entityKey: z.ZodString;
73
+ baseTable: z.ZodString;
74
+ joins: z.ZodArray<z.ZodObject<{
75
+ key: z.ZodString;
76
+ table: z.ZodString;
77
+ type: z.ZodEnum<{
78
+ inner: "inner";
79
+ left: "left";
80
+ }>;
81
+ on: z.ZodArray<z.ZodObject<{
82
+ left: z.ZodObject<{
83
+ table: z.ZodString;
84
+ column: z.ZodString;
85
+ }, z.core.$strict>;
86
+ op: z.ZodLiteral<"eq">;
87
+ right: z.ZodObject<{
88
+ table: z.ZodString;
89
+ column: z.ZodString;
90
+ }, z.core.$strict>;
91
+ }, z.core.$strict>>;
92
+ }, z.core.$strict>>;
93
+ fieldBindings: z.ZodRecord<z.ZodString, z.ZodObject<{
94
+ from: z.ZodObject<{
95
+ table: z.ZodString;
96
+ column: z.ZodString;
97
+ }, z.core.$strict>;
98
+ }, z.core.$strict>>;
99
+ identity: z.ZodUnion<readonly [z.ZodObject<{
100
+ kind: z.ZodLiteral<"base_pk">;
101
+ column: z.ZodOptional<z.ZodString>;
102
+ }, z.core.$strict>, z.ZodObject<{
103
+ kind: z.ZodLiteral<"column">;
104
+ from: z.ZodObject<{
105
+ table: z.ZodString;
106
+ column: z.ZodString;
107
+ }, z.core.$strict>;
108
+ }, z.core.$strict>, z.ZodObject<{
109
+ kind: z.ZodLiteral<"composite">;
110
+ parts: z.ZodArray<z.ZodObject<{
111
+ table: z.ZodString;
112
+ column: z.ZodString;
113
+ }, z.core.$strict>>;
114
+ }, z.core.$strict>]>;
115
+ }, z.core.$strict>;
116
+ type ProjectionMapping = z.infer<typeof projectionSchema>;
117
+ /**
118
+ * Every column ref an identity depends on. `basePkColumn` is the base table's
119
+ * discovered PK, used only when a `base_pk` identity omits an explicit column.
120
+ */
121
+ declare function identityRefs(identity: ProjectionIdentity, basePkColumn: string): ProjectionRef[];
122
+ //#endregion
123
+ //#region src/mappings-config.d.ts
124
+ /**
125
+ * Mappings → Entity Config bridge.
126
+ *
127
+ * `crm_entity_mappings` rows are the single source of truth for how a host
128
+ * schema maps onto Clivly's CRM concepts. This module compiles those rows into
129
+ * a `ClivlyEntitiesConfig` — the contract the view compiler and sync engine
130
+ * consume — so the persisted mapping and the runtime pipeline never diverge.
131
+ *
132
+ * The row model has no explicit entity-key column (it's keyed by
133
+ * `sourceTable + targetConcept + role`), but the config's `relationships`
134
+ * reference other entities *by key*. This builder therefore derives a stable
135
+ * key per row and resolves each relationship's `entity` reference through the
136
+ * same derivation — so a relationship stored as `entity: "<relatedTable>"`
137
+ * (the shape `mappings.suggest` produces) resolves to the right entity.
138
+ */
139
+ /**
140
+ * A persisted `crm_entity_mappings` row, narrowed to the columns the builder
141
+ * reads. Structurally compatible with the Drizzle row (jsonb columns typed
142
+ * loosely) so the API layer can pass `db.select()` results straight in.
143
+ */
144
+ interface EntityMappingRow {
145
+ fieldMap: Record<string, string>;
146
+ filter?: Record<string, unknown> | null;
147
+ /**
148
+ * Advanced mapping: a base table plus explicit joins. When set, this — not
149
+ * `fieldMap` — is the authored source of truth, and `sourceTable` merely
150
+ * mirrors `projection.baseTable`.
151
+ */
152
+ projection?: ProjectionMapping | null;
153
+ relationships?: Record<string, unknown> | null;
154
+ role?: string | null;
155
+ sourceTable: string;
156
+ status?: string | null;
157
+ targetConcept: string;
158
+ targetObjectTypeId?: string | null;
159
+ }
160
+ /**
161
+ * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
162
+ * rows with at least one mapped field are included; disabled/suggested rows and
163
+ * unmapped rows are skipped. Returns `{ entities: {} }` when nothing is
164
+ * eligible — a no-op the view compiler and sync engine both tolerate.
165
+ */
166
+ declare function mappingsToEntitiesConfig(rows: EntityMappingRow[]): ClivlyEntitiesConfig;
167
+ //#endregion
168
+ export { ProjectionIdentity as a, ProjectionRef as c, ProjectionFieldBinding as i, identityRefs as l, mappingsToEntitiesConfig as n, ProjectionJoin as o, BASE_REF as r, ProjectionMapping as s, EntityMappingRow as t, projectionSchema as u };
@@ -0,0 +1,168 @@
1
+ import { ClivlyEntitiesConfig } from "./entity-config.cjs";
2
+ import { z } from "zod";
3
+ //#region src/projection.d.ts
4
+ /**
5
+ * Projection-backed custom mappings — one custom object derived from a base
6
+ * table plus explicit joins.
7
+ *
8
+ * A projection is deliberately narrow: equality joins only, direct column
9
+ * bindings only, no expressions and no aggregation. Everything a user can
10
+ * express here compiles to a single read-only view whose `id` column is
11
+ * provably unique per row, which is what lets the sync engine consume it
12
+ * unchanged.
13
+ *
14
+ * Refs address tables symbolically: `"base"` for the base table, or a join's
15
+ * `key`. Raw table names are never accepted in a ref, so a projection can
16
+ * never reach a table it did not declare.
17
+ */
18
+ /** The reserved ref name for the projection's base table. */
19
+ declare const BASE_REF = "base";
20
+ declare const projectionRefSchema: z.ZodObject<{
21
+ table: z.ZodString;
22
+ column: z.ZodString;
23
+ }, z.core.$strict>;
24
+ type ProjectionRef = z.infer<typeof projectionRefSchema>;
25
+ declare const projectionJoinSchema: z.ZodObject<{
26
+ key: z.ZodString;
27
+ table: z.ZodString;
28
+ type: z.ZodEnum<{
29
+ inner: "inner";
30
+ left: "left";
31
+ }>;
32
+ on: z.ZodArray<z.ZodObject<{
33
+ left: z.ZodObject<{
34
+ table: z.ZodString;
35
+ column: z.ZodString;
36
+ }, z.core.$strict>;
37
+ op: z.ZodLiteral<"eq">;
38
+ right: z.ZodObject<{
39
+ table: z.ZodString;
40
+ column: z.ZodString;
41
+ }, z.core.$strict>;
42
+ }, z.core.$strict>>;
43
+ }, z.core.$strict>;
44
+ type ProjectionJoin = z.infer<typeof projectionJoinSchema>;
45
+ declare const projectionFieldBindingSchema: z.ZodObject<{
46
+ from: z.ZodObject<{
47
+ table: z.ZodString;
48
+ column: z.ZodString;
49
+ }, z.core.$strict>;
50
+ }, z.core.$strict>;
51
+ type ProjectionFieldBinding = z.infer<typeof projectionFieldBindingSchema>;
52
+ declare const projectionIdentitySchema: z.ZodUnion<readonly [z.ZodObject<{
53
+ kind: z.ZodLiteral<"base_pk">;
54
+ column: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strict>, z.ZodObject<{
56
+ kind: z.ZodLiteral<"column">;
57
+ from: z.ZodObject<{
58
+ table: z.ZodString;
59
+ column: z.ZodString;
60
+ }, z.core.$strict>;
61
+ }, z.core.$strict>, z.ZodObject<{
62
+ kind: z.ZodLiteral<"composite">;
63
+ parts: z.ZodArray<z.ZodObject<{
64
+ table: z.ZodString;
65
+ column: z.ZodString;
66
+ }, z.core.$strict>>;
67
+ }, z.core.$strict>]>;
68
+ type ProjectionIdentity = z.infer<typeof projectionIdentitySchema>;
69
+ declare const projectionSchema: z.ZodObject<{
70
+ version: z.ZodLiteral<1>;
71
+ kind: z.ZodLiteral<"projection">;
72
+ entityKey: z.ZodString;
73
+ baseTable: z.ZodString;
74
+ joins: z.ZodArray<z.ZodObject<{
75
+ key: z.ZodString;
76
+ table: z.ZodString;
77
+ type: z.ZodEnum<{
78
+ inner: "inner";
79
+ left: "left";
80
+ }>;
81
+ on: z.ZodArray<z.ZodObject<{
82
+ left: z.ZodObject<{
83
+ table: z.ZodString;
84
+ column: z.ZodString;
85
+ }, z.core.$strict>;
86
+ op: z.ZodLiteral<"eq">;
87
+ right: z.ZodObject<{
88
+ table: z.ZodString;
89
+ column: z.ZodString;
90
+ }, z.core.$strict>;
91
+ }, z.core.$strict>>;
92
+ }, z.core.$strict>>;
93
+ fieldBindings: z.ZodRecord<z.ZodString, z.ZodObject<{
94
+ from: z.ZodObject<{
95
+ table: z.ZodString;
96
+ column: z.ZodString;
97
+ }, z.core.$strict>;
98
+ }, z.core.$strict>>;
99
+ identity: z.ZodUnion<readonly [z.ZodObject<{
100
+ kind: z.ZodLiteral<"base_pk">;
101
+ column: z.ZodOptional<z.ZodString>;
102
+ }, z.core.$strict>, z.ZodObject<{
103
+ kind: z.ZodLiteral<"column">;
104
+ from: z.ZodObject<{
105
+ table: z.ZodString;
106
+ column: z.ZodString;
107
+ }, z.core.$strict>;
108
+ }, z.core.$strict>, z.ZodObject<{
109
+ kind: z.ZodLiteral<"composite">;
110
+ parts: z.ZodArray<z.ZodObject<{
111
+ table: z.ZodString;
112
+ column: z.ZodString;
113
+ }, z.core.$strict>>;
114
+ }, z.core.$strict>]>;
115
+ }, z.core.$strict>;
116
+ type ProjectionMapping = z.infer<typeof projectionSchema>;
117
+ /**
118
+ * Every column ref an identity depends on. `basePkColumn` is the base table's
119
+ * discovered PK, used only when a `base_pk` identity omits an explicit column.
120
+ */
121
+ declare function identityRefs(identity: ProjectionIdentity, basePkColumn: string): ProjectionRef[];
122
+ //#endregion
123
+ //#region src/mappings-config.d.ts
124
+ /**
125
+ * Mappings → Entity Config bridge.
126
+ *
127
+ * `crm_entity_mappings` rows are the single source of truth for how a host
128
+ * schema maps onto Clivly's CRM concepts. This module compiles those rows into
129
+ * a `ClivlyEntitiesConfig` — the contract the view compiler and sync engine
130
+ * consume — so the persisted mapping and the runtime pipeline never diverge.
131
+ *
132
+ * The row model has no explicit entity-key column (it's keyed by
133
+ * `sourceTable + targetConcept + role`), but the config's `relationships`
134
+ * reference other entities *by key*. This builder therefore derives a stable
135
+ * key per row and resolves each relationship's `entity` reference through the
136
+ * same derivation — so a relationship stored as `entity: "<relatedTable>"`
137
+ * (the shape `mappings.suggest` produces) resolves to the right entity.
138
+ */
139
+ /**
140
+ * A persisted `crm_entity_mappings` row, narrowed to the columns the builder
141
+ * reads. Structurally compatible with the Drizzle row (jsonb columns typed
142
+ * loosely) so the API layer can pass `db.select()` results straight in.
143
+ */
144
+ interface EntityMappingRow {
145
+ fieldMap: Record<string, string>;
146
+ filter?: Record<string, unknown> | null;
147
+ /**
148
+ * Advanced mapping: a base table plus explicit joins. When set, this — not
149
+ * `fieldMap` — is the authored source of truth, and `sourceTable` merely
150
+ * mirrors `projection.baseTable`.
151
+ */
152
+ projection?: ProjectionMapping | null;
153
+ relationships?: Record<string, unknown> | null;
154
+ role?: string | null;
155
+ sourceTable: string;
156
+ status?: string | null;
157
+ targetConcept: string;
158
+ targetObjectTypeId?: string | null;
159
+ }
160
+ /**
161
+ * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
162
+ * rows with at least one mapped field are included; disabled/suggested rows and
163
+ * unmapped rows are skipped. Returns `{ entities: {} }` when nothing is
164
+ * eligible — a no-op the view compiler and sync engine both tolerate.
165
+ */
166
+ declare function mappingsToEntitiesConfig(rows: EntityMappingRow[]): ClivlyEntitiesConfig;
167
+ //#endregion
168
+ export { ProjectionIdentity as a, ProjectionRef as c, ProjectionFieldBinding as i, identityRefs as l, mappingsToEntitiesConfig as n, ProjectionJoin as o, BASE_REF as r, ProjectionMapping as s, EntityMappingRow as t, projectionSchema as u };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_entity_config = require("./entity-config.cjs");
2
+ const require_entity_config = require("./entity-config-PoOGzvpN.cjs");
3
3
  //#region src/mappings-config.ts
4
4
  const RESERVED_FIELD = "id";
5
5
  /**
@@ -22,7 +22,9 @@ function canonicalFields(concept, fieldMap) {
22
22
  const DEFAULT_ROLE = "primary";
23
23
  const ACTIVE_STATUS = "active";
24
24
  function isEligible(row) {
25
- return (row.status ?? ACTIVE_STATUS) === ACTIVE_STATUS && Object.keys(row.fieldMap ?? {}).length > 0;
25
+ if ((row.status ?? ACTIVE_STATUS) !== ACTIVE_STATUS) return false;
26
+ if (row.projection) return Object.keys(row.projection.fieldBindings ?? {}).length > 0;
27
+ return Object.keys(row.fieldMap ?? {}).length > 0;
26
28
  }
27
29
  /**
28
30
  * Preferred key for a row: the bare source table when it backs a single entity,
@@ -46,13 +48,28 @@ function uniquify(base, used) {
46
48
  used.add(key);
47
49
  return key;
48
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
+ }
49
65
  function buildEntity(row, refIndex) {
50
66
  const entity = {
51
67
  concept: row.targetConcept,
52
- source: row.sourceTable,
68
+ source: row.projection?.baseTable ?? row.sourceTable,
53
69
  role: row.role ?? DEFAULT_ROLE,
54
- fields: canonicalFields(row.targetConcept, row.fieldMap)
70
+ fields: row.projection ? projectionFields(row.projection) : canonicalFields(row.targetConcept, row.fieldMap)
55
71
  };
72
+ if (row.projection) entity.projection = row.projection;
56
73
  if (row.targetConcept === "custom" && row.targetObjectTypeId) entity.targetObjectTypeId = row.targetObjectTypeId;
57
74
  if (row.filter && Object.keys(row.filter).length > 0) entity.filter = row.filter;
58
75
  if (row.relationships && Object.keys(row.relationships).length > 0) {
@@ -77,11 +94,16 @@ function buildEntity(row, refIndex) {
77
94
  function mappingsToEntitiesConfig(rows) {
78
95
  const eligible = rows.filter(isEligible);
79
96
  const tableCounts = /* @__PURE__ */ new Map();
80
- for (const row of eligible) tableCounts.set(row.sourceTable, (tableCounts.get(row.sourceTable) ?? 0) + 1);
97
+ for (const row of eligible) {
98
+ if (row.projection) continue;
99
+ tableCounts.set(row.sourceTable, (tableCounts.get(row.sourceTable) ?? 0) + 1);
100
+ }
81
101
  const used = /* @__PURE__ */ new Set();
82
102
  const tableAnchor = /* @__PURE__ */ new Map();
83
103
  const assigned = [];
84
- for (const row of eligible) {
104
+ const legacyRows = eligible.filter((row) => !row.projection);
105
+ const projectionRows = eligible.filter((row) => row.projection);
106
+ for (const row of legacyRows) {
85
107
  const key = uniquify(preferredKey(row, tableCounts), used);
86
108
  assigned.push({
87
109
  row,
@@ -89,6 +111,10 @@ function mappingsToEntitiesConfig(rows) {
89
111
  });
90
112
  if ((row.role ?? DEFAULT_ROLE) === DEFAULT_ROLE || !tableAnchor.has(row.sourceTable)) tableAnchor.set(row.sourceTable, key);
91
113
  }
114
+ for (const row of projectionRows) assigned.push({
115
+ row,
116
+ key: uniquify(row.projection?.entityKey ?? row.sourceTable, used)
117
+ });
92
118
  const refIndex = /* @__PURE__ */ new Map();
93
119
  for (const { key } of assigned) refIndex.set(key, key);
94
120
  for (const [table, key] of tableAnchor) if (!refIndex.has(table)) refIndex.set(table, key);
@@ -1,41 +1,2 @@
1
- import { ClivlyEntitiesConfig } from "./entity-config.cjs";
2
- //#region src/mappings-config.d.ts
3
- /**
4
- * Mappings → Entity Config bridge.
5
- *
6
- * `crm_entity_mappings` rows are the single source of truth for how a host
7
- * schema maps onto Clivly's CRM concepts. This module compiles those rows into
8
- * a `ClivlyEntitiesConfig` — the contract the view compiler and sync engine
9
- * consume — so the persisted mapping and the runtime pipeline never diverge.
10
- *
11
- * The row model has no explicit entity-key column (it's keyed by
12
- * `sourceTable + targetConcept + role`), but the config's `relationships`
13
- * reference other entities *by key*. This builder therefore derives a stable
14
- * key per row and resolves each relationship's `entity` reference through the
15
- * same derivation — so a relationship stored as `entity: "<relatedTable>"`
16
- * (the shape `mappings.suggest` produces) resolves to the right entity.
17
- */
18
- /**
19
- * A persisted `crm_entity_mappings` row, narrowed to the columns the builder
20
- * reads. Structurally compatible with the Drizzle row (jsonb columns typed
21
- * loosely) so the API layer can pass `db.select()` results straight in.
22
- */
23
- interface EntityMappingRow {
24
- fieldMap: Record<string, string>;
25
- filter?: Record<string, unknown> | null;
26
- relationships?: Record<string, unknown> | null;
27
- role?: string | null;
28
- sourceTable: string;
29
- status?: string | null;
30
- targetConcept: string;
31
- targetObjectTypeId?: string | null;
32
- }
33
- /**
34
- * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
35
- * rows with at least one mapped field are included; disabled/suggested rows and
36
- * unmapped rows are skipped. Returns `{ entities: {} }` when nothing is
37
- * eligible — a no-op the view compiler and sync engine both tolerate.
38
- */
39
- declare function mappingsToEntitiesConfig(rows: EntityMappingRow[]): ClivlyEntitiesConfig;
40
- //#endregion
1
+ import { n as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-BWo4cRDF.cjs";
41
2
  export { EntityMappingRow, mappingsToEntitiesConfig };
@@ -1,41 +1,2 @@
1
- import { ClivlyEntitiesConfig } from "./entity-config.mjs";
2
- //#region src/mappings-config.d.ts
3
- /**
4
- * Mappings → Entity Config bridge.
5
- *
6
- * `crm_entity_mappings` rows are the single source of truth for how a host
7
- * schema maps onto Clivly's CRM concepts. This module compiles those rows into
8
- * a `ClivlyEntitiesConfig` — the contract the view compiler and sync engine
9
- * consume — so the persisted mapping and the runtime pipeline never diverge.
10
- *
11
- * The row model has no explicit entity-key column (it's keyed by
12
- * `sourceTable + targetConcept + role`), but the config's `relationships`
13
- * reference other entities *by key*. This builder therefore derives a stable
14
- * key per row and resolves each relationship's `entity` reference through the
15
- * same derivation — so a relationship stored as `entity: "<relatedTable>"`
16
- * (the shape `mappings.suggest` produces) resolves to the right entity.
17
- */
18
- /**
19
- * A persisted `crm_entity_mappings` row, narrowed to the columns the builder
20
- * reads. Structurally compatible with the Drizzle row (jsonb columns typed
21
- * loosely) so the API layer can pass `db.select()` results straight in.
22
- */
23
- interface EntityMappingRow {
24
- fieldMap: Record<string, string>;
25
- filter?: Record<string, unknown> | null;
26
- relationships?: Record<string, unknown> | null;
27
- role?: string | null;
28
- sourceTable: string;
29
- status?: string | null;
30
- targetConcept: string;
31
- targetObjectTypeId?: string | null;
32
- }
33
- /**
34
- * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
35
- * rows with at least one mapped field are included; disabled/suggested rows and
36
- * unmapped rows are skipped. Returns `{ entities: {} }` when nothing is
37
- * eligible — a no-op the view compiler and sync engine both tolerate.
38
- */
39
- declare function mappingsToEntitiesConfig(rows: EntityMappingRow[]): ClivlyEntitiesConfig;
40
- //#endregion
1
+ import { n as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-B0W5bVZj.mjs";
41
2
  export { EntityMappingRow, mappingsToEntitiesConfig };