clivly 0.2.0 → 0.2.2

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 (117) hide show
  1. package/README.md +54 -3
  2. package/adapter-Cd6D_3JD.d.mts +46 -0
  3. package/adapter-Cy4q2CLs.d.cts +46 -0
  4. package/auth-adapter-CBOKDhlc.cjs +11 -0
  5. package/auth-adapter-CRBW4elj.d.mts +30 -0
  6. package/auth-adapter-CdLODn2p.d.cts +30 -0
  7. package/auth-adapter-wqnnEjo3.mjs +6 -0
  8. package/auth-better-auth.cjs +28 -0
  9. package/auth-better-auth.d.cts +33 -0
  10. package/auth-better-auth.d.mts +33 -0
  11. package/auth-better-auth.mjs +26 -0
  12. package/auth-clerk.cjs +34 -0
  13. package/auth-clerk.d.cts +32 -0
  14. package/auth-clerk.d.mts +32 -0
  15. package/auth-clerk.mjs +32 -0
  16. package/auth-workos.cjs +47 -0
  17. package/auth-workos.d.cts +30 -0
  18. package/auth-workos.d.mts +30 -0
  19. package/auth-workos.mjs +45 -0
  20. package/core-adapter.cjs +0 -0
  21. package/core-adapter.d.cts +2 -0
  22. package/core-adapter.d.mts +2 -0
  23. package/core-adapter.mjs +1 -0
  24. package/core-auth-adapter.cjs +3 -0
  25. package/core-auth-adapter.d.cts +2 -0
  26. package/core-auth-adapter.d.mts +2 -0
  27. package/core-auth-adapter.mjs +2 -0
  28. package/core-drizzle.cjs +32 -0
  29. package/core-drizzle.d.cts +21 -0
  30. package/core-drizzle.d.mts +21 -0
  31. package/core-drizzle.mjs +31 -0
  32. package/core-entity-config.cjs +13 -0
  33. package/core-entity-config.d.cts +2 -0
  34. package/core-entity-config.d.mts +2 -0
  35. package/core-entity-config.mjs +2 -0
  36. package/core-entity-heuristics.cjs +442 -0
  37. package/core-entity-heuristics.d.cts +68 -0
  38. package/core-entity-heuristics.d.mts +68 -0
  39. package/core-entity-heuristics.mjs +431 -0
  40. package/core-mapping-form.cjs +5 -0
  41. package/core-mapping-form.d.cts +2 -0
  42. package/core-mapping-form.d.mts +2 -0
  43. package/core-mapping-form.mjs +2 -0
  44. package/core-mapping-score.cjs +12 -0
  45. package/core-mapping-score.d.cts +2 -0
  46. package/core-mapping-score.d.mts +2 -0
  47. package/core-mapping-score.mjs +2 -0
  48. package/core-mappings-config.cjs +3 -0
  49. package/core-mappings-config.d.cts +2 -0
  50. package/core-mappings-config.d.mts +2 -0
  51. package/core-mappings-config.mjs +2 -0
  52. package/core-sync-engine.cjs +6 -0
  53. package/core-sync-engine.d.cts +2 -0
  54. package/core-sync-engine.d.mts +2 -0
  55. package/core-sync-engine.mjs +2 -0
  56. package/core-types.cjs +0 -0
  57. package/core-types.d.cts +2 -0
  58. package/core-types.d.mts +2 -0
  59. package/core-types.mjs +1 -0
  60. package/core-view-compiler.cjs +5 -0
  61. package/core-view-compiler.d.cts +2 -0
  62. package/core-view-compiler.d.mts +2 -0
  63. package/core-view-compiler.mjs +2 -0
  64. package/core.cjs +27 -0
  65. package/core.d.cts +9 -0
  66. package/core.d.mts +9 -0
  67. package/core.mjs +7 -0
  68. package/drizzle-meta-BGn0BqGb.cjs +9 -0
  69. package/drizzle-meta-CnC336L-.mjs +4 -0
  70. package/drizzle.cjs +107 -0
  71. package/drizzle.d.cts +28 -0
  72. package/drizzle.d.mts +28 -0
  73. package/drizzle.mjs +105 -0
  74. package/entity-config-Cvjo1cpv.cjs +4598 -0
  75. package/entity-config-D1DFyeUp.d.mts +2403 -0
  76. package/entity-config-DoR0khce.mjs +4533 -0
  77. package/entity-config-GczjD3y8.d.cts +2403 -0
  78. package/index.cjs +961 -0
  79. package/index.d.cts +57 -0
  80. package/index.d.mts +57 -0
  81. package/index.mjs +960 -0
  82. package/mapping-form-BHtPexzI.mjs +47 -0
  83. package/mapping-form-CUlFVfG8.d.cts +35 -0
  84. package/mapping-form-DdmDebC3.cjs +64 -0
  85. package/mapping-form-kjSiJaCX.d.mts +35 -0
  86. package/mapping-score-A2lRnlpk.d.mts +49 -0
  87. package/mapping-score-BJNe0lFn.mjs +87 -0
  88. package/mapping-score-BcjC0ZQs.d.cts +49 -0
  89. package/mapping-score-D9gfhtRu.cjs +146 -0
  90. package/mappings-config-CYPrzB3K.mjs +99 -0
  91. package/mappings-config-DTu_2cyx.cjs +104 -0
  92. package/mappings-config-DjRLCb2Q.d.cts +42 -0
  93. package/mappings-config-GxiYsSBQ.d.mts +42 -0
  94. package/namespace-probe-BtS0nXcr.d.cts +299 -0
  95. package/namespace-probe-CYMie-BI.d.mts +299 -0
  96. package/package.json +244 -21
  97. package/sdk-BZUn6wzs.mjs +764 -0
  98. package/sdk-CP_mYZa5.cjs +793 -0
  99. package/sdk.cjs +7 -0
  100. package/sdk.d.cts +33 -0
  101. package/sdk.d.mts +33 -0
  102. package/sdk.mjs +2 -0
  103. package/sync-engine-DUc0qWgo.cjs +230 -0
  104. package/sync-engine-DWnQPjV3.d.mts +183 -0
  105. package/sync-engine-DedXngop.mjs +207 -0
  106. package/sync-engine-uhfzKM-e.d.cts +183 -0
  107. package/types-BpsKVccL.d.cts +159 -0
  108. package/types-_qbJXMt_.d.mts +159 -0
  109. package/view-compiler-B4UYTvRM.d.cts +71 -0
  110. package/view-compiler-BHzbI9b7.cjs +202 -0
  111. package/view-compiler-CosFGar8.mjs +185 -0
  112. package/view-compiler-i_xFf-L0.d.mts +71 -0
  113. package/vite.cjs +38 -0
  114. package/vite.d.cts +23 -0
  115. package/vite.d.mts +23 -0
  116. package/vite.mjs +37 -0
  117. package/dist/index.mjs +0 -542
@@ -0,0 +1,45 @@
1
+ import { WorkOS } from "@workos-inc/node";
2
+ //#region ../auth-workos/src/map.ts
3
+ function workosUserToClivlyUser(user) {
4
+ if (!user.email) return null;
5
+ const name = [user.firstName, user.lastName].filter(Boolean).join(" ") || void 0;
6
+ return {
7
+ id: user.id,
8
+ email: user.email,
9
+ name
10
+ };
11
+ }
12
+ function readCookie(req, name) {
13
+ const header = req.headers.get("cookie");
14
+ if (!header) return null;
15
+ for (const part of header.split(";")) {
16
+ const eq = part.indexOf("=");
17
+ if (eq === -1) continue;
18
+ if (part.slice(0, eq).trim() === name) return decodeURIComponent(part.slice(eq + 1).trim());
19
+ }
20
+ return null;
21
+ }
22
+ //#endregion
23
+ //#region ../auth-workos/src/index.ts
24
+ const DEFAULT_COOKIE_NAME = "wos-session";
25
+ /**
26
+ * WorkOS → ClivlyAuthAdapter. Unseals the host app's AuthKit session cookie and
27
+ * resolves the CRM end-user's identity. Answers "who is this person?" only —
28
+ * CRM authorization lives in the separate `crm_members` layer.
29
+ */
30
+ function createWorkOSAuthAdapter(options) {
31
+ const workos = new WorkOS(options.apiKey, { clientId: options.clientId });
32
+ const cookieName = options.cookieName ?? DEFAULT_COOKIE_NAME;
33
+ return { async getUser(req) {
34
+ const sessionData = readCookie(req, cookieName);
35
+ if (!sessionData) return null;
36
+ const result = await workos.userManagement.loadSealedSession({
37
+ sessionData,
38
+ cookiePassword: options.cookiePassword
39
+ }).authenticate();
40
+ if (!result.authenticated) return null;
41
+ return workosUserToClivlyUser(result.user);
42
+ } };
43
+ }
44
+ //#endregion
45
+ export { createWorkOSAuthAdapter, workosUserToClivlyUser };
File without changes
@@ -0,0 +1,2 @@
1
+ import { t as CRMAdapter } from "./adapter-Cy4q2CLs.cjs";
2
+ export { CRMAdapter };
@@ -0,0 +1,2 @@
1
+ import { t as CRMAdapter } from "./adapter-Cd6D_3JD.mjs";
2
+ export { CRMAdapter };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_adapter = require("./auth-adapter-CBOKDhlc.cjs");
3
+ exports.authCustom = require_auth_adapter.authCustom;
@@ -0,0 +1,2 @@
1
+ import { a as authCustom, i as MembershipResolver, n as ClivlyUser, r as CrmMembership, t as ClivlyAuthAdapter } from "./auth-adapter-CdLODn2p.cjs";
2
+ export { ClivlyAuthAdapter, ClivlyUser, CrmMembership, MembershipResolver, authCustom };
@@ -0,0 +1,2 @@
1
+ import { a as authCustom, i as MembershipResolver, n as ClivlyUser, r as CrmMembership, t as ClivlyAuthAdapter } from "./auth-adapter-CRBW4elj.mjs";
2
+ export { ClivlyAuthAdapter, ClivlyUser, CrmMembership, MembershipResolver, authCustom };
@@ -0,0 +1,2 @@
1
+ import { t as authCustom } from "./auth-adapter-wqnnEjo3.mjs";
2
+ export { authCustom };
@@ -0,0 +1,32 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let drizzle_orm = require("drizzle-orm");
3
+ //#region ../core/src/drizzle.ts
4
+ /**
5
+ * Derive Clivly's discovery shape (table + column names) from a Drizzle schema
6
+ * object — the `* as schema` you pass to `drizzle(client, { schema })`. This
7
+ * delivers the "reads your schema" promise for Drizzle hosts: no hand-authored
8
+ * `DiscoveredTable[]`. Feed the result to the SDK's `schema` option, or diff it
9
+ * against your entity config.
10
+ *
11
+ * Column names are the real database column names (e.g. `full_name`), not the
12
+ * Drizzle property keys (`fullName`). Non-table values in the schema object
13
+ * (relations, enums, helpers) are ignored.
14
+ *
15
+ * `drizzle-orm` is an **optional peer dependency** — you only need it installed
16
+ * when you import this entry (`@clivly/core/drizzle`); the rest of `@clivly/core`
17
+ * stays dependency-free.
18
+ */
19
+ function discoverFromDrizzle(schema) {
20
+ const tables = [];
21
+ for (const value of Object.values(schema)) {
22
+ if (!(0, drizzle_orm.is)(value, drizzle_orm.Table)) continue;
23
+ const columns = (0, drizzle_orm.getTableColumns)(value);
24
+ tables.push({
25
+ name: (0, drizzle_orm.getTableName)(value),
26
+ columns: Object.values(columns).map((column) => column.name)
27
+ });
28
+ }
29
+ return tables;
30
+ }
31
+ //#endregion
32
+ exports.discoverFromDrizzle = discoverFromDrizzle;
@@ -0,0 +1,21 @@
1
+ import { u as DiscoveredSchemaTable } from "./entity-config-GczjD3y8.cjs";
2
+
3
+ //#region ../core/src/drizzle.d.ts
4
+ /**
5
+ * Derive Clivly's discovery shape (table + column names) from a Drizzle schema
6
+ * object — the `* as schema` you pass to `drizzle(client, { schema })`. This
7
+ * delivers the "reads your schema" promise for Drizzle hosts: no hand-authored
8
+ * `DiscoveredTable[]`. Feed the result to the SDK's `schema` option, or diff it
9
+ * against your entity config.
10
+ *
11
+ * Column names are the real database column names (e.g. `full_name`), not the
12
+ * Drizzle property keys (`fullName`). Non-table values in the schema object
13
+ * (relations, enums, helpers) are ignored.
14
+ *
15
+ * `drizzle-orm` is an **optional peer dependency** — you only need it installed
16
+ * when you import this entry (`@clivly/core/drizzle`); the rest of `@clivly/core`
17
+ * stays dependency-free.
18
+ */
19
+ declare function discoverFromDrizzle(schema: Record<string, unknown>): DiscoveredSchemaTable[];
20
+ //#endregion
21
+ export { discoverFromDrizzle };
@@ -0,0 +1,21 @@
1
+ import { u as DiscoveredSchemaTable } from "./entity-config-D1DFyeUp.mjs";
2
+
3
+ //#region ../core/src/drizzle.d.ts
4
+ /**
5
+ * Derive Clivly's discovery shape (table + column names) from a Drizzle schema
6
+ * object — the `* as schema` you pass to `drizzle(client, { schema })`. This
7
+ * delivers the "reads your schema" promise for Drizzle hosts: no hand-authored
8
+ * `DiscoveredTable[]`. Feed the result to the SDK's `schema` option, or diff it
9
+ * against your entity config.
10
+ *
11
+ * Column names are the real database column names (e.g. `full_name`), not the
12
+ * Drizzle property keys (`fullName`). Non-table values in the schema object
13
+ * (relations, enums, helpers) are ignored.
14
+ *
15
+ * `drizzle-orm` is an **optional peer dependency** — you only need it installed
16
+ * when you import this entry (`@clivly/core/drizzle`); the rest of `@clivly/core`
17
+ * stays dependency-free.
18
+ */
19
+ declare function discoverFromDrizzle(schema: Record<string, unknown>): DiscoveredSchemaTable[];
20
+ //#endregion
21
+ export { discoverFromDrizzle };
@@ -0,0 +1,31 @@
1
+ import { Table, getTableColumns, getTableName, is } from "drizzle-orm";
2
+ //#region ../core/src/drizzle.ts
3
+ /**
4
+ * Derive Clivly's discovery shape (table + column names) from a Drizzle schema
5
+ * object — the `* as schema` you pass to `drizzle(client, { schema })`. This
6
+ * delivers the "reads your schema" promise for Drizzle hosts: no hand-authored
7
+ * `DiscoveredTable[]`. Feed the result to the SDK's `schema` option, or diff it
8
+ * against your entity config.
9
+ *
10
+ * Column names are the real database column names (e.g. `full_name`), not the
11
+ * Drizzle property keys (`fullName`). Non-table values in the schema object
12
+ * (relations, enums, helpers) are ignored.
13
+ *
14
+ * `drizzle-orm` is an **optional peer dependency** — you only need it installed
15
+ * when you import this entry (`@clivly/core/drizzle`); the rest of `@clivly/core`
16
+ * stays dependency-free.
17
+ */
18
+ function discoverFromDrizzle(schema) {
19
+ const tables = [];
20
+ for (const value of Object.values(schema)) {
21
+ if (!is(value, Table)) continue;
22
+ const columns = getTableColumns(value);
23
+ tables.push({
24
+ name: getTableName(value),
25
+ columns: Object.values(columns).map((column) => column.name)
26
+ });
27
+ }
28
+ return tables;
29
+ }
30
+ //#endregion
31
+ export { discoverFromDrizzle };
@@ -0,0 +1,13 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_entity_config = require("./entity-config-Cvjo1cpv.cjs");
3
+ exports.CANONICAL_FIELDS = require_entity_config.CANONICAL_FIELDS;
4
+ exports.CRM_CONCEPTS = require_entity_config.CRM_CONCEPTS;
5
+ exports.CUSTOM_CONCEPT = require_entity_config.CUSTOM_CONCEPT;
6
+ exports.ClivlyConfigError = require_entity_config.ClivlyConfigError;
7
+ exports.MATERIALIZING_CONCEPTS = require_entity_config.MATERIALIZING_CONCEPTS;
8
+ exports.defineClivlyConfig = require_entity_config.defineClivlyConfig;
9
+ exports.entitiesConfigSchema = require_entity_config.entitiesConfigSchema;
10
+ exports.filterSchema = require_entity_config.filterSchema;
11
+ exports.relationshipSchema = require_entity_config.relationshipSchema;
12
+ exports.relationshipsSchema = require_entity_config.relationshipsSchema;
13
+ exports.validateEntitiesConfig = require_entity_config.validateEntitiesConfig;
@@ -0,0 +1,2 @@
1
+ import { C as validateEntitiesConfig, S as relationshipsSchema, _ as RelationshipsMap, a as ClivlyEntitiesConfig, b as filterSchema, c as ConfigValidationResult, d as FilterExpr, f as FilterValue, g as RelationshipVia, h as RelationshipSpec, i as ClivlyConfigError, l as CrmConcept, m as MaterializingConcept, n as CRM_CONCEPTS, o as ClivlyEntityConfig, p as MATERIALIZING_CONCEPTS, r as CUSTOM_CONCEPT, s as ConfigValidationError, t as CANONICAL_FIELDS, u as DiscoveredSchemaTable, v as defineClivlyConfig, x as relationshipSchema, y as entitiesConfigSchema } from "./entity-config-GczjD3y8.cjs";
2
+ export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntityConfig, ConfigValidationError, ConfigValidationResult, CrmConcept, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
@@ -0,0 +1,2 @@
1
+ import { C as validateEntitiesConfig, S as relationshipsSchema, _ as RelationshipsMap, a as ClivlyEntitiesConfig, b as filterSchema, c as ConfigValidationResult, d as FilterExpr, f as FilterValue, g as RelationshipVia, h as RelationshipSpec, i as ClivlyConfigError, l as CrmConcept, m as MaterializingConcept, n as CRM_CONCEPTS, o as ClivlyEntityConfig, p as MATERIALIZING_CONCEPTS, r as CUSTOM_CONCEPT, s as ConfigValidationError, t as CANONICAL_FIELDS, u as DiscoveredSchemaTable, v as defineClivlyConfig, x as relationshipSchema, y as entitiesConfigSchema } from "./entity-config-D1DFyeUp.mjs";
2
+ export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, ClivlyEntitiesConfig, ClivlyEntityConfig, ConfigValidationError, ConfigValidationResult, CrmConcept, DiscoveredSchemaTable, FilterExpr, FilterValue, MATERIALIZING_CONCEPTS, MaterializingConcept, RelationshipSpec, RelationshipVia, RelationshipsMap, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
@@ -0,0 +1,2 @@
1
+ import { a as MATERIALIZING_CONCEPTS, c as filterSchema, d as validateEntitiesConfig, i as ClivlyConfigError, l as relationshipSchema, n as CRM_CONCEPTS, o as defineClivlyConfig, r as CUSTOM_CONCEPT, s as entitiesConfigSchema, t as CANONICAL_FIELDS, u as relationshipsSchema } from "./entity-config-DoR0khce.mjs";
2
+ export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, MATERIALIZING_CONCEPTS, defineClivlyConfig, entitiesConfigSchema, filterSchema, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
@@ -0,0 +1,442 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_entity_config = require("./entity-config-Cvjo1cpv.cjs");
3
+ const require_mapping_score = require("./mapping-score-D9gfhtRu.cjs");
4
+ //#region ../core/src/entity-heuristics.ts
5
+ const TARGET_CONCEPTS = [
6
+ "contact",
7
+ "company",
8
+ "deal",
9
+ "activity",
10
+ "conversation",
11
+ "note"
12
+ ];
13
+ const TABLE_PATTERNS = [
14
+ {
15
+ concept: "contact",
16
+ names: [
17
+ "users",
18
+ "customers",
19
+ "clients",
20
+ "accounts",
21
+ "participants",
22
+ "attendees",
23
+ "people",
24
+ "members",
25
+ "contacts"
26
+ ]
27
+ },
28
+ {
29
+ concept: "company",
30
+ names: [
31
+ "companies",
32
+ "organizations",
33
+ "organisations",
34
+ "orgs",
35
+ "teams",
36
+ "businesses",
37
+ "accounts"
38
+ ]
39
+ },
40
+ {
41
+ concept: "deal",
42
+ names: [
43
+ "deals",
44
+ "opportunities",
45
+ "subscriptions",
46
+ "orders",
47
+ "invoices",
48
+ "sales"
49
+ ]
50
+ },
51
+ {
52
+ concept: "conversation",
53
+ names: [
54
+ "conversations",
55
+ "messages",
56
+ "tickets",
57
+ "threads",
58
+ "chats",
59
+ "inbox"
60
+ ]
61
+ },
62
+ {
63
+ concept: "activity",
64
+ names: [
65
+ "events",
66
+ "activities",
67
+ "jobs",
68
+ "logs",
69
+ "audit",
70
+ "shifts",
71
+ "scans",
72
+ "sessions"
73
+ ]
74
+ },
75
+ {
76
+ concept: "note",
77
+ names: [
78
+ "notes",
79
+ "comments",
80
+ "annotations"
81
+ ]
82
+ }
83
+ ];
84
+ const CONCEPT_FIELDS = {
85
+ contact: [{
86
+ field: "name",
87
+ candidates: [
88
+ "name",
89
+ "full_name",
90
+ "fullname",
91
+ "display_name"
92
+ ],
93
+ required: false
94
+ }, {
95
+ field: "email",
96
+ candidates: ["email", "email_address"],
97
+ required: true
98
+ }],
99
+ company: [{
100
+ field: "name",
101
+ candidates: [
102
+ "name",
103
+ "company_name",
104
+ "legal_name",
105
+ "title"
106
+ ],
107
+ required: true
108
+ }, {
109
+ field: "domain",
110
+ candidates: [
111
+ "domain",
112
+ "website",
113
+ "url"
114
+ ],
115
+ required: false
116
+ }],
117
+ deal: [
118
+ {
119
+ field: "name",
120
+ candidates: ["name", "title"],
121
+ required: true
122
+ },
123
+ {
124
+ field: "value",
125
+ candidates: [
126
+ "value",
127
+ "amount",
128
+ "total",
129
+ "price"
130
+ ],
131
+ required: false
132
+ },
133
+ {
134
+ field: "stage",
135
+ candidates: [
136
+ "stage",
137
+ "status",
138
+ "state"
139
+ ],
140
+ required: false
141
+ }
142
+ ],
143
+ activity: [
144
+ {
145
+ field: "type",
146
+ candidates: [
147
+ "type",
148
+ "kind",
149
+ "event_type"
150
+ ],
151
+ required: false
152
+ },
153
+ {
154
+ field: "summary",
155
+ candidates: [
156
+ "summary",
157
+ "description",
158
+ "message",
159
+ "title"
160
+ ],
161
+ required: false
162
+ },
163
+ {
164
+ field: "createdAt",
165
+ candidates: [
166
+ "created_at",
167
+ "createdat",
168
+ "timestamp"
169
+ ],
170
+ required: false
171
+ }
172
+ ],
173
+ conversation: [{
174
+ field: "status",
175
+ candidates: ["status", "state"],
176
+ required: false
177
+ }, {
178
+ field: "subject",
179
+ candidates: [
180
+ "subject",
181
+ "title",
182
+ "preview"
183
+ ],
184
+ required: false
185
+ }],
186
+ note: [{
187
+ field: "body",
188
+ candidates: [
189
+ "body",
190
+ "content",
191
+ "text",
192
+ "note"
193
+ ],
194
+ required: true
195
+ }]
196
+ };
197
+ const CURSOR_CANDIDATES = [
198
+ "updatedat",
199
+ "updated_at",
200
+ "modifiedat",
201
+ "modified_at",
202
+ "createdat",
203
+ "created_at"
204
+ ];
205
+ function suggestCursorField(columnKeys) {
206
+ const lowerToActual = new Map(columnKeys.map((c) => [c.toLowerCase(), c]));
207
+ for (const candidate of CURSOR_CANDIDATES) {
208
+ const match = lowerToActual.get(candidate);
209
+ if (match) return match;
210
+ }
211
+ return null;
212
+ }
213
+ const MATERIALIZING = new Set(require_entity_config.MATERIALIZING_CONCEPTS);
214
+ function matchesConcept(tableName, concept) {
215
+ const normalized = tableName.toLowerCase();
216
+ const entry = TABLE_PATTERNS.find((p) => p.concept === concept);
217
+ if (!entry) return null;
218
+ if (entry.names.includes(normalized)) return { exact: true };
219
+ if (entry.names.some((name) => normalized.includes(name))) return { exact: false };
220
+ return null;
221
+ }
222
+ function metaFor(table, column) {
223
+ return table.columnsMeta?.find((c) => c.name === column);
224
+ }
225
+ function hasDefiningColumn(concept, columns) {
226
+ const lower = new Set(columns.map((c) => c.toLowerCase()));
227
+ const required = CONCEPT_FIELDS[concept].filter((f) => f.required);
228
+ return required.length > 0 && required.every((f) => f.candidates.some((c) => lower.has(c)));
229
+ }
230
+ function scoreConcepts(table) {
231
+ const out = [];
232
+ for (const concept of TARGET_CONCEPTS) {
233
+ if (!MATERIALIZING.has(concept)) continue;
234
+ const nameMatch = matchesConcept(table.name, concept);
235
+ if (!nameMatch) continue;
236
+ let score = 0;
237
+ const reasons = [];
238
+ if (nameMatch.exact) {
239
+ score += require_mapping_score.W.nameExact;
240
+ reasons.push(`name matches "${table.name}"`);
241
+ } else {
242
+ score += require_mapping_score.W.nameSubstring;
243
+ reasons.push(`name looks like ${concept}`);
244
+ }
245
+ if (hasDefiningColumn(concept, table.columns)) {
246
+ score += require_mapping_score.W.definingColumn;
247
+ reasons.push(`has the columns a ${concept} needs`);
248
+ }
249
+ if (table.columnsMeta?.some((c) => c.isPrimaryKey)) {
250
+ score += require_mapping_score.W.hasPrimaryKey;
251
+ reasons.push("has primary key");
252
+ }
253
+ if (table.rowCount !== void 0) if (table.rowCount === 0) {
254
+ score += require_mapping_score.W.emptyTable;
255
+ reasons.push("table is empty");
256
+ } else {
257
+ score += require_mapping_score.W.hasRows;
258
+ reasons.push(`${table.rowCount.toLocaleString("en-US")} rows`);
259
+ }
260
+ out.push({
261
+ value: concept,
262
+ score,
263
+ tier: require_mapping_score.tierFor(score, require_mapping_score.CONCEPT_TIERS),
264
+ reasons
265
+ });
266
+ }
267
+ return out.sort((a, b) => b.score - a.score);
268
+ }
269
+ function topConcept(table) {
270
+ const ranked = scoreConcepts(table);
271
+ const best = ranked[0];
272
+ if (!best) return null;
273
+ return {
274
+ best,
275
+ ambiguous: require_mapping_score.isAmbiguous(ranked)
276
+ };
277
+ }
278
+ function requiredFieldsFor(concept) {
279
+ if (concept === "custom") return [];
280
+ return CONCEPT_FIELDS[concept].filter((f) => f.required).map((f) => f.field);
281
+ }
282
+ function validateMapping(requiredFields, fieldMap) {
283
+ const missing = requiredFields.filter((field) => !fieldMap[field]);
284
+ return {
285
+ valid: missing.length === 0,
286
+ missing
287
+ };
288
+ }
289
+ const DISCRIMINATOR_COLUMNS = [
290
+ "role",
291
+ "type",
292
+ "kind",
293
+ "status",
294
+ "state",
295
+ "category",
296
+ "tier"
297
+ ];
298
+ function suggestDiscriminatorColumns(columns) {
299
+ const lowerToActual = new Map(columns.map((c) => [c.toLowerCase(), c]));
300
+ const out = [];
301
+ for (const name of DISCRIMINATOR_COLUMNS) {
302
+ const match = lowerToActual.get(name);
303
+ if (match) out.push(match);
304
+ }
305
+ return out;
306
+ }
307
+ const COMPANY_FK_HINTS = [
308
+ "organization_id",
309
+ "organisation_id",
310
+ "org_id",
311
+ "company_id",
312
+ "account_id",
313
+ "team_id"
314
+ ];
315
+ function firstMatchingColumn(columns, hints) {
316
+ const lowerToActual = new Map(columns.map((c) => [c.toLowerCase(), c]));
317
+ for (const hint of hints) {
318
+ const match = lowerToActual.get(hint);
319
+ if (match) return match;
320
+ }
321
+ return null;
322
+ }
323
+ /**
324
+ * Suggest contact→company relationships. When the source table carries FK
325
+ * metadata (`references`), the target table is a *fact*, not a name guess —
326
+ * that scores highest. Without metadata (v1 hosts) we fall back to the legacy
327
+ * FK-name hints at a lower tier.
328
+ */
329
+ function scoreRelationships(source, allTables) {
330
+ const companyTables = allTables.filter((t) => t.name !== source.name && topConcept(t)?.best.value === "company");
331
+ if (companyTables.length === 0) return [];
332
+ const companyNames = new Set(companyTables.map((t) => t.name.toLowerCase()));
333
+ const out = [];
334
+ const seenTargets = /* @__PURE__ */ new Set();
335
+ for (const col of source.columnsMeta ?? []) {
336
+ const ref = col.references;
337
+ if (!(ref && companyNames.has(ref.table.toLowerCase()))) continue;
338
+ if (seenTargets.has(ref.table.toLowerCase())) continue;
339
+ seenTargets.add(ref.table.toLowerCase());
340
+ out.push({
341
+ value: {
342
+ name: "company",
343
+ entityTable: ref.table,
344
+ via: {
345
+ fkOn: "contact",
346
+ column: `${source.name}.${col.name}`
347
+ }
348
+ },
349
+ score: require_mapping_score.W.fkResolved,
350
+ tier: require_mapping_score.tierFor(require_mapping_score.W.fkResolved, require_mapping_score.RELATIONSHIP_TIERS),
351
+ reasons: [`foreign key → ${ref.table}.${ref.column}`]
352
+ });
353
+ }
354
+ if (source.columnsMeta?.length) return out.sort((a, b) => b.score - a.score);
355
+ const contactFk = firstMatchingColumn(source.columns, COMPANY_FK_HINTS);
356
+ if (contactFk) out.push({
357
+ value: {
358
+ name: "company",
359
+ entityTable: companyTables[0]?.name ?? "",
360
+ via: {
361
+ fkOn: "contact",
362
+ column: `${source.name}.${contactFk}`
363
+ }
364
+ },
365
+ score: require_mapping_score.W.fkNameHint,
366
+ tier: require_mapping_score.tierFor(require_mapping_score.W.fkNameHint, require_mapping_score.RELATIONSHIP_TIERS),
367
+ reasons: [`column "${contactFk}" looks like a company link`]
368
+ });
369
+ return out.sort((a, b) => b.score - a.score);
370
+ }
371
+ const FIELD_TYPE_EXPECTATION = {
372
+ name: "text",
373
+ email: "text",
374
+ domain: "text",
375
+ stage: "text",
376
+ status: "text",
377
+ subject: "text",
378
+ body: "text",
379
+ summary: "text",
380
+ type: "text",
381
+ value: "numeric",
382
+ createdAt: "timestamp"
383
+ };
384
+ function typeFits(field, type) {
385
+ const expectation = FIELD_TYPE_EXPECTATION[field];
386
+ if (!(expectation && type)) return null;
387
+ if (expectation === "text") return require_mapping_score.isTextLike(type);
388
+ if (expectation === "numeric") return require_mapping_score.isNumericLike(type);
389
+ return require_mapping_score.isTimestampLike(type);
390
+ }
391
+ function scoreFieldMap(concept, table) {
392
+ const lowerToActual = new Map(table.columns.map((c) => [c.toLowerCase(), c]));
393
+ const out = [];
394
+ for (const { field, candidates, required } of CONCEPT_FIELDS[concept]) for (const candidate of candidates) {
395
+ const column = lowerToActual.get(candidate);
396
+ if (!column) continue;
397
+ let score = require_mapping_score.W.fieldNameMatch;
398
+ const reasons = [`name matches "${column}"`];
399
+ const meta = metaFor(table, column);
400
+ const fits = typeFits(field, meta?.type);
401
+ if (fits === true) {
402
+ score += require_mapping_score.W.typeCompatible;
403
+ reasons.push(`type ${meta?.type}`);
404
+ } else if (fits === false) {
405
+ score += require_mapping_score.W.typeMismatch;
406
+ reasons.push(`type ${meta?.type} does not fit ${field}`);
407
+ }
408
+ if (required && meta?.nullable === false) {
409
+ score += require_mapping_score.W.requiredNotNull;
410
+ reasons.push("not null");
411
+ }
412
+ out.push({
413
+ value: {
414
+ field,
415
+ column,
416
+ required
417
+ },
418
+ score,
419
+ tier: require_mapping_score.tierFor(score, require_mapping_score.FIELD_TIERS),
420
+ reasons
421
+ });
422
+ break;
423
+ }
424
+ return out;
425
+ }
426
+ function toFieldMap(scored) {
427
+ const map = {};
428
+ for (const { value } of scored) map[value.field] = value.column;
429
+ return map;
430
+ }
431
+ //#endregion
432
+ exports.TARGET_CONCEPTS = TARGET_CONCEPTS;
433
+ exports.matchesConcept = matchesConcept;
434
+ exports.requiredFieldsFor = requiredFieldsFor;
435
+ exports.scoreConcepts = scoreConcepts;
436
+ exports.scoreFieldMap = scoreFieldMap;
437
+ exports.scoreRelationships = scoreRelationships;
438
+ exports.suggestCursorField = suggestCursorField;
439
+ exports.suggestDiscriminatorColumns = suggestDiscriminatorColumns;
440
+ exports.toFieldMap = toFieldMap;
441
+ exports.topConcept = topConcept;
442
+ exports.validateMapping = validateMapping;