@memberjunction/integration-pk-classifier 0.0.1 → 5.39.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/SoftPKClassifier.d.ts +141 -0
- package/dist/SoftPKClassifier.d.ts.map +1 -0
- package/dist/SoftPKClassifier.js +278 -0
- package/dist/SoftPKClassifier.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -7
- package/README.md +0 -45
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { MJIntegrationObjectEntity, MJIntegrationObjectFieldEntity } from '@memberjunction/core-entities';
|
|
2
|
+
/** Strategy that produced the classifier's nominee, when one exists. */
|
|
3
|
+
export type PKClassifierStrategy = 'universal-convention' | 'naming-heuristic' | 'statistical' | 'composite' | 'llm' | 'synthetic' | 'none';
|
|
4
|
+
/**
|
|
5
|
+
* Canonical name of the synthetic identity-hash column nominated by the
|
|
6
|
+
* synthetic-fallback tier. The framework materializes this column (an identity
|
|
7
|
+
* hash of the record) so a table that has no provable natural PK is still
|
|
8
|
+
* syncable (plan.md §4).
|
|
9
|
+
*/
|
|
10
|
+
export declare const SYNTHETIC_PK_FIELD_NAME: "__mj_integration_IdentityHash";
|
|
11
|
+
/** Result returned by SoftPKClassifier.Classify(). */
|
|
12
|
+
export interface PKClassifierResult {
|
|
13
|
+
/** Whether the classifier reached a confident nominee. */
|
|
14
|
+
Confident: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Field name of the PK nominee, when Confident=true.
|
|
17
|
+
* - Single-column natural PK → the resolving column's name.
|
|
18
|
+
* - Synthetic fallback → SYNTHETIC_PK_FIELD_NAME.
|
|
19
|
+
* - Composite key → the first member of NomineeFields (for callers that
|
|
20
|
+
* only read a single name); NomineeFields carries the full set.
|
|
21
|
+
*/
|
|
22
|
+
Nominee?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Ordered set of field names that together form the key. Populated for the
|
|
25
|
+
* composite strategy (the minimal unique+stable column set). For single-column
|
|
26
|
+
* and synthetic strategies this is the one-element set matching Nominee.
|
|
27
|
+
*/
|
|
28
|
+
NomineeFields?: string[];
|
|
29
|
+
/** Confidence score 0..1. */
|
|
30
|
+
Confidence: number;
|
|
31
|
+
/** Which strategy in the cascade produced the nominee. */
|
|
32
|
+
Strategy: PKClassifierStrategy;
|
|
33
|
+
/** Human-readable reason — used in audit trail + structured progress events. */
|
|
34
|
+
Reason: string;
|
|
35
|
+
}
|
|
36
|
+
/** Options passed per Classify() call. */
|
|
37
|
+
export interface ClassifyOptions {
|
|
38
|
+
/** The IntegrationObject being classified. */
|
|
39
|
+
object: MJIntegrationObjectEntity;
|
|
40
|
+
/** All IOF rows for that object. */
|
|
41
|
+
fields: MJIntegrationObjectFieldEntity[];
|
|
42
|
+
/**
|
|
43
|
+
* Optional vendor-wide universal PK hint (e.g. "all HubSpot object PKs are `id`").
|
|
44
|
+
* When set and a matching field exists, returned with strategy='universal-convention'
|
|
45
|
+
* and high confidence. Agent populates this from research.
|
|
46
|
+
*/
|
|
47
|
+
universalConvention?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Optional pre-fetched sample rows for statistical uniqueness check.
|
|
50
|
+
* When omitted, the statistical step is skipped (no chicken-and-egg fetching).
|
|
51
|
+
*/
|
|
52
|
+
sampleRows?: Array<Record<string, unknown>>;
|
|
53
|
+
/**
|
|
54
|
+
* Optional LLM callback for the one-shot fallback. When omitted, the LLM step
|
|
55
|
+
* is skipped and the classifier returns Confident=false rather than guessing.
|
|
56
|
+
*/
|
|
57
|
+
llmInference?: LLMOneShotCallback;
|
|
58
|
+
/** Confidence floor for Confident=true. Defaults to 0.7. */
|
|
59
|
+
confidenceFloor?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Largest column-set size the composite-key uniqueness scan will try when
|
|
62
|
+
* no single column is unique. Defaults to 3 (try 2-col then 3-col sets).
|
|
63
|
+
* Only consulted when sampleRows are present.
|
|
64
|
+
*/
|
|
65
|
+
maxCompositeKeySize?: number;
|
|
66
|
+
/**
|
|
67
|
+
* When true (default), and every other tier fails to find/prove a PK, the
|
|
68
|
+
* classifier returns a Confident synthetic verdict nominating
|
|
69
|
+
* SYNTHETIC_PK_FIELD_NAME — an identity-hash column the framework
|
|
70
|
+
* materializes so NO table is left without a PK (plan.md §4). Set false to
|
|
71
|
+
* opt out and receive the honest 'none' verdict instead.
|
|
72
|
+
*/
|
|
73
|
+
syntheticFallback?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** Callback shape for the one-shot LLM step. */
|
|
76
|
+
export type LLMOneShotCallback = (prompt: LLMPrompt) => Promise<LLMResponse>;
|
|
77
|
+
export interface LLMPrompt {
|
|
78
|
+
objectName: string;
|
|
79
|
+
fields: Array<{
|
|
80
|
+
name: string;
|
|
81
|
+
type?: string | null;
|
|
82
|
+
isUnique?: boolean | null;
|
|
83
|
+
length?: number | null;
|
|
84
|
+
}>;
|
|
85
|
+
sampleSnippet?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface LLMResponse {
|
|
88
|
+
nominee: string | null;
|
|
89
|
+
confidence: number;
|
|
90
|
+
reason: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lightweight PK classifier. Cascade (each step is independent; first confident
|
|
94
|
+
* nominee wins):
|
|
95
|
+
*
|
|
96
|
+
* 1. Universal convention — vendor-wide hint matches a field
|
|
97
|
+
* 2. Naming heuristic — common PK names (id, ID, <ObjectName>Id, …)
|
|
98
|
+
* 3. Statistical uniqueness — sample rows: exactly one column is unique+non-null
|
|
99
|
+
* 4. One-shot LLM — single inference; no iteration; honest 'none'
|
|
100
|
+
*
|
|
101
|
+
* Returns Confident=false when nothing is confident enough. The pipeline then
|
|
102
|
+
* leaves the IO row PK-less; no `__mj.Entity` is created for it until a PK
|
|
103
|
+
* resolves (the runtime D7 rule). No fabrication.
|
|
104
|
+
*/
|
|
105
|
+
export declare class SoftPKClassifier {
|
|
106
|
+
Classify(opts: ClassifyOptions): Promise<PKClassifierResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Terminal tier. When syntheticFallback is on (default), nominates the
|
|
109
|
+
* synthetic identity-hash column so no table is left PK-less (plan.md §4).
|
|
110
|
+
* When off, returns the honest 'none' verdict.
|
|
111
|
+
*/
|
|
112
|
+
private finalVerdict;
|
|
113
|
+
/** Case-insensitive equality with simple whitespace tolerance. */
|
|
114
|
+
private nameMatches;
|
|
115
|
+
/** Common PK naming patterns. Order matters — most-specific first. */
|
|
116
|
+
private namingHeuristic;
|
|
117
|
+
/** Sample uniqueness: exactly one field is unique + non-null across the sample. */
|
|
118
|
+
private statisticalUniqueness;
|
|
119
|
+
/**
|
|
120
|
+
* Composite-key uniqueness: when no single column uniquely identifies a row,
|
|
121
|
+
* search for the SMALLEST set of columns (size 2..maxSetSize) whose
|
|
122
|
+
* concatenated values are unique + non-null across the sample. Returns the
|
|
123
|
+
* minimal such set; among sets of equal size, the one over the fewest-
|
|
124
|
+
* distinct-but-still-unique columns wins (deterministic by field order).
|
|
125
|
+
*/
|
|
126
|
+
private compositeUniqueness;
|
|
127
|
+
/** Columns that are non-null across every sample row (PK members must be non-null). */
|
|
128
|
+
private stableColumns;
|
|
129
|
+
/** First combination of `size` columns whose concatenated values are all distinct. */
|
|
130
|
+
private firstUniqueCombination;
|
|
131
|
+
/** Whether the concatenated values of `combo` are distinct across all rows. */
|
|
132
|
+
private isCombinationUnique;
|
|
133
|
+
/** Lazily yield all k-combinations of `items`, preserving input order. */
|
|
134
|
+
private combinations;
|
|
135
|
+
/** A value counts as present (non-null) for PK purposes. */
|
|
136
|
+
private isPresent;
|
|
137
|
+
/** Very simple singularization — sufficient for common cases (Members→Member, Companies→Companie isn't perfect but covers most). */
|
|
138
|
+
private singularize;
|
|
139
|
+
private escapeRe;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=SoftPKClassifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SoftPKClassifier.d.ts","sourceRoot":"","sources":["../src/SoftPKClassifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,yBAAyB,EACzB,8BAA8B,EACjC,MAAM,+BAA+B,CAAC;AAEvC,wEAAwE;AACxE,MAAM,MAAM,oBAAoB,GAC1B,sBAAsB,GACtB,kBAAkB,GAClB,aAAa,GACb,WAAW,GACX,KAAK,GACL,WAAW,GACX,MAAM,CAAC;AAEb;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAG,+BAAwC,CAAC;AAEhF,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IAC/B,0DAA0D;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,0CAA0C;AAC1C,MAAM,WAAW,eAAe;IAC5B,8CAA8C;IAC9C,MAAM,EAAE,yBAAyB,CAAC;IAClC,oCAAoC;IACpC,MAAM,EAAE,8BAA8B,EAAE,CAAC;IACzC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,gDAAgD;AAChD,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAC7E,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACzG,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAgB;IACZ,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAmGzE;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAoBpB,kEAAkE;IAClE,OAAO,CAAC,WAAW;IAInB,sEAAsE;IACtE,OAAO,CAAC,eAAe;IAyBvB,mFAAmF;IACnF,OAAO,CAAC,qBAAqB;IA6B7B;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAqB3B,uFAAuF;IACvF,OAAO,CAAC,aAAa;IASrB,sFAAsF;IACtF,OAAO,CAAC,sBAAsB;IAW9B,+EAA+E;IAC/E,OAAO,CAAC,mBAAmB;IAU3B,0EAA0E;IAC1E,OAAO,CAAE,YAAY;IAcrB,4DAA4D;IAC5D,OAAO,CAAC,SAAS;IAIjB,oIAAoI;IACpI,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,QAAQ;CAGnB"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical name of the synthetic identity-hash column nominated by the
|
|
3
|
+
* synthetic-fallback tier. The framework materializes this column (an identity
|
|
4
|
+
* hash of the record) so a table that has no provable natural PK is still
|
|
5
|
+
* syncable (plan.md §4).
|
|
6
|
+
*/
|
|
7
|
+
export const SYNTHETIC_PK_FIELD_NAME = '__mj_integration_IdentityHash';
|
|
8
|
+
/**
|
|
9
|
+
* Lightweight PK classifier. Cascade (each step is independent; first confident
|
|
10
|
+
* nominee wins):
|
|
11
|
+
*
|
|
12
|
+
* 1. Universal convention — vendor-wide hint matches a field
|
|
13
|
+
* 2. Naming heuristic — common PK names (id, ID, <ObjectName>Id, …)
|
|
14
|
+
* 3. Statistical uniqueness — sample rows: exactly one column is unique+non-null
|
|
15
|
+
* 4. One-shot LLM — single inference; no iteration; honest 'none'
|
|
16
|
+
*
|
|
17
|
+
* Returns Confident=false when nothing is confident enough. The pipeline then
|
|
18
|
+
* leaves the IO row PK-less; no `__mj.Entity` is created for it until a PK
|
|
19
|
+
* resolves (the runtime D7 rule). No fabrication.
|
|
20
|
+
*/
|
|
21
|
+
export class SoftPKClassifier {
|
|
22
|
+
async Classify(opts) {
|
|
23
|
+
const floor = opts.confidenceFloor ?? 0.7;
|
|
24
|
+
// 1) Universal convention
|
|
25
|
+
if (opts.universalConvention) {
|
|
26
|
+
const m = opts.fields.find(f => this.nameMatches(f.Name, opts.universalConvention));
|
|
27
|
+
if (m) {
|
|
28
|
+
return {
|
|
29
|
+
Confident: true,
|
|
30
|
+
Nominee: m.Name,
|
|
31
|
+
Confidence: 0.95,
|
|
32
|
+
Strategy: 'universal-convention',
|
|
33
|
+
Reason: `Vendor universal convention "${opts.universalConvention}" matched field "${m.Name}".`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// 2) Naming heuristic
|
|
38
|
+
const namingMatch = this.namingHeuristic(opts.object.Name, opts.fields);
|
|
39
|
+
if (namingMatch) {
|
|
40
|
+
return {
|
|
41
|
+
Confident: true,
|
|
42
|
+
Nominee: namingMatch.fieldName,
|
|
43
|
+
Confidence: namingMatch.confidence,
|
|
44
|
+
Strategy: 'naming-heuristic',
|
|
45
|
+
Reason: namingMatch.reason,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// 3) Statistical (when sample rows present)
|
|
49
|
+
if (opts.sampleRows && opts.sampleRows.length > 0) {
|
|
50
|
+
const statMatch = this.statisticalUniqueness(opts.fields, opts.sampleRows);
|
|
51
|
+
if (statMatch && statMatch.confidence >= floor) {
|
|
52
|
+
return {
|
|
53
|
+
Confident: true,
|
|
54
|
+
Nominee: statMatch.fieldName,
|
|
55
|
+
NomineeFields: [statMatch.fieldName],
|
|
56
|
+
Confidence: statMatch.confidence,
|
|
57
|
+
Strategy: 'statistical',
|
|
58
|
+
Reason: statMatch.reason,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 3b) Composite uniqueness (when no single column is unique) — try minimal
|
|
63
|
+
// 2- then 3-col concatenated key sets; smallest unique+stable set wins.
|
|
64
|
+
if (opts.sampleRows && opts.sampleRows.length > 0) {
|
|
65
|
+
const maxSetSize = opts.maxCompositeKeySize ?? 3;
|
|
66
|
+
const compMatch = this.compositeUniqueness(opts.fields, opts.sampleRows, maxSetSize);
|
|
67
|
+
if (compMatch && compMatch.confidence >= floor) {
|
|
68
|
+
return {
|
|
69
|
+
Confident: true,
|
|
70
|
+
Nominee: compMatch.fieldNames[0],
|
|
71
|
+
NomineeFields: compMatch.fieldNames,
|
|
72
|
+
Confidence: compMatch.confidence,
|
|
73
|
+
Strategy: 'composite',
|
|
74
|
+
Reason: compMatch.reason,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 4) LLM one-shot (when callback provided)
|
|
79
|
+
if (opts.llmInference) {
|
|
80
|
+
try {
|
|
81
|
+
const llm = await opts.llmInference({
|
|
82
|
+
objectName: opts.object.Name,
|
|
83
|
+
fields: opts.fields.map(f => ({
|
|
84
|
+
name: f.Name,
|
|
85
|
+
type: f.Type ?? null,
|
|
86
|
+
isUnique: f.IsUniqueKey,
|
|
87
|
+
length: f.Length ?? null,
|
|
88
|
+
})),
|
|
89
|
+
sampleSnippet: opts.sampleRows ? JSON.stringify(opts.sampleRows.slice(0, 3)) : undefined,
|
|
90
|
+
});
|
|
91
|
+
if (llm.nominee && llm.confidence >= floor) {
|
|
92
|
+
const exists = opts.fields.some(f => f.Name === llm.nominee);
|
|
93
|
+
if (exists) {
|
|
94
|
+
return {
|
|
95
|
+
Confident: true,
|
|
96
|
+
Nominee: llm.nominee,
|
|
97
|
+
NomineeFields: [llm.nominee],
|
|
98
|
+
Confidence: llm.confidence,
|
|
99
|
+
Strategy: 'llm',
|
|
100
|
+
Reason: llm.reason || 'LLM one-shot classification.',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
// LLM failures never crash the pipeline. Fall through to the synthetic
|
|
107
|
+
// fallback (when enabled) so the table is still rescued; otherwise 'none'.
|
|
108
|
+
const llmFailNote = `LLM inference failed: ${err instanceof Error ? err.message : String(err)}.`;
|
|
109
|
+
return this.finalVerdict(opts, llmFailNote);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// 5) Synthetic fallback (default) or honest no-PK.
|
|
113
|
+
return this.finalVerdict(opts);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Terminal tier. When syntheticFallback is on (default), nominates the
|
|
117
|
+
* synthetic identity-hash column so no table is left PK-less (plan.md §4).
|
|
118
|
+
* When off, returns the honest 'none' verdict.
|
|
119
|
+
*/
|
|
120
|
+
finalVerdict(opts, prefix) {
|
|
121
|
+
const note = prefix ? `${prefix} ` : '';
|
|
122
|
+
if (opts.syntheticFallback ?? true) {
|
|
123
|
+
return {
|
|
124
|
+
Confident: true,
|
|
125
|
+
Nominee: SYNTHETIC_PK_FIELD_NAME,
|
|
126
|
+
NomineeFields: [SYNTHETIC_PK_FIELD_NAME],
|
|
127
|
+
Confidence: 0.7,
|
|
128
|
+
Strategy: 'synthetic',
|
|
129
|
+
Reason: `${note}No natural PK proven via universal-convention, naming, statistical, composite, or LLM strategies. Falling back to a synthetic identity-hash key "${SYNTHETIC_PK_FIELD_NAME}" so the table remains syncable (plan.md §4).`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
Confident: false,
|
|
134
|
+
Confidence: 0,
|
|
135
|
+
Strategy: 'none',
|
|
136
|
+
Reason: `${note}No confident PK candidate via universal-convention, naming, statistical, composite, or LLM strategies, and synthetic fallback is disabled. IO row persists; MJ entity not generated until a PK resolves.`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Case-insensitive equality with simple whitespace tolerance. */
|
|
140
|
+
nameMatches(actual, hint) {
|
|
141
|
+
return actual.trim().toLowerCase() === hint.trim().toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
/** Common PK naming patterns. Order matters — most-specific first. */
|
|
144
|
+
namingHeuristic(objectName, fields) {
|
|
145
|
+
const sing = this.singularize(objectName);
|
|
146
|
+
const candidates = [
|
|
147
|
+
{ pattern: new RegExp(`^${this.escapeRe(sing)}_?id$`, 'i'), confidence: 0.9, reasonTpl: 'Field matches "<ObjectNameSingular>Id" pattern' },
|
|
148
|
+
{ pattern: new RegExp(`^${this.escapeRe(objectName)}_?id$`, 'i'), confidence: 0.88, reasonTpl: 'Field matches "<ObjectName>Id" pattern' },
|
|
149
|
+
{ pattern: /^id$/i, confidence: 0.85, reasonTpl: 'Field is named "id" (common universal PK)' },
|
|
150
|
+
{ pattern: /^uuid$/i, confidence: 0.8, reasonTpl: 'Field is named "uuid"' },
|
|
151
|
+
{ pattern: /^guid$/i, confidence: 0.75, reasonTpl: 'Field is named "guid"' },
|
|
152
|
+
];
|
|
153
|
+
for (const c of candidates) {
|
|
154
|
+
const m = fields.find(f => c.pattern.test(f.Name));
|
|
155
|
+
if (m) {
|
|
156
|
+
return {
|
|
157
|
+
fieldName: m.Name,
|
|
158
|
+
confidence: c.confidence,
|
|
159
|
+
reason: c.reasonTpl.replace('<ObjectName>', objectName).replace('<ObjectNameSingular>', sing),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
/** Sample uniqueness: exactly one field is unique + non-null across the sample. */
|
|
166
|
+
statisticalUniqueness(fields, rows) {
|
|
167
|
+
const candidates = [];
|
|
168
|
+
for (const f of fields) {
|
|
169
|
+
const values = rows.map(r => r[f.Name]);
|
|
170
|
+
const nonNull = values.filter(v => this.isPresent(v));
|
|
171
|
+
const nullRatio = 1 - (nonNull.length / rows.length);
|
|
172
|
+
if (nullRatio > 0)
|
|
173
|
+
continue; // PK fields are non-null
|
|
174
|
+
const distinct = new Set(nonNull.map(v => String(v)));
|
|
175
|
+
const uniqueRatio = distinct.size / nonNull.length;
|
|
176
|
+
if (uniqueRatio === 1.0) {
|
|
177
|
+
candidates.push({ fieldName: f.Name, uniqueRatio, nullRatio });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (candidates.length === 0)
|
|
181
|
+
return undefined;
|
|
182
|
+
// Single uniquely-identifying column → strong signal
|
|
183
|
+
if (candidates.length === 1) {
|
|
184
|
+
return {
|
|
185
|
+
fieldName: candidates[0].fieldName,
|
|
186
|
+
confidence: 0.85,
|
|
187
|
+
reason: `Sole field unique + non-null across ${rows.length} sample rows.`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Multiple unique columns → ambiguous; defer to a more specific signal
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Composite-key uniqueness: when no single column uniquely identifies a row,
|
|
195
|
+
* search for the SMALLEST set of columns (size 2..maxSetSize) whose
|
|
196
|
+
* concatenated values are unique + non-null across the sample. Returns the
|
|
197
|
+
* minimal such set; among sets of equal size, the one over the fewest-
|
|
198
|
+
* distinct-but-still-unique columns wins (deterministic by field order).
|
|
199
|
+
*/
|
|
200
|
+
compositeUniqueness(fields, rows, maxSetSize) {
|
|
201
|
+
const stable = this.stableColumns(fields, rows);
|
|
202
|
+
if (stable.length < 2)
|
|
203
|
+
return undefined;
|
|
204
|
+
const cap = Math.min(Math.max(maxSetSize, 2), stable.length);
|
|
205
|
+
for (let size = 2; size <= cap; size++) {
|
|
206
|
+
const winner = this.firstUniqueCombination(stable, rows, size);
|
|
207
|
+
if (winner) {
|
|
208
|
+
return {
|
|
209
|
+
fieldNames: winner,
|
|
210
|
+
confidence: 0.8,
|
|
211
|
+
reason: `Composite key of ${winner.length} columns [${winner.join(', ')}] is unique + non-null across ${rows.length} sample rows (no single column was unique).`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
/** Columns that are non-null across every sample row (PK members must be non-null). */
|
|
218
|
+
stableColumns(fields, rows) {
|
|
219
|
+
return fields
|
|
220
|
+
.filter(f => rows.every(r => this.isPresent(r[f.Name])))
|
|
221
|
+
.map(f => f.Name);
|
|
222
|
+
}
|
|
223
|
+
/** First combination of `size` columns whose concatenated values are all distinct. */
|
|
224
|
+
firstUniqueCombination(columns, rows, size) {
|
|
225
|
+
for (const combo of this.combinations(columns, size)) {
|
|
226
|
+
if (this.isCombinationUnique(combo, rows))
|
|
227
|
+
return combo;
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
/** Whether the concatenated values of `combo` are distinct across all rows. */
|
|
232
|
+
isCombinationUnique(combo, rows) {
|
|
233
|
+
const seen = new Set();
|
|
234
|
+
for (const r of rows) {
|
|
235
|
+
const key = combo.map(c => `${String(r[c])}`).join('');
|
|
236
|
+
if (seen.has(key))
|
|
237
|
+
return false;
|
|
238
|
+
seen.add(key);
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
/** Lazily yield all k-combinations of `items`, preserving input order. */
|
|
243
|
+
*combinations(items, k) {
|
|
244
|
+
const n = items.length;
|
|
245
|
+
if (k > n || k <= 0)
|
|
246
|
+
return;
|
|
247
|
+
const idx = Array.from({ length: k }, (_, i) => i);
|
|
248
|
+
while (true) {
|
|
249
|
+
yield idx.map(i => items[i]);
|
|
250
|
+
let p = k - 1;
|
|
251
|
+
while (p >= 0 && idx[p] === n - k + p)
|
|
252
|
+
p--;
|
|
253
|
+
if (p < 0)
|
|
254
|
+
return;
|
|
255
|
+
idx[p]++;
|
|
256
|
+
for (let j = p + 1; j < k; j++)
|
|
257
|
+
idx[j] = idx[j - 1] + 1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/** A value counts as present (non-null) for PK purposes. */
|
|
261
|
+
isPresent(v) {
|
|
262
|
+
return v !== null && v !== undefined && v !== '';
|
|
263
|
+
}
|
|
264
|
+
/** Very simple singularization — sufficient for common cases (Members→Member, Companies→Companie isn't perfect but covers most). */
|
|
265
|
+
singularize(name) {
|
|
266
|
+
if (/ies$/i.test(name))
|
|
267
|
+
return name.slice(0, -3) + 'y';
|
|
268
|
+
if (/ses$/i.test(name))
|
|
269
|
+
return name.slice(0, -2);
|
|
270
|
+
if (/s$/i.test(name) && name.length > 1)
|
|
271
|
+
return name.slice(0, -1);
|
|
272
|
+
return name;
|
|
273
|
+
}
|
|
274
|
+
escapeRe(s) {
|
|
275
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=SoftPKClassifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SoftPKClassifier.js","sourceRoot":"","sources":["../src/SoftPKClassifier.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,+BAAwC,CAAC;AAiFhF;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,gBAAgB;IAClB,KAAK,CAAC,QAAQ,CAAC,IAAqB;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,IAAI,GAAG,CAAC;QAE1C,0BAA0B;QAC1B,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAoB,CAAC,CAAC,CAAC;YACrF,IAAI,CAAC,EAAE,CAAC;gBACJ,OAAO;oBACH,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,CAAC,CAAC,IAAI;oBACf,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE,sBAAsB;oBAChC,MAAM,EAAE,gCAAgC,IAAI,CAAC,mBAAmB,oBAAoB,CAAC,CAAC,IAAI,IAAI;iBACjG,CAAC;YACN,CAAC;QACL,CAAC;QAED,sBAAsB;QACtB,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACxE,IAAI,WAAW,EAAE,CAAC;YACd,OAAO;gBACH,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,WAAW,CAAC,SAAS;gBAC9B,UAAU,EAAE,WAAW,CAAC,UAAU;gBAClC,QAAQ,EAAE,kBAAkB;gBAC5B,MAAM,EAAE,WAAW,CAAC,MAAM;aAC7B,CAAC;QACN,CAAC;QAED,4CAA4C;QAC5C,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAC3E,IAAI,SAAS,IAAI,SAAS,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC;gBAC7C,OAAO;oBACH,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,SAAS,CAAC,SAAS;oBAC5B,aAAa,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC;oBACpC,UAAU,EAAE,SAAS,CAAC,UAAU;oBAChC,QAAQ,EAAE,aAAa;oBACvB,MAAM,EAAE,SAAS,CAAC,MAAM;iBAC3B,CAAC;YACN,CAAC;QACL,CAAC;QAED,2EAA2E;QAC3E,4EAA4E;QAC5E,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,IAAI,CAAC,CAAC;YACjD,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACrF,IAAI,SAAS,IAAI,SAAS,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC;gBAC7C,OAAO;oBACH,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;oBAChC,aAAa,EAAE,SAAS,CAAC,UAAU;oBACnC,UAAU,EAAE,SAAS,CAAC,UAAU;oBAChC,QAAQ,EAAE,WAAW;oBACrB,MAAM,EAAE,SAAS,CAAC,MAAM;iBAC3B,CAAC;YACN,CAAC;QACL,CAAC;QAED,2CAA2C;QAC3C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC;gBACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;oBAChC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;oBAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBAC1B,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;wBACpB,QAAQ,EAAE,CAAC,CAAC,WAAW;wBACvB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;qBAC3B,CAAC,CAAC;oBACH,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;iBAC3F,CAAC,CAAC;gBACH,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC;oBACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;oBAC7D,IAAI,MAAM,EAAE,CAAC;wBACT,OAAO;4BACH,SAAS,EAAE,IAAI;4BACf,OAAO,EAAE,GAAG,CAAC,OAAO;4BACpB,aAAa,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;4BAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;4BAC1B,QAAQ,EAAE,KAAK;4BACf,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,8BAA8B;yBACvD,CAAC;oBACN,CAAC;gBACL,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,uEAAuE;gBACvE,2EAA2E;gBAC3E,MAAM,WAAW,GAAG,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBACjG,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAChD,CAAC;QACL,CAAC;QAED,mDAAmD;QACnD,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACK,YAAY,CAAC,IAAqB,EAAE,MAAe;QACvD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,EAAE,CAAC;YACjC,OAAO;gBACH,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,uBAAuB;gBAChC,aAAa,EAAE,CAAC,uBAAuB,CAAC;gBACxC,UAAU,EAAE,GAAG;gBACf,QAAQ,EAAE,WAAW;gBACrB,MAAM,EAAE,GAAG,IAAI,oJAAoJ,uBAAuB,+CAA+C;aAC5O,CAAC;QACN,CAAC;QACD,OAAO;YACH,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE,CAAC;YACb,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,GAAG,IAAI,0MAA0M;SAC5N,CAAC;IACN,CAAC;IAED,kEAAkE;IAC1D,WAAW,CAAC,MAAc,EAAE,IAAY;QAC5C,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrE,CAAC;IAED,sEAAsE;IAC9D,eAAe,CACnB,UAAkB,EAClB,MAAwC;QAExC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAsE;YAClF,EAAE,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,gDAAgD,EAAE;YAC1I,EAAE,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,wCAAwC,EAAE;YACzI,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,2CAA2C,EAAE;YAC9F,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,uBAAuB,EAAE;YAC3E,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,uBAAuB,EAAE;SAC/E,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACnD,IAAI,CAAC,EAAE,CAAC;gBACJ,OAAO;oBACH,SAAS,EAAE,CAAC,CAAC,IAAI;oBACjB,UAAU,EAAE,CAAC,CAAC,UAAU;oBACxB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,sBAAsB,EAAE,IAAI,CAAC;iBAChG,CAAC;YACN,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,mFAAmF;IAC3E,qBAAqB,CACzB,MAAwC,EACxC,IAAoC;QAEpC,MAAM,UAAU,GAAyE,EAAE,CAAC;QAC5F,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACtD,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YACrD,IAAI,SAAS,GAAG,CAAC;gBAAE,SAAS,CAAC,yBAAyB;YACtD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtD,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;YACnD,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;gBACtB,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;YACnE,CAAC;QACL,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9C,qDAAqD;QACrD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACH,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;gBAClC,UAAU,EAAE,IAAI;gBAChB,MAAM,EAAE,uCAAuC,IAAI,CAAC,MAAM,eAAe;aAC5E,CAAC;QACN,CAAC;QACD,uEAAuE;QACvE,OAAO,SAAS,CAAC;IACrB,CAAC;IAED;;;;;;OAMG;IACK,mBAAmB,CACvB,MAAwC,EACxC,IAAoC,EACpC,UAAkB;QAElB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7D,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YAC/D,IAAI,MAAM,EAAE,CAAC;gBACT,OAAO;oBACH,UAAU,EAAE,MAAM;oBAClB,UAAU,EAAE,GAAG;oBACf,MAAM,EAAE,oBAAoB,MAAM,CAAC,MAAM,aAAa,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,iCAAiC,IAAI,CAAC,MAAM,6CAA6C;iBACnK,CAAC;YACN,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,uFAAuF;IAC/E,aAAa,CACjB,MAAwC,EACxC,IAAoC;QAEpC,OAAO,MAAM;aACR,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACvD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,sFAAsF;IAC9E,sBAAsB,CAC1B,OAAiB,EACjB,IAAoC,EACpC,IAAY;QAEZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;QAC5D,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,+EAA+E;IACvE,mBAAmB,CAAC,KAAe,EAAE,IAAoC;QAC7E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,0EAA0E;IAClE,CAAC,YAAY,CAAC,KAAe,EAAE,CAAS;QAC5C,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,EAAE,CAAC;YACV,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO;YAClB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACT,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;gBAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;IAED,4DAA4D;IACpD,SAAS,CAAC,CAAU;QACxB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;IACrD,CAAC;IAED,oIAAoI;IAC5H,WAAW,CAAC,IAAY;QAC5B,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QACvD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,QAAQ,CAAC,CAAS;QACtB,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;CACJ"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAClF,YAAY,EACR,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,SAAS,EACT,WAAW,GACd,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/integration-pk-classifier",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "5.39.0",
|
|
5
|
+
"description": "Lightweight soft-PK classifier for integration objects. Cascades: universal convention (vendor-level hint) → naming heuristic → statistical uniqueness sampling → one-shot LLM. Built from scratch — does NOT depend on DBAutoDoc. Narrow scope: PK only. Honest: returns no candidate when nothing is confident enough — the framework then leaves the entity PK-less rather than synthesizing.",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"/dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && tsc-alias -f",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@memberjunction/core": "5.39.0",
|
|
18
|
+
"@memberjunction/core-entities": "5.39.0",
|
|
19
|
+
"@memberjunction/global": "5.39.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.9.3",
|
|
23
|
+
"tsc-alias": "^1.8.16",
|
|
24
|
+
"vitest": "^4.0.18"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/MemberJunction/MJ"
|
|
29
|
+
},
|
|
30
|
+
"license": "ISC",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
}
|
|
10
34
|
}
|
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @memberjunction/integration-pk-classifier
|
|
2
|
-
|
|
3
|
-
## ⚠️ IMPORTANT NOTICE ⚠️
|
|
4
|
-
|
|
5
|
-
**This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
|
|
6
|
-
|
|
7
|
-
This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@memberjunction/integration-pk-classifier`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|