mcp-scraper 0.3.13 → 0.3.15

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 (51) hide show
  1. package/dist/bin/api-server.cjs +1991 -508
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/browser-agent-stdio-server.cjs +9 -9
  5. package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
  6. package/dist/bin/browser-agent-stdio-server.js +2 -2
  7. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  8. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-cli.js +1 -1
  10. package/dist/bin/mcp-scraper-combined-stdio-server.cjs +428 -47
  11. package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-scraper-combined-stdio-server.js +6 -5
  13. package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
  14. package/dist/bin/mcp-scraper-install.cjs +2 -2
  15. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  16. package/dist/bin/mcp-scraper-install.js +2 -2
  17. package/dist/bin/mcp-stdio-server.cjs +419 -38
  18. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  19. package/dist/bin/mcp-stdio-server.js +2 -2
  20. package/dist/chunk-2MX5WOII.js +7 -0
  21. package/dist/chunk-2MX5WOII.js.map +1 -0
  22. package/dist/{chunk-DBQDG7EH.js → chunk-5H22TOXQ.js} +28 -1
  23. package/dist/chunk-5H22TOXQ.js.map +1 -0
  24. package/dist/{chunk-KPXMPAJ3.js → chunk-7Y6JDBML.js} +2 -2
  25. package/dist/chunk-7Y6JDBML.js.map +1 -0
  26. package/dist/{chunk-QAOIH5U2.js → chunk-WJ2LWHPA.js} +10 -10
  27. package/dist/chunk-WJ2LWHPA.js.map +1 -0
  28. package/dist/{chunk-YGV2XZDT.js → chunk-WQAIOY2D.js} +424 -39
  29. package/dist/chunk-WQAIOY2D.js.map +1 -0
  30. package/dist/{chunk-AZ5PKU4F.js → chunk-ZAUMSBV3.js} +2 -2
  31. package/dist/{db-BE4JVB3V.js → db-P76GVIFN.js} +2 -2
  32. package/dist/{server-LX65PB7D.js → server-UOQ2JIUQ.js} +1106 -93
  33. package/dist/server-UOQ2JIUQ.js.map +1 -0
  34. package/dist/{worker-FG7ZWEGA.js → worker-2WVKKCC7.js} +3 -3
  35. package/docs/final-tooling-spec.md +206 -0
  36. package/docs/mcp-tool-craft-lint.generated.md +1 -1
  37. package/docs/mcp-tool-design-guide.md +225 -0
  38. package/docs/mcp-tool-manifest.generated.json +13 -13
  39. package/docs/seo-crawl-report-spec.md +287 -0
  40. package/docs/tool-catalog-spec.md +388 -0
  41. package/package.json +2 -1
  42. package/dist/chunk-BEZJ4G32.js +0 -7
  43. package/dist/chunk-BEZJ4G32.js.map +0 -1
  44. package/dist/chunk-DBQDG7EH.js.map +0 -1
  45. package/dist/chunk-KPXMPAJ3.js.map +0 -1
  46. package/dist/chunk-QAOIH5U2.js.map +0 -1
  47. package/dist/chunk-YGV2XZDT.js.map +0 -1
  48. package/dist/server-LX65PB7D.js.map +0 -1
  49. /package/dist/{chunk-AZ5PKU4F.js.map → chunk-ZAUMSBV3.js.map} +0 -0
  50. /package/dist/{db-BE4JVB3V.js.map → db-P76GVIFN.js.map} +0 -0
  51. /package/dist/{worker-FG7ZWEGA.js.map → worker-2WVKKCC7.js.map} +0 -0
@@ -0,0 +1,388 @@
1
+ # Tool Catalog Spec — Firecrawl-style categorized tool surface
2
+
3
+ Engineering blueprint for a DB-backed **tool catalog** that drives the frontend's categorized
4
+ tool picker (Discover / Extract / Crawl / Special), the per-tool **source dropdowns**
5
+ (Search, Transcribe), **mode toggles** (Scrape scope, Browser Mode), and the dynamic **param forms**.
6
+
7
+ The catalog is the single source of truth the frontend reads via `GET /catalog`. It is seeded
8
+ idempotently from a canonical TS definition on startup, so the DB is the runtime store and code is
9
+ the authored source. Underlying execution still routes to the existing MCP tools / REST endpoints —
10
+ the catalog only describes *how to present and assemble a request*.
11
+
12
+ ---
13
+
14
+ ## 0. Current-state anchors (verified this codebase)
15
+
16
+ - MCP tool metadata already lives in `src/mcp/mcp-tool-manifest.ts` (name, surfaces, sample prompts,
17
+ format examples). The catalog is a *presentation* layer on top; it references tool names but adds
18
+ grouping, sources, modes, and form fields the manifest doesn't model.
19
+ - Input schemas: `src/mcp/mcp-tool-schemas.ts` (Zod). Param definitions in the catalog should mirror
20
+ these (same keys, bounds, enums) so the form posts valid bodies.
21
+ - REST routes: `src/api/server.ts` (e.g. `POST /extract-site`, `POST /map-urls`, `POST /extract-url`,
22
+ `/youtube/*`, `/maps/*`, `/instagram/*`). Catalog rows store the route each tool/source hits.
23
+ - DB: Turso/libSQL via `src/api/db.ts` `migrate()` (CREATE TABLE IF NOT EXISTS, TEXT/INTEGER,
24
+ `datetime('now')`). Billing ops in `src/api/rates.ts` (`LedgerOperation`, `MC_COSTS`).
25
+
26
+ ---
27
+
28
+ ## 1. Information architecture
29
+
30
+ Three render primitives, chosen per tool by `catalog_tools.kind`:
31
+
32
+ | kind | Frontend control | Examples |
33
+ |---|---|---|
34
+ | `simple` | param form only | Map, Directory, Place Intel |
35
+ | `composite_source` | **source dropdown** (multi-select) + per-source params | Search, Transcribe |
36
+ | `modal` | **mode toggle groups** + param form | Scrape (scope + browser_mode) |
37
+
38
+ Groups (top nav, Firecrawl's DISCOVER/EXTRACT/CRAWL plus our SPECIAL):
39
+
40
+ | group key | label | tools |
41
+ |---|---|---|
42
+ | `discover` | Discover | search |
43
+ | `extract` | Extract | scrape, transcribe |
44
+ | `crawl` | Crawl | map, crawl |
45
+ | `special` | Special | directory, seo_analysis, deep_research, place_intel, page_intel, rank_tracker, ai_visibility |
46
+
47
+ ---
48
+
49
+ ## 2. DB schema — full DDL (add to `src/api/db.ts` `migrate()`)
50
+
51
+ All TEXT PKs are stable string keys (not autoincrement) so seed data is idempotent and referenceable.
52
+
53
+ ```sql
54
+ CREATE TABLE IF NOT EXISTS catalog_groups (
55
+ key TEXT PRIMARY KEY, -- 'discover' | 'extract' | 'crawl' | 'special'
56
+ label TEXT NOT NULL,
57
+ icon TEXT, -- lucide icon name
58
+ display_order INTEGER NOT NULL DEFAULT 0
59
+ );
60
+
61
+ CREATE TABLE IF NOT EXISTS catalog_tools (
62
+ key TEXT PRIMARY KEY, -- 'search' | 'scrape' | 'crawl' | 'map' | 'transcribe' | ...
63
+ group_key TEXT NOT NULL REFERENCES catalog_groups(key),
64
+ label TEXT NOT NULL,
65
+ tagline TEXT, -- one-liner under the card
66
+ description TEXT, -- full help text
67
+ icon TEXT,
68
+ kind TEXT NOT NULL, -- 'simple' | 'composite_source' | 'modal'
69
+ is_special INTEGER NOT NULL DEFAULT 0,
70
+ is_workflow INTEGER NOT NULL DEFAULT 0, -- backed by a skill/workflow vs a direct tool/route
71
+ default_endpoint TEXT, -- REST route for simple/modal tools (null for composite_source — see sources)
72
+ default_mcp_name TEXT, -- MCP tool name for simple/modal tools
73
+ billing_op TEXT, -- LedgerOperation key (rates.ts) for cost display
74
+ cost_hint_mc INTEGER, -- per-unit MC estimate for the UI
75
+ enabled INTEGER NOT NULL DEFAULT 1,
76
+ display_order INTEGER NOT NULL DEFAULT 0,
77
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
78
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
79
+ );
80
+
81
+ CREATE TABLE IF NOT EXISTS catalog_sources (
82
+ id TEXT PRIMARY KEY, -- '<tool>.<source>' e.g. 'search.web'
83
+ tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
84
+ key TEXT NOT NULL, -- 'web' | 'maps' | 'fb_ads' | 'youtube' | 'fb_video' ...
85
+ label TEXT NOT NULL,
86
+ icon TEXT,
87
+ endpoint TEXT NOT NULL, -- REST route this source posts to
88
+ mcp_name TEXT NOT NULL, -- MCP tool name this source maps to
89
+ billing_op TEXT,
90
+ cost_hint_mc INTEGER,
91
+ default_selected INTEGER NOT NULL DEFAULT 0,
92
+ multi_select INTEGER NOT NULL DEFAULT 1, -- may be combined with other sources
93
+ display_order INTEGER NOT NULL DEFAULT 0,
94
+ UNIQUE (tool_key, key)
95
+ );
96
+
97
+ CREATE TABLE IF NOT EXISTS catalog_modes (
98
+ id TEXT PRIMARY KEY, -- '<tool>.<group>.<option>' e.g. 'scrape.scope.bulk'
99
+ tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
100
+ group_key TEXT NOT NULL, -- 'scope' | 'browser_mode'
101
+ group_label TEXT NOT NULL,
102
+ option_key TEXT NOT NULL, -- 'single' | 'bulk' | 'default' | 'chrome_profile'
103
+ option_label TEXT NOT NULL,
104
+ is_default INTEGER NOT NULL DEFAULT 0,
105
+ endpoint_override TEXT, -- if this option changes the target route
106
+ mcp_name_override TEXT,
107
+ param_overrides TEXT, -- JSON object merged into the request body when selected
108
+ display_order INTEGER NOT NULL DEFAULT 0,
109
+ UNIQUE (tool_key, group_key, option_key)
110
+ );
111
+
112
+ CREATE TABLE IF NOT EXISTS catalog_params (
113
+ id TEXT PRIMARY KEY, -- '<tool>.<source|*>.<param>'
114
+ tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
115
+ source_key TEXT, -- null = applies to all; else only when that source/mode active
116
+ key TEXT NOT NULL, -- the actual API body field name
117
+ label TEXT NOT NULL,
118
+ type TEXT NOT NULL, -- 'string'|'number'|'boolean'|'enum'|'multi_enum'|'url'
119
+ required INTEGER NOT NULL DEFAULT 0,
120
+ default_value TEXT, -- JSON-encoded
121
+ min_value REAL,
122
+ max_value REAL,
123
+ enum_values TEXT, -- JSON array of {value,label}
124
+ placeholder TEXT,
125
+ help TEXT,
126
+ depends_on TEXT, -- another param key
127
+ depends_value TEXT, -- show this param only when depends_on == depends_value
128
+ advanced INTEGER NOT NULL DEFAULT 0, -- behind an "Advanced" disclosure
129
+ display_order INTEGER NOT NULL DEFAULT 0,
130
+ UNIQUE (tool_key, source_key, key)
131
+ );
132
+
133
+ CREATE TABLE IF NOT EXISTS catalog_examples (
134
+ id TEXT PRIMARY KEY,
135
+ tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
136
+ text TEXT NOT NULL,
137
+ display_order INTEGER NOT NULL DEFAULT 0
138
+ );
139
+ ```
140
+
141
+ Indexes:
142
+ ```sql
143
+ CREATE INDEX IF NOT EXISTS catalog_tools_group ON catalog_tools(group_key);
144
+ CREATE INDEX IF NOT EXISTS catalog_sources_tool ON catalog_sources(tool_key);
145
+ CREATE INDEX IF NOT EXISTS catalog_modes_tool ON catalog_modes(tool_key);
146
+ CREATE INDEX IF NOT EXISTS catalog_params_tool ON catalog_params(tool_key);
147
+ CREATE INDEX IF NOT EXISTS catalog_examples_tool ON catalog_examples(tool_key);
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 3. Complete catalog mapping (the seed data — every tool, source, mode, param)
153
+
154
+ Authored in `src/api/catalog-seed.ts` as typed constants, written via `seedCatalog()` (Section 5).
155
+
156
+ ### 3.1 Groups
157
+ ```
158
+ discover "Discover" order 0
159
+ extract "Extract" order 1
160
+ crawl "Crawl" order 2
161
+ special "Special" order 3
162
+ ```
163
+
164
+ ### 3.2 Tools
165
+
166
+ Endpoints and billing_op VERIFIED against `http-mcp-tool-executor.ts` and `rates.ts LedgerOperation`:
167
+
168
+ | key | group | label | kind | endpoint · mcp | special | workflow | billing_op |
169
+ |---|---|---|---|---|---|---|---|
170
+ | `search` | discover | Search | composite_source | (per source) | 0 | 0 | (per source) |
171
+ | `scrape` | extract | Scrape | modal | (per mode) | 0 | 0 | `EXTRACT_URL` / `EXTRACT_SITE` |
172
+ | `transcribe` | extract | Transcribe | composite_source | (per source) | 0 | 0 | (per source) |
173
+ | `map` | crawl | Map | simple | `/map-urls` · `map_site_urls` | 0 | 0 | `URL_MAP` ✅(was MAP_SITE) |
174
+ | `crawl` | crawl | Crawl | simple | `/extract-site` · `extract_site` | 0 | 0 | `EXTRACT_SITE` |
175
+ | `directory` | special | Directory | simple | `/directory/run` ✅(was /directory) · `directory_workflow` | 1 | 1 | ⚠️ no dedicated op — bills per-city via `MAPS_SEARCH`; verify in `/directory/run` handler |
176
+ | `seo_analysis` | special | SEO Technical Analysis | simple | `/extract-site` (background) · `extract_site` | 1 | 1 | `EXTRACT_SITE` |
177
+ | `deep_research` | special | Deep Research | simple | `/workflows/run` · `workflow_run` | 1 | 1 | ⚠️ no `WORKFLOW` op — confirm how `/workflows/run` bills |
178
+ | `place_intel` | special | Place Intel | simple | `/maps/place` ✅(was /maps/place-intel) · `maps_place_intel` | 1 | 0 | `MAPS_PLACE` |
179
+ | `page_intel` | special | Page Intel | simple | `/facebook/page-intel` · `facebook_page_intel` | 1 | 0 | ⚠️ no `FB_PAGE` op — verify which op `/facebook/page-intel` debits |
180
+ | `rank_tracker` | special | Rank Tracker | simple | ⚠️ NO REST route in executor · `rank_tracker_blueprint` | 1 | 1 | ⚠️ unknown — see §9 |
181
+ | `ai_visibility` | special | AI Visibility (Fan-out) | simple | ⚠️ no simple REST route — driven by `browser_*` agent · `browser_capture_fanout` | 1 | 0 | `BROWSER_SESSION` ✅(was BROWSER_AGENT) |
182
+
183
+ ### 3.3 Sources
184
+
185
+ **search** (multi-select, default = web) — routes VERIFIED against `http-mcp-tool-executor.ts`:
186
+ | key | label | endpoint | mcp_name | billing_op | default |
187
+ |---|---|---|---|---|---|
188
+ | `web` | Web | `/harvest/sync` (body `serpOnly:true`) | `search_serp` | `SERP` | ✓ |
189
+ | `web_detailed` | Web — SERP + PAA detail | `/harvest/sync` | `harvest_paa` | `PAA` | |
190
+ | `maps` | Maps | `/maps/search` | `maps_search` | `MAPS_SEARCH` | |
191
+ | `fb_ads` | Facebook Ads | `/facebook/search` | `facebook_ad_search` | `FB_SEARCH` | |
192
+ | `youtube` | YouTube | `/youtube/harvest` | `youtube_harvest` | `YT_CHANNEL` | |
193
+ | `instagram` | Instagram | `/instagram/profile-content` | `instagram_profile_content` | `INSTAGRAM_PROFILE` | |
194
+
195
+ > Note: `search_serp` and `harvest_paa` BOTH hit `/harvest/sync` — `search_serp` adds `serpOnly:true`.
196
+ > The catalog source rows carry that override in `catalog_sources` (add a `body_overrides TEXT` column,
197
+ > see §9) rather than via separate endpoints.
198
+
199
+ **transcribe** (single-select, default = youtube) — routes VERIFIED:
200
+ | key | label | endpoint | mcp_name | billing_op | default |
201
+ |---|---|---|---|---|---|
202
+ | `youtube` | YouTube Video | `/youtube/transcribe` | `youtube_transcribe` | `TRANSCRIPTION` | ✓ |
203
+ | `fb_video` | Facebook Video | `/facebook/video-transcribe` | `facebook_video_transcribe` | `TRANSCRIPTION` | |
204
+ | `fb_ad` | Facebook Ad | `/facebook/transcribe` | `facebook_ad_transcribe` | `FB_TRANSCRIBE` | |
205
+ | `instagram` | Instagram Reel | `/instagram/media-download` | `instagram_media_download` | `INSTAGRAM_MEDIA` | |
206
+
207
+ ### 3.4 Modes (scrape)
208
+
209
+ group `scope` (default single):
210
+ | option | label | endpoint_override | mcp_name_override | param_overrides |
211
+ |---|---|---|---|---|
212
+ | `single` | Headless Single | `/extract-url` | `extract_url` | `{}` |
213
+ | `bulk` | Headless Bulk | `/extract-site` | `extract_site` | `{ "rotateProxies": true }` |
214
+
215
+ group `browser_mode` (default default):
216
+ | option | label | param_overrides |
217
+ |---|---|---|
218
+ | `default` | Default | `{}` |
219
+ | `chrome_profile` | Use Chrome Profile | `{ "browserProfile": true }` |
220
+
221
+ > `browserProfile` is a new body flag the scrape route would honor to route through a saved hosted
222
+ > profile (the `browser_profile_*` system). Wire in a follow-up; the catalog can ship the toggle disabled.
223
+
224
+ ### 3.5 Params (per tool/source) — keys MUST match the Zod schemas in `mcp-tool-schemas.ts`
225
+
226
+ **search / web** (`search_serp`): `query`(string,req), `location`(string), `gl`(string),
227
+ `maxResults`(number,1..100,default 10).
228
+ **search / web_detailed** (`harvest_paa`): `query`(string,req), `maxQuestions`(number,1..200,default 30), `location`(string).
229
+ **search / maps** (`maps_search`): `query`(string,req), `location`(string,req), `maxResults`(number,1..20,default 10), `includeReviews`(boolean).
230
+ **search / fb_ads** (`facebook_ad_search`): `query`(string,req), `country`(string,default US), `maxResults`(number,1..20,default 10).
231
+ **search / youtube** (`youtube_harvest`): mode `enum[search,channel]`, `query`(string), `channelHandle`(string), `maxVideos`(number,1..500,default 50).
232
+ **search / instagram** (`instagram_profile_content`): `handle|url`(string,req), `maxItems`(number,1..2000,default 50), `maxScrolls`(number,0..250,default 10).
233
+
234
+ **transcribe / ***: `url`(url,req) for all four. Add `includeTranscript`(boolean,default true) +
235
+ `mediaTypes`(multi_enum[image,video,audio]) for instagram.
236
+
237
+ **scrape (all)**: `url`(url,req), `formats`(multi_enum[markdown,links,json,images,branding]),
238
+ `screenshot`(boolean,advanced), `downloadMedia`(boolean,advanced).
239
+ **scrape / scope=bulk only** (`source_key='scope:bulk'`): `maxPages`(number,1..10000,default 100),
240
+ `rotateProxyEvery`(number,1..100,default 30,advanced), `background`(boolean).
241
+
242
+ **map** (`map_site_urls`): `url`(url,req), `maxUrls`(number,1..10000,default 100).
243
+ **crawl** (`extract_site`): `url`(url,req), `maxPages`(number,1..10000,default 100),
244
+ `formats`(multi_enum...), `rotateProxies`(boolean,default true), `background`(boolean,default true),
245
+ `rotateProxyEvery`(number,advanced).
246
+
247
+ **directory** (`directory_workflow`): `query`(string,req), `state`(string,req), `minPopulation`(number,default 100000),
248
+ `maxResultsPerCity`(number,1..50,default 50,advanced), `concurrency`(number,advanced).
249
+ **seo_analysis** (`extract_site` background): `url`(url,req), `maxPages`(number,default 1000),
250
+ `formats` preset `["markdown","links","json","images"]` (hidden/locked).
251
+ **deep_research**: `question`(string,req), `depth`(enum[quick,standard,deep],default standard).
252
+ **place_intel** (`maps_place_intel`): `businessName`(string,req), `location`(string,req), `includeReviews`(boolean), `maxReviews`(number,1..500,default 50).
253
+ **page_intel** (`facebook_page_intel`): `url|pageId`(string,req).
254
+ **rank_tracker** (`rank_tracker_blueprint`): `domain`(string,req), `keywords`(array<string>), `location`(string).
255
+ **ai_visibility** (`browser_capture_fanout`): `prompt`(string), `session_id`(string), `first_party_domain`(string), `export`(boolean).
256
+
257
+ ### 3.6 Examples (sample prompts)
258
+ - search: "Top restaurants in San Francisco"
259
+ - scrape: "https://steenshoney.com/"
260
+ - crawl: "https://steenshoney.com/ (full site SEO crawl)"
261
+ - transcribe: a Facebook/YouTube video URL
262
+ - directory: "roofers in TN, minPopulation 100000"
263
+
264
+ ---
265
+
266
+ ## 4. Repository — `src/api/tool-catalog-repository.ts`
267
+
268
+ ```ts
269
+ export interface CatalogParam { key: string; label: string; type: string; required: boolean;
270
+ default?: unknown; min?: number; max?: number; enumValues?: Array<{value:string;label:string}>;
271
+ placeholder?: string; help?: string; dependsOn?: string; dependsValue?: string; advanced: boolean; sourceKey: string | null }
272
+ export interface CatalogSource { key: string; label: string; icon?: string; endpoint: string; mcpName: string;
273
+ defaultSelected: boolean; multiSelect: boolean; costHintMc?: number }
274
+ export interface CatalogModeOption { groupKey: string; groupLabel: string; optionKey: string; optionLabel: string;
275
+ isDefault: boolean; endpointOverride?: string; mcpNameOverride?: string; paramOverrides?: Record<string,unknown> }
276
+ export interface CatalogTool { key: string; groupKey: string; label: string; tagline?: string; description?: string;
277
+ icon?: string; kind: 'simple'|'composite_source'|'modal'; isSpecial: boolean; isWorkflow: boolean;
278
+ defaultEndpoint?: string; defaultMcpName?: string; billingOp?: string; costHintMc?: number;
279
+ sources: CatalogSource[]; modes: CatalogModeOption[]; params: CatalogParam[]; examples: string[] }
280
+ export interface CatalogGroup { key: string; label: string; icon?: string; tools: CatalogTool[] }
281
+
282
+ export async function getCatalog(): Promise<CatalogGroup[]> // one assembled tree, ordered
283
+ export async function seedCatalog(): Promise<void> // idempotent upsert from catalog-seed.ts
284
+ ```
285
+
286
+ `getCatalog()`: 5 SELECTs (groups, tools, sources, modes, params, examples) ordered by `display_order`,
287
+ assembled in memory into the nested tree. Single round-trip-ish; cache in-process for 60s (catalog is static).
288
+
289
+ ---
290
+
291
+ ## 5. Seeding — `src/api/catalog-seed.ts` + wire into `migrate()`
292
+
293
+ - `catalog-seed.ts` exports `CATALOG_SEED` (typed literal mirroring Section 3).
294
+ - `seedCatalog()` performs `INSERT ... ON CONFLICT(key/id) DO UPDATE` (libSQL `INSERT OR REPLACE`)
295
+ for every row, so redeploys converge the DB to the authored definition. Rows removed from the seed
296
+ are pruned with `DELETE FROM <table> WHERE key NOT IN (...)`.
297
+ - Call order in `db.ts`: after `migrate()` creates the tables, call `await seedCatalog()` once at boot
298
+ (in `bin/api-server.ts` startup, after `migrate()`), behind `if (process.env.SEED_CATALOG !== 'false')`.
299
+
300
+ ---
301
+
302
+ ## 6. REST endpoint — `src/api/server.ts`
303
+
304
+ ```
305
+ GET /catalog -> 200 { groups: CatalogGroup[] } (public; no auth; cache-control 60s)
306
+ ```
307
+ - Add `app.get('/catalog', async (c) => c.json({ groups: await getCatalog() }))`.
308
+ - No vendor leakage risk (static metadata) but still run labels/descriptions through the existing
309
+ output path conventions if any dynamic field is ever added.
310
+
311
+ Frontend contract (the exact JSON the picker consumes):
312
+ ```jsonc
313
+ { "groups": [
314
+ { "key":"discover","label":"Discover","icon":"search","tools":[
315
+ { "key":"search","label":"Search","kind":"composite_source","isSpecial":false,
316
+ "sources":[{"key":"web","label":"Web","endpoint":"/search-serp","mcpName":"search_serp","defaultSelected":true,"multiSelect":true}, ...],
317
+ "modes":[], "params":[{"key":"query","label":"Query","type":"string","required":true,"sourceKey":null}, ...],
318
+ "examples":["Top restaurants in San Francisco"] } ]},
319
+ ... ] }
320
+ ```
321
+
322
+ Frontend behavior:
323
+ - Render groups as the top nav (Discover/Extract/Crawl/Special). `isSpecial` tools live under the
324
+ **Special** group already by `group_key='special'`; the flag is redundant insurance for filtering.
325
+ - `composite_source` → render the source dropdown (multi/single per `multiSelect`); union the params of
326
+ selected sources (params with matching `sourceKey`) plus shared params (`sourceKey=null`).
327
+ - `modal` → render mode toggle groups; on selection, merge `paramOverrides` into the body and switch
328
+ endpoint per `endpointOverride`.
329
+ - `simple` → render `params` form; POST to `defaultEndpoint`.
330
+ - "Get code" button: serialize the assembled request (endpoint + body) to a curl/SDK snippet.
331
+
332
+ ---
333
+
334
+ ## 7. Build order
335
+ 1. **Schema** — add the 6 tables + indexes to `db.ts migrate()`. (P1)
336
+ 2. **Seed authoring** — `catalog-seed.ts` with the full Section-3 mapping. (P1)
337
+ 3. **Repository + seed routine** — `tool-catalog-repository.ts`; call `seedCatalog()` at boot. (P1)
338
+ 4. **Endpoint** — `GET /catalog`. (P1)
339
+ 5. **Billing ops** — add any missing `LedgerOperation` keys referenced by `billing_op`. (P1, gating)
340
+ 6. **Frontend picker** — consume `/catalog`, render the three kinds. (P2)
341
+ 7. **Mode wiring follow-ups** — `browserProfile` flag on the scrape route; `seo_analysis`/`deep_research`
342
+ workflow runners if not already endpoints. (P2/P3)
343
+
344
+ ## 8. Non-goals (v1)
345
+ Auth-gated per-plan tool visibility, usage analytics on catalog, A/B of layouts, i18n of labels,
346
+ and the actual `browserProfile` / workflow-runner execution wiring (catalog ships them as
347
+ described-but-some-disabled until the routes exist).
348
+
349
+ ---
350
+
351
+ ## 9. Verification notes (self-checked against the codebase)
352
+
353
+ Routes verified against `src/mcp/http-mcp-tool-executor.ts`; ledger ops against
354
+ `src/api/rates.ts LedgerOperation`. Corrections already applied above:
355
+
356
+ **Route fixes (my first draft was wrong):**
357
+ - `search_serp` → **`/harvest/sync` with body `serpOnly:true`** (not `/search-serp` — that route doesn't exist).
358
+ - `harvest_paa` → **`/harvest/sync`** (not `/harvest-paa`). Both search sources share this route.
359
+ - `facebook_ad_search` → **`/facebook/search`** (not `/facebook/ad-search`).
360
+ - `facebook_ad_transcribe` → **`/facebook/transcribe`** (not `/facebook/ad-transcribe`).
361
+ - `maps_place_intel` → **`/maps/place`** (not `/maps/place-intel`).
362
+ - `directory_workflow` → **`/directory/run`** (the executor route; there is also a separate top-level `/directory` page route — do not confuse).
363
+
364
+ **Billing-op fixes:**
365
+ - `map` → **`URL_MAP`** (not the invented `MAP_SITE`).
366
+ - `ai_visibility` → **`BROWSER_SESSION`** (not `BROWSER_AGENT`).
367
+ - Verified-correct as-is: `EXTRACT_URL`, `EXTRACT_SITE`, `MAPS_PLACE`, `MAPS_SEARCH`, `SERP`, `PAA`,
368
+ `FB_SEARCH`, `FB_TRANSCRIBE`, `YT_CHANNEL`, `TRANSCRIPTION`, `INSTAGRAM_PROFILE`, `INSTAGRAM_MEDIA`.
369
+
370
+ **Unresolved — needs a human/code check before seeding (do NOT guess):**
371
+ 1. **`rank_tracker_blueprint` has NO route in `http-mcp-tool-executor.ts`.** It's a registered MCP tool
372
+ but isn't wired through the HTTP executor — it may be served by a different server (`paa-mcp-server`),
373
+ be skill-only, or unimplemented over REST. Confirm its execution path before adding it to the catalog;
374
+ otherwise ship it disabled.
375
+ 2. **No dedicated ledger ops for `directory`, `deep_research`/workflows, `page_intel`.** Directory likely
376
+ bills per-city through `MAPS_SEARCH`; `/workflows/run` and `/facebook/page-intel` billing must be read
377
+ from their handlers. Set `billing_op` only after confirming, or leave null and compute cost server-side.
378
+ 3. **`ai_visibility` / `browser_capture_fanout` is not a simple POST** — it requires an open browser
379
+ session (`session_id` from `browser_open`). It can't be a one-shot form like the others; model it as a
380
+ guided flow or omit from v1.
381
+
382
+ **Schema addendum from verification:** add `body_overrides TEXT` (JSON) to `catalog_sources` so the
383
+ `web` source can carry `{"serpOnly":true}` against the shared `/harvest/sync` route. (The §2 DDL should
384
+ include this column.)
385
+
386
+ **Confidence:** the four core verbs (Search, Scrape, Map, Crawl) + Transcribe + Place/Page Intel are
387
+ fully route- and billing-verified and safe to build. The three flagged Special tools
388
+ (rank_tracker, deep_research, ai_visibility) need route/billing confirmation first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-scraper",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "MCP server for MCP Scraper web intelligence tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -58,6 +58,7 @@
58
58
  "@modelcontextprotocol/sdk": "^1.29.0",
59
59
  "@onkernel/sdk": "^0.52.0",
60
60
  "@resvg/resvg-js": "^2.6.2",
61
+ "@vercel/blob": "^2.4.1",
61
62
  "commander": "^12.0.0",
62
63
  "hono": "^4.12.18",
63
64
  "inngest": "^4.4.0",
@@ -1,7 +0,0 @@
1
- // src/version.ts
2
- var PACKAGE_VERSION = "0.3.13";
3
-
4
- export {
5
- PACKAGE_VERSION
6
- };
7
- //# sourceMappingURL=chunk-BEZJ4G32.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = '0.3.13'\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/api/db.ts"],"sourcesContent":["import { createClient, type Client } from '@libsql/client/http'\nimport { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from 'node:crypto'\nimport { z } from 'zod'\n\nconst DB_URL = process.env.TURSO_DATABASE_URL ?? 'file:./paa-api.db'\nconst DB_TOKEN = process.env.TURSO_AUTH_TOKEN\n\nlet _db: Client | null = null\nlet _rateLimitSchemaReady = false\n\nexport function getDb(): Client {\n if (!_db) _db = createClient({ url: DB_URL, authToken: DB_TOKEN })\n return _db\n}\n\nexport async function migrate(): Promise<void> {\n const db = getDb()\n await db.execute(`\n CREATE TABLE IF NOT EXISTS users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n email TEXT UNIQUE NOT NULL,\n name TEXT,\n api_key TEXT UNIQUE NOT NULL,\n password_hash TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n active INTEGER NOT NULL DEFAULT 1\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n status TEXT NOT NULL DEFAULT 'pending',\n query TEXT NOT NULL,\n options TEXT NOT NULL,\n callback_url TEXT,\n result TEXT,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n started_at TEXT,\n completed_at TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS jobs_user_id ON jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS jobs_status ON jobs(status)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS harvest_attempts (\n id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES jobs(id),\n user_id INTEGER NOT NULL REFERENCES users(id),\n attempt_number INTEGER NOT NULL,\n max_attempts INTEGER NOT NULL,\n query TEXT NOT NULL,\n location TEXT,\n max_questions INTEGER NOT NULL,\n status TEXT NOT NULL,\n outcome TEXT,\n kernel_session_id TEXT,\n question_count INTEGER,\n duration_ms INTEGER,\n error TEXT,\n will_retry INTEGER,\n kernel_delete_started INTEGER NOT NULL DEFAULT 0,\n kernel_delete_succeeded INTEGER,\n kernel_delete_error TEXT,\n browser_close_succeeded INTEGER,\n browser_close_error TEXT,\n debug_json TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n completed_at TEXT,\n UNIQUE(job_id, attempt_number)\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS harvest_attempts_job_id ON harvest_attempts(job_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS harvest_attempts_user_created_at ON harvest_attempts(user_id, created_at DESC)`)\n try { await db.execute(`ALTER TABLE harvest_attempts ADD COLUMN debug_json TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN password_hash TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN key_active INTEGER NOT NULL DEFAULT 1`) } catch {}\n\n await db.execute(`\n CREATE TABLE IF NOT EXISTS kpo_jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n status TEXT NOT NULL,\n entity_name TEXT NOT NULL,\n entity_type TEXT NOT NULL,\n url TEXT,\n request TEXT NOT NULL,\n result TEXT,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS kpo_phase_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id TEXT NOT NULL REFERENCES kpo_jobs(id),\n phase TEXT NOT NULL,\n output TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_user_id ON kpo_jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_status ON kpo_jobs(status)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS kpo_phase_log_job_id ON kpo_phase_log(job_id)`)\n try { await db.execute(`ALTER TABLE users ADD COLUMN stripe_customer_id TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN balance_mc INTEGER NOT NULL DEFAULT 0`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN extra_concurrency_slots INTEGER NOT NULL DEFAULT 0`) } catch {}\n try { await db.execute(`ALTER TABLE users ADD COLUMN concurrency_stripe_sub_id TEXT`) } catch {}\n await db.execute(`\n CREATE TABLE IF NOT EXISTS concurrency_locks (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n operation TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n acquired_at TEXT NOT NULL DEFAULT (datetime('now')),\n expires_at TEXT NOT NULL,\n released_at TEXT,\n metadata TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS ledger (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL REFERENCES users(id),\n amount_mc INTEGER NOT NULL,\n operation TEXT NOT NULL,\n description TEXT,\n stripe_pi TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS free_credit_refreshes (\n user_id INTEGER NOT NULL REFERENCES users(id),\n month TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (user_id, month)\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS site_audit_jobs (\n id TEXT PRIMARY KEY,\n user_id INTEGER,\n status TEXT DEFAULT 'pending',\n client_domain TEXT,\n session_path TEXT,\n request TEXT,\n result TEXT,\n error TEXT,\n created_at TEXT DEFAULT (datetime('now')),\n updated_at TEXT DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS site_audit_phase_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id TEXT REFERENCES site_audit_jobs(id),\n phase TEXT,\n completed_at TEXT DEFAULT (datetime('now')),\n output_summary TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_user_id ON site_audit_jobs(user_id)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_status ON site_audit_jobs(status)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_phase_log_job_id ON site_audit_phase_log(job_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS password_reset_tokens (\n token TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n expires_at TEXT NOT NULL,\n used INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS prt_user_id ON password_reset_tokens(user_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n scope TEXT NOT NULL,\n key_hash TEXT NOT NULL,\n window_start INTEGER NOT NULL,\n count INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (scope, key_hash, window_start)\n )\n `)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS request_events (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n source TEXT NOT NULL,\n status TEXT NOT NULL,\n query TEXT NOT NULL,\n location TEXT,\n result_count INTEGER,\n result TEXT,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS request_events_user_created_at ON request_events(user_id, created_at DESC)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS workflow_schedules (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n workflow_id TEXT NOT NULL,\n name TEXT NOT NULL,\n input_json TEXT NOT NULL,\n cadence TEXT NOT NULL,\n cron_expr TEXT,\n timezone TEXT NOT NULL DEFAULT 'UTC',\n webhook_url TEXT,\n status TEXT NOT NULL DEFAULT 'active',\n next_run_at TEXT,\n last_run_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_schedules_user_status ON workflow_schedules(user_id, status, created_at DESC)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_schedules_due ON workflow_schedules(status, next_run_at)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS workflow_runs (\n id TEXT PRIMARY KEY,\n user_id INTEGER NOT NULL REFERENCES users(id),\n schedule_id TEXT,\n workflow_id TEXT NOT NULL,\n input_json TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'queued',\n manifest_json TEXT,\n error_message TEXT,\n queued_at TEXT NOT NULL DEFAULT (datetime('now')),\n started_at TEXT,\n completed_at TEXT,\n idempotency_key TEXT,\n step_index INTEGER NOT NULL DEFAULT 0,\n total_steps INTEGER,\n step_state_json TEXT,\n step_log_json TEXT,\n webhook_url TEXT\n )\n `)\n try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_index INTEGER NOT NULL DEFAULT 0`) } catch {}\n try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN total_steps INTEGER`) } catch {}\n try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_state_json TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_log_json TEXT`) } catch {}\n try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN webhook_url TEXT`) } catch {}\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_runs_user_created ON workflow_runs(user_id, queued_at DESC)`)\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_runs_schedule ON workflow_runs(schedule_id, queued_at DESC)`)\n await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS workflow_runs_idempotency ON workflow_runs(idempotency_key) WHERE idempotency_key IS NOT NULL`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS workflow_artifacts (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES workflow_runs(id),\n user_id INTEGER NOT NULL REFERENCES users(id),\n kind TEXT NOT NULL,\n label TEXT NOT NULL,\n path TEXT NOT NULL,\n content_type TEXT NOT NULL,\n bytes INTEGER,\n rows_count INTEGER,\n content TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n try { await db.execute(`ALTER TABLE workflow_artifacts ADD COLUMN content TEXT`) } catch {}\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_artifacts_run ON workflow_artifacts(run_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS workflow_webhook_deliveries (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES workflow_runs(id),\n schedule_id TEXT,\n user_id INTEGER NOT NULL REFERENCES users(id),\n webhook_url TEXT NOT NULL,\n status TEXT NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n last_error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n delivered_at TEXT\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS workflow_webhook_deliveries_run ON workflow_webhook_deliveries(run_id)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS stripe_events (\n event_id TEXT PRIMARY KEY,\n processed_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n}\n\nexport async function recordStripeEvent(eventId: string): Promise<boolean> {\n const res = await getDb().execute({\n sql: 'INSERT OR IGNORE INTO stripe_events (event_id) VALUES (?)',\n args: [eventId],\n })\n return res.rowsAffected > 0\n}\n\nexport async function stripeEventAlreadyProcessed(eventId: string): Promise<boolean> {\n const res = await getDb().execute({\n sql: 'SELECT 1 FROM stripe_events WHERE event_id = ? LIMIT 1',\n args: [eventId],\n })\n return res.rows.length > 0\n}\n\nexport function generateApiKey(): string {\n return 'sk_' + randomBytes(24).toString('hex')\n}\n\nexport interface User {\n id: number\n email: string\n name: string | null\n api_key: string\n key_active: number\n password_hash: string | null\n created_at: string\n active: number\n stripe_customer_id: string | null\n balance_mc: number\n extra_concurrency_slots: number\n concurrency_stripe_sub_id: string | null\n}\n\nexport function hashPassword(password: string): string {\n const salt = randomBytes(16).toString('hex')\n const hash = scryptSync(password, salt, 64)\n return `${salt}:${hash.toString('hex')}`\n}\n\nexport function verifyPassword(password: string, stored: string): boolean {\n const [salt, hash] = stored.split(':')\n const hashBuf = Buffer.from(hash, 'hex')\n const derived = scryptSync(password, salt, 64)\n return timingSafeEqual(hashBuf, derived)\n}\n\nfunction rowToUser(row: Record<string, unknown>): User {\n return {\n id: Number(row.id),\n email: String(row.email),\n name: row.name != null ? String(row.name) : null,\n api_key: String(row.api_key),\n key_active: Number(row.key_active ?? 1),\n password_hash: row.password_hash != null ? String(row.password_hash) : null,\n created_at: String(row.created_at),\n active: Number(row.active),\n stripe_customer_id: row.stripe_customer_id != null ? String(row.stripe_customer_id) : null,\n balance_mc: Number(row.balance_mc ?? 0),\n extra_concurrency_slots: Number(row.extra_concurrency_slots ?? 0),\n concurrency_stripe_sub_id: row.concurrency_stripe_sub_id != null ? String(row.concurrency_stripe_sub_id) : null,\n }\n}\n\nexport async function getUserByEmail(email: string): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE email = ? AND active = 1', args: [email] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserByApiKey(api_key: string): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1', args: [api_key] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserById(id: number | bigint): Promise<User | undefined> {\n const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE id = ? AND active = 1', args: [id] })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function setPassword(id: number | bigint, password: string): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET password_hash = ? WHERE id = ?', args: [hashPassword(password), id] })\n}\n\nexport async function getUserStats(userId: number | bigint): Promise<{ total: number; done: number; failed: number; totalQuestions: number; lastUsed: string | null }> {\n const db = getDb()\n const [totR, doneR, failR, tqR, luR] = await Promise.all([\n db.execute({ sql: 'SELECT COUNT(*) as n FROM jobs WHERE user_id = ?', args: [userId] }),\n db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'failed'\", args: [userId] }),\n db.execute({ sql: \"SELECT COALESCE(SUM(json_extract(result, '$.totalQuestions')), 0) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n db.execute({ sql: 'SELECT MAX(created_at) as t FROM jobs WHERE user_id = ?', args: [userId] }),\n ])\n return {\n total: Number(totR.rows[0]?.n ?? 0),\n done: Number(doneR.rows[0]?.n ?? 0),\n failed: Number(failR.rows[0]?.n ?? 0),\n totalQuestions: Number(tqR.rows[0]?.n ?? 0),\n lastUsed: luR.rows[0]?.t != null ? String(luR.rows[0].t) : null,\n }\n}\n\nexport async function createUser(email: string, name?: string, password?: string, stripeCustomerId?: string): Promise<{ id: number | bigint; email: string; name?: string; api_key: string; password?: string }> {\n const db = getDb()\n const api_key = generateApiKey()\n const plainPassword = password ?? randomBytes(6).toString('hex')\n const password_hash = hashPassword(plainPassword)\n const result = await db.execute({\n sql: 'INSERT INTO users (email, name, api_key, password_hash, stripe_customer_id) VALUES (?, ?, ?, ?, ?)',\n args: [email, name ?? null, api_key, password_hash, stripeCustomerId ?? null],\n })\n return { id: result.lastInsertRowid!, email, name, api_key, password: plainPassword }\n}\n\nexport async function checkRateLimit(\n scope: string,\n key: string,\n limit: number,\n windowSeconds: number,\n): Promise<{ allowed: boolean; count: number; remaining: number; resetSeconds: number }> {\n const nowSeconds = Math.floor(Date.now() / 1000)\n const windowStart = Math.floor(nowSeconds / windowSeconds) * windowSeconds\n const keyHash = createHash('sha256').update(key).digest('hex')\n const db = getDb()\n\n if (!_rateLimitSchemaReady) {\n await db.execute(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n scope TEXT NOT NULL,\n key_hash TEXT NOT NULL,\n window_start INTEGER NOT NULL,\n count INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (scope, key_hash, window_start)\n )\n `)\n _rateLimitSchemaReady = true\n }\n\n await db.execute({\n sql: `\n INSERT INTO rate_limits (scope, key_hash, window_start, count)\n VALUES (?, ?, ?, 1)\n ON CONFLICT(scope, key_hash, window_start)\n DO UPDATE SET count = count + 1, updated_at = datetime('now')\n `,\n args: [scope, keyHash, windowStart],\n })\n\n const res = await db.execute({\n sql: 'SELECT count FROM rate_limits WHERE scope = ? AND key_hash = ? AND window_start = ?',\n args: [scope, keyHash, windowStart],\n })\n const count = Number(res.rows[0]?.count ?? 1)\n return {\n allowed: count <= limit,\n count,\n remaining: Math.max(0, limit - count),\n resetSeconds: Math.max(1, windowStart + windowSeconds - nowSeconds),\n }\n}\n\nexport async function createPasswordResetToken(userId: number | bigint): Promise<string> {\n const token = randomBytes(32).toString('hex')\n const tokenHash = createHash('sha256').update(token).digest('hex')\n const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString()\n await getDb().execute({\n sql: 'INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)',\n args: [tokenHash, userId, expiresAt],\n })\n return token\n}\n\nexport async function consumePasswordResetToken(token: string): Promise<number | null> {\n const db = getDb()\n const tokenHash = createHash('sha256').update(token).digest('hex')\n const res = await db.execute({\n sql: \"SELECT user_id, expires_at, used FROM password_reset_tokens WHERE token = ?\",\n args: [tokenHash],\n })\n const row = res.rows[0]\n if (!row) return null\n if (Number(row.used)) return null\n if (new Date(String(row.expires_at)) < new Date()) return null\n await db.execute({ sql: 'UPDATE password_reset_tokens SET used = 1 WHERE token = ?', args: [tokenHash] })\n return Number(row.user_id)\n}\n\nexport async function rotateApiKey(userId: number | bigint): Promise<string> {\n const newKey = generateApiKey()\n await getDb().execute({ sql: 'UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?', args: [newKey, userId] })\n return newKey\n}\n\nexport async function revokeApiKey(userId: number | bigint): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET key_active = 0 WHERE id = ?', args: [userId] })\n}\n\nexport async function listUsers(): Promise<Omit<User, 'api_key'>[]> {\n const res = await getDb().execute('SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC')\n return res.rows.map(r => rowToUser({ ...r as unknown as Record<string, unknown>, api_key: '', key_active: 1 }))\n}\n\nexport async function deactivateUser(id: number): Promise<void> {\n await getDb().execute({ sql: 'UPDATE users SET active = 0 WHERE id = ?', args: [id] })\n}\n\ninterface RawJob {\n id: string\n user_id: number\n status: string\n query: string\n options: string\n callback_url: string | null\n result: string | null\n error: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n}\n\nexport interface Job {\n id: string\n user_id: number\n status: string\n query: string\n options: Record<string, unknown>\n callback_url: string | null\n result: unknown | null\n error: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n}\n\nexport interface HarvestAttemptLog {\n id: string\n job_id: string\n user_id: number\n attempt_number: number\n max_attempts: number\n query: string\n location: string | null\n max_questions: number\n status: string\n outcome: string | null\n kernel_session_id: string | null\n question_count: number | null\n duration_ms: number | null\n error: string | null\n will_retry: boolean | null\n kernel_delete_started: boolean\n kernel_delete_succeeded: boolean | null\n kernel_delete_error: string | null\n browser_close_succeeded: boolean | null\n browser_close_error: string | null\n debug: unknown | null\n created_at: string\n completed_at: string | null\n}\n\nexport interface StartHarvestAttemptInput {\n jobId: string\n userId: number | bigint\n attemptNumber: number\n maxAttempts: number\n query: string\n location: string | null\n maxQuestions: number\n startedAt: string\n}\n\nexport interface FinishHarvestAttemptInput {\n jobId: string\n attemptNumber: number\n outcome: string\n kernelSessionId: string | null\n questionCount: number\n durationMs: number\n error: string | null\n willRetry: boolean\n kernelDeleteStarted: boolean\n kernelDeleteSucceeded: boolean | null\n kernelDeleteError: string | null\n browserCloseSucceeded: boolean | null\n browserCloseError: string | null\n debug: unknown | null\n completedAt: string\n}\n\nfunction rowToRawJob(row: Record<string, unknown>): RawJob {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n status: String(row.status),\n query: String(row.query),\n options: String(row.options),\n callback_url: row.callback_url != null ? String(row.callback_url) : null,\n result: row.result != null ? String(row.result) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n started_at: row.started_at != null ? String(row.started_at) : null,\n completed_at: row.completed_at != null ? String(row.completed_at) : null,\n }\n}\n\nfunction nullableBoolean(value: unknown): boolean | null {\n return value == null ? null : Number(value) === 1\n}\n\nfunction parseNullableJson(value: unknown): unknown | null {\n if (value == null) return null\n try {\n return JSON.parse(String(value)) as unknown\n } catch {\n return null\n }\n}\n\nfunction rowToHarvestAttemptLog(row: Record<string, unknown>): HarvestAttemptLog {\n return {\n id: String(row.id),\n job_id: String(row.job_id),\n user_id: Number(row.user_id),\n attempt_number: Number(row.attempt_number),\n max_attempts: Number(row.max_attempts),\n query: String(row.query),\n location: row.location != null ? String(row.location) : null,\n max_questions: Number(row.max_questions),\n status: String(row.status),\n outcome: row.outcome != null ? String(row.outcome) : null,\n kernel_session_id: row.kernel_session_id != null ? String(row.kernel_session_id) : null,\n question_count: row.question_count != null ? Number(row.question_count) : null,\n duration_ms: row.duration_ms != null ? Number(row.duration_ms) : null,\n error: row.error != null ? String(row.error) : null,\n will_retry: nullableBoolean(row.will_retry),\n kernel_delete_started: Number(row.kernel_delete_started ?? 0) === 1,\n kernel_delete_succeeded: nullableBoolean(row.kernel_delete_succeeded),\n kernel_delete_error: row.kernel_delete_error != null ? String(row.kernel_delete_error) : null,\n browser_close_succeeded: nullableBoolean(row.browser_close_succeeded),\n browser_close_error: row.browser_close_error != null ? String(row.browser_close_error) : null,\n debug: parseNullableJson(row.debug_json),\n created_at: String(row.created_at),\n completed_at: row.completed_at != null ? String(row.completed_at) : null,\n }\n}\n\nfunction deserialize(raw: RawJob): Job {\n return {\n ...raw,\n options: JSON.parse(raw.options) as Record<string, unknown>,\n result: raw.result ? JSON.parse(raw.result) : null,\n }\n}\n\nexport async function createJob(userId: number | bigint, query: string, options: object, callbackUrl?: string): Promise<string> {\n const id = randomUUID()\n await getDb().execute({\n sql: 'INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)',\n args: [id, userId, query, JSON.stringify(options), callbackUrl ?? null],\n })\n return id\n}\n\nexport async function createRunningJob(userId: number | bigint, query: string, options: object): Promise<string> {\n const id = randomUUID()\n await getDb().execute({\n sql: `INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))`,\n args: [id, userId, query, JSON.stringify(options)],\n })\n return id\n}\n\nexport async function startHarvestAttempt(input: StartHarvestAttemptInput): Promise<void> {\n await getDb().execute({\n sql: `\n INSERT INTO harvest_attempts (\n id, job_id, user_id, attempt_number, max_attempts, query, location, max_questions, status, created_at\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?)\n ON CONFLICT(job_id, attempt_number) DO UPDATE SET\n status = 'running',\n created_at = excluded.created_at,\n completed_at = NULL,\n outcome = NULL,\n error = NULL,\n debug_json = NULL\n `,\n args: [\n randomUUID(),\n input.jobId,\n input.userId,\n input.attemptNumber,\n input.maxAttempts,\n input.query,\n input.location,\n input.maxQuestions,\n input.startedAt,\n ],\n })\n}\n\nexport async function finishHarvestAttempt(input: FinishHarvestAttemptInput): Promise<void> {\n await getDb().execute({\n sql: `\n UPDATE harvest_attempts SET\n status = 'finished',\n outcome = ?,\n kernel_session_id = ?,\n question_count = ?,\n duration_ms = ?,\n error = ?,\n will_retry = ?,\n kernel_delete_started = ?,\n kernel_delete_succeeded = ?,\n kernel_delete_error = ?,\n browser_close_succeeded = ?,\n browser_close_error = ?,\n debug_json = ?,\n completed_at = ?\n WHERE job_id = ? AND attempt_number = ?\n `,\n args: [\n input.outcome,\n input.kernelSessionId,\n input.questionCount,\n input.durationMs,\n input.error,\n input.willRetry ? 1 : 0,\n input.kernelDeleteStarted ? 1 : 0,\n input.kernelDeleteSucceeded == null ? null : input.kernelDeleteSucceeded ? 1 : 0,\n input.kernelDeleteError,\n input.browserCloseSucceeded == null ? null : input.browserCloseSucceeded ? 1 : 0,\n input.browserCloseError,\n input.debug == null ? null : JSON.stringify(input.debug),\n input.completedAt,\n input.jobId,\n input.attemptNumber,\n ],\n })\n}\n\nexport async function listHarvestAttempts(jobId: string, userId?: number | bigint): Promise<HarvestAttemptLog[]> {\n const res = userId == null\n ? await getDb().execute({\n sql: `SELECT * FROM harvest_attempts WHERE job_id = ? ORDER BY attempt_number ASC`,\n args: [jobId],\n })\n : await getDb().execute({\n sql: `\n SELECT ha.*\n FROM harvest_attempts ha\n INNER JOIN jobs j ON j.id = ha.job_id\n WHERE ha.job_id = ? AND j.user_id = ?\n ORDER BY ha.attempt_number ASC\n `,\n args: [jobId, userId],\n })\n return res.rows.map(r => rowToHarvestAttemptLog(r as unknown as Record<string, unknown>))\n}\n\nexport async function getJob(id: string, userId?: number | bigint): Promise<Job | undefined> {\n const db = getDb()\n const res = userId\n ? await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ? AND user_id = ?', args: [id, userId] })\n : await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ?', args: [id] })\n return res.rows[0] ? deserialize(rowToRawJob(res.rows[0] as unknown as Record<string, unknown>)) : undefined\n}\n\nexport async function listJobs(userId: number | bigint): Promise<Job[]> {\n const res = await getDb().execute({ sql: 'SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50', args: [userId] })\n return res.rows.map(r => deserialize(rowToRawJob(r as unknown as Record<string, unknown>)))\n}\n\nexport interface RequestEvent {\n id: string\n user_id: number\n source: string\n status: string\n query: string\n location: string | null\n result_count: number | null\n result: unknown | null\n error: string | null\n created_at: string\n}\n\nfunction rowToRequestEvent(row: Record<string, unknown>): RequestEvent {\n const rawResult = row.result != null ? String(row.result) : null\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n source: String(row.source),\n status: String(row.status),\n query: String(row.query),\n location: row.location != null ? String(row.location) : null,\n result_count: row.result_count != null ? Number(row.result_count) : null,\n result: rawResult ? JSON.parse(rawResult) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n }\n}\n\nexport async function logRequestEvent(input: {\n userId: number | bigint\n source: string\n status: 'done' | 'failed'\n query: string\n location?: string | null\n resultCount?: number | null\n result?: unknown\n error?: string | null\n}): Promise<string> {\n const id = randomUUID()\n await getDb().execute({\n sql: `\n INSERT INTO request_events (id, user_id, source, status, query, location, result_count, result, error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args: [\n id,\n input.userId,\n input.source,\n input.status,\n input.query,\n input.location ?? null,\n input.resultCount ?? null,\n input.result != null ? JSON.stringify(input.result) : null,\n input.error ?? null,\n ],\n })\n return id\n}\n\nexport async function listRequestEvents(userId: number | bigint, limit = 100): Promise<RequestEvent[]> {\n const res = await getDb().execute({\n sql: `\n SELECT id, user_id, source, status, query, location, result_count, result, error, created_at\n FROM request_events\n WHERE user_id = ?\n ORDER BY created_at DESC\n LIMIT ?\n `,\n args: [userId, limit],\n })\n return res.rows.map(r => rowToRequestEvent(r as unknown as Record<string, unknown>))\n}\n\nexport type WorkflowScheduleStatus = 'active' | 'paused' | 'deleted'\nexport type WorkflowCadence = 'daily' | 'weekly' | 'monthly'\nexport type WorkflowRunStatus = 'queued' | 'running' | 'succeeded' | 'partial' | 'failed' | 'cancelled'\n\nexport interface WorkflowScheduleRecord {\n id: string\n user_id: number\n workflow_id: string\n name: string\n input: Record<string, unknown>\n cadence: WorkflowCadence\n cron_expr: string | null\n timezone: string\n webhook_url: string | null\n status: WorkflowScheduleStatus\n next_run_at: string | null\n last_run_at: string | null\n created_at: string\n updated_at: string\n}\n\nexport interface WorkflowRunRecord {\n id: string\n user_id: number\n schedule_id: string | null\n workflow_id: string\n input: Record<string, unknown>\n status: WorkflowRunStatus\n manifest: unknown | null\n error_message: string | null\n queued_at: string\n started_at: string | null\n completed_at: string | null\n idempotency_key: string | null\n step_index: number\n total_steps: number | null\n step_log: WorkflowStepLogEntry[]\n webhook_url: string | null\n}\n\nexport interface WorkflowStepLogEntry {\n stepIndex: number\n stepId: string\n stepTitle: string\n output: Record<string, unknown>\n warnings: string[]\n at: string\n}\n\nexport interface WorkflowArtifactRecord {\n id: string\n run_id: string\n user_id: number\n kind: string\n label: string\n path: string\n content_type: string\n bytes: number | null\n rows_count: number | null\n content: string | null\n created_at: string\n}\n\nfunction parseJsonRecordValue(value: unknown): Record<string, unknown> {\n if (value == null) return {}\n try {\n const parsed = JSON.parse(String(value)) as unknown\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed as Record<string, unknown> : {}\n } catch {\n return {}\n }\n}\n\nfunction rowToWorkflowSchedule(row: Record<string, unknown>): WorkflowScheduleRecord {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n workflow_id: String(row.workflow_id),\n name: String(row.name),\n input: parseJsonRecordValue(row.input_json),\n cadence: String(row.cadence) as WorkflowCadence,\n cron_expr: row.cron_expr != null ? String(row.cron_expr) : null,\n timezone: String(row.timezone ?? 'UTC'),\n webhook_url: row.webhook_url != null ? String(row.webhook_url) : null,\n status: String(row.status) as WorkflowScheduleStatus,\n next_run_at: row.next_run_at != null ? String(row.next_run_at) : null,\n last_run_at: row.last_run_at != null ? String(row.last_run_at) : null,\n created_at: String(row.created_at),\n updated_at: String(row.updated_at),\n }\n}\n\nfunction rowToWorkflowRun(row: Record<string, unknown>): WorkflowRunRecord {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n schedule_id: row.schedule_id != null ? String(row.schedule_id) : null,\n workflow_id: String(row.workflow_id),\n input: parseJsonRecordValue(row.input_json),\n status: String(row.status) as WorkflowRunStatus,\n manifest: row.manifest_json != null ? parseNullableJson(row.manifest_json) : null,\n error_message: row.error_message != null ? String(row.error_message) : null,\n queued_at: String(row.queued_at),\n started_at: row.started_at != null ? String(row.started_at) : null,\n completed_at: row.completed_at != null ? String(row.completed_at) : null,\n idempotency_key: row.idempotency_key != null ? String(row.idempotency_key) : null,\n step_index: row.step_index != null ? Number(row.step_index) : 0,\n total_steps: row.total_steps != null ? Number(row.total_steps) : null,\n step_log: parseWorkflowStepLog(row.step_log_json),\n webhook_url: row.webhook_url != null ? String(row.webhook_url) : null,\n }\n}\n\nfunction parseWorkflowStepLog(value: unknown): WorkflowStepLogEntry[] {\n if (value == null) return []\n try {\n const parsed = JSON.parse(String(value)) as unknown\n return Array.isArray(parsed) ? parsed as WorkflowStepLogEntry[] : []\n } catch {\n return []\n }\n}\n\nfunction rowToWorkflowArtifact(row: Record<string, unknown>): WorkflowArtifactRecord {\n return {\n id: String(row.id),\n run_id: String(row.run_id),\n user_id: Number(row.user_id),\n kind: String(row.kind),\n label: String(row.label),\n path: String(row.path),\n content_type: String(row.content_type),\n bytes: row.bytes != null ? Number(row.bytes) : null,\n rows_count: row.rows_count != null ? Number(row.rows_count) : null,\n content: row.content != null ? String(row.content) : null,\n created_at: String(row.created_at),\n }\n}\n\nexport async function createWorkflowSchedule(input: {\n userId: number | bigint\n workflowId: string\n name: string\n workflowInput: Record<string, unknown>\n cadence: WorkflowCadence\n timezone?: string\n webhookUrl?: string | null\n nextRunAt?: string | null\n}): Promise<WorkflowScheduleRecord> {\n const id = randomUUID()\n await getDb().execute({\n sql: `\n INSERT INTO workflow_schedules (\n id, user_id, workflow_id, name, input_json, cadence, timezone, webhook_url, next_run_at\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args: [\n id,\n input.userId,\n input.workflowId,\n input.name,\n JSON.stringify(input.workflowInput),\n input.cadence,\n input.timezone ?? 'UTC',\n input.webhookUrl ?? null,\n input.nextRunAt ?? null,\n ],\n })\n const schedule = await getWorkflowSchedule(id, input.userId)\n if (!schedule) throw new Error('Failed to create workflow schedule')\n return schedule\n}\n\nexport async function getWorkflowSchedule(id: string, userId?: number | bigint): Promise<WorkflowScheduleRecord | null> {\n const res = userId == null\n ? await getDb().execute({ sql: 'SELECT * FROM workflow_schedules WHERE id = ? AND status != ? LIMIT 1', args: [id, 'deleted'] })\n : await getDb().execute({ sql: 'SELECT * FROM workflow_schedules WHERE id = ? AND user_id = ? AND status != ? LIMIT 1', args: [id, userId, 'deleted'] })\n return res.rows[0] ? rowToWorkflowSchedule(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function listWorkflowSchedules(userId: number | bigint): Promise<WorkflowScheduleRecord[]> {\n const res = await getDb().execute({\n sql: `SELECT * FROM workflow_schedules WHERE user_id = ? AND status != 'deleted' ORDER BY created_at DESC LIMIT 100`,\n args: [userId],\n })\n return res.rows.map(r => rowToWorkflowSchedule(r as unknown as Record<string, unknown>))\n}\n\nexport async function patchWorkflowSchedule(input: {\n id: string\n userId: number | bigint\n name?: string\n status?: WorkflowScheduleStatus\n workflowInput?: Record<string, unknown>\n cadence?: WorkflowCadence\n timezone?: string\n webhookUrl?: string | null\n nextRunAt?: string | null\n}): Promise<WorkflowScheduleRecord | null> {\n const sets: string[] = []\n const args: Array<string | number | bigint | null> = []\n if (input.name !== undefined) { sets.push('name = ?'); args.push(input.name) }\n if (input.status !== undefined) { sets.push('status = ?'); args.push(input.status) }\n if (input.workflowInput !== undefined) { sets.push('input_json = ?'); args.push(JSON.stringify(input.workflowInput)) }\n if (input.cadence !== undefined) { sets.push('cadence = ?'); args.push(input.cadence) }\n if (input.timezone !== undefined) { sets.push('timezone = ?'); args.push(input.timezone) }\n if (input.webhookUrl !== undefined) { sets.push('webhook_url = ?'); args.push(input.webhookUrl) }\n if (input.nextRunAt !== undefined) { sets.push('next_run_at = ?'); args.push(input.nextRunAt) }\n if (sets.length === 0) return getWorkflowSchedule(input.id, input.userId)\n sets.push(`updated_at = datetime('now')`)\n await getDb().execute({\n sql: `UPDATE workflow_schedules SET ${sets.join(', ')} WHERE id = ? AND user_id = ? AND status != 'deleted'`,\n args: [...args, input.id, input.userId],\n })\n return getWorkflowSchedule(input.id, input.userId)\n}\n\nexport async function deleteWorkflowSchedule(id: string, userId: number | bigint): Promise<boolean> {\n const res = await getDb().execute({\n sql: `UPDATE workflow_schedules SET status = 'deleted', updated_at = datetime('now') WHERE id = ? AND user_id = ? AND status != 'deleted'`,\n args: [id, userId],\n })\n return res.rowsAffected > 0\n}\n\nexport async function listDueWorkflowSchedules(nowIso: string, limit = 10): Promise<WorkflowScheduleRecord[]> {\n const res = await getDb().execute({\n sql: `\n SELECT * FROM workflow_schedules\n WHERE status = 'active' AND next_run_at IS NOT NULL AND next_run_at <= ?\n ORDER BY next_run_at ASC\n LIMIT ?\n `,\n args: [nowIso, limit],\n })\n return res.rows.map(r => rowToWorkflowSchedule(r as unknown as Record<string, unknown>))\n}\n\nexport async function markWorkflowScheduleRan(id: string, lastRunAt: string, nextRunAt: string | null): Promise<void> {\n await getDb().execute({\n sql: `UPDATE workflow_schedules SET last_run_at = ?, next_run_at = ?, updated_at = datetime('now') WHERE id = ?`,\n args: [lastRunAt, nextRunAt, id],\n })\n}\n\nexport async function createWorkflowRun(input: {\n id?: string\n userId: number | bigint\n workflowId: string\n workflowInput: Record<string, unknown>\n scheduleId?: string | null\n idempotencyKey?: string | null\n webhookUrl?: string | null\n totalSteps?: number | null\n}): Promise<WorkflowRunRecord> {\n const id = input.id ?? randomUUID()\n const args = [\n id,\n input.userId,\n input.scheduleId ?? null,\n input.workflowId,\n JSON.stringify(input.workflowInput),\n input.idempotencyKey ?? null,\n input.webhookUrl ?? null,\n input.totalSteps ?? null,\n ]\n try {\n await getDb().execute({\n sql: `\n INSERT INTO workflow_runs (id, user_id, schedule_id, workflow_id, input_json, idempotency_key, webhook_url, total_steps)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args,\n })\n } catch (err) {\n if (input.idempotencyKey) {\n const existing = await getWorkflowRunByIdempotencyKey(input.idempotencyKey)\n if (existing) return existing\n }\n throw err\n }\n const run = await getWorkflowRun(id, input.userId)\n if (!run) throw new Error('Failed to create workflow run')\n return run\n}\n\nexport async function getWorkflowRun(id: string, userId?: number | bigint): Promise<WorkflowRunRecord | null> {\n const res = userId == null\n ? await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE id = ? LIMIT 1', args: [id] })\n : await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1', args: [id, userId] })\n return res.rows[0] ? rowToWorkflowRun(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function getWorkflowRunByIdempotencyKey(idempotencyKey: string): Promise<WorkflowRunRecord | null> {\n const res = await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE idempotency_key = ? LIMIT 1', args: [idempotencyKey] })\n return res.rows[0] ? rowToWorkflowRun(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function listWorkflowRuns(userId: number | bigint, limit = 50): Promise<WorkflowRunRecord[]> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM workflow_runs WHERE user_id = ? ORDER BY queued_at DESC LIMIT ?',\n args: [userId, limit],\n })\n return res.rows.map(r => rowToWorkflowRun(r as unknown as Record<string, unknown>))\n}\n\nexport async function markWorkflowRunRunning(id: string): Promise<void> {\n await getDb().execute({\n sql: `UPDATE workflow_runs SET status = 'running', started_at = COALESCE(started_at, datetime('now')) WHERE id = ?`,\n args: [id],\n })\n}\n\nexport async function completeWorkflowRunRecord(id: string, status: Exclude<WorkflowRunStatus, 'queued' | 'running' | 'cancelled'>, manifest: unknown): Promise<void> {\n await getDb().execute({\n sql: `UPDATE workflow_runs SET status = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?`,\n args: [status, JSON.stringify(manifest), id],\n })\n}\n\nexport async function failWorkflowRunRecord(id: string, errorMessage: string, manifest?: unknown): Promise<void> {\n await getDb().execute({\n sql: `UPDATE workflow_runs SET status = 'failed', error_message = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?`,\n args: [errorMessage, manifest == null ? null : JSON.stringify(manifest), id],\n })\n}\n\nfunction workflowArtifactContentType(kind: string): string {\n if (kind === 'html_report') return 'text/html; charset=utf-8'\n if (kind === 'csv') return 'text/csv; charset=utf-8'\n if (kind === 'json') return 'application/json; charset=utf-8'\n return 'text/plain; charset=utf-8'\n}\n\nexport interface WorkflowArtifactInput {\n kind: string\n label: string\n path: string\n bytes?: number\n rows?: number\n content?: string | null\n}\n\nasync function insertWorkflowArtifact(runId: string, userId: number | bigint, artifact: WorkflowArtifactInput): Promise<void> {\n await getDb().execute({\n sql: `\n INSERT INTO workflow_artifacts (id, run_id, user_id, kind, label, path, content_type, bytes, rows_count, content)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args: [\n randomUUID(),\n runId,\n userId,\n artifact.kind,\n artifact.label,\n artifact.path,\n workflowArtifactContentType(artifact.kind),\n artifact.bytes ?? null,\n artifact.rows ?? null,\n artifact.content ?? null,\n ],\n })\n}\n\nexport async function replaceWorkflowArtifacts(runId: string, userId: number | bigint, artifacts: WorkflowArtifactInput[]): Promise<WorkflowArtifactRecord[]> {\n await getDb().execute({ sql: 'DELETE FROM workflow_artifacts WHERE run_id = ? AND user_id = ?', args: [runId, userId] })\n for (const artifact of artifacts) await insertWorkflowArtifact(runId, userId, artifact)\n return listWorkflowArtifacts(runId, userId)\n}\n\nexport async function appendWorkflowArtifacts(runId: string, userId: number | bigint, artifacts: WorkflowArtifactInput[]): Promise<WorkflowArtifactRecord[]> {\n for (const artifact of artifacts) await insertWorkflowArtifact(runId, userId, artifact)\n return listWorkflowArtifacts(runId, userId)\n}\n\nexport async function getWorkflowRunStepState(id: string, userId?: number | bigint): Promise<{ stepIndex: number; totalSteps: number | null; state: unknown; stepLog: WorkflowStepLogEntry[] } | null> {\n const res = userId == null\n ? await getDb().execute({ sql: 'SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? LIMIT 1', args: [id] })\n : await getDb().execute({ sql: 'SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1', args: [id, userId] })\n const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n if (!row) return null\n return {\n stepIndex: row.step_index != null ? Number(row.step_index) : 0,\n totalSteps: row.total_steps != null ? Number(row.total_steps) : null,\n state: row.step_state_json != null ? parseNullableJson(row.step_state_json) : null,\n stepLog: parseWorkflowStepLog(row.step_log_json),\n }\n}\n\nexport async function advanceWorkflowRunStep(id: string, input: {\n stepIndex: number\n totalSteps: number\n state: unknown\n status: WorkflowRunStatus\n logEntry: WorkflowStepLogEntry\n appendLog: WorkflowStepLogEntry[]\n}): Promise<void> {\n const stepLog = [...input.appendLog, input.logEntry]\n await getDb().execute({\n sql: `UPDATE workflow_runs SET status = ?, step_index = ?, total_steps = ?, step_state_json = ?, step_log_json = ?, started_at = COALESCE(started_at, datetime('now')) WHERE id = ?`,\n args: [input.status, input.stepIndex, input.totalSteps, JSON.stringify(input.state ?? null), JSON.stringify(stepLog), id],\n })\n}\n\nexport async function listWorkflowArtifacts(runId: string, userId: number | bigint): Promise<WorkflowArtifactRecord[]> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM workflow_artifacts WHERE run_id = ? AND user_id = ? ORDER BY created_at ASC',\n args: [runId, userId],\n })\n return res.rows.map(r => rowToWorkflowArtifact(r as unknown as Record<string, unknown>))\n}\n\nexport async function getWorkflowArtifact(id: string, userId: number | bigint): Promise<WorkflowArtifactRecord | null> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM workflow_artifacts WHERE id = ? AND user_id = ? LIMIT 1',\n args: [id, userId],\n })\n return res.rows[0] ? rowToWorkflowArtifact(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function recordWorkflowWebhookDelivery(input: {\n runId: string\n scheduleId?: string | null\n userId: number | bigint\n webhookUrl: string\n status: 'delivered' | 'failed'\n attempts: number\n lastError?: string | null\n}): Promise<void> {\n await getDb().execute({\n sql: `\n INSERT INTO workflow_webhook_deliveries (\n id, run_id, schedule_id, user_id, webhook_url, status, attempts, last_error, delivered_at\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args: [\n randomUUID(),\n input.runId,\n input.scheduleId ?? null,\n input.userId,\n input.webhookUrl,\n input.status,\n input.attempts,\n input.lastError ?? null,\n input.status === 'delivered' ? new Date().toISOString() : null,\n ],\n })\n}\n\nexport async function countActiveUsers(): Promise<number> {\n const res = await getDb().execute(`SELECT COUNT(*) as n FROM users WHERE active = 1`)\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function countActiveJobsForUser(userId: number | bigint): Promise<number> {\n const res = await getDb().execute({\n sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND (status = 'pending' OR (status = 'running' AND started_at > datetime('now', '-10 minutes')))`,\n args: [userId],\n })\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function claimPendingJob(): Promise<RawJob | undefined> {\n const db = getDb()\n const res = await db.execute(`SELECT * FROM jobs WHERE status = 'pending' ORDER BY created_at LIMIT 1`)\n const job = res.rows[0] ? rowToRawJob(res.rows[0] as unknown as Record<string, unknown>) : undefined\n if (!job) return undefined\n const upd = await db.execute({\n sql: `UPDATE jobs SET status = 'running', started_at = datetime('now') WHERE id = ? AND status = 'pending'`,\n args: [job.id],\n })\n return upd.rowsAffected === 0 ? undefined : { ...job, status: 'running' }\n}\n\nexport async function completeJob(id: string, result: object): Promise<void> {\n await getDb().execute({ sql: `UPDATE jobs SET status = 'done', result = ?, completed_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), id] })\n}\n\nexport async function failJob(id: string, error: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE jobs SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [error, id] })\n}\n\nexport async function cancelJob(id: string, reason: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE jobs SET status = 'cancelled', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [reason, id] })\n}\n\nexport interface KpoJobRow {\n id: string\n user_id: number\n status: string\n entity_name: string\n entity_type: string\n url: string | null\n request: string\n result: string | null\n error: string | null\n created_at: string\n updated_at: string\n}\n\nexport interface KpoPhaseLogRow {\n id: number\n job_id: string\n phase: string\n output: string\n created_at: string\n}\n\nfunction rowToKpoJob(row: Record<string, unknown>): KpoJobRow {\n return {\n id: String(row.id),\n user_id: Number(row.user_id),\n status: String(row.status),\n entity_name: String(row.entity_name),\n entity_type: String(row.entity_type),\n url: row.url != null ? String(row.url) : null,\n request: String(row.request),\n result: row.result != null ? String(row.result) : null,\n error: row.error != null ? String(row.error) : null,\n created_at: String(row.created_at),\n updated_at: String(row.updated_at),\n }\n}\n\nfunction rowToKpoPhaseLog(row: Record<string, unknown>): KpoPhaseLogRow {\n return {\n id: Number(row.id),\n job_id: String(row.job_id),\n phase: String(row.phase),\n output: String(row.output),\n created_at: String(row.created_at),\n }\n}\n\nexport async function createKpoJob(jobId: string, userId: number, entityName: string, entityType: string, request: object, url?: string): Promise<void> {\n await getDb().execute({\n sql: `INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)`,\n args: [jobId, userId, entityName, entityType, url ?? null, JSON.stringify(request)],\n })\n}\n\nexport async function claimPendingKpoJob(): Promise<KpoJobRow | null> {\n const db = getDb()\n const upd = await db.execute(\n `UPDATE kpo_jobs SET status='running', updated_at=datetime('now') WHERE id = (SELECT id FROM kpo_jobs WHERE status='pending' ORDER BY created_at ASC LIMIT 1) RETURNING *`\n )\n if (upd.rowsAffected === 0) return null\n return upd.rows[0] ? rowToKpoJob(upd.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function getKpoJob(jobId: string): Promise<KpoJobRow | null> {\n const res = await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE id = ?`, args: [jobId] })\n return res.rows[0] ? rowToKpoJob(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function updateKpoJobState(jobId: string, status: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?`, args: [status, jobId] })\n}\n\nexport async function completeKpoJob(jobId: string, result: object): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), jobId] })\n}\n\nexport async function failKpoJob(jobId: string, error: string): Promise<void> {\n await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?`, args: [error, jobId] })\n}\n\nexport async function logKpoPhaseComplete(jobId: string, phase: string, output: unknown): Promise<void> {\n await getDb().execute({\n sql: `INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)`,\n args: [jobId, phase, JSON.stringify(output)],\n })\n}\n\nexport async function getKpoPhaseLog(jobId: string): Promise<KpoPhaseLogRow[]> {\n const res = await getDb().execute({ sql: `SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC`, args: [jobId] })\n return res.rows.map(r => rowToKpoPhaseLog(r as unknown as Record<string, unknown>))\n}\n\nexport async function listKpoJobs(userId?: number): Promise<KpoJobRow[]> {\n const res = userId != null\n ? await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50`, args: [userId] })\n : await getDb().execute(`SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50`)\n return res.rows.map(r => rowToKpoJob(r as unknown as Record<string, unknown>))\n}\n\nexport async function getUserByStripeCustomerId(customerId: string): Promise<User | undefined> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1',\n args: [customerId],\n })\n return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function setStripeCustomerId(userId: number, customerId: string): Promise<void> {\n await getDb().execute({\n sql: 'UPDATE users SET stripe_customer_id = ? WHERE id = ?',\n args: [customerId, userId],\n })\n}\n\nexport async function setExtraConcurrencySlots(userId: number, slots: number): Promise<void> {\n await getDb().execute({\n sql: 'UPDATE users SET extra_concurrency_slots = ? WHERE id = ?',\n args: [Math.max(0, slots), userId],\n })\n}\n\nexport async function setConcurrencySubId(userId: number, subId: string | null): Promise<void> {\n await getDb().execute({\n sql: 'UPDATE users SET concurrency_stripe_sub_id = ? WHERE id = ?',\n args: [subId, userId],\n })\n}\n\nexport async function creditMc(\n userId: number,\n mc: number,\n operation: string,\n description?: string,\n stripePaymentIntent?: string,\n): Promise<number> {\n const db = getDb()\n await db.batch([\n { sql: 'UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?', args: [mc, userId] },\n { sql: 'INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)', args: [userId, mc, operation, description ?? null, stripePaymentIntent ?? null] },\n ], 'write')\n const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n return Number(res.rows[0]?.balance_mc ?? 0)\n}\n\nexport async function ledgerExistsForOperation(userId: number, operation: string): Promise<boolean> {\n const res = await getDb().execute({\n sql: 'SELECT 1 FROM ledger WHERE user_id = ? AND operation = ? LIMIT 1',\n args: [userId, operation],\n })\n return res.rows.length > 0\n}\n\nexport async function claimMonthlyFreeRefresh(userId: number, month: string): Promise<boolean> {\n const res = await getDb().execute({\n sql: 'INSERT OR IGNORE INTO free_credit_refreshes (user_id, month) VALUES (?, ?)',\n args: [userId, month],\n })\n return res.rowsAffected > 0\n}\n\nexport async function debitMc(\n userId: number,\n mc: number,\n operation: string,\n description?: string,\n): Promise<{ ok: boolean; balance_mc: number }> {\n const db = getDb()\n const upd = await db.execute({\n sql: 'UPDATE users SET balance_mc = balance_mc - ? WHERE id = ? AND balance_mc >= ?',\n args: [mc, userId, mc],\n })\n if (upd.rowsAffected === 0) {\n const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n return { ok: false, balance_mc: Number(res.rows[0]?.balance_mc ?? 0) }\n }\n await db.execute({\n sql: 'INSERT INTO ledger (user_id, amount_mc, operation, description) VALUES (?, ?, ?, ?)',\n args: [userId, -mc, operation, description ?? null],\n })\n const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n return { ok: true, balance_mc: Number(res.rows[0]?.balance_mc ?? 0) }\n}\n\nexport async function getLedger(userId: number, limit = 50): Promise<LedgerRow[]> {\n const res = await getDb().execute({\n sql: 'SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',\n args: [userId, limit],\n })\n return res.rows.map(r => ({\n id: Number(r.id),\n user_id: Number(r.user_id),\n amount_mc: Number(r.amount_mc),\n operation: String(r.operation),\n description: r.description != null ? String(r.description) : null,\n stripe_pi: r.stripe_pi != null ? String(r.stripe_pi) : null,\n created_at: String(r.created_at),\n }))\n}\n\nexport async function ledgerExistsForStripePI(stripePI: string): Promise<boolean> {\n const res = await getDb().execute({\n sql: 'SELECT 1 FROM ledger WHERE stripe_pi = ? LIMIT 1',\n args: [stripePI],\n })\n return res.rows.length > 0\n}\n\nexport async function reconcileBalanceMc(userId: number | bigint): Promise<number> {\n const db = getDb()\n const res = await db.execute({\n sql: 'SELECT COALESCE(SUM(amount_mc), 0) AS balance_mc FROM ledger WHERE user_id = ?',\n args: [userId],\n })\n const balanceMc = Number(res.rows[0]?.balance_mc ?? 0)\n await db.execute({\n sql: 'UPDATE users SET balance_mc = ? WHERE id = ? AND balance_mc != ?',\n args: [balanceMc, userId, balanceMc],\n })\n return balanceMc\n}\n\nexport interface LedgerRow {\n id: number\n user_id: number\n amount_mc: number\n operation: string\n description: string | null\n stripe_pi: string | null\n created_at: string\n}\n\nexport const SiteAuditJobRowSchema = z.object({\n id: z.string(),\n user_id: z.number(),\n status: z.string().default('pending'),\n client_domain: z.string(),\n session_path: z.string(),\n request: z.string(),\n result: z.string().nullable(),\n error: z.string().nullable(),\n created_at: z.string(),\n updated_at: z.string(),\n});\nexport type SiteAuditJobRow = z.infer<typeof SiteAuditJobRowSchema>;\n\nexport const SiteAuditPhaseLogRowSchema = z.object({\n id: z.number(),\n job_id: z.string(),\n phase: z.string(),\n completed_at: z.string(),\n output_summary: z.string(),\n});\nexport type SiteAuditPhaseLogRow = z.infer<typeof SiteAuditPhaseLogRowSchema>;\n"],"mappings":";AAAA,SAAS,oBAAiC;AAC1C,SAAS,YAAY,aAAa,YAAY,YAAY,uBAAuB;AACjF,SAAS,SAAS;AAElB,IAAM,SAAS,QAAQ,IAAI,sBAAsB;AACjD,IAAM,WAAW,QAAQ,IAAI;AAE7B,IAAI,MAAqB;AACzB,IAAI,wBAAwB;AAErB,SAAS,QAAgB;AAC9B,MAAI,CAAC,IAAK,OAAM,aAAa,EAAE,KAAK,QAAQ,WAAW,SAAS,CAAC;AACjE,SAAO;AACT;AAEA,eAAsB,UAAyB;AAC7C,QAAM,KAAK,MAAM;AACjB,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUhB;AACD,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAchB;AACD,QAAM,GAAG,QAAQ,0DAA0D;AAC3E,QAAM,GAAG,QAAQ,wDAAwD;AACzE,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA2BhB;AACD,QAAM,GAAG,QAAQ,gFAAgF;AACjG,QAAM,GAAG,QAAQ,2GAA2G;AAC5H,MAAI;AAAE,UAAM,GAAG,QAAQ,yDAAyD;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC3F,MAAI;AAAE,UAAM,GAAG,QAAQ,iDAAiD;AAAA,EAAE,QAAQ;AAAA,EAAC;AACnF,MAAI;AAAE,UAAM,GAAG,QAAQ,oEAAoE;AAAA,EAAE,QAAQ;AAAA,EAAC;AAEtG,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAchB;AACD,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQhB;AACD,QAAM,GAAG,QAAQ,kEAAkE;AACnF,QAAM,GAAG,QAAQ,gEAAgE;AACjF,QAAM,GAAG,QAAQ,0EAA0E;AAC3F,MAAI;AAAE,UAAM,GAAG,QAAQ,sDAAsD;AAAA,EAAE,QAAQ;AAAA,EAAC;AACxF,MAAI;AAAE,UAAM,GAAG,QAAQ,oEAAoE;AAAA,EAAE,QAAQ;AAAA,EAAC;AACtG,MAAI;AAAE,UAAM,GAAG,QAAQ,iFAAiF;AAAA,EAAE,QAAQ;AAAA,EAAC;AACnH,MAAI;AAAE,UAAM,GAAG,QAAQ,6DAA6D;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC/F,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWhB;AACD,QAAM,GAAG,QAAQ,4GAA4G;AAC7H,QAAM,GAAG,QAAQ,4GAA4G;AAC7H,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUhB;AACD,QAAM,GAAG,QAAQ,8DAA8D;AAC/E,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOhB;AACD,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAahB;AACD,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQhB;AACD,QAAM,GAAG,QAAQ,gFAAgF;AACjG,QAAM,GAAG,QAAQ,8EAA8E;AAC/F,QAAM,GAAG,QAAQ,wFAAwF;AACzG,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQhB;AACD,QAAM,GAAG,QAAQ,0EAA0E;AAC3F,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAShB;AACD,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAahB;AACD,QAAM,GAAG,QAAQ,uGAAuG;AACxH,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBhB;AACD,QAAM,GAAG,QAAQ,mHAAmH;AACpI,QAAM,GAAG,QAAQ,8FAA8F;AAC/G,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAoBhB;AACD,MAAI;AAAE,UAAM,GAAG,QAAQ,4EAA4E;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC9G,MAAI;AAAE,UAAM,GAAG,QAAQ,0DAA0D;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC5F,MAAI;AAAE,UAAM,GAAG,QAAQ,2DAA2D;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC7F,MAAI;AAAE,UAAM,GAAG,QAAQ,yDAAyD;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC3F,MAAI;AAAE,UAAM,GAAG,QAAQ,uDAAuD;AAAA,EAAE,QAAQ;AAAA,EAAC;AACzF,QAAM,GAAG,QAAQ,iGAAiG;AAClH,QAAM,GAAG,QAAQ,iGAAiG;AAClH,QAAM,GAAG,QAAQ,iIAAiI;AAClJ,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAchB;AACD,MAAI;AAAE,UAAM,GAAG,QAAQ,wDAAwD;AAAA,EAAE,QAAQ;AAAA,EAAC;AAC1F,QAAM,GAAG,QAAQ,iFAAiF;AAClG,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAahB;AACD,QAAM,GAAG,QAAQ,mGAAmG;AACpH,QAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,GAKhB;AACH;AAEA,eAAsB,kBAAkB,SAAmC;AACzE,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,OAAO;AAAA,EAChB,CAAC;AACD,SAAO,IAAI,eAAe;AAC5B;AAEA,eAAsB,4BAA4B,SAAmC;AACnF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,OAAO;AAAA,EAChB,CAAC;AACD,SAAO,IAAI,KAAK,SAAS;AAC3B;AAEO,SAAS,iBAAyB;AACvC,SAAO,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC/C;AAiBO,SAAS,aAAa,UAA0B;AACrD,QAAM,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAC3C,QAAM,OAAO,WAAW,UAAU,MAAM,EAAE;AAC1C,SAAO,GAAG,IAAI,IAAI,KAAK,SAAS,KAAK,CAAC;AACxC;AAEO,SAAS,eAAe,UAAkB,QAAyB;AACxE,QAAM,CAAC,MAAM,IAAI,IAAI,OAAO,MAAM,GAAG;AACrC,QAAM,UAAU,OAAO,KAAK,MAAM,KAAK;AACvC,QAAM,UAAU,WAAW,UAAU,MAAM,EAAE;AAC7C,SAAO,gBAAgB,SAAS,OAAO;AACzC;AAEA,SAAS,UAAU,KAAoC;AACrD,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,MAAM,IAAI,QAAQ,OAAO,OAAO,IAAI,IAAI,IAAI;AAAA,IAC5C,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,YAAY,OAAO,IAAI,cAAc,CAAC;AAAA,IACtC,eAAe,IAAI,iBAAiB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,IACvE,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,oBAAoB,IAAI,sBAAsB,OAAO,OAAO,IAAI,kBAAkB,IAAI;AAAA,IACtF,YAAY,OAAO,IAAI,cAAc,CAAC;AAAA,IACtC,yBAAyB,OAAO,IAAI,2BAA2B,CAAC;AAAA,IAChE,2BAA2B,IAAI,6BAA6B,OAAO,OAAO,IAAI,yBAAyB,IAAI;AAAA,EAC7G;AACF;AAEA,eAAsB,eAAe,OAA0C;AAC7E,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,sDAAsD,MAAM,CAAC,KAAK,EAAE,CAAC;AAC9G,SAAO,IAAI,KAAK,CAAC,IAAI,UAAU,IAAI,KAAK,CAAC,CAAuC,IAAI;AACtF;AAEA,eAAsB,gBAAgB,SAA4C;AAChF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,2EAA2E,MAAM,CAAC,OAAO,EAAE,CAAC;AACrI,SAAO,IAAI,KAAK,CAAC,IAAI,UAAU,IAAI,KAAK,CAAC,CAAuC,IAAI;AACtF;AAEA,eAAsB,YAAY,IAAgD;AAChF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,mDAAmD,MAAM,CAAC,EAAE,EAAE,CAAC;AACxG,SAAO,IAAI,KAAK,CAAC,IAAI,UAAU,IAAI,KAAK,CAAC,CAAuC,IAAI;AACtF;AAEA,eAAsB,YAAY,IAAqB,UAAiC;AACtF,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,mDAAmD,MAAM,CAAC,aAAa,QAAQ,GAAG,EAAE,EAAE,CAAC;AACtH;AAEA,eAAsB,aAAa,QAAoI;AACrK,QAAM,KAAK,MAAM;AACjB,QAAM,CAAC,MAAM,OAAO,OAAO,KAAK,GAAG,IAAI,MAAM,QAAQ,IAAI;AAAA,IACvD,GAAG,QAAQ,EAAE,KAAK,oDAAoD,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,IACtF,GAAG,QAAQ,EAAE,KAAK,wEAAwE,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,IAC1G,GAAG,QAAQ,EAAE,KAAK,0EAA0E,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,IAC5G,GAAG,QAAQ,EAAE,KAAK,0HAA0H,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,IAC5J,GAAG,QAAQ,EAAE,KAAK,2DAA2D,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,EAC/F,CAAC;AACD,SAAO;AAAA,IACL,OAAO,OAAO,KAAK,KAAK,CAAC,GAAG,KAAK,CAAC;AAAA,IAClC,MAAM,OAAO,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC;AAAA,IAClC,QAAQ,OAAO,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC;AAAA,IACpC,gBAAgB,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC;AAAA,IAC1C,UAAU,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC,IAAI;AAAA,EAC7D;AACF;AAEA,eAAsB,WAAW,OAAe,MAAe,UAAmB,kBAA+H;AAC/M,QAAM,KAAK,MAAM;AACjB,QAAM,UAAU,eAAe;AAC/B,QAAM,gBAAgB,YAAY,YAAY,CAAC,EAAE,SAAS,KAAK;AAC/D,QAAM,gBAAgB,aAAa,aAAa;AAChD,QAAM,SAAS,MAAM,GAAG,QAAQ;AAAA,IAC9B,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,QAAQ,MAAM,SAAS,eAAe,oBAAoB,IAAI;AAAA,EAC9E,CAAC;AACD,SAAO,EAAE,IAAI,OAAO,iBAAkB,OAAO,MAAM,SAAS,UAAU,cAAc;AACtF;AAEA,eAAsB,eACpB,OACA,KACA,OACA,eACuF;AACvF,QAAM,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAC/C,QAAM,cAAc,KAAK,MAAM,aAAa,aAAa,IAAI;AAC7D,QAAM,UAAU,WAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,KAAK;AAC7D,QAAM,KAAK,MAAM;AAEjB,MAAI,CAAC,uBAAuB;AAC1B,UAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAShB;AACD,4BAAwB;AAAA,EAC1B;AAEA,QAAM,GAAG,QAAQ;AAAA,IACf,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM,CAAC,OAAO,SAAS,WAAW;AAAA,EACpC,CAAC;AAED,QAAM,MAAM,MAAM,GAAG,QAAQ;AAAA,IAC3B,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,SAAS,WAAW;AAAA,EACpC,CAAC;AACD,QAAM,QAAQ,OAAO,IAAI,KAAK,CAAC,GAAG,SAAS,CAAC;AAC5C,SAAO;AAAA,IACL,SAAS,SAAS;AAAA,IAClB;AAAA,IACA,WAAW,KAAK,IAAI,GAAG,QAAQ,KAAK;AAAA,IACpC,cAAc,KAAK,IAAI,GAAG,cAAc,gBAAgB,UAAU;AAAA,EACpE;AACF;AAEA,eAAsB,yBAAyB,QAA0C;AACvF,QAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,QAAM,YAAY,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACjE,QAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAI,EAAE,YAAY;AACpE,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,WAAW,QAAQ,SAAS;AAAA,EACrC,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,0BAA0B,OAAuC;AACrF,QAAM,KAAK,MAAM;AACjB,QAAM,YAAY,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACjE,QAAM,MAAM,MAAM,GAAG,QAAQ;AAAA,IAC3B,KAAK;AAAA,IACL,MAAM,CAAC,SAAS;AAAA,EAClB,CAAC;AACD,QAAM,MAAM,IAAI,KAAK,CAAC;AACtB,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,OAAO,IAAI,IAAI,EAAG,QAAO;AAC7B,MAAI,IAAI,KAAK,OAAO,IAAI,UAAU,CAAC,IAAI,oBAAI,KAAK,EAAG,QAAO;AAC1D,QAAM,GAAG,QAAQ,EAAE,KAAK,6DAA6D,MAAM,CAAC,SAAS,EAAE,CAAC;AACxG,SAAO,OAAO,IAAI,OAAO;AAC3B;AAEA,eAAsB,aAAa,QAA0C;AAC3E,QAAM,SAAS,eAAe;AAC9B,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,6DAA6D,MAAM,CAAC,QAAQ,MAAM,EAAE,CAAC;AAClH,SAAO;AACT;AAEA,eAAsB,aAAa,QAAwC;AACzE,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,gDAAgD,MAAM,CAAC,MAAM,EAAE,CAAC;AAC/F;AAEA,eAAsB,YAA8C;AAClE,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,gFAAgF;AAClH,SAAO,IAAI,KAAK,IAAI,OAAK,UAAU,EAAE,GAAG,GAAyC,SAAS,IAAI,YAAY,EAAE,CAAC,CAAC;AAChH;AAEA,eAAsB,eAAe,IAA2B;AAC9D,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,4CAA4C,MAAM,CAAC,EAAE,EAAE,CAAC;AACvF;AAqFA,SAAS,YAAY,KAAsC;AACzD,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,cAAc,IAAI,gBAAgB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,IACpE,QAAQ,IAAI,UAAU,OAAO,OAAO,IAAI,MAAM,IAAI;AAAA,IAClD,OAAO,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAC/C,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,YAAY,IAAI,cAAc,OAAO,OAAO,IAAI,UAAU,IAAI;AAAA,IAC9D,cAAc,IAAI,gBAAgB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,EACtE;AACF;AAEA,SAAS,gBAAgB,OAAgC;AACvD,SAAO,SAAS,OAAO,OAAO,OAAO,KAAK,MAAM;AAClD;AAEA,SAAS,kBAAkB,OAAgC;AACzD,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI;AACF,WAAO,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,uBAAuB,KAAiD;AAC/E,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,gBAAgB,OAAO,IAAI,cAAc;AAAA,IACzC,cAAc,OAAO,IAAI,YAAY;AAAA,IACrC,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,UAAU,IAAI,YAAY,OAAO,OAAO,IAAI,QAAQ,IAAI;AAAA,IACxD,eAAe,OAAO,IAAI,aAAa;AAAA,IACvC,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,SAAS,IAAI,WAAW,OAAO,OAAO,IAAI,OAAO,IAAI;AAAA,IACrD,mBAAmB,IAAI,qBAAqB,OAAO,OAAO,IAAI,iBAAiB,IAAI;AAAA,IACnF,gBAAgB,IAAI,kBAAkB,OAAO,OAAO,IAAI,cAAc,IAAI;AAAA,IAC1E,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,OAAO,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAC/C,YAAY,gBAAgB,IAAI,UAAU;AAAA,IAC1C,uBAAuB,OAAO,IAAI,yBAAyB,CAAC,MAAM;AAAA,IAClE,yBAAyB,gBAAgB,IAAI,uBAAuB;AAAA,IACpE,qBAAqB,IAAI,uBAAuB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IACzF,yBAAyB,gBAAgB,IAAI,uBAAuB;AAAA,IACpE,qBAAqB,IAAI,uBAAuB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IACzF,OAAO,kBAAkB,IAAI,UAAU;AAAA,IACvC,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,cAAc,IAAI,gBAAgB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,EACtE;AACF;AAEA,SAAS,YAAY,KAAkB;AACrC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,KAAK,MAAM,IAAI,OAAO;AAAA,IAC/B,QAAQ,IAAI,SAAS,KAAK,MAAM,IAAI,MAAM,IAAI;AAAA,EAChD;AACF;AAEA,eAAsB,UAAU,QAAyB,OAAe,SAAiB,aAAuC;AAC9H,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,QAAQ,OAAO,KAAK,UAAU,OAAO,GAAG,eAAe,IAAI;AAAA,EACxE,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,iBAAiB,QAAyB,OAAe,SAAkC;AAC/G,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,QAAQ,OAAO,KAAK,UAAU,OAAO,CAAC;AAAA,EACnD,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,oBAAoB,OAAgD;AACxF,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaL,MAAM;AAAA,MACJ,WAAW;AAAA,MACX,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,qBAAqB,OAAiD;AAC1F,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBL,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,YAAY,IAAI;AAAA,MACtB,MAAM,sBAAsB,IAAI;AAAA,MAChC,MAAM,yBAAyB,OAAO,OAAO,MAAM,wBAAwB,IAAI;AAAA,MAC/E,MAAM;AAAA,MACN,MAAM,yBAAyB,OAAO,OAAO,MAAM,wBAAwB,IAAI;AAAA,MAC/E,MAAM;AAAA,MACN,MAAM,SAAS,OAAO,OAAO,KAAK,UAAU,MAAM,KAAK;AAAA,MACvD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,oBAAoB,OAAe,QAAwD;AAC/G,QAAM,MAAM,UAAU,OAClB,MAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,KAAK;AAAA,EACd,CAAC,IACD,MAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,MAAM,CAAC,OAAO,MAAM;AAAA,EACtB,CAAC;AACL,SAAO,IAAI,KAAK,IAAI,OAAK,uBAAuB,CAAuC,CAAC;AAC1F;AAEA,eAAsB,OAAO,IAAY,QAAoD;AAC3F,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,SACR,MAAM,GAAG,QAAQ,EAAE,KAAK,mDAAmD,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC,IAC/F,MAAM,GAAG,QAAQ,EAAE,KAAK,mCAAmC,MAAM,CAAC,EAAE,EAAE,CAAC;AAC3E,SAAO,IAAI,KAAK,CAAC,IAAI,YAAY,YAAY,IAAI,KAAK,CAAC,CAAuC,CAAC,IAAI;AACrG;AAEA,eAAsB,SAAS,QAAyC;AACtE,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,0EAA0E,MAAM,CAAC,MAAM,EAAE,CAAC;AACnI,SAAO,IAAI,KAAK,IAAI,OAAK,YAAY,YAAY,CAAuC,CAAC,CAAC;AAC5F;AAeA,SAAS,kBAAkB,KAA4C;AACrE,QAAM,YAAY,IAAI,UAAU,OAAO,OAAO,IAAI,MAAM,IAAI;AAC5D,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,UAAU,IAAI,YAAY,OAAO,OAAO,IAAI,QAAQ,IAAI;AAAA,IACxD,cAAc,IAAI,gBAAgB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,IACpE,QAAQ,YAAY,KAAK,MAAM,SAAS,IAAI;AAAA,IAC5C,OAAO,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAC/C,YAAY,OAAO,IAAI,UAAU;AAAA,EACnC;AACF;AAEA,eAAsB,gBAAgB,OASlB;AAClB,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA,IAIL,MAAM;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,YAAY;AAAA,MAClB,MAAM,eAAe;AAAA,MACrB,MAAM,UAAU,OAAO,KAAK,UAAU,MAAM,MAAM,IAAI;AAAA,MACtD,MAAM,SAAS;AAAA,IACjB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,kBAAkB,QAAyB,QAAQ,KAA8B;AACrG,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,MAAM,CAAC,QAAQ,KAAK;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,OAAK,kBAAkB,CAAuC,CAAC;AACrF;AAiEA,SAAS,qBAAqB,OAAyC;AACrE,MAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC;AACvC,WAAO,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IAAI,SAAoC,CAAC;AAAA,EAC/G,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,sBAAsB,KAAsD;AACnF,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,aAAa,OAAO,IAAI,WAAW;AAAA,IACnC,MAAM,OAAO,IAAI,IAAI;AAAA,IACrB,OAAO,qBAAqB,IAAI,UAAU;AAAA,IAC1C,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,WAAW,IAAI,aAAa,OAAO,OAAO,IAAI,SAAS,IAAI;AAAA,IAC3D,UAAU,OAAO,IAAI,YAAY,KAAK;AAAA,IACtC,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,YAAY,OAAO,IAAI,UAAU;AAAA,EACnC;AACF;AAEA,SAAS,iBAAiB,KAAiD;AACzE,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,aAAa,OAAO,IAAI,WAAW;AAAA,IACnC,OAAO,qBAAqB,IAAI,UAAU;AAAA,IAC1C,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,UAAU,IAAI,iBAAiB,OAAO,kBAAkB,IAAI,aAAa,IAAI;AAAA,IAC7E,eAAe,IAAI,iBAAiB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,IACvE,WAAW,OAAO,IAAI,SAAS;AAAA,IAC/B,YAAY,IAAI,cAAc,OAAO,OAAO,IAAI,UAAU,IAAI;AAAA,IAC9D,cAAc,IAAI,gBAAgB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,IACpE,iBAAiB,IAAI,mBAAmB,OAAO,OAAO,IAAI,eAAe,IAAI;AAAA,IAC7E,YAAY,IAAI,cAAc,OAAO,OAAO,IAAI,UAAU,IAAI;AAAA,IAC9D,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IACjE,UAAU,qBAAqB,IAAI,aAAa;AAAA,IAChD,aAAa,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,EACnE;AACF;AAEA,SAAS,qBAAqB,OAAwC;AACpE,MAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC;AACvC,WAAO,MAAM,QAAQ,MAAM,IAAI,SAAmC,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,sBAAsB,KAAsD;AACnF,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,MAAM,OAAO,IAAI,IAAI;AAAA,IACrB,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,MAAM,OAAO,IAAI,IAAI;AAAA,IACrB,cAAc,OAAO,IAAI,YAAY;AAAA,IACrC,OAAO,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAC/C,YAAY,IAAI,cAAc,OAAO,OAAO,IAAI,UAAU,IAAI;AAAA,IAC9D,SAAS,IAAI,WAAW,OAAO,OAAO,IAAI,OAAO,IAAI;AAAA,IACrD,YAAY,OAAO,IAAI,UAAU;AAAA,EACnC;AACF;AAEA,eAAsB,uBAAuB,OAST;AAClC,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,UAAU,MAAM,aAAa;AAAA,MAClC,MAAM;AAAA,MACN,MAAM,YAAY;AAAA,MAClB,MAAM,cAAc;AAAA,MACpB,MAAM,aAAa;AAAA,IACrB;AAAA,EACF,CAAC;AACD,QAAM,WAAW,MAAM,oBAAoB,IAAI,MAAM,MAAM;AAC3D,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,oCAAoC;AACnE,SAAO;AACT;AAEA,eAAsB,oBAAoB,IAAY,QAAkE;AACtH,QAAM,MAAM,UAAU,OAClB,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,yEAAyE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC,IAC7H,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,yFAAyF,MAAM,CAAC,IAAI,QAAQ,SAAS,EAAE,CAAC;AACzJ,SAAO,IAAI,KAAK,CAAC,IAAI,sBAAsB,IAAI,KAAK,CAAC,CAAuC,IAAI;AAClG;AAEA,eAAsB,sBAAsB,QAA4D;AACtG,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,MAAM;AAAA,EACf,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,OAAK,sBAAsB,CAAuC,CAAC;AACzF;AAEA,eAAsB,sBAAsB,OAUD;AACzC,QAAM,OAAiB,CAAC;AACxB,QAAM,OAA+C,CAAC;AACtD,MAAI,MAAM,SAAS,QAAW;AAAE,SAAK,KAAK,UAAU;AAAG,SAAK,KAAK,MAAM,IAAI;AAAA,EAAE;AAC7E,MAAI,MAAM,WAAW,QAAW;AAAE,SAAK,KAAK,YAAY;AAAG,SAAK,KAAK,MAAM,MAAM;AAAA,EAAE;AACnF,MAAI,MAAM,kBAAkB,QAAW;AAAE,SAAK,KAAK,gBAAgB;AAAG,SAAK,KAAK,KAAK,UAAU,MAAM,aAAa,CAAC;AAAA,EAAE;AACrH,MAAI,MAAM,YAAY,QAAW;AAAE,SAAK,KAAK,aAAa;AAAG,SAAK,KAAK,MAAM,OAAO;AAAA,EAAE;AACtF,MAAI,MAAM,aAAa,QAAW;AAAE,SAAK,KAAK,cAAc;AAAG,SAAK,KAAK,MAAM,QAAQ;AAAA,EAAE;AACzF,MAAI,MAAM,eAAe,QAAW;AAAE,SAAK,KAAK,iBAAiB;AAAG,SAAK,KAAK,MAAM,UAAU;AAAA,EAAE;AAChG,MAAI,MAAM,cAAc,QAAW;AAAE,SAAK,KAAK,iBAAiB;AAAG,SAAK,KAAK,MAAM,SAAS;AAAA,EAAE;AAC9F,MAAI,KAAK,WAAW,EAAG,QAAO,oBAAoB,MAAM,IAAI,MAAM,MAAM;AACxE,OAAK,KAAK,8BAA8B;AACxC,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK,iCAAiC,KAAK,KAAK,IAAI,CAAC;AAAA,IACrD,MAAM,CAAC,GAAG,MAAM,MAAM,IAAI,MAAM,MAAM;AAAA,EACxC,CAAC;AACD,SAAO,oBAAoB,MAAM,IAAI,MAAM,MAAM;AACnD;AAEA,eAAsB,uBAAuB,IAAY,QAA2C;AAClG,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,MAAM;AAAA,EACnB,CAAC;AACD,SAAO,IAAI,eAAe;AAC5B;AAEA,eAAsB,yBAAyB,QAAgB,QAAQ,IAAuC;AAC5G,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM,CAAC,QAAQ,KAAK;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,OAAK,sBAAsB,CAAuC,CAAC;AACzF;AAEA,eAAsB,wBAAwB,IAAY,WAAmB,WAAyC;AACpH,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,WAAW,WAAW,EAAE;AAAA,EACjC,CAAC;AACH;AAEA,eAAsB,kBAAkB,OAST;AAC7B,QAAM,KAAK,MAAM,MAAM,WAAW;AAClC,QAAM,OAAO;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM,cAAc;AAAA,IACpB,MAAM;AAAA,IACN,KAAK,UAAU,MAAM,aAAa;AAAA,IAClC,MAAM,kBAAkB;AAAA,IACxB,MAAM,cAAc;AAAA,IACpB,MAAM,cAAc;AAAA,EACtB;AACA,MAAI;AACF,UAAM,MAAM,EAAE,QAAQ;AAAA,MACpB,KAAK;AAAA;AAAA;AAAA;AAAA,MAIL;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,MAAM,gBAAgB;AACxB,YAAM,WAAW,MAAM,+BAA+B,MAAM,cAAc;AAC1E,UAAI,SAAU,QAAO;AAAA,IACvB;AACA,UAAM;AAAA,EACR;AACA,QAAM,MAAM,MAAM,eAAe,IAAI,MAAM,MAAM;AACjD,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,+BAA+B;AACzD,SAAO;AACT;AAEA,eAAsB,eAAe,IAAY,QAA6D;AAC5G,QAAM,MAAM,UAAU,OAClB,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,oDAAoD,MAAM,CAAC,EAAE,EAAE,CAAC,IAC7F,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,oEAAoE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;AACzH,SAAO,IAAI,KAAK,CAAC,IAAI,iBAAiB,IAAI,KAAK,CAAC,CAAuC,IAAI;AAC7F;AAEA,eAAsB,+BAA+B,gBAA2D;AAC9G,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,iEAAiE,MAAM,CAAC,cAAc,EAAE,CAAC;AAClI,SAAO,IAAI,KAAK,CAAC,IAAI,iBAAiB,IAAI,KAAK,CAAC,CAAuC,IAAI;AAC7F;AAEA,eAAsB,iBAAiB,QAAyB,QAAQ,IAAkC;AACxG,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,KAAK;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,OAAK,iBAAiB,CAAuC,CAAC;AACpF;AAEA,eAAsB,uBAAuB,IAA2B;AACtE,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,EAAE;AAAA,EACX,CAAC;AACH;AAEA,eAAsB,0BAA0B,IAAY,QAAwE,UAAkC;AACpK,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,KAAK,UAAU,QAAQ,GAAG,EAAE;AAAA,EAC7C,CAAC;AACH;AAEA,eAAsB,sBAAsB,IAAY,cAAsB,UAAmC;AAC/G,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,cAAc,YAAY,OAAO,OAAO,KAAK,UAAU,QAAQ,GAAG,EAAE;AAAA,EAC7E,CAAC;AACH;AAEA,SAAS,4BAA4B,MAAsB;AACzD,MAAI,SAAS,cAAe,QAAO;AACnC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,SAAS,OAAQ,QAAO;AAC5B,SAAO;AACT;AAWA,eAAe,uBAAuB,OAAe,QAAyB,UAAgD;AAC5H,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA,IAIL,MAAM;AAAA,MACJ,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,4BAA4B,SAAS,IAAI;AAAA,MACzC,SAAS,SAAS;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,SAAS,WAAW;AAAA,IACtB;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,yBAAyB,OAAe,QAAyB,WAAuE;AAC5J,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,mEAAmE,MAAM,CAAC,OAAO,MAAM,EAAE,CAAC;AACvH,aAAW,YAAY,UAAW,OAAM,uBAAuB,OAAO,QAAQ,QAAQ;AACtF,SAAO,sBAAsB,OAAO,MAAM;AAC5C;AAEA,eAAsB,wBAAwB,OAAe,QAAyB,WAAuE;AAC3J,aAAW,YAAY,UAAW,OAAM,uBAAuB,OAAO,QAAQ,QAAQ;AACtF,SAAO,sBAAsB,OAAO,MAAM;AAC5C;AAEA,eAAsB,wBAAwB,IAAY,QAA6I;AACrM,QAAM,MAAM,UAAU,OAClB,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,0GAA0G,MAAM,CAAC,EAAE,EAAE,CAAC,IACnJ,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,0HAA0H,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;AAC/K,QAAM,MAAM,IAAI,KAAK,CAAC;AACtB,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO;AAAA,IACL,WAAW,IAAI,cAAc,OAAO,OAAO,IAAI,UAAU,IAAI;AAAA,IAC7D,YAAY,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IAChE,OAAO,IAAI,mBAAmB,OAAO,kBAAkB,IAAI,eAAe,IAAI;AAAA,IAC9E,SAAS,qBAAqB,IAAI,aAAa;AAAA,EACjD;AACF;AAEA,eAAsB,uBAAuB,IAAY,OAOvC;AAChB,QAAM,UAAU,CAAC,GAAG,MAAM,WAAW,MAAM,QAAQ;AACnD,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,MAAM,QAAQ,MAAM,WAAW,MAAM,YAAY,KAAK,UAAU,MAAM,SAAS,IAAI,GAAG,KAAK,UAAU,OAAO,GAAG,EAAE;AAAA,EAC1H,CAAC;AACH;AAEA,eAAsB,sBAAsB,OAAe,QAA4D;AACrH,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,MAAM;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,OAAK,sBAAsB,CAAuC,CAAC;AACzF;AAEA,eAAsB,oBAAoB,IAAY,QAAiE;AACrH,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,MAAM;AAAA,EACnB,CAAC;AACD,SAAO,IAAI,KAAK,CAAC,IAAI,sBAAsB,IAAI,KAAK,CAAC,CAAuC,IAAI;AAClG;AAEA,eAAsB,8BAA8B,OAQlC;AAChB,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM;AAAA,MACJ,WAAW;AAAA,MACX,MAAM;AAAA,MACN,MAAM,cAAc;AAAA,MACpB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,aAAa;AAAA,MACnB,MAAM,WAAW,eAAc,oBAAI,KAAK,GAAE,YAAY,IAAI;AAAA,IAC5D;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,mBAAoC;AACxD,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,kDAAkD;AACpF,SAAO,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC;AACnC;AAEA,eAAsB,uBAAuB,QAA0C;AACrF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,MAAM;AAAA,EACf,CAAC;AACD,SAAO,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC;AACnC;AAEA,eAAsB,kBAA+C;AACnE,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,MAAM,GAAG,QAAQ,yEAAyE;AACtG,QAAM,MAAM,IAAI,KAAK,CAAC,IAAI,YAAY,IAAI,KAAK,CAAC,CAAuC,IAAI;AAC3F,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,MAAM,MAAM,GAAG,QAAQ;AAAA,IAC3B,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,EAAE;AAAA,EACf,CAAC;AACD,SAAO,IAAI,iBAAiB,IAAI,SAAY,EAAE,GAAG,KAAK,QAAQ,UAAU;AAC1E;AAEA,eAAsB,YAAY,IAAY,QAA+B;AAC3E,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,4FAA4F,MAAM,CAAC,KAAK,UAAU,MAAM,GAAG,EAAE,EAAE,CAAC;AAC/J;AAEA,eAAsB,QAAQ,IAAY,OAA8B;AACtE,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,6FAA6F,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;AAC/I;AAEA,eAAsB,UAAU,IAAY,QAA+B;AACzE,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,gGAAgG,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACnJ;AAwBA,SAAS,YAAY,KAAyC;AAC5D,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,aAAa,OAAO,IAAI,WAAW;AAAA,IACnC,aAAa,OAAO,IAAI,WAAW;AAAA,IACnC,KAAK,IAAI,OAAO,OAAO,OAAO,IAAI,GAAG,IAAI;AAAA,IACzC,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,QAAQ,IAAI,UAAU,OAAO,OAAO,IAAI,MAAM,IAAI;AAAA,IAClD,OAAO,IAAI,SAAS,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAC/C,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,YAAY,OAAO,IAAI,UAAU;AAAA,EACnC;AACF;AAEA,SAAS,iBAAiB,KAA8C;AACtE,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,OAAO,OAAO,IAAI,KAAK;AAAA,IACvB,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,YAAY,OAAO,IAAI,UAAU;AAAA,EACnC;AACF;AAEA,eAAsB,aAAa,OAAe,QAAgB,YAAoB,YAAoB,SAAiB,KAA6B;AACtJ,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,QAAQ,YAAY,YAAY,OAAO,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,EACpF,CAAC;AACH;AAEA,eAAsB,qBAAgD;AACpE,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,MAAM,GAAG;AAAA,IACnB;AAAA,EACF;AACA,MAAI,IAAI,iBAAiB,EAAG,QAAO;AACnC,SAAO,IAAI,KAAK,CAAC,IAAI,YAAY,IAAI,KAAK,CAAC,CAAuC,IAAI;AACxF;AAEA,eAAsB,UAAU,OAA0C;AACxE,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,uCAAuC,MAAM,CAAC,KAAK,EAAE,CAAC;AAC/F,SAAO,IAAI,KAAK,CAAC,IAAI,YAAY,IAAI,KAAK,CAAC,CAAuC,IAAI;AACxF;AAEA,eAAsB,kBAAkB,OAAe,QAA+B;AACpF,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,6EAA6E,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC;AACnI;AAEA,eAAsB,eAAe,OAAe,QAA+B;AACjF,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,8FAA8F,MAAM,CAAC,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,CAAC;AACpK;AAEA,eAAsB,WAAW,OAAe,OAA8B;AAC5E,QAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,+FAA+F,MAAM,CAAC,OAAO,KAAK,EAAE,CAAC;AACpJ;AAEA,eAAsB,oBAAoB,OAAe,OAAe,QAAgC;AACtG,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,OAAO,KAAK,UAAU,MAAM,CAAC;AAAA,EAC7C,CAAC;AACH;AAEA,eAAsB,eAAe,OAA0C;AAC7E,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,wEAAwE,MAAM,CAAC,KAAK,EAAE,CAAC;AAChI,SAAO,IAAI,KAAK,IAAI,OAAK,iBAAiB,CAAuC,CAAC;AACpF;AAEA,eAAsB,YAAY,QAAuC;AACvE,QAAM,MAAM,UAAU,OAClB,MAAM,MAAM,EAAE,QAAQ,EAAE,KAAK,8EAA8E,MAAM,CAAC,MAAM,EAAE,CAAC,IAC3H,MAAM,MAAM,EAAE,QAAQ,0DAA0D;AACpF,SAAO,IAAI,KAAK,IAAI,OAAK,YAAY,CAAuC,CAAC;AAC/E;AAEA,eAAsB,0BAA0B,YAA+C;AAC7F,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,UAAU;AAAA,EACnB,CAAC;AACD,SAAO,IAAI,KAAK,CAAC,IAAI,UAAU,IAAI,KAAK,CAAC,CAAuC,IAAI;AACtF;AAEA,eAAsB,oBAAoB,QAAgB,YAAmC;AAC3F,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,YAAY,MAAM;AAAA,EAC3B,CAAC;AACH;AAEA,eAAsB,yBAAyB,QAAgB,OAA8B;AAC3F,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,KAAK,IAAI,GAAG,KAAK,GAAG,MAAM;AAAA,EACnC,CAAC;AACH;AAEA,eAAsB,oBAAoB,QAAgB,OAAqC;AAC7F,QAAM,MAAM,EAAE,QAAQ;AAAA,IACpB,KAAK;AAAA,IACL,MAAM,CAAC,OAAO,MAAM;AAAA,EACtB,CAAC;AACH;AAEA,eAAsB,SACpB,QACA,IACA,WACA,aACA,qBACiB;AACjB,QAAM,KAAK,MAAM;AACjB,QAAM,GAAG,MAAM;AAAA,IACb,EAAE,KAAK,6DAA6D,MAAM,CAAC,IAAI,MAAM,EAAE;AAAA,IACvF,EAAE,KAAK,qGAAqG,MAAM,CAAC,QAAQ,IAAI,WAAW,eAAe,MAAM,uBAAuB,IAAI,EAAE;AAAA,EAC9L,GAAG,OAAO;AACV,QAAM,MAAM,MAAM,GAAG,QAAQ,EAAE,KAAK,6CAA6C,MAAM,CAAC,MAAM,EAAE,CAAC;AACjG,SAAO,OAAO,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC;AAC5C;AAEA,eAAsB,yBAAyB,QAAgB,WAAqC;AAClG,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,SAAS;AAAA,EAC1B,CAAC;AACD,SAAO,IAAI,KAAK,SAAS;AAC3B;AAEA,eAAsB,wBAAwB,QAAgB,OAAiC;AAC7F,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,KAAK;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,eAAe;AAC5B;AAEA,eAAsB,QACpB,QACA,IACA,WACA,aAC8C;AAC9C,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,MAAM,GAAG,QAAQ;AAAA,IAC3B,KAAK;AAAA,IACL,MAAM,CAAC,IAAI,QAAQ,EAAE;AAAA,EACvB,CAAC;AACD,MAAI,IAAI,iBAAiB,GAAG;AAC1B,UAAMA,OAAM,MAAM,GAAG,QAAQ,EAAE,KAAK,6CAA6C,MAAM,CAAC,MAAM,EAAE,CAAC;AACjG,WAAO,EAAE,IAAI,OAAO,YAAY,OAAOA,KAAI,KAAK,CAAC,GAAG,cAAc,CAAC,EAAE;AAAA,EACvE;AACA,QAAM,GAAG,QAAQ;AAAA,IACf,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,eAAe,IAAI;AAAA,EACpD,CAAC;AACD,QAAM,MAAM,MAAM,GAAG,QAAQ,EAAE,KAAK,6CAA6C,MAAM,CAAC,MAAM,EAAE,CAAC;AACjG,SAAO,EAAE,IAAI,MAAM,YAAY,OAAO,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC,EAAE;AACtE;AAEA,eAAsB,UAAU,QAAgB,QAAQ,IAA0B;AAChF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ,KAAK;AAAA,EACtB,CAAC;AACD,SAAO,IAAI,KAAK,IAAI,QAAM;AAAA,IACxB,IAAa,OAAO,EAAE,EAAE;AAAA,IACxB,SAAa,OAAO,EAAE,OAAO;AAAA,IAC7B,WAAa,OAAO,EAAE,SAAS;AAAA,IAC/B,WAAa,OAAO,EAAE,SAAS;AAAA,IAC/B,aAAa,EAAE,eAAe,OAAO,OAAO,EAAE,WAAW,IAAI;AAAA,IAC7D,WAAa,EAAE,aAAe,OAAO,OAAO,EAAE,SAAS,IAAM;AAAA,IAC7D,YAAa,OAAO,EAAE,UAAU;AAAA,EAClC,EAAE;AACJ;AAEA,eAAsB,wBAAwB,UAAoC;AAChF,QAAM,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,IAChC,KAAK;AAAA,IACL,MAAM,CAAC,QAAQ;AAAA,EACjB,CAAC;AACD,SAAO,IAAI,KAAK,SAAS;AAC3B;AAEA,eAAsB,mBAAmB,QAA0C;AACjF,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,MAAM,GAAG,QAAQ;AAAA,IAC3B,KAAK;AAAA,IACL,MAAM,CAAC,MAAM;AAAA,EACf,CAAC;AACD,QAAM,YAAY,OAAO,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC;AACrD,QAAM,GAAG,QAAQ;AAAA,IACf,KAAK;AAAA,IACL,MAAM,CAAC,WAAW,QAAQ,SAAS;AAAA,EACrC,CAAC;AACD,SAAO;AACT;AAYO,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,IAAI,EAAE,OAAO;AAAA,EACb,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ,EAAE,OAAO,EAAE,QAAQ,SAAS;AAAA,EACpC,eAAe,EAAE,OAAO;AAAA,EACxB,cAAc,EAAE,OAAO;AAAA,EACvB,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AACvB,CAAC;AAGM,IAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,IAAI,EAAE,OAAO;AAAA,EACb,QAAQ,EAAE,OAAO;AAAA,EACjB,OAAO,EAAE,OAAO;AAAA,EAChB,cAAc,EAAE,OAAO;AAAA,EACvB,gBAAgB,EAAE,OAAO;AAC3B,CAAC;","names":["res"]}