mcp-scraper 0.46.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/analytics-repository-WWXVU3VW.js +119 -0
- package/dist/bin/api-server.cjs +7745 -1934
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +8 -4
- package/dist/bin/api-server.js.map +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +1950 -1050
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +5 -5
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-JFOT5JEL.js → chunk-6DPIE262.js} +2 -2
- package/dist/chunk-IAXQIHNS.js +7 -0
- package/dist/chunk-IAXQIHNS.js.map +1 -0
- package/dist/{chunk-RJMOOII7.js → chunk-MFE6RMIA.js} +2 -2
- package/dist/{chunk-5X7CJEK3.js → chunk-MFNGUM4L.js} +2 -2
- package/dist/chunk-MFNGUM4L.js.map +1 -0
- package/dist/{chunk-T5AFM4G5.js → chunk-PODASLGT.js} +2 -2
- package/dist/{chunk-BP2IUIAH.js → chunk-PTABZABJ.js} +2260 -1287
- package/dist/chunk-PTABZABJ.js.map +1 -0
- package/dist/{chunk-QSUGPYGQ.js → chunk-QAH7WMYC.js} +2 -2
- package/dist/{chunk-QSUGPYGQ.js.map → chunk-QAH7WMYC.js.map} +1 -1
- package/dist/{chunk-WT2UHDSE.js → chunk-R66PJOZW.js} +2 -2
- package/dist/chunk-YW2LXLDU.js +2865 -0
- package/dist/chunk-YW2LXLDU.js.map +1 -0
- package/dist/{chunk-OVD4E4AP.js → chunk-ZEMHL36J.js} +2 -2
- package/dist/{db-4ABEYNDM.js → db-A4YUPV4Z.js} +2 -2
- package/dist/{extract-bundle-DYMQRQWG.js → extract-bundle-2HONVC5T.js} +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{location-data-repository-HDNCU4Z4.js → location-data-repository-YWFJGNIP.js} +3 -3
- package/dist/{server-SMEHT3OU.js → server-EVTCL3KB.js} +2297 -329
- package/dist/server-EVTCL3KB.js.map +1 -0
- package/dist/{site-extract-repository-G723PZIL.js → site-extract-repository-STNOI5OH.js} +3 -3
- package/dist/site-extract-repository-STNOI5OH.js.map +1 -0
- package/dist/{worker-4LZGRHDA.js → worker-4MHZS3OB.js} +5 -5
- package/package.json +4 -2
- package/dist/chunk-5X7CJEK3.js.map +0 -1
- package/dist/chunk-BP2IUIAH.js.map +0 -1
- package/dist/chunk-TQKQK7OV.js +0 -7
- package/dist/chunk-TQKQK7OV.js.map +0 -1
- package/dist/server-SMEHT3OU.js.map +0 -1
- /package/dist/{db-4ABEYNDM.js.map → analytics-repository-WWXVU3VW.js.map} +0 -0
- /package/dist/{chunk-JFOT5JEL.js.map → chunk-6DPIE262.js.map} +0 -0
- /package/dist/{chunk-RJMOOII7.js.map → chunk-MFE6RMIA.js.map} +0 -0
- /package/dist/{chunk-T5AFM4G5.js.map → chunk-PODASLGT.js.map} +0 -0
- /package/dist/{chunk-WT2UHDSE.js.map → chunk-R66PJOZW.js.map} +0 -0
- /package/dist/{chunk-OVD4E4AP.js.map → chunk-ZEMHL36J.js.map} +0 -0
- /package/dist/{location-data-repository-HDNCU4Z4.js.map → db-A4YUPV4Z.js.map} +0 -0
- /package/dist/{extract-bundle-DYMQRQWG.js.map → extract-bundle-2HONVC5T.js.map} +0 -0
- /package/dist/{site-extract-repository-G723PZIL.js.map → location-data-repository-YWFJGNIP.js.map} +0 -0
- /package/dist/{worker-4LZGRHDA.js.map → worker-4MHZS3OB.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/analytics-repository.ts","../src/api/session.ts","../src/api/analytics-attribution.ts"],"sourcesContent":["import { createHash, createHmac, randomBytes, randomUUID } from \"node:crypto\";\nimport { Pool, type PoolClient, type QueryResultRow } from \"pg\";\nimport { getSessionSecret } from \"./session.js\";\nimport { resolveAnalyticsAttribution } from \"./analytics-attribution.js\";\n\nexport type AnalyticsRole = \"viewer\" | \"editor\" | \"owner\";\nexport type PixelStatus = \"active\" | \"disabled\";\nexport type PixelDomainState =\n \"observed_unverified\" | \"approved\" | \"rejected\" | \"revoked\";\n\nexport class AnalyticsRepositoryError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly status = 400,\n ) {\n super(message);\n this.name = \"AnalyticsRepositoryError\";\n }\n}\n\nlet pool: Pool | null = null;\n\nfunction databaseUrl(): string {\n const value = process.env.ANALYTICS_DATABASE_URL?.trim();\n if (!value) {\n throw new AnalyticsRepositoryError(\n \"analytics_database_unavailable\",\n \"Site Analytics is not configured.\",\n 503,\n );\n }\n return value;\n}\n\nexport function getAnalyticsPool(): Pool {\n if (!pool) pool = new Pool({ connectionString: databaseUrl(), max: 8 });\n return pool;\n}\n\nexport async function closeAnalyticsPool(): Promise<void> {\n if (!pool) return;\n const current = pool;\n pool = null;\n await current.end();\n}\n\nexport async function migrateAnalytics(): Promise<void> {\n const db = getAnalyticsPool();\n const client = await db.connect();\n await client.query(\"BEGIN\");\n try {\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_schema_migrations (\n version text PRIMARY KEY,\n applied_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_sites (\n id uuid PRIMARY KEY,\n slug text NOT NULL,\n name text NOT NULL,\n owner_user_id bigint NOT NULL,\n timezone text NOT NULL DEFAULT 'UTC',\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(owner_user_id, slug)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_site_grants (\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n user_id bigint NOT NULL,\n role text NOT NULL CHECK (role IN ('viewer', 'editor', 'owner')),\n created_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY(site_id, user_id)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_pixels (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n public_id text NOT NULL UNIQUE,\n name text NOT NULL,\n environment text NOT NULL DEFAULT 'production',\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n disabled_at timestamptz\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_pixel_domains (\n id uuid PRIMARY KEY,\n pixel_id uuid NOT NULL REFERENCES analytics_pixels(id) ON DELETE CASCADE,\n hostname text NOT NULL,\n state text NOT NULL DEFAULT 'observed_unverified'\n CHECK (state IN ('observed_unverified', 'approved', 'rejected', 'revoked')),\n first_seen_at timestamptz NOT NULL DEFAULT now(),\n last_seen_at timestamptz NOT NULL DEFAULT now(),\n accepted_count bigint NOT NULL DEFAULT 0,\n rejected_count bigint NOT NULL DEFAULT 0,\n updated_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(pixel_id, hostname)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_events (\n id uuid PRIMARY KEY,\n event_id text NOT NULL,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid NOT NULL REFERENCES analytics_pixels(id) ON DELETE CASCADE,\n hostname text NOT NULL,\n visitor_id text,\n session_id text,\n event_name text NOT NULL,\n occurred_at timestamptz NOT NULL,\n received_at timestamptz NOT NULL DEFAULT now(),\n path text NOT NULL DEFAULT '/',\n canonical_url text,\n title text,\n referrer text,\n source text,\n medium text,\n campaign text,\n device_class text,\n properties jsonb NOT NULL DEFAULT '{}'::jsonb,\n bot_status text NOT NULL DEFAULT 'unknown',\n UNIQUE(site_id, event_id)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_conversions (\n id uuid PRIMARY KEY,\n source_event_id text NOT NULL,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid REFERENCES analytics_pixels(id) ON DELETE SET NULL,\n session_id text,\n conversion_kind text NOT NULL,\n value_minor bigint NOT NULL DEFAULT 0,\n currency text NOT NULL DEFAULT 'USD',\n occurred_at timestamptz NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(site_id, source_event_id)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_ingestion_receipts (\n id uuid PRIMARY KEY,\n request_id text NOT NULL UNIQUE,\n site_id uuid REFERENCES analytics_sites(id) ON DELETE SET NULL,\n pixel_id uuid REFERENCES analytics_pixels(id) ON DELETE SET NULL,\n hostname text,\n accepted_count integer NOT NULL DEFAULT 0,\n rejected_count integer NOT NULL DEFAULT 0,\n reason_codes jsonb NOT NULL DEFAULT '[]'::jsonb,\n created_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_rollups_daily (\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid NOT NULL REFERENCES analytics_pixels(id) ON DELETE CASCADE,\n hostname text NOT NULL,\n bucket date NOT NULL,\n source text NOT NULL DEFAULT '(direct)',\n medium text NOT NULL DEFAULT '(none)',\n campaign text NOT NULL DEFAULT '(none)',\n pageviews integer NOT NULL DEFAULT 0,\n events integer NOT NULL DEFAULT 0,\n visitors integer NOT NULL DEFAULT 0,\n sessions integer NOT NULL DEFAULT 0,\n refreshed_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY(site_id, pixel_id, hostname, bucket, source, medium, campaign)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_rollup_runs (\n id uuid PRIMARY KEY,\n window_start timestamptz NOT NULL,\n window_end timestamptz NOT NULL,\n status text NOT NULL CHECK (status IN ('running', 'complete', 'failed')),\n row_count integer NOT NULL DEFAULT 0,\n started_at timestamptz NOT NULL DEFAULT now(),\n completed_at timestamptz,\n error_code text\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_exports (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n requested_by_user_id bigint NOT NULL,\n idempotency_key text,\n report text NOT NULL,\n format text NOT NULL CHECK (format IN ('csv', 'json', 'markdown')),\n filters jsonb NOT NULL,\n content text NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_campaign_links (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid REFERENCES analytics_pixels(id) ON DELETE SET NULL,\n name text NOT NULL,\n destination_url text NOT NULL,\n short_code text NOT NULL UNIQUE,\n utm_source text NOT NULL,\n utm_medium text NOT NULL,\n utm_campaign text NOT NULL,\n utm_term text,\n utm_content text,\n ad_group text,\n ad_name text,\n creative_id text,\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')),\n created_by_user_id bigint NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_campaign_clicks (\n id uuid PRIMARY KEY,\n link_id uuid NOT NULL REFERENCES analytics_campaign_links(id) ON DELETE CASCADE,\n occurred_at timestamptz NOT NULL DEFAULT now(),\n referrer text\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_forms (\n id uuid PRIMARY KEY,\n public_id text NOT NULL UNIQUE,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid NOT NULL REFERENCES analytics_pixels(id) ON DELETE RESTRICT,\n name text NOT NULL,\n slug text NOT NULL,\n fields jsonb NOT NULL,\n brand jsonb NOT NULL,\n submit_label text NOT NULL DEFAULT 'Submit',\n success_message text NOT NULL DEFAULT 'Thank you. Your response was received.',\n redirect_url text,\n consent_text text,\n status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),\n version integer NOT NULL DEFAULT 1,\n created_by_user_id bigint NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(site_id, slug)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_form_submissions (\n id uuid PRIMARY KEY,\n form_id uuid NOT NULL REFERENCES analytics_forms(id) ON DELETE CASCADE,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n pixel_id uuid NOT NULL REFERENCES analytics_pixels(id) ON DELETE RESTRICT,\n visitor_id text,\n session_id text,\n crm_person_ref text,\n source text,\n medium text,\n campaign text,\n occurred_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(\n `ALTER TABLE analytics_form_submissions ADD COLUMN IF NOT EXISTS crm_delivery_status text NOT NULL DEFAULT 'pending'`,\n );\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_form_delivery_jobs (\n id uuid PRIMARY KEY,\n submission_id uuid NOT NULL UNIQUE REFERENCES analytics_form_submissions(id) ON DELETE CASCADE,\n owner_user_id bigint NOT NULL,\n payload_ciphertext text NOT NULL,\n status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'delivered', 'failed', 'expired')),\n attempts integer NOT NULL DEFAULT 0,\n last_error_code text,\n next_attempt_at timestamptz NOT NULL DEFAULT now(),\n expires_at timestamptz NOT NULL DEFAULT now() + interval '7 days',\n created_at timestamptz NOT NULL DEFAULT now(),\n delivered_at timestamptz\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_people (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n crm_person_ref text NOT NULL,\n first_seen_at timestamptz NOT NULL DEFAULT now(),\n last_seen_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(site_id, crm_person_ref)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_identity_nodes (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n kind text NOT NULL CHECK (kind IN ('visitor_id','session_id','email','phone','customer_id','click_id','device_signal','network_signal')),\n value_hmac text NOT NULL,\n first_seen_at timestamptz NOT NULL DEFAULT now(),\n last_seen_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(site_id, kind, value_hmac)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_identity_edges (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n person_id uuid NOT NULL REFERENCES analytics_people(id) ON DELETE CASCADE,\n identity_node_id uuid NOT NULL REFERENCES analytics_identity_nodes(id) ON DELETE CASCADE,\n evidence_kind text NOT NULL,\n confidence numeric(4,3) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),\n consent_scope text NOT NULL DEFAULT 'analytics',\n first_seen_at timestamptz NOT NULL DEFAULT now(),\n last_seen_at timestamptz NOT NULL DEFAULT now(),\n UNIQUE(person_id, identity_node_id, evidence_kind)\n )\n `);\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS click_ids jsonb NOT NULL DEFAULT '{}'::jsonb`,\n );\n await client.query(\n `ALTER TABLE analytics_form_submissions ADD COLUMN IF NOT EXISTS click_ids jsonb NOT NULL DEFAULT '{}'::jsonb`,\n );\n await client.query(\n `ALTER TABLE analytics_form_submissions ADD COLUMN IF NOT EXISTS person_id uuid REFERENCES analytics_people(id) ON DELETE SET NULL`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS person_id uuid REFERENCES analytics_people(id) ON DELETE SET NULL`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS channel_family text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS platform text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS campaign_id text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS ad_set_id text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS ad_id text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS creative_id text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS placement text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS utm_term text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS utm_content text`,\n );\n await client.query(\n `ALTER TABLE analytics_conversions ADD COLUMN IF NOT EXISTS person_id uuid REFERENCES analytics_people(id) ON DELETE SET NULL`,\n );\n await client.query(\n `ALTER TABLE analytics_sites ADD COLUMN IF NOT EXISTS business_model text NOT NULL DEFAULT 'leadgen' CHECK (business_model IN ('leadgen','saas','ecommerce'))`,\n );\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_ad_spend_daily (\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n bucket date NOT NULL,\n platform text NOT NULL,\n campaign text NOT NULL DEFAULT '(all)',\n spend_minor bigint NOT NULL CHECK (spend_minor >= 0),\n currency text NOT NULL DEFAULT 'USD',\n source_ref text,\n updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY(site_id, bucket, platform, campaign)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_crm_imports (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n requested_by_user_id bigint NOT NULL,\n source_system text NOT NULL,\n filename text NOT NULL,\n status text NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','processing','complete','partial','failed')),\n accepted_count integer NOT NULL DEFAULT 0,\n rejected_count integer NOT NULL DEFAULT 0,\n created_at timestamptz NOT NULL DEFAULT now(),\n completed_at timestamptz\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_crm_import_rows (\n id uuid PRIMARY KEY,\n import_id uuid NOT NULL REFERENCES analytics_crm_imports(id) ON DELETE CASCADE,\n crm_person_ref text NOT NULL,\n payload_ciphertext text NOT NULL,\n delivery_status text NOT NULL DEFAULT 'pending' CHECK (delivery_status IN ('pending','delivered','failed','expired')),\n attempts integer NOT NULL DEFAULT 0,\n last_error_code text,\n created_at timestamptz NOT NULL DEFAULT now(),\n delivered_at timestamptz,\n UNIQUE(import_id, crm_person_ref)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_activation_destinations (\n id uuid PRIMARY KEY,\n site_id uuid NOT NULL REFERENCES analytics_sites(id) ON DELETE CASCADE,\n platform text NOT NULL CHECK (platform IN ('meta','google','tiktok','reddit')),\n name text NOT NULL,\n connection_ref text,\n external_dataset_id text,\n status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','paused','archived')),\n event_mapping jsonb NOT NULL DEFAULT '{}'::jsonb,\n created_by_user_id bigint NOT NULL,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_activation_jobs (\n id uuid PRIMARY KEY,\n destination_id uuid NOT NULL REFERENCES analytics_activation_destinations(id) ON DELETE CASCADE,\n conversion_id uuid NOT NULL REFERENCES analytics_conversions(id) ON DELETE CASCADE,\n person_id uuid REFERENCES analytics_people(id) ON DELETE SET NULL,\n payload_ciphertext text NOT NULL,\n status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','delivered','failed','expired')),\n attempts integer NOT NULL DEFAULT 0,\n last_error_code text,\n next_attempt_at timestamptz NOT NULL DEFAULT now(),\n created_at timestamptz NOT NULL DEFAULT now(),\n delivered_at timestamptz,\n UNIQUE(destination_id, conversion_id)\n )\n `);\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_thorbit_entitlements (\n user_id bigint PRIMARY KEY,\n thorbit_org_id text,\n connection_source text NOT NULL DEFAULT 'trial' CHECK (connection_source IN ('trial','api_key','thorbit_session')),\n eligible boolean NOT NULL DEFAULT false,\n reason text NOT NULL,\n plan_id text,\n subscription_status text,\n monthly_recurring_revenue_cents integer NOT NULL DEFAULT 0,\n minimum_monthly_recurring_revenue_cents integer NOT NULL DEFAULT 5000,\n verified_at timestamptz NOT NULL,\n refresh_after timestamptz NOT NULL,\n access_expires_at timestamptz NOT NULL,\n trial_started_at timestamptz NOT NULL DEFAULT now(),\n trial_ends_at timestamptz NOT NULL DEFAULT (now() + interval '30 days'),\n last_refresh_error text,\n updated_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(\n `ALTER TABLE analytics_thorbit_entitlements ALTER COLUMN thorbit_org_id DROP NOT NULL`,\n );\n await client.query(\n `ALTER TABLE analytics_thorbit_entitlements ADD COLUMN IF NOT EXISTS trial_started_at timestamptz NOT NULL DEFAULT now()`,\n );\n await client.query(\n `ALTER TABLE analytics_thorbit_entitlements ADD COLUMN IF NOT EXISTS trial_ends_at timestamptz NOT NULL DEFAULT (now() + interval '30 days')`,\n );\n await client.query(\n `ALTER TABLE analytics_thorbit_entitlements DROP CONSTRAINT IF EXISTS analytics_thorbit_entitlements_connection_source_check`,\n );\n await client.query(\n `ALTER TABLE analytics_thorbit_entitlements ADD CONSTRAINT analytics_thorbit_entitlements_connection_source_check CHECK (connection_source IN ('trial','api_key','thorbit_session'))`,\n );\n await client.query(`\n CREATE TABLE IF NOT EXISTS analytics_thorbit_connect_states (\n state_hash text PRIMARY KEY,\n user_id bigint NOT NULL,\n expires_at timestamptz NOT NULL,\n consumed_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now()\n )\n `);\n await client.query(\n `ALTER TABLE analytics_exports ADD COLUMN IF NOT EXISTS idempotency_key text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS country_code text`,\n );\n await client.query(\n `ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS region_code text`,\n );\n await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS analytics_exports_owner_idempotency\n ON analytics_exports(requested_by_user_id, idempotency_key) WHERE idempotency_key IS NOT NULL`);\n await client.query(\n \"CREATE INDEX IF NOT EXISTS analytics_events_site_time ON analytics_events(site_id, occurred_at DESC)\",\n );\n await client.query(\n \"CREATE INDEX IF NOT EXISTS analytics_events_pixel_time ON analytics_events(pixel_id, occurred_at DESC)\",\n );\n await client.query(\n \"CREATE INDEX IF NOT EXISTS analytics_events_site_hostname_time ON analytics_events(site_id, hostname, occurred_at DESC)\",\n );\n await client.query(\n \"CREATE INDEX IF NOT EXISTS analytics_events_acquisition ON analytics_events(site_id, source, medium, campaign, occurred_at DESC)\",\n );\n await client.query(\n \"CREATE INDEX IF NOT EXISTS analytics_conversions_site_time ON analytics_conversions(site_id, occurred_at DESC)\",\n );\n await client.query(`\n INSERT INTO analytics_schema_migrations(version)\n VALUES ('2026-08-05.1'), ('2026-08-05.2'), ('2026-08-05.3'), ('2026-08-05.4'), ('2026-08-05.5'), ('2026-08-05.6'), ('2026-08-05.7'), ('2026-08-05.8'), ('2026-08-05.9'), ('2026-08-05.10'), ('2026-08-05.11'), ('2026-08-05.12'), ('2026-08-05.13'), ('2026-08-05.14')\n ON CONFLICT (version) DO NOTHING\n `);\n await client.query(\"COMMIT\");\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n}\n\nfunction normalizeSlug(value: string): string {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 80);\n return slug || `site-${randomBytes(4).toString(\"hex\")}`;\n}\n\nexport function normalizeObservedHostname(\n origin: string | null | undefined,\n): string | null {\n if (!origin || origin === \"null\") return null;\n try {\n const url = new URL(origin);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n return url.hostname.toLowerCase().replace(/\\.$/, \"\");\n } catch {\n return null;\n }\n}\n\nfunction publicPixelId(): string {\n return `px_${randomBytes(18).toString(\"base64url\")}`;\n}\n\nfunction mapRows<T>(rows: QueryResultRow[]): T[] {\n return rows as T[];\n}\n\nasync function accessFor(\n client: Pool | PoolClient,\n siteId: string,\n userId: number,\n): Promise<AnalyticsRole> {\n const result = await client.query<{ role: AnalyticsRole }>(\n `SELECT role FROM analytics_site_grants WHERE site_id = $1 AND user_id = $2`,\n [siteId, userId],\n );\n const role = result.rows[0]?.role;\n if (!role)\n throw new AnalyticsRepositoryError(\n \"analytics_site_not_found\",\n \"Site not found.\",\n 404,\n );\n return role;\n}\n\nasync function requireEditor(\n client: Pool | PoolClient,\n siteId: string,\n userId: number,\n): Promise<AnalyticsRole> {\n const role = await accessFor(client, siteId, userId);\n if (role === \"viewer\")\n throw new AnalyticsRepositoryError(\n \"analytics_forbidden\",\n \"Editor access is required.\",\n 403,\n );\n return role;\n}\n\nexport interface AnalyticsSiteRow {\n id: string;\n slug: string;\n name: string;\n timezone: string;\n status: string;\n role: AnalyticsRole;\n pixel_count: number;\n last_event_at: string | null;\n business_model: \"leadgen\" | \"saas\" | \"ecommerce\";\n}\n\nexport async function createAnalyticsSite(input: {\n userId: number;\n name: string;\n slug?: string;\n timezone?: string;\n}): Promise<AnalyticsSiteRow> {\n const db = getAnalyticsPool();\n const client = await db.connect();\n const id = randomUUID();\n const baseSlug = normalizeSlug(input.slug || input.name);\n try {\n await client.query(\"BEGIN\");\n let slug = baseSlug;\n for (let attempt = 0; attempt < 3; attempt += 1) {\n try {\n await client.query(\n `INSERT INTO analytics_sites(id, slug, name, owner_user_id, timezone)\n VALUES ($1, $2, $3, $4, $5)`,\n [id, slug, input.name.trim(), input.userId, input.timezone || \"UTC\"],\n );\n break;\n } catch (error) {\n const code = (error as { code?: string }).code;\n if (code !== \"23505\" || attempt === 2) throw error;\n slug = `${baseSlug}-${randomBytes(2).toString(\"hex\")}`;\n }\n }\n await client.query(\n `INSERT INTO analytics_site_grants(site_id, user_id, role) VALUES ($1, $2, 'owner')`,\n [id, input.userId],\n );\n await client.query(\"COMMIT\");\n return {\n id,\n slug,\n name: input.name.trim(),\n timezone: input.timezone || \"UTC\",\n status: \"active\",\n role: \"owner\",\n pixel_count: 0,\n last_event_at: null,\n business_model: \"leadgen\",\n };\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function listAnalyticsSites(\n userId: number,\n): Promise<AnalyticsSiteRow[]> {\n const result = await getAnalyticsPool().query(\n `SELECT s.id, s.slug, s.name, s.timezone, s.status, s.business_model, g.role,\n count(DISTINCT p.id)::int AS pixel_count,\n max(e.occurred_at)::text AS last_event_at\n FROM analytics_sites s\n JOIN analytics_site_grants g ON g.site_id = s.id AND g.user_id = $1\n LEFT JOIN analytics_pixels p ON p.site_id = s.id\n LEFT JOIN analytics_events e ON e.site_id = s.id\n GROUP BY s.id, g.role\n ORDER BY s.created_at ASC`,\n [userId],\n );\n return mapRows<AnalyticsSiteRow>(result.rows);\n}\n\nexport async function updateAnalyticsBusinessModel(input: {\n siteId: string;\n userId: number;\n businessModel: \"leadgen\" | \"saas\" | \"ecommerce\";\n}) {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n await db.query(\n \"UPDATE analytics_sites SET business_model = $1, updated_at = now() WHERE id = $2\",\n [input.businessModel, input.siteId],\n );\n}\n\nexport async function upsertAnalyticsAdSpend(input: {\n siteId: string;\n userId: number;\n rows: Array<{\n date: string;\n platform: string;\n campaign?: string;\n spendMinor: number;\n currency?: string;\n sourceRef?: string;\n }>;\n}) {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n for (const row of input.rows) {\n await db.query(\n `INSERT INTO analytics_ad_spend_daily(site_id, bucket, platform, campaign, spend_minor, currency, source_ref)\n VALUES ($1,$2::date,$3,$4,$5,$6,$7)\n ON CONFLICT(site_id,bucket,platform,campaign) DO UPDATE SET spend_minor=EXCLUDED.spend_minor,currency=EXCLUDED.currency,source_ref=EXCLUDED.source_ref,updated_at=now()`,\n [\n input.siteId,\n row.date,\n row.platform.slice(0, 80),\n row.campaign?.slice(0, 240) || \"(all)\",\n row.spendMinor,\n (row.currency || \"USD\").toUpperCase(),\n row.sourceRef?.slice(0, 500) || null,\n ],\n );\n }\n return { accepted: input.rows.length };\n}\n\nexport async function analyticsBusinessMetrics(\n siteId: string,\n userId: number,\n start: string,\n end: string,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const site = await db.query<{\n business_model: \"leadgen\" | \"saas\" | \"ecommerce\";\n }>(\"SELECT business_model FROM analytics_sites WHERE id=$1\", [siteId]);\n const result = await db.query<{\n conversions: number;\n customers: number;\n leads: number;\n purchases: number;\n trials: number;\n subscriptions: number;\n churns: number;\n revenue_minor: string;\n repeat_customers: number;\n }>(\n `SELECT count(*)::int AS conversions, count(DISTINCT person_id)::int AS customers,\n count(*) FILTER (WHERE conversion_kind IN ('form_submitted','lead','qualified_lead'))::int AS leads,\n count(*) FILTER (WHERE conversion_kind IN ('purchase','order_completed'))::int AS purchases,\n count(*) FILTER (WHERE conversion_kind IN ('trial_started','demo_started'))::int AS trials,\n count(*) FILTER (WHERE conversion_kind IN ('subscription_started','subscription_renewed'))::int AS subscriptions,\n count(*) FILTER (WHERE conversion_kind IN ('subscription_cancelled','churned'))::int AS churns,\n COALESCE(sum(value_minor),0)::bigint AS revenue_minor,\n (SELECT count(*)::int FROM (SELECT person_id FROM analytics_conversions WHERE site_id=$1 AND person_id IS NOT NULL AND conversion_kind IN ('purchase','order_completed') AND occurred_at >= $2 AND occurred_at < $3 GROUP BY person_id HAVING count(*) > 1) r) AS repeat_customers\n FROM analytics_conversions WHERE site_id=$1 AND occurred_at >= $2 AND occurred_at < $3`,\n [siteId, start, end],\n );\n const spend = await db.query<{ spend_minor: string }>(\n \"SELECT COALESCE(sum(spend_minor),0)::bigint AS spend_minor FROM analytics_ad_spend_daily WHERE site_id=$1 AND bucket >= $2::date AND bucket < $3::date\",\n [siteId, start, end],\n );\n const row = result.rows[0];\n const spendMinor = Number(spend.rows[0]?.spend_minor || 0);\n const revenueMinor = Number(row.revenue_minor || 0);\n return {\n businessModel: site.rows[0]?.business_model || \"leadgen\",\n period: { start, end },\n metrics: {\n ...row,\n revenue_minor: revenueMinor,\n spend_minor: spendMinor,\n roas: spendMinor > 0 ? revenueMinor / spendMinor : null,\n cost_per_lead:\n spendMinor > 0 && row.leads > 0 ? spendMinor / row.leads : null,\n average_order_value:\n row.purchases > 0 ? revenueMinor / row.purchases : null,\n repeat_customer_rate:\n row.customers > 0 ? row.repeat_customers / row.customers : null,\n churn_rate: row.subscriptions > 0 ? row.churns / row.subscriptions : null,\n },\n missingInputs: spendMinor > 0 ? [] : [\"ad_spend\"],\n };\n}\n\nexport async function analyticsForecast(\n siteId: string,\n userId: number,\n horizonMonths = 12,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const rows = await db.query<{\n month: string;\n revenue_minor: string;\n spend_minor: string;\n }>(\n `WITH months AS (SELECT generate_series(date_trunc('month', now()) - interval '11 months', date_trunc('month', now()), interval '1 month') AS month)\n SELECT m.month::date::text AS month,\n COALESCE((SELECT sum(value_minor) FROM analytics_conversions c WHERE c.site_id=$1 AND c.occurred_at >= m.month AND c.occurred_at < m.month+interval '1 month'),0)::bigint AS revenue_minor,\n COALESCE((SELECT sum(spend_minor) FROM analytics_ad_spend_daily s WHERE s.site_id=$1 AND s.bucket >= m.month::date AND s.bucket < (m.month+interval '1 month')::date),0)::bigint AS spend_minor\n FROM months m ORDER BY m.month`,\n [siteId],\n );\n const history = rows.rows.map((row) => ({\n month: row.month,\n revenueMinor: Number(row.revenue_minor),\n spendMinor: Number(row.spend_minor),\n }));\n const nonEmpty = history.filter(\n (row) => row.revenueMinor > 0 || row.spendMinor > 0,\n );\n if (nonEmpty.length < 3)\n return {\n status: \"insufficient_data\",\n history,\n forecast: [],\n horizonMonths,\n missingInputs: [\"at_least_3_months_of_revenue_or_spend\"],\n };\n const values = history.map((row) => row.revenueMinor);\n const n = values.length;\n const meanX = (n - 1) / 2;\n const meanY = values.reduce((a, b) => a + b, 0) / n;\n const slope =\n values.reduce(\n (sum, value, index) => sum + (index - meanX) * (value - meanY),\n 0,\n ) / values.reduce((sum, _value, index) => sum + (index - meanX) ** 2, 0);\n const intercept = meanY - slope * meanX;\n const residual = Math.sqrt(\n values.reduce(\n (sum, value, index) => sum + (value - (intercept + slope * index)) ** 2,\n 0,\n ) / n,\n );\n const spendMonths = history.filter((row) => row.spendMinor > 0);\n const avgSpend = spendMonths.length\n ? spendMonths.reduce((sum, row) => sum + row.spendMinor, 0) /\n spendMonths.length\n : 0;\n const lastMonth = new Date(`${history.at(-1)?.month}T00:00:00Z`);\n const forecast = Array.from(\n { length: Math.min(Math.max(horizonMonths, 1), 36) },\n (_, offset) => {\n const date = new Date(\n Date.UTC(\n lastMonth.getUTCFullYear(),\n lastMonth.getUTCMonth() + offset + 1,\n 1,\n ),\n );\n const base = Math.max(0, intercept + slope * (n + offset));\n return {\n month: date.toISOString().slice(0, 10),\n lowerRevenueMinor: Math.max(0, Math.round(base - residual)),\n baseRevenueMinor: Math.round(base),\n upperRevenueMinor: Math.round(base + residual),\n projectedSpendMinor: Math.round(avgSpend),\n baseRoas: avgSpend > 0 ? base / avgSpend : null,\n };\n },\n );\n return {\n status: \"modeled\",\n method: \"monthly_linear_trend_v1\",\n history,\n forecast,\n horizonMonths: forecast.length,\n missingInputs: avgSpend > 0 ? [] : [\"ad_spend\"],\n caveats: [\n \"Scenario range reflects historical residual variation, not a guarantee.\",\n ],\n };\n}\n\nexport async function deleteAnalyticsSite(input: {\n siteId: string;\n userId: number;\n}): Promise<void> {\n const db = getAnalyticsPool();\n const role = await accessFor(db, input.siteId, input.userId);\n if (role !== \"owner\")\n throw new AnalyticsRepositoryError(\n \"analytics_forbidden\",\n \"Owner access is required.\",\n 403,\n );\n const result = await db.query(\n `DELETE FROM analytics_sites WHERE id = $1 AND owner_user_id = $2`,\n [input.siteId, input.userId],\n );\n if (!result.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_site_not_found\",\n \"Site not found.\",\n 404,\n );\n}\n\nexport interface AnalyticsPixelRow {\n id: string;\n site_id: string;\n public_id: string;\n name: string;\n environment: string;\n status: PixelStatus;\n created_at: string;\n last_event_at: string | null;\n domains: Array<{\n hostname: string;\n state: PixelDomainState;\n first_seen_at: string;\n last_seen_at: string;\n accepted_count: number;\n rejected_count: number;\n }>;\n}\n\nexport async function createAnalyticsPixel(input: {\n siteId: string;\n userId: number;\n name: string;\n environment?: string;\n}): Promise<AnalyticsPixelRow> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const row = await db.query<AnalyticsPixelRow>(\n `INSERT INTO analytics_pixels(id, site_id, public_id, name, environment)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,\n [\n randomUUID(),\n input.siteId,\n publicPixelId(),\n input.name.trim(),\n input.environment || \"production\",\n ],\n );\n return { ...row.rows[0], domains: [] };\n}\n\nexport async function listAnalyticsPixels(\n siteId: string,\n userId: number,\n): Promise<AnalyticsPixelRow[]> {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const [pixels, domains] = await Promise.all([\n db.query<Omit<AnalyticsPixelRow, \"domains\">>(\n `SELECT p.id, p.site_id, p.public_id, p.name, p.environment, p.status,\n p.created_at::text, max(e.occurred_at)::text AS last_event_at\n FROM analytics_pixels p\n LEFT JOIN analytics_events e ON e.pixel_id = p.id\n WHERE p.site_id = $1\n GROUP BY p.id\n ORDER BY p.created_at ASC`,\n [siteId],\n ),\n db.query<AnalyticsPixelRow[\"domains\"][number] & { pixel_id: string }>(\n `SELECT pixel_id, hostname, state, first_seen_at::text, last_seen_at::text,\n accepted_count::int, rejected_count::int\n FROM analytics_pixel_domains\n WHERE pixel_id IN (SELECT id FROM analytics_pixels WHERE site_id = $1)\n ORDER BY last_seen_at DESC`,\n [siteId],\n ),\n ]);\n return pixels.rows.map((pixel) => ({\n ...pixel,\n domains: domains.rows\n .filter((domain) => domain.pixel_id === pixel.id)\n .map(({ pixel_id: _pixelId, ...domain }) => domain),\n }));\n}\n\nexport async function updateAnalyticsPixel(input: {\n siteId: string;\n pixelId: string;\n userId: number;\n name?: string;\n environment?: string;\n status?: PixelStatus;\n}): Promise<void> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const result = await db.query(\n `UPDATE analytics_pixels\n SET name = COALESCE($3, name),\n environment = COALESCE($4, environment),\n status = COALESCE($5, status),\n disabled_at = CASE WHEN $5 = 'disabled' THEN now() WHEN $5 = 'active' THEN NULL ELSE disabled_at END,\n updated_at = now()\n WHERE id = $1 AND site_id = $2`,\n [\n input.pixelId,\n input.siteId,\n input.name ?? null,\n input.environment ?? null,\n input.status ?? null,\n ],\n );\n if (!result.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_pixel_not_found\",\n \"Pixel not found.\",\n 404,\n );\n}\n\nexport async function setAnalyticsPixelDomainState(input: {\n siteId: string;\n pixelId: string;\n userId: number;\n hostname: string;\n state: \"approved\" | \"rejected\" | \"revoked\";\n}): Promise<void> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const hostname = normalizeObservedHostname(`https://${input.hostname}`);\n if (!hostname)\n throw new AnalyticsRepositoryError(\n \"invalid_hostname\",\n \"Enter a valid hostname.\",\n );\n const result = await db.query(\n `INSERT INTO analytics_pixel_domains(id, pixel_id, hostname, state)\n SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3\n ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()\n RETURNING id`,\n [randomUUID(), input.pixelId, input.siteId, hostname, input.state],\n );\n if (!result.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_pixel_not_found\",\n \"Pixel not found.\",\n 404,\n );\n}\n\nexport interface IngestionEvent {\n eventId: string;\n eventName: string;\n occurredAt: string;\n visitorId?: string;\n sessionId?: string;\n path?: string;\n canonicalUrl?: string;\n title?: string;\n referrer?: string;\n source?: string;\n medium?: string;\n campaign?: string;\n clickIds?: Record<string, string>;\n deviceClass?: string;\n properties?: Record<string, unknown>;\n}\n\nconst blockedPropertyName =\n /(email|phone|password|token|secret|authorization|cookie|name|address|form|body)/i;\n\nexport function normalizeAnalyticsPath(value: string | undefined): string {\n if (!value) return \"/\";\n try {\n const parsed = new URL(value, \"https://analytics.invalid\");\n return parsed.pathname.slice(0, 2000) || \"/\";\n } catch {\n return \"/\";\n }\n}\n\nexport function normalizeAnalyticsUrl(\n value: string | undefined,\n): string | null {\n if (!value) return null;\n try {\n const parsed = new URL(value);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\")\n return null;\n parsed.search = \"\";\n parsed.hash = \"\";\n return parsed.toString().slice(0, 3000);\n } catch {\n return null;\n }\n}\n\nexport function sanitizeAnalyticsProperties(\n value: Record<string, unknown> | undefined,\n): Record<string, string | number | boolean | null> {\n const output: Record<string, string | number | boolean | null> = {};\n for (const [key, property] of Object.entries(value ?? {}).slice(0, 24)) {\n if (!/^[a-z][a-z0-9_]{0,63}$/i.test(key) || blockedPropertyName.test(key))\n continue;\n if (\n property === null ||\n typeof property === \"boolean\" ||\n typeof property === \"number\"\n )\n output[key] = property;\n else if (typeof property === \"string\") output[key] = property.slice(0, 300);\n }\n return output;\n}\n\nexport interface IngestionResult {\n requestId: string;\n accepted: number;\n rejected: number;\n reasonCodes: string[];\n hostname: string | null;\n siteId: string | null;\n pixelId: string | null;\n}\n\nexport async function ingestAnalyticsEvents(input: {\n publicPixelId: string;\n origin: string | null;\n events: IngestionEvent[];\n geography?: { countryCode?: string | null; regionCode?: string | null };\n}): Promise<IngestionResult> {\n const db = getAnalyticsPool();\n const requestId = `air_${randomUUID()}`;\n const hostname = normalizeObservedHostname(input.origin);\n const pixelResult = await db.query<{\n id: string;\n site_id: string;\n status: PixelStatus;\n entitled: boolean;\n }>(\n `SELECT p.id, p.site_id, p.status,\n COALESCE(\n (e.eligible AND e.access_expires_at > now()) OR e.trial_ends_at > now(),\n false\n ) AS entitled\n FROM analytics_pixels p\n JOIN analytics_sites s ON s.id = p.site_id\n LEFT JOIN analytics_thorbit_entitlements e ON e.user_id = s.owner_user_id\n WHERE p.public_id = $1`,\n [input.publicPixelId],\n );\n const pixel = pixelResult.rows[0];\n if (!pixel || pixel.status !== \"active\") {\n const reasons = [pixel ? \"pixel_disabled\" : \"pixel_unknown\"];\n await db.query(\n `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,\n [\n randomUUID(),\n requestId,\n pixel?.site_id ?? null,\n pixel?.id ?? null,\n hostname,\n input.events.length,\n JSON.stringify(reasons),\n ],\n );\n return {\n requestId,\n accepted: 0,\n rejected: input.events.length,\n reasonCodes: reasons,\n hostname,\n siteId: pixel?.site_id ?? null,\n pixelId: pixel?.id ?? null,\n };\n }\n if (!pixel.entitled) {\n const reasons = [\"thorbit_entitlement_required\"];\n await db.query(\n `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,\n [\n randomUUID(),\n requestId,\n pixel.site_id,\n pixel.id,\n hostname,\n input.events.length,\n JSON.stringify(reasons),\n ],\n );\n return {\n requestId,\n accepted: 0,\n rejected: input.events.length,\n reasonCodes: reasons,\n hostname,\n siteId: pixel.site_id,\n pixelId: pixel.id,\n };\n }\n if (!hostname) {\n const reasons = [\"origin_missing_or_invalid\"];\n await db.query(\n `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,\n [\n randomUUID(),\n requestId,\n pixel.site_id,\n pixel.id,\n input.events.length,\n JSON.stringify(reasons),\n ],\n );\n return {\n requestId,\n accepted: 0,\n rejected: input.events.length,\n reasonCodes: reasons,\n hostname,\n siteId: pixel.site_id,\n pixelId: pixel.id,\n };\n }\n\n const domainResult = await db.query<{ state: PixelDomainState }>(\n `INSERT INTO analytics_pixel_domains(id, pixel_id, hostname, state, rejected_count)\n VALUES ($1, $2, $3, 'observed_unverified', 0)\n ON CONFLICT(pixel_id, hostname) DO UPDATE\n SET last_seen_at = now(), updated_at = now()\n RETURNING state`,\n [randomUUID(), pixel.id, hostname],\n );\n if (domainResult.rows[0]?.state !== \"approved\") {\n await db.query(\n `UPDATE analytics_pixel_domains\n SET rejected_count = rejected_count + $3, last_seen_at = now(), updated_at = now()\n WHERE pixel_id = $1 AND hostname = $2`,\n [pixel.id, hostname, input.events.length],\n );\n const reasons = [\"domain_unapproved\"];\n await db.query(\n `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,\n [\n randomUUID(),\n requestId,\n pixel.site_id,\n pixel.id,\n hostname,\n input.events.length,\n JSON.stringify(reasons),\n ],\n );\n return {\n requestId,\n accepted: 0,\n rejected: input.events.length,\n reasonCodes: reasons,\n hostname,\n siteId: pixel.site_id,\n pixelId: pixel.id,\n };\n }\n\n const client = await db.connect();\n let accepted = 0;\n let rejected = 0;\n const reasonCodes = new Set<string>();\n try {\n await client.query(\"BEGIN\");\n for (const event of input.events) {\n try {\n const attribution = resolveAnalyticsAttribution(event);\n const resolvedPerson =\n event.visitorId || event.sessionId\n ? await client.query<{ person_id: string }>(\n `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id\n WHERE n.site_id = $1 AND ((n.kind = 'visitor_id' AND n.value_hmac = $2) OR (n.kind = 'session_id' AND n.value_hmac = $3))\n ORDER BY e.confidence DESC, e.last_seen_at DESC LIMIT 1`,\n [\n pixel.site_id,\n event.visitorId\n ? identityHmac(\"visitor_id\", event.visitorId)\n : \"\",\n event.sessionId\n ? identityHmac(\"session_id\", event.sessionId)\n : \"\",\n ],\n )\n : { rows: [] };\n const inserted = await client.query(\n `INSERT INTO analytics_events(\n id, event_id, site_id, pixel_id, hostname, visitor_id, session_id, event_name,\n occurred_at, path, canonical_url, title, referrer, source, medium, campaign,\n device_class, properties, country_code, region_code, click_ids, person_id,\n channel_family, platform, campaign_id, ad_set_id, ad_id, creative_id, placement, utm_term, utm_content\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18::jsonb, $19, $20, $21::jsonb, $22,\n $23, $24, $25, $26, $27, $28, $29, $30, $31\n ) ON CONFLICT(site_id, event_id) DO NOTHING`,\n [\n randomUUID(),\n event.eventId,\n pixel.site_id,\n pixel.id,\n hostname,\n event.visitorId ?? null,\n event.sessionId ?? null,\n event.eventName,\n event.occurredAt,\n normalizeAnalyticsPath(event.path || event.canonicalUrl),\n normalizeAnalyticsUrl(event.canonicalUrl),\n event.title?.slice(0, 500) ?? null,\n normalizeAnalyticsUrl(event.referrer),\n event.source?.slice(0, 180) ?? null,\n event.medium?.slice(0, 180) ?? null,\n event.campaign?.slice(0, 240) ?? null,\n event.deviceClass ?? null,\n JSON.stringify(sanitizeAnalyticsProperties(event.properties)),\n normalizeGeographyCode(input.geography?.countryCode, 2),\n normalizeGeographyCode(input.geography?.regionCode, 8),\n JSON.stringify(sanitizeClickIds(event.clickIds)),\n resolvedPerson.rows[0]?.person_id ?? null,\n attribution.channelFamily,\n attribution.platform,\n attribution.campaignId,\n attribution.adSetId,\n attribution.adId,\n attribution.creativeId,\n attribution.placement,\n attribution.term,\n attribution.content,\n ],\n );\n if (inserted.rowCount) accepted += 1;\n else {\n rejected += 1;\n reasonCodes.add(\"duplicate_event\");\n }\n } catch {\n rejected += 1;\n reasonCodes.add(\"event_insert_failed\");\n }\n }\n await client.query(\n `UPDATE analytics_pixel_domains\n SET accepted_count = accepted_count + $3,\n rejected_count = rejected_count + $4,\n last_seen_at = now(), updated_at = now()\n WHERE pixel_id = $1 AND hostname = $2`,\n [pixel.id, hostname, accepted, rejected],\n );\n await client.query(\n `INSERT INTO analytics_ingestion_receipts(\n id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,\n [\n randomUUID(),\n requestId,\n pixel.site_id,\n pixel.id,\n hostname,\n accepted,\n rejected,\n JSON.stringify([...reasonCodes]),\n ],\n );\n await client.query(\"COMMIT\");\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n return {\n requestId,\n accepted,\n rejected,\n reasonCodes: [...reasonCodes],\n hostname,\n siteId: pixel.site_id,\n pixelId: pixel.id,\n };\n}\n\nexport interface AnalyticsFilters {\n start: string;\n end: string;\n timezone?: string;\n pixelIds?: string[];\n hostnames?: string[];\n source?: string;\n medium?: string;\n campaign?: string;\n eventName?: string;\n attributionModel?: \"first_touch\" | \"last_touch\";\n channelFamily?: string;\n platform?: string;\n referrer?: string;\n path?: string;\n deviceClass?: string;\n countryCode?: string;\n regionCode?: string;\n conversionKind?: string;\n}\n\nfunction normalizeGeographyCode(\n value: string | null | undefined,\n max: number,\n): string | null {\n const normalized = value?.trim().toUpperCase();\n return normalized && /^[A-Z0-9-]+$/.test(normalized)\n ? normalized.slice(0, max)\n : null;\n}\n\nexport interface AnalyticsPageInput {\n limit?: number;\n cursor?: string;\n}\n\nexport interface AnalyticsPageInfo {\n nextCursor: string | null;\n hasNextPage: boolean;\n}\n\nfunction pageFingerprint(\n scope: string,\n siteId: string,\n filters: AnalyticsFilters,\n): string {\n return createHash(\"sha256\")\n .update(JSON.stringify({ scope, siteId, filters }))\n .digest(\"base64url\")\n .slice(0, 18);\n}\n\nfunction decodePageOffset(\n cursor: string | undefined,\n fingerprint: string,\n): number {\n if (!cursor) return 0;\n try {\n const parsed = JSON.parse(\n Buffer.from(cursor, \"base64url\").toString(\"utf8\"),\n ) as { v?: number; f?: string; o?: number };\n if (\n parsed.v !== 1 ||\n parsed.f !== fingerprint ||\n !Number.isInteger(parsed.o) ||\n Number(parsed.o) < 0\n )\n throw new Error(\"invalid\");\n return Number(parsed.o);\n } catch {\n throw new AnalyticsRepositoryError(\n \"invalid_cursor\",\n \"The pagination cursor is invalid for these filters.\",\n );\n }\n}\n\nfunction paginateRows<T>(\n rows: T[],\n input: AnalyticsPageInput | undefined,\n fingerprint: string,\n): { items: T[]; pageInfo: AnalyticsPageInfo } {\n if (!input)\n return { items: rows, pageInfo: { hasNextPage: false, nextCursor: null } };\n const limit = Math.min(100, Math.max(1, Math.trunc(input?.limit ?? 25)));\n const offset = decodePageOffset(input?.cursor, fingerprint);\n const items = rows.slice(offset, offset + limit);\n const hasNextPage = offset + items.length < rows.length;\n return {\n items,\n pageInfo: {\n hasNextPage,\n nextCursor: hasNextPage\n ? Buffer.from(\n JSON.stringify({ v: 1, f: fingerprint, o: offset + items.length }),\n ).toString(\"base64url\")\n : null,\n },\n };\n}\n\nexport async function createAnalyticsConversion(input: {\n userId: number;\n siteId: string;\n pixelId?: string;\n sessionId?: string;\n sourceEventId: string;\n conversionKind: string;\n valueMinor?: number;\n currency?: string;\n occurredAt: string;\n}): Promise<{ id: string; created: boolean }> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n if (input.pixelId) {\n const pixel = await db.query(\n `SELECT 1 FROM analytics_pixels WHERE id = $1 AND site_id = $2`,\n [input.pixelId, input.siteId],\n );\n if (!pixel.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_pixel_not_found\",\n \"Pixel not found.\",\n 404,\n );\n }\n const id = randomUUID();\n const resolvedPerson = input.sessionId\n ? await db.query<{ person_id: string }>(\n `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id\n WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,\n [input.siteId, identityHmac(\"session_id\", input.sessionId)],\n )\n : { rows: [] };\n const result = await db.query<{ id: string }>(\n `INSERT INTO analytics_conversions(\n id, source_event_id, site_id, pixel_id, session_id, conversion_kind, value_minor, currency, occurred_at, person_id\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ON CONFLICT(site_id, source_event_id) DO NOTHING\n RETURNING id`,\n [\n id,\n input.sourceEventId,\n input.siteId,\n input.pixelId ?? null,\n input.sessionId ?? null,\n input.conversionKind,\n input.valueMinor ?? 0,\n (input.currency || \"USD\").toUpperCase(),\n input.occurredAt,\n resolvedPerson.rows[0]?.person_id ?? null,\n ],\n );\n return { id: result.rows[0]?.id ?? id, created: Boolean(result.rowCount) };\n}\n\nfunction eventFilterSql(siteId: string, filters: AnalyticsFilters) {\n const values: unknown[] = [siteId, filters.start, filters.end];\n const clauses = [\n \"e.site_id = $1\",\n \"e.occurred_at >= $2::timestamptz\",\n \"e.occurred_at < $3::timestamptz\",\n ];\n const add = (sql: string, value: unknown) => {\n values.push(value);\n clauses.push(sql.replace(\"?\", `$${values.length}`));\n };\n if (filters.pixelIds?.length)\n add(\"e.pixel_id = ANY(?::uuid[])\", filters.pixelIds);\n if (filters.hostnames?.length)\n add(\"e.hostname = ANY(?::text[])\", filters.hostnames);\n if (filters.source) add(`COALESCE(e.source, '(direct)') = ?`, filters.source);\n if (filters.medium) add(`COALESCE(e.medium, '(none)') = ?`, filters.medium);\n if (filters.campaign)\n add(`COALESCE(e.campaign, '(none)') = ?`, filters.campaign);\n if (filters.eventName) add(\"e.event_name = ?\", filters.eventName);\n if (filters.channelFamily)\n add(`COALESCE(e.channel_family, 'other') = ?`, filters.channelFamily);\n if (filters.platform)\n add(`COALESCE(e.platform, e.source, '(direct)') = ?`, filters.platform);\n if (filters.referrer)\n add(`COALESCE(e.referrer, '') ILIKE '%' || ? || '%'`, filters.referrer);\n if (filters.path) add(`e.path = ?`, filters.path);\n if (filters.deviceClass)\n add(`COALESCE(e.device_class, 'unknown') = ?`, filters.deviceClass);\n if (filters.countryCode)\n add(\n `COALESCE(e.country_code, 'unknown') = ?`,\n filters.countryCode.toUpperCase(),\n );\n if (filters.regionCode)\n add(\n `COALESCE(e.region_code, 'unknown') = ?`,\n filters.regionCode.toUpperCase(),\n );\n return { where: clauses.join(\" AND \"), values };\n}\n\nfunction conversionFilterSql(siteId: string, filters: AnalyticsFilters) {\n const values: unknown[] = [siteId, filters.start, filters.end];\n const clauses = [\n \"c.site_id = $1\",\n \"c.occurred_at >= $2::timestamptz\",\n \"c.occurred_at < $3::timestamptz\",\n ];\n const add = (sql: string, value: unknown) => {\n values.push(value);\n clauses.push(sql.replaceAll(\"?\", `$${values.length}`));\n };\n if (filters.pixelIds?.length)\n add(\"c.pixel_id = ANY(?::uuid[])\", filters.pixelIds);\n if (filters.hostnames?.length)\n add(\n \"EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND ce.hostname = ANY(?::text[]))\",\n filters.hostnames,\n );\n if (filters.source)\n add(`COALESCE(first_event.source, '(direct)') = ?`, filters.source);\n if (filters.medium)\n add(`COALESCE(first_event.medium, '(none)') = ?`, filters.medium);\n if (filters.campaign)\n add(`COALESCE(first_event.campaign, '(none)') = ?`, filters.campaign);\n if (filters.eventName)\n add(\n \"EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND ce.event_name = ?)\",\n filters.eventName,\n );\n if (filters.channelFamily)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.channel_family, 'other') = ?)`,\n filters.channelFamily,\n );\n if (filters.platform)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.platform, ce.source, '(direct)') = ?)`,\n filters.platform,\n );\n if (filters.referrer)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.referrer, '') ILIKE '%' || ? || '%')`,\n filters.referrer,\n );\n if (filters.path)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND ce.path = ?)`,\n filters.path,\n );\n if (filters.deviceClass)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.device_class, 'unknown') = ?)`,\n filters.deviceClass,\n );\n if (filters.countryCode)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.country_code, 'unknown') = ?)`,\n filters.countryCode.toUpperCase(),\n );\n if (filters.regionCode)\n add(\n `EXISTS (SELECT 1 FROM analytics_events ce WHERE ce.site_id = c.site_id AND ce.session_id = c.session_id AND COALESCE(ce.region_code, 'unknown') = ?)`,\n filters.regionCode.toUpperCase(),\n );\n if (filters.conversionKind)\n add(`c.conversion_kind = ?`, filters.conversionKind);\n return { where: clauses.join(\" AND \"), values };\n}\n\nexport async function analyticsOverview(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const result = await db.query<{\n visitors: number;\n sessions: number;\n pageviews: number;\n events: number;\n first_event_at: string | null;\n last_event_at: string | null;\n }>(\n `SELECT count(DISTINCT visitor_id)::int AS visitors,\n count(DISTINCT session_id)::int AS sessions,\n count(*) FILTER (WHERE event_name = 'page_view')::int AS pageviews,\n count(*)::int AS events,\n min(occurred_at)::text AS first_event_at,\n max(occurred_at)::text AS last_event_at\n FROM analytics_events e WHERE ${query.where}`,\n query.values,\n );\n const conversionQuery = conversionFilterSql(siteId, filters);\n const conversions = await db.query<{\n conversions: number;\n revenue_minor: number;\n }>(\n `SELECT count(*)::int AS conversions, COALESCE(sum(c.value_minor), 0)::int AS revenue_minor\n FROM analytics_conversions c\n LEFT JOIN LATERAL (\n SELECT e.source, e.medium, e.campaign FROM analytics_events e\n WHERE e.site_id = c.site_id AND e.session_id = c.session_id\n ORDER BY e.occurred_at ASC LIMIT 1\n ) first_event ON true\n WHERE ${conversionQuery.where}`,\n conversionQuery.values,\n );\n const row = result.rows[0];\n const conversion = conversions.rows[0];\n return {\n ...row,\n conversions: conversion.conversions,\n revenueMinor: conversion.revenue_minor,\n conversionRate:\n row.sessions > 0 ? conversion.conversions / row.sessions : 0,\n dataFreshThrough: row.last_event_at,\n };\n}\n\nexport async function analyticsTimeseries(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const timezone = filters.timezone || \"UTC\";\n const timezoneParameter = `$${query.values.length + 1}`;\n const result = await db.query(\n `SELECT date_trunc('day', e.occurred_at AT TIME ZONE ${timezoneParameter})::date::text AS date,\n count(DISTINCT e.visitor_id)::int AS visitors,\n count(DISTINCT e.session_id)::int AS sessions,\n count(*) FILTER (WHERE e.event_name = 'page_view')::int AS pageviews,\n count(*)::int AS events\n FROM analytics_events e\n WHERE ${query.where}\n GROUP BY 1 ORDER BY 1`,\n [...query.values, timezone],\n );\n const conversionQuery = conversionFilterSql(siteId, filters);\n const conversionTimezoneParameter = `$${conversionQuery.values.length + 1}`;\n const conversions = await db.query<{\n date: string;\n conversions: number;\n revenue_minor: string;\n }>(\n `SELECT date_trunc('day', c.occurred_at AT TIME ZONE ${conversionTimezoneParameter})::date::text AS date,\n count(*)::int AS conversions,\n COALESCE(sum(c.value_minor), 0)::bigint AS revenue_minor\n FROM analytics_conversions c\n LEFT JOIN LATERAL (\n SELECT e.source, e.medium, e.campaign FROM analytics_events e\n WHERE e.site_id = c.site_id AND e.session_id = c.session_id\n ORDER BY e.occurred_at ASC LIMIT 1\n ) first_event ON true\n WHERE ${conversionQuery.where}\n GROUP BY 1 ORDER BY 1`,\n [...conversionQuery.values, timezone],\n );\n const byDate = new Map<string, Record<string, string | number>>();\n for (const row of result.rows)\n byDate.set(String(row.date), { ...row, conversions: 0, revenueMinor: 0 });\n for (const row of conversions.rows) {\n const current = byDate.get(row.date) ?? {\n date: row.date,\n visitors: 0,\n sessions: 0,\n pageviews: 0,\n events: 0,\n };\n byDate.set(row.date, {\n ...current,\n conversions: row.conversions,\n revenueMinor: Number(row.revenue_minor),\n });\n }\n const points = [...byDate.values()].sort((left, right) =>\n String(left.date).localeCompare(String(right.date)),\n );\n return { points, timezone, dataFreshThrough: points.at(-1)?.date ?? null };\n}\n\nexport async function analyticsAcquisition(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const result = await db.query(\n `SELECT COALESCE(e.source, '(direct)') AS source,\n COALESCE(e.medium, '(none)') AS medium,\n COALESCE(e.campaign, '(none)') AS campaign,\n count(DISTINCT e.visitor_id)::int AS visitors,\n count(DISTINCT e.session_id)::int AS sessions,\n count(*) FILTER (WHERE e.event_name = 'page_view')::int AS pageviews\n FROM analytics_events e\n WHERE ${query.where}\n GROUP BY 1, 2, 3\n ORDER BY sessions DESC, visitors DESC\n LIMIT 1000`,\n query.values,\n );\n const values: unknown[] = [siteId, filters.start, filters.end];\n const touchClauses = [\n \"c.site_id = $1\",\n \"c.occurred_at >= $2::timestamptz\",\n \"c.occurred_at < $3::timestamptz\",\n ];\n const addTouch = (sql: string, value: unknown) => {\n values.push(value);\n touchClauses.push(sql.replace(\"?\", `$${values.length}`));\n };\n if (filters.pixelIds?.length)\n addTouch(\"touch.pixel_id = ANY(?::uuid[])\", filters.pixelIds);\n if (filters.hostnames?.length)\n addTouch(\"touch.hostname = ANY(?::text[])\", filters.hostnames);\n if (filters.source)\n addTouch(`COALESCE(touch.source, '(direct)') = ?`, filters.source);\n if (filters.medium)\n addTouch(`COALESCE(touch.medium, '(none)') = ?`, filters.medium);\n if (filters.campaign)\n addTouch(`COALESCE(touch.campaign, '(none)') = ?`, filters.campaign);\n if (filters.channelFamily)\n addTouch(\n `COALESCE(touch.channel_family, 'other') = ?`,\n filters.channelFamily,\n );\n if (filters.platform)\n addTouch(\n `COALESCE(touch.platform, touch.source, '(direct)') = ?`,\n filters.platform,\n );\n if (filters.referrer)\n addTouch(\n `COALESCE(touch.referrer, '') ILIKE '%' || ? || '%'`,\n filters.referrer,\n );\n if (filters.path) addTouch(`touch.path = ?`, filters.path);\n if (filters.deviceClass)\n addTouch(\n `COALESCE(touch.device_class, 'unknown') = ?`,\n filters.deviceClass,\n );\n if (filters.countryCode)\n addTouch(\n `COALESCE(touch.country_code, 'unknown') = ?`,\n filters.countryCode.toUpperCase(),\n );\n if (filters.regionCode)\n addTouch(\n `COALESCE(touch.region_code, 'unknown') = ?`,\n filters.regionCode.toUpperCase(),\n );\n if (filters.conversionKind)\n addTouch(`c.conversion_kind = ?`, filters.conversionKind);\n const direction = filters.attributionModel === \"last_touch\" ? \"DESC\" : \"ASC\";\n const attributed = await db.query<{\n source: string;\n medium: string;\n campaign: string;\n conversions: number;\n revenue_minor: number;\n }>(\n `SELECT COALESCE(touch.source, '(direct)') AS source,\n COALESCE(touch.medium, '(none)') AS medium,\n COALESCE(touch.campaign, '(none)') AS campaign,\n count(*)::int AS conversions, COALESCE(sum(c.value_minor), 0)::bigint AS revenue_minor\n FROM analytics_conversions c\n JOIN LATERAL (\n SELECT e.* FROM analytics_events e\n WHERE e.site_id = c.site_id AND e.occurred_at <= c.occurred_at\n AND ((c.person_id IS NOT NULL AND e.person_id = c.person_id)\n OR (c.person_id IS NULL AND e.session_id = c.session_id))\n ORDER BY e.occurred_at ${direction} LIMIT 1\n ) touch ON true\n WHERE ${touchClauses.join(\" AND \")}\n GROUP BY 1,2,3`,\n values,\n );\n const outcomes = new Map(\n attributed.rows.map((row) => [\n `${row.source}\\u0000${row.medium}\\u0000${row.campaign}`,\n row,\n ]),\n );\n const rows = result.rows.map((row: any) => {\n const outcome = outcomes.get(\n `${row.source}\\u0000${row.medium}\\u0000${row.campaign}`,\n );\n const conversions = Number(outcome?.conversions ?? 0);\n return {\n ...row,\n conversions,\n conversionRate: row.sessions > 0 ? conversions / row.sessions : 0,\n revenueMinor: Number(outcome?.revenue_minor ?? 0),\n };\n });\n for (const outcome of attributed.rows) {\n const key = `${outcome.source}\\u0000${outcome.medium}\\u0000${outcome.campaign}`;\n if (\n !result.rows.some(\n (row: any) =>\n `${row.source}\\u0000${row.medium}\\u0000${row.campaign}` === key,\n )\n ) {\n rows.push({\n source: outcome.source,\n medium: outcome.medium,\n campaign: outcome.campaign,\n visitors: 0,\n sessions: 0,\n pageviews: 0,\n conversions: Number(outcome.conversions),\n conversionRate: 0,\n revenueMinor: Number(outcome.revenue_minor),\n });\n }\n }\n rows.sort(\n (a: any, b: any) =>\n Number(b.sessions) - Number(a.sessions) ||\n Number(b.conversions) - Number(a.conversions),\n );\n const paged = paginateRows(\n rows,\n page,\n pageFingerprint(\"acquisition\", siteId, filters),\n );\n return {\n channels: paged.items,\n pageInfo: paged.pageInfo,\n dataFreshThrough: new Date().toISOString(),\n };\n}\n\nconst inferredFamilySql = `COALESCE(e.channel_family, CASE\n WHEN lower(COALESCE(e.source,'') || ' ' || COALESCE(e.referrer,'')) ~ '(perplexity|chatgpt|openai|claude|anthropic|grok|xai|ai[_ -]?overview|google[_ -]?ai|gemini)' THEN 'llm'\n WHEN lower(COALESCE(e.source,'') || ' ' || COALESCE(e.referrer,'')) ~ '(facebook|instagram|linkedin|tiktok|youtube|youtu\\\\.be|twitter|x\\\\.com)' THEN 'social'\n WHEN lower(COALESCE(e.source,'') || ' ' || COALESCE(e.referrer,'')) ~ '(^|[^a-z])(g2|bbb)([^a-z]|$)|trustpilot|capterra|yelp|better business bureau' THEN 'review'\n WHEN COALESCE(e.source,'') = '' THEN 'direct'\n WHEN lower(COALESCE(e.medium,'')) ~ 'email|newsletter' THEN 'email'\n ELSE 'other' END)`;\n\nexport async function analyticsChannelBreakdown(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const result = await db.query(\n `\n SELECT ${inferredFamilySql} AS family,\n COALESCE(e.platform, NULLIF(e.source,''), '(direct)') AS platform,\n COALESCE(NULLIF(e.campaign,''), '(none)') AS campaign,\n COALESCE(NULLIF(e.campaign_id,''), '(unknown)') AS campaign_id,\n COALESCE(NULLIF(e.ad_set_id,''), '(unknown)') AS ad_set_id,\n COALESCE(NULLIF(e.ad_id,''), '(unknown)') AS ad_id,\n COALESCE(NULLIF(e.creative_id,''), '(unknown)') AS creative_id,\n count(DISTINCT e.visitor_id)::int AS visitors,\n count(DISTINCT e.session_id)::int AS sessions,\n count(*) FILTER (WHERE e.event_name = 'page_view')::int AS pageviews,\n count(*)::int AS events\n FROM analytics_events e\n WHERE ${query.where}\n GROUP BY 1,2,3,4,5,6,7\n ORDER BY sessions DESC, events DESC\n LIMIT 2000`,\n query.values,\n );\n const families = [\"llm\", \"social\", \"review\"] as const;\n const sections = Object.fromEntries(\n families.map((family) => [\n family,\n result.rows.filter((row) => row.family === family),\n ]),\n );\n return {\n sections,\n rows: result.rows,\n dataFreshThrough: new Date().toISOString(),\n };\n}\n\nexport async function analyticsContent(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const result = await db.query(\n `WITH filtered AS (\n SELECT e.* FROM analytics_events e WHERE ${query.where}\n ), page_metrics AS (\n SELECT path, count(DISTINCT visitor_id)::int AS visitors,\n count(DISTINCT session_id)::int AS sessions,\n count(*) FILTER (WHERE event_name = 'page_view')::int AS pageviews,\n count(*) FILTER (WHERE event_name <> 'page_view')::int AS engagement_events,\n count(*)::int AS events\n FROM filtered GROUP BY path\n ), session_bounds AS (\n SELECT session_id,\n (array_agg(path ORDER BY occurred_at ASC, id ASC))[1] AS entrance_path,\n (array_agg(path ORDER BY occurred_at DESC, id DESC))[1] AS exit_path\n FROM filtered WHERE session_id IS NOT NULL GROUP BY session_id\n ), entrances AS (\n SELECT entrance_path AS path, count(*)::int AS entrances FROM session_bounds GROUP BY entrance_path\n ), exits AS (\n SELECT exit_path AS path, count(*)::int AS exits FROM session_bounds GROUP BY exit_path\n ), assisted AS (\n SELECT f.path, count(DISTINCT c.id)::int AS conversions\n FROM analytics_conversions c JOIN filtered f ON f.session_id = c.session_id\n WHERE c.site_id = $1 AND c.occurred_at >= $2::timestamptz AND c.occurred_at < $3::timestamptz\n GROUP BY f.path\n )\n SELECT metrics.*, COALESCE(entrances.entrances, 0)::int AS entrances,\n COALESCE(exits.exits, 0)::int AS exits,\n COALESCE(assisted.conversions, 0)::int AS conversions\n FROM page_metrics metrics\n LEFT JOIN entrances USING(path)\n LEFT JOIN exits USING(path)\n LEFT JOIN assisted USING(path)\n ORDER BY metrics.pageviews DESC, metrics.events DESC, metrics.path ASC\n LIMIT 1000`,\n query.values,\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"content\", siteId, filters),\n );\n return {\n content: paged.items,\n pageInfo: paged.pageInfo,\n dataFreshThrough: new Date().toISOString(),\n };\n}\n\nexport async function analyticsEventCounts(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const [result, samples] = await Promise.all([\n db.query(\n `SELECT e.event_name AS name, count(*)::int AS count, max(e.occurred_at)::text AS last_seen_at\n FROM analytics_events e\n WHERE ${query.where}\n GROUP BY e.event_name ORDER BY count DESC, e.event_name ASC\n LIMIT 1000`,\n query.values,\n ),\n db.query(\n `SELECT e.event_id, e.event_name, e.path, e.hostname,\n COALESCE(e.source, '(direct)') AS source,\n COALESCE(e.campaign, '(none)') AS campaign,\n e.occurred_at::text\n FROM analytics_events e WHERE ${query.where}\n ORDER BY e.occurred_at DESC, e.id DESC LIMIT 25`,\n query.values,\n ),\n ]);\n const receiptValues: unknown[] = [siteId, filters.start, filters.end];\n const receiptClauses = [\n \"r.site_id = $1\",\n \"r.created_at >= $2::timestamptz\",\n \"r.created_at < $3::timestamptz\",\n ];\n const addReceipt = (clause: string, value: unknown) => {\n receiptValues.push(value);\n receiptClauses.push(clause.replace(\"?\", `$${receiptValues.length}`));\n };\n if (filters.pixelIds?.length)\n addReceipt(\"r.pixel_id = ANY(?::uuid[])\", filters.pixelIds);\n if (filters.hostnames?.length)\n addReceipt(\"r.hostname = ANY(?::text[])\", filters.hostnames);\n const rejections = await db.query(\n `SELECT reason.code, count(*)::int AS requests,\n COALESCE(sum(r.rejected_count), 0)::int AS rejected_events,\n max(r.created_at)::text AS last_seen_at\n FROM analytics_ingestion_receipts r\n CROSS JOIN LATERAL jsonb_array_elements_text(r.reason_codes) AS reason(code)\n WHERE ${receiptClauses.join(\" AND \")} AND r.rejected_count > 0\n GROUP BY reason.code ORDER BY rejected_events DESC, reason.code ASC`,\n receiptValues,\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"events\", siteId, filters),\n );\n return {\n events: paged.items,\n samples: samples.rows,\n rejections: rejections.rows,\n pageInfo: paged.pageInfo,\n dataFreshThrough: samples.rows[0]?.occurred_at ?? null,\n };\n}\n\nexport async function analyticsConversions(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = conversionFilterSql(siteId, filters);\n const result = await db.query(\n `SELECT c.id, c.source_event_id, c.pixel_id, c.session_id, c.conversion_kind,\n c.value_minor::int, c.currency, c.occurred_at::text,\n COALESCE(first_event.source, '(direct)') AS source,\n COALESCE(first_event.medium, '(none)') AS medium,\n COALESCE(first_event.campaign, '(none)') AS campaign\n FROM analytics_conversions c\n LEFT JOIN LATERAL (\n SELECT e.source, e.medium, e.campaign\n FROM analytics_events e\n WHERE e.site_id = c.site_id AND e.session_id = c.session_id\n ORDER BY e.occurred_at ASC LIMIT 1\n ) first_event ON true\n WHERE ${query.where}\n ORDER BY c.occurred_at DESC\n LIMIT 1000`,\n query.values,\n );\n const summary = result.rows.reduce(\n (current, row) => ({\n count: current.count + 1,\n valueMinor: current.valueMinor + Number(row.value_minor ?? 0),\n }),\n { count: 0, valueMinor: 0 },\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"conversions\", siteId, filters),\n );\n return {\n conversions: paged.items,\n pageInfo: paged.pageInfo,\n summary,\n dataFreshThrough: result.rows[0]?.occurred_at ?? null,\n };\n}\n\nexport async function analyticsPaths(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = conversionFilterSql(siteId, filters);\n const result = await db.query(\n `SELECT c.session_id, c.person_id,\n string_agg(COALESCE(path_event.platform, path_event.source, '(direct)') || ' / ' || COALESCE(path_event.medium, '(none)') ||\n CASE WHEN path_event.campaign IS NOT NULL THEN ' / ' || path_event.campaign ELSE '' END ||\n CASE WHEN path_event.ad_id IS NOT NULL THEN ' / ad:' || path_event.ad_id ELSE '' END,\n ' → ' ORDER BY path_event.occurred_at) AS path,\n count(path_event.id)::int AS touchpoints,\n 1::int AS conversions,\n c.value_minor::int AS value_minor\n FROM analytics_conversions c\n LEFT JOIN LATERAL (\n SELECT e.source, e.medium, e.campaign FROM analytics_events e\n WHERE e.site_id = c.site_id AND e.session_id = c.session_id\n ORDER BY e.occurred_at ASC LIMIT 1\n ) first_event ON true\n JOIN analytics_events path_event ON path_event.site_id = c.site_id AND\n ((c.person_id IS NOT NULL AND path_event.person_id = c.person_id) OR (c.person_id IS NULL AND path_event.session_id = c.session_id))\n WHERE ${query.where}\n GROUP BY c.id\n ORDER BY conversions DESC, value_minor DESC\n LIMIT 1000`,\n query.values,\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"paths\", siteId, filters),\n );\n return {\n paths: paged.items,\n pageInfo: paged.pageInfo,\n dataFreshThrough: new Date().toISOString(),\n };\n}\n\nexport type AnalyticsDimension =\n \"device\" | \"source\" | \"country\" | \"region\" | \"weekday_hour\";\n\nexport async function analyticsDimensions(\n siteId: string,\n userId: number,\n filters: AnalyticsFilters,\n dimension: AnalyticsDimension,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const query = eventFilterSql(siteId, filters);\n const select =\n dimension === \"device\"\n ? `COALESCE(NULLIF(e.device_class, ''), 'unknown') AS key, COALESCE(NULLIF(e.device_class, ''), 'Unknown') AS label`\n : dimension === \"source\"\n ? `COALESCE(NULLIF(e.source, ''), '(direct)') AS key, COALESCE(NULLIF(e.source, ''), '(direct)') AS label`\n : dimension === \"country\"\n ? `COALESCE(NULLIF(e.country_code, ''), 'unknown') AS key, COALESCE(NULLIF(e.country_code, ''), 'Unknown') AS label`\n : dimension === \"region\"\n ? `COALESCE(NULLIF(e.region_code, ''), 'unknown') AS key, COALESCE(NULLIF(e.region_code, ''), 'Unknown') AS label`\n : `extract(dow from e.occurred_at)::int::text || '-' || extract(hour from e.occurred_at)::int::text AS key,\n to_char(e.occurred_at, 'Dy') || ' ' || lpad(extract(hour from e.occurred_at)::int::text, 2, '0') || ':00' AS label`;\n const result = await db.query<{\n key: string;\n label: string;\n visitors: number;\n sessions: number;\n pageviews: number;\n events: number;\n value: number;\n }>(\n `SELECT ${select},\n count(DISTINCT e.visitor_id)::int AS visitors,\n count(DISTINCT e.session_id)::int AS sessions,\n count(*) FILTER (WHERE e.event_name = 'page_view')::int AS pageviews,\n count(*)::int AS events,\n count(*)::int AS value\n FROM analytics_events e\n WHERE ${query.where}\n GROUP BY 1, 2\n ORDER BY value DESC, key ASC\n LIMIT 1000`,\n query.values,\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(`dimension:${dimension}`, siteId, filters),\n );\n const chartRecommendation =\n dimension === \"device\"\n ? \"donut\"\n : dimension === \"weekday_hour\"\n ? \"heatmap\"\n : dimension === \"country\" || dimension === \"region\"\n ? \"choropleth_or_bar\"\n : \"horizontal_bar\";\n return {\n dimension,\n unit: \"events\",\n chartRecommendation,\n rows: paged.items,\n pageInfo: paged.pageInfo,\n dataFreshThrough: new Date().toISOString(),\n };\n}\n\nexport interface AnalyticsCampaignLinkRow {\n id: string;\n site_id: string;\n pixel_id: string | null;\n name: string;\n destination_url: string;\n short_code: string;\n utm_source: string;\n utm_medium: string;\n utm_campaign: string;\n utm_term: string | null;\n utm_content: string | null;\n ad_group: string | null;\n ad_name: string | null;\n creative_id: string | null;\n status: \"active\" | \"archived\";\n created_at: string;\n click_count: number;\n tagged_url: string;\n}\n\nfunction buildTaggedCampaignUrl(\n row: Pick<\n AnalyticsCampaignLinkRow,\n | \"destination_url\"\n | \"utm_source\"\n | \"utm_medium\"\n | \"utm_campaign\"\n | \"utm_term\"\n | \"utm_content\"\n | \"ad_group\"\n | \"ad_name\"\n | \"creative_id\"\n >,\n): string {\n const url = new URL(row.destination_url);\n for (const [key, value] of [\n [\"utm_source\", row.utm_source],\n [\"utm_medium\", row.utm_medium],\n [\"utm_campaign\", row.utm_campaign],\n [\"utm_term\", row.utm_term],\n [\"utm_content\", row.utm_content],\n [\"ad_group\", row.ad_group],\n [\"ad_name\", row.ad_name],\n [\"creative_id\", row.creative_id],\n ] as const)\n if (value) url.searchParams.set(key, value);\n return url.toString();\n}\n\nfunction campaignLinkWithUrl(\n row: Omit<AnalyticsCampaignLinkRow, \"tagged_url\">,\n): AnalyticsCampaignLinkRow {\n return { ...row, tagged_url: buildTaggedCampaignUrl(row) };\n}\n\nexport async function createAnalyticsCampaignLink(input: {\n siteId: string;\n userId: number;\n pixelId?: string;\n name: string;\n destinationUrl: string;\n shortCode?: string;\n source: string;\n medium: string;\n campaign: string;\n term?: string;\n content?: string;\n adGroup?: string;\n adName?: string;\n creativeId?: string;\n}): Promise<AnalyticsCampaignLinkRow> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n let destination: URL;\n try {\n destination = new URL(input.destinationUrl);\n } catch {\n throw new AnalyticsRepositoryError(\n \"invalid_destination_url\",\n \"Enter a valid destination URL.\",\n );\n }\n if (![\"http:\", \"https:\"].includes(destination.protocol))\n throw new AnalyticsRepositoryError(\n \"invalid_destination_url\",\n \"Destination URL must use HTTP or HTTPS.\",\n );\n if (input.pixelId) {\n const pixel = await db.query(\n \"SELECT 1 FROM analytics_pixels WHERE id = $1 AND site_id = $2\",\n [input.pixelId, input.siteId],\n );\n if (!pixel.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_pixel_not_found\",\n \"Pixel not found.\",\n 404,\n );\n }\n const shortCode = (\n input.shortCode\n ?.trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, \"-\") ||\n randomBytes(5).toString(\"base64url\").toLowerCase()\n ).slice(0, 48);\n try {\n const result = await db.query<Omit<AnalyticsCampaignLinkRow, \"tagged_url\">>(\n `INSERT INTO analytics_campaign_links(\n id, site_id, pixel_id, name, destination_url, short_code, utm_source, utm_medium,\n utm_campaign, utm_term, utm_content, ad_group, ad_name, creative_id, created_by_user_id\n ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)\n RETURNING *, 0::int AS click_count`,\n [\n randomUUID(),\n input.siteId,\n input.pixelId ?? null,\n input.name.trim(),\n destination.toString(),\n shortCode,\n input.source.trim(),\n input.medium.trim(),\n input.campaign.trim(),\n input.term?.trim() || null,\n input.content?.trim() || null,\n input.adGroup?.trim() || null,\n input.adName?.trim() || null,\n input.creativeId?.trim() || null,\n input.userId,\n ],\n );\n return campaignLinkWithUrl(result.rows[0]);\n } catch (error) {\n if ((error as { code?: string }).code === \"23505\")\n throw new AnalyticsRepositoryError(\n \"campaign_short_code_taken\",\n \"That short code is already in use.\",\n 409,\n );\n throw error;\n }\n}\n\nexport async function listAnalyticsCampaignLinks(\n siteId: string,\n userId: number,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const result = await db.query<Omit<AnalyticsCampaignLinkRow, \"tagged_url\">>(\n `SELECT l.*, count(c.id)::int AS click_count\n FROM analytics_campaign_links l LEFT JOIN analytics_campaign_clicks c ON c.link_id = l.id\n WHERE l.site_id = $1\n GROUP BY l.id ORDER BY l.created_at DESC, l.id DESC LIMIT 1000`,\n [siteId],\n );\n const rows = result.rows.map(campaignLinkWithUrl);\n const filters: AnalyticsFilters = { start: \"all\", end: \"all\" };\n const paged = paginateRows(\n rows,\n page,\n pageFingerprint(\"campaign-links\", siteId, filters),\n );\n return { links: paged.items, pageInfo: paged.pageInfo };\n}\n\nexport async function archiveAnalyticsCampaignLink(input: {\n siteId: string;\n linkId: string;\n userId: number;\n}): Promise<void> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const result = await db.query(\n `UPDATE analytics_campaign_links SET status = 'archived', updated_at = now() WHERE id = $1 AND site_id = $2`,\n [input.linkId, input.siteId],\n );\n if (!result.rowCount)\n throw new AnalyticsRepositoryError(\n \"campaign_link_not_found\",\n \"Campaign link not found.\",\n 404,\n );\n}\n\nexport async function resolveAnalyticsCampaignLink(\n shortCode: string,\n referrer?: string | null,\n): Promise<string | null> {\n const db = getAnalyticsPool();\n const result = await db.query<Omit<AnalyticsCampaignLinkRow, \"tagged_url\">>(\n `SELECT l.*, 0::int AS click_count FROM analytics_campaign_links l WHERE l.short_code = $1 AND l.status = 'active'`,\n [shortCode],\n );\n const row = result.rows[0];\n if (!row) return null;\n await db.query(\n `INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,\n [randomUUID(), row.id, normalizeAnalyticsUrl(referrer || undefined)],\n );\n return buildTaggedCampaignUrl(row);\n}\n\nexport interface AnalyticsFormField {\n id: string;\n type:\n | \"text\"\n | \"email\"\n | \"tel\"\n | \"textarea\"\n | \"select\"\n | \"checkbox\"\n | \"radio\"\n | \"number\";\n label: string;\n required: boolean;\n options?: string[];\n}\n\nexport interface AnalyticsFormRow {\n id: string;\n public_id: string;\n site_id: string;\n pixel_id: string;\n name: string;\n slug: string;\n fields: AnalyticsFormField[];\n brand: {\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n radius: number;\n };\n submit_label: string;\n success_message: string;\n redirect_url: string | null;\n consent_text: string | null;\n status: \"draft\" | \"published\" | \"archived\";\n version: number;\n created_at: string;\n submission_count: number;\n}\n\nexport async function createAnalyticsForm(input: {\n siteId: string;\n userId: number;\n pixelId: string;\n name: string;\n fields: AnalyticsFormField[];\n brand: AnalyticsFormRow[\"brand\"];\n submitLabel?: string;\n successMessage?: string;\n redirectUrl?: string;\n consentText?: string;\n publish?: boolean;\n}): Promise<AnalyticsFormRow> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const pixel = await db.query(\n \"SELECT 1 FROM analytics_pixels WHERE id = $1 AND site_id = $2\",\n [input.pixelId, input.siteId],\n );\n if (!pixel.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_pixel_not_found\",\n \"Pixel not found.\",\n 404,\n );\n const baseSlug = normalizeSlug(input.name);\n const slug = `${baseSlug}-${randomBytes(3).toString(\"hex\")}`;\n const publicId = `form_${randomBytes(18).toString(\"base64url\")}`;\n const result = await db.query<AnalyticsFormRow>(\n `INSERT INTO analytics_forms(id, public_id, site_id, pixel_id, name, slug, fields, brand, submit_label, success_message, redirect_url, consent_text, status, created_by_user_id)\n VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)\n RETURNING *, 0::int AS submission_count`,\n [\n randomUUID(),\n publicId,\n input.siteId,\n input.pixelId,\n input.name.trim(),\n slug,\n JSON.stringify(input.fields),\n JSON.stringify(input.brand),\n input.submitLabel?.trim() || \"Submit\",\n input.successMessage?.trim() || \"Thank you. Your response was received.\",\n input.redirectUrl?.trim() || null,\n input.consentText?.trim() || null,\n input.publish ? \"published\" : \"draft\",\n input.userId,\n ],\n );\n return result.rows[0];\n}\n\nexport async function listAnalyticsForms(\n siteId: string,\n userId: number,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const result = await db.query<AnalyticsFormRow>(\n `SELECT f.*, count(s.id)::int AS submission_count FROM analytics_forms f\n LEFT JOIN analytics_form_submissions s ON s.form_id = f.id\n WHERE f.site_id = $1 GROUP BY f.id ORDER BY f.created_at DESC, f.id DESC LIMIT 1000`,\n [siteId],\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"forms\", siteId, { start: \"all\", end: \"all\" }),\n );\n return { forms: paged.items, pageInfo: paged.pageInfo };\n}\n\nexport async function getPublicAnalyticsForm(\n publicId: string,\n): Promise<\n (AnalyticsFormRow & { owner_user_id: number; pixel_public_id: string }) | null\n> {\n const result = await getAnalyticsPool().query<\n AnalyticsFormRow & { owner_user_id: number; pixel_public_id: string }\n >(\n `SELECT f.*, s.owner_user_id, p.public_id AS pixel_public_id,\n (SELECT count(*)::int FROM analytics_form_submissions fs WHERE fs.form_id = f.id) AS submission_count\n FROM analytics_forms f JOIN analytics_sites s ON s.id = f.site_id JOIN analytics_pixels p ON p.id = f.pixel_id\n WHERE f.public_id = $1 AND f.status = 'published'`,\n [publicId],\n );\n return result.rows[0] ?? null;\n}\n\nexport async function recordAnalyticsFormSubmission(input: {\n form: AnalyticsFormRow;\n visitorId?: string;\n sessionId?: string;\n crmPersonRef?: string;\n crmDeliveryStatus?: \"pending\" | \"delivered\" | \"failed\";\n source?: string;\n medium?: string;\n campaign?: string;\n clickIds?: Record<string, string>;\n}): Promise<{ submissionId: string }> {\n const id = randomUUID();\n await getAnalyticsPool().query(\n `INSERT INTO analytics_form_submissions(id, form_id, site_id, pixel_id, visitor_id, session_id, crm_person_ref, source, medium, campaign, crm_delivery_status, click_ids)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,\n [\n id,\n input.form.id,\n input.form.site_id,\n input.form.pixel_id,\n input.visitorId ?? null,\n input.sessionId ?? null,\n input.crmPersonRef ?? null,\n input.source?.slice(0, 180) ?? null,\n input.medium?.slice(0, 180) ?? null,\n input.campaign?.slice(0, 240) ?? null,\n input.crmDeliveryStatus ?? \"pending\",\n JSON.stringify(sanitizeClickIds(input.clickIds)),\n ],\n );\n return { submissionId: id };\n}\n\nconst clickIdKeys = new Set([\n \"fbclid\",\n \"gclid\",\n \"gbraid\",\n \"wbraid\",\n \"ttclid\",\n \"rdt_cid\",\n \"msclkid\",\n]);\n\nexport function sanitizeClickIds(\n value: Record<string, string> | undefined,\n): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, raw] of Object.entries(value ?? {})) {\n if (!clickIdKeys.has(key) || typeof raw !== \"string\") continue;\n const normalized = raw.trim();\n if (normalized) output[key] = normalized.slice(0, 500);\n }\n return output;\n}\n\nfunction identityHmac(kind: string, value: string): string {\n return createHmac(\"sha256\", getSessionSecret())\n .update(`${kind}:${value.trim().toLowerCase()}`)\n .digest(\"hex\");\n}\n\nexport async function linkAnalyticsFormIdentity(input: {\n submissionId?: string;\n siteId: string;\n crmPersonRef: string;\n visitorId?: string;\n sessionId?: string;\n email?: string;\n phone?: string;\n clickIds?: Record<string, string>;\n}): Promise<{ personId: string; linkedSignals: number }> {\n const client = await getAnalyticsPool().connect();\n try {\n await client.query(\"BEGIN\");\n const person = await client.query<{ id: string }>(\n `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)\n ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,\n [randomUUID(), input.siteId, input.crmPersonRef],\n );\n const personId = person.rows[0].id;\n const signals: Array<{\n kind: string;\n value: string;\n evidence: string;\n confidence: number;\n }> = [];\n if (input.visitorId)\n signals.push({\n kind: \"visitor_id\",\n value: input.visitorId,\n evidence: \"first_party_form\",\n confidence: 0.98,\n });\n if (input.sessionId)\n signals.push({\n kind: \"session_id\",\n value: input.sessionId,\n evidence: \"first_party_form\",\n confidence: 0.95,\n });\n if (input.email)\n signals.push({\n kind: \"email\",\n value: input.email,\n evidence: \"submitted_email\",\n confidence: 1,\n });\n if (input.phone)\n signals.push({\n kind: \"phone\",\n value: input.phone,\n evidence: \"submitted_phone\",\n confidence: 0.99,\n });\n for (const [platform, clickId] of Object.entries(\n sanitizeClickIds(input.clickIds),\n )) {\n signals.push({\n kind: \"click_id\",\n value: `${platform}:${clickId}`,\n evidence: platform,\n confidence: 0.9,\n });\n }\n for (const signal of signals) {\n const node = await client.query<{ id: string }>(\n `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)\n ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,\n [\n randomUUID(),\n input.siteId,\n signal.kind,\n identityHmac(signal.kind, signal.value),\n ],\n );\n await client.query(\n `INSERT INTO analytics_identity_edges(id, site_id, person_id, identity_node_id, evidence_kind, confidence)\n VALUES ($1,$2,$3,$4,$5,$6)\n ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence)`,\n [\n randomUUID(),\n input.siteId,\n personId,\n node.rows[0].id,\n signal.evidence,\n signal.confidence,\n ],\n );\n }\n if (input.submissionId)\n await client.query(\n \"UPDATE analytics_form_submissions SET person_id = $1 WHERE id = $2 AND site_id = $3\",\n [personId, input.submissionId, input.siteId],\n );\n if (input.visitorId || input.sessionId) {\n await client.query(\n `UPDATE analytics_events SET person_id = $1 WHERE site_id = $2 AND person_id IS NULL\n AND (($3::text IS NOT NULL AND visitor_id = $3) OR ($4::text IS NOT NULL AND session_id = $4))`,\n [\n personId,\n input.siteId,\n input.visitorId ?? null,\n input.sessionId ?? null,\n ],\n );\n await client.query(\n `UPDATE analytics_conversions SET person_id = $1 WHERE site_id = $2 AND person_id IS NULL AND $3::text IS NOT NULL AND session_id = $3`,\n [personId, input.siteId, input.sessionId ?? null],\n );\n }\n await client.query(\"COMMIT\");\n return { personId, linkedSignals: signals.length };\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function listAnalyticsPeople(\n siteId: string,\n userId: number,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const result = await db.query(\n `SELECT p.id, p.first_seen_at, p.last_seen_at, true AS crm_linked,\n (SELECT count(*)::int FROM analytics_identity_edges e WHERE e.person_id = p.id) AS signal_count,\n (SELECT count(*)::int FROM analytics_form_submissions f WHERE f.person_id = p.id) AS form_submission_count,\n (SELECT count(*)::int FROM analytics_conversions c WHERE c.person_id = p.id) AS conversion_count,\n (SELECT COALESCE(sum(c.value_minor), 0)::bigint FROM analytics_conversions c WHERE c.person_id = p.id) AS revenue_minor\n FROM analytics_people p WHERE p.site_id = $1 ORDER BY p.last_seen_at DESC, p.id DESC LIMIT 1000`,\n [siteId],\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"people\", siteId, { start: \"all\", end: \"all\" }),\n );\n return { people: paged.items, pageInfo: paged.pageInfo };\n}\n\nexport async function getAnalyticsPersonJourney(\n siteId: string,\n personId: string,\n userId: number,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const exists = await db.query(\n \"SELECT 1 FROM analytics_people WHERE id = $1 AND site_id = $2\",\n [personId, siteId],\n );\n if (!exists.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_person_not_found\",\n \"Attributed person not found.\",\n 404,\n );\n const result = await db.query(\n `SELECT * FROM (\n SELECT e.occurred_at, 'event'::text AS kind, e.event_name AS label, e.source, e.medium, e.campaign, e.path, 0::bigint AS value_minor, NULL::text AS currency\n FROM analytics_events e WHERE e.site_id = $1 AND e.person_id = $2\n UNION ALL\n SELECT f.occurred_at, 'form_submission', af.name, f.source, f.medium, f.campaign, NULL, 0, NULL\n FROM analytics_form_submissions f JOIN analytics_forms af ON af.id = f.form_id WHERE f.site_id = $1 AND f.person_id = $2\n UNION ALL\n SELECT c.occurred_at, 'conversion', c.conversion_kind, NULL, NULL, NULL, NULL, c.value_minor, c.currency\n FROM analytics_conversions c WHERE c.site_id = $1 AND c.person_id = $2\n ) journey ORDER BY occurred_at DESC LIMIT 1000`,\n [siteId, personId],\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"person_journey\", siteId, {\n start: personId,\n end: personId,\n }),\n );\n return { personId, touchpoints: paged.items, pageInfo: paged.pageInfo };\n}\n\nexport interface AnalyticsCrmImportRow {\n crmPersonRef: string;\n payloadCiphertext: string;\n}\n\nexport async function createAnalyticsCrmImport(input: {\n siteId: string;\n userId: number;\n sourceSystem: string;\n filename: string;\n rows: AnalyticsCrmImportRow[];\n rejectedCount: number;\n}): Promise<{\n id: string;\n status: string;\n acceptedCount: number;\n rejectedCount: number;\n}> {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const client = await db.connect();\n const id = randomUUID();\n try {\n await client.query(\"BEGIN\");\n await client.query(\n `INSERT INTO analytics_crm_imports(id, site_id, requested_by_user_id, source_system, filename, accepted_count, rejected_count)\n VALUES ($1,$2,$3,$4,$5,$6,$7)`,\n [\n id,\n input.siteId,\n input.userId,\n input.sourceSystem.slice(0, 80),\n input.filename.slice(0, 240),\n input.rows.length,\n input.rejectedCount,\n ],\n );\n for (const row of input.rows) {\n await client.query(\n `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)\n VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,\n [randomUUID(), id, row.crmPersonRef, row.payloadCiphertext],\n );\n }\n await client.query(\"COMMIT\");\n return {\n id,\n status: \"queued\",\n acceptedCount: input.rows.length,\n rejectedCount: input.rejectedCount,\n };\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function listAnalyticsCrmImports(\n siteId: string,\n userId: number,\n page?: AnalyticsPageInput,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const result = await db.query(\n `SELECT id, source_system, filename, status, accepted_count, rejected_count, created_at, completed_at\n FROM analytics_crm_imports WHERE site_id = $1 ORDER BY created_at DESC, id DESC LIMIT 1000`,\n [siteId],\n );\n const paged = paginateRows(\n result.rows,\n page,\n pageFingerprint(\"crm_imports\", siteId, { start: \"all\", end: \"all\" }),\n );\n return { imports: paged.items, pageInfo: paged.pageInfo };\n}\n\nexport async function createAnalyticsActivationDestination(input: {\n siteId: string;\n userId: number;\n platform: \"meta\" | \"google\" | \"tiktok\" | \"reddit\";\n name: string;\n connectionRef?: string;\n externalDatasetId?: string;\n eventMapping?: Record<string, string>;\n}) {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const result = await db.query(\n `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)\n VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,\n [\n randomUUID(),\n input.siteId,\n input.platform,\n input.name.trim(),\n input.connectionRef?.trim() || null,\n input.externalDatasetId?.trim() || null,\n JSON.stringify(input.eventMapping ?? {}),\n input.userId,\n ],\n );\n return result.rows[0];\n}\n\nexport async function listAnalyticsActivationDestinations(\n siteId: string,\n userId: number,\n) {\n const db = getAnalyticsPool();\n await accessFor(db, siteId, userId);\n const result = await db.query(\n `SELECT d.*, count(j.id)::int AS delivery_count,\n count(j.id) FILTER (WHERE j.status = 'pending')::int AS pending_count,\n count(j.id) FILTER (WHERE j.status = 'delivered')::int AS delivered_count,\n count(j.id) FILTER (WHERE j.status = 'failed')::int AS failed_count\n FROM analytics_activation_destinations d LEFT JOIN analytics_activation_jobs j ON j.destination_id = d.id\n WHERE d.site_id = $1 AND d.status <> 'archived' GROUP BY d.id ORDER BY d.created_at DESC`,\n [siteId],\n );\n return result.rows;\n}\n\nexport async function archiveAnalyticsActivationDestination(input: {\n siteId: string;\n destinationId: string;\n userId: number;\n}) {\n const db = getAnalyticsPool();\n await requireEditor(db, input.siteId, input.userId);\n const result = await db.query(\n `UPDATE analytics_activation_destinations SET status = 'archived', updated_at = now() WHERE id = $1 AND site_id = $2`,\n [input.destinationId, input.siteId],\n );\n if (!result.rowCount)\n throw new AnalyticsRepositoryError(\n \"analytics_activation_destination_not_found\",\n \"Activation destination not found.\",\n 404,\n );\n}\n\nexport async function queueAnalyticsActivation(input: {\n siteId: string;\n conversionId: string;\n personId?: string;\n payloadCiphertext: string;\n}): Promise<number> {\n const db = getAnalyticsPool();\n const destinations = await db.query<{ id: string }>(\n `SELECT id FROM analytics_activation_destinations WHERE site_id = $1 AND status = 'active'`,\n [input.siteId],\n );\n let queued = 0;\n for (const destination of destinations.rows) {\n const result = await db.query(\n `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)\n VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,\n [\n randomUUID(),\n destination.id,\n input.conversionId,\n input.personId ?? null,\n input.payloadCiphertext,\n ],\n );\n queued += result.rowCount ?? 0;\n }\n return queued;\n}\n\nexport async function queueAnalyticsFormDelivery(input: {\n submissionId: string;\n ownerUserId: number;\n payloadCiphertext: string;\n errorCode?: string;\n}): Promise<string> {\n const id = randomUUID();\n await getAnalyticsPool().query(\n `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)\n VALUES ($1,$2,$3,$4,$5)`,\n [\n id,\n input.submissionId,\n input.ownerUserId,\n input.payloadCiphertext,\n input.errorCode ?? null,\n ],\n );\n return id;\n}\n\nexport interface AnalyticsFormDeliveryJob {\n id: string;\n submissionId: string;\n ownerUserId: number;\n payloadCiphertext: string;\n attempts: number;\n}\n\nexport async function claimAnalyticsFormDeliveryJobs(\n limit = 10,\n): Promise<AnalyticsFormDeliveryJob[]> {\n const result = await getAnalyticsPool().query<{\n id: string;\n submission_id: string;\n owner_user_id: string;\n payload_ciphertext: string;\n attempts: number;\n }>(\n `WITH due AS (\n SELECT id FROM analytics_form_delivery_jobs\n WHERE status = 'pending' AND next_attempt_at <= now() AND expires_at > now()\n ORDER BY next_attempt_at, created_at\n FOR UPDATE SKIP LOCKED LIMIT $1\n )\n UPDATE analytics_form_delivery_jobs j\n SET attempts = j.attempts + 1,\n next_attempt_at = now() + interval '5 minutes'\n FROM due WHERE j.id = due.id\n RETURNING j.id, j.submission_id, j.owner_user_id, j.payload_ciphertext, j.attempts`,\n [Math.min(Math.max(limit, 1), 50)],\n );\n return result.rows.map((row) => ({\n id: row.id,\n submissionId: row.submission_id,\n ownerUserId: Number(row.owner_user_id),\n payloadCiphertext: row.payload_ciphertext,\n attempts: row.attempts,\n }));\n}\n\nexport async function completeAnalyticsFormDelivery(\n jobId: string,\n submissionId: string,\n): Promise<void> {\n const db = getAnalyticsPool();\n const client = await db.connect();\n try {\n await client.query(\"BEGIN\");\n await client.query(\n `UPDATE analytics_form_delivery_jobs SET status='delivered', delivered_at=now(), last_error_code=NULL WHERE id=$1`,\n [jobId],\n );\n await client.query(\n `UPDATE analytics_form_submissions SET crm_delivery_status='delivered' WHERE id=$1`,\n [submissionId],\n );\n await client.query(\"COMMIT\");\n } catch (error) {\n await client.query(\"ROLLBACK\");\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function deferAnalyticsFormDelivery(\n jobId: string,\n attempts: number,\n errorCode: string,\n): Promise<void> {\n const terminal = attempts >= 8;\n const delayMinutes = Math.min(5 * 2 ** Math.max(0, attempts - 1), 360);\n await getAnalyticsPool().query(\n `UPDATE analytics_form_delivery_jobs\n SET status = CASE WHEN expires_at <= now() THEN 'expired' WHEN $2 THEN 'failed' ELSE 'pending' END,\n last_error_code = $3,\n next_attempt_at = now() + ($4::text || ' minutes')::interval\n WHERE id = $1`,\n [jobId, terminal, errorCode.slice(0, 80), String(delayMinutes)],\n );\n}\n\nexport async function isAnalyticsFormPlacementApproved(\n pixelId: string,\n placementUrl: string | undefined,\n): Promise<boolean> {\n if (!placementUrl) return false;\n let hostname: string | null = null;\n try {\n hostname = new URL(placementUrl).hostname.toLowerCase();\n } catch {\n return false;\n }\n const result = await getAnalyticsPool().query(\n `SELECT 1 FROM analytics_pixel_domains WHERE pixel_id = $1 AND hostname = $2 AND state = 'approved'`,\n [pixelId, hostname],\n );\n return Boolean(result.rowCount);\n}\n\nexport async function analyticsHealth(siteId: string, userId: number) {\n const db = getAnalyticsPool();\n const pixels = await listAnalyticsPixels(siteId, userId);\n const lastEventAt =\n pixels\n .map((pixel) => pixel.last_event_at)\n .filter(Boolean)\n .sort()\n .at(-1) ?? null;\n const [conversion, rollup, receipts] = await Promise.all([\n db.query<{ last_conversion_at: string | null }>(\n `SELECT max(occurred_at)::text AS last_conversion_at FROM analytics_conversions WHERE site_id = $1`,\n [siteId],\n ),\n db.query<{ last_rollup_at: string | null }>(\n `SELECT max(refreshed_at)::text AS last_rollup_at FROM analytics_rollups_daily WHERE site_id = $1`,\n [siteId],\n ),\n db.query<{ accepted: number; rejected: number }>(\n `SELECT COALESCE(sum(accepted_count), 0)::int AS accepted,\n COALESCE(sum(rejected_count), 0)::int AS rejected\n FROM analytics_ingestion_receipts WHERE site_id = $1 AND created_at >= now() - interval '24 hours'`,\n [siteId],\n ),\n ]);\n const recent = receipts.rows[0];\n const totalRecent = recent.accepted + recent.rejected;\n const rejectionRate = totalRecent ? recent.rejected / totalRecent : 0;\n const stale = Boolean(\n lastEventAt && Date.now() - Date.parse(lastEventAt) > 48 * 60 * 60 * 1000,\n );\n const hasMismatch = pixels.some((pixel) =>\n pixel.domains.some((domain) => domain.state === \"observed_unverified\"),\n );\n return {\n status:\n pixels.length === 0\n ? \"not_installed\"\n : hasMismatch && !lastEventAt\n ? \"domain_mismatch\"\n : rejectionRate > 0.25\n ? \"rejecting\"\n : stale\n ? \"stale\"\n : lastEventAt\n ? \"healthy\"\n : \"waiting\",\n pixels,\n lastEventAt,\n lastConversionAt: conversion.rows[0]?.last_conversion_at ?? null,\n lastRollupAt: rollup.rows[0]?.last_rollup_at ?? null,\n acceptedLast24Hours: recent.accepted,\n rejectedLast24Hours: recent.rejected,\n rejectionRate,\n checkedAt: new Date().toISOString(),\n };\n}\n\nexport async function refreshAnalyticsDailyRollups(input: {\n start: string;\n end: string;\n}): Promise<{ runId: string; rows: number }> {\n const db = getAnalyticsPool();\n const runId = randomUUID();\n await db.query(\n `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,\n [runId, input.start, input.end],\n );\n const client = await db.connect();\n try {\n await client.query(\"BEGIN\");\n await client.query(\n `DELETE FROM analytics_rollups_daily WHERE bucket >= $1::date AND bucket < $2::date`,\n [input.start, input.end],\n );\n const inserted = await client.query(\n `INSERT INTO analytics_rollups_daily(\n site_id, pixel_id, hostname, bucket, source, medium, campaign,\n pageviews, events, visitors, sessions, refreshed_at\n )\n SELECT site_id, pixel_id, hostname, occurred_at::date,\n COALESCE(source, '(direct)'), COALESCE(medium, '(none)'), COALESCE(campaign, '(none)'),\n count(*) FILTER (WHERE event_name = 'page_view')::int,\n count(*)::int,\n count(DISTINCT visitor_id)::int,\n count(DISTINCT session_id)::int,\n now()\n FROM analytics_events\n WHERE occurred_at >= $1::timestamptz AND occurred_at < $2::timestamptz\n GROUP BY 1, 2, 3, 4, 5, 6, 7`,\n [input.start, input.end],\n );\n await client.query(\"COMMIT\");\n const rows = inserted.rowCount ?? 0;\n await db.query(\n `UPDATE analytics_rollup_runs SET status = 'complete', row_count = $2, completed_at = now() WHERE id = $1`,\n [runId, rows],\n );\n return { runId, rows };\n } catch (error) {\n await client.query(\"ROLLBACK\");\n await db.query(\n `UPDATE analytics_rollup_runs SET status = 'failed', completed_at = now(), error_code = 'rollup_failed' WHERE id = $1`,\n [runId],\n );\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function refreshAnalyticsDailyRollupsIfDue(\n now = new Date(),\n): Promise<\n | { status: \"complete\"; runId: string; rows: number }\n | { status: \"not_due\" | \"already_running\"; rows: 0 }\n> {\n const db = getAnalyticsPool();\n const lockClient = await db.connect();\n const advisoryLockId = 2_026_080_501;\n let locked = false;\n try {\n const lock = await lockClient.query<{ acquired: boolean }>(\n \"SELECT pg_try_advisory_lock($1) AS acquired\",\n [advisoryLockId],\n );\n locked = Boolean(lock.rows[0]?.acquired);\n if (!locked) return { status: \"already_running\", rows: 0 };\n const recent = await lockClient.query<{ completed_at: string }>(\n `SELECT completed_at FROM analytics_rollup_runs\n WHERE status = 'complete' AND completed_at > $1::timestamptz\n ORDER BY completed_at DESC LIMIT 1`,\n [new Date(now.getTime() - 55 * 60_000).toISOString()],\n );\n if (recent.rows[0]) return { status: \"not_due\", rows: 0 };\n const end = new Date(now);\n const start = new Date(end.getTime() - 3 * 24 * 60 * 60 * 1000);\n const result = await refreshAnalyticsDailyRollups({\n start: start.toISOString(),\n end: end.toISOString(),\n });\n return { status: \"complete\", ...result };\n } finally {\n if (locked)\n await lockClient\n .query(\"SELECT pg_advisory_unlock($1)\", [advisoryLockId])\n .catch(() => undefined);\n lockClient.release();\n }\n}\n\nexport type AnalyticsExportReport =\n \"overview\" | \"acquisition\" | \"content\" | \"conversions\";\nexport type AnalyticsExportFormat = \"csv\" | \"json\" | \"markdown\";\ntype AnalyticsExportArtifact = {\n id: string;\n report: AnalyticsExportReport;\n format: AnalyticsExportFormat;\n mediaType: string;\n filename: string;\n content: string;\n};\n\nfunction csvCell(value: unknown): string {\n const text = value == null ? \"\" : String(value);\n return /[\\n\\r,\\\"]/.test(text) ? `\"${text.replaceAll('\"', '\"\"')}\"` : text;\n}\n\nfunction rowsToCsv(rows: Array<Record<string, unknown>>): string {\n const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];\n if (!columns.length) return \"\";\n return [\n columns.join(\",\"),\n ...rows.map((row) =>\n columns.map((column) => csvCell(row[column])).join(\",\"),\n ),\n ].join(\"\\n\");\n}\n\nexport async function createAnalyticsExport(input: {\n siteId: string;\n userId: number;\n report: AnalyticsExportReport;\n format: AnalyticsExportFormat;\n filters: AnalyticsFilters;\n idempotencyKey?: string;\n}): Promise<AnalyticsExportArtifact> {\n const site = (await listAnalyticsSites(input.userId)).find(\n (candidate) => candidate.id === input.siteId,\n );\n if (!site)\n throw new AnalyticsRepositoryError(\n \"analytics_site_not_found\",\n \"Site not found.\",\n 404,\n );\n const describeArtifact = (\n artifact: Omit<AnalyticsExportArtifact, \"mediaType\" | \"filename\">,\n ): AnalyticsExportArtifact => {\n const extension = artifact.format === \"markdown\" ? \"md\" : artifact.format;\n return {\n ...artifact,\n mediaType:\n artifact.format === \"csv\"\n ? \"text/csv\"\n : artifact.format === \"json\"\n ? \"application/json\"\n : \"text/markdown\",\n filename: `${site.slug}-${artifact.report}.${extension}`,\n };\n };\n if (input.idempotencyKey) {\n const existing = await getAnalyticsPool().query<{\n id: string;\n report: AnalyticsExportReport;\n format: AnalyticsExportFormat;\n content: string;\n }>(\n `SELECT id, report, format, content FROM analytics_exports\n WHERE requested_by_user_id = $1 AND idempotency_key = $2`,\n [input.userId, input.idempotencyKey],\n );\n const artifact = existing.rows[0];\n if (artifact) return describeArtifact(artifact);\n }\n const data =\n input.report === \"overview\"\n ? await analyticsOverview(input.siteId, input.userId, input.filters)\n : input.report === \"acquisition\"\n ? await analyticsAcquisition(input.siteId, input.userId, input.filters)\n : input.report === \"content\"\n ? await analyticsContent(input.siteId, input.userId, input.filters)\n : await analyticsConversions(\n input.siteId,\n input.userId,\n input.filters,\n );\n const rows =\n input.report === \"overview\"\n ? [data as Record<string, unknown>]\n : input.report === \"acquisition\"\n ? (data as { channels: Array<Record<string, unknown>> }).channels\n : input.report === \"content\"\n ? (data as { content: Array<Record<string, unknown>> }).content\n : (data as { conversions: Array<Record<string, unknown>> })\n .conversions;\n let content: string;\n if (input.format === \"json\") {\n content = JSON.stringify(\n {\n version: 1,\n site: { id: site.id, name: site.name },\n report: input.report,\n filters: input.filters,\n data,\n },\n null,\n 2,\n );\n } else if (input.format === \"csv\") {\n content = rowsToCsv(rows);\n } else {\n const overview = await analyticsOverview(\n input.siteId,\n input.userId,\n input.filters,\n );\n content = [\n `# ${site.name} Site Analytics`,\n \"\",\n `Report: ${input.report}`,\n `Window: ${input.filters.start} to ${input.filters.end}`,\n `Fresh through: ${overview.dataFreshThrough ?? \"No accepted events\"}`,\n \"\",\n `- Visitors: ${overview.visitors}`,\n `- Sessions: ${overview.sessions}`,\n `- Pageviews: ${overview.pageviews}`,\n `- Verified conversions: ${overview.conversions}`,\n `- Verified revenue: ${(overview.revenueMinor / 100).toFixed(2)}`,\n \"\",\n `Generated ${new Date().toISOString()} from the governed ${input.report} report contract.`,\n ].join(\"\\n\");\n }\n const id = randomUUID();\n const inserted = await getAnalyticsPool().query<{\n id: string;\n report: AnalyticsExportReport;\n format: AnalyticsExportFormat;\n content: string;\n }>(\n `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)\n ON CONFLICT (requested_by_user_id, idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING\n RETURNING id, report, format, content`,\n [\n id,\n input.siteId,\n input.userId,\n input.idempotencyKey ?? null,\n input.report,\n input.format,\n JSON.stringify(input.filters),\n content,\n ],\n );\n const artifact = inserted.rows[0];\n if (artifact) return describeArtifact(artifact);\n const replay = await getAnalyticsPool().query<{\n id: string;\n report: AnalyticsExportReport;\n format: AnalyticsExportFormat;\n content: string;\n }>(\n `SELECT id, report, format, content FROM analytics_exports\n WHERE requested_by_user_id = $1 AND idempotency_key = $2`,\n [input.userId, input.idempotencyKey],\n );\n if (!replay.rows[0]) {\n throw new AnalyticsRepositoryError(\n \"analytics_export_conflict\",\n \"The export could not be created or replayed.\",\n 409,\n );\n }\n return describeArtifact(replay.rows[0]);\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto'\n\nconst isProduction = () => process.env.NODE_ENV === 'production' || process.env.VERCEL === '1'\n\nexport function getSessionSecret(): string {\n const configured = process.env.SESSION_SECRET?.trim()\n if (configured) return configured\n if (isProduction()) throw new Error('SESSION_SECRET is not set — add it to your Vercel env vars (see src/api/env.ts)')\n return 'dev-secret-change-me'\n}\n\nconst secret = () => getSessionSecret()\n\nfunction safeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n try {\n return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'))\n } catch {\n return false\n }\n}\n\nexport function signSession(userId: number | bigint): string {\n const payload = String(userId)\n const sig = createHmac('sha256', secret()).update(payload).digest('hex')\n return `${payload}.${sig}`\n}\n\nexport function verifySession(token: string): number | null {\n const dot = token.lastIndexOf('.')\n if (dot === -1) return null\n const payload = token.slice(0, dot)\n const sig = token.slice(dot + 1)\n const expected = createHmac('sha256', secret()).update(payload).digest('hex')\n if (!safeEqualHex(sig, expected)) return null\n const id = parseInt(payload)\n return isNaN(id) ? null : id\n}\n\nexport function sessionCookie(token: string): string {\n const secure = isProduction() ? '; Secure' : ''\n return `session=${token}; HttpOnly${secure}; Path=/; SameSite=Strict; Max-Age=${60 * 60 * 24 * 30}`\n}\n\nexport function clearCookie(): string {\n return 'session=; HttpOnly; Path=/; Max-Age=0'\n}\n","export type AnalyticsChannelFamily = 'llm' | 'social' | 'review' | 'search' | 'email' | 'referral' | 'direct' | 'other'\n\nexport interface ResolvedAnalyticsAttribution {\n channelFamily: AnalyticsChannelFamily\n platform: string\n source: string\n medium: string\n campaign: string | null\n campaignId: string | null\n adSetId: string | null\n adId: string | null\n creativeId: string | null\n placement: string | null\n term: string | null\n content: string | null\n}\n\nconst aliases: Array<[RegExp, AnalyticsChannelFamily, string]> = [\n [/perplexity/i, 'llm', 'Perplexity'], [/chatgpt|chat\\.openai|openai/i, 'llm', 'ChatGPT'],\n [/claude|anthropic/i, 'llm', 'Claude'], [/\\bgrok\\b|xai/i, 'llm', 'Grok'],\n [/ai[ _-]?overview|google[ _-]?ai|\\bsge\\b|gemini/i, 'llm', 'Google AI Overview'],\n [/instagram/i, 'social', 'Instagram'], [/facebook|\\bfb\\b|meta/i, 'social', 'Facebook'],\n [/linkedin/i, 'social', 'LinkedIn'], [/tiktok/i, 'social', 'TikTok'],\n [/youtube|youtu\\.be/i, 'social', 'YouTube'], [/twitter|x\\.com/i, 'social', 'X'],\n [/\\bg2\\b|g2\\.com/i, 'review', 'G2'], [/trustpilot/i, 'review', 'Trustpilot'],\n [/\\bbbb\\b|better business bureau/i, 'review', 'BBB'], [/capterra/i, 'review', 'Capterra'], [/yelp/i, 'review', 'Yelp'],\n [/google|bing|duckduckgo|yahoo/i, 'search', 'Search'],\n]\n\nfunction clean(value: unknown, max = 240): string | null {\n return typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : null\n}\nfunction referrerHost(referrer?: string): string {\n try { return referrer ? new URL(referrer).hostname.replace(/^www\\./, '') : '' } catch { return '' }\n}\n\nexport function resolveAnalyticsAttribution(input: {\n source?: string; medium?: string; campaign?: string; referrer?: string\n clickIds?: Record<string, string>; properties?: Record<string, unknown>\n}): ResolvedAnalyticsAttribution {\n const properties = input.properties ?? {}\n const source = clean(input.source, 180) || referrerHost(input.referrer) || '(direct)'\n const medium = clean(input.medium, 180) || (source === '(direct)' ? '(none)' : 'referral')\n const clickIds = input.clickIds ?? {}\n const haystack = `${source} ${medium} ${referrerHost(input.referrer)}`\n let channelFamily: AnalyticsChannelFamily = source === '(direct)' ? 'direct' : /email|newsletter/i.test(medium) ? 'email' : 'referral'\n let platform = source === '(direct)' ? 'Direct' : source\n for (const [pattern, family, label] of aliases) if (pattern.test(haystack)) { channelFamily = family; platform = label; break }\n if (clickIds.fbclid) { channelFamily = 'social'; platform = /instagram/i.test(haystack) ? 'Instagram' : 'Facebook' }\n else if (clickIds.ttclid) { channelFamily = 'social'; platform = 'TikTok' }\n else if (clickIds.rdt_cid) { channelFamily = 'social'; platform = 'Reddit' }\n else if (clickIds.gclid || clickIds.gbraid || clickIds.wbraid) { channelFamily = 'search'; platform = 'Google' }\n else if (clickIds.msclkid) { channelFamily = 'search'; platform = 'Microsoft Ads' }\n return {\n channelFamily, platform, source, medium, campaign: clean(input.campaign),\n campaignId: clean(properties.campaign_id ?? properties.utm_id),\n adSetId: clean(properties.adset_id ?? properties.ad_set_id ?? properties.adgroup_id ?? properties.ad_group_id),\n adId: clean(properties.ad_id), creativeId: clean(properties.creative_id), placement: clean(properties.placement),\n term: clean(properties.utm_term), content: clean(properties.utm_content),\n }\n}\n"],"mappings":";AAAA,SAAS,YAAY,cAAAA,aAAY,aAAa,kBAAkB;AAChE,SAAS,YAAkD;;;ACD3D,SAAS,YAAY,uBAAuB;AAE5C,IAAM,eAAe,MAAM,QAAQ,IAAI,aAAa,gBAAgB,QAAQ,IAAI,WAAW;AAEpF,SAAS,mBAA2B;AACzC,QAAM,aAAa,QAAQ,IAAI,gBAAgB,KAAK;AACpD,MAAI,WAAY,QAAO;AACvB,MAAI,aAAa,EAAG,OAAM,IAAI,MAAM,sFAAiF;AACrH,SAAO;AACT;AAEA,IAAM,SAAS,MAAM,iBAAiB;AAEtC,SAAS,aAAa,GAAW,GAAoB;AACnD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,MAAI;AACF,WAAO,gBAAgB,OAAO,KAAK,GAAG,KAAK,GAAG,OAAO,KAAK,GAAG,KAAK,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,YAAY,QAAiC;AAC3D,QAAM,UAAU,OAAO,MAAM;AAC7B,QAAM,MAAM,WAAW,UAAU,OAAO,CAAC,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AACvE,SAAO,GAAG,OAAO,IAAI,GAAG;AAC1B;AAEO,SAAS,cAAc,OAA8B;AAC1D,QAAM,MAAM,MAAM,YAAY,GAAG;AACjC,MAAI,QAAQ,GAAI,QAAO;AACvB,QAAM,UAAU,MAAM,MAAM,GAAG,GAAG;AAClC,QAAM,MAAM,MAAM,MAAM,MAAM,CAAC;AAC/B,QAAM,WAAW,WAAW,UAAU,OAAO,CAAC,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC5E,MAAI,CAAC,aAAa,KAAK,QAAQ,EAAG,QAAO;AACzC,QAAM,KAAK,SAAS,OAAO;AAC3B,SAAO,MAAM,EAAE,IAAI,OAAO;AAC5B;;;ACpBA,IAAM,UAA2D;AAAA,EAC/D,CAAC,eAAe,OAAO,YAAY;AAAA,EAAG,CAAC,gCAAgC,OAAO,SAAS;AAAA,EACvF,CAAC,qBAAqB,OAAO,QAAQ;AAAA,EAAG,CAAC,iBAAiB,OAAO,MAAM;AAAA,EACvE,CAAC,mDAAmD,OAAO,oBAAoB;AAAA,EAC/E,CAAC,cAAc,UAAU,WAAW;AAAA,EAAG,CAAC,yBAAyB,UAAU,UAAU;AAAA,EACrF,CAAC,aAAa,UAAU,UAAU;AAAA,EAAG,CAAC,WAAW,UAAU,QAAQ;AAAA,EACnE,CAAC,sBAAsB,UAAU,SAAS;AAAA,EAAG,CAAC,mBAAmB,UAAU,GAAG;AAAA,EAC9E,CAAC,mBAAmB,UAAU,IAAI;AAAA,EAAG,CAAC,eAAe,UAAU,YAAY;AAAA,EAC3E,CAAC,mCAAmC,UAAU,KAAK;AAAA,EAAG,CAAC,aAAa,UAAU,UAAU;AAAA,EAAG,CAAC,SAAS,UAAU,MAAM;AAAA,EACrH,CAAC,iCAAiC,UAAU,QAAQ;AACtD;AAEA,SAAS,MAAM,OAAgB,MAAM,KAAoB;AACvD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,EAAE,MAAM,GAAG,GAAG,IAAI;AAClF;AACA,SAAS,aAAa,UAA2B;AAC/C,MAAI;AAAE,WAAO,WAAW,IAAI,IAAI,QAAQ,EAAE,SAAS,QAAQ,UAAU,EAAE,IAAI;AAAA,EAAG,QAAQ;AAAE,WAAO;AAAA,EAAG;AACpG;AAEO,SAAS,4BAA4B,OAGX;AAC/B,QAAM,aAAa,MAAM,cAAc,CAAC;AACxC,QAAM,SAAS,MAAM,MAAM,QAAQ,GAAG,KAAK,aAAa,MAAM,QAAQ,KAAK;AAC3E,QAAM,SAAS,MAAM,MAAM,QAAQ,GAAG,MAAM,WAAW,aAAa,WAAW;AAC/E,QAAM,WAAW,MAAM,YAAY,CAAC;AACpC,QAAM,WAAW,GAAG,MAAM,IAAI,MAAM,IAAI,aAAa,MAAM,QAAQ,CAAC;AACpE,MAAI,gBAAwC,WAAW,aAAa,WAAW,oBAAoB,KAAK,MAAM,IAAI,UAAU;AAC5H,MAAI,WAAW,WAAW,aAAa,WAAW;AAClD,aAAW,CAAC,SAAS,QAAQ,KAAK,KAAK,QAAS,KAAI,QAAQ,KAAK,QAAQ,GAAG;AAAE,oBAAgB;AAAQ,eAAW;AAAO;AAAA,EAAM;AAC9H,MAAI,SAAS,QAAQ;AAAE,oBAAgB;AAAU,eAAW,aAAa,KAAK,QAAQ,IAAI,cAAc;AAAA,EAAW,WAC1G,SAAS,QAAQ;AAAE,oBAAgB;AAAU,eAAW;AAAA,EAAS,WACjE,SAAS,SAAS;AAAE,oBAAgB;AAAU,eAAW;AAAA,EAAS,WAClE,SAAS,SAAS,SAAS,UAAU,SAAS,QAAQ;AAAE,oBAAgB;AAAU,eAAW;AAAA,EAAS,WACtG,SAAS,SAAS;AAAE,oBAAgB;AAAU,eAAW;AAAA,EAAgB;AAClF,SAAO;AAAA,IACL;AAAA,IAAe;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAQ,UAAU,MAAM,MAAM,QAAQ;AAAA,IACvE,YAAY,MAAM,WAAW,eAAe,WAAW,MAAM;AAAA,IAC7D,SAAS,MAAM,WAAW,YAAY,WAAW,aAAa,WAAW,cAAc,WAAW,WAAW;AAAA,IAC7G,MAAM,MAAM,WAAW,KAAK;AAAA,IAAG,YAAY,MAAM,WAAW,WAAW;AAAA,IAAG,WAAW,MAAM,WAAW,SAAS;AAAA,IAC/G,MAAM,MAAM,WAAW,QAAQ;AAAA,IAAG,SAAS,MAAM,WAAW,WAAW;AAAA,EACzE;AACF;;;AFlDO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,YACkB,MAChB,SACgB,SAAS,KACzB;AACA,UAAM,OAAO;AAJG;AAEA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EANkB;AAAA,EAEA;AAKpB;AAEA,IAAI,OAAoB;AAExB,SAAS,cAAsB;AAC7B,QAAM,QAAQ,QAAQ,IAAI,wBAAwB,KAAK;AACvD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,mBAAyB;AACvC,MAAI,CAAC,KAAM,QAAO,IAAI,KAAK,EAAE,kBAAkB,YAAY,GAAG,KAAK,EAAE,CAAC;AACtE,SAAO;AACT;AAEA,eAAsB,qBAAoC;AACxD,MAAI,CAAC,KAAM;AACX,QAAM,UAAU;AAChB,SAAO;AACP,QAAM,QAAQ,IAAI;AACpB;AAEA,eAAsB,mBAAkC;AACtD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,QAAM,OAAO,MAAM,OAAO;AAC1B,MAAI;AACF,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,KAKlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAclB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAwBlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAclB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAgBlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAWlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAqBlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAOlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAqBlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAclB;AACD,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAclB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KASlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAUlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAalB;AACD,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYlB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAalB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAalB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAclB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAelB;AACD,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAmBlB;AACD,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQlB;AACD,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AAAA,oGAC6E;AAChG,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA,KAIlB;AACD,UAAM,OAAO,MAAM,QAAQ;AAAA,EAC7B,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,SAAS,cAAc,OAAuB;AAC5C,QAAM,OAAO,MACV,KAAK,EACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE;AACd,SAAO,QAAQ,QAAQ,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AACvD;AAEO,SAAS,0BACd,QACe;AACf,MAAI,CAAC,UAAU,WAAW,OAAQ,QAAO;AACzC,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,SAAU,QAAO;AAClE,WAAO,IAAI,SAAS,YAAY,EAAE,QAAQ,OAAO,EAAE;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAwB;AAC/B,SAAO,MAAM,YAAY,EAAE,EAAE,SAAS,WAAW,CAAC;AACpD;AAEA,SAAS,QAAW,MAA6B;AAC/C,SAAO;AACT;AAEA,eAAe,UACb,QACA,QACA,QACwB;AACxB,QAAM,SAAS,MAAM,OAAO;AAAA,IAC1B;AAAA,IACA,CAAC,QAAQ,MAAM;AAAA,EACjB;AACA,QAAM,OAAO,OAAO,KAAK,CAAC,GAAG;AAC7B,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,SAAO;AACT;AAEA,eAAe,cACb,QACA,QACA,QACwB;AACxB,QAAM,OAAO,MAAM,UAAU,QAAQ,QAAQ,MAAM;AACnD,MAAI,SAAS;AACX,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,SAAO;AACT;AAcA,eAAsB,oBAAoB,OAKZ;AAC5B,QAAM,KAAK,iBAAiB;AAC5B,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,QAAM,KAAK,WAAW;AACtB,QAAM,WAAW,cAAc,MAAM,QAAQ,MAAM,IAAI;AACvD,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,QAAI,OAAO;AACX,aAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,UAAI;AACF,cAAM,OAAO;AAAA,UACX;AAAA;AAAA,UAEA,CAAC,IAAI,MAAM,MAAM,KAAK,KAAK,GAAG,MAAM,QAAQ,MAAM,YAAY,KAAK;AAAA,QACrE;AACA;AAAA,MACF,SAAS,OAAO;AACd,cAAM,OAAQ,MAA4B;AAC1C,YAAI,SAAS,WAAW,YAAY,EAAG,OAAM;AAC7C,eAAO,GAAG,QAAQ,IAAI,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AAAA,MACtD;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA,MACA,CAAC,IAAI,MAAM,MAAM;AAAA,IACnB;AACA,UAAM,OAAO,MAAM,QAAQ;AAC3B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,MAAM,KAAK,KAAK;AAAA,MACtB,UAAU,MAAM,YAAY;AAAA,MAC5B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,MACb,eAAe;AAAA,MACf,gBAAgB;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,eAAsB,mBACpB,QAC6B;AAC7B,QAAM,SAAS,MAAM,iBAAiB,EAAE;AAAA,IACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,CAAC,MAAM;AAAA,EACT;AACA,SAAO,QAA0B,OAAO,IAAI;AAC9C;AAEA,eAAsB,6BAA6B,OAIhD;AACD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,GAAG;AAAA,IACP;AAAA,IACA,CAAC,MAAM,eAAe,MAAM,MAAM;AAAA,EACpC;AACF;AAEA,eAAsB,uBAAuB,OAW1C;AACD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,aAAW,OAAO,MAAM,MAAM;AAC5B,UAAM,GAAG;AAAA,MACP;AAAA;AAAA;AAAA,MAGA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI,SAAS,MAAM,GAAG,EAAE;AAAA,QACxB,IAAI,UAAU,MAAM,GAAG,GAAG,KAAK;AAAA,QAC/B,IAAI;AAAA,SACH,IAAI,YAAY,OAAO,YAAY;AAAA,QACpC,IAAI,WAAW,MAAM,GAAG,GAAG,KAAK;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,UAAU,MAAM,KAAK,OAAO;AACvC;AAEA,eAAsB,yBACpB,QACA,QACA,OACA,KACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,OAAO,MAAM,GAAG,MAEnB,0DAA0D,CAAC,MAAM,CAAC;AACrE,QAAM,SAAS,MAAM,GAAG;AAAA,IAWtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,CAAC,QAAQ,OAAO,GAAG;AAAA,EACrB;AACA,QAAM,QAAQ,MAAM,GAAG;AAAA,IACrB;AAAA,IACA,CAAC,QAAQ,OAAO,GAAG;AAAA,EACrB;AACA,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,QAAM,aAAa,OAAO,MAAM,KAAK,CAAC,GAAG,eAAe,CAAC;AACzD,QAAM,eAAe,OAAO,IAAI,iBAAiB,CAAC;AAClD,SAAO;AAAA,IACL,eAAe,KAAK,KAAK,CAAC,GAAG,kBAAkB;AAAA,IAC/C,QAAQ,EAAE,OAAO,IAAI;AAAA,IACrB,SAAS;AAAA,MACP,GAAG;AAAA,MACH,eAAe;AAAA,MACf,aAAa;AAAA,MACb,MAAM,aAAa,IAAI,eAAe,aAAa;AAAA,MACnD,eACE,aAAa,KAAK,IAAI,QAAQ,IAAI,aAAa,IAAI,QAAQ;AAAA,MAC7D,qBACE,IAAI,YAAY,IAAI,eAAe,IAAI,YAAY;AAAA,MACrD,sBACE,IAAI,YAAY,IAAI,IAAI,mBAAmB,IAAI,YAAY;AAAA,MAC7D,YAAY,IAAI,gBAAgB,IAAI,IAAI,SAAS,IAAI,gBAAgB;AAAA,IACvE;AAAA,IACA,eAAe,aAAa,IAAI,CAAC,IAAI,CAAC,UAAU;AAAA,EAClD;AACF;AAEA,eAAsB,kBACpB,QACA,QACA,gBAAgB,IAChB;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,OAAO,MAAM,GAAG;AAAA,IAKpB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,UAAU,KAAK,KAAK,IAAI,CAAC,SAAS;AAAA,IACtC,OAAO,IAAI;AAAA,IACX,cAAc,OAAO,IAAI,aAAa;AAAA,IACtC,YAAY,OAAO,IAAI,WAAW;AAAA,EACpC,EAAE;AACF,QAAM,WAAW,QAAQ;AAAA,IACvB,CAAC,QAAQ,IAAI,eAAe,KAAK,IAAI,aAAa;AAAA,EACpD;AACA,MAAI,SAAS,SAAS;AACpB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,UAAU,CAAC;AAAA,MACX;AAAA,MACA,eAAe,CAAC,uCAAuC;AAAA,IACzD;AACF,QAAM,SAAS,QAAQ,IAAI,CAAC,QAAQ,IAAI,YAAY;AACpD,QAAM,IAAI,OAAO;AACjB,QAAM,SAAS,IAAI,KAAK;AACxB,QAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI;AAClD,QAAM,QACJ,OAAO;AAAA,IACL,CAAC,KAAK,OAAO,UAAU,OAAO,QAAQ,UAAU,QAAQ;AAAA,IACxD;AAAA,EACF,IAAI,OAAO,OAAO,CAAC,KAAK,QAAQ,UAAU,OAAO,QAAQ,UAAU,GAAG,CAAC;AACzE,QAAM,YAAY,QAAQ,QAAQ;AAClC,QAAM,WAAW,KAAK;AAAA,IACpB,OAAO;AAAA,MACL,CAAC,KAAK,OAAO,UAAU,OAAO,SAAS,YAAY,QAAQ,WAAW;AAAA,MACtE;AAAA,IACF,IAAI;AAAA,EACN;AACA,QAAM,cAAc,QAAQ,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;AAC9D,QAAM,WAAW,YAAY,SACzB,YAAY,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,YAAY,CAAC,IACxD,YAAY,SACZ;AACJ,QAAM,YAAY,oBAAI,KAAK,GAAG,QAAQ,GAAG,EAAE,GAAG,KAAK,YAAY;AAC/D,QAAM,WAAW,MAAM;AAAA,IACrB,EAAE,QAAQ,KAAK,IAAI,KAAK,IAAI,eAAe,CAAC,GAAG,EAAE,EAAE;AAAA,IACnD,CAAC,GAAG,WAAW;AACb,YAAM,OAAO,IAAI;AAAA,QACf,KAAK;AAAA,UACH,UAAU,eAAe;AAAA,UACzB,UAAU,YAAY,IAAI,SAAS;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AACA,YAAM,OAAO,KAAK,IAAI,GAAG,YAAY,SAAS,IAAI,OAAO;AACzD,aAAO;AAAA,QACL,OAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,QACrC,mBAAmB,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,QAAQ,CAAC;AAAA,QAC1D,kBAAkB,KAAK,MAAM,IAAI;AAAA,QACjC,mBAAmB,KAAK,MAAM,OAAO,QAAQ;AAAA,QAC7C,qBAAqB,KAAK,MAAM,QAAQ;AAAA,QACxC,UAAU,WAAW,IAAI,OAAO,WAAW;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,eAAe,SAAS;AAAA,IACxB,eAAe,WAAW,IAAI,CAAC,IAAI,CAAC,UAAU;AAAA,IAC9C,SAAS;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,oBAAoB,OAGxB;AAChB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,OAAO,MAAM,UAAU,IAAI,MAAM,QAAQ,MAAM,MAAM;AAC3D,MAAI,SAAS;AACX,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,IACA,CAAC,MAAM,QAAQ,MAAM,MAAM;AAAA,EAC7B;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAqBA,eAAsB,qBAAqB,OAKZ;AAC7B,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,MAAM,MAAM,GAAG;AAAA,IACnB;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,WAAW;AAAA,MACX,MAAM;AAAA,MACN,cAAc;AAAA,MACd,MAAM,KAAK,KAAK;AAAA,MAChB,MAAM,eAAe;AAAA,IACvB;AAAA,EACF;AACA,SAAO,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG,SAAS,CAAC,EAAE;AACvC;AAEA,eAAsB,oBACpB,QACA,QAC8B;AAC9B,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,CAAC,QAAQ,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC1C,GAAG;AAAA,MACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,CAAC,MAAM;AAAA,IACT;AAAA,IACA,GAAG;AAAA,MACD;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,CAAC,MAAM;AAAA,IACT;AAAA,EACF,CAAC;AACD,SAAO,OAAO,KAAK,IAAI,CAAC,WAAW;AAAA,IACjC,GAAG;AAAA,IACH,SAAS,QAAQ,KACd,OAAO,CAAC,WAAW,OAAO,aAAa,MAAM,EAAE,EAC/C,IAAI,CAAC,EAAE,UAAU,UAAU,GAAG,OAAO,MAAM,MAAM;AAAA,EACtD,EAAE;AACJ;AAEA,eAAsB,qBAAqB,OAOzB;AAChB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,QAAQ;AAAA,MACd,MAAM,eAAe;AAAA,MACrB,MAAM,UAAU;AAAA,IAClB;AAAA,EACF;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAEA,eAAsB,6BAA6B,OAMjC;AAChB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,WAAW,0BAA0B,WAAW,MAAM,QAAQ,EAAE;AACtE,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AACF,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,WAAW,GAAG,MAAM,SAAS,MAAM,QAAQ,UAAU,MAAM,KAAK;AAAA,EACnE;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAoBA,IAAM,sBACJ;AAEK,SAAS,uBAAuB,OAAmC;AACxE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,OAAO,2BAA2B;AACzD,WAAO,OAAO,SAAS,MAAM,GAAG,GAAI,KAAK;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,sBACd,OACe;AACf,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa;AACrD,aAAO;AACT,WAAO,SAAS;AAChB,WAAO,OAAO;AACd,WAAO,OAAO,SAAS,EAAE,MAAM,GAAG,GAAI;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,4BACd,OACkD;AAClD,QAAM,SAA2D,CAAC;AAClE,aAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,EAAE,GAAG;AACtE,QAAI,CAAC,0BAA0B,KAAK,GAAG,KAAK,oBAAoB,KAAK,GAAG;AACtE;AACF,QACE,aAAa,QACb,OAAO,aAAa,aACpB,OAAO,aAAa;AAEpB,aAAO,GAAG,IAAI;AAAA,aACP,OAAO,aAAa,SAAU,QAAO,GAAG,IAAI,SAAS,MAAM,GAAG,GAAG;AAAA,EAC5E;AACA,SAAO;AACT;AAYA,eAAsB,sBAAsB,OAKf;AAC3B,QAAM,KAAK,iBAAiB;AAC5B,QAAM,YAAY,OAAO,WAAW,CAAC;AACrC,QAAM,WAAW,0BAA0B,MAAM,MAAM;AACvD,QAAM,cAAc,MAAM,GAAG;AAAA,IAM3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,CAAC,MAAM,aAAa;AAAA,EACtB;AACA,QAAM,QAAQ,YAAY,KAAK,CAAC;AAChC,MAAI,CAAC,SAAS,MAAM,WAAW,UAAU;AACvC,UAAM,UAAU,CAAC,QAAQ,mBAAmB,eAAe;AAC3D,UAAM,GAAG;AAAA,MACP;AAAA;AAAA,MAEA;AAAA,QACE,WAAW;AAAA,QACX;AAAA,QACA,OAAO,WAAW;AAAA,QAClB,OAAO,MAAM;AAAA,QACb;AAAA,QACA,MAAM,OAAO;AAAA,QACb,KAAK,UAAU,OAAO;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,UAAU,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA,QAAQ,OAAO,WAAW;AAAA,MAC1B,SAAS,OAAO,MAAM;AAAA,IACxB;AAAA,EACF;AACA,MAAI,CAAC,MAAM,UAAU;AACnB,UAAM,UAAU,CAAC,8BAA8B;AAC/C,UAAM,GAAG;AAAA,MACP;AAAA;AAAA,MAEA;AAAA,QACE,WAAW;AAAA,QACX;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,MAAM,OAAO;AAAA,QACb,KAAK,UAAU,OAAO;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,UAAU,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AACA,MAAI,CAAC,UAAU;AACb,UAAM,UAAU,CAAC,2BAA2B;AAC5C,UAAM,GAAG;AAAA,MACP;AAAA;AAAA,MAEA;AAAA,QACE,WAAW;AAAA,QACX;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,KAAK,UAAU,OAAO;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,UAAU,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,eAAe,MAAM,GAAG;AAAA,IAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,CAAC,WAAW,GAAG,MAAM,IAAI,QAAQ;AAAA,EACnC;AACA,MAAI,aAAa,KAAK,CAAC,GAAG,UAAU,YAAY;AAC9C,UAAM,GAAG;AAAA,MACP;AAAA;AAAA;AAAA,MAGA,CAAC,MAAM,IAAI,UAAU,MAAM,OAAO,MAAM;AAAA,IAC1C;AACA,UAAM,UAAU,CAAC,mBAAmB;AACpC,UAAM,GAAG;AAAA,MACP;AAAA;AAAA,MAEA;AAAA,QACE,WAAW;AAAA,QACX;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,MAAM,OAAO;AAAA,QACb,KAAK,UAAU,OAAO;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,UAAU,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,MAAI,WAAW;AACf,MAAI,WAAW;AACf,QAAM,cAAc,oBAAI,IAAY;AACpC,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,eAAW,SAAS,MAAM,QAAQ;AAChC,UAAI;AACF,cAAM,cAAc,4BAA4B,KAAK;AACrD,cAAM,iBACJ,MAAM,aAAa,MAAM,YACrB,MAAM,OAAO;AAAA,UACX;AAAA;AAAA;AAAA,UAGA;AAAA,YACE,MAAM;AAAA,YACN,MAAM,YACF,aAAa,cAAc,MAAM,SAAS,IAC1C;AAAA,YACJ,MAAM,YACF,aAAa,cAAc,MAAM,SAAS,IAC1C;AAAA,UACN;AAAA,QACF,IACA,EAAE,MAAM,CAAC,EAAE;AACjB,cAAM,WAAW,MAAM,OAAO;AAAA,UAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UASA;AAAA,YACE,WAAW;AAAA,YACX,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN;AAAA,YACA,MAAM,aAAa;AAAA,YACnB,MAAM,aAAa;AAAA,YACnB,MAAM;AAAA,YACN,MAAM;AAAA,YACN,uBAAuB,MAAM,QAAQ,MAAM,YAAY;AAAA,YACvD,sBAAsB,MAAM,YAAY;AAAA,YACxC,MAAM,OAAO,MAAM,GAAG,GAAG,KAAK;AAAA,YAC9B,sBAAsB,MAAM,QAAQ;AAAA,YACpC,MAAM,QAAQ,MAAM,GAAG,GAAG,KAAK;AAAA,YAC/B,MAAM,QAAQ,MAAM,GAAG,GAAG,KAAK;AAAA,YAC/B,MAAM,UAAU,MAAM,GAAG,GAAG,KAAK;AAAA,YACjC,MAAM,eAAe;AAAA,YACrB,KAAK,UAAU,4BAA4B,MAAM,UAAU,CAAC;AAAA,YAC5D,uBAAuB,MAAM,WAAW,aAAa,CAAC;AAAA,YACtD,uBAAuB,MAAM,WAAW,YAAY,CAAC;AAAA,YACrD,KAAK,UAAU,iBAAiB,MAAM,QAAQ,CAAC;AAAA,YAC/C,eAAe,KAAK,CAAC,GAAG,aAAa;AAAA,YACrC,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,YAAY;AAAA,UACd;AAAA,QACF;AACA,YAAI,SAAS,SAAU,aAAY;AAAA,aAC9B;AACH,sBAAY;AACZ,sBAAY,IAAI,iBAAiB;AAAA,QACnC;AAAA,MACF,QAAQ;AACN,oBAAY;AACZ,oBAAY,IAAI,qBAAqB;AAAA,MACvC;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,CAAC,MAAM,IAAI,UAAU,UAAU,QAAQ;AAAA,IACzC;AACA,UAAM,OAAO;AAAA,MACX;AAAA;AAAA;AAAA,MAGA;AAAA,QACE,WAAW;AAAA,QACX;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK,UAAU,CAAC,GAAG,WAAW,CAAC;AAAA,MACjC;AAAA,IACF;AACA,UAAM,OAAO,MAAM,QAAQ;AAAA,EAC7B,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,CAAC,GAAG,WAAW;AAAA,IAC5B;AAAA,IACA,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,EACjB;AACF;AAuBA,SAAS,uBACP,OACA,KACe;AACf,QAAM,aAAa,OAAO,KAAK,EAAE,YAAY;AAC7C,SAAO,cAAc,eAAe,KAAK,UAAU,IAC/C,WAAW,MAAM,GAAG,GAAG,IACvB;AACN;AAYA,SAAS,gBACP,OACA,QACA,SACQ;AACR,SAAO,WAAW,QAAQ,EACvB,OAAO,KAAK,UAAU,EAAE,OAAO,QAAQ,QAAQ,CAAC,CAAC,EACjD,OAAO,WAAW,EAClB,MAAM,GAAG,EAAE;AAChB;AAEA,SAAS,iBACP,QACA,aACQ;AACR,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,SAAS,KAAK;AAAA,MAClB,OAAO,KAAK,QAAQ,WAAW,EAAE,SAAS,MAAM;AAAA,IAClD;AACA,QACE,OAAO,MAAM,KACb,OAAO,MAAM,eACb,CAAC,OAAO,UAAU,OAAO,CAAC,KAC1B,OAAO,OAAO,CAAC,IAAI;AAEnB,YAAM,IAAI,MAAM,SAAS;AAC3B,WAAO,OAAO,OAAO,CAAC;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,aACP,MACA,OACA,aAC6C;AAC7C,MAAI,CAAC;AACH,WAAO,EAAE,OAAO,MAAM,UAAU,EAAE,aAAa,OAAO,YAAY,KAAK,EAAE;AAC3E,QAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,EAAE,CAAC,CAAC;AACvE,QAAM,SAAS,iBAAiB,OAAO,QAAQ,WAAW;AAC1D,QAAM,QAAQ,KAAK,MAAM,QAAQ,SAAS,KAAK;AAC/C,QAAM,cAAc,SAAS,MAAM,SAAS,KAAK;AACjD,SAAO;AAAA,IACL;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA,YAAY,cACR,OAAO;AAAA,QACL,KAAK,UAAU,EAAE,GAAG,GAAG,GAAG,aAAa,GAAG,SAAS,MAAM,OAAO,CAAC;AAAA,MACnE,EAAE,SAAS,WAAW,IACtB;AAAA,IACN;AAAA,EACF;AACF;AAEA,eAAsB,0BAA0B,OAUF;AAC5C,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,MAAI,MAAM,SAAS;AACjB,UAAM,QAAQ,MAAM,GAAG;AAAA,MACrB;AAAA,MACA,CAAC,MAAM,SAAS,MAAM,MAAM;AAAA,IAC9B;AACA,QAAI,CAAC,MAAM;AACT,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,EACJ;AACA,QAAM,KAAK,WAAW;AACtB,QAAM,iBAAiB,MAAM,YACzB,MAAM,GAAG;AAAA,IACP;AAAA;AAAA,IAEA,CAAC,MAAM,QAAQ,aAAa,cAAc,MAAM,SAAS,CAAC;AAAA,EAC5D,IACA,EAAE,MAAM,CAAC,EAAE;AACf,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA;AAAA,MACE;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,WAAW;AAAA,MACjB,MAAM,aAAa;AAAA,MACnB,MAAM;AAAA,MACN,MAAM,cAAc;AAAA,OACnB,MAAM,YAAY,OAAO,YAAY;AAAA,MACtC,MAAM;AAAA,MACN,eAAe,KAAK,CAAC,GAAG,aAAa;AAAA,IACvC;AAAA,EACF;AACA,SAAO,EAAE,IAAI,OAAO,KAAK,CAAC,GAAG,MAAM,IAAI,SAAS,QAAQ,OAAO,QAAQ,EAAE;AAC3E;AAEA,SAAS,eAAe,QAAgB,SAA2B;AACjE,QAAM,SAAoB,CAAC,QAAQ,QAAQ,OAAO,QAAQ,GAAG;AAC7D,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,MAAM,CAAC,KAAa,UAAmB;AAC3C,WAAO,KAAK,KAAK;AACjB,YAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,OAAO,MAAM,EAAE,CAAC;AAAA,EACpD;AACA,MAAI,QAAQ,UAAU;AACpB,QAAI,+BAA+B,QAAQ,QAAQ;AACrD,MAAI,QAAQ,WAAW;AACrB,QAAI,+BAA+B,QAAQ,SAAS;AACtD,MAAI,QAAQ,OAAQ,KAAI,sCAAsC,QAAQ,MAAM;AAC5E,MAAI,QAAQ,OAAQ,KAAI,oCAAoC,QAAQ,MAAM;AAC1E,MAAI,QAAQ;AACV,QAAI,sCAAsC,QAAQ,QAAQ;AAC5D,MAAI,QAAQ,UAAW,KAAI,oBAAoB,QAAQ,SAAS;AAChE,MAAI,QAAQ;AACV,QAAI,2CAA2C,QAAQ,aAAa;AACtE,MAAI,QAAQ;AACV,QAAI,kDAAkD,QAAQ,QAAQ;AACxE,MAAI,QAAQ;AACV,QAAI,kDAAkD,QAAQ,QAAQ;AACxE,MAAI,QAAQ,KAAM,KAAI,cAAc,QAAQ,IAAI;AAChD,MAAI,QAAQ;AACV,QAAI,2CAA2C,QAAQ,WAAW;AACpE,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,YAAY,YAAY;AAAA,IAClC;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,WAAW,YAAY;AAAA,IACjC;AACF,SAAO,EAAE,OAAO,QAAQ,KAAK,OAAO,GAAG,OAAO;AAChD;AAEA,SAAS,oBAAoB,QAAgB,SAA2B;AACtE,QAAM,SAAoB,CAAC,QAAQ,QAAQ,OAAO,QAAQ,GAAG;AAC7D,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,MAAM,CAAC,KAAa,UAAmB;AAC3C,WAAO,KAAK,KAAK;AACjB,YAAQ,KAAK,IAAI,WAAW,KAAK,IAAI,OAAO,MAAM,EAAE,CAAC;AAAA,EACvD;AACA,MAAI,QAAQ,UAAU;AACpB,QAAI,+BAA+B,QAAQ,QAAQ;AACrD,MAAI,QAAQ,WAAW;AACrB;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV,QAAI,gDAAgD,QAAQ,MAAM;AACpE,MAAI,QAAQ;AACV,QAAI,8CAA8C,QAAQ,MAAM;AAClE,MAAI,QAAQ;AACV,QAAI,gDAAgD,QAAQ,QAAQ;AACtE,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,YAAY,YAAY;AAAA,IAClC;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,WAAW,YAAY;AAAA,IACjC;AACF,MAAI,QAAQ;AACV,QAAI,yBAAyB,QAAQ,cAAc;AACrD,SAAO,EAAE,OAAO,QAAQ,KAAK,OAAO,GAAG,OAAO;AAChD;AAEA,eAAsB,kBACpB,QACA,QACA,SACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,SAAS,MAAM,GAAG;AAAA,IAQtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAMmC,MAAM,KAAK;AAAA,IAC9C,MAAM;AAAA,EACR;AACA,QAAM,kBAAkB,oBAAoB,QAAQ,OAAO;AAC3D,QAAM,cAAc,MAAM,GAAG;AAAA,IAI3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOU,gBAAgB,KAAK;AAAA,IAC/B,gBAAgB;AAAA,EAClB;AACA,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,QAAM,aAAa,YAAY,KAAK,CAAC;AACrC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aAAa,WAAW;AAAA,IACxB,cAAc,WAAW;AAAA,IACzB,gBACE,IAAI,WAAW,IAAI,WAAW,cAAc,IAAI,WAAW;AAAA,IAC7D,kBAAkB,IAAI;AAAA,EACxB;AACF;AAEA,eAAsB,oBACpB,QACA,QACA,SACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,oBAAoB,IAAI,MAAM,OAAO,SAAS,CAAC;AACrD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB,uDAAuD,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAM9D,MAAM,KAAK;AAAA;AAAA,IAErB,CAAC,GAAG,MAAM,QAAQ,QAAQ;AAAA,EAC5B;AACA,QAAM,kBAAkB,oBAAoB,QAAQ,OAAO;AAC3D,QAAM,8BAA8B,IAAI,gBAAgB,OAAO,SAAS,CAAC;AACzE,QAAM,cAAc,MAAM,GAAG;AAAA,IAK3B,uDAAuD,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cASxE,gBAAgB,KAAK;AAAA;AAAA,IAE/B,CAAC,GAAG,gBAAgB,QAAQ,QAAQ;AAAA,EACtC;AACA,QAAM,SAAS,oBAAI,IAA6C;AAChE,aAAW,OAAO,OAAO;AACvB,WAAO,IAAI,OAAO,IAAI,IAAI,GAAG,EAAE,GAAG,KAAK,aAAa,GAAG,cAAc,EAAE,CAAC;AAC1E,aAAW,OAAO,YAAY,MAAM;AAClC,UAAM,UAAU,OAAO,IAAI,IAAI,IAAI,KAAK;AAAA,MACtC,MAAM,IAAI;AAAA,MACV,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MACX,QAAQ;AAAA,IACV;AACA,WAAO,IAAI,IAAI,MAAM;AAAA,MACnB,GAAG;AAAA,MACH,aAAa,IAAI;AAAA,MACjB,cAAc,OAAO,IAAI,aAAa;AAAA,IACxC,CAAC;AAAA,EACH;AACA,QAAM,SAAS,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,IAAK,CAAC,MAAM,UAC9C,OAAO,KAAK,IAAI,EAAE,cAAc,OAAO,MAAM,IAAI,CAAC;AAAA,EACpD;AACA,SAAO,EAAE,QAAQ,UAAU,kBAAkB,OAAO,GAAG,EAAE,GAAG,QAAQ,KAAK;AAC3E;AAEA,eAAsB,qBACpB,QACA,QACA,SACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOU,MAAM,KAAK;AAAA;AAAA;AAAA;AAAA,IAIrB,MAAM;AAAA,EACR;AACA,QAAM,SAAoB,CAAC,QAAQ,QAAQ,OAAO,QAAQ,GAAG;AAC7D,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,WAAW,CAAC,KAAa,UAAmB;AAChD,WAAO,KAAK,KAAK;AACjB,iBAAa,KAAK,IAAI,QAAQ,KAAK,IAAI,OAAO,MAAM,EAAE,CAAC;AAAA,EACzD;AACA,MAAI,QAAQ,UAAU;AACpB,aAAS,mCAAmC,QAAQ,QAAQ;AAC9D,MAAI,QAAQ,WAAW;AACrB,aAAS,mCAAmC,QAAQ,SAAS;AAC/D,MAAI,QAAQ;AACV,aAAS,0CAA0C,QAAQ,MAAM;AACnE,MAAI,QAAQ;AACV,aAAS,wCAAwC,QAAQ,MAAM;AACjE,MAAI,QAAQ;AACV,aAAS,0CAA0C,QAAQ,QAAQ;AACrE,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ,KAAM,UAAS,kBAAkB,QAAQ,IAAI;AACzD,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ;AAAA,IACV;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,YAAY,YAAY;AAAA,IAClC;AACF,MAAI,QAAQ;AACV;AAAA,MACE;AAAA,MACA,QAAQ,WAAW,YAAY;AAAA,IACjC;AACF,MAAI,QAAQ;AACV,aAAS,yBAAyB,QAAQ,cAAc;AAC1D,QAAM,YAAY,QAAQ,qBAAqB,eAAe,SAAS;AACvE,QAAM,aAAa,MAAM,GAAG;AAAA,IAO1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAUgC,SAAS;AAAA;AAAA,eAE9B,aAAa,KAAK,OAAO,CAAC;AAAA;AAAA,IAErC;AAAA,EACF;AACA,QAAM,WAAW,IAAI;AAAA,IACnB,WAAW,KAAK,IAAI,CAAC,QAAQ;AAAA,MAC3B,GAAG,IAAI,MAAM,KAAS,IAAI,MAAM,KAAS,IAAI,QAAQ;AAAA,MACrD;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,OAAO,OAAO,KAAK,IAAI,CAAC,QAAa;AACzC,UAAM,UAAU,SAAS;AAAA,MACvB,GAAG,IAAI,MAAM,KAAS,IAAI,MAAM,KAAS,IAAI,QAAQ;AAAA,IACvD;AACA,UAAM,cAAc,OAAO,SAAS,eAAe,CAAC;AACpD,WAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,gBAAgB,IAAI,WAAW,IAAI,cAAc,IAAI,WAAW;AAAA,MAChE,cAAc,OAAO,SAAS,iBAAiB,CAAC;AAAA,IAClD;AAAA,EACF,CAAC;AACD,aAAW,WAAW,WAAW,MAAM;AACrC,UAAM,MAAM,GAAG,QAAQ,MAAM,KAAS,QAAQ,MAAM,KAAS,QAAQ,QAAQ;AAC7E,QACE,CAAC,OAAO,KAAK;AAAA,MACX,CAAC,QACC,GAAG,IAAI,MAAM,KAAS,IAAI,MAAM,KAAS,IAAI,QAAQ,OAAO;AAAA,IAChE,GACA;AACA,WAAK,KAAK;AAAA,QACR,QAAQ,QAAQ;AAAA,QAChB,QAAQ,QAAQ;AAAA,QAChB,UAAU,QAAQ;AAAA,QAClB,UAAU;AAAA,QACV,UAAU;AAAA,QACV,WAAW;AAAA,QACX,aAAa,OAAO,QAAQ,WAAW;AAAA,QACvC,gBAAgB;AAAA,QAChB,cAAc,OAAO,QAAQ,aAAa;AAAA,MAC5C,CAAC;AAAA,IACH;AAAA,EACF;AACA,OAAK;AAAA,IACH,CAAC,GAAQ,MACP,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,QAAQ,KACtC,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,WAAW;AAAA,EAChD;AACA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,gBAAgB,eAAe,QAAQ,OAAO;AAAA,EAChD;AACA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,mBAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC3C;AACF;AAEA,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1B,eAAsB,0BACpB,QACA,QACA,SACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,aACS,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAYjB,MAAM,KAAK;AAAA;AAAA;AAAA;AAAA,IAIpB,MAAM;AAAA,EACR;AACA,QAAM,WAAW,CAAC,OAAO,UAAU,QAAQ;AAC3C,QAAM,WAAW,OAAO;AAAA,IACtB,SAAS,IAAI,CAAC,WAAW;AAAA,MACvB;AAAA,MACA,OAAO,KAAK,OAAO,CAAC,QAAQ,IAAI,WAAW,MAAM;AAAA,IACnD,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,OAAO;AAAA,IACb,mBAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC3C;AACF;AAEA,eAAsB,iBACpB,QACA,QACA,SACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,kDAC8C,MAAM,KAAK;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;AAAA;AAAA;AAAA;AAAA;AAAA,IAgCzD,MAAM;AAAA,EACR;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,WAAW,QAAQ,OAAO;AAAA,EAC5C;AACA,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,UAAU,MAAM;AAAA,IAChB,mBAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC3C;AACF;AAEA,eAAsB,qBACpB,QACA,QACA,SACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,CAAC,QAAQ,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC1C,GAAG;AAAA,MACD;AAAA;AAAA,cAEQ,MAAM,KAAK;AAAA;AAAA;AAAA,MAGnB,MAAM;AAAA,IACR;AAAA,IACA,GAAG;AAAA,MACD;AAAA;AAAA;AAAA;AAAA,uCAIiC,MAAM,KAAK;AAAA;AAAA,MAE5C,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AACD,QAAM,gBAA2B,CAAC,QAAQ,QAAQ,OAAO,QAAQ,GAAG;AACpE,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,aAAa,CAAC,QAAgB,UAAmB;AACrD,kBAAc,KAAK,KAAK;AACxB,mBAAe,KAAK,OAAO,QAAQ,KAAK,IAAI,cAAc,MAAM,EAAE,CAAC;AAAA,EACrE;AACA,MAAI,QAAQ,UAAU;AACpB,eAAW,+BAA+B,QAAQ,QAAQ;AAC5D,MAAI,QAAQ,WAAW;AACrB,eAAW,+BAA+B,QAAQ,SAAS;AAC7D,QAAM,aAAa,MAAM,GAAG;AAAA,IAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,cAKU,eAAe,KAAK,OAAO,CAAC;AAAA;AAAA,IAEtC;AAAA,EACF;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,UAAU,QAAQ,OAAO;AAAA,EAC3C;AACA,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,SAAS,QAAQ;AAAA,IACjB,YAAY,WAAW;AAAA,IACvB,UAAU,MAAM;AAAA,IAChB,kBAAkB,QAAQ,KAAK,CAAC,GAAG,eAAe;AAAA,EACpD;AACF;AAEA,eAAsB,qBACpB,QACA,QACA,SACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,oBAAoB,QAAQ,OAAO;AACjD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAYU,MAAM,KAAK;AAAA;AAAA;AAAA,IAGrB,MAAM;AAAA,EACR;AACA,QAAM,UAAU,OAAO,KAAK;AAAA,IAC1B,CAAC,SAAS,SAAS;AAAA,MACjB,OAAO,QAAQ,QAAQ;AAAA,MACvB,YAAY,QAAQ,aAAa,OAAO,IAAI,eAAe,CAAC;AAAA,IAC9D;AAAA,IACA,EAAE,OAAO,GAAG,YAAY,EAAE;AAAA,EAC5B;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,eAAe,QAAQ,OAAO;AAAA,EAChD;AACA,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,UAAU,MAAM;AAAA,IAChB;AAAA,IACA,kBAAkB,OAAO,KAAK,CAAC,GAAG,eAAe;AAAA,EACnD;AACF;AAEA,eAAsB,eACpB,QACA,QACA,SACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,oBAAoB,QAAQ,OAAO;AACjD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAgBU,MAAM,KAAK;AAAA;AAAA;AAAA;AAAA,IAIrB,MAAM;AAAA,EACR;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,SAAS,QAAQ,OAAO;AAAA,EAC1C;AACA,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,UAAU,MAAM;AAAA,IAChB,mBAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC3C;AACF;AAKA,eAAsB,oBACpB,QACA,QACA,SACA,WACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,SACJ,cAAc,WACV,qHACA,cAAc,WACZ,2GACA,cAAc,YACZ,qHACA,cAAc,WACZ,mHACA;AAAA;AAEZ,QAAM,SAAS,MAAM,GAAG;AAAA,IAStB,UAAU,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAON,MAAM,KAAK;AAAA;AAAA;AAAA;AAAA,IAIrB,MAAM;AAAA,EACR;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,aAAa,SAAS,IAAI,QAAQ,OAAO;AAAA,EAC3D;AACA,QAAM,sBACJ,cAAc,WACV,UACA,cAAc,iBACZ,YACA,cAAc,aAAa,cAAc,WACvC,sBACA;AACV,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,UAAU,MAAM;AAAA,IAChB,mBAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC3C;AACF;AAuBA,SAAS,uBACP,KAYQ;AACR,QAAM,MAAM,IAAI,IAAI,IAAI,eAAe;AACvC,aAAW,CAAC,KAAK,KAAK,KAAK;AAAA,IACzB,CAAC,cAAc,IAAI,UAAU;AAAA,IAC7B,CAAC,cAAc,IAAI,UAAU;AAAA,IAC7B,CAAC,gBAAgB,IAAI,YAAY;AAAA,IACjC,CAAC,YAAY,IAAI,QAAQ;AAAA,IACzB,CAAC,eAAe,IAAI,WAAW;AAAA,IAC/B,CAAC,YAAY,IAAI,QAAQ;AAAA,IACzB,CAAC,WAAW,IAAI,OAAO;AAAA,IACvB,CAAC,eAAe,IAAI,WAAW;AAAA,EACjC;AACE,QAAI,MAAO,KAAI,aAAa,IAAI,KAAK,KAAK;AAC5C,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,oBACP,KAC0B;AAC1B,SAAO,EAAE,GAAG,KAAK,YAAY,uBAAuB,GAAG,EAAE;AAC3D;AAEA,eAAsB,4BAA4B,OAeZ;AACpC,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,MAAI;AACJ,MAAI;AACF,kBAAc,IAAI,IAAI,MAAM,cAAc;AAAA,EAC5C,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,YAAY,QAAQ;AACpD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AACF,MAAI,MAAM,SAAS;AACjB,UAAM,QAAQ,MAAM,GAAG;AAAA,MACrB;AAAA,MACA,CAAC,MAAM,SAAS,MAAM,MAAM;AAAA,IAC9B;AACA,QAAI,CAAC,MAAM;AACT,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,EACJ;AACA,QAAM,aACJ,MAAM,WACF,KAAK,EACN,YAAY,EACZ,QAAQ,gBAAgB,GAAG,KAC9B,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,YAAY,GACjD,MAAM,GAAG,EAAE;AACb,MAAI;AACF,UAAM,SAAS,MAAM,GAAG;AAAA,MACtB;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,QACE,WAAW;AAAA,QACX,MAAM;AAAA,QACN,MAAM,WAAW;AAAA,QACjB,MAAM,KAAK,KAAK;AAAA,QAChB,YAAY,SAAS;AAAA,QACrB;AAAA,QACA,MAAM,OAAO,KAAK;AAAA,QAClB,MAAM,OAAO,KAAK;AAAA,QAClB,MAAM,SAAS,KAAK;AAAA,QACpB,MAAM,MAAM,KAAK,KAAK;AAAA,QACtB,MAAM,SAAS,KAAK,KAAK;AAAA,QACzB,MAAM,SAAS,KAAK,KAAK;AAAA,QACzB,MAAM,QAAQ,KAAK,KAAK;AAAA,QACxB,MAAM,YAAY,KAAK,KAAK;AAAA,QAC5B,MAAM;AAAA,MACR;AAAA,IACF;AACA,WAAO,oBAAoB,OAAO,KAAK,CAAC,CAAC;AAAA,EAC3C,SAAS,OAAO;AACd,QAAK,MAA4B,SAAS;AACxC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACF,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,2BACpB,QACA,QACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,OAAO,OAAO,KAAK,IAAI,mBAAmB;AAChD,QAAM,UAA4B,EAAE,OAAO,OAAO,KAAK,MAAM;AAC7D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,gBAAgB,kBAAkB,QAAQ,OAAO;AAAA,EACnD;AACA,SAAO,EAAE,OAAO,MAAM,OAAO,UAAU,MAAM,SAAS;AACxD;AAEA,eAAsB,6BAA6B,OAIjC;AAChB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,IACA,CAAC,MAAM,QAAQ,MAAM,MAAM;AAAA,EAC7B;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAEA,eAAsB,6BACpB,WACA,UACwB;AACxB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AACA,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,GAAG;AAAA,IACP;AAAA,IACA,CAAC,WAAW,GAAG,IAAI,IAAI,sBAAsB,YAAY,MAAS,CAAC;AAAA,EACrE;AACA,SAAO,uBAAuB,GAAG;AACnC;AA0CA,eAAsB,oBAAoB,OAYZ;AAC5B,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,QAAQ,MAAM,GAAG;AAAA,IACrB;AAAA,IACA,CAAC,MAAM,SAAS,MAAM,MAAM;AAAA,EAC9B;AACA,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,QAAM,WAAW,cAAc,MAAM,IAAI;AACzC,QAAM,OAAO,GAAG,QAAQ,IAAI,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AAC1D,QAAM,WAAW,QAAQ,YAAY,EAAE,EAAE,SAAS,WAAW,CAAC;AAC9D,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,WAAW;AAAA,MACX;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,KAAK,KAAK;AAAA,MAChB;AAAA,MACA,KAAK,UAAU,MAAM,MAAM;AAAA,MAC3B,KAAK,UAAU,MAAM,KAAK;AAAA,MAC1B,MAAM,aAAa,KAAK,KAAK;AAAA,MAC7B,MAAM,gBAAgB,KAAK,KAAK;AAAA,MAChC,MAAM,aAAa,KAAK,KAAK;AAAA,MAC7B,MAAM,aAAa,KAAK,KAAK;AAAA,MAC7B,MAAM,UAAU,cAAc;AAAA,MAC9B,MAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO,OAAO,KAAK,CAAC;AACtB;AAEA,eAAsB,mBACpB,QACA,QACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA,IAGA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,SAAS,QAAQ,EAAE,OAAO,OAAO,KAAK,MAAM,CAAC;AAAA,EAC/D;AACA,SAAO,EAAE,OAAO,MAAM,OAAO,UAAU,MAAM,SAAS;AACxD;AAEA,eAAsB,uBACpB,UAGA;AACA,QAAM,SAAS,MAAM,iBAAiB,EAAE;AAAA,IAGtC;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,OAAO,KAAK,CAAC,KAAK;AAC3B;AAEA,eAAsB,8BAA8B,OAUd;AACpC,QAAM,KAAK,WAAW;AACtB,QAAM,iBAAiB,EAAE;AAAA,IACvB;AAAA;AAAA,IAEA;AAAA,MACE;AAAA,MACA,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,MAAM,aAAa;AAAA,MACnB,MAAM,aAAa;AAAA,MACnB,MAAM,gBAAgB;AAAA,MACtB,MAAM,QAAQ,MAAM,GAAG,GAAG,KAAK;AAAA,MAC/B,MAAM,QAAQ,MAAM,GAAG,GAAG,KAAK;AAAA,MAC/B,MAAM,UAAU,MAAM,GAAG,GAAG,KAAK;AAAA,MACjC,MAAM,qBAAqB;AAAA,MAC3B,KAAK,UAAU,iBAAiB,MAAM,QAAQ,CAAC;AAAA,IACjD;AAAA,EACF;AACA,SAAO,EAAE,cAAc,GAAG;AAC5B;AAEA,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,iBACd,OACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,SAAS,CAAC,CAAC,GAAG;AACpD,QAAI,CAAC,YAAY,IAAI,GAAG,KAAK,OAAO,QAAQ,SAAU;AACtD,UAAM,aAAa,IAAI,KAAK;AAC5B,QAAI,WAAY,QAAO,GAAG,IAAI,WAAW,MAAM,GAAG,GAAG;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAc,OAAuB;AACzD,SAAOC,YAAW,UAAU,iBAAiB,CAAC,EAC3C,OAAO,GAAG,IAAI,IAAI,MAAM,KAAK,EAAE,YAAY,CAAC,EAAE,EAC9C,OAAO,KAAK;AACjB;AAEA,eAAsB,0BAA0B,OASS;AACvD,QAAM,SAAS,MAAM,iBAAiB,EAAE,QAAQ;AAChD,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B;AAAA;AAAA,MAEA,CAAC,WAAW,GAAG,MAAM,QAAQ,MAAM,YAAY;AAAA,IACjD;AACA,UAAM,WAAW,OAAO,KAAK,CAAC,EAAE;AAChC,UAAM,UAKD,CAAC;AACN,QAAI,MAAM;AACR,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,UAAU;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AACH,QAAI,MAAM;AACR,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,UAAU;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AACH,QAAI,MAAM;AACR,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,UAAU;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AACH,QAAI,MAAM;AACR,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,UAAU;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AACH,eAAW,CAAC,UAAU,OAAO,KAAK,OAAO;AAAA,MACvC,iBAAiB,MAAM,QAAQ;AAAA,IACjC,GAAG;AACD,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,GAAG,QAAQ,IAAI,OAAO;AAAA,QAC7B,UAAU;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AACA,eAAW,UAAU,SAAS;AAC5B,YAAM,OAAO,MAAM,OAAO;AAAA,QACxB;AAAA;AAAA,QAEA;AAAA,UACE,WAAW;AAAA,UACX,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa,OAAO,MAAM,OAAO,KAAK;AAAA,QACxC;AAAA,MACF;AACA,YAAM,OAAO;AAAA,QACX;AAAA;AAAA;AAAA,QAGA;AAAA,UACE,WAAW;AAAA,UACX,MAAM;AAAA,UACN;AAAA,UACA,KAAK,KAAK,CAAC,EAAE;AAAA,UACb,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM;AACR,YAAM,OAAO;AAAA,QACX;AAAA,QACA,CAAC,UAAU,MAAM,cAAc,MAAM,MAAM;AAAA,MAC7C;AACF,QAAI,MAAM,aAAa,MAAM,WAAW;AACtC,YAAM,OAAO;AAAA,QACX;AAAA;AAAA,QAEA;AAAA,UACE;AAAA,UACA,MAAM;AAAA,UACN,MAAM,aAAa;AAAA,UACnB,MAAM,aAAa;AAAA,QACrB;AAAA,MACF;AACA,YAAM,OAAO;AAAA,QACX;AAAA,QACA,CAAC,UAAU,MAAM,QAAQ,MAAM,aAAa,IAAI;AAAA,MAClD;AAAA,IACF;AACA,UAAM,OAAO,MAAM,QAAQ;AAC3B,WAAO,EAAE,UAAU,eAAe,QAAQ,OAAO;AAAA,EACnD,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,eAAsB,oBACpB,QACA,QACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,UAAU,QAAQ,EAAE,OAAO,OAAO,KAAK,MAAM,CAAC;AAAA,EAChE;AACA,SAAO,EAAE,QAAQ,MAAM,OAAO,UAAU,MAAM,SAAS;AACzD;AAEA,eAAsB,0BACpB,QACA,UACA,QACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,IACA,CAAC,UAAU,MAAM;AAAA,EACnB;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,kBAAkB,QAAQ;AAAA,MACxC,OAAO;AAAA,MACP,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AACA,SAAO,EAAE,UAAU,aAAa,MAAM,OAAO,UAAU,MAAM,SAAS;AACxE;AAOA,eAAsB,yBAAyB,OAY5C;AACD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,QAAM,KAAK,WAAW;AACtB,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,UAAM,OAAO;AAAA,MACX;AAAA;AAAA,MAEA;AAAA,QACE;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,aAAa,MAAM,GAAG,EAAE;AAAA,QAC9B,MAAM,SAAS,MAAM,GAAG,GAAG;AAAA,QAC3B,MAAM,KAAK;AAAA,QACX,MAAM;AAAA,MACR;AAAA,IACF;AACA,eAAW,OAAO,MAAM,MAAM;AAC5B,YAAM,OAAO;AAAA,QACX;AAAA;AAAA,QAEA,CAAC,WAAW,GAAG,IAAI,IAAI,cAAc,IAAI,iBAAiB;AAAA,MAC5D;AAAA,IACF;AACA,UAAM,OAAO,MAAM,QAAQ;AAC3B,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,eAAe,MAAM,KAAK;AAAA,MAC1B,eAAe,MAAM;AAAA,IACvB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,eAAsB,wBACpB,QACA,QACA,MACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA,IAEA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB,eAAe,QAAQ,EAAE,OAAO,OAAO,KAAK,MAAM,CAAC;AAAA,EACrE;AACA,SAAO,EAAE,SAAS,MAAM,OAAO,UAAU,MAAM,SAAS;AAC1D;AAEA,eAAsB,qCAAqC,OAQxD;AACD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA,IAEA;AAAA,MACE,WAAW;AAAA,MACX,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,KAAK,KAAK;AAAA,MAChB,MAAM,eAAe,KAAK,KAAK;AAAA,MAC/B,MAAM,mBAAmB,KAAK,KAAK;AAAA,MACnC,KAAK,UAAU,MAAM,gBAAgB,CAAC,CAAC;AAAA,MACvC,MAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO,OAAO,KAAK,CAAC;AACtB;AAEA,eAAsB,oCACpB,QACA,QACA;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,CAAC,MAAM;AAAA,EACT;AACA,SAAO,OAAO;AAChB;AAEA,eAAsB,sCAAsC,OAIzD;AACD,QAAM,KAAK,iBAAiB;AAC5B,QAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,MAAM;AAClD,QAAM,SAAS,MAAM,GAAG;AAAA,IACtB;AAAA,IACA,CAAC,MAAM,eAAe,MAAM,MAAM;AAAA,EACpC;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAEA,eAAsB,yBAAyB,OAK3B;AAClB,QAAM,KAAK,iBAAiB;AAC5B,QAAM,eAAe,MAAM,GAAG;AAAA,IAC5B;AAAA,IACA,CAAC,MAAM,MAAM;AAAA,EACf;AACA,MAAI,SAAS;AACb,aAAW,eAAe,aAAa,MAAM;AAC3C,UAAM,SAAS,MAAM,GAAG;AAAA,MACtB;AAAA;AAAA,MAEA;AAAA,QACE,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,MAAM,YAAY;AAAA,QAClB,MAAM;AAAA,MACR;AAAA,IACF;AACA,cAAU,OAAO,YAAY;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,eAAsB,2BAA2B,OAK7B;AAClB,QAAM,KAAK,WAAW;AACtB,QAAM,iBAAiB,EAAE;AAAA,IACvB;AAAA;AAAA,IAEA;AAAA,MACE;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,aAAa;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAUA,eAAsB,+BACpB,QAAQ,IAC6B;AACrC,QAAM,SAAS,MAAM,iBAAiB,EAAE;AAAA,IAOtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWA,CAAC,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AAAA,EACnC;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,SAAS;AAAA,IAC/B,IAAI,IAAI;AAAA,IACR,cAAc,IAAI;AAAA,IAClB,aAAa,OAAO,IAAI,aAAa;AAAA,IACrC,mBAAmB,IAAI;AAAA,IACvB,UAAU,IAAI;AAAA,EAChB,EAAE;AACJ;AAEA,eAAsB,8BACpB,OACA,cACe;AACf,QAAM,KAAK,iBAAiB;AAC5B,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,UAAM,OAAO;AAAA,MACX;AAAA,MACA,CAAC,KAAK;AAAA,IACR;AACA,UAAM,OAAO;AAAA,MACX;AAAA,MACA,CAAC,YAAY;AAAA,IACf;AACA,UAAM,OAAO,MAAM,QAAQ;AAAA,EAC7B,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,eAAsB,2BACpB,OACA,UACA,WACe;AACf,QAAM,WAAW,YAAY;AAC7B,QAAM,eAAe,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,WAAW,CAAC,GAAG,GAAG;AACrE,QAAM,iBAAiB,EAAE;AAAA,IACvB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,CAAC,OAAO,UAAU,UAAU,MAAM,GAAG,EAAE,GAAG,OAAO,YAAY,CAAC;AAAA,EAChE;AACF;AAEA,eAAsB,iCACpB,SACA,cACkB;AAClB,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,WAA0B;AAC9B,MAAI;AACF,eAAW,IAAI,IAAI,YAAY,EAAE,SAAS,YAAY;AAAA,EACxD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAM,iBAAiB,EAAE;AAAA,IACtC;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,EACpB;AACA,SAAO,QAAQ,OAAO,QAAQ;AAChC;AAEA,eAAsB,gBAAgB,QAAgB,QAAgB;AACpE,QAAM,KAAK,iBAAiB;AAC5B,QAAM,SAAS,MAAM,oBAAoB,QAAQ,MAAM;AACvD,QAAM,cACJ,OACG,IAAI,CAAC,UAAU,MAAM,aAAa,EAClC,OAAO,OAAO,EACd,KAAK,EACL,GAAG,EAAE,KAAK;AACf,QAAM,CAAC,YAAY,QAAQ,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,IACvD,GAAG;AAAA,MACD;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AAAA,IACA,GAAG;AAAA,MACD;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AAAA,IACA,GAAG;AAAA,MACD;AAAA;AAAA;AAAA,MAGA,CAAC,MAAM;AAAA,IACT;AAAA,EACF,CAAC;AACD,QAAM,SAAS,SAAS,KAAK,CAAC;AAC9B,QAAM,cAAc,OAAO,WAAW,OAAO;AAC7C,QAAM,gBAAgB,cAAc,OAAO,WAAW,cAAc;AACpE,QAAM,QAAQ;AAAA,IACZ,eAAe,KAAK,IAAI,IAAI,KAAK,MAAM,WAAW,IAAI,KAAK,KAAK,KAAK;AAAA,EACvE;AACA,QAAM,cAAc,OAAO;AAAA,IAAK,CAAC,UAC/B,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,qBAAqB;AAAA,EACvE;AACA,SAAO;AAAA,IACL,QACE,OAAO,WAAW,IACd,kBACA,eAAe,CAAC,cACd,oBACA,gBAAgB,OACd,cACA,QACE,UACA,cACE,YACA;AAAA,IACd;AAAA,IACA;AAAA,IACA,kBAAkB,WAAW,KAAK,CAAC,GAAG,sBAAsB;AAAA,IAC5D,cAAc,OAAO,KAAK,CAAC,GAAG,kBAAkB;AAAA,IAChD,qBAAqB,OAAO;AAAA,IAC5B,qBAAqB,OAAO;AAAA,IAC5B;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACF;AAEA,eAAsB,6BAA6B,OAGN;AAC3C,QAAM,KAAK,iBAAiB;AAC5B,QAAM,QAAQ,WAAW;AACzB,QAAM,GAAG;AAAA,IACP;AAAA,IACA,CAAC,OAAO,MAAM,OAAO,MAAM,GAAG;AAAA,EAChC;AACA,QAAM,SAAS,MAAM,GAAG,QAAQ;AAChC,MAAI;AACF,UAAM,OAAO,MAAM,OAAO;AAC1B,UAAM,OAAO;AAAA,MACX;AAAA,MACA,CAAC,MAAM,OAAO,MAAM,GAAG;AAAA,IACzB;AACA,UAAM,WAAW,MAAM,OAAO;AAAA,MAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAcA,CAAC,MAAM,OAAO,MAAM,GAAG;AAAA,IACzB;AACA,UAAM,OAAO,MAAM,QAAQ;AAC3B,UAAM,OAAO,SAAS,YAAY;AAClC,UAAM,GAAG;AAAA,MACP;AAAA,MACA,CAAC,OAAO,IAAI;AAAA,IACd;AACA,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,UAAU;AAC7B,UAAM,GAAG;AAAA,MACP;AAAA,MACA,CAAC,KAAK;AAAA,IACR;AACA,UAAM;AAAA,EACR,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,eAAsB,kCACpB,MAAM,oBAAI,KAAK,GAIf;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,aAAa,MAAM,GAAG,QAAQ;AACpC,QAAM,iBAAiB;AACvB,MAAI,SAAS;AACb,MAAI;AACF,UAAM,OAAO,MAAM,WAAW;AAAA,MAC5B;AAAA,MACA,CAAC,cAAc;AAAA,IACjB;AACA,aAAS,QAAQ,KAAK,KAAK,CAAC,GAAG,QAAQ;AACvC,QAAI,CAAC,OAAQ,QAAO,EAAE,QAAQ,mBAAmB,MAAM,EAAE;AACzD,UAAM,SAAS,MAAM,WAAW;AAAA,MAC9B;AAAA;AAAA;AAAA,MAGA,CAAC,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,GAAM,EAAE,YAAY,CAAC;AAAA,IACtD;AACA,QAAI,OAAO,KAAK,CAAC,EAAG,QAAO,EAAE,QAAQ,WAAW,MAAM,EAAE;AACxD,UAAM,MAAM,IAAI,KAAK,GAAG;AACxB,UAAM,QAAQ,IAAI,KAAK,IAAI,QAAQ,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAC9D,UAAM,SAAS,MAAM,6BAA6B;AAAA,MAChD,OAAO,MAAM,YAAY;AAAA,MACzB,KAAK,IAAI,YAAY;AAAA,IACvB,CAAC;AACD,WAAO,EAAE,QAAQ,YAAY,GAAG,OAAO;AAAA,EACzC,UAAE;AACA,QAAI;AACF,YAAM,WACH,MAAM,iCAAiC,CAAC,cAAc,CAAC,EACvD,MAAM,MAAM,MAAS;AAC1B,eAAW,QAAQ;AAAA,EACrB;AACF;AAcA,SAAS,QAAQ,OAAwB;AACvC,QAAM,OAAO,SAAS,OAAO,KAAK,OAAO,KAAK;AAC9C,SAAO,YAAY,KAAK,IAAI,IAAI,IAAI,KAAK,WAAW,KAAK,IAAI,CAAC,MAAM;AACtE;AAEA,SAAS,UAAU,MAA8C;AAC/D,QAAM,UAAU,CAAC,GAAG,IAAI,IAAI,KAAK,QAAQ,CAAC,QAAQ,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC;AACpE,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SAAO;AAAA,IACL,QAAQ,KAAK,GAAG;AAAA,IAChB,GAAG,KAAK;AAAA,MAAI,CAAC,QACX,QAAQ,IAAI,CAAC,WAAW,QAAQ,IAAI,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AAAA,IACxD;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,sBAAsB,OAOP;AACnC,QAAM,QAAQ,MAAM,mBAAmB,MAAM,MAAM,GAAG;AAAA,IACpD,CAAC,cAAc,UAAU,OAAO,MAAM;AAAA,EACxC;AACA,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACF,QAAM,mBAAmB,CACvBC,cAC4B;AAC5B,UAAM,YAAYA,UAAS,WAAW,aAAa,OAAOA,UAAS;AACnE,WAAO;AAAA,MACL,GAAGA;AAAA,MACH,WACEA,UAAS,WAAW,QAChB,aACAA,UAAS,WAAW,SAClB,qBACA;AAAA,MACR,UAAU,GAAG,KAAK,IAAI,IAAIA,UAAS,MAAM,IAAI,SAAS;AAAA,IACxD;AAAA,EACF;AACA,MAAI,MAAM,gBAAgB;AACxB,UAAM,WAAW,MAAM,iBAAiB,EAAE;AAAA,MAMxC;AAAA;AAAA,MAEA,CAAC,MAAM,QAAQ,MAAM,cAAc;AAAA,IACrC;AACA,UAAMA,YAAW,SAAS,KAAK,CAAC;AAChC,QAAIA,UAAU,QAAO,iBAAiBA,SAAQ;AAAA,EAChD;AACA,QAAM,OACJ,MAAM,WAAW,aACb,MAAM,kBAAkB,MAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO,IACjE,MAAM,WAAW,gBACf,MAAM,qBAAqB,MAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO,IACpE,MAAM,WAAW,YACf,MAAM,iBAAiB,MAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO,IAChE,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACV,QAAM,OACJ,MAAM,WAAW,aACb,CAAC,IAA+B,IAChC,MAAM,WAAW,gBACd,KAAsD,WACvD,MAAM,WAAW,YACd,KAAqD,UACrD,KACE;AACb,MAAI;AACJ,MAAI,MAAM,WAAW,QAAQ;AAC3B,cAAU,KAAK;AAAA,MACb;AAAA,QACE,SAAS;AAAA,QACT,MAAM,EAAE,IAAI,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,QACrC,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM;AAAA,QACf;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,WAAW,MAAM,WAAW,OAAO;AACjC,cAAU,UAAU,IAAI;AAAA,EAC1B,OAAO;AACL,UAAM,WAAW,MAAM;AAAA,MACrB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AACA,cAAU;AAAA,MACR,KAAK,KAAK,IAAI;AAAA,MACd;AAAA,MACA,WAAW,MAAM,MAAM;AAAA,MACvB,WAAW,MAAM,QAAQ,KAAK,OAAO,MAAM,QAAQ,GAAG;AAAA,MACtD,kBAAkB,SAAS,oBAAoB,oBAAoB;AAAA,MACnE;AAAA,MACA,eAAe,SAAS,QAAQ;AAAA,MAChC,eAAe,SAAS,QAAQ;AAAA,MAChC,gBAAgB,SAAS,SAAS;AAAA,MAClC,2BAA2B,SAAS,WAAW;AAAA,MAC/C,wBAAwB,SAAS,eAAe,KAAK,QAAQ,CAAC,CAAC;AAAA,MAC/D;AAAA,MACA,cAAa,oBAAI,KAAK,GAAE,YAAY,CAAC,sBAAsB,MAAM,MAAM;AAAA,IACzE,EAAE,KAAK,IAAI;AAAA,EACb;AACA,QAAM,KAAK,WAAW;AACtB,QAAM,WAAW,MAAM,iBAAiB,EAAE;AAAA,IAMxC;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,MACE;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,kBAAkB;AAAA,MACxB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,UAAU,MAAM,OAAO;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,SAAS,KAAK,CAAC;AAChC,MAAI,SAAU,QAAO,iBAAiB,QAAQ;AAC9C,QAAM,SAAS,MAAM,iBAAiB,EAAE;AAAA,IAMtC;AAAA;AAAA,IAEA,CAAC,MAAM,QAAQ,MAAM,cAAc;AAAA,EACrC;AACA,MAAI,CAAC,OAAO,KAAK,CAAC,GAAG;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,iBAAiB,OAAO,KAAK,CAAC,CAAC;AACxC;","names":["createHmac","createHmac","artifact"]}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
finishHarvestAttempt,
|
|
10
10
|
startHarvestAttempt
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MFNGUM4L.js";
|
|
12
12
|
|
|
13
13
|
// src/api/webhook.ts
|
|
14
14
|
async function deliverWebhook(url, payload, retries = 3) {
|
|
@@ -191,4 +191,4 @@ export {
|
|
|
191
191
|
harvestProblemResponse,
|
|
192
192
|
createHarvestAttemptRecorder
|
|
193
193
|
};
|
|
194
|
-
//# sourceMappingURL=chunk-
|
|
194
|
+
//# sourceMappingURL=chunk-ZEMHL36J.js.map
|
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
updateKpoJobState,
|
|
125
125
|
upsertPageSnapshot,
|
|
126
126
|
verifyPassword
|
|
127
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-MFNGUM4L.js";
|
|
128
128
|
export {
|
|
129
129
|
CORE_SCHEMA_VERSION,
|
|
130
130
|
CREDIT_LOTS_EPOCH,
|
|
@@ -252,4 +252,4 @@ export {
|
|
|
252
252
|
upsertPageSnapshot,
|
|
253
253
|
verifyPassword
|
|
254
254
|
};
|
|
255
|
-
//# sourceMappingURL=db-
|
|
255
|
+
//# sourceMappingURL=db-A4YUPV4Z.js.map
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
} from "./chunk-2K74LVV7.js";
|
|
11
11
|
import {
|
|
12
12
|
extractJobLimitInfo
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-6DPIE262.js";
|
|
14
14
|
import "./chunk-SOEYDWJU.js";
|
|
15
15
|
import "./chunk-5UN33CGU.js";
|
|
16
16
|
import {
|
|
17
17
|
getDb
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MFNGUM4L.js";
|
|
19
19
|
|
|
20
20
|
// src/api/extract-bundle.ts
|
|
21
21
|
import { createWriteStream, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
@@ -565,4 +565,4 @@ export {
|
|
|
565
565
|
SITE_EXTRACT_PAGE_CHUNK,
|
|
566
566
|
assembleExtractArtifacts
|
|
567
567
|
};
|
|
568
|
-
//# sourceMappingURL=extract-bundle-
|
|
568
|
+
//# sourceMappingURL=extract-bundle-2HONVC5T.js.map
|