@monoes/monodesign 1.2.18 → 1.2.22

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 (104) hide show
  1. package/README.md +3 -3
  2. package/cli/engine/design-system-checks.mjs +381 -0
  3. package/cli/engine/design-system-parse.mjs +433 -0
  4. package/cli/engine/design-system.mjs +20 -736
  5. package/cli/engine/detect-antipatterns-browser.js +12 -1
  6. package/cli/engine/engines/regex/detect-text-matchers.mjs +371 -0
  7. package/cli/engine/engines/regex/detect-text-run.mjs +186 -0
  8. package/cli/engine/engines/regex/detect-text.mjs +11 -523
  9. package/cli/engine/engines/static-html/css-cascade-color.mjs +219 -0
  10. package/cli/engine/engines/static-html/css-cascade-static-data.mjs +138 -0
  11. package/cli/engine/engines/static-html/css-cascade-static-dom.mjs +132 -0
  12. package/cli/engine/engines/static-html/css-cascade-static-parse.mjs +393 -0
  13. package/cli/engine/engines/static-html/css-cascade.mjs +24 -837
  14. package/cli/engine/registry/antipatterns-provider.mjs +60 -0
  15. package/cli/engine/registry/antipatterns-quality.mjs +218 -0
  16. package/cli/engine/registry/antipatterns-slop.mjs +226 -0
  17. package/cli/engine/registry/antipatterns.mjs +7 -494
  18. package/cli/engine/rules/checks-background.mjs +155 -0
  19. package/cli/engine/rules/checks-css-values.mjs +137 -0
  20. package/cli/engine/rules/checks-dom-effects.mjs +243 -0
  21. package/cli/engine/rules/checks-dom.mjs +148 -0
  22. package/cli/engine/rules/checks-element.mjs +286 -0
  23. package/cli/engine/rules/checks-html.mjs +204 -0
  24. package/cli/engine/rules/checks-overflow.mjs +263 -0
  25. package/cli/engine/rules/checks-page.mjs +465 -0
  26. package/cli/engine/rules/checks-pure.mjs +453 -0
  27. package/cli/engine/rules/checks-quality.mjs +447 -0
  28. package/cli/engine/rules/checks-stylesheet.mjs +324 -0
  29. package/cli/engine/rules/checks.mjs +98 -2939
  30. package/cli/lib/monodesign-config-consent.mjs +93 -0
  31. package/cli/lib/monodesign-config-core.mjs +37 -0
  32. package/cli/lib/monodesign-config-detection.mjs +133 -0
  33. package/cli/lib/monodesign-config-filter.mjs +198 -0
  34. package/cli/lib/monodesign-config-ignore-value.mjs +212 -0
  35. package/cli/lib/monodesign-config.mjs +21 -623
  36. package/package.json +2 -2
  37. package/skill/scripts/context-fields.mjs +65 -0
  38. package/skill/scripts/context-resolve.mjs +425 -0
  39. package/skill/scripts/context-update.mjs +151 -0
  40. package/skill/scripts/context-utils.mjs +68 -0
  41. package/skill/scripts/context-workspace.mjs +245 -0
  42. package/skill/scripts/context.mjs +17 -891
  43. package/skill/scripts/hook-admin-config.mjs +152 -0
  44. package/skill/scripts/hook-admin-ignore.mjs +131 -0
  45. package/skill/scripts/hook-admin-manifest.mjs +292 -0
  46. package/skill/scripts/hook-admin.mjs +8 -541
  47. package/skill/scripts/hook-before-edit-shell.mjs +184 -0
  48. package/skill/scripts/hook-before-edit.mjs +9 -165
  49. package/skill/scripts/hook-lib-cache.mjs +145 -0
  50. package/skill/scripts/hook-lib-config.mjs +292 -0
  51. package/skill/scripts/hook-lib-detector.mjs +72 -0
  52. package/skill/scripts/hook-lib-events.mjs +182 -0
  53. package/skill/scripts/hook-lib-findings.mjs +201 -0
  54. package/skill/scripts/hook-lib-ignore.mjs +205 -0
  55. package/skill/scripts/hook-lib-render.mjs +273 -0
  56. package/skill/scripts/hook-lib-run.mjs +281 -0
  57. package/skill/scripts/hook-lib-scan-targets.mjs +128 -0
  58. package/skill/scripts/hook-lib.mjs +71 -1747
  59. package/skill/scripts/lib/design-parser-colors.mjs +182 -0
  60. package/skill/scripts/lib/design-parser-extract.mjs +333 -0
  61. package/skill/scripts/lib/design-parser-sections.mjs +214 -0
  62. package/skill/scripts/lib/design-parser-yaml.mjs +135 -0
  63. package/skill/scripts/lib/design-parser.mjs +10 -783
  64. package/skill/scripts/live/manual-apply-chunk.mjs +151 -0
  65. package/skill/scripts/live/manual-apply-compact.mjs +122 -0
  66. package/skill/scripts/live/manual-apply-evidence.mjs +115 -0
  67. package/skill/scripts/live/manual-apply-log.mjs +179 -0
  68. package/skill/scripts/live/manual-apply-transaction.mjs +122 -0
  69. package/skill/scripts/live/manual-apply.mjs +65 -640
  70. package/skill/scripts/live/svelte-component-accept.mjs +248 -0
  71. package/skill/scripts/live/svelte-component-css.mjs +230 -0
  72. package/skill/scripts/live/svelte-component-markup.mjs +104 -0
  73. package/skill/scripts/live/svelte-component-scaffold.mjs +118 -0
  74. package/skill/scripts/live/svelte-component-session.mjs +158 -0
  75. package/skill/scripts/live/svelte-component.mjs +32 -792
  76. package/skill/scripts/live-accept-mutate.mjs +152 -0
  77. package/skill/scripts/live-accept-parse.mjs +370 -0
  78. package/skill/scripts/live-accept-scrub.mjs +59 -0
  79. package/skill/scripts/live-accept-search.mjs +58 -0
  80. package/skill/scripts/live-accept.mjs +15 -631
  81. package/skill/scripts/live-commit-manual-edits-paths.mjs +27 -0
  82. package/skill/scripts/live-commit-manual-edits-repair.mjs +166 -0
  83. package/skill/scripts/live-commit-manual-edits-rollback.mjs +231 -0
  84. package/skill/scripts/live-commit-manual-edits-utils.mjs +167 -0
  85. package/skill/scripts/live-commit-manual-edits-verify.mjs +356 -0
  86. package/skill/scripts/live-commit-manual-edits.mjs +24 -881
  87. package/skill/scripts/live-copy-edit-agent.mjs +16 -551
  88. package/skill/scripts/live-copy-edit-compact.mjs +137 -0
  89. package/skill/scripts/live-copy-edit-post-apply.mjs +175 -0
  90. package/skill/scripts/live-copy-edit-runners.mjs +297 -0
  91. package/skill/scripts/live-inject-csp.mjs +132 -0
  92. package/skill/scripts/live-inject-files.mjs +229 -0
  93. package/skill/scripts/live-inject-tag.mjs +114 -0
  94. package/skill/scripts/live-inject.mjs +13 -440
  95. package/skill/scripts/live-server-poll.mjs +232 -0
  96. package/skill/scripts/live-server-routes.mjs +449 -0
  97. package/skill/scripts/live-server-state.mjs +361 -0
  98. package/skill/scripts/live-server.mjs +10 -946
  99. package/skill/scripts/live-wrap-css.mjs +132 -0
  100. package/skill/scripts/live-wrap-manual-edit.mjs +153 -0
  101. package/skill/scripts/live-wrap-search.mjs +247 -0
  102. package/skill/scripts/live-wrap.mjs +21 -484
  103. package/skill/scripts/palette-seeds.mjs +401 -0
  104. package/skill/scripts/palette.mjs +1 -395
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Source-verification helpers for live-commit-manual-edits.mjs: checking
3
+ * whether a staged copy-edit op's newText actually landed in a plausible
4
+ * source location, and rolling that up per entry.
5
+ *
6
+ * Split out of live-commit-manual-edits.mjs. See that file for context.
7
+ */
8
+
9
+ import { candidatesForEntry, uniqueStrings } from './live-commit-manual-edits-utils.mjs';
10
+ import { normalizeRelativeFile } from './live-commit-manual-edits-paths.mjs';
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+
14
+ function sourceHintWindowFailure(cwd, op) {
15
+ const hint = op?.sourceHint;
16
+ if (!hint?.file || !hint.line) return null;
17
+ const relative = normalizeRelativeFile(cwd, hint.file);
18
+ if (!relative) return null;
19
+ const absolute = path.resolve(cwd, relative);
20
+ let content;
21
+ try { content = fs.readFileSync(absolute, 'utf-8'); } catch { return null; }
22
+ const lines = content.split('\n');
23
+ const line = Math.max(1, Number(hint.line) || 1);
24
+ const lineText = lines[line - 1] || '';
25
+ const start = Math.max(0, line - 5);
26
+ const end = Math.min(lines.length, line + 4);
27
+ if (
28
+ typeof op.originalText === 'string'
29
+ && op.originalText
30
+ && lineText.includes(op.originalText)
31
+ && !lineShowsAppliedOp(lineText, op)
32
+ ) {
33
+ return {
34
+ file: relative,
35
+ line,
36
+ reason: 'source_hint_still_contains_original_text',
37
+ };
38
+ }
39
+ if (lines.slice(start, end).some((candidateLine) => lineShowsAppliedOp(candidateLine, op))) return null;
40
+ return null;
41
+ }
42
+
43
+ function verificationTargetsForOp(batch, op, reportedFiles, cwd) {
44
+ const candidate = (batch.candidates || []).find((item) => item.entryId === op.entryId && item.ref === op.ref);
45
+ const out = [];
46
+ const reportedFileSet = new Set(reportedFiles || []);
47
+ const add = (file, line, kind) => {
48
+ const relativeFile = normalizeRelativeFile(cwd, file);
49
+ const lineNumber = Number(line);
50
+ if (!relativeFile || !Number.isFinite(lineNumber) || lineNumber < 1) return;
51
+ out.push({ file: relativeFile, line: lineNumber, kind, reported: reportedFileSet.has(relativeFile) });
52
+ };
53
+
54
+ add(op.sourceHint?.file, op.sourceHint?.line, 'source_hint');
55
+ add(candidate?.sourceHint?.relativeFile || candidate?.sourceHint?.file, candidate?.sourceHint?.line, 'candidate_source_hint');
56
+ for (const item of candidate?.textMatches || []) add(item.file, item.line, 'text_match');
57
+ for (const item of candidate?.objectKeyMatches || []) add(item.file, item.line, 'object_key_match');
58
+ for (const item of candidate?.locatorMatches || []) add(item.file, item.line, 'locator_match');
59
+ for (const item of candidate?.contextTextMatches || []) add(item.file, item.line, 'context_text_match');
60
+
61
+ // Manual copy edits often stage coupled leaves from the same UI object, e.g.
62
+ // a card label plus its count. Dynamic source stores both on the label/key
63
+ // line, so the count op may need the sibling label's data candidates.
64
+ for (const siblingCandidate of siblingCandidatesForEntry(batch, op)) {
65
+ add(siblingCandidate.sourceHint?.relativeFile || siblingCandidate.sourceHint?.file, siblingCandidate.sourceHint?.line, 'entry_source_hint');
66
+ for (const item of siblingCandidate.textMatches || []) add(item.file, item.line, 'entry_text_match');
67
+ for (const item of siblingCandidate.objectKeyMatches || []) add(item.file, item.line, 'entry_object_key_match');
68
+ for (const item of siblingCandidate.contextTextMatches || []) add(item.file, item.line, 'entry_context_text_match');
69
+ }
70
+
71
+ for (const relativeFile of reportedFiles || []) {
72
+ for (const target of locatorTargetsInFile(cwd, relativeFile, op)) {
73
+ out.push(target);
74
+ }
75
+ }
76
+
77
+ const seen = new Set();
78
+ return out.filter((target) => {
79
+ const key = `${target.file}:${target.line}:${target.kind}`;
80
+ if (seen.has(key)) return false;
81
+ seen.add(key);
82
+ return true;
83
+ });
84
+ }
85
+
86
+ function objectKeyCandidatesForOp(batch, op) {
87
+ const candidates = (batch.candidates || [])
88
+ .filter((item) => item.entryId === op.entryId && item.ref === op.ref);
89
+ return candidates.flatMap((candidate) => candidate.objectKeyMatches || []);
90
+ }
91
+
92
+ function lineHasObjectKey(line, text) {
93
+ if (typeof text !== 'string' || text.length === 0) return false;
94
+ const quotedKey = new RegExp(`(^|[\\s,{])(['"\`])${escapeRegExp(text)}\\2\\s*:`);
95
+ if (quotedKey.test(line)) return true;
96
+ const identifierSafe = /^[A-Za-z_$][\w$]*$/.test(text);
97
+ if (!identifierSafe) return false;
98
+ const bareKey = new RegExp(`(^|[\\s,{])${escapeRegExp(text)}\\s*:`);
99
+ return bareKey.test(line);
100
+ }
101
+
102
+ function objectKeyMatchStillUsesOriginal(cwd, match, op) {
103
+ const relative = normalizeRelativeFile(cwd, match?.file);
104
+ const lineNumber = Number(match?.line);
105
+ if (!relative || !Number.isFinite(lineNumber) || lineNumber < 1) return false;
106
+ let lines;
107
+ try { lines = fs.readFileSync(path.resolve(cwd, relative), 'utf-8').split('\n'); } catch { return false; }
108
+ const start = Math.max(0, lineNumber - 4);
109
+ const end = Math.min(lines.length, lineNumber + 3);
110
+ const windowLines = lines.slice(start, end);
111
+ if (windowLines.some((line) => lineHasObjectKey(line, op.newText))) return false;
112
+ return windowLines.some((line) => lineHasObjectKey(line, op.originalText));
113
+ }
114
+
115
+ function coupledObjectKeyFailuresForOp(batch, op, cwd) {
116
+ if (
117
+ typeof op?.originalText !== 'string'
118
+ || typeof op?.newText !== 'string'
119
+ || op.originalText === op.newText
120
+ ) return [];
121
+ return objectKeyCandidatesForOp(batch, op)
122
+ .filter((match) => objectKeyMatchStillUsesOriginal(cwd, match, op))
123
+ .map((match) => ({
124
+ ref: op.ref,
125
+ reason: 'source_verification_failed',
126
+ detail: 'edited_text_source_key_dependency_not_updated',
127
+ candidates: [{
128
+ file: normalizeRelativeFile(cwd, match.file) || match.file,
129
+ line: match.line,
130
+ kind: 'object_key_match',
131
+ reason: 'edited text is also a source key; update the coupled key to newText or fail the entry',
132
+ }],
133
+ }));
134
+ }
135
+
136
+ function siblingCandidatesForEntry(batch, op) {
137
+ if (!op?.entryId) return [];
138
+ return (batch.candidates || []).filter((item) => item.entryId === op.entryId && item.ref !== op.ref);
139
+ }
140
+
141
+ function locatorTargetsInFile(cwd, relativeFile, op) {
142
+ if (!opHasLocator(op)) return [];
143
+ const absolute = path.resolve(cwd, relativeFile);
144
+ let lines;
145
+ try { lines = fs.readFileSync(absolute, 'utf-8').split('\n'); } catch { return []; }
146
+ const out = [];
147
+ for (let index = 0; index < lines.length; index += 1) {
148
+ if (!lineMatchesManualEditLocator(lines[index], op)) continue;
149
+ out.push({ file: relativeFile, line: index + 1, kind: 'reported_locator_match' });
150
+ if (out.length >= 20) break;
151
+ }
152
+ return out;
153
+ }
154
+
155
+ function verificationTargetPasses(cwd, target, op) {
156
+ let lines;
157
+ try { lines = fs.readFileSync(path.resolve(cwd, target.file), 'utf-8').split('\n'); } catch { return false; }
158
+ return verificationTargetPassesLines(lines, target, op);
159
+ }
160
+
161
+ function verificationTargetPassesLines(lines, target, op) {
162
+ const line = lines[target.line - 1] || '';
163
+ if (lineShowsAppliedOp(line, op)) return true;
164
+ const originalText = typeof op?.originalText === 'string' ? op.originalText : '';
165
+ if (originalText && line.includes(originalText)) return false;
166
+ const kind = String(target.kind || '');
167
+ const canSearchWindow = target.reported
168
+ || kind.includes('context_text_match')
169
+ || kind.includes('object_key_match')
170
+ || kind.includes('text_match');
171
+ if (!canSearchWindow) return false;
172
+ const radius = kind.includes('context_text_match') ? 20 : 4;
173
+ const start = Math.max(0, target.line - radius - 1);
174
+ const end = Math.min(lines.length, target.line + radius);
175
+ const windowLines = lines.slice(start, end);
176
+ if (windowLines.some((candidateLine) => lineShowsAppliedOp(candidateLine, op))) return true;
177
+ if (windowShowsAppliedOp(windowLines, op)) return true;
178
+ return false;
179
+ }
180
+
181
+ function windowShowsAppliedOp(lines, op) {
182
+ const newText = typeof op?.newText === 'string' ? op.newText : '';
183
+ if (!newText) return false;
184
+ const originalText = typeof op?.originalText === 'string' ? op.originalText : '';
185
+ const normalizedNew = normalizeVerificationText(newText);
186
+ const normalizedOriginal = normalizeVerificationText(originalText);
187
+ const normalizedWindow = normalizeVerificationText(lines.join('\n'));
188
+ if (!normalizedNew || !normalizedWindow.includes(normalizedNew)) return false;
189
+ if (normalizedOriginal && !normalizedNew.includes(normalizedOriginal) && normalizedWindow.includes(normalizedOriginal)) return false;
190
+ return true;
191
+ }
192
+
193
+ function normalizeVerificationText(text) {
194
+ return String(text || '').replace(/\s+/g, ' ').trim();
195
+ }
196
+
197
+ function lineShowsAppliedOp(line, op) {
198
+ const originalText = typeof op?.originalText === 'string' ? op.originalText : '';
199
+ const newText = typeof op?.newText === 'string' ? op.newText : '';
200
+ const deletion = op?.deleted === true || newText.length === 0;
201
+ if (deletion) return !!originalText && !line.includes(originalText);
202
+ if (!line.includes(newText)) return false;
203
+ if (originalText && !newText.includes(originalText) && line.includes(originalText)) return false;
204
+ return true;
205
+ }
206
+
207
+ function opHasLocator(op) {
208
+ return !!(
209
+ op?.tag
210
+ || op?.elementId
211
+ || (Array.isArray(op?.classes) && op.classes.filter(Boolean).length > 0)
212
+ );
213
+ }
214
+
215
+ function lineMatchesManualEditLocator(line, op) {
216
+ if (op.tag) {
217
+ const tagRe = new RegExp(`<\\s*${escapeRegExp(op.tag)}(?=[\\s>/]|$)`, 'i');
218
+ if (!tagRe.test(line)) return false;
219
+ }
220
+
221
+ if (op.elementId) {
222
+ const idRe = new RegExp(`\\bid\\s*=\\s*["']${escapeRegExp(op.elementId)}["']`);
223
+ if (!idRe.test(line)) return false;
224
+ }
225
+
226
+ const classes = Array.isArray(op.classes) ? op.classes.filter(Boolean) : [];
227
+ for (const className of classes) {
228
+ if (!line.includes(className)) return false;
229
+ }
230
+
231
+ return true;
232
+ }
233
+
234
+ export function verifyAppliedEntry({ batch, entry, reportedFiles, cwd }) {
235
+ const failures = [];
236
+ for (const rawOp of entry.ops || []) {
237
+ const op = { ...rawOp, entryId: entry.id };
238
+ if (op.deleted === true && typeof op.newText !== 'string') op.newText = '';
239
+ if (typeof op.newText !== 'string') {
240
+ failures.push({
241
+ ref: op.ref,
242
+ reason: 'source_verification_failed',
243
+ detail: 'missing_newText',
244
+ candidates: candidatesForEntry(batch, entry.id).slice(0, 12),
245
+ });
246
+ continue;
247
+ }
248
+ const targets = verificationTargetsForOp(batch, op, reportedFiles, cwd);
249
+ const coupledObjectKeyFailures = coupledObjectKeyFailuresForOp(batch, op, cwd);
250
+ if (
251
+ coupledObjectKeyFailures.length === 0
252
+ && targets.some((target) => verificationTargetPasses(cwd, target, op))
253
+ ) continue;
254
+
255
+ if (coupledObjectKeyFailures.length > 0) {
256
+ failures.push(...coupledObjectKeyFailures.map((failure) => ({
257
+ ...failure,
258
+ candidates: [
259
+ ...(failure.candidates || []),
260
+ ...targets.map((target) => ({ file: target.file, line: target.line, kind: target.kind })),
261
+ ...candidatesForEntry(batch, entry.id),
262
+ ].slice(0, 12),
263
+ })));
264
+ continue;
265
+ }
266
+
267
+ const hintedOldText = sourceHintWindowFailure(cwd, op);
268
+ if (hintedOldText) {
269
+ failures.push({
270
+ ref: op.ref,
271
+ reason: 'source_verification_failed',
272
+ detail: hintedOldText.reason,
273
+ candidates: [hintedOldText, ...targets.map((target) => ({ file: target.file, line: target.line, kind: target.kind })), ...candidatesForEntry(batch, entry.id)].slice(0, 12),
274
+ });
275
+ continue;
276
+ }
277
+
278
+ failures.push({
279
+ ref: op.ref,
280
+ reason: 'source_verification_failed',
281
+ detail: op.newText.length === 0 ? 'originalText_still_present_in_plausible_source_location' : 'newText_not_found_in_plausible_source_location',
282
+ candidates: targets.map((target) => ({ file: target.file, line: target.line, kind: target.kind })).concat(candidatesForEntry(batch, entry.id)).slice(0, 12),
283
+ });
284
+ }
285
+ return failures;
286
+ }
287
+
288
+ function snapshotTargetPasses(snapshot, target, op) {
289
+ const before = snapshot.get(target.file)?.content;
290
+ if (typeof before !== 'string') return false;
291
+ return verificationTargetPassesLines(before.split('\n'), target, op);
292
+ }
293
+
294
+ export function findUnappliedEntrySourceChanges({ batch, entries, reportedFiles, cwd, rollbackSnapshot }) {
295
+ const failures = [];
296
+ for (const entry of entries || []) {
297
+ for (const rawOp of entry.ops || []) {
298
+ const op = { ...rawOp, entryId: entry.id };
299
+ if (typeof op.newText !== 'string' || op.newText.length === 0) continue;
300
+ const targets = verificationTargetsForOp(batch, op, reportedFiles, cwd);
301
+ const leakedTargets = targets.filter((target) =>
302
+ verificationTargetPasses(cwd, target, op)
303
+ && !snapshotTargetPasses(rollbackSnapshot, target, op)
304
+ );
305
+ if (leakedTargets.length === 0) continue;
306
+ failures.push({
307
+ id: entry.id,
308
+ reason: 'failed_entry_source_changed',
309
+ ref: op.ref,
310
+ newText: op.newText,
311
+ candidates: leakedTargets
312
+ .map((target) => ({ file: target.file, line: target.line, kind: target.kind }))
313
+ .concat(candidatesForEntry(batch, entry.id))
314
+ .slice(0, 12),
315
+ });
316
+ break;
317
+ }
318
+ }
319
+ return failures;
320
+ }
321
+
322
+ export function verificationFailuresForEntries(batch, entries, reason, extra = {}) {
323
+ return entries.map((entry) => ({
324
+ id: entry.id,
325
+ reason,
326
+ candidates: candidatesForEntry(batch, entry.id),
327
+ ...extra,
328
+ }));
329
+ }
330
+
331
+ export function verifyEntriesAfterRepair({ batch, appliedEntryIds, files, cwd }) {
332
+ const reportedFiles = uniqueStrings(files || [])
333
+ .map((file) => normalizeRelativeFile(cwd, file))
334
+ .filter(Boolean);
335
+ const entries = (batch.entries || []).filter((entry) => appliedEntryIds.includes(entry.id));
336
+ const verifiedIds = [];
337
+ const failed = [];
338
+ for (const entry of entries) {
339
+ const failures = verifyAppliedEntry({ batch, entry, reportedFiles, cwd });
340
+ if (failures.length === 0) {
341
+ verifiedIds.push(entry.id);
342
+ } else {
343
+ failed.push({
344
+ id: entry.id,
345
+ reason: 'source_verification_failed',
346
+ failures,
347
+ candidates: candidatesForEntry(batch, entry.id),
348
+ });
349
+ }
350
+ }
351
+ return { verifiedIds, failed, reportedFiles };
352
+ }
353
+
354
+ function escapeRegExp(value) {
355
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
356
+ }