@clivly/core 0.11.0 → 0.12.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/adapter.d.cts +19 -1
- package/dist/adapter.d.mts +19 -1
- package/dist/index.cjs +29 -0
- package/dist/index.d.cts +37 -2
- package/dist/index.d.mts +37 -2
- package/dist/index.mjs +29 -1
- package/dist/types.d.cts +36 -0
- package/dist/types.d.mts +36 -0
- package/package.json +1 -1
package/dist/adapter.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Activity, Company, CompanyFilters, Contact, ContactFilters, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, Deal, RecordSource, Task, TaskFilters, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput } from "./types.cjs";
|
|
1
|
+
import { Activity, Company, CompanyFilters, Contact, ContactFilters, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, Deal, Page, PageRequest, RecordSource, Task, TaskFilters, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput } from "./types.cjs";
|
|
2
2
|
//#region src/adapter.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The contract every ORM implementation fulfils. `packages/drizzle` is the first
|
|
@@ -58,6 +58,24 @@ export interface CRMAdapter {
|
|
|
58
58
|
source?: RecordSource;
|
|
59
59
|
}): Promise<Deal[]>;
|
|
60
60
|
getTasks(orgId: string, filters?: TaskFilters): Promise<Task[]>;
|
|
61
|
+
/**
|
|
62
|
+
* One page of companies, newest first.
|
|
63
|
+
*
|
|
64
|
+
* Separate from `getCompanies` rather than a `limit` on `CompanyFilters`,
|
|
65
|
+
* and that is the whole point: an optional field on a shared filter type is
|
|
66
|
+
* silently inheritable, and the callers that must never inherit it are the
|
|
67
|
+
* ones where truncation corrupts data rather than merely hiding rows.
|
|
68
|
+
* `importContacts` builds its dedup set from the unpaged read — a truncated
|
|
69
|
+
* set resurrects a contact archived as a duplicate. A distinct method makes
|
|
70
|
+
* "this call is paginated" a decision at every call site.
|
|
71
|
+
*/
|
|
72
|
+
listCompaniesPage(orgId: string, filters?: CompanyFilters, page?: PageRequest): Promise<Page<Company>>;
|
|
73
|
+
/** One page of contacts, newest first. See {@link CRMAdapter.listCompaniesPage}. */
|
|
74
|
+
listContactsPage(orgId: string, filters?: ContactFilters, page?: PageRequest): Promise<Page<Contact>>;
|
|
75
|
+
/** One page of deals, newest first. See {@link CRMAdapter.listCompaniesPage}. */
|
|
76
|
+
listDealsPage(orgId: string, filters?: {
|
|
77
|
+
source?: RecordSource;
|
|
78
|
+
}, page?: PageRequest): Promise<Page<Deal>>;
|
|
61
79
|
/**
|
|
62
80
|
* Move a deal to `stage` and reorder it directly after `afterId` (null = top
|
|
63
81
|
* of the column). The adapter computes the fractional `position` from the
|
package/dist/adapter.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Activity, Company, CompanyFilters, Contact, ContactFilters, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, Deal, RecordSource, Task, TaskFilters, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput } from "./types.mjs";
|
|
1
|
+
import { Activity, Company, CompanyFilters, Contact, ContactFilters, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, Deal, Page, PageRequest, RecordSource, Task, TaskFilters, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput } from "./types.mjs";
|
|
2
2
|
//#region src/adapter.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The contract every ORM implementation fulfils. `packages/drizzle` is the first
|
|
@@ -58,6 +58,24 @@ export interface CRMAdapter {
|
|
|
58
58
|
source?: RecordSource;
|
|
59
59
|
}): Promise<Deal[]>;
|
|
60
60
|
getTasks(orgId: string, filters?: TaskFilters): Promise<Task[]>;
|
|
61
|
+
/**
|
|
62
|
+
* One page of companies, newest first.
|
|
63
|
+
*
|
|
64
|
+
* Separate from `getCompanies` rather than a `limit` on `CompanyFilters`,
|
|
65
|
+
* and that is the whole point: an optional field on a shared filter type is
|
|
66
|
+
* silently inheritable, and the callers that must never inherit it are the
|
|
67
|
+
* ones where truncation corrupts data rather than merely hiding rows.
|
|
68
|
+
* `importContacts` builds its dedup set from the unpaged read — a truncated
|
|
69
|
+
* set resurrects a contact archived as a duplicate. A distinct method makes
|
|
70
|
+
* "this call is paginated" a decision at every call site.
|
|
71
|
+
*/
|
|
72
|
+
listCompaniesPage(orgId: string, filters?: CompanyFilters, page?: PageRequest): Promise<Page<Company>>;
|
|
73
|
+
/** One page of contacts, newest first. See {@link CRMAdapter.listCompaniesPage}. */
|
|
74
|
+
listContactsPage(orgId: string, filters?: ContactFilters, page?: PageRequest): Promise<Page<Contact>>;
|
|
75
|
+
/** One page of deals, newest first. See {@link CRMAdapter.listCompaniesPage}. */
|
|
76
|
+
listDealsPage(orgId: string, filters?: {
|
|
77
|
+
source?: RecordSource;
|
|
78
|
+
}, page?: PageRequest): Promise<Page<Deal>>;
|
|
61
79
|
/**
|
|
62
80
|
* Move a deal to `stage` and reorder it directly after `afterId` (null = top
|
|
63
81
|
* of the column). The adapter computes the fractional `position` from the
|
package/dist/index.cjs
CHANGED
|
@@ -8,6 +8,34 @@ const require_mappings_config = require("./mappings-config.cjs");
|
|
|
8
8
|
const require_view_compiler = require("./view-compiler-DP4Fbj7-.cjs");
|
|
9
9
|
const require_sync_engine = require("./sync-engine.cjs");
|
|
10
10
|
const require_types = require("./types.cjs");
|
|
11
|
+
//#region src/deal-value.ts
|
|
12
|
+
const MONTHS_PER_YEAR = 12;
|
|
13
|
+
/** Only two decimal-free seat counts make sense; anything else is bad data. */
|
|
14
|
+
const SEAT_COUNT_REGEX = /^\d+$/;
|
|
15
|
+
/**
|
|
16
|
+
* The value one deal should have, in cents, or null to leave it alone.
|
|
17
|
+
*
|
|
18
|
+
* Returning null rather than 0 for unusable input is deliberate (D33). A
|
|
19
|
+
* renamed column would otherwise zero an entire pipeline silently, and a zeroed
|
|
20
|
+
* board reads as a business problem rather than a configuration problem. The
|
|
21
|
+
* unmapped case is surfaced through the rule-health warning surface instead of
|
|
22
|
+
* being written into the data — the same fail-safe `matchesCondition` applies.
|
|
23
|
+
*/
|
|
24
|
+
function computeDealValue(source, fields, mappedFields, stageType) {
|
|
25
|
+
if (source.zeroInStageTypes?.includes(stageType)) return 0;
|
|
26
|
+
if (!(mappedFields.has(source.seatField) && mappedFields.has(source.planField))) return null;
|
|
27
|
+
const planKey = fields[source.planField];
|
|
28
|
+
if (planKey == null || planKey === "") return null;
|
|
29
|
+
const unitPriceCents = source.plans[planKey];
|
|
30
|
+
if (unitPriceCents === void 0) return null;
|
|
31
|
+
const rawSeats = fields[source.seatField];
|
|
32
|
+
if (rawSeats == null || !SEAT_COUNT_REGEX.test(rawSeats)) return null;
|
|
33
|
+
const seats = Number(rawSeats);
|
|
34
|
+
if (!Number.isSafeInteger(seats)) return null;
|
|
35
|
+
const monthly = (source.baseFeeCents ?? 0) + seats * unitPriceCents;
|
|
36
|
+
return source.intervalField !== void 0 && mappedFields.has(source.intervalField) && fields[source.intervalField] === "year" ? Math.round(monthly / MONTHS_PER_YEAR) : monthly;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
11
39
|
//#region src/host-owned-fields.ts
|
|
12
40
|
/**
|
|
13
41
|
* Which fields a CRM record lets a user edit.
|
|
@@ -250,6 +278,7 @@ exports.compileProjectionIdentity = require_view_compiler.compileProjectionIdent
|
|
|
250
278
|
exports.compileProjectionJoins = require_view_compiler.compileProjectionJoins;
|
|
251
279
|
exports.compileProjectionPreview = require_view_compiler.compileProjectionPreview;
|
|
252
280
|
exports.compileProjectionSelect = require_view_compiler.compileProjectionSelect;
|
|
281
|
+
exports.computeDealValue = computeDealValue;
|
|
253
282
|
exports.coversUniqueKey = require_schema_meta.coversUniqueKey;
|
|
254
283
|
exports.defineClivlyConfig = require_entity_config.defineClivlyConfig;
|
|
255
284
|
exports.entitiesConfigSchema = require_entity_config.entitiesConfigSchema;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Activity, ActivityType, Company, CompanyFilters, 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, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput, ValueInterval, isClosedStageType } from "./types.cjs";
|
|
1
|
+
import { Activity, ActivityType, Company, CompanyFilters, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, OPEN_STAGE_TYPE, Page, PageRequest, PipelineConfig, PipelineKind, PipelineStageConfig, RecordSource, StageType, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateCompanyInput, 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
4
|
import { A as relationshipsSchema, B as identityRefs, D as missingRequiredFields, E as filterSchema, F as ProjectionFieldBinding, I as ProjectionIdentity, L as ProjectionJoin, M as validateEntitiesConfig, N as BASE_REF, O as projectionFields, P as PROJECTION_KEY_PATTERN, R as ProjectionMapping, S as RelationshipsMap, T as entitiesConfigSchema, V as projectionSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipSpec, d as CrmConcept, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, j as requiredFieldsFor, k as relationshipSchema, 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 defineClivlyConfig, x as RelationshipVia, y as REQUIRED_FIELDS, z as ProjectionRef } from "./entity-config-OhC4Otkz.cjs";
|
|
@@ -7,6 +7,41 @@ import { RelationshipCandidate, buildFilterFromDiscriminator, buildRelationships
|
|
|
7
7
|
import { EntityMappingRow, mappingsToEntitiesConfig } from "./mappings-config.cjs";
|
|
8
8
|
import { CompileOptions, CompiledView, DEFAULT_VIEW_PREFIX, ExplainResult, compileEntityView, compileEntityViews, explainEntitiesConfig } from "./view-compiler.cjs";
|
|
9
9
|
import { EntitySyncResult, MirrorRecord, PersistAction, SourceRow, SyncAction, SyncResult, SyncStore, reconcile, runSync } from "./sync-engine.cjs";
|
|
10
|
+
//#region src/deal-value.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* Value derived as a flat fee plus a per-seat price that depends on the plan.
|
|
13
|
+
*
|
|
14
|
+
* Every field name is configuration and every plan key is data, so a second
|
|
15
|
+
* subscription business configures this without a code change. The formula is
|
|
16
|
+
* fixed with named slots rather than an expression language — the same choice
|
|
17
|
+
* D8 made for stage-rule conditions.
|
|
18
|
+
*/
|
|
19
|
+
interface PerSeatValueSource {
|
|
20
|
+
/** Flat monthly fee in cents, added before the interval is normalised. */
|
|
21
|
+
baseFeeCents?: number;
|
|
22
|
+
/** Mapped column holding "month" or "year". Absent means always monthly. */
|
|
23
|
+
intervalField?: string;
|
|
24
|
+
kind: "per_seat";
|
|
25
|
+
/** Mapped column holding the plan key. */
|
|
26
|
+
planField: string;
|
|
27
|
+
/** Unit price in cents, keyed by the value found in `planField`. */
|
|
28
|
+
plans: Record<string, number>;
|
|
29
|
+
/** Mapped column holding the seat count. */
|
|
30
|
+
seatField: string;
|
|
31
|
+
/** Stage types whose deals are worth nothing (usually `["lost"]`). */
|
|
32
|
+
zeroInStageTypes?: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The value one deal should have, in cents, or null to leave it alone.
|
|
36
|
+
*
|
|
37
|
+
* Returning null rather than 0 for unusable input is deliberate (D33). A
|
|
38
|
+
* renamed column would otherwise zero an entire pipeline silently, and a zeroed
|
|
39
|
+
* board reads as a business problem rather than a configuration problem. The
|
|
40
|
+
* unmapped case is surfaced through the rule-health warning surface instead of
|
|
41
|
+
* being written into the data — the same fail-safe `matchesCondition` applies.
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeDealValue(source: PerSeatValueSource, fields: Record<string, string | null>, mappedFields: Set<string>, stageType: string): number | null;
|
|
44
|
+
//#endregion
|
|
10
45
|
//#region src/host-owned-fields.d.ts
|
|
11
46
|
/**
|
|
12
47
|
* Which fields a CRM record lets a user edit.
|
|
@@ -298,4 +333,4 @@ export declare function matchesCondition(condition: Condition, fields: Record<st
|
|
|
298
333
|
*/
|
|
299
334
|
export declare function evaluateStage(rules: EvaluableRule[], fields: Record<string, string | null>, mappedFields: Set<string>): string | null;
|
|
300
335
|
//#endregion
|
|
301
|
-
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 CompanyFilters, 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 HostOwnedCompanyField, type HostOwnedContactField, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, REQUIRED_FIELDS, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SourcedRecord, 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 UpdateCompanyInput, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, identityRefs, isClosedStageType, mappingsToEntitiesConfig, missingRequiredFields, projectionFields, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, validateEntitiesConfig };
|
|
336
|
+
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 CompanyFilters, 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 HostOwnedCompanyField, type HostOwnedContactField, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PROJECTION_KEY_PATTERN, type Page, type PageRequest, type PerSeatValueSource, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, REQUIRED_FIELDS, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SourcedRecord, 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 UpdateCompanyInput, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, identityRefs, isClosedStageType, mappingsToEntitiesConfig, missingRequiredFields, projectionFields, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, validateEntitiesConfig };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Activity, ActivityType, Company, CompanyFilters, 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, UpdateCompanyInput, UpdateContactInput, UpdateDealInput, UpdateTaskInput, ValueInterval, isClosedStageType } from "./types.mjs";
|
|
1
|
+
import { Activity, ActivityType, Company, CompanyFilters, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, OPEN_STAGE_TYPE, Page, PageRequest, PipelineConfig, PipelineKind, PipelineStageConfig, RecordSource, StageType, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateCompanyInput, 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
4
|
import { A as relationshipsSchema, B as identityRefs, D as missingRequiredFields, E as filterSchema, F as ProjectionFieldBinding, I as ProjectionIdentity, L as ProjectionJoin, M as validateEntitiesConfig, N as BASE_REF, O as projectionFields, P as PROJECTION_KEY_PATTERN, R as ProjectionMapping, S as RelationshipsMap, T as entitiesConfigSchema, V as projectionSchema, _ as MATERIALIZING_CONCEPTS, a as ClivlyEntitiesConfig, b as RelationshipSpec, d as CrmConcept, g as FilterValue, h as FilterExpr, i as ClivlyConfigError, j as requiredFieldsFor, k as relationshipSchema, 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 defineClivlyConfig, x as RelationshipVia, y as REQUIRED_FIELDS, z as ProjectionRef } from "./entity-config-OhC4Otkz.mjs";
|
|
@@ -7,6 +7,41 @@ import { RelationshipCandidate, buildFilterFromDiscriminator, buildRelationships
|
|
|
7
7
|
import { EntityMappingRow, mappingsToEntitiesConfig } from "./mappings-config.mjs";
|
|
8
8
|
import { CompileOptions, CompiledView, DEFAULT_VIEW_PREFIX, ExplainResult, compileEntityView, compileEntityViews, explainEntitiesConfig } from "./view-compiler.mjs";
|
|
9
9
|
import { EntitySyncResult, MirrorRecord, PersistAction, SourceRow, SyncAction, SyncResult, SyncStore, reconcile, runSync } from "./sync-engine.mjs";
|
|
10
|
+
//#region src/deal-value.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* Value derived as a flat fee plus a per-seat price that depends on the plan.
|
|
13
|
+
*
|
|
14
|
+
* Every field name is configuration and every plan key is data, so a second
|
|
15
|
+
* subscription business configures this without a code change. The formula is
|
|
16
|
+
* fixed with named slots rather than an expression language — the same choice
|
|
17
|
+
* D8 made for stage-rule conditions.
|
|
18
|
+
*/
|
|
19
|
+
interface PerSeatValueSource {
|
|
20
|
+
/** Flat monthly fee in cents, added before the interval is normalised. */
|
|
21
|
+
baseFeeCents?: number;
|
|
22
|
+
/** Mapped column holding "month" or "year". Absent means always monthly. */
|
|
23
|
+
intervalField?: string;
|
|
24
|
+
kind: "per_seat";
|
|
25
|
+
/** Mapped column holding the plan key. */
|
|
26
|
+
planField: string;
|
|
27
|
+
/** Unit price in cents, keyed by the value found in `planField`. */
|
|
28
|
+
plans: Record<string, number>;
|
|
29
|
+
/** Mapped column holding the seat count. */
|
|
30
|
+
seatField: string;
|
|
31
|
+
/** Stage types whose deals are worth nothing (usually `["lost"]`). */
|
|
32
|
+
zeroInStageTypes?: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The value one deal should have, in cents, or null to leave it alone.
|
|
36
|
+
*
|
|
37
|
+
* Returning null rather than 0 for unusable input is deliberate (D33). A
|
|
38
|
+
* renamed column would otherwise zero an entire pipeline silently, and a zeroed
|
|
39
|
+
* board reads as a business problem rather than a configuration problem. The
|
|
40
|
+
* unmapped case is surfaced through the rule-health warning surface instead of
|
|
41
|
+
* being written into the data — the same fail-safe `matchesCondition` applies.
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeDealValue(source: PerSeatValueSource, fields: Record<string, string | null>, mappedFields: Set<string>, stageType: string): number | null;
|
|
44
|
+
//#endregion
|
|
10
45
|
//#region src/host-owned-fields.d.ts
|
|
11
46
|
/**
|
|
12
47
|
* Which fields a CRM record lets a user edit.
|
|
@@ -298,4 +333,4 @@ export declare function matchesCondition(condition: Condition, fields: Record<st
|
|
|
298
333
|
*/
|
|
299
334
|
export declare function evaluateStage(rules: EvaluableRule[], fields: Record<string, string | null>, mappedFields: Set<string>): string | null;
|
|
300
335
|
//#endregion
|
|
301
|
-
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 CompanyFilters, 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 HostOwnedCompanyField, type HostOwnedContactField, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, REQUIRED_FIELDS, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SourcedRecord, 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 UpdateCompanyInput, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, identityRefs, isClosedStageType, mappingsToEntitiesConfig, missingRequiredFields, projectionFields, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, validateEntitiesConfig };
|
|
336
|
+
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 CompanyFilters, 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 HostOwnedCompanyField, type HostOwnedContactField, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PROJECTION_KEY_PATTERN, type Page, type PageRequest, type PerSeatValueSource, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, REQUIRED_FIELDS, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SourcedRecord, 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 UpdateCompanyInput, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, compileEntityView, compileEntityViews, defineClivlyConfig, entitiesConfigSchema, explainEntitiesConfig, filterSchema, identityRefs, isClosedStageType, mappingsToEntitiesConfig, missingRequiredFields, projectionFields, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, validateEntitiesConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,34 @@ import { mappingsToEntitiesConfig } from "./mappings-config.mjs";
|
|
|
7
7
|
import { c as compileProjectionIdentity, d as compileProjectionSelect, f as quoteIdent, i as explainEntitiesConfig, l as compileProjectionJoins, n as compileEntityView, o as PREVIEW_MAX_ROWS, r as compileEntityViews, s as compileProjectionFrom, t as DEFAULT_VIEW_PREFIX, u as compileProjectionPreview } from "./view-compiler-CLvZ0wJW.mjs";
|
|
8
8
|
import { reconcile, runSync } from "./sync-engine.mjs";
|
|
9
9
|
import { OPEN_STAGE_TYPE, isClosedStageType } from "./types.mjs";
|
|
10
|
+
//#region src/deal-value.ts
|
|
11
|
+
const MONTHS_PER_YEAR = 12;
|
|
12
|
+
/** Only two decimal-free seat counts make sense; anything else is bad data. */
|
|
13
|
+
const SEAT_COUNT_REGEX = /^\d+$/;
|
|
14
|
+
/**
|
|
15
|
+
* The value one deal should have, in cents, or null to leave it alone.
|
|
16
|
+
*
|
|
17
|
+
* Returning null rather than 0 for unusable input is deliberate (D33). A
|
|
18
|
+
* renamed column would otherwise zero an entire pipeline silently, and a zeroed
|
|
19
|
+
* board reads as a business problem rather than a configuration problem. The
|
|
20
|
+
* unmapped case is surfaced through the rule-health warning surface instead of
|
|
21
|
+
* being written into the data — the same fail-safe `matchesCondition` applies.
|
|
22
|
+
*/
|
|
23
|
+
function computeDealValue(source, fields, mappedFields, stageType) {
|
|
24
|
+
if (source.zeroInStageTypes?.includes(stageType)) return 0;
|
|
25
|
+
if (!(mappedFields.has(source.seatField) && mappedFields.has(source.planField))) return null;
|
|
26
|
+
const planKey = fields[source.planField];
|
|
27
|
+
if (planKey == null || planKey === "") return null;
|
|
28
|
+
const unitPriceCents = source.plans[planKey];
|
|
29
|
+
if (unitPriceCents === void 0) return null;
|
|
30
|
+
const rawSeats = fields[source.seatField];
|
|
31
|
+
if (rawSeats == null || !SEAT_COUNT_REGEX.test(rawSeats)) return null;
|
|
32
|
+
const seats = Number(rawSeats);
|
|
33
|
+
if (!Number.isSafeInteger(seats)) return null;
|
|
34
|
+
const monthly = (source.baseFeeCents ?? 0) + seats * unitPriceCents;
|
|
35
|
+
return source.intervalField !== void 0 && mappedFields.has(source.intervalField) && fields[source.intervalField] === "year" ? Math.round(monthly / MONTHS_PER_YEAR) : monthly;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
10
38
|
//#region src/host-owned-fields.ts
|
|
11
39
|
/**
|
|
12
40
|
* Which fields a CRM record lets a user edit.
|
|
@@ -228,4 +256,4 @@ function evaluateStage(rules, fields, mappedFields) {
|
|
|
228
256
|
return null;
|
|
229
257
|
}
|
|
230
258
|
//#endregion
|
|
231
|
-
export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, REQUIRED_FIELDS, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, hostOwnedFieldMessage, identityRefs, isClosedStageType, isHostOwned, joinFansOut, mappingsToEntitiesConfig, matchesCondition, missingRequiredFields, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, rejectedCompanyFields, rejectedContactFields, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
|
|
259
|
+
export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, REQUIRED_FIELDS, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, computeDealValue, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, hostOwnedFieldMessage, identityRefs, isClosedStageType, isHostOwned, joinFansOut, mappingsToEntitiesConfig, matchesCondition, missingRequiredFields, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, rejectedCompanyFields, rejectedContactFields, relationshipSchema, relationshipViaSignature, relationshipsSchema, requiredFieldsFor, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
|
package/dist/types.d.cts
CHANGED
|
@@ -92,7 +92,21 @@ export interface Deal {
|
|
|
92
92
|
owner: string | null;
|
|
93
93
|
position: number | null;
|
|
94
94
|
stage: DealStage;
|
|
95
|
+
/**
|
|
96
|
+
* When a human last moved this deal by hand. Non-null means stage derivation
|
|
97
|
+
* no longer touches it, so a board must be able to mark it as overridden and
|
|
98
|
+
* offer a way back — otherwise an overridden deal is indistinguishable from
|
|
99
|
+
* one the rules simply never matched.
|
|
100
|
+
*/
|
|
101
|
+
stageOverrideAt: Date | null;
|
|
95
102
|
value: number;
|
|
103
|
+
/**
|
|
104
|
+
* When a human last set this deal's value by hand. Non-null means value
|
|
105
|
+
* derivation no longer touches it. Mirrors {@link Deal.stageOverrideAt} — and
|
|
106
|
+
* needs the same visibility, since an honoured-but-invisible override reads
|
|
107
|
+
* as a stuck deal rather than a pinned one.
|
|
108
|
+
*/
|
|
109
|
+
valueOverrideAt: Date | null;
|
|
96
110
|
}
|
|
97
111
|
export interface Task {
|
|
98
112
|
archivedAt: Date | null;
|
|
@@ -234,6 +248,28 @@ export interface ContactFilters {
|
|
|
234
248
|
source?: RecordSource;
|
|
235
249
|
status?: ContactStatus;
|
|
236
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* One page of a keyset-paginated read.
|
|
253
|
+
*
|
|
254
|
+
* Keyset, not offset: `LIMIT/OFFSET` re-scans every skipped row, so page 200
|
|
255
|
+
* costs 200 pages of work, and a row inserted mid-listing shifts everything
|
|
256
|
+
* after it — the reader sees a record twice or never. A cursor anchored to
|
|
257
|
+
* `(createdAt, id)` is stable under concurrent writes and costs the same at
|
|
258
|
+
* page 1 and page 200.
|
|
259
|
+
*
|
|
260
|
+
* `audit.list` already does this; these types generalise its shape so the CRM
|
|
261
|
+
* lists cannot drift into a second convention.
|
|
262
|
+
*/
|
|
263
|
+
export interface PageRequest {
|
|
264
|
+
/** Opaque `${createdAtISO}|${id}`. Rows strictly older than this. */
|
|
265
|
+
cursor?: string;
|
|
266
|
+
limit?: number;
|
|
267
|
+
}
|
|
268
|
+
export interface Page<T> {
|
|
269
|
+
/** Null when this is the last page. */
|
|
270
|
+
nextCursor: string | null;
|
|
271
|
+
rows: T[];
|
|
272
|
+
}
|
|
237
273
|
export interface CompanyFilters {
|
|
238
274
|
/** See {@link ContactFilters.excludeArchived}. */
|
|
239
275
|
excludeArchived?: boolean;
|
package/dist/types.d.mts
CHANGED
|
@@ -92,7 +92,21 @@ export interface Deal {
|
|
|
92
92
|
owner: string | null;
|
|
93
93
|
position: number | null;
|
|
94
94
|
stage: DealStage;
|
|
95
|
+
/**
|
|
96
|
+
* When a human last moved this deal by hand. Non-null means stage derivation
|
|
97
|
+
* no longer touches it, so a board must be able to mark it as overridden and
|
|
98
|
+
* offer a way back — otherwise an overridden deal is indistinguishable from
|
|
99
|
+
* one the rules simply never matched.
|
|
100
|
+
*/
|
|
101
|
+
stageOverrideAt: Date | null;
|
|
95
102
|
value: number;
|
|
103
|
+
/**
|
|
104
|
+
* When a human last set this deal's value by hand. Non-null means value
|
|
105
|
+
* derivation no longer touches it. Mirrors {@link Deal.stageOverrideAt} — and
|
|
106
|
+
* needs the same visibility, since an honoured-but-invisible override reads
|
|
107
|
+
* as a stuck deal rather than a pinned one.
|
|
108
|
+
*/
|
|
109
|
+
valueOverrideAt: Date | null;
|
|
96
110
|
}
|
|
97
111
|
export interface Task {
|
|
98
112
|
archivedAt: Date | null;
|
|
@@ -234,6 +248,28 @@ export interface ContactFilters {
|
|
|
234
248
|
source?: RecordSource;
|
|
235
249
|
status?: ContactStatus;
|
|
236
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* One page of a keyset-paginated read.
|
|
253
|
+
*
|
|
254
|
+
* Keyset, not offset: `LIMIT/OFFSET` re-scans every skipped row, so page 200
|
|
255
|
+
* costs 200 pages of work, and a row inserted mid-listing shifts everything
|
|
256
|
+
* after it — the reader sees a record twice or never. A cursor anchored to
|
|
257
|
+
* `(createdAt, id)` is stable under concurrent writes and costs the same at
|
|
258
|
+
* page 1 and page 200.
|
|
259
|
+
*
|
|
260
|
+
* `audit.list` already does this; these types generalise its shape so the CRM
|
|
261
|
+
* lists cannot drift into a second convention.
|
|
262
|
+
*/
|
|
263
|
+
export interface PageRequest {
|
|
264
|
+
/** Opaque `${createdAtISO}|${id}`. Rows strictly older than this. */
|
|
265
|
+
cursor?: string;
|
|
266
|
+
limit?: number;
|
|
267
|
+
}
|
|
268
|
+
export interface Page<T> {
|
|
269
|
+
/** Null when this is the last page. */
|
|
270
|
+
nextCursor: string | null;
|
|
271
|
+
rows: T[];
|
|
272
|
+
}
|
|
237
273
|
export interface CompanyFilters {
|
|
238
274
|
/** See {@link ContactFilters.excludeArchived}. */
|
|
239
275
|
excludeArchived?: boolean;
|