@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.
Files changed (50) hide show
  1. package/README.md +30 -10
  2. package/dist/builder.cjs +599 -0
  3. package/dist/builder.d.cts +336 -0
  4. package/dist/builder.d.mts +336 -0
  5. package/dist/builder.mjs +589 -0
  6. package/dist/drizzle.cjs +9 -1
  7. package/dist/drizzle.d.cts +4 -2
  8. package/dist/drizzle.d.mts +4 -2
  9. package/dist/drizzle.mjs +9 -1
  10. package/dist/{entity-config-BAaZOIID.cjs → entity-config-C5jBmG2C.cjs} +115 -71
  11. package/dist/{entity-config-buOIVtMM.d.cts → entity-config-DCa785OE.d.cts} +27 -1
  12. package/dist/{entity-config-buOIVtMM.d.mts → entity-config-DCa785OE.d.mts} +27 -1
  13. package/dist/{entity-config-kusaRiDs.mjs → entity-config-DpHPMFIS.mjs} +104 -42
  14. package/dist/entity-config.cjs +1 -1
  15. package/dist/entity-config.d.cts +1 -1
  16. package/dist/entity-config.d.mts +1 -1
  17. package/dist/entity-config.mjs +1 -1
  18. package/dist/entity-heuristics.cjs +34 -1
  19. package/dist/entity-heuristics.d.cts +8 -2
  20. package/dist/entity-heuristics.d.mts +8 -2
  21. package/dist/entity-heuristics.mjs +34 -2
  22. package/dist/index.cjs +136 -199
  23. package/dist/index.d.cts +135 -89
  24. package/dist/index.d.mts +135 -89
  25. package/dist/index.mjs +124 -188
  26. package/dist/mapping-form.d.cts +1 -1
  27. package/dist/mapping-form.d.mts +1 -1
  28. package/dist/mappings-config.cjs +2 -16
  29. package/dist/mappings-config.d.cts +1 -1
  30. package/dist/mappings-config.d.mts +1 -1
  31. package/dist/mappings-config.mjs +1 -15
  32. package/dist/{projection-OJYCYmS_.cjs → projection-DFgCoyZl.cjs} +0 -6
  33. package/dist/{projection-BXw_iiw5.mjs → projection-SCNrLJMV.mjs} +1 -1
  34. package/dist/schema-meta-Ac7938mT.mjs +46 -0
  35. package/dist/schema-meta-Cgi0MnHh.cjs +69 -0
  36. package/dist/sync-engine.cjs +1 -1
  37. package/dist/sync-engine.d.cts +1 -1
  38. package/dist/sync-engine.d.mts +1 -1
  39. package/dist/sync-engine.mjs +1 -1
  40. package/dist/types.cjs +11 -0
  41. package/dist/types.d.cts +28 -2
  42. package/dist/types.d.mts +28 -2
  43. package/dist/types.mjs +9 -1
  44. package/dist/{view-compiler-u3GGRgnv.mjs → view-compiler-CLvZ0wJW.mjs} +1 -1
  45. package/dist/{view-compiler-D30HNRnA.cjs → view-compiler-DP4Fbj7-.cjs} +1 -1
  46. package/dist/view-compiler.cjs +1 -1
  47. package/dist/view-compiler.d.cts +1 -1
  48. package/dist/view-compiler.d.mts +1 -1
  49. package/dist/view-compiler.mjs +1 -1
  50. 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 ProjectionRef, I as identityRefs, L as projectionSchema, M as ProjectionIdentity, N as ProjectionJoin, O as relationshipsSchema, P as ProjectionMapping, 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 ProjectionFieldBinding, 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-buOIVtMM.cjs";
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
- /** Is this table's payload rich enough to validate a projection against? */
186
- declare function hasRichMeta(table: DiscoveredSchemaTable): boolean;
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 DraftRefOption, type EntityMappingRow, type EntitySyncResult, 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, PREVIEW_MAX_ROWS, 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 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, addDraftJoin, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftRefOptions, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, removeDraftJoin, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
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 ProjectionRef, I as identityRefs, L as projectionSchema, M as ProjectionIdentity, N as ProjectionJoin, O as relationshipsSchema, P as ProjectionMapping, 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 ProjectionFieldBinding, 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-buOIVtMM.mjs";
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
- /** Is this table's payload rich enough to validate a projection against? */
186
- declare function hasRichMeta(table: DiscoveredSchemaTable): boolean;
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 DraftRefOption, type EntityMappingRow, type EntitySyncResult, 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, PREVIEW_MAX_ROWS, 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 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, addDraftJoin, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftRefOptions, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, removeDraftJoin, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
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 };