@clivly/core 0.9.6 → 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 +30 -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.cjs +9 -1
- package/dist/drizzle.d.cts +4 -2
- package/dist/drizzle.d.mts +4 -2
- package/dist/drizzle.mjs +9 -1
- package/dist/{entity-config-BAaZOIID.cjs → entity-config-C5jBmG2C.cjs} +115 -71
- 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-kusaRiDs.mjs → entity-config-DpHPMFIS.mjs} +104 -42
- 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.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.cjs";
|
|
1
|
+
import { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, OPEN_STAGE_TYPE, PipelineConfig, PipelineKind, PipelineStageConfig, RecordSource, StageType, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput, ValueInterval, isClosedStageType } from "./types.cjs";
|
|
2
2
|
import { CRMAdapter } from "./adapter.cjs";
|
|
3
3
|
import { ClivlyAuthAdapter, ClivlyUser, CrmMembership, MembershipResolver, authCustom } from "./auth-adapter.cjs";
|
|
4
|
-
import { A as BASE_REF, C as defineClivlyConfig, D as relationshipSchema, F as
|
|
4
|
+
import { A as BASE_REF, C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, F as ProjectionMapping, I as ProjectionRef, L as identityRefs, M as ProjectionFieldBinding, N as ProjectionIdentity, O as relationshipsSchema, P as ProjectionJoin, R as projectionSchema, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, d as CrmConcept, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, j as PROJECTION_KEY_PATTERN, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, p as DiscoveredColumnMeta, 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";
|
|
5
5
|
import { ConnectionLiveness, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, StepId, StepProblem, StepState } from "./integration-state.cjs";
|
|
6
6
|
import { RelationshipCandidate, buildFilterFromDiscriminator, buildRelationships, relationshipViaSignature } from "./mapping-form.cjs";
|
|
7
7
|
import { EntityMappingRow, mappingsToEntitiesConfig } from "./mappings-config.cjs";
|
|
@@ -12,90 +12,6 @@ declare function joinFansOut(join: ProjectionJoin, joinTable: DiscoveredSchemaTa
|
|
|
12
12
|
/** Every join in the projection that can multiply rows. */
|
|
13
13
|
declare function fanOutJoins(projection: ProjectionMapping, tablesByName: Map<string, DiscoveredSchemaTable>): ProjectionJoin[];
|
|
14
14
|
//#endregion
|
|
15
|
-
//#region src/projection-draft.d.ts
|
|
16
|
-
/**
|
|
17
|
-
* The builder's in-progress shape.
|
|
18
|
-
*
|
|
19
|
-
* A draft is deliberately looser than a `ProjectionMapping`: it exists while
|
|
20
|
-
* the user is still choosing things, so every field can be empty. All of the
|
|
21
|
-
* builder's rules live here as pure functions, which keeps the React components
|
|
22
|
-
* presentational and lets the rules be tested without a DOM.
|
|
23
|
-
*/
|
|
24
|
-
interface ProjectionDraft {
|
|
25
|
-
baseTable: string;
|
|
26
|
-
entityKey: string;
|
|
27
|
-
fieldBindings: Record<string, ProjectionFieldBinding>;
|
|
28
|
-
identity: ProjectionIdentity;
|
|
29
|
-
joins: ProjectionJoin[];
|
|
30
|
-
}
|
|
31
|
-
declare function emptyProjectionDraft(baseTable: string): ProjectionDraft;
|
|
32
|
-
/**
|
|
33
|
-
* The ON conditions implied by a foreign key between the two tables, so the
|
|
34
|
-
* builder can pre-fill a join instead of making the user hand-pick columns.
|
|
35
|
-
* Returns [] when no FK relates them — the user then picks manually.
|
|
36
|
-
*/
|
|
37
|
-
declare function suggestJoinConditions(baseTable: string, joinTable: string, schema: DiscoveredSchemaTable[], joinKey: string): ProjectionJoin["on"];
|
|
38
|
-
/**
|
|
39
|
-
* Add a join on `joinTable`, keyed and pre-filled.
|
|
40
|
-
*
|
|
41
|
-
* The key is the table's own name where that is free, because it is what the
|
|
42
|
-
* user already calls the table; a collision falls back to `_2`, `_3` and so on.
|
|
43
|
-
* Uniqueness is not cosmetic — the key is the SQL alias and the name every ref
|
|
44
|
-
* addresses the table by, so two joins sharing one would make every ref
|
|
45
|
-
* ambiguous. `base` is reserved for the base table and can never be taken.
|
|
46
|
-
*/
|
|
47
|
-
declare function addDraftJoin(draft: ProjectionDraft, joinTable: string, schema: DiscoveredSchemaTable[]): ProjectionDraft;
|
|
48
|
-
/**
|
|
49
|
-
* Remove a join, and everything that addressed it.
|
|
50
|
-
*
|
|
51
|
-
* Refs to a removed join are refs to a table the projection no longer declares.
|
|
52
|
-
* Left in place they fail validation as "unknown ref table", which blames the
|
|
53
|
-
* field — or, worse, the identity — for the join's removal. Identity falls back
|
|
54
|
-
* to the base primary key, the same default a new draft starts from.
|
|
55
|
-
*/
|
|
56
|
-
declare function removeDraftJoin(draft: ProjectionDraft, joinKey: string): ProjectionDraft;
|
|
57
|
-
/** One selectable table in the builder's column pickers. */
|
|
58
|
-
interface DraftRefOption {
|
|
59
|
-
columns: string[];
|
|
60
|
-
label: string;
|
|
61
|
-
ref: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Every table a field or identity may currently read from, in ref order: the
|
|
65
|
-
* base table first, then each join.
|
|
66
|
-
*
|
|
67
|
-
* Returns [] when the base table is not in the discovered schema — there is
|
|
68
|
-
* nothing truthful to offer, and inventing columns would let someone build a
|
|
69
|
-
* projection that cannot compile.
|
|
70
|
-
*/
|
|
71
|
-
declare function draftRefOptions(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): DraftRefOption[];
|
|
72
|
-
/** A draft becomes a projection once it has a valid key and ≥1 binding. */
|
|
73
|
-
declare function draftToProjection(draft: ProjectionDraft): ProjectionMapping | null;
|
|
74
|
-
/**
|
|
75
|
-
* Every reason this draft cannot be saved, one per cause and each anchored to
|
|
76
|
-
* the section that owns it.
|
|
77
|
-
*
|
|
78
|
-
* One lumped "Name the object and map a field." was all this used to return for
|
|
79
|
-
* an incomplete draft, at the bare path "projection". Two consequences, both
|
|
80
|
-
* bad: a builder had nowhere to attach the message, and — because it returned
|
|
81
|
-
* before validating — a fanning join under a `base_pk` identity stayed silent
|
|
82
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
83
|
-
* the user chose the join.
|
|
84
|
-
*/
|
|
85
|
-
declare function draftErrors(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): ConfigValidationError[];
|
|
86
|
-
/**
|
|
87
|
-
* Per-join fan-out, for the builder's inline cardinality hints.
|
|
88
|
-
*
|
|
89
|
-
* Answered from the joins alone. Joins are chosen before the key and the fields
|
|
90
|
-
* are filled in, and a fan-out warning is precisely what should steer the
|
|
91
|
-
* identity choice while it is being made — requiring a complete draft put the
|
|
92
|
-
* hint after the decision it exists to inform.
|
|
93
|
-
*/
|
|
94
|
-
declare function describeCardinality(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): Array<{
|
|
95
|
-
fansOut: boolean;
|
|
96
|
-
joinKey: string;
|
|
97
|
-
}>;
|
|
98
|
-
//#endregion
|
|
99
15
|
//#region src/projection-sql.d.ts
|
|
100
16
|
/**
|
|
101
17
|
* SQL generation for projection-backed entities.
|
|
@@ -153,6 +69,27 @@ declare const PREVIEW_MAX_ROWS = 50;
|
|
|
153
69
|
declare function compileProjectionPreview(projection: ProjectionMapping, limit: number): string;
|
|
154
70
|
//#endregion
|
|
155
71
|
//#region src/projection-validate.d.ts
|
|
72
|
+
/**
|
|
73
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
74
|
+
* it can only name columns of the base table.
|
|
75
|
+
*
|
|
76
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
77
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
78
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
79
|
+
*
|
|
80
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
81
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
82
|
+
* syncs UNFILTERED.
|
|
83
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
84
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
85
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
86
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
87
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
88
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
89
|
+
*
|
|
90
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
91
|
+
*/
|
|
92
|
+
declare function validateProjectionFilter(filter: FilterExpr | undefined, projection: ProjectionMapping, schema: DiscoveredSchemaTable[], base: string): ConfigValidationError[];
|
|
156
93
|
/**
|
|
157
94
|
* Validate a projection against the discovered schema. Returns every error
|
|
158
95
|
* found (not just the first) so a builder UI can show them all at once.
|
|
@@ -182,7 +119,116 @@ declare function primaryKeyColumns(table: DiscoveredSchemaTable): string[];
|
|
|
182
119
|
* match many.
|
|
183
120
|
*/
|
|
184
121
|
declare function coversUniqueKey(table: DiscoveredSchemaTable, columns: string[]): boolean;
|
|
185
|
-
/**
|
|
186
|
-
|
|
122
|
+
/**
|
|
123
|
+
* A discovered table whose enrichment metadata is known to be present.
|
|
124
|
+
*
|
|
125
|
+
* This narrows only the *presence* of the two optional fields. It says nothing
|
|
126
|
+
* about their contents — an empty `uniqueConstraints` is a known-empty fact
|
|
127
|
+
* (the table genuinely has no unique keys), which is exactly the distinction
|
|
128
|
+
* `hasRichMeta` has always drawn and must keep drawing.
|
|
129
|
+
*/
|
|
130
|
+
type RichMetaTable = DiscoveredSchemaTable & {
|
|
131
|
+
columnsMeta: DiscoveredColumnMeta[];
|
|
132
|
+
uniqueConstraints: string[][];
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
136
|
+
*
|
|
137
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
138
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
139
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
140
|
+
* rather than a silent walk over `undefined`.
|
|
141
|
+
*
|
|
142
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
143
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
144
|
+
*/
|
|
145
|
+
declare function hasRichMeta(table: DiscoveredSchemaTable): table is RichMetaTable;
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/slug.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* The one `slugify`.
|
|
150
|
+
*
|
|
151
|
+
* Three byte-identical copies existed before this module — in `@clivly/api`
|
|
152
|
+
* (the custom object-type slug), in `apps/web`'s onboarding route (the
|
|
153
|
+
* organisation slug), and in `@clivly/sdk`. The first two now import this; the
|
|
154
|
+
* SDK's copy stays where it is on purpose, because `@clivly/sdk` cannot depend
|
|
155
|
+
* on this package and its own comment records that its pattern is deliberately
|
|
156
|
+
* kept in step with the server's by hand.
|
|
157
|
+
*
|
|
158
|
+
* Note that the two callers this replaces slug *different things* — an
|
|
159
|
+
* object type and an organisation. They share a transform, not a namespace.
|
|
160
|
+
* Sharing the function stops the transform drifting; it does not make those two
|
|
161
|
+
* identifiers the same identifier.
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* Lowercase, trim, collapse every run of non-alphanumerics to one hyphen, and
|
|
165
|
+
* drop leading/trailing hyphens. `"Participant Info"` becomes
|
|
166
|
+
* `"participant-info"`.
|
|
167
|
+
*
|
|
168
|
+
* The result is NOT guaranteed to be an acceptable slug. Both
|
|
169
|
+
* `OBJECT_TYPE_SLUG_PATTERN` (server) and `PROJECTION_KEY_PATTERN` (this
|
|
170
|
+
* package) require a leading letter, and this function happily returns
|
|
171
|
+
* `"2024-invoices"` for `"2024 Invoices"`. Callers that need an
|
|
172
|
+
* acceptance-guaranteed identifier want `toObjectTypeSlug`.
|
|
173
|
+
*/
|
|
174
|
+
declare function slugify(value: string): string;
|
|
175
|
+
/**
|
|
176
|
+
* A slug the server will accept, derived from a display name.
|
|
177
|
+
*
|
|
178
|
+
* `slugify` alone is not enough: the server's `OBJECT_TYPE_SLUG_PATTERN`
|
|
179
|
+
* (`apps/server/src/index.ts`) demands a leading letter, so a name beginning
|
|
180
|
+
* with a digit slugs to something the platform rejects at push time — long
|
|
181
|
+
* after the object was authored. `@clivly/sdk`'s `custom-slug.ts` already knows
|
|
182
|
+
* this case and deliberately withholds `"2024-invoices"` as a suggestion
|
|
183
|
+
* because it would be rejected too.
|
|
184
|
+
*
|
|
185
|
+
* The prefix is applied ONLY when the derived slug does not already start with
|
|
186
|
+
* a letter, so the overwhelmingly common name is untouched.
|
|
187
|
+
*
|
|
188
|
+
* This value must be shown to whoever authored the name. It is the identifier
|
|
189
|
+
* the host app declares as its `objectType`, so a slug derived silently is a
|
|
190
|
+
* slug the developer cannot copy into their config.
|
|
191
|
+
*
|
|
192
|
+
* Returns an empty string for a name with no alphanumerics at all — the caller
|
|
193
|
+
* decides whether that is "keep typing" or an error, since only it knows
|
|
194
|
+
* whether the field is still being edited.
|
|
195
|
+
*/
|
|
196
|
+
declare function toObjectTypeSlug(displayName: string): string;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/stage-rules.d.ts
|
|
199
|
+
type ConditionOp = "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "before" | "after";
|
|
200
|
+
interface Condition {
|
|
201
|
+
field: string;
|
|
202
|
+
op: ConditionOp;
|
|
203
|
+
value?: string;
|
|
204
|
+
}
|
|
205
|
+
interface EvaluableRule {
|
|
206
|
+
conditions: Condition[];
|
|
207
|
+
id: string;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Whether one condition holds for a host row.
|
|
211
|
+
*
|
|
212
|
+
* `mappedFields` is the set of columns the organization actually exposed. A
|
|
213
|
+
* condition naming anything outside it never matches, whatever the operator: a
|
|
214
|
+
* rule referencing a dropped column must fail safe, because treating the field
|
|
215
|
+
* as null would make `is_empty` match every deal.
|
|
216
|
+
*/
|
|
217
|
+
declare function matchesCondition(condition: Condition, fields: Record<string, string | null>, mappedFields: Set<string>): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* The id of the first rule whose conditions all hold, or null when none do.
|
|
220
|
+
*
|
|
221
|
+
* Returns a rule id rather than a stage key because the caller needs the
|
|
222
|
+
* matched rule's `allowClose` before it may move a deal into a closed stage.
|
|
223
|
+
*
|
|
224
|
+
* `rules` must arrive already ordered — stage position first, then rule
|
|
225
|
+
* position within a stage. Sorting needs stage rows this module deliberately
|
|
226
|
+
* does not see.
|
|
227
|
+
*
|
|
228
|
+
* A rule with no conditions never matches: an empty list would otherwise
|
|
229
|
+
* vacuously AND to true and capture every deal, which is the opposite of what
|
|
230
|
+
* a half-written rule should do.
|
|
231
|
+
*/
|
|
232
|
+
declare function evaluateStage(rules: EvaluableRule[], fields: Record<string, string | null>, mappedFields: Set<string>): string | null;
|
|
187
233
|
//#endregion
|
|
188
|
-
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 ConnectionLiveness, 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
|
|
234
|
+
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 Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, 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 EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, 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/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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";
|
|
1
|
+
import { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, OPEN_STAGE_TYPE, PipelineConfig, PipelineKind, PipelineStageConfig, RecordSource, StageType, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput, ValueInterval, isClosedStageType } from "./types.mjs";
|
|
2
2
|
import { CRMAdapter } from "./adapter.mjs";
|
|
3
3
|
import { ClivlyAuthAdapter, ClivlyUser, CrmMembership, MembershipResolver, authCustom } from "./auth-adapter.mjs";
|
|
4
|
-
import { A as BASE_REF, C as defineClivlyConfig, D as relationshipSchema, F as
|
|
4
|
+
import { A as BASE_REF, C as defineClivlyConfig, D as relationshipSchema, E as projectionFields, F as ProjectionMapping, I as ProjectionRef, L as identityRefs, M as ProjectionFieldBinding, N as ProjectionIdentity, O as relationshipsSchema, P as ProjectionJoin, R as projectionSchema, T as filterSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipVia, d as CrmConcept, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, j as PROJECTION_KEY_PATTERN, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, n as CRM_CONCEPTS, p as DiscoveredColumnMeta, 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";
|
|
5
5
|
import { ConnectionLiveness, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, StepId, StepProblem, StepState } from "./integration-state.mjs";
|
|
6
6
|
import { RelationshipCandidate, buildFilterFromDiscriminator, buildRelationships, relationshipViaSignature } from "./mapping-form.mjs";
|
|
7
7
|
import { EntityMappingRow, mappingsToEntitiesConfig } from "./mappings-config.mjs";
|
|
@@ -12,90 +12,6 @@ declare function joinFansOut(join: ProjectionJoin, joinTable: DiscoveredSchemaTa
|
|
|
12
12
|
/** Every join in the projection that can multiply rows. */
|
|
13
13
|
declare function fanOutJoins(projection: ProjectionMapping, tablesByName: Map<string, DiscoveredSchemaTable>): ProjectionJoin[];
|
|
14
14
|
//#endregion
|
|
15
|
-
//#region src/projection-draft.d.ts
|
|
16
|
-
/**
|
|
17
|
-
* The builder's in-progress shape.
|
|
18
|
-
*
|
|
19
|
-
* A draft is deliberately looser than a `ProjectionMapping`: it exists while
|
|
20
|
-
* the user is still choosing things, so every field can be empty. All of the
|
|
21
|
-
* builder's rules live here as pure functions, which keeps the React components
|
|
22
|
-
* presentational and lets the rules be tested without a DOM.
|
|
23
|
-
*/
|
|
24
|
-
interface ProjectionDraft {
|
|
25
|
-
baseTable: string;
|
|
26
|
-
entityKey: string;
|
|
27
|
-
fieldBindings: Record<string, ProjectionFieldBinding>;
|
|
28
|
-
identity: ProjectionIdentity;
|
|
29
|
-
joins: ProjectionJoin[];
|
|
30
|
-
}
|
|
31
|
-
declare function emptyProjectionDraft(baseTable: string): ProjectionDraft;
|
|
32
|
-
/**
|
|
33
|
-
* The ON conditions implied by a foreign key between the two tables, so the
|
|
34
|
-
* builder can pre-fill a join instead of making the user hand-pick columns.
|
|
35
|
-
* Returns [] when no FK relates them — the user then picks manually.
|
|
36
|
-
*/
|
|
37
|
-
declare function suggestJoinConditions(baseTable: string, joinTable: string, schema: DiscoveredSchemaTable[], joinKey: string): ProjectionJoin["on"];
|
|
38
|
-
/**
|
|
39
|
-
* Add a join on `joinTable`, keyed and pre-filled.
|
|
40
|
-
*
|
|
41
|
-
* The key is the table's own name where that is free, because it is what the
|
|
42
|
-
* user already calls the table; a collision falls back to `_2`, `_3` and so on.
|
|
43
|
-
* Uniqueness is not cosmetic — the key is the SQL alias and the name every ref
|
|
44
|
-
* addresses the table by, so two joins sharing one would make every ref
|
|
45
|
-
* ambiguous. `base` is reserved for the base table and can never be taken.
|
|
46
|
-
*/
|
|
47
|
-
declare function addDraftJoin(draft: ProjectionDraft, joinTable: string, schema: DiscoveredSchemaTable[]): ProjectionDraft;
|
|
48
|
-
/**
|
|
49
|
-
* Remove a join, and everything that addressed it.
|
|
50
|
-
*
|
|
51
|
-
* Refs to a removed join are refs to a table the projection no longer declares.
|
|
52
|
-
* Left in place they fail validation as "unknown ref table", which blames the
|
|
53
|
-
* field — or, worse, the identity — for the join's removal. Identity falls back
|
|
54
|
-
* to the base primary key, the same default a new draft starts from.
|
|
55
|
-
*/
|
|
56
|
-
declare function removeDraftJoin(draft: ProjectionDraft, joinKey: string): ProjectionDraft;
|
|
57
|
-
/** One selectable table in the builder's column pickers. */
|
|
58
|
-
interface DraftRefOption {
|
|
59
|
-
columns: string[];
|
|
60
|
-
label: string;
|
|
61
|
-
ref: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Every table a field or identity may currently read from, in ref order: the
|
|
65
|
-
* base table first, then each join.
|
|
66
|
-
*
|
|
67
|
-
* Returns [] when the base table is not in the discovered schema — there is
|
|
68
|
-
* nothing truthful to offer, and inventing columns would let someone build a
|
|
69
|
-
* projection that cannot compile.
|
|
70
|
-
*/
|
|
71
|
-
declare function draftRefOptions(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): DraftRefOption[];
|
|
72
|
-
/** A draft becomes a projection once it has a valid key and ≥1 binding. */
|
|
73
|
-
declare function draftToProjection(draft: ProjectionDraft): ProjectionMapping | null;
|
|
74
|
-
/**
|
|
75
|
-
* Every reason this draft cannot be saved, one per cause and each anchored to
|
|
76
|
-
* the section that owns it.
|
|
77
|
-
*
|
|
78
|
-
* One lumped "Name the object and map a field." was all this used to return for
|
|
79
|
-
* an incomplete draft, at the bare path "projection". Two consequences, both
|
|
80
|
-
* bad: a builder had nowhere to attach the message, and — because it returned
|
|
81
|
-
* before validating — a fanning join under a `base_pk` identity stayed silent
|
|
82
|
-
* until the draft was otherwise complete, which is well after the point where
|
|
83
|
-
* the user chose the join.
|
|
84
|
-
*/
|
|
85
|
-
declare function draftErrors(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): ConfigValidationError[];
|
|
86
|
-
/**
|
|
87
|
-
* Per-join fan-out, for the builder's inline cardinality hints.
|
|
88
|
-
*
|
|
89
|
-
* Answered from the joins alone. Joins are chosen before the key and the fields
|
|
90
|
-
* are filled in, and a fan-out warning is precisely what should steer the
|
|
91
|
-
* identity choice while it is being made — requiring a complete draft put the
|
|
92
|
-
* hint after the decision it exists to inform.
|
|
93
|
-
*/
|
|
94
|
-
declare function describeCardinality(draft: ProjectionDraft, schema: DiscoveredSchemaTable[]): Array<{
|
|
95
|
-
fansOut: boolean;
|
|
96
|
-
joinKey: string;
|
|
97
|
-
}>;
|
|
98
|
-
//#endregion
|
|
99
15
|
//#region src/projection-sql.d.ts
|
|
100
16
|
/**
|
|
101
17
|
* SQL generation for projection-backed entities.
|
|
@@ -153,6 +69,27 @@ declare const PREVIEW_MAX_ROWS = 50;
|
|
|
153
69
|
declare function compileProjectionPreview(projection: ProjectionMapping, limit: number): string;
|
|
154
70
|
//#endregion
|
|
155
71
|
//#region src/projection-validate.d.ts
|
|
72
|
+
/**
|
|
73
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
74
|
+
* it can only name columns of the base table.
|
|
75
|
+
*
|
|
76
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
77
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
78
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
79
|
+
*
|
|
80
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
81
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
82
|
+
* syncs UNFILTERED.
|
|
83
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
84
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
85
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
86
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
87
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
88
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
89
|
+
*
|
|
90
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
91
|
+
*/
|
|
92
|
+
declare function validateProjectionFilter(filter: FilterExpr | undefined, projection: ProjectionMapping, schema: DiscoveredSchemaTable[], base: string): ConfigValidationError[];
|
|
156
93
|
/**
|
|
157
94
|
* Validate a projection against the discovered schema. Returns every error
|
|
158
95
|
* found (not just the first) so a builder UI can show them all at once.
|
|
@@ -182,7 +119,116 @@ declare function primaryKeyColumns(table: DiscoveredSchemaTable): string[];
|
|
|
182
119
|
* match many.
|
|
183
120
|
*/
|
|
184
121
|
declare function coversUniqueKey(table: DiscoveredSchemaTable, columns: string[]): boolean;
|
|
185
|
-
/**
|
|
186
|
-
|
|
122
|
+
/**
|
|
123
|
+
* A discovered table whose enrichment metadata is known to be present.
|
|
124
|
+
*
|
|
125
|
+
* This narrows only the *presence* of the two optional fields. It says nothing
|
|
126
|
+
* about their contents — an empty `uniqueConstraints` is a known-empty fact
|
|
127
|
+
* (the table genuinely has no unique keys), which is exactly the distinction
|
|
128
|
+
* `hasRichMeta` has always drawn and must keep drawing.
|
|
129
|
+
*/
|
|
130
|
+
type RichMetaTable = DiscoveredSchemaTable & {
|
|
131
|
+
columnsMeta: DiscoveredColumnMeta[];
|
|
132
|
+
uniqueConstraints: string[][];
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
136
|
+
*
|
|
137
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
138
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
139
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
140
|
+
* rather than a silent walk over `undefined`.
|
|
141
|
+
*
|
|
142
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
143
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
144
|
+
*/
|
|
145
|
+
declare function hasRichMeta(table: DiscoveredSchemaTable): table is RichMetaTable;
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/slug.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* The one `slugify`.
|
|
150
|
+
*
|
|
151
|
+
* Three byte-identical copies existed before this module — in `@clivly/api`
|
|
152
|
+
* (the custom object-type slug), in `apps/web`'s onboarding route (the
|
|
153
|
+
* organisation slug), and in `@clivly/sdk`. The first two now import this; the
|
|
154
|
+
* SDK's copy stays where it is on purpose, because `@clivly/sdk` cannot depend
|
|
155
|
+
* on this package and its own comment records that its pattern is deliberately
|
|
156
|
+
* kept in step with the server's by hand.
|
|
157
|
+
*
|
|
158
|
+
* Note that the two callers this replaces slug *different things* — an
|
|
159
|
+
* object type and an organisation. They share a transform, not a namespace.
|
|
160
|
+
* Sharing the function stops the transform drifting; it does not make those two
|
|
161
|
+
* identifiers the same identifier.
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* Lowercase, trim, collapse every run of non-alphanumerics to one hyphen, and
|
|
165
|
+
* drop leading/trailing hyphens. `"Participant Info"` becomes
|
|
166
|
+
* `"participant-info"`.
|
|
167
|
+
*
|
|
168
|
+
* The result is NOT guaranteed to be an acceptable slug. Both
|
|
169
|
+
* `OBJECT_TYPE_SLUG_PATTERN` (server) and `PROJECTION_KEY_PATTERN` (this
|
|
170
|
+
* package) require a leading letter, and this function happily returns
|
|
171
|
+
* `"2024-invoices"` for `"2024 Invoices"`. Callers that need an
|
|
172
|
+
* acceptance-guaranteed identifier want `toObjectTypeSlug`.
|
|
173
|
+
*/
|
|
174
|
+
declare function slugify(value: string): string;
|
|
175
|
+
/**
|
|
176
|
+
* A slug the server will accept, derived from a display name.
|
|
177
|
+
*
|
|
178
|
+
* `slugify` alone is not enough: the server's `OBJECT_TYPE_SLUG_PATTERN`
|
|
179
|
+
* (`apps/server/src/index.ts`) demands a leading letter, so a name beginning
|
|
180
|
+
* with a digit slugs to something the platform rejects at push time — long
|
|
181
|
+
* after the object was authored. `@clivly/sdk`'s `custom-slug.ts` already knows
|
|
182
|
+
* this case and deliberately withholds `"2024-invoices"` as a suggestion
|
|
183
|
+
* because it would be rejected too.
|
|
184
|
+
*
|
|
185
|
+
* The prefix is applied ONLY when the derived slug does not already start with
|
|
186
|
+
* a letter, so the overwhelmingly common name is untouched.
|
|
187
|
+
*
|
|
188
|
+
* This value must be shown to whoever authored the name. It is the identifier
|
|
189
|
+
* the host app declares as its `objectType`, so a slug derived silently is a
|
|
190
|
+
* slug the developer cannot copy into their config.
|
|
191
|
+
*
|
|
192
|
+
* Returns an empty string for a name with no alphanumerics at all — the caller
|
|
193
|
+
* decides whether that is "keep typing" or an error, since only it knows
|
|
194
|
+
* whether the field is still being edited.
|
|
195
|
+
*/
|
|
196
|
+
declare function toObjectTypeSlug(displayName: string): string;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/stage-rules.d.ts
|
|
199
|
+
type ConditionOp = "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "before" | "after";
|
|
200
|
+
interface Condition {
|
|
201
|
+
field: string;
|
|
202
|
+
op: ConditionOp;
|
|
203
|
+
value?: string;
|
|
204
|
+
}
|
|
205
|
+
interface EvaluableRule {
|
|
206
|
+
conditions: Condition[];
|
|
207
|
+
id: string;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Whether one condition holds for a host row.
|
|
211
|
+
*
|
|
212
|
+
* `mappedFields` is the set of columns the organization actually exposed. A
|
|
213
|
+
* condition naming anything outside it never matches, whatever the operator: a
|
|
214
|
+
* rule referencing a dropped column must fail safe, because treating the field
|
|
215
|
+
* as null would make `is_empty` match every deal.
|
|
216
|
+
*/
|
|
217
|
+
declare function matchesCondition(condition: Condition, fields: Record<string, string | null>, mappedFields: Set<string>): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* The id of the first rule whose conditions all hold, or null when none do.
|
|
220
|
+
*
|
|
221
|
+
* Returns a rule id rather than a stage key because the caller needs the
|
|
222
|
+
* matched rule's `allowClose` before it may move a deal into a closed stage.
|
|
223
|
+
*
|
|
224
|
+
* `rules` must arrive already ordered — stage position first, then rule
|
|
225
|
+
* position within a stage. Sorting needs stage rows this module deliberately
|
|
226
|
+
* does not see.
|
|
227
|
+
*
|
|
228
|
+
* A rule with no conditions never matches: an empty list would otherwise
|
|
229
|
+
* vacuously AND to true and capture every deal, which is the opposite of what
|
|
230
|
+
* a half-written rule should do.
|
|
231
|
+
*/
|
|
232
|
+
declare function evaluateStage(rules: EvaluableRule[], fields: Record<string, string | null>, mappedFields: Set<string>): string | null;
|
|
187
233
|
//#endregion
|
|
188
|
-
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 ConnectionLiveness, 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
|
|
234
|
+
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 Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, 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 EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, 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 };
|