webpeel 0.8.1 → 0.9.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/README.md +39 -5
- package/dist/cli.js +1299 -85
- package/dist/cli.js.map +1 -1
- package/dist/core/application-tracker.d.ts +85 -0
- package/dist/core/application-tracker.d.ts.map +1 -0
- package/dist/core/application-tracker.js +184 -0
- package/dist/core/application-tracker.js.map +1 -0
- package/dist/core/apply.d.ts +163 -0
- package/dist/core/apply.d.ts.map +1 -0
- package/dist/core/apply.js +817 -0
- package/dist/core/apply.js.map +1 -0
- package/dist/core/branding.d.ts +1 -1
- package/dist/core/branding.d.ts.map +1 -1
- package/dist/core/budget.d.ts +43 -0
- package/dist/core/budget.d.ts.map +1 -0
- package/dist/core/budget.js +325 -0
- package/dist/core/budget.js.map +1 -0
- package/dist/core/challenge-detection.d.ts +27 -0
- package/dist/core/challenge-detection.d.ts.map +1 -0
- package/dist/core/challenge-detection.js +436 -0
- package/dist/core/challenge-detection.js.map +1 -0
- package/dist/core/change-tracking.d.ts.map +1 -1
- package/dist/core/change-tracking.js +10 -1
- package/dist/core/change-tracking.js.map +1 -1
- package/dist/core/crawler.d.ts.map +1 -1
- package/dist/core/crawler.js +17 -4
- package/dist/core/crawler.js.map +1 -1
- package/dist/core/diff.d.ts +62 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +289 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/extract-listings.d.ts +39 -0
- package/dist/core/extract-listings.d.ts.map +1 -0
- package/dist/core/extract-listings.js +331 -0
- package/dist/core/extract-listings.js.map +1 -0
- package/dist/core/extract.d.ts.map +1 -1
- package/dist/core/extract.js +15 -2
- package/dist/core/extract.js.map +1 -1
- package/dist/core/fetcher.d.ts +29 -3
- package/dist/core/fetcher.d.ts.map +1 -1
- package/dist/core/fetcher.js +158 -20
- package/dist/core/fetcher.js.map +1 -1
- package/dist/core/human.d.ts +176 -0
- package/dist/core/human.d.ts.map +1 -0
- package/dist/core/human.js +681 -0
- package/dist/core/human.js.map +1 -0
- package/dist/core/jobs.d.ts +12 -2
- package/dist/core/jobs.d.ts.map +1 -1
- package/dist/core/jobs.js +124 -2
- package/dist/core/jobs.js.map +1 -1
- package/dist/core/map.d.ts.map +1 -1
- package/dist/core/map.js +14 -2
- package/dist/core/map.js.map +1 -1
- package/dist/core/paginate.d.ts +32 -0
- package/dist/core/paginate.d.ts.map +1 -0
- package/dist/core/paginate.js +107 -0
- package/dist/core/paginate.js.map +1 -0
- package/dist/core/rate-governor.d.ts +81 -0
- package/dist/core/rate-governor.d.ts.map +1 -0
- package/dist/core/rate-governor.js +238 -0
- package/dist/core/rate-governor.js.map +1 -0
- package/dist/core/search-provider.d.ts +5 -0
- package/dist/core/search-provider.d.ts.map +1 -1
- package/dist/core/search-provider.js +81 -2
- package/dist/core/search-provider.js.map +1 -1
- package/dist/core/site-search.d.ts +45 -0
- package/dist/core/site-search.d.ts.map +1 -0
- package/dist/core/site-search.js +253 -0
- package/dist/core/site-search.js.map +1 -0
- package/dist/core/strategies.d.ts +8 -0
- package/dist/core/strategies.d.ts.map +1 -1
- package/dist/core/strategies.js +185 -45
- package/dist/core/strategies.js.map +1 -1
- package/dist/core/strategy-hooks.d.ts +6 -0
- package/dist/core/strategy-hooks.d.ts.map +1 -1
- package/dist/core/strategy-hooks.js.map +1 -1
- package/dist/core/table-format.d.ts +31 -0
- package/dist/core/table-format.d.ts.map +1 -0
- package/dist/core/table-format.js +147 -0
- package/dist/core/table-format.js.map +1 -0
- package/dist/core/user-agents.d.ts +58 -0
- package/dist/core/user-agents.d.ts.map +1 -0
- package/dist/core/user-agents.js +159 -0
- package/dist/core/user-agents.js.map +1 -0
- package/dist/core/watch.d.ts +100 -0
- package/dist/core/watch.d.ts.map +1 -0
- package/dist/core/watch.js +368 -0
- package/dist/core/watch.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/llms.txt +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Tracker — persists job application records to disk.
|
|
3
|
+
*
|
|
4
|
+
* Stored at: ~/.webpeel/applications.json
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Add / update application records
|
|
8
|
+
* - Duplicate-URL detection
|
|
9
|
+
* - Filter by platform, status, or since-date
|
|
10
|
+
* - Quick stats summary (total, by-platform, by-status, this-week, today)
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { randomUUID } from 'crypto';
|
|
16
|
+
// ── Storage ────────────────────────────────────────────────────────────
|
|
17
|
+
const WEBPEEL_DIR = join(homedir(), '.webpeel');
|
|
18
|
+
const APPLICATIONS_PATH = join(WEBPEEL_DIR, 'applications.json');
|
|
19
|
+
// ── ApplicationTracker class ───────────────────────────────────────────
|
|
20
|
+
export class ApplicationTracker {
|
|
21
|
+
records;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.records = this.loadRecords();
|
|
24
|
+
}
|
|
25
|
+
// ── Public API ───────────────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Add a new application record.
|
|
28
|
+
* Assigns a random UUID and returns the completed record.
|
|
29
|
+
*/
|
|
30
|
+
add(record) {
|
|
31
|
+
const full = {
|
|
32
|
+
id: randomUUID(),
|
|
33
|
+
...record,
|
|
34
|
+
};
|
|
35
|
+
this.records.push(full);
|
|
36
|
+
this.save();
|
|
37
|
+
return full;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Update the status (and optionally notes) of an existing record.
|
|
41
|
+
* Throws if the ID is not found.
|
|
42
|
+
*/
|
|
43
|
+
updateStatus(id, status, notes) {
|
|
44
|
+
const record = this.records.find((r) => r.id === id);
|
|
45
|
+
if (!record) {
|
|
46
|
+
throw new Error(`Application record not found: ${id}`);
|
|
47
|
+
}
|
|
48
|
+
record.status = status;
|
|
49
|
+
if (notes !== undefined) {
|
|
50
|
+
record.notes = notes;
|
|
51
|
+
}
|
|
52
|
+
this.save();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Return true if there is already an 'applied' record for this URL.
|
|
56
|
+
* Normalises the URL by stripping trailing slashes and query strings that
|
|
57
|
+
* are tracking parameters (utm_*, ref, etc.) before comparison.
|
|
58
|
+
*/
|
|
59
|
+
hasApplied(url) {
|
|
60
|
+
const norm = normaliseUrl(url);
|
|
61
|
+
return this.records.some((r) => normaliseUrl(r.url) === norm && r.status === 'applied');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Return records, optionally filtered by platform, status, and/or since-date.
|
|
65
|
+
* Results are sorted newest-first.
|
|
66
|
+
*/
|
|
67
|
+
list(filter) {
|
|
68
|
+
let results = [...this.records];
|
|
69
|
+
if (filter?.platform) {
|
|
70
|
+
const p = filter.platform.toLowerCase();
|
|
71
|
+
results = results.filter((r) => r.platform.toLowerCase() === p);
|
|
72
|
+
}
|
|
73
|
+
if (filter?.status) {
|
|
74
|
+
const s = filter.status.toLowerCase();
|
|
75
|
+
results = results.filter((r) => r.status.toLowerCase() === s);
|
|
76
|
+
}
|
|
77
|
+
if (filter?.since) {
|
|
78
|
+
const sinceTs = Date.parse(filter.since);
|
|
79
|
+
if (!isNaN(sinceTs)) {
|
|
80
|
+
results = results.filter((r) => Date.parse(r.appliedAt) >= sinceTs);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Newest first
|
|
84
|
+
results.sort((a, b) => Date.parse(b.appliedAt) - Date.parse(a.appliedAt));
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Return an aggregate stats summary.
|
|
89
|
+
*/
|
|
90
|
+
stats() {
|
|
91
|
+
const now = new Date();
|
|
92
|
+
const todayStr = toDateString(now);
|
|
93
|
+
const weekAgo = new Date(now);
|
|
94
|
+
weekAgo.setDate(weekAgo.getDate() - 7);
|
|
95
|
+
const weekAgoTs = weekAgo.getTime();
|
|
96
|
+
const byPlatform = {};
|
|
97
|
+
const byStatus = {};
|
|
98
|
+
let thisWeek = 0;
|
|
99
|
+
let today = 0;
|
|
100
|
+
for (const r of this.records) {
|
|
101
|
+
// Platform counts
|
|
102
|
+
byPlatform[r.platform] = (byPlatform[r.platform] ?? 0) + 1;
|
|
103
|
+
// Status counts
|
|
104
|
+
byStatus[r.status] = (byStatus[r.status] ?? 0) + 1;
|
|
105
|
+
// Time-based counts
|
|
106
|
+
const appliedTs = Date.parse(r.appliedAt);
|
|
107
|
+
if (!isNaN(appliedTs)) {
|
|
108
|
+
if (appliedTs >= weekAgoTs)
|
|
109
|
+
thisWeek++;
|
|
110
|
+
if (toDateString(new Date(appliedTs)) === todayStr)
|
|
111
|
+
today++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
total: this.records.length,
|
|
116
|
+
byPlatform,
|
|
117
|
+
byStatus,
|
|
118
|
+
thisWeek,
|
|
119
|
+
today,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ── Private helpers ──────────────────────────────────────────────────
|
|
123
|
+
loadRecords() {
|
|
124
|
+
try {
|
|
125
|
+
if (existsSync(APPLICATIONS_PATH)) {
|
|
126
|
+
const raw = readFileSync(APPLICATIONS_PATH, 'utf-8');
|
|
127
|
+
const parsed = JSON.parse(raw);
|
|
128
|
+
if (Array.isArray(parsed))
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Corrupt / missing data — start fresh
|
|
134
|
+
}
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
save() {
|
|
138
|
+
try {
|
|
139
|
+
mkdirSync(WEBPEEL_DIR, { recursive: true });
|
|
140
|
+
writeFileSync(APPLICATIONS_PATH, JSON.stringify(this.records, null, 2), 'utf-8');
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
console.error('[application-tracker] Failed to save records:', err);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ── Utility ────────────────────────────────────────────────────────────
|
|
148
|
+
/** Format a Date as YYYY-MM-DD in local time. */
|
|
149
|
+
function toDateString(d) {
|
|
150
|
+
const y = d.getFullYear();
|
|
151
|
+
const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
152
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
153
|
+
return `${y}-${m}-${day}`;
|
|
154
|
+
}
|
|
155
|
+
/** Tracking parameters to strip before URL comparison. */
|
|
156
|
+
const TRACKING_PARAMS = new Set([
|
|
157
|
+
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',
|
|
158
|
+
'ref', 'referer', 'referrer', 'source', 'trk', 'trackingId',
|
|
159
|
+
]);
|
|
160
|
+
/**
|
|
161
|
+
* Strip trailing slashes, fragments, and common tracking query parameters
|
|
162
|
+
* from a URL for stable duplicate detection.
|
|
163
|
+
*/
|
|
164
|
+
function normaliseUrl(rawUrl) {
|
|
165
|
+
try {
|
|
166
|
+
const u = new URL(rawUrl);
|
|
167
|
+
u.hash = '';
|
|
168
|
+
for (const param of TRACKING_PARAMS) {
|
|
169
|
+
u.searchParams.delete(param);
|
|
170
|
+
}
|
|
171
|
+
// Sort remaining params for canonical ordering
|
|
172
|
+
u.searchParams.sort();
|
|
173
|
+
let result = u.toString();
|
|
174
|
+
// Strip trailing slash
|
|
175
|
+
if (result.endsWith('/'))
|
|
176
|
+
result = result.slice(0, -1);
|
|
177
|
+
return result.toLowerCase();
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Not a valid URL — return as-is lowercased
|
|
181
|
+
return rawUrl.toLowerCase().replace(/\/$/, '');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=application-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-tracker.js","sourceRoot":"","sources":["../../src/core/application-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job application pipeline — stealth automated job applications.
|
|
3
|
+
* Uses the human behavior engine for natural interaction.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Rate-limit check (daily limit)
|
|
7
|
+
* 2. Launch persistent browser (preserves login cookies)
|
|
8
|
+
* 3. Warmup browse (optional, looks human)
|
|
9
|
+
* 4. Navigate to job posting naturally
|
|
10
|
+
* 5. Detect & click Apply button
|
|
11
|
+
* 6. Scan form fields & categorize
|
|
12
|
+
* 7. Fill fields with human behavior
|
|
13
|
+
* 8. Handle multi-step forms
|
|
14
|
+
* 9. Review / dry-run / auto submit
|
|
15
|
+
* 10. Log to ~/.webpeel/applications.json
|
|
16
|
+
*/
|
|
17
|
+
export interface ApplyProfile {
|
|
18
|
+
/** Full name */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Email */
|
|
21
|
+
email: string;
|
|
22
|
+
/** Phone number */
|
|
23
|
+
phone: string;
|
|
24
|
+
/** LinkedIn profile URL */
|
|
25
|
+
linkedin?: string;
|
|
26
|
+
/** Portfolio/website URL */
|
|
27
|
+
website?: string;
|
|
28
|
+
/** City, State */
|
|
29
|
+
location: string;
|
|
30
|
+
/** Work authorization status (e.g. "US Citizen", "Permanent Resident", "H-1B", "Need Sponsorship") */
|
|
31
|
+
workAuthorization: string;
|
|
32
|
+
/** Years of experience */
|
|
33
|
+
yearsExperience: number;
|
|
34
|
+
/** Current/most recent title */
|
|
35
|
+
currentTitle: string;
|
|
36
|
+
/** Skills list */
|
|
37
|
+
skills: string[];
|
|
38
|
+
/** Education: degree, school (e.g. "B.S. Computer Science, MIT") */
|
|
39
|
+
education: string;
|
|
40
|
+
/** Path to resume file (PDF) */
|
|
41
|
+
resumePath: string;
|
|
42
|
+
/** Brief professional summary (for cover letter / LLM question generation) */
|
|
43
|
+
summary: string;
|
|
44
|
+
/** Desired salary range */
|
|
45
|
+
salaryRange?: {
|
|
46
|
+
min: number;
|
|
47
|
+
max: number;
|
|
48
|
+
};
|
|
49
|
+
/** Willing to relocate? */
|
|
50
|
+
willingToRelocate?: boolean;
|
|
51
|
+
/** Sponsorship needed? */
|
|
52
|
+
needsSponsorship?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface ApplyOptions {
|
|
55
|
+
/** Job URL to apply to */
|
|
56
|
+
url: string;
|
|
57
|
+
/** Profile data */
|
|
58
|
+
profile: ApplyProfile;
|
|
59
|
+
/** Path to persistent browser session directory (cookies, localStorage) */
|
|
60
|
+
sessionDir?: string;
|
|
61
|
+
/** Mode: 'auto' (fully automated) | 'review' (pause before submit) | 'dry-run' (fill but don't submit) */
|
|
62
|
+
mode?: 'auto' | 'review' | 'dry-run';
|
|
63
|
+
/** LLM API key for generating tailored answers to custom questions */
|
|
64
|
+
llmKey?: string;
|
|
65
|
+
/** LLM provider: 'openai' | 'anthropic' (default: 'openai') */
|
|
66
|
+
llmProvider?: string;
|
|
67
|
+
/** Daily application limit (default: 8) */
|
|
68
|
+
dailyLimit?: number;
|
|
69
|
+
/** Timeout per application in ms (default: 120000) */
|
|
70
|
+
timeout?: number;
|
|
71
|
+
/** Browse the site naturally before applying (default: true) */
|
|
72
|
+
warmup?: boolean;
|
|
73
|
+
/** Warmup duration in ms (default: 15000-30000 random) */
|
|
74
|
+
warmupDuration?: number;
|
|
75
|
+
/** Callback for progress updates */
|
|
76
|
+
onProgress?: (event: ApplyProgressEvent) => void;
|
|
77
|
+
}
|
|
78
|
+
export interface ApplyProgressEvent {
|
|
79
|
+
stage: 'warmup' | 'navigating' | 'reading' | 'filling' | 'reviewing' | 'submitting' | 'done' | 'error';
|
|
80
|
+
message: string;
|
|
81
|
+
/** Form fields detected (during 'filling' stage) */
|
|
82
|
+
fields?: DetectedField[];
|
|
83
|
+
/** Answers generated for fields */
|
|
84
|
+
answers?: Record<string, string>;
|
|
85
|
+
}
|
|
86
|
+
export interface DetectedField {
|
|
87
|
+
/** Field type: text, email, tel, textarea, select, radio, checkbox, file */
|
|
88
|
+
type: string;
|
|
89
|
+
/** Label or placeholder text */
|
|
90
|
+
label: string;
|
|
91
|
+
/** CSS selector */
|
|
92
|
+
selector: string;
|
|
93
|
+
/** Select/radio options if applicable */
|
|
94
|
+
options?: string[];
|
|
95
|
+
/** Whether field is required */
|
|
96
|
+
required: boolean;
|
|
97
|
+
/** What we think this field is asking for */
|
|
98
|
+
category: 'name' | 'email' | 'phone' | 'linkedin' | 'website' | 'location' | 'work-auth' | 'experience' | 'salary' | 'education' | 'resume' | 'cover-letter' | 'skills' | 'custom-question' | 'unknown';
|
|
99
|
+
}
|
|
100
|
+
export interface ApplyResult {
|
|
101
|
+
/** Whether the application was submitted */
|
|
102
|
+
submitted: boolean;
|
|
103
|
+
/** Job details extracted during the process */
|
|
104
|
+
job: {
|
|
105
|
+
title: string;
|
|
106
|
+
company: string;
|
|
107
|
+
location?: string;
|
|
108
|
+
salary?: string;
|
|
109
|
+
};
|
|
110
|
+
/** Fields that were filled */
|
|
111
|
+
fieldsFilled: number;
|
|
112
|
+
/** Fields that needed LLM-generated answers */
|
|
113
|
+
llmAnswers: number;
|
|
114
|
+
/** Fields that couldn't be filled (unknown/ambiguous) */
|
|
115
|
+
fieldsSkipped: string[];
|
|
116
|
+
/** Any warnings */
|
|
117
|
+
warnings: string[];
|
|
118
|
+
/** Total time taken in ms */
|
|
119
|
+
elapsed: number;
|
|
120
|
+
/** Error if failed */
|
|
121
|
+
error?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface ApplicationRecord {
|
|
124
|
+
id: string;
|
|
125
|
+
url: string;
|
|
126
|
+
company: string;
|
|
127
|
+
title: string;
|
|
128
|
+
location?: string;
|
|
129
|
+
salary?: string;
|
|
130
|
+
appliedAt: string;
|
|
131
|
+
mode: 'auto' | 'review' | 'dry-run';
|
|
132
|
+
status: 'applied' | 'interview' | 'rejected' | 'offer' | 'withdrawn';
|
|
133
|
+
fieldsFilled: number;
|
|
134
|
+
fieldsSkipped: string[];
|
|
135
|
+
warnings: string[];
|
|
136
|
+
notes?: string;
|
|
137
|
+
}
|
|
138
|
+
export declare function loadApplications(): ApplicationRecord[];
|
|
139
|
+
export declare function saveApplication(record: ApplicationRecord): void;
|
|
140
|
+
export declare function getApplicationsToday(): number;
|
|
141
|
+
export declare function updateApplicationStatus(id: string, status: ApplicationRecord['status']): void;
|
|
142
|
+
/**
|
|
143
|
+
* Apply to a job with stealth human-like behavior.
|
|
144
|
+
*
|
|
145
|
+
* Default mode is 'review' — it fills the form and waits for your approval
|
|
146
|
+
* before submitting. Use 'auto' for fully automated, 'dry-run' to see what
|
|
147
|
+
* would be filled without actually clicking submit.
|
|
148
|
+
*
|
|
149
|
+
* Requires a persistent browser session for login state preservation.
|
|
150
|
+
* On first run, the browser will open — log into LinkedIn, then the session
|
|
151
|
+
* is saved to `~/.webpeel/sessions/linkedin/` for future runs.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const result = await applyToJob({
|
|
156
|
+
* url: 'https://linkedin.com/jobs/view/...',
|
|
157
|
+
* profile: myProfile,
|
|
158
|
+
* mode: 'review',
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function applyToJob(options: ApplyOptions): Promise<ApplyResult>;
|
|
163
|
+
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"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"}
|