@konfeature/ap-email-guessr 0.1.5 → 0.1.7
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/package.json +1 -1
- package/src/lib/common/find-email.d.ts +15 -12
- package/src/lib/common/find-email.d.ts.map +1 -1
- package/src/lib/common/find-email.js +45 -16
- package/src/lib/common/find-email.js.map +1 -1
- package/src/lib/common/patterns.d.ts +9 -0
- package/src/lib/common/patterns.d.ts.map +1 -1
- package/src/lib/common/patterns.js +104 -13
- package/src/lib/common/patterns.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konfeature/ap-email-guessr",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Activepieces piece that finds a person's email from their name and company domain — free Microsoft 365 account checks plus no2bounce verification, with early-stop to minimize credits.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credit-minimizing email search (layers 1 + 2).
|
|
3
3
|
*
|
|
4
|
-
* Candidates arrive pre-ordered by likelihood
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `verify` calls; either
|
|
8
|
-
*
|
|
4
|
+
* Candidates arrive pre-ordered by likelihood. The single most-likely address is
|
|
5
|
+
* probed on its own first, then the remainder is swept in rounds of `batchSize`
|
|
6
|
+
* (1 = strictly sequential). A round is one bulk provider call when the verifier
|
|
7
|
+
* exposes `verifyBatch` (no2bounce), otherwise concurrent `verify` calls; either
|
|
8
|
+
* way the round is read back in candidate order, so the verdict is identical to
|
|
9
|
+
* checking one at a time:
|
|
9
10
|
* - stop at the first `valid` hit (layer 2: early-stop), and
|
|
10
|
-
* - stop
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* - stop as soon as a result proves the domain is accept-all (`catchAll` with a
|
|
12
|
+
* non-`valid` verdict), since a catch-all domain can't disambiguate individual
|
|
13
|
+
* mailboxes — every candidate then gets the same catch-all verdict and we stop
|
|
14
|
+
* paying for more rounds (layer 1). The lone first probe means an accept-all
|
|
15
|
+
* domain is caught for one credit instead of a whole round.
|
|
13
16
|
*
|
|
14
17
|
* no2bounce bills per email with no refund, so a round is paid for the moment it
|
|
15
|
-
* is submitted: `creditsUsed` counts every email
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* is submitted: `creditsUsed` counts every email actually queried, even when an
|
|
19
|
+
* early candidate in the final round is the hit. A larger `batchSize` is thus
|
|
20
|
+
* faster (fewer submit/poll cycles) but coarser on credits. `maxChecks` is a hard
|
|
21
|
+
* ceiling; a `valid` hit on an accept-all domain is reported with reduced
|
|
19
22
|
* confidence and `catchAll: true`.
|
|
20
23
|
*/
|
|
21
24
|
import type { Verifier, VerifyOutcome, VerifyVerdict } from "./verifier";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-email.d.ts","sourceRoot":"","sources":["../../../../src/lib/common/find-email.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"find-email.d.ts","sourceRoot":"","sources":["../../../../src/lib/common/find-email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC7B,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC7B;AAyDD,wBAAsB,SAAS,CAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,gBAAqB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA4E1B"}
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Credit-minimizing email search (layers 1 + 2).
|
|
4
4
|
*
|
|
5
|
-
* Candidates arrive pre-ordered by likelihood
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `verify` calls; either
|
|
9
|
-
*
|
|
5
|
+
* Candidates arrive pre-ordered by likelihood. The single most-likely address is
|
|
6
|
+
* probed on its own first, then the remainder is swept in rounds of `batchSize`
|
|
7
|
+
* (1 = strictly sequential). A round is one bulk provider call when the verifier
|
|
8
|
+
* exposes `verifyBatch` (no2bounce), otherwise concurrent `verify` calls; either
|
|
9
|
+
* way the round is read back in candidate order, so the verdict is identical to
|
|
10
|
+
* checking one at a time:
|
|
10
11
|
* - stop at the first `valid` hit (layer 2: early-stop), and
|
|
11
|
-
* - stop
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* - stop as soon as a result proves the domain is accept-all (`catchAll` with a
|
|
13
|
+
* non-`valid` verdict), since a catch-all domain can't disambiguate individual
|
|
14
|
+
* mailboxes — every candidate then gets the same catch-all verdict and we stop
|
|
15
|
+
* paying for more rounds (layer 1). The lone first probe means an accept-all
|
|
16
|
+
* domain is caught for one credit instead of a whole round.
|
|
14
17
|
*
|
|
15
18
|
* no2bounce bills per email with no refund, so a round is paid for the moment it
|
|
16
|
-
* is submitted: `creditsUsed` counts every email
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* is submitted: `creditsUsed` counts every email actually queried, even when an
|
|
20
|
+
* early candidate in the final round is the hit. A larger `batchSize` is thus
|
|
21
|
+
* faster (fewer submit/poll cycles) but coarser on credits. `maxChecks` is a hard
|
|
22
|
+
* ceiling; a `valid` hit on an accept-all domain is reported with reduced
|
|
20
23
|
* confidence and `catchAll: true`.
|
|
21
24
|
*/
|
|
22
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -41,6 +44,24 @@ function result(found, email, verdict, catchAll, attempts, creditsUsed) {
|
|
|
41
44
|
attempts,
|
|
42
45
|
};
|
|
43
46
|
}
|
|
47
|
+
// Accept-all is a domain property, so every candidate gets the same catch-all
|
|
48
|
+
// verdict — but only the addresses actually queried were billed (zero-cost rows
|
|
49
|
+
// for the rest keep `creditsUsed` honest while `candidatesChecked` covers all).
|
|
50
|
+
function catchAllForAll(candidates, limit, detected, queried, creditsUsed) {
|
|
51
|
+
const billed = new Map(queried.map((o) => [o.email, o]));
|
|
52
|
+
const attempts = candidates.slice(0, limit).map((email) => {
|
|
53
|
+
const prior = billed.get(email);
|
|
54
|
+
return {
|
|
55
|
+
email,
|
|
56
|
+
verdict: "catch_all",
|
|
57
|
+
catchAll: true,
|
|
58
|
+
creditsUsed: prior?.creditsUsed ?? 0,
|
|
59
|
+
reason: detected.reason,
|
|
60
|
+
raw: prior?.raw,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
return result(false, null, "catch_all", true, attempts, creditsUsed);
|
|
64
|
+
}
|
|
44
65
|
async function findEmail(candidates, verifier, options = {}) {
|
|
45
66
|
const stopOnFirstHit = options.stopOnFirstHit ?? true;
|
|
46
67
|
const batchSize = Math.max(1, options.batchSize ?? 1);
|
|
@@ -48,8 +69,12 @@ async function findEmail(candidates, verifier, options = {}) {
|
|
|
48
69
|
const attempts = [];
|
|
49
70
|
let creditsUsed = 0;
|
|
50
71
|
let firstValid = null;
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
// Probe the single most-likely candidate first (one credit), then sweep the
|
|
73
|
+
// rest in rounds of `batchSize`.
|
|
74
|
+
let start = 0;
|
|
75
|
+
while (start < limit) {
|
|
76
|
+
const end = Math.min(start + (start === 0 ? 1 : batchSize), limit);
|
|
77
|
+
const round = candidates.slice(start, end);
|
|
53
78
|
const outcomes = verifier.verifyBatch
|
|
54
79
|
? await verifier.verifyBatch(round)
|
|
55
80
|
: await Promise.all(round.map((email) => verifier.verify(email)));
|
|
@@ -60,8 +85,11 @@ async function findEmail(candidates, verifier, options = {}) {
|
|
|
60
85
|
creditsUsed += outcome.creditsUsed;
|
|
61
86
|
}
|
|
62
87
|
for (const outcome of outcomes) {
|
|
63
|
-
|
|
64
|
-
|
|
88
|
+
// Accept-all (a catch-all, or an address scored undeliverable *on* an
|
|
89
|
+
// accept-all domain): no single mailbox can be confirmed, so stop and
|
|
90
|
+
// give every candidate the same catch-all verdict.
|
|
91
|
+
if (outcome.catchAll === true && outcome.verdict !== "valid") {
|
|
92
|
+
return catchAllForAll(candidates, limit, outcome, attempts, creditsUsed);
|
|
65
93
|
}
|
|
66
94
|
if (outcome.verdict === "valid") {
|
|
67
95
|
if (!firstValid) {
|
|
@@ -72,6 +100,7 @@ async function findEmail(candidates, verifier, options = {}) {
|
|
|
72
100
|
}
|
|
73
101
|
}
|
|
74
102
|
}
|
|
103
|
+
start = end;
|
|
75
104
|
}
|
|
76
105
|
if (firstValid) {
|
|
77
106
|
return result(true, firstValid.email, "valid", firstValid.catchAll ?? false, attempts, creditsUsed);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-email.js","sourceRoot":"","sources":["../../../../src/lib/common/find-email.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"find-email.js","sourceRoot":"","sources":["../../../../src/lib/common/find-email.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
* The order is load-bearing: the verifier checks candidates top-down and stops
|
|
6
6
|
* on the first hit, so the most likely patterns must come first to minimize
|
|
7
7
|
* paid verifications. "common" keeps only the leading high-probability slice.
|
|
8
|
+
*
|
|
9
|
+
* Compound names (hyphens, spaces, particles like "van"/"de") are expanded.
|
|
10
|
+
* The PRIMARY block runs the historical pattern list over the whole joined
|
|
11
|
+
* name — unchanged output for simple names, and the only block "common" (and
|
|
12
|
+
* therefore the early-stop search by default) ever sees, so credit cost is
|
|
13
|
+
* stable. An ALTERNATE block then appends per-token and compound-initial
|
|
14
|
+
* variants that only enrich the "all" set: e.g. "Jean-Pierre Dupont" also
|
|
15
|
+
* yields jean.dupont / jp.dupont, "van der Berg" also yields berg / j.berg,
|
|
16
|
+
* "García López" also yields maria.garcia / maria.lopez.
|
|
8
17
|
*/
|
|
9
18
|
/** Lowercase, strip accents and keep only [a-z0-9]. */
|
|
10
19
|
export declare function normalizeNamePart(value: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../../src/lib/common/patterns.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../../src/lib/common/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAyFD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAmF9E"}
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* The order is load-bearing: the verifier checks candidates top-down and stops
|
|
7
7
|
* on the first hit, so the most likely patterns must come first to minimize
|
|
8
8
|
* paid verifications. "common" keeps only the leading high-probability slice.
|
|
9
|
+
*
|
|
10
|
+
* Compound names (hyphens, spaces, particles like "van"/"de") are expanded.
|
|
11
|
+
* The PRIMARY block runs the historical pattern list over the whole joined
|
|
12
|
+
* name — unchanged output for simple names, and the only block "common" (and
|
|
13
|
+
* therefore the early-stop search by default) ever sees, so credit cost is
|
|
14
|
+
* stable. An ALTERNATE block then appends per-token and compound-initial
|
|
15
|
+
* variants that only enrich the "all" set: e.g. "Jean-Pierre Dupont" also
|
|
16
|
+
* yields jean.dupont / jp.dupont, "van der Berg" also yields berg / j.berg,
|
|
17
|
+
* "García López" also yields maria.garcia / maria.lopez.
|
|
9
18
|
*/
|
|
10
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
20
|
exports.normalizeNamePart = normalizeNamePart;
|
|
@@ -42,7 +51,35 @@ function normalizeDomain(value) {
|
|
|
42
51
|
const LOCAL_PART_REGEX = /^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/;
|
|
43
52
|
/** Leading patterns that cover the large majority of real corporate addresses. */
|
|
44
53
|
const COMMON_LIMIT = 10;
|
|
54
|
+
/**
|
|
55
|
+
* Surname prefixes written as separate tokens. Dropped from the front of a
|
|
56
|
+
* surname to find the "core" name that usually drives the email — "van der
|
|
57
|
+
* Berg" → berg, "de la Cruz" → cruz.
|
|
58
|
+
*/
|
|
59
|
+
const SURNAME_PARTICLES = new Set([
|
|
60
|
+
"van", "von", "der", "den", "de", "del", "della", "di", "da", "dos", "das",
|
|
61
|
+
"du", "la", "le", "el", "lo", "ter", "ten", "op", "af", "av", "zu", "y", "e",
|
|
62
|
+
]);
|
|
45
63
|
const join = (a, sep, b) => a && b ? `${a}${sep}${b}` : "";
|
|
64
|
+
const uniq = (values) => [
|
|
65
|
+
...new Set(values.filter(Boolean)),
|
|
66
|
+
];
|
|
67
|
+
/** Split a name into normalized alphanumeric tokens (spaces, hyphens, dots, _). */
|
|
68
|
+
function tokenize(value) {
|
|
69
|
+
return stripDiacritics(value ?? "")
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.split(/[\s._-]+/)
|
|
72
|
+
.map((token) => token.replace(/[^a-z0-9]/g, ""))
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
/** Drop leading particle tokens, keeping at least the final token. */
|
|
76
|
+
function coreSurname(tokens) {
|
|
77
|
+
let i = 0;
|
|
78
|
+
while (i < tokens.length - 1 && SURNAME_PARTICLES.has(tokens[i])) {
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
return tokens.slice(i);
|
|
82
|
+
}
|
|
46
83
|
// Ordered by approximate real-world B2B frequency (most common first). Each
|
|
47
84
|
// trailing comment names the produced local-part so the terse builders stay
|
|
48
85
|
// readable. This order drives how many paid verifications an early-stop search
|
|
@@ -69,36 +106,90 @@ const PATTERNS = [
|
|
|
69
106
|
(p) => join(p.li, "", p.fi), // lf
|
|
70
107
|
(p) => join(p.first, ".", p.li), // first.l
|
|
71
108
|
];
|
|
109
|
+
// Applied to every alternate (first-form, last-form) combination to widen the
|
|
110
|
+
// "all" set. Kept to the formats that actually appear in the wild so compound
|
|
111
|
+
// names don't explode into noise.
|
|
112
|
+
const ALT_PATTERNS = [
|
|
113
|
+
(f, l) => join(f, ".", l), // first.last
|
|
114
|
+
(f, l) => join(f.charAt(0), "", l), // flast
|
|
115
|
+
(f, l) => join(f, "", l), // firstlast
|
|
116
|
+
(f, l) => join(f, "_", l), // first_last
|
|
117
|
+
(f, l) => join(f, "-", l), // first-last
|
|
118
|
+
(f, l) => join(f, ".", l.charAt(0)), // first.l
|
|
119
|
+
(f, l) => join(l, ".", f), // last.first
|
|
120
|
+
];
|
|
72
121
|
/**
|
|
73
122
|
* Generate a de-duplicated list of candidate emails ordered by likelihood.
|
|
74
123
|
* Returns an empty array when the domain or both name parts are missing.
|
|
75
124
|
*/
|
|
76
125
|
function generateEmailCandidates(params) {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
126
|
+
const firstTokens = tokenize(params.firstName);
|
|
127
|
+
const lastTokens = tokenize(params.lastName);
|
|
79
128
|
const domain = normalizeDomain(params.domain);
|
|
80
|
-
|
|
129
|
+
const firstJoined = firstTokens.join("");
|
|
130
|
+
const lastJoined = lastTokens.join("");
|
|
131
|
+
if (!domain || (!firstJoined && !lastJoined)) {
|
|
81
132
|
return [];
|
|
82
133
|
}
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
134
|
+
const lastCore = coreSurname(lastTokens);
|
|
135
|
+
const firstPrimary = firstTokens[0] ?? "";
|
|
136
|
+
const lastMain = lastCore[lastCore.length - 1] ?? "";
|
|
137
|
+
// Distinct first/last spellings to cross in the alternate block, most likely
|
|
138
|
+
// first. Compound-first initials ("jp") only when there is more than one
|
|
139
|
+
// token; otherwise that is just the single initial already covered by f.last.
|
|
140
|
+
const firstForms = uniq([
|
|
141
|
+
firstJoined,
|
|
142
|
+
firstPrimary,
|
|
143
|
+
firstTokens.length > 1 ? firstTokens.map((t) => t.charAt(0)).join("") : "",
|
|
144
|
+
]);
|
|
145
|
+
const lastForms = uniq([
|
|
146
|
+
lastJoined,
|
|
147
|
+
lastCore.join(""),
|
|
148
|
+
lastCore[0] ?? "",
|
|
149
|
+
lastMain,
|
|
150
|
+
]);
|
|
151
|
+
const primary = {
|
|
152
|
+
first: firstJoined,
|
|
153
|
+
last: lastJoined,
|
|
154
|
+
fi: (firstPrimary || firstJoined).charAt(0),
|
|
155
|
+
li: (lastMain || lastJoined).charAt(0),
|
|
88
156
|
};
|
|
89
|
-
const seen = new Set();
|
|
90
157
|
const locals = [];
|
|
158
|
+
// Primary block — unchanged historical ordering, the slice "common" sees.
|
|
91
159
|
for (const build of PATTERNS) {
|
|
92
|
-
|
|
160
|
+
locals.push(build(primary));
|
|
161
|
+
}
|
|
162
|
+
// Alternate block — compound coverage, grouped by pattern so the dominant
|
|
163
|
+
// first.last spread across name-forms lands ahead of rarer shapes.
|
|
164
|
+
for (const build of ALT_PATTERNS) {
|
|
165
|
+
for (const f of firstForms) {
|
|
166
|
+
for (const l of lastForms) {
|
|
167
|
+
if (f === firstJoined && l === lastJoined) {
|
|
168
|
+
continue; // already produced by the primary block
|
|
169
|
+
}
|
|
170
|
+
locals.push(build(f, l));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Standalone surname / given-name tokens (berg@, garcia@, jean@).
|
|
175
|
+
for (const l of lastForms) {
|
|
176
|
+
locals.push(l);
|
|
177
|
+
}
|
|
178
|
+
for (const f of firstForms) {
|
|
179
|
+
locals.push(f);
|
|
180
|
+
}
|
|
181
|
+
const seen = new Set();
|
|
182
|
+
const ordered = [];
|
|
183
|
+
for (const local of locals) {
|
|
93
184
|
if (!local || seen.has(local) || !LOCAL_PART_REGEX.test(local)) {
|
|
94
185
|
continue;
|
|
95
186
|
}
|
|
96
187
|
seen.add(local);
|
|
97
|
-
|
|
188
|
+
ordered.push(local);
|
|
98
189
|
}
|
|
99
190
|
const limit = params.maxVariants ??
|
|
100
|
-
(params.patternSet === "common" ? COMMON_LIMIT :
|
|
101
|
-
return
|
|
191
|
+
(params.patternSet === "common" ? COMMON_LIMIT : ordered.length);
|
|
192
|
+
return ordered
|
|
102
193
|
.slice(0, Math.max(0, limit))
|
|
103
194
|
.map((local) => `${local}@${domain}`);
|
|
104
195
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../../src/lib/common/patterns.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../../src/lib/common/patterns.ts"],"names":[],"mappings":"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"}
|