@clivly/core 0.2.0 → 0.3.0-next.1

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.
@@ -0,0 +1,87 @@
1
+ //#region src/mapping-score.ts
2
+ const CONCEPT_TIERS = {
3
+ high: 70,
4
+ medium: 40
5
+ };
6
+ const FIELD_TIERS = {
7
+ high: 70,
8
+ medium: 40
9
+ };
10
+ const RELATIONSHIP_TIERS = {
11
+ high: 60,
12
+ medium: 30
13
+ };
14
+ /** Two concept candidates closer than this are treated as ambiguous. */
15
+ const AMBIGUITY_MARGIN = 15;
16
+ /** Signal weights. Tuned against fixtures; adjust here, not at call sites. */
17
+ const W = {
18
+ nameExact: 50,
19
+ nameSubstring: 25,
20
+ definingColumn: 20,
21
+ hasPrimaryKey: 10,
22
+ hasRows: 10,
23
+ emptyTable: -20,
24
+ fieldNameMatch: 50,
25
+ typeCompatible: 30,
26
+ typeMismatch: -30,
27
+ requiredNotNull: 10,
28
+ fkResolved: 60,
29
+ fkNameHint: 30,
30
+ joinTable: 20
31
+ };
32
+ function tierFor(score, t) {
33
+ if (score >= t.high) return "High";
34
+ if (score >= t.medium) return "Medium";
35
+ return "Low";
36
+ }
37
+ const TEXT_TYPES = [
38
+ "text",
39
+ "varchar",
40
+ "char",
41
+ "citext",
42
+ "uuid",
43
+ "string"
44
+ ];
45
+ const NUMERIC_TYPES = [
46
+ "numeric",
47
+ "decimal",
48
+ "int",
49
+ "integer",
50
+ "bigint",
51
+ "smallint",
52
+ "float",
53
+ "double",
54
+ "real",
55
+ "money"
56
+ ];
57
+ const TIMESTAMP_TYPES = [
58
+ "timestamp",
59
+ "timestamptz",
60
+ "date",
61
+ "datetime"
62
+ ];
63
+ function matchesType(type, list) {
64
+ if (!type) return false;
65
+ const normalized = type.toLowerCase();
66
+ return list.some((t) => normalized.startsWith(t));
67
+ }
68
+ function isTextLike(type) {
69
+ return matchesType(type, TEXT_TYPES);
70
+ }
71
+ function isNumericLike(type) {
72
+ return matchesType(type, NUMERIC_TYPES);
73
+ }
74
+ function isTimestampLike(type) {
75
+ return matchesType(type, TIMESTAMP_TYPES);
76
+ }
77
+ /**
78
+ * True when the top two candidates are too close to call. Callers must never
79
+ * one-click apply an ambiguous result — the user picks.
80
+ */
81
+ function isAmbiguous(ranked) {
82
+ const [first, second] = ranked;
83
+ if (!(first && second)) return false;
84
+ return first.score - second.score < 15;
85
+ }
86
+ //#endregion
87
+ export { AMBIGUITY_MARGIN, CONCEPT_TIERS, FIELD_TIERS, RELATIONSHIP_TIERS, W, isAmbiguous, isNumericLike, isTextLike, isTimestampLike, tierFor };
@@ -53,6 +53,7 @@ function buildEntity(row, refIndex) {
53
53
  role: row.role ?? DEFAULT_ROLE,
54
54
  fields: canonicalFields(row.targetConcept, row.fieldMap)
55
55
  };
56
+ if (row.targetConcept === "custom" && row.targetObjectTypeId) entity.targetObjectTypeId = row.targetObjectTypeId;
56
57
  if (row.filter && Object.keys(row.filter).length > 0) entity.filter = row.filter;
57
58
  if (row.relationships && Object.keys(row.relationships).length > 0) {
58
59
  const relationships = {};
@@ -29,6 +29,7 @@ interface EntityMappingRow {
29
29
  sourceTable: string;
30
30
  status?: string | null;
31
31
  targetConcept: string;
32
+ targetObjectTypeId?: string | null;
32
33
  }
33
34
  /**
34
35
  * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
@@ -29,6 +29,7 @@ interface EntityMappingRow {
29
29
  sourceTable: string;
30
30
  status?: string | null;
31
31
  targetConcept: string;
32
+ targetObjectTypeId?: string | null;
32
33
  }
33
34
  /**
34
35
  * Compile persisted mapping rows into a `ClivlyEntitiesConfig`. Only `active`
@@ -52,6 +52,7 @@ function buildEntity(row, refIndex) {
52
52
  role: row.role ?? DEFAULT_ROLE,
53
53
  fields: canonicalFields(row.targetConcept, row.fieldMap)
54
54
  };
55
+ if (row.targetConcept === "custom" && row.targetObjectTypeId) entity.targetObjectTypeId = row.targetObjectTypeId;
55
56
  if (row.filter && Object.keys(row.filter).length > 0) entity.filter = row.filter;
56
57
  if (row.relationships && Object.keys(row.relationships).length > 0) {
57
58
  const relationships = {};
@@ -114,7 +114,8 @@ function isCompositeEntity(config, entityKey) {
114
114
  * companies first. Returns per-entity and total action counts.
115
115
  */
116
116
  async function runSync(config, store, options = {}) {
117
- const views = require_view_compiler.compileEntityViews(config, options).sort(syncOrder);
117
+ const allViews = require_view_compiler.compileEntityViews(config, options).sort(syncOrder);
118
+ const views = options.sample ? allViews.filter((v) => v.entityKey === options.sample?.entityKey) : allViews;
118
119
  const entities = [];
119
120
  const totals = {
120
121
  created: 0,
@@ -123,15 +124,18 @@ async function runSync(config, store, options = {}) {
123
124
  archived: 0
124
125
  };
125
126
  for (const view of views) {
126
- const [source, mirror] = await Promise.all([store.readView(view), store.listMirror({
127
+ const objectTypeId = config.entities[view.entityKey]?.targetObjectTypeId;
128
+ const [source, mirror] = await Promise.all([store.readView(view, options.sample ? { limit: 1 } : void 0), store.listMirror({
127
129
  concept: view.concept,
128
- role: view.role
130
+ role: view.role,
131
+ objectTypeId
129
132
  })]);
130
- const actions = reconcile(source, mirror, { composite: isCompositeEntity(config, view.entityKey) });
133
+ const actions = reconcile(source, mirror, { composite: isCompositeEntity(config, view.entityKey) }).filter((a) => !(options.sample && a.kind === "archive"));
131
134
  const persistActions = await resolveActions(store, actions);
132
135
  await store.persist({
133
136
  concept: view.concept,
134
137
  role: view.role,
138
+ objectTypeId,
135
139
  actions: persistActions
136
140
  });
137
141
  const counts = emptyTally();
@@ -156,6 +160,52 @@ async function runSync(config, store, options = {}) {
156
160
  totals
157
161
  };
158
162
  }
163
+ function isEmptyValue(value) {
164
+ return value === null || value === void 0 || value === "";
165
+ }
166
+ /**
167
+ * Sample-scoped data-quality warnings over projected preview rows. Each mapped
168
+ * (or required-but-unmapped) field yields at most one warning, by precedence:
169
+ * a required field empty in ≥1 row → `K/N` form; an optional field empty in
170
+ * ALL rows → all-empty form; zero source rows → a single table-level warning.
171
+ */
172
+ function computeSampleWarnings(rows, requiredFields, sourceTable) {
173
+ if (rows.length === 0) return [{ message: `no rows found in ${sourceTable}` }];
174
+ const required = new Set(requiredFields);
175
+ const fields = new Set(requiredFields);
176
+ for (const row of rows) for (const key of Object.keys(row.fields)) fields.add(key);
177
+ const warnings = [];
178
+ for (const field of [...fields].sort()) {
179
+ const emptyCount = rows.filter((row) => isEmptyValue(row.fields[field])).length;
180
+ if (required.has(field)) {
181
+ if (emptyCount > 0) warnings.push({
182
+ field,
183
+ message: `${field} empty in ${emptyCount}/${rows.length} sampled`
184
+ });
185
+ } else if (emptyCount === rows.length) warnings.push({
186
+ field,
187
+ message: `${field} is empty in all ${rows.length} sampled rows`
188
+ });
189
+ }
190
+ return warnings;
191
+ }
192
+ /**
193
+ * Zero-write preview: read ≤`limit` rows through the compiled view (a pure
194
+ * SELECT whose SQL already projects each row into its mapped fields) and
195
+ * compute sample warnings. Calls ONLY `store.readView` — never a write path —
196
+ * so it can never mutate the mirror. The distinct, write-one-row sibling is
197
+ * `runSync`'s sample mode (T13).
198
+ */
199
+ async function previewSync(store, view, opts) {
200
+ const rows = await store.readView(view, { limit: opts.limit });
201
+ return {
202
+ rows,
203
+ sampledCount: rows.length,
204
+ warnings: computeSampleWarnings(rows, opts.requiredFields, opts.sourceTable)
205
+ };
206
+ }
159
207
  //#endregion
208
+ exports.computeSampleWarnings = computeSampleWarnings;
209
+ exports.previewSync = previewSync;
160
210
  exports.reconcile = reconcile;
161
211
  exports.runSync = runSync;
@@ -108,16 +108,23 @@ interface SyncStore {
108
108
  /** Current mirror rows for a concept + role (including archived). */
109
109
  listMirror(input: {
110
110
  concept: string;
111
- role: string;
111
+ role: string; /** Target custom-object type id, for `concept: "custom"` entities. */
112
+ objectTypeId?: string;
112
113
  }): Promise<MirrorRecord[]>;
113
114
  /** Apply the resolved actions to the mirror table for this concept + role. */
114
115
  persist(input: {
115
116
  concept: string;
116
- role: string;
117
+ role: string; /** Target custom-object type id, for `concept: "custom"` entities. */
118
+ objectTypeId?: string;
117
119
  actions: PersistAction[];
118
120
  }): Promise<void>;
119
- /** Execute the view's SQL and return its rows. */
120
- readView(view: CompiledView): Promise<SourceRow[]>;
121
+ /**
122
+ * Execute the view's SQL and return its rows. `opts.limit`, when given,
123
+ * caps the number of rows returned (used by `runSync`'s sample mode).
124
+ */
125
+ readView(view: CompiledView, opts?: {
126
+ limit?: number;
127
+ }): Promise<SourceRow[]>;
121
128
  /** Map host-side company refs → `crm_companies.id` (already-synced companies only). */
122
129
  resolveCompanyRefs(refs: string[]): Promise<Map<string, string>>;
123
130
  }
@@ -144,5 +151,33 @@ interface SyncResult {
144
151
  * companies first. Returns per-entity and total action counts.
145
152
  */
146
153
  declare function runSync(config: ClivlyEntitiesConfig, store: SyncStore, options?: CompileOptions): Promise<SyncResult>;
154
+ interface PreviewWarning {
155
+ field?: string;
156
+ message: string;
157
+ }
158
+ interface PreviewResult {
159
+ rows: SourceRow[];
160
+ sampledCount: number;
161
+ warnings: PreviewWarning[];
162
+ }
163
+ /**
164
+ * Sample-scoped data-quality warnings over projected preview rows. Each mapped
165
+ * (or required-but-unmapped) field yields at most one warning, by precedence:
166
+ * a required field empty in ≥1 row → `K/N` form; an optional field empty in
167
+ * ALL rows → all-empty form; zero source rows → a single table-level warning.
168
+ */
169
+ declare function computeSampleWarnings(rows: SourceRow[], requiredFields: string[], sourceTable: string): PreviewWarning[];
170
+ /**
171
+ * Zero-write preview: read ≤`limit` rows through the compiled view (a pure
172
+ * SELECT whose SQL already projects each row into its mapped fields) and
173
+ * compute sample warnings. Calls ONLY `store.readView` — never a write path —
174
+ * so it can never mutate the mirror. The distinct, write-one-row sibling is
175
+ * `runSync`'s sample mode (T13).
176
+ */
177
+ declare function previewSync(store: SyncStore, view: CompiledView, opts: {
178
+ limit: number;
179
+ requiredFields: string[];
180
+ sourceTable: string;
181
+ }): Promise<PreviewResult>;
147
182
  //#endregion
148
- export { EntitySyncResult, MirrorRecord, PersistAction, SourceRow, SyncAction, SyncResult, SyncStore, reconcile, runSync };
183
+ export { EntitySyncResult, MirrorRecord, PersistAction, PreviewResult, PreviewWarning, SourceRow, SyncAction, SyncResult, SyncStore, computeSampleWarnings, previewSync, reconcile, runSync };
@@ -108,16 +108,23 @@ interface SyncStore {
108
108
  /** Current mirror rows for a concept + role (including archived). */
109
109
  listMirror(input: {
110
110
  concept: string;
111
- role: string;
111
+ role: string; /** Target custom-object type id, for `concept: "custom"` entities. */
112
+ objectTypeId?: string;
112
113
  }): Promise<MirrorRecord[]>;
113
114
  /** Apply the resolved actions to the mirror table for this concept + role. */
114
115
  persist(input: {
115
116
  concept: string;
116
- role: string;
117
+ role: string; /** Target custom-object type id, for `concept: "custom"` entities. */
118
+ objectTypeId?: string;
117
119
  actions: PersistAction[];
118
120
  }): Promise<void>;
119
- /** Execute the view's SQL and return its rows. */
120
- readView(view: CompiledView): Promise<SourceRow[]>;
121
+ /**
122
+ * Execute the view's SQL and return its rows. `opts.limit`, when given,
123
+ * caps the number of rows returned (used by `runSync`'s sample mode).
124
+ */
125
+ readView(view: CompiledView, opts?: {
126
+ limit?: number;
127
+ }): Promise<SourceRow[]>;
121
128
  /** Map host-side company refs → `crm_companies.id` (already-synced companies only). */
122
129
  resolveCompanyRefs(refs: string[]): Promise<Map<string, string>>;
123
130
  }
@@ -144,5 +151,33 @@ interface SyncResult {
144
151
  * companies first. Returns per-entity and total action counts.
145
152
  */
146
153
  declare function runSync(config: ClivlyEntitiesConfig, store: SyncStore, options?: CompileOptions): Promise<SyncResult>;
154
+ interface PreviewWarning {
155
+ field?: string;
156
+ message: string;
157
+ }
158
+ interface PreviewResult {
159
+ rows: SourceRow[];
160
+ sampledCount: number;
161
+ warnings: PreviewWarning[];
162
+ }
163
+ /**
164
+ * Sample-scoped data-quality warnings over projected preview rows. Each mapped
165
+ * (or required-but-unmapped) field yields at most one warning, by precedence:
166
+ * a required field empty in ≥1 row → `K/N` form; an optional field empty in
167
+ * ALL rows → all-empty form; zero source rows → a single table-level warning.
168
+ */
169
+ declare function computeSampleWarnings(rows: SourceRow[], requiredFields: string[], sourceTable: string): PreviewWarning[];
170
+ /**
171
+ * Zero-write preview: read ≤`limit` rows through the compiled view (a pure
172
+ * SELECT whose SQL already projects each row into its mapped fields) and
173
+ * compute sample warnings. Calls ONLY `store.readView` — never a write path —
174
+ * so it can never mutate the mirror. The distinct, write-one-row sibling is
175
+ * `runSync`'s sample mode (T13).
176
+ */
177
+ declare function previewSync(store: SyncStore, view: CompiledView, opts: {
178
+ limit: number;
179
+ requiredFields: string[];
180
+ sourceTable: string;
181
+ }): Promise<PreviewResult>;
147
182
  //#endregion
148
- export { EntitySyncResult, MirrorRecord, PersistAction, SourceRow, SyncAction, SyncResult, SyncStore, reconcile, runSync };
183
+ export { EntitySyncResult, MirrorRecord, PersistAction, PreviewResult, PreviewWarning, SourceRow, SyncAction, SyncResult, SyncStore, computeSampleWarnings, previewSync, reconcile, runSync };
@@ -113,7 +113,8 @@ function isCompositeEntity(config, entityKey) {
113
113
  * companies first. Returns per-entity and total action counts.
114
114
  */
115
115
  async function runSync(config, store, options = {}) {
116
- const views = compileEntityViews(config, options).sort(syncOrder);
116
+ const allViews = compileEntityViews(config, options).sort(syncOrder);
117
+ const views = options.sample ? allViews.filter((v) => v.entityKey === options.sample?.entityKey) : allViews;
117
118
  const entities = [];
118
119
  const totals = {
119
120
  created: 0,
@@ -122,15 +123,18 @@ async function runSync(config, store, options = {}) {
122
123
  archived: 0
123
124
  };
124
125
  for (const view of views) {
125
- const [source, mirror] = await Promise.all([store.readView(view), store.listMirror({
126
+ const objectTypeId = config.entities[view.entityKey]?.targetObjectTypeId;
127
+ const [source, mirror] = await Promise.all([store.readView(view, options.sample ? { limit: 1 } : void 0), store.listMirror({
126
128
  concept: view.concept,
127
- role: view.role
129
+ role: view.role,
130
+ objectTypeId
128
131
  })]);
129
- const actions = reconcile(source, mirror, { composite: isCompositeEntity(config, view.entityKey) });
132
+ const actions = reconcile(source, mirror, { composite: isCompositeEntity(config, view.entityKey) }).filter((a) => !(options.sample && a.kind === "archive"));
130
133
  const persistActions = await resolveActions(store, actions);
131
134
  await store.persist({
132
135
  concept: view.concept,
133
136
  role: view.role,
137
+ objectTypeId,
134
138
  actions: persistActions
135
139
  });
136
140
  const counts = emptyTally();
@@ -155,5 +159,49 @@ async function runSync(config, store, options = {}) {
155
159
  totals
156
160
  };
157
161
  }
162
+ function isEmptyValue(value) {
163
+ return value === null || value === void 0 || value === "";
164
+ }
165
+ /**
166
+ * Sample-scoped data-quality warnings over projected preview rows. Each mapped
167
+ * (or required-but-unmapped) field yields at most one warning, by precedence:
168
+ * a required field empty in ≥1 row → `K/N` form; an optional field empty in
169
+ * ALL rows → all-empty form; zero source rows → a single table-level warning.
170
+ */
171
+ function computeSampleWarnings(rows, requiredFields, sourceTable) {
172
+ if (rows.length === 0) return [{ message: `no rows found in ${sourceTable}` }];
173
+ const required = new Set(requiredFields);
174
+ const fields = new Set(requiredFields);
175
+ for (const row of rows) for (const key of Object.keys(row.fields)) fields.add(key);
176
+ const warnings = [];
177
+ for (const field of [...fields].sort()) {
178
+ const emptyCount = rows.filter((row) => isEmptyValue(row.fields[field])).length;
179
+ if (required.has(field)) {
180
+ if (emptyCount > 0) warnings.push({
181
+ field,
182
+ message: `${field} empty in ${emptyCount}/${rows.length} sampled`
183
+ });
184
+ } else if (emptyCount === rows.length) warnings.push({
185
+ field,
186
+ message: `${field} is empty in all ${rows.length} sampled rows`
187
+ });
188
+ }
189
+ return warnings;
190
+ }
191
+ /**
192
+ * Zero-write preview: read ≤`limit` rows through the compiled view (a pure
193
+ * SELECT whose SQL already projects each row into its mapped fields) and
194
+ * compute sample warnings. Calls ONLY `store.readView` — never a write path —
195
+ * so it can never mutate the mirror. The distinct, write-one-row sibling is
196
+ * `runSync`'s sample mode (T13).
197
+ */
198
+ async function previewSync(store, view, opts) {
199
+ const rows = await store.readView(view, { limit: opts.limit });
200
+ return {
201
+ rows,
202
+ sampledCount: rows.length,
203
+ warnings: computeSampleWarnings(rows, opts.requiredFields, opts.sourceTable)
204
+ };
205
+ }
158
206
  //#endregion
159
- export { reconcile, runSync };
207
+ export { computeSampleWarnings, previewSync, reconcile, runSync };
package/dist/types.d.cts CHANGED
@@ -34,6 +34,7 @@ interface Deal {
34
34
  name: string;
35
35
  orgId: string;
36
36
  owner: string | null;
37
+ position: number | null;
37
38
  stage: DealStage;
38
39
  value: number;
39
40
  }
@@ -138,9 +139,11 @@ interface CreateActivityInput {
138
139
  summary: string;
139
140
  type: ActivityType;
140
141
  }
142
+ type RecordSource = "all" | "synced";
141
143
  interface ContactFilters {
142
144
  companyId?: string;
143
145
  search?: string;
146
+ source?: RecordSource;
144
147
  status?: ContactStatus;
145
148
  }
146
149
  interface TaskFilters {
@@ -153,4 +156,4 @@ interface TaskFilters {
153
156
  status?: TaskStatus;
154
157
  }
155
158
  //#endregion
156
- export { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput };
159
+ export { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, RecordSource, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput };
package/dist/types.d.mts CHANGED
@@ -34,6 +34,7 @@ interface Deal {
34
34
  name: string;
35
35
  orgId: string;
36
36
  owner: string | null;
37
+ position: number | null;
37
38
  stage: DealStage;
38
39
  value: number;
39
40
  }
@@ -138,9 +139,11 @@ interface CreateActivityInput {
138
139
  summary: string;
139
140
  type: ActivityType;
140
141
  }
142
+ type RecordSource = "all" | "synced";
141
143
  interface ContactFilters {
142
144
  companyId?: string;
143
145
  search?: string;
146
+ source?: RecordSource;
144
147
  status?: ContactStatus;
145
148
  }
146
149
  interface TaskFilters {
@@ -153,4 +156,4 @@ interface TaskFilters {
153
156
  status?: TaskStatus;
154
157
  }
155
158
  //#endregion
156
- export { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput };
159
+ export { Activity, ActivityType, Company, Contact, ContactFilters, ContactStatus, CreateActivityInput, CreateCompanyInput, CreateContactInput, CreateDealInput, CreateTaskInput, CrmRole, Deal, DealStage, Note, RecordSource, Task, TaskEntityType, TaskFilters, TaskPriority, TaskStatus, UpdateContactInput, UpdateDealInput, UpdateTaskInput };
@@ -11,6 +11,13 @@ interface CompileOptions {
11
11
  column: string;
12
12
  value: string | number;
13
13
  };
14
+ /**
15
+ * Restrict `runSync` to a single entity's view, for a "test with one
16
+ * record" dry run. See `runSync`'s `sample` option in `sync-engine.ts`.
17
+ */
18
+ sample?: {
19
+ entityKey: string;
20
+ };
14
21
  /**
15
22
  * Emit views `WITH (security_invoker = true)` so they run with the querying
16
23
  * role's privileges instead of the (owner's) definer privileges — the
@@ -11,6 +11,13 @@ interface CompileOptions {
11
11
  column: string;
12
12
  value: string | number;
13
13
  };
14
+ /**
15
+ * Restrict `runSync` to a single entity's view, for a "test with one
16
+ * record" dry run. See `runSync`'s `sample` option in `sync-engine.ts`.
17
+ */
18
+ sample?: {
19
+ entityKey: string;
20
+ };
14
21
  /**
15
22
  * Emit views `WITH (security_invoker = true)` so they run with the querying
16
23
  * role's privileges instead of the (owner's) definer privileges — the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clivly/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0-next.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "ORM/auth-agnostic CRM adapter contracts and shared domain types for Clivly.",
@@ -37,6 +37,16 @@
37
37
  "default": "./dist/entity-heuristics.cjs"
38
38
  }
39
39
  },
40
+ "./mapping-score": {
41
+ "import": {
42
+ "types": "./dist/mapping-score.d.mts",
43
+ "default": "./dist/mapping-score.mjs"
44
+ },
45
+ "require": {
46
+ "types": "./dist/mapping-score.d.cts",
47
+ "default": "./dist/mapping-score.cjs"
48
+ }
49
+ },
40
50
  "./auth-adapter": {
41
51
  "import": {
42
52
  "types": "./dist/auth-adapter.d.mts",
@@ -139,7 +149,7 @@
139
149
  "drizzle-orm": "^0.45.1",
140
150
  "tsdown": "^0.21.9",
141
151
  "typescript": "^6",
142
- "vitest": "^3.2.4"
152
+ "vitest": "3.2.7"
143
153
  },
144
154
  "scripts": {
145
155
  "build": "tsdown",