@schmock/faker 1.9.4 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+ import type { FieldMapping } from "./field-mappings.js";
3
+ /**
4
+ * Split a field name (camelCase, snake_case, kebab-case) into lowercase tokens.
5
+ * "userFirstName" → ["user", "first", "name"]
6
+ * "created_at" → ["created", "at"]
7
+ * "HTMLParser" → ["html", "parser"]
8
+ * "is_active" → ["is", "active"]
9
+ */
10
+ export declare function tokenizeFieldName(name: string): string[];
11
+ /**
12
+ * Score how well a set of keyword tokens matches field name tokens.
13
+ * Returns 0-1:
14
+ * 1.0 — exact full match (joined tokens equal joined keywords)
15
+ * 0.9 — all keyword tokens found in field tokens with high coverage (>50%)
16
+ * 0.8 — field ends with keyword tokens
17
+ * 0.7 — substring match (keyword appears in joined field name)
18
+ * 0.65 — all keyword tokens found but low coverage (<=50%)
19
+ */
20
+ export declare function scoreMatch(fieldTokens: string[], keywords: string[]): number;
21
+ interface MatchResult {
22
+ mapping: FieldMapping;
23
+ score: number;
24
+ }
25
+ /**
26
+ * Find the best field mapping for a given field name and schema.
27
+ * Returns the highest-scoring match above its threshold, or undefined.
28
+ */
29
+ export declare function findBestMapping(fieldName: string, schema: JSONSchema7, mappings?: FieldMapping[]): MatchResult | undefined;
30
+ export {};
31
+ //# sourceMappingURL=field-name-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-name-matcher.d.ts","sourceRoot":"","sources":["../src/field-name-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAsC5E;AAED,UAAU,WAAW;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,EACnB,QAAQ,GAAE,YAAY,EAAuB,GAC5C,WAAW,GAAG,SAAS,CA6EzB"}
@@ -0,0 +1,136 @@
1
+ import { ALL_FIELD_MAPPINGS } from "./field-mappings.js";
2
+ /**
3
+ * Split a field name (camelCase, snake_case, kebab-case) into lowercase tokens.
4
+ * "userFirstName" → ["user", "first", "name"]
5
+ * "created_at" → ["created", "at"]
6
+ * "HTMLParser" → ["html", "parser"]
7
+ * "is_active" → ["is", "active"]
8
+ */
9
+ export function tokenizeFieldName(name) {
10
+ // Split on _ and -
11
+ const parts = name.split(/[_-]/);
12
+ const tokens = [];
13
+ for (const part of parts) {
14
+ if (!part)
15
+ continue;
16
+ // Split camelCase and consecutive uppercase (e.g., HTMLParser → HTML, Parser)
17
+ const camelTokens = part
18
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2")
19
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
20
+ .split("_");
21
+ for (const t of camelTokens) {
22
+ if (t)
23
+ tokens.push(t.toLowerCase());
24
+ }
25
+ }
26
+ return tokens;
27
+ }
28
+ /**
29
+ * Score how well a set of keyword tokens matches field name tokens.
30
+ * Returns 0-1:
31
+ * 1.0 — exact full match (joined tokens equal joined keywords)
32
+ * 0.9 — all keyword tokens found in field tokens with high coverage (>50%)
33
+ * 0.8 — field ends with keyword tokens
34
+ * 0.7 — substring match (keyword appears in joined field name)
35
+ * 0.65 — all keyword tokens found but low coverage (<=50%)
36
+ */
37
+ export function scoreMatch(fieldTokens, keywords) {
38
+ const fieldJoined = fieldTokens.join("");
39
+ let bestScore = 0;
40
+ // Try each keyword variant, keep the highest score
41
+ for (const keyword of keywords) {
42
+ const kwTokens = tokenizeFieldName(keyword);
43
+ const kwJoined = kwTokens.join("");
44
+ // Exact match: joined tokens are identical
45
+ if (fieldJoined === kwJoined)
46
+ return 1.0;
47
+ // All keyword tokens present in field tokens with high coverage
48
+ if (kwTokens.length > 0 &&
49
+ kwTokens.every((kt) => fieldTokens.includes(kt))) {
50
+ const coverage = kwTokens.length / fieldTokens.length;
51
+ const score = coverage > 0.5 ? 0.9 : 0.65;
52
+ bestScore = Math.max(bestScore, score);
53
+ }
54
+ // Field ends with keyword tokens
55
+ if (kwTokens.length > 0 && kwTokens.length <= fieldTokens.length) {
56
+ const tail = fieldTokens.slice(-kwTokens.length);
57
+ if (tail.every((t, i) => t === kwTokens[i])) {
58
+ bestScore = Math.max(bestScore, 0.8);
59
+ }
60
+ }
61
+ // Substring match: keyword joined appears in field joined
62
+ if (kwJoined.length >= 3 && fieldJoined.includes(kwJoined)) {
63
+ bestScore = Math.max(bestScore, 0.7);
64
+ }
65
+ }
66
+ return bestScore;
67
+ }
68
+ /**
69
+ * Find the best field mapping for a given field name and schema.
70
+ * Returns the highest-scoring match above its threshold, or undefined.
71
+ */
72
+ export function findBestMapping(fieldName, schema, mappings = ALL_FIELD_MAPPINGS) {
73
+ const schemaAny = schema;
74
+ const schemaType = typeof schema.type === "string" ? schema.type : undefined;
75
+ // Priority: format:uuid always maps to string.uuid
76
+ if (schemaType === "string" && schema.format === "uuid") {
77
+ return {
78
+ mapping: {
79
+ keywords: ["uuid"],
80
+ fakerMethod: "string.uuid",
81
+ schemaType: "string",
82
+ minScore: 0.5,
83
+ },
84
+ score: 1.0,
85
+ };
86
+ }
87
+ // Skip if schema already has pattern, enum, or faker constraint
88
+ if (schemaAny.pattern || schemaAny.enum || schemaAny.faker) {
89
+ return undefined;
90
+ }
91
+ // Skip numeric faker mapping when schema already constrains the range
92
+ const hasNumericConstraints = schema.minimum !== undefined ||
93
+ schema.maximum !== undefined ||
94
+ schema.exclusiveMinimum !== undefined ||
95
+ schema.exclusiveMaximum !== undefined;
96
+ const tokens = tokenizeFieldName(fieldName);
97
+ let best;
98
+ for (const mapping of mappings) {
99
+ // Skip numeric faker mappings when schema has explicit constraints
100
+ if (hasNumericConstraints &&
101
+ (mapping.schemaType === "number" || mapping.schemaType === "integer")) {
102
+ continue;
103
+ }
104
+ // Check schema type constraint
105
+ if (mapping.schemaType && schemaType && mapping.schemaType !== schemaType) {
106
+ const isNumeric = (mapping.schemaType === "number" && schemaType === "integer") ||
107
+ (mapping.schemaType === "integer" && schemaType === "number");
108
+ if (!isNumeric)
109
+ continue;
110
+ }
111
+ const score = scoreMatch(tokens, mapping.keywords);
112
+ if (score >= mapping.minScore && (!best || score > best.score)) {
113
+ best = { mapping, score };
114
+ }
115
+ }
116
+ // ID suffix detection: fields ending in Id/_id with string type → UUID
117
+ // Only if no better match was found or the existing match is weak
118
+ if (schemaType === "string") {
119
+ const lastToken = tokens[tokens.length - 1];
120
+ if (lastToken === "id" && tokens.length > 1) {
121
+ const idScore = 0.8;
122
+ if (!best || best.score < idScore) {
123
+ best = {
124
+ mapping: {
125
+ keywords: ["id"],
126
+ fakerMethod: "string.uuid",
127
+ schemaType: "string",
128
+ minScore: 0.5,
129
+ },
130
+ score: idScore,
131
+ };
132
+ }
133
+ }
134
+ }
135
+ return best;
136
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CA+CvE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,GAAG,GAAG,CA6CxE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CA+CvE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,GAAG,GAAG,CA8CxE"}