mcp-scraper 0.89.0 → 0.89.2
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/CHANGELOG.md +37 -0
- package/README.md +3 -1
- package/dist/{analytics-repository-2BMT5JNE.js → analytics-repository-X6J7TWTL.js} +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-core.js +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.js +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/{chunk-4AI7DOS7.js → chunk-2O3UYISB.js} +1 -1
- package/dist/{chunk-PUHFVA7P.js → chunk-3MUEBDWY.js} +1 -1
- package/dist/{chunk-MASR22K4.js → chunk-D4NOJAUN.js} +1 -1
- package/dist/{chunk-WSCGYRWA.js → chunk-DEPJ4ZZ6.js} +1 -1
- package/dist/{chunk-HDPYG3XV.js → chunk-DMCYIWB3.js} +1 -1
- package/dist/{chunk-XPZVJIZ2.js → chunk-EN66HBAW.js} +1 -1
- package/dist/{chunk-E2WRWV3A.js → chunk-HYQDXTS7.js} +1 -1
- package/dist/{chunk-7YGVI5J4.js → chunk-J7EVLE6H.js} +1 -1
- package/dist/{chunk-TXENITMS.js → chunk-K7KMLT33.js} +1 -1
- package/dist/{chunk-L4CGLFPU.js → chunk-MKEMCMRO.js} +1 -1
- package/dist/{chunk-X54CQLK2.js → chunk-S7RA23GR.js} +1 -1
- package/dist/chunk-TW46NKVH.js +1 -0
- package/dist/{chunk-CFI6CXIV.js → chunk-VEJQ4XPI.js} +1 -1
- package/dist/{chunk-M22MM4N4.js → chunk-Z27SYWYD.js} +1 -1
- package/dist/{chunk-GMWKPIYX.js → chunk-Z66Z2HV4.js} +182 -79
- package/dist/{chunk-CCYWSJNG.js → chunk-ZWZ6OWY6.js} +1 -1
- package/dist/db-NLGNTQC3.js +1 -0
- package/dist/{extract-bundle-SNUIHM3J.js → extract-bundle-TXH3VNXH.js} +1 -1
- package/dist/{gmail-service-BZ3H75XC.js → gmail-service-J2DWSQJX.js} +1 -1
- package/dist/index.cjs +143 -32
- package/dist/index.js +1 -1
- package/dist/{lead-list-enrichment-repository-S2H3U7T7.js → lead-list-enrichment-repository-G7Z5DQZR.js} +1 -1
- package/dist/{location-data-repository-OTWHWMV6.js → location-data-repository-TEXZ3I2N.js} +1 -1
- package/dist/{server-RFR2A5UJ.js → server-LBOFC2EE.js} +849 -816
- package/dist/{site-extract-repository-SE776XDC.js → site-extract-repository-MBSCAP4D.js} +1 -1
- package/dist/{worker-XUDSM3AL.js → worker-SUCJKWU2.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-Z2QGQJS2.js +0 -1
- package/dist/db-F2MX63GI.js +0 -1
|
@@ -1,4 +1,115 @@
|
|
|
1
|
-
import{createClient as Ce}from"@libsql/client/http";import{createHash as A,randomBytes as C,randomUUID as m,scryptSync as ie,timingSafeEqual as ke}from"crypto";import{z as T}from"zod";var O="2026-06-26 21:58:03";var Qe="Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.",be="This operation is temporarily unavailable. Please retry later.",Le=["request_aborted","captcha_exhausted","captcha_or_blocked","location_mismatch","proxy_tunnel_failed","proxy_unavailable","harvest_timeout","mcp_request_timeout","mcp_http_error","extraction_failed","concurrency_limit_exceeded","unauthorized","forbidden","invalid_request","rate_limited","upstream_rate_limited","insufficient_balance","service_unavailable","response_lost","vendor_unavailable","page_not_found","page_forbidden","page_rate_limited","page_server_error","page_http_error","bot_check_unresolved","page_too_large","page_unreachable","browser_session_interrupted","site_export_not_found","site_export_format_unavailable","site_export_read_failed","site_export_image_not_found","image_url_invalid","image_url_not_https","image_host_unresolvable","image_host_blocked","image_redirect_invalid","image_source_unavailable","image_too_large","image_redirect_limit","image_base64_invalid","image_source_missing","image_source_ambiguous","image_source_type_invalid","image_artifact_not_found","image_page_too_large","page_image_not_found","page_image_candidates_unusable","image_format_unsupported","image_storage_unconfigured","featured_image_unhostable"],Se=["request_aborted","captcha","location_mismatch","connection","timeout","extraction","concurrency_limit","unauthorized","forbidden","invalid_request","rate_limit","billing","service_unavailable"];function L(e){return e?JSON.stringify(e):null}function z(e){if(e==null)return null;try{let t=typeof e=="string"?JSON.parse(e):e;if(!t||typeof t!="object"||Array.isArray(t))return null;let r=t;if(typeof r.error_code!="string"||!Q.has(r.error_code))return null;let n=Ie({errorCode:r.error_code,errorType:r.error_type,retryable:r.retryable,retryAfterSeconds:r.retry_after_seconds,chargeStatus:r.charge_status,details:r.details});if(typeof r.message=="string"){let i=Z(r.message).trim().slice(0,1e3);i&&!/https?:\/\//i.test(i)&&(n.message=i)}return n}catch{return null}}var Q=new Set(Le),Re=new Set(Se),we=new Set(["active","limit","operation","upgrade_url","topup_url","upgrade_command","slots_per_pack","pack_price_usd","balance_credits","required_credits","retry_guidance","http_status"]),Ae=12,fe=500;function U(e){return typeof e=="string"&&Q.has(e)?e:"service_unavailable"}function Oe(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t={};for(let[r,n]of Object.entries(e)){if(Object.keys(t).length>=Ae)break;if(we.has(r))if(typeof n=="string"){if((r==="upgrade_url"||r==="topup_url")&&!xe(n))continue;let i=Z(n).slice(0,fe);i&&(t[r]=i)}else(typeof n=="number"&&Number.isFinite(n)||typeof n=="boolean"||n===null)&&(t[r]=n)}return Object.keys(t).length>0?t:void 0}function xe(e){if(e.startsWith("/")&&!e.startsWith("//"))return!0;try{let t=new URL(e);return t.protocol==="https:"&&(t.hostname==="mcpscraper.dev"||t.hostname.endsWith(".mcpscraper.dev"))}catch{return!1}}function he(e){let t=U(e);switch(t){case"captcha_exhausted":case"captcha_or_blocked":return"captcha";case"proxy_tunnel_failed":case"proxy_unavailable":case"response_lost":return"connection";case"harvest_timeout":case"mcp_request_timeout":return"timeout";case"concurrency_limit_exceeded":return"concurrency_limit";case"rate_limited":case"upstream_rate_limited":return"rate_limit";case"extraction_failed":case"page_not_found":case"page_forbidden":case"page_rate_limited":case"page_server_error":case"page_http_error":case"bot_check_unresolved":case"page_too_large":case"page_unreachable":case"browser_session_interrupted":case"site_export_not_found":case"site_export_format_unavailable":case"site_export_read_failed":case"site_export_image_not_found":return"extraction";case"image_url_invalid":case"image_url_not_https":case"image_host_unresolvable":case"image_host_blocked":case"image_redirect_invalid":case"image_source_unavailable":case"image_too_large":case"image_redirect_limit":case"image_base64_invalid":case"image_source_missing":case"image_source_ambiguous":case"image_source_type_invalid":case"image_artifact_not_found":case"image_page_too_large":case"page_image_not_found":case"page_image_candidates_unusable":case"image_format_unsupported":case"image_storage_unconfigured":case"featured_image_unhostable":return"invalid_request";case"insufficient_balance":return"billing";case"vendor_unavailable":case"mcp_http_error":return"service_unavailable";default:return t}}function ye(e){switch(U(e)){case"request_aborted":return"The request was cancelled before it completed.";case"captcha_exhausted":case"captcha_or_blocked":return"The target site returned a verification challenge. Heavy traffic can temporarily increase these challenges; retry in a few minutes.";case"location_mismatch":return"The target site returned results for a different location. Please retry.";case"proxy_tunnel_failed":case"proxy_unavailable":return"The web request could not establish a working connection. Please retry later.";case"harvest_timeout":return"The request timed out before it completed. Please retry with a smaller request.";case"mcp_request_timeout":return"The MCP caller stopped waiting before a final response arrived. Durable work may still be running; continue with the returned Task or job handle instead of restarting it.";case"concurrency_limit_exceeded":return"The service is available, but this account has reached its parallel-operation limit. Wait for an active operation to finish or add capacity.";case"unauthorized":return"Authentication is required to perform this operation.";case"forbidden":return"This account does not have permission to perform this operation.";case"invalid_request":return"The request is invalid. Check the supplied inputs and try again.";case"rate_limited":return"This account is sending requests too quickly. Wait briefly and retry.";case"upstream_rate_limited":return"The connected provider is temporarily rate-limiting requests. Wait briefly and retry.";case"mcp_http_error":return"The API returned an unexpected HTTP response.";case"insufficient_balance":return"This account does not have enough Credits for the operation.";case"extraction_failed":return"The target could not be extracted. Please retry later.";case"response_lost":return"The connection was lost before the response arrived. The operation may have already completed and been billed on our side \u2014 check credits_info before retrying blind.";case"vendor_unavailable":return"MCP Scraper's internal browser services require MCP Scraper team attention. Servers or IPs are down until this is fixed \u2014 this is not caused by your request. Please retry in a few minutes.";case"page_not_found":return"The page could not be found. It may have been moved or deleted.";case"page_forbidden":return"The site refused the request.";case"page_rate_limited":return"The site is rate-limiting requests right now. Retrying after a short wait usually works.";case"page_server_error":return"The page's own server returned an error. This is on the target site's side, not something a retry here can fix.";case"page_http_error":return"The page returned an unexpected HTTP response.";case"bot_check_unresolved":return"This site has automated-traffic protection (a bot/CAPTCHA check) that could not be resolved in time. Some sites are simply not extractable this way.";case"page_too_large":return"The page is larger than we can safely process.";case"page_unreachable":return"The page did not respond in time, or the connection could not be completed.";case"browser_session_interrupted":return"The browser session closed before extraction completed. MCP Scraper will retry once with a fresh session; retry the request if this persists.";case"site_export_not_found":return"The site export or requested page was not found, expired, or does not belong to this account.";case"site_export_format_unavailable":return"That representation was not retained for this export page. Read the export manifest and request one of the formats marked available.";case"site_export_read_failed":return"The stored site export could not be read or did not pass its integrity check.";case"site_export_image_not_found":return"The requested site-export image was not found, expired, or does not belong to this account.";case"image_url_invalid":return"The image source is not a valid URL. Use a stable public HTTPS URL or inline image bytes.";case"image_url_not_https":return"Commons image sources must use a public HTTPS URL or inline image bytes.";case"image_host_unresolvable":return"The image host could not be resolved. Use a different stable public URL or inline image bytes.";case"image_host_blocked":return"The image source resolves to a network address Commons cannot fetch safely.";case"image_redirect_invalid":return"The image source redirected to an invalid or unsafe destination.";case"image_source_unavailable":return"The image source could not be fetched. Host a different stable public URL or provide inline image bytes.";case"image_too_large":return"The image exceeds the Commons image size limit.";case"image_redirect_limit":return"The image source exceeded the Commons redirect limit.";case"image_base64_invalid":return"The supplied inline image bytes are invalid.";case"image_source_missing":return"Provide exactly one of a public image or webpage URL, inline image bytes, or an owner-scoped image artifact.";case"image_source_ambiguous":return"Provide only one image source: sourceUrl, imageBase64, or artifactId.";case"image_source_type_invalid":return"Use sourceType only with inline image bytes or an owner-scoped image artifact.";case"image_artifact_not_found":return"The image artifact was not found, expired, or does not belong to this account.";case"image_page_too_large":return"The webpage is too large for bounded Commons image discovery.";case"page_image_not_found":return"The webpage did not expose a usable featured or content image.";case"page_image_candidates_unusable":return"The webpage exposed image candidates, but none returned supported public image bytes.";case"image_format_unsupported":return"Commons accepts JPEG, PNG, GIF, or WebP image bytes.";case"image_storage_unconfigured":return"Commons image storage is temporarily unavailable.";case"featured_image_unhostable":return"The required featured image could not be hosted for Commons publication.";default:return be}}function Ie(e){let t=U(e.errorCode),r=typeof e.retryAfterSeconds=="number"&&Number.isFinite(e.retryAfterSeconds)?Math.max(1,Math.min(86400,Math.trunc(e.retryAfterSeconds))):void 0,n=e.chargeStatus==="not_charged"||e.chargeStatus==="refund_pending"||e.chargeStatus==="refunded"||e.chargeStatus==="charged"?e.chargeStatus:void 0,i=typeof e.errorType=="string"&&Re.has(e.errorType)?e.errorType:void 0,a=Oe(e.details);return{error_code:t,error_type:i??he(t),message:ye(t),retryable:e.retryable===!0,...r?{retry_after_seconds:r}:{},...n?{charge_status:n}:{},...a?{details:a}:{}}}function Z(e){return e.replace((new RegExp("https?:\\/\\/[^\\s]*(?:\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061|on\u006b\u0065\u0072\u006e\u0065\u006c|\u006b\u0065\u0072\u006e\u0065\u006c\\.sh)[^\\s]*", "gi")),"the service").replace(/bright\s*data/gi,"the service").replace((new RegExp("\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+session", "gi")),"this session").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+session", "gi")),"this session").replace(/\b(?:sub|cus|sess|proxy)_[A-Za-z0-9_-]+\b/g,"[redacted]").replace((new RegExp("\\b(?:\u004b\u0045\u0052\u004e\u0045\u004c|\u0042\u0052\u0049\u0047\u0048\u0054\u0044\u0041\u0054\u0041|\u0042\u0052\u0049\u0047\u0048\u0054_\u0044\u0041\u0054\u0041)_[A-Z0-9_]*API_KEY\\b", "g")),"BROWSER_SERVICE_API_KEY").replace((new RegExp("\\b\u006b\u0065\u0072\u006e\u0065\u006c\\b", "gi")),"the service").replace(/ +/g," ").trim()}var G=class extends Error{constructor(r){super(`CAPTCHA detected. ${r}`);this.instructions=r}instructions;name="CaptchaError"},Y=class extends Error{constructor(r,n){super(r);this.cause=n}cause;name="ExtractionError"},J=class extends Error{constructor(r){super(`Google Maps search did not reach a complete result state (${r})`);this.pageState=r}pageState;name="MapsSearchIncompletePageError"},$=class extends Error{name="RequestAbortedError";constructor(t="Request aborted before harvest completed"){super(t)}},K=class extends Error{name="PaaInteractionInterruptedError"},V=class extends Error{name="LocationMismatchError";constructor(t="Google returned results for a different location than requested"){super(t)}};var De=process.env.TURSO_DATABASE_URL??"file:./paa-api.db",Ue=process.env.TURSO_AUTH_TOKEN,h=null,ee=!1,v="2026-08-27.1",te="data-2026-07-28-site-extract-counters.1",re=["admin_credit_adjustment_events","admin_credit_adjustments","billing_authorizations","billing_credits","billing_debits","billing_debit_allocations","billing_events","browser_profile_labels","connected_account_billing","concurrency_locks","credit_lot_expirations","credit_lots","credit_lot_repairs","free_credit_refreshes","harvest_attempts","inbox_messages","jobs","kpo_jobs","kpo_phase_log","ledger","oauth_clients","oauth_codes","oauth_tokens","page_snapshots","password_reset_tokens","rate_limits","request_events","scheduled_run_view_links","serp_identities","site_audit_jobs","site_audit_phase_log","site_extract_jobs","site_extract_pages","stripe_events","users","workflow_artifacts","workflow_runs","workflow_schedules","workflow_webhook_deliveries","workshop_registrations"],ne=["admin_credit_adjustment_events_reference","admin_credit_adjustments_user_created","billing_authorizations_user_status","billing_credits_user_status","billing_debits_user_status","billing_events_authorization","billing_events_user_created_at","concurrency_locks_active_user","concurrency_locks_operation","concurrency_locks_user_status","credit_lots_user_active","credit_lot_repairs_user_applied","harvest_attempts_job_id","harvest_attempts_provider_lookup","harvest_attempts_user_created_at","inbox_messages_user_sent_at","jobs_created_at_status_user","jobs_status","jobs_user_created_at_status","jobs_user_id","kpo_jobs_status","kpo_jobs_user_id","kpo_phase_log_job_id","ledger_stripe_pi_unique","ledger_user_id","oauth_tokens_identity","page_snapshots_user_checked_at","prt_user_id","request_events_created_at","request_events_user_created_at","scheduled_run_view_links_expiry","scheduled_run_view_links_owner_run","serp_identities_user_name","site_audit_jobs_status","site_audit_jobs_user_id","site_audit_phase_log_job_id","site_extract_jobs_status","site_extract_jobs_idempotency","site_extract_jobs_user_id","site_extract_pages_job_id","workflow_artifacts_run","workflow_runs_idempotency","workflow_runs_schedule","workflow_runs_user_created","workflow_schedules_due","workflow_schedules_user_status","workflow_webhook_deliveries_run","workshop_registrations_event_status"],se=["credit_lots_bounds_insert","credit_lots_bounds_update","credit_lots_one_migration_insert","credit_lots_one_migration_update"],ve={credit_lots:["never_expires"],harvest_attempts:["debug_json","browser_provider","provider_session_id","provider_session_observed_at","provider_lookup_status","provider_status","provider_error_code","provider_error_message","provider_reason_source","provider_duration_ms","provider_bandwidth_bytes","provider_captcha","provider_checked_at","provider_detail_json","provider_disconnect_observed","provider_disconnect_message"],jobs:["public_error_json"],oauth_tokens:["replaced_by","rotated_at"],site_extract_jobs:["attempted_urls","successful_urls","failed_urls","idempotency_key","request_fingerprint","dispatch_attempts","next_dispatch_at","dispatch_error","settlement_attempts","next_settlement_at","settlement_error","public_error_json"],stripe_events:["attempt_count","claim_token","last_error","status","updated_at"],users:["balance_mc","concurrency_stripe_sub_id","concurrency_pack_quantity","extra_concurrency_slots","key_active","memory_identity","memory_key","memory_plan","memory_provisioned","memory_subscription_id","password_hash","stripe_customer_id","subscription_concurrency","subscription_id","subscription_tier"],workshop_registrations:["field_notes_email_id","reminder_48h_email_id","reminder_24h_email_id","reminder_1h_email_id"],workflow_artifacts:["content"],workflow_runs:["step_index","step_log_json","step_state_json","total_steps","webhook_url"]};function x(e){return e.map(t=>`'${t.replaceAll("'","''")}'`).join(", ")}async function X(e){let t=[`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN (${x(re)})) = ${re.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name IN (${x(ne)})) = ${ne.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name IN (${x(se)})) = ${se.length}`,...Object.entries(ve).map(([n,i])=>`(SELECT COUNT(*) FROM pragma_table_info('${n}') WHERE name IN (${x(i)})) = ${i.length}`)],r=await e.execute(`SELECT ${t.join(" AND ")} AS ready`);return Number(r.rows[0]?.ready??0)===1}async function Xe(e){await e.execute(`
|
|
1
|
+
import{createClient as Be}from"@libsql/client/http";import{createHash as y,randomBytes as M,randomUUID as g,scryptSync as Te,timingSafeEqual as Ge}from"crypto";import{z as p}from"zod";var C="2026-06-26 21:58:03";var dt="Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.",xe="This operation is temporarily unavailable. Please retry later.",ye=["request_aborted","captcha_exhausted","captcha_or_blocked","location_mismatch","proxy_tunnel_failed","proxy_unavailable","harvest_timeout","mcp_request_timeout","mcp_http_error","extraction_failed","concurrency_limit_exceeded","unauthorized","forbidden","invalid_request","rate_limited","upstream_rate_limited","insufficient_balance","service_unavailable","response_lost","vendor_unavailable","page_not_found","page_forbidden","page_rate_limited","page_server_error","page_http_error","bot_check_unresolved","page_too_large","page_unreachable","browser_session_interrupted","site_export_not_found","site_export_format_unavailable","site_export_read_failed","site_export_image_not_found","image_url_invalid","image_url_not_https","image_host_unresolvable","image_host_blocked","image_redirect_invalid","image_source_unavailable","image_too_large","image_redirect_limit","image_base64_invalid","image_source_missing","image_source_ambiguous","image_source_type_invalid","image_artifact_not_found","image_page_too_large","page_image_not_found","page_image_candidates_unusable","image_format_unsupported","image_storage_unconfigured","featured_image_unhostable"],Ie=["request_aborted","captcha","location_mismatch","connection","timeout","extraction","concurrency_limit","unauthorized","forbidden","invalid_request","rate_limit","billing","service_unavailable"];function R(e){return e?JSON.stringify(e):null}function se(e){if(e==null)return null;try{let t=typeof e=="string"?JSON.parse(e):e;if(!t||typeof t!="object"||Array.isArray(t))return null;let r=t;if(typeof r.error_code!="string"||!ne.has(r.error_code))return null;let s=Pe({errorCode:r.error_code,errorType:r.error_type,retryable:r.retryable,retryAfterSeconds:r.retry_after_seconds,chargeStatus:r.charge_status,details:r.details});if(typeof r.message=="string"){let i=ie(r.message).trim().slice(0,1e3);i&&!/https?:\/\//i.test(i)&&(s.message=i)}return s}catch{return null}}var ne=new Set(ye),Ce=new Set(Ie),ke=new Set(["active","limit","operation","upgrade_url","topup_url","upgrade_command","slots_per_pack","pack_price_usd","balance_credits","required_credits","retry_guidance","http_status"]),Ue=12,De=500;function P(e){return typeof e=="string"&&ne.has(e)?e:"service_unavailable"}function ve(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t={};for(let[r,s]of Object.entries(e)){if(Object.keys(t).length>=Ue)break;if(ke.has(r))if(typeof s=="string"){if((r==="upgrade_url"||r==="topup_url")&&!Xe(s))continue;let i=ie(s).slice(0,De);i&&(t[r]=i)}else(typeof s=="number"&&Number.isFinite(s)||typeof s=="boolean"||s===null)&&(t[r]=s)}return Object.keys(t).length>0?t:void 0}function Xe(e){if(e.startsWith("/")&&!e.startsWith("//"))return!0;try{let t=new URL(e);return t.protocol==="https:"&&(t.hostname==="mcpscraper.dev"||t.hostname.endsWith(".mcpscraper.dev"))}catch{return!1}}function Me(e){let t=P(e);switch(t){case"captcha_exhausted":case"captcha_or_blocked":return"captcha";case"proxy_tunnel_failed":case"proxy_unavailable":case"response_lost":return"connection";case"harvest_timeout":case"mcp_request_timeout":return"timeout";case"concurrency_limit_exceeded":return"concurrency_limit";case"rate_limited":case"upstream_rate_limited":return"rate_limit";case"extraction_failed":case"page_not_found":case"page_forbidden":case"page_rate_limited":case"page_server_error":case"page_http_error":case"bot_check_unresolved":case"page_too_large":case"page_unreachable":case"browser_session_interrupted":case"site_export_not_found":case"site_export_format_unavailable":case"site_export_read_failed":case"site_export_image_not_found":return"extraction";case"image_url_invalid":case"image_url_not_https":case"image_host_unresolvable":case"image_host_blocked":case"image_redirect_invalid":case"image_source_unavailable":case"image_too_large":case"image_redirect_limit":case"image_base64_invalid":case"image_source_missing":case"image_source_ambiguous":case"image_source_type_invalid":case"image_artifact_not_found":case"image_page_too_large":case"page_image_not_found":case"page_image_candidates_unusable":case"image_format_unsupported":case"image_storage_unconfigured":case"featured_image_unhostable":return"invalid_request";case"insufficient_balance":return"billing";case"vendor_unavailable":case"mcp_http_error":return"service_unavailable";default:return t}}function Fe(e){switch(P(e)){case"request_aborted":return"The request was cancelled before it completed.";case"captcha_exhausted":case"captcha_or_blocked":return"The target site returned a verification challenge. Heavy traffic can temporarily increase these challenges; retry in a few minutes.";case"location_mismatch":return"The target site returned results for a different location. Please retry.";case"proxy_tunnel_failed":case"proxy_unavailable":return"The web request could not establish a working connection. Please retry later.";case"harvest_timeout":return"The request timed out before it completed. Please retry with a smaller request.";case"mcp_request_timeout":return"The MCP caller stopped waiting before a final response arrived. Durable work may still be running; continue with the returned Task or job handle instead of restarting it.";case"concurrency_limit_exceeded":return"The service is available, but this account has reached its parallel-operation limit. Wait for an active operation to finish or add capacity.";case"unauthorized":return"Authentication is required to perform this operation.";case"forbidden":return"This account does not have permission to perform this operation.";case"invalid_request":return"The request is invalid. Check the supplied inputs and try again.";case"rate_limited":return"This account is sending requests too quickly. Wait briefly and retry.";case"upstream_rate_limited":return"The connected provider is temporarily rate-limiting requests. Wait briefly and retry.";case"mcp_http_error":return"The API returned an unexpected HTTP response.";case"insufficient_balance":return"This account does not have enough Credits for the operation.";case"extraction_failed":return"The target could not be extracted. Please retry later.";case"response_lost":return"The connection was lost before the response arrived. The operation may have already completed and been billed on our side \u2014 check credits_info before retrying blind.";case"vendor_unavailable":return"MCP Scraper's internal browser services require MCP Scraper team attention. Servers or IPs are down until this is fixed \u2014 this is not caused by your request. Please retry in a few minutes.";case"page_not_found":return"The page could not be found. It may have been moved or deleted.";case"page_forbidden":return"The site refused the request.";case"page_rate_limited":return"The site is rate-limiting requests right now. Retrying after a short wait usually works.";case"page_server_error":return"The page's own server returned an error. This is on the target site's side, not something a retry here can fix.";case"page_http_error":return"The page returned an unexpected HTTP response.";case"bot_check_unresolved":return"This site has automated-traffic protection (a bot/CAPTCHA check) that could not be resolved in time. Some sites are simply not extractable this way.";case"page_too_large":return"The page is larger than we can safely process.";case"page_unreachable":return"The page did not respond in time, or the connection could not be completed.";case"browser_session_interrupted":return"The browser session closed before extraction completed. MCP Scraper will retry once with a fresh session; retry the request if this persists.";case"site_export_not_found":return"The site export or requested page was not found, expired, or does not belong to this account.";case"site_export_format_unavailable":return"That representation was not retained for this export page. Read the export manifest and request one of the formats marked available.";case"site_export_read_failed":return"The stored site export could not be read or did not pass its integrity check.";case"site_export_image_not_found":return"The requested site-export image was not found, expired, or does not belong to this account.";case"image_url_invalid":return"The image source is not a valid URL. Use a stable public HTTPS URL or inline image bytes.";case"image_url_not_https":return"Commons image sources must use a public HTTPS URL or inline image bytes.";case"image_host_unresolvable":return"The image host could not be resolved. Use a different stable public URL or inline image bytes.";case"image_host_blocked":return"The image source resolves to a network address Commons cannot fetch safely.";case"image_redirect_invalid":return"The image source redirected to an invalid or unsafe destination.";case"image_source_unavailable":return"The image source could not be fetched. Host a different stable public URL or provide inline image bytes.";case"image_too_large":return"The image exceeds the Commons image size limit.";case"image_redirect_limit":return"The image source exceeded the Commons redirect limit.";case"image_base64_invalid":return"The supplied inline image bytes are invalid.";case"image_source_missing":return"Provide exactly one of a public image or webpage URL, inline image bytes, or an owner-scoped image artifact.";case"image_source_ambiguous":return"Provide only one image source: sourceUrl, imageBase64, or artifactId.";case"image_source_type_invalid":return"Use sourceType only with inline image bytes or an owner-scoped image artifact.";case"image_artifact_not_found":return"The image artifact was not found, expired, or does not belong to this account.";case"image_page_too_large":return"The webpage is too large for bounded Commons image discovery.";case"page_image_not_found":return"The webpage did not expose a usable featured or content image.";case"page_image_candidates_unusable":return"The webpage exposed image candidates, but none returned supported public image bytes.";case"image_format_unsupported":return"Commons accepts JPEG, PNG, GIF, or WebP image bytes.";case"image_storage_unconfigured":return"Commons image storage is temporarily unavailable.";case"featured_image_unhostable":return"The required featured image could not be hosted for Commons publication.";default:return xe}}function Pe(e){let t=P(e.errorCode),r=typeof e.retryAfterSeconds=="number"&&Number.isFinite(e.retryAfterSeconds)?Math.max(1,Math.min(86400,Math.trunc(e.retryAfterSeconds))):void 0,s=e.chargeStatus==="not_charged"||e.chargeStatus==="refund_pending"||e.chargeStatus==="refunded"||e.chargeStatus==="charged"?e.chargeStatus:void 0,i=typeof e.errorType=="string"&&Ce.has(e.errorType)?e.errorType:void 0,a=ve(e.details);return{error_code:t,error_type:i??Me(t),message:Fe(t),retryable:e.retryable===!0,...r?{retry_after_seconds:r}:{},...s?{charge_status:s}:{},...a?{details:a}:{}}}function ie(e){return e.replace((new RegExp("https?:\\/\\/[^\\s]*(?:\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061|on\u006b\u0065\u0072\u006e\u0065\u006c|\u006b\u0065\u0072\u006e\u0065\u006c\\.sh)[^\\s]*", "gi")),"the service").replace(/bright\s*data/gi,"the service").replace((new RegExp("\u0062\u0072\u0069\u0067\u0068\u0074\u0064\u0061\u0074\u0061", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh\\s+session", "gi")),"this session").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\.sh", "gi")),"the service").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+sessions?", "gi")),"sessions").replace((new RegExp("\u006b\u0065\u0072\u006e\u0065\u006c\\s+session", "gi")),"this session").replace(/\b(?:sub|cus|sess|proxy)_[A-Za-z0-9_-]+\b/g,"[redacted]").replace((new RegExp("\\b(?:\u004b\u0045\u0052\u004e\u0045\u004c|\u0042\u0052\u0049\u0047\u0048\u0054\u0044\u0041\u0054\u0041|\u0042\u0052\u0049\u0047\u0048\u0054_\u0044\u0041\u0054\u0041)_[A-Z0-9_]*API_KEY\\b", "g")),"BROWSER_SERVICE_API_KEY").replace((new RegExp("\\b\u006b\u0065\u0072\u006e\u0065\u006c\\b", "gi")),"the service").replace(/ +/g," ").trim()}var z=class extends Error{constructor(r){super(`CAPTCHA detected. ${r}`);this.instructions=r}instructions;name="CaptchaError"},Q=class extends Error{constructor(r,s){super(r);this.cause=s}cause;name="ExtractionError"},Z=class extends Error{constructor(r){super(`Google Maps search did not reach a complete result state (${r})`);this.pageState=r}pageState;name="MapsSearchIncompletePageError"},ee=class extends Error{name="RequestAbortedError";constructor(t="Request aborted before harvest completed"){super(t)}},te=class extends Error{name="PaaInteractionInterruptedError"},re=class extends Error{name="LocationMismatchError";constructor(t="Google returned results for a different location than requested"){super(t)}};var f=["oauth_store_metadata","oauth_clients","oauth_client_redirect_uris","oauth_codes","oauth_code_exchanges","oauth_refresh_families","oauth_refresh_tokens","oauth_refresh_rotations","oauth_security_events","oauth_rate_limit_buckets"],O=["oauth_clients_inactivity_idx","oauth_clients_cleanup_idx","oauth_codes_expiry_idx","oauth_codes_client_idx","oauth_code_exchanges_replay_expiry_idx","oauth_refresh_families_lifecycle_idx","oauth_refresh_families_user_status_idx","oauth_refresh_families_client_idx","oauth_refresh_families_cleanup_idx","oauth_refresh_tokens_expiry_idx","oauth_refresh_rotations_replay_expiry_idx","oauth_security_events_retention_idx","oauth_rate_limit_buckets_expiry_idx"],ae={oauth_clients:"oauth_clients_legacy_raw",oauth_codes:"oauth_codes_legacy_raw",oauth_tokens:"oauth_tokens_legacy_raw"},b="strftime('%Y-%m-%dT%H:%M:%fZ','now')";async function h(e,t,r=!1){try{await e.execute(t)}catch(s){let i=s.code;if(!r||i!=="SQL_PARSE_ERROR")throw s}}var He=[`CREATE TABLE IF NOT EXISTS oauth_store_metadata (
|
|
2
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
3
|
+
store_id TEXT NOT NULL UNIQUE,
|
|
4
|
+
schema_version INTEGER NOT NULL CHECK (schema_version > 0),
|
|
5
|
+
authority_epoch INTEGER NOT NULL CHECK (authority_epoch > 0),
|
|
6
|
+
authority_state TEXT NOT NULL CHECK (authority_state IN ('ready','authoritative','retired')),
|
|
7
|
+
updated_at TEXT NOT NULL DEFAULT (${b})
|
|
8
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
9
|
+
client_id TEXT PRIMARY KEY,
|
|
10
|
+
client_name TEXT NOT NULL,
|
|
11
|
+
client_type TEXT NOT NULL CHECK (client_type = 'public'),
|
|
12
|
+
token_endpoint_auth_method TEXT NOT NULL CHECK (token_endpoint_auth_method = 'none'),
|
|
13
|
+
status TEXT NOT NULL CHECK (status IN ('active','disabled','expired')),
|
|
14
|
+
created_at TEXT NOT NULL DEFAULT (${b}),
|
|
15
|
+
last_used_at TEXT,
|
|
16
|
+
expires_at TEXT
|
|
17
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_client_redirect_uris (
|
|
18
|
+
client_id TEXT NOT NULL REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
|
|
19
|
+
redirect_uri TEXT NOT NULL,
|
|
20
|
+
redirect_kind TEXT NOT NULL CHECK (redirect_kind IN ('https','loopback','private_scheme')),
|
|
21
|
+
PRIMARY KEY (client_id, redirect_uri)
|
|
22
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_codes (
|
|
23
|
+
code_digest BLOB PRIMARY KEY,
|
|
24
|
+
lookup_key_id TEXT NOT NULL,
|
|
25
|
+
client_id TEXT NOT NULL REFERENCES oauth_clients(client_id) ON DELETE RESTRICT,
|
|
26
|
+
redirect_uri TEXT NOT NULL,
|
|
27
|
+
code_challenge TEXT NOT NULL,
|
|
28
|
+
challenge_method TEXT NOT NULL CHECK (challenge_method = 'S256'),
|
|
29
|
+
user_id TEXT NOT NULL,
|
|
30
|
+
scope TEXT NOT NULL,
|
|
31
|
+
resource TEXT,
|
|
32
|
+
issued_at TEXT NOT NULL DEFAULT (${b}),
|
|
33
|
+
expires_at TEXT NOT NULL
|
|
34
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_code_exchanges (
|
|
35
|
+
code_digest BLOB PRIMARY KEY,
|
|
36
|
+
request_fingerprint BLOB NOT NULL UNIQUE,
|
|
37
|
+
client_id TEXT NOT NULL,
|
|
38
|
+
family_id TEXT NOT NULL UNIQUE,
|
|
39
|
+
grant_scope TEXT NOT NULL,
|
|
40
|
+
grant_resource TEXT,
|
|
41
|
+
scope TEXT NOT NULL,
|
|
42
|
+
resource TEXT,
|
|
43
|
+
sealed_refresh BLOB NOT NULL,
|
|
44
|
+
sealed_nonce BLOB NOT NULL,
|
|
45
|
+
sealed_tag BLOB NOT NULL,
|
|
46
|
+
replay_key_id TEXT NOT NULL,
|
|
47
|
+
completed_at TEXT NOT NULL DEFAULT (${b}),
|
|
48
|
+
replay_expires_at TEXT NOT NULL
|
|
49
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_refresh_families (
|
|
50
|
+
family_id TEXT PRIMARY KEY,
|
|
51
|
+
user_id TEXT NOT NULL,
|
|
52
|
+
client_id TEXT NOT NULL REFERENCES oauth_clients(client_id) ON DELETE RESTRICT,
|
|
53
|
+
issuer TEXT NOT NULL,
|
|
54
|
+
scope TEXT NOT NULL,
|
|
55
|
+
resource TEXT,
|
|
56
|
+
status TEXT NOT NULL CHECK (status IN ('active','revoked','compromised','expired')),
|
|
57
|
+
current_generation INTEGER NOT NULL CHECK (current_generation > 0),
|
|
58
|
+
created_at TEXT NOT NULL DEFAULT (${b}),
|
|
59
|
+
expires_at TEXT NOT NULL,
|
|
60
|
+
revoked_at TEXT,
|
|
61
|
+
compromised_at TEXT,
|
|
62
|
+
UNIQUE (family_id, client_id),
|
|
63
|
+
FOREIGN KEY (family_id, current_generation)
|
|
64
|
+
REFERENCES oauth_refresh_tokens(family_id, generation)
|
|
65
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
66
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
|
|
67
|
+
family_id TEXT NOT NULL REFERENCES oauth_refresh_families(family_id) ON DELETE CASCADE,
|
|
68
|
+
generation INTEGER NOT NULL CHECK (generation > 0),
|
|
69
|
+
token_digest BLOB NOT NULL UNIQUE,
|
|
70
|
+
lookup_key_id TEXT NOT NULL,
|
|
71
|
+
token_prefix TEXT NOT NULL,
|
|
72
|
+
predecessor_generation INTEGER,
|
|
73
|
+
issued_at TEXT NOT NULL DEFAULT (${b}),
|
|
74
|
+
expires_at TEXT NOT NULL,
|
|
75
|
+
used_at TEXT,
|
|
76
|
+
revoked_at TEXT,
|
|
77
|
+
PRIMARY KEY (family_id, generation),
|
|
78
|
+
CHECK (predecessor_generation IS NULL OR (predecessor_generation > 0 AND predecessor_generation < generation))
|
|
79
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_refresh_rotations (
|
|
80
|
+
family_id TEXT NOT NULL REFERENCES oauth_refresh_families(family_id) ON DELETE CASCADE,
|
|
81
|
+
predecessor_generation INTEGER NOT NULL,
|
|
82
|
+
request_fingerprint BLOB NOT NULL,
|
|
83
|
+
successor_generation INTEGER NOT NULL,
|
|
84
|
+
sealed_successor BLOB NOT NULL,
|
|
85
|
+
sealed_nonce BLOB NOT NULL,
|
|
86
|
+
sealed_tag BLOB NOT NULL,
|
|
87
|
+
replay_key_id TEXT NOT NULL,
|
|
88
|
+
rotated_at TEXT NOT NULL DEFAULT (${b}),
|
|
89
|
+
replay_expires_at TEXT NOT NULL,
|
|
90
|
+
PRIMARY KEY (family_id, predecessor_generation),
|
|
91
|
+
UNIQUE (family_id, successor_generation),
|
|
92
|
+
CHECK (successor_generation = predecessor_generation + 1)
|
|
93
|
+
) STRICT`,`CREATE TABLE IF NOT EXISTS oauth_security_events (
|
|
94
|
+
event_id TEXT PRIMARY KEY,
|
|
95
|
+
family_id TEXT NOT NULL,
|
|
96
|
+
event_type TEXT NOT NULL CHECK (event_type IN ('reuse','binding_mismatch','manual_revoke','expiry')),
|
|
97
|
+
reason_code TEXT NOT NULL,
|
|
98
|
+
occurred_at TEXT NOT NULL DEFAULT (${b}),
|
|
99
|
+
retain_until TEXT NOT NULL
|
|
100
|
+
) STRICT`,"CREATE INDEX IF NOT EXISTS oauth_clients_inactivity_idx ON oauth_clients(status, last_used_at, client_id)","CREATE INDEX IF NOT EXISTS oauth_codes_expiry_idx ON oauth_codes(expires_at, code_digest)","CREATE INDEX IF NOT EXISTS oauth_code_exchanges_replay_expiry_idx ON oauth_code_exchanges(replay_expires_at, code_digest)","CREATE INDEX IF NOT EXISTS oauth_refresh_families_lifecycle_idx ON oauth_refresh_families(status, expires_at, family_id)","CREATE INDEX IF NOT EXISTS oauth_refresh_families_user_status_idx ON oauth_refresh_families(user_id, status)","CREATE INDEX IF NOT EXISTS oauth_refresh_tokens_expiry_idx ON oauth_refresh_tokens(expires_at, family_id, generation)","CREATE INDEX IF NOT EXISTS oauth_refresh_rotations_replay_expiry_idx ON oauth_refresh_rotations(replay_expires_at, family_id, predecessor_generation)","CREATE INDEX IF NOT EXISTS oauth_security_events_retention_idx ON oauth_security_events(retain_until, event_id)"],oe=["CREATE INDEX IF NOT EXISTS oauth_clients_cleanup_idx ON oauth_clients(COALESCE(last_used_at,created_at), client_id)","CREATE INDEX IF NOT EXISTS oauth_codes_client_idx ON oauth_codes(client_id, code_digest)","CREATE INDEX IF NOT EXISTS oauth_refresh_families_client_idx ON oauth_refresh_families(client_id, family_id)",`CREATE INDEX IF NOT EXISTS oauth_refresh_families_cleanup_idx
|
|
101
|
+
ON oauth_refresh_families(COALESCE(compromised_at,revoked_at,expires_at), family_id)
|
|
102
|
+
WHERE status!='active'`],H=[`CREATE TABLE IF NOT EXISTS oauth_rate_limit_buckets (
|
|
103
|
+
scope TEXT NOT NULL CHECK (length(scope) BETWEEN 1 AND 80),
|
|
104
|
+
key_digest BLOB NOT NULL CHECK (typeof(key_digest) = 'blob' AND length(key_digest) = 32),
|
|
105
|
+
window_start INTEGER NOT NULL CHECK (window_start >= 0),
|
|
106
|
+
window_seconds INTEGER NOT NULL CHECK (window_seconds BETWEEN 1 AND 604800),
|
|
107
|
+
count INTEGER NOT NULL CHECK (count >= 1),
|
|
108
|
+
expires_at INTEGER NOT NULL CHECK (expires_at = window_start + window_seconds),
|
|
109
|
+
updated_at TEXT NOT NULL DEFAULT (${b}),
|
|
110
|
+
PRIMARY KEY (scope, key_digest, window_start, window_seconds)
|
|
111
|
+
) STRICT`,`CREATE INDEX IF NOT EXISTS oauth_rate_limit_buckets_expiry_idx
|
|
112
|
+
ON oauth_rate_limit_buckets(expires_at, scope, key_digest, window_start, window_seconds)`],k=[...He,...oe,...H];async function qe(e){if(await h(e,"PRAGMA foreign_keys = ON"),await h(e,"PRAGMA busy_timeout = 5000",!0),(await e.execute("SELECT name FROM pragma_table_info('oauth_codes')")).rows.some(u=>String(u.name)==="code"))throw new Error("Legacy raw OAuth tables require the explicit maintenance-mode coexistence migration");let s=(await e.execute("SELECT 1 present FROM sqlite_master WHERE type='table' AND name='oauth_store_metadata'")).rows.length>0?await e.execute("SELECT schema_version FROM oauth_store_metadata WHERE id=1"):void 0,i=s?.rows[0]?.schema_version==null?void 0:Number(s.rows[0].schema_version);if(i!==void 0&&(!Number.isSafeInteger(i)||i<1||i>3))throw new Error("OAuth store schema version cannot be migrated by this runtime");let a=i===void 0?k:i===1?[...oe,...H]:i===2?H:[];for(let u of a)await e.execute(u);if(i!==void 0&&i<3&&(await e.execute({sql:`UPDATE oauth_store_metadata SET schema_version=?,updated_at=${b} WHERE id=1 AND schema_version=?`,args:[3,i]})).rowsAffected!==1)throw new Error("OAuth store schema version changed during migration");await h(e,"PRAGMA optimize",!0)}async function ue(e){await h(e,"PRAGMA foreign_keys = ON"),await h(e,"PRAGMA busy_timeout = 5000",!0);let t=await e.execute("SELECT name FROM pragma_table_info('oauth_codes')");if(!t.rows.some(a=>String(a.name)==="code")){let a=t.rows.some(u=>String(u.name)==="code_digest");return await qe(e),a?"current":"created"}if((await e.execute({sql:"SELECT name FROM sqlite_master WHERE type='table' AND name IN (?,?,?)",args:Object.values(ae)})).rows.length>0)throw new Error("Legacy OAuth archive names already exist; refusing ambiguous migration");let i=await e.transaction("write");try{for(let[a,u]of Object.entries(ae))(await i.execute({sql:"SELECT 1 present FROM sqlite_master WHERE type='table' AND name=?",args:[a]})).rows.length>0&&await i.execute({sql:`ALTER TABLE ${a} RENAME TO ${u}`,args:[]});for(let a of k)await i.execute({sql:a,args:[]});await i.commit()}finally{i.close()}return"archived_legacy"}var We={oauth_store_metadata:["id","store_id","schema_version","authority_epoch","authority_state","updated_at"],oauth_clients:["client_id","client_name","client_type","token_endpoint_auth_method","status","created_at","last_used_at","expires_at"],oauth_client_redirect_uris:["client_id","redirect_uri","redirect_kind"],oauth_codes:["code_digest","lookup_key_id","client_id","redirect_uri","code_challenge","challenge_method","user_id","scope","resource","issued_at","expires_at"],oauth_code_exchanges:["code_digest","request_fingerprint","client_id","family_id","grant_scope","grant_resource","scope","resource","sealed_refresh","sealed_nonce","sealed_tag","replay_key_id","completed_at","replay_expires_at"],oauth_refresh_families:["family_id","user_id","client_id","issuer","scope","resource","status","current_generation","created_at","expires_at","revoked_at","compromised_at"],oauth_refresh_tokens:["family_id","generation","token_digest","lookup_key_id","token_prefix","predecessor_generation","issued_at","expires_at","used_at","revoked_at"],oauth_refresh_rotations:["family_id","predecessor_generation","request_fingerprint","successor_generation","sealed_successor","sealed_nonce","sealed_tag","replay_key_id","rotated_at","replay_expires_at"],oauth_security_events:["event_id","family_id","event_type","reason_code","occurred_at","retain_until"],oauth_rate_limit_buckets:["scope","key_digest","window_start","window_seconds","count","expires_at","updated_at"]},je={oauth_clients_inactivity_idx:["status","last_used_at","client_id"],oauth_clients_cleanup_idx:[null,"client_id"],oauth_codes_expiry_idx:["expires_at","code_digest"],oauth_codes_client_idx:["client_id","code_digest"],oauth_code_exchanges_replay_expiry_idx:["replay_expires_at","code_digest"],oauth_refresh_families_lifecycle_idx:["status","expires_at","family_id"],oauth_refresh_families_user_status_idx:["user_id","status"],oauth_refresh_families_client_idx:["client_id","family_id"],oauth_refresh_families_cleanup_idx:[null,"family_id"],oauth_refresh_tokens_expiry_idx:["expires_at","family_id","generation"],oauth_refresh_rotations_replay_expiry_idx:["replay_expires_at","family_id","predecessor_generation"],oauth_security_events_retention_idx:["retain_until","event_id"],oauth_rate_limit_buckets_expiry_idx:["expires_at","scope","key_digest","window_start","window_seconds"]};async function ce(e){let t=await e.execute({sql:`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${f.map(()=>"?").join(",")})`,args:[...f]}),r=await e.execute({sql:`SELECT name FROM sqlite_master WHERE type = 'index' AND name IN (${O.map(()=>"?").join(",")})`,args:[...O]}),s=new Set(t.rows.map(o=>String(o.name))),i=new Set(r.rows.map(o=>String(o.name))),a=await e.execute("PRAGMA foreign_keys"),u=[],c=o=>o.replace(/\bIF\s+NOT\s+EXISTS\b/gi,"").replace(/\s+/g," ").trim().toLowerCase();for(let o of f){if(!s.has(o))continue;let d=(await e.execute(`PRAGMA table_info('${o}')`)).rows.map(N=>String(N.name));JSON.stringify(d)!==JSON.stringify(We[o])&&u.push(`${o}:columns`);let E=await e.execute({sql:"SELECT sql FROM sqlite_master WHERE type='table' AND name=?",args:[o]}),l=String(E.rows[0]?.sql??""),m=k.find(N=>c(N).startsWith(`create table ${o} `));(!m||c(l)!==c(m))&&u.push(`${o}:definition`)}for(let o of O){if(!i.has(o))continue;let d=(await e.execute(`PRAGMA index_info('${o}')`)).rows.map(N=>N.name==null?null:String(N.name));JSON.stringify(d)!==JSON.stringify(je[o])&&u.push(`${o}:columns`);let E=await e.execute({sql:"SELECT sql FROM sqlite_master WHERE type='index' AND name=?",args:[o]}),l=String(E.rows[0]?.sql??""),m=k.find(N=>c(N).startsWith(`create index ${o} `));(!m||c(l)!==c(m))&&u.push(`${o}:definition`)}return{ok:f.every(o=>s.has(o))&&O.every(o=>i.has(o))&&Number(a.rows[0]?.foreign_keys??0)===1&&u.length===0,missingTables:f.filter(o=>!s.has(o)),missingIndexes:O.filter(o=>!i.has(o)),foreignKeysEnabled:Number(a.rows[0]?.foreign_keys??0)===1,problems:u}}var Ye=process.env.TURSO_DATABASE_URL??"file:./paa-api.db",Je=process.env.TURSO_AUTH_TOKEN,D=null,q="2026-08-27.1",_e="data-2026-07-28-site-extract-counters.1",de=["admin_credit_adjustment_events","admin_credit_adjustments","billing_authorizations","billing_credits","billing_debits","billing_debit_allocations","billing_events","browser_profile_labels","connected_account_billing","concurrency_locks","credit_lot_expirations","credit_lots","credit_lot_repairs","free_credit_refreshes","harvest_attempts","inbox_messages","jobs","kpo_jobs","kpo_phase_log","ledger","oauth_clients","oauth_codes","oauth_tokens","page_snapshots","password_reset_tokens","rate_limits","request_events","scheduled_run_view_links","serp_identities","site_audit_jobs","site_audit_phase_log","site_extract_jobs","site_extract_pages","stripe_events","users","workflow_artifacts","workflow_runs","workflow_schedules","workflow_webhook_deliveries","workshop_registrations"],le=["admin_credit_adjustment_events_reference","admin_credit_adjustments_user_created","billing_authorizations_user_status","billing_credits_user_status","billing_debits_user_status","billing_events_authorization","billing_events_user_created_at","concurrency_locks_active_user","concurrency_locks_operation","concurrency_locks_user_status","credit_lots_user_active","credit_lot_repairs_user_applied","harvest_attempts_job_id","harvest_attempts_provider_lookup","harvest_attempts_user_created_at","inbox_messages_user_sent_at","jobs_created_at_status_user","jobs_status","jobs_user_created_at_status","jobs_user_id","kpo_jobs_status","kpo_jobs_user_id","kpo_phase_log_job_id","ledger_stripe_pi_unique","ledger_user_id","oauth_tokens_identity","page_snapshots_user_checked_at","prt_user_id","request_events_created_at","request_events_user_created_at","scheduled_run_view_links_expiry","scheduled_run_view_links_owner_run","serp_identities_user_name","site_audit_jobs_status","site_audit_jobs_user_id","site_audit_phase_log_job_id","site_extract_jobs_status","site_extract_jobs_idempotency","site_extract_jobs_user_id","site_extract_pages_job_id","workflow_artifacts_run","workflow_runs_idempotency","workflow_runs_schedule","workflow_runs_user_created","workflow_schedules_due","workflow_schedules_user_status","workflow_webhook_deliveries_run","workshop_registrations_event_status"],Ee=["credit_lots_bounds_insert","credit_lots_bounds_update","credit_lots_one_migration_insert","credit_lots_one_migration_update"],$e={credit_lots:["never_expires"],harvest_attempts:["debug_json","browser_provider","provider_session_id","provider_session_observed_at","provider_lookup_status","provider_status","provider_error_code","provider_error_message","provider_reason_source","provider_duration_ms","provider_bandwidth_bytes","provider_captcha","provider_checked_at","provider_detail_json","provider_disconnect_observed","provider_disconnect_message"],jobs:["public_error_json"],oauth_tokens:["replaced_by","rotated_at"],site_extract_jobs:["attempted_urls","successful_urls","failed_urls","idempotency_key","request_fingerprint","dispatch_attempts","next_dispatch_at","dispatch_error","settlement_attempts","next_settlement_at","settlement_error","public_error_json"],stripe_events:["attempt_count","claim_token","last_error","status","updated_at"],users:["balance_mc","concurrency_stripe_sub_id","concurrency_pack_quantity","extra_concurrency_slots","key_active","memory_identity","memory_key","memory_plan","memory_provisioned","memory_subscription_id","password_hash","stripe_customer_id","subscription_concurrency","subscription_id","subscription_tier"],workshop_registrations:["field_notes_email_id","reminder_48h_email_id","reminder_24h_email_id","reminder_1h_email_id"],workflow_artifacts:["content"],workflow_runs:["step_index","step_log_json","step_state_json","total_steps","webhook_url"]};function U(e){return e.map(t=>`'${t.replaceAll("'","''")}'`).join(", ")}async function W(e){let t=[`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN (${U(de)})) = ${de.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name IN (${U(le)})) = ${le.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name IN (${U(Ee)})) = ${Ee.length}`,...Object.entries($e).map(([s,i])=>`(SELECT COUNT(*) FROM pragma_table_info('${s}') WHERE name IN (${U(i)})) = ${i.length}`)],r=await e.execute(`SELECT ${t.join(" AND ")} AS ready`);return Number(r.rows[0]?.ready??0)===1}async function Ke(e){await e.execute(`
|
|
2
113
|
UPDATE site_extract_jobs SET
|
|
3
114
|
attempted_urls = done_urls,
|
|
4
115
|
successful_urls = (
|
|
@@ -35,12 +146,12 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
35
146
|
AND attempted_urls = 0
|
|
36
147
|
AND successful_urls = 0
|
|
37
148
|
AND failed_urls = 0
|
|
38
|
-
`)}async function
|
|
149
|
+
`)}async function j(e){(await e.execute({sql:"SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",args:[_e]})).rows.length>0||(await Ke(e),await e.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[_e]}))}function n(){return D||(D=Be({url:Ye,authToken:Je})),D}function Lt(e){D=e}async function St(e=n()){if(process.env.OAUTH_MAINTENANCE_MODE!=="true")throw new Error("Secured main OAuth schema migration requires OAUTH_MAINTENANCE_MODE=true");await ue(e)}async function Rt(e=n()){return ce(e)}async function At(e={}){let t=n(),r;try{r=await t.execute({sql:"SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",args:[q]})}catch(s){let i=s instanceof Error?s.message.toLowerCase():String(s).toLowerCase();if(!i.includes("no such table")||!i.includes("schema_migrations"))throw s;await t.execute(`
|
|
39
150
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
40
151
|
version TEXT PRIMARY KEY,
|
|
41
152
|
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42
153
|
)
|
|
43
|
-
`),r={rows:[]}}if(r.rows.length>0){if(!e.verifyCurrentSchema)return;if(await
|
|
154
|
+
`),r={rows:[]}}if(r.rows.length>0){if(!e.verifyCurrentSchema)return;if(await W(t)){e.runDataRepairs&&await j(t);return}}if(await W(t)){await j(t),await t.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[q]});return}await t.execute(`
|
|
44
155
|
CREATE TABLE IF NOT EXISTS users (
|
|
45
156
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
157
|
email TEXT UNIQUE NOT NULL,
|
|
@@ -107,7 +218,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
107
218
|
completed_at TEXT,
|
|
108
219
|
UNIQUE(job_id, attempt_number)
|
|
109
220
|
)
|
|
110
|
-
`),await t.execute("CREATE INDEX IF NOT EXISTS harvest_attempts_job_id ON harvest_attempts(job_id)"),await t.execute("CREATE INDEX IF NOT EXISTS harvest_attempts_user_created_at ON harvest_attempts(user_id, created_at DESC)");try{await t.execute("ALTER TABLE harvest_attempts ADD COLUMN debug_json TEXT")}catch{}for(let
|
|
221
|
+
`),await t.execute("CREATE INDEX IF NOT EXISTS harvest_attempts_job_id ON harvest_attempts(job_id)"),await t.execute("CREATE INDEX IF NOT EXISTS harvest_attempts_user_created_at ON harvest_attempts(user_id, created_at DESC)");try{await t.execute("ALTER TABLE harvest_attempts ADD COLUMN debug_json TEXT")}catch{}for(let s of["browser_provider TEXT","provider_session_id TEXT","provider_session_observed_at TEXT","provider_lookup_status TEXT","provider_status TEXT","provider_error_code TEXT","provider_error_message TEXT","provider_reason_source TEXT","provider_duration_ms INTEGER","provider_bandwidth_bytes INTEGER","provider_captcha INTEGER","provider_checked_at TEXT","provider_detail_json TEXT","provider_disconnect_observed INTEGER","provider_disconnect_message TEXT"])try{await t.execute(`ALTER TABLE harvest_attempts ADD COLUMN ${s}`)}catch{}await t.execute("CREATE INDEX IF NOT EXISTS harvest_attempts_provider_lookup ON harvest_attempts(browser_provider, provider_lookup_status, provider_checked_at)");try{await t.execute("ALTER TABLE users ADD COLUMN password_hash TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN key_active INTEGER NOT NULL DEFAULT 1")}catch{}await t.execute(`
|
|
111
222
|
CREATE TABLE IF NOT EXISTS kpo_jobs (
|
|
112
223
|
id TEXT PRIMARY KEY,
|
|
113
224
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
@@ -538,7 +649,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
538
649
|
processed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
539
650
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
540
651
|
)
|
|
541
|
-
`);for(let
|
|
652
|
+
`);for(let s of["status TEXT NOT NULL DEFAULT 'processed'","attempt_count INTEGER NOT NULL DEFAULT 1","claim_token TEXT","last_error TEXT","updated_at TEXT"])try{await t.execute(`ALTER TABLE stripe_events ADD COLUMN ${s}`)}catch{}await t.execute(`
|
|
542
653
|
UPDATE stripe_events
|
|
543
654
|
SET updated_at = COALESCE(updated_at, processed_at),
|
|
544
655
|
status = COALESCE(status, 'processed'),
|
|
@@ -577,7 +688,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
577
688
|
revoked INTEGER NOT NULL DEFAULT 0,
|
|
578
689
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
579
690
|
)
|
|
580
|
-
`),await t.execute("CREATE INDEX IF NOT EXISTS oauth_tokens_identity ON oauth_tokens(identity)");for(let
|
|
691
|
+
`),await t.execute("CREATE INDEX IF NOT EXISTS oauth_tokens_identity ON oauth_tokens(identity)");for(let s of["replaced_by TEXT","rotated_at TEXT"])try{await t.execute(`ALTER TABLE oauth_tokens ADD COLUMN ${s}`)}catch{}await t.execute(`
|
|
581
692
|
CREATE TABLE IF NOT EXISTS page_snapshots (
|
|
582
693
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
583
694
|
url TEXT NOT NULL,
|
|
@@ -621,7 +732,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
621
732
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
622
733
|
UNIQUE(event_slug, email)
|
|
623
734
|
)
|
|
624
|
-
`);for(let
|
|
735
|
+
`);for(let s of["field_notes_email_id TEXT","reminder_48h_email_id TEXT","reminder_24h_email_id TEXT","reminder_1h_email_id TEXT"])try{await t.execute(`ALTER TABLE workshop_registrations ADD COLUMN ${s}`)}catch{}if(await t.execute(`
|
|
625
736
|
CREATE INDEX IF NOT EXISTS workshop_registrations_event_status
|
|
626
737
|
ON workshop_registrations(event_slug, status, registered_at)
|
|
627
738
|
`),await t.execute(`
|
|
@@ -651,7 +762,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
651
762
|
`),await t.execute(`
|
|
652
763
|
CREATE UNIQUE INDEX IF NOT EXISTS serp_identities_user_name
|
|
653
764
|
ON serp_identities(user_id, name)
|
|
654
|
-
`),!await
|
|
765
|
+
`),!await W(t))throw new Error("core schema migration finished without all required tables, indexes, and columns");await j(t),await t.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[q]})}async function wt(e,t,r){await n().execute({sql:"INSERT INTO oauth_clients (client_id, redirect_uris, name) VALUES (?, ?, ?)",args:[e,JSON.stringify(t),r]})}async function ft(e){let r=(await n().execute({sql:"SELECT * FROM oauth_clients WHERE client_id = ?",args:[e]})).rows[0];if(!r)return;let s=[];try{let i=JSON.parse(String(r.redirect_uris));Array.isArray(i)&&(s=i.filter(a=>typeof a=="string"))}catch{s=[]}return{client_id:String(r.client_id),redirect_uris:s,name:r.name!=null?String(r.name):null,created_at:String(r.created_at)}}async function Ot(e){await n().execute({sql:"INSERT INTO oauth_codes (code, client_id, redirect_uri, code_challenge, identity, scope, resource, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",args:[e.code,e.client_id,e.redirect_uri,e.code_challenge,e.identity,e.scope,e.resource,e.expires_at]})}async function ht(e){let t=n(),s=(await t.execute({sql:"SELECT * FROM oauth_codes WHERE code = ?",args:[e]})).rows[0];if(await t.execute({sql:"DELETE FROM oauth_codes WHERE code = ?",args:[e]}),!!s)return{code:String(s.code),client_id:String(s.client_id),redirect_uri:String(s.redirect_uri),code_challenge:String(s.code_challenge),identity:String(s.identity),scope:String(s.scope),resource:s.resource!=null?String(s.resource):null,expires_at:String(s.expires_at)}}async function Ve(e){await n().execute({sql:"INSERT INTO oauth_tokens (refresh_token, client_id, identity, scope, resource, expires_at, revoked) VALUES (?, ?, ?, ?, ?, ?, 0)",args:[e.refresh_token,e.client_id,e.identity,e.scope,e.resource,e.expires_at]})}async function xt(e){let r=(await n().execute({sql:"SELECT * FROM oauth_tokens WHERE refresh_token = ?",args:[e]})).rows[0];if(r)return{refresh_token:String(r.refresh_token),client_id:String(r.client_id),identity:String(r.identity),scope:String(r.scope),resource:r.resource!=null?String(r.resource):null,expires_at:String(r.expires_at),revoked:Number(r.revoked??0),replaced_by:r.replaced_by!=null?String(r.replaced_by):null,rotated_at:r.rotated_at!=null?String(r.rotated_at):null}}async function yt(e){await n().execute({sql:"UPDATE oauth_tokens SET revoked = 1 WHERE refresh_token = ?",args:[e]})}async function It(e,t){await Ve(t),await n().execute({sql:"UPDATE oauth_tokens SET revoked = 1, replaced_by = ?, rotated_at = ? WHERE refresh_token = ?",args:[t.refresh_token,new Date().toISOString(),e]})}async function Ct(e){let t=g();if((await n().execute({sql:`INSERT INTO stripe_events
|
|
655
766
|
(event_id, status, attempt_count, claim_token, last_error, processed_at, updated_at)
|
|
656
767
|
VALUES (?, 'processing', 1, ?, NULL, datetime('now'), datetime('now'))
|
|
657
768
|
ON CONFLICT(event_id) DO UPDATE SET
|
|
@@ -664,40 +775,32 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
664
775
|
OR (
|
|
665
776
|
stripe_events.status = 'processing'
|
|
666
777
|
AND stripe_events.updated_at <= datetime('now', '-10 minutes')
|
|
667
|
-
)`,args:[e,t]})).rowsAffected>0)return{state:"claimed",token:t};let
|
|
778
|
+
)`,args:[e,t]})).rowsAffected>0)return{state:"claimed",token:t};let s=await n().execute({sql:"SELECT status FROM stripe_events WHERE event_id = ? LIMIT 1",args:[e]});return{state:String(s.rows[0]?.status)==="processed"?"processed":"processing"}}async function kt(e,t){return(await n().execute({sql:`UPDATE stripe_events
|
|
668
779
|
SET status = 'processed',
|
|
669
780
|
claim_token = NULL,
|
|
670
781
|
last_error = NULL,
|
|
671
782
|
processed_at = datetime('now'),
|
|
672
783
|
updated_at = datetime('now')
|
|
673
|
-
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[e,t]})).rowsAffected===1}async function
|
|
784
|
+
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[e,t]})).rowsAffected===1}async function Ut(e,t,r){return(await n().execute({sql:`UPDATE stripe_events
|
|
674
785
|
SET status = 'failed',
|
|
675
786
|
claim_token = NULL,
|
|
676
787
|
last_error = ?,
|
|
677
788
|
updated_at = datetime('now')
|
|
678
|
-
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[r.slice(0,500),e,t]})).rowsAffected===1}async function
|
|
679
|
-
CREATE TABLE IF NOT EXISTS rate_limits (
|
|
680
|
-
scope TEXT NOT NULL,
|
|
681
|
-
key_hash TEXT NOT NULL,
|
|
682
|
-
window_start INTEGER NOT NULL,
|
|
683
|
-
count INTEGER NOT NULL DEFAULT 0,
|
|
684
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
685
|
-
PRIMARY KEY (scope, key_hash, window_start)
|
|
686
|
-
)
|
|
687
|
-
`),ee=!0),await u.execute({sql:`
|
|
789
|
+
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[r.slice(0,500),e,t]})).rowsAffected===1}async function Dt(e){return(await n().execute({sql:"SELECT 1 FROM stripe_events WHERE event_id = ? AND status = 'processed' LIMIT 1",args:[e]})).rows.length>0}function pe(){return"sk_"+M(24).toString("hex")}function me(e){let t=M(16).toString("hex"),r=Te(e,t,64);return`${t}:${r.toString("hex")}`}function vt(e,t){let[r,s]=t.split(":"),i=Buffer.from(s,"hex"),a=Te(e,r,64);return Ge(i,a)}function L(e){return{id:Number(e.id),email:String(e.email),name:e.name!=null?String(e.name):null,api_key:String(e.api_key),key_active:Number(e.key_active??1),password_hash:e.password_hash!=null?String(e.password_hash):null,created_at:String(e.created_at),active:Number(e.active),stripe_customer_id:e.stripe_customer_id!=null?String(e.stripe_customer_id):null,balance_mc:Number(e.balance_mc??0),extra_concurrency_slots:Number(e.extra_concurrency_slots??0),concurrency_stripe_sub_id:e.concurrency_stripe_sub_id!=null?String(e.concurrency_stripe_sub_id):null,concurrency_pack_quantity:Number(e.concurrency_pack_quantity??0),subscription_concurrency:Number(e.subscription_concurrency??0),subscription_tier:e.subscription_tier!=null?String(e.subscription_tier):null,subscription_id:e.subscription_id!=null?String(e.subscription_id):null,memory_plan:e.memory_plan!=null?String(e.memory_plan):"free",memory_subscription_id:e.memory_subscription_id!=null?String(e.memory_subscription_id):null,memory_provisioned:Number(e.memory_provisioned??0)}}async function Xt(e){let t=await n().execute({sql:"SELECT * FROM users WHERE email = ? AND active = 1",args:[e]});return t.rows[0]?L(t.rows[0]):void 0}async function Mt(e){let t=await n().execute({sql:"SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1",args:[e]});return t.rows[0]?L(t.rows[0]):void 0}async function Ft(e){let t=await n().execute({sql:"SELECT * FROM users WHERE id = ? AND active = 1",args:[e]});return t.rows[0]?L(t.rows[0]):void 0}async function Pt(e){let r=(await n().execute({sql:"SELECT memory_key, memory_identity FROM users WHERE id = ?",args:[e]})).rows[0];return{memory_key:r?.memory_key!=null?String(r.memory_key):null,memory_identity:r?.memory_identity!=null?String(r.memory_identity):null}}async function Ht(e,t,r){await n().execute({sql:"UPDATE users SET memory_key = ?, memory_identity = ? WHERE id = ?",args:[t,r,e]})}async function qt(e,t){await n().execute({sql:"UPDATE users SET password_hash = ? WHERE id = ?",args:[me(t),e]})}async function Wt(e){let t=n(),[r,s,i,a,u]=await Promise.all([t.execute({sql:"SELECT COUNT(*) as n FROM jobs WHERE user_id = ?",args:[e]}),t.execute({sql:"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'done'",args:[e]}),t.execute({sql:"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'failed'",args:[e]}),t.execute({sql:"SELECT COALESCE(SUM(json_extract(result, '$.totalQuestions')), 0) as n FROM jobs WHERE user_id = ? AND status = 'done'",args:[e]}),t.execute({sql:"SELECT MAX(created_at) as t FROM jobs WHERE user_id = ?",args:[e]})]);return{total:Number(r.rows[0]?.n??0),done:Number(s.rows[0]?.n??0),failed:Number(i.rows[0]?.n??0),totalQuestions:Number(a.rows[0]?.n??0),lastUsed:u.rows[0]?.t!=null?String(u.rows[0].t):null}}async function jt(e,t,r,s){let i=n(),a=pe(),u=r??M(6).toString("hex"),c=me(u);return{id:(await i.execute({sql:"INSERT INTO users (email, name, api_key, password_hash, stripe_customer_id) VALUES (?, ?, ?, ?, ?)",args:[e,t??null,a,c,s??null]})).lastInsertRowid,email:e,name:t,api_key:a,password:u}}async function Bt(e,t,r,s){let i=Math.floor(Date.now()/1e3),a=Math.floor(i/s)*s,u=y("sha256").update(t).digest("hex"),o=await n().execute({sql:`
|
|
688
790
|
INSERT INTO rate_limits (scope, key_hash, window_start, count)
|
|
689
791
|
VALUES (?, ?, ?, 1)
|
|
690
792
|
ON CONFLICT(scope, key_hash, window_start)
|
|
691
793
|
DO UPDATE SET count = count + 1, updated_at = datetime('now')
|
|
692
|
-
|
|
794
|
+
RETURNING count
|
|
795
|
+
`,args:[e,u,a]}),_=Number(o.rows[0]?.count);if(!Number.isSafeInteger(_)||_<1)throw new Error("Rate-limit bucket mutation did not return a valid count");return{allowed:_<=r,count:_,remaining:Math.max(0,r-_),resetSeconds:Math.max(1,a+s-i)}}async function Gt(e){let t=M(32).toString("hex"),r=y("sha256").update(t).digest("hex"),s=new Date(Date.now()+3600*1e3).toISOString();return await n().execute({sql:"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)",args:[r,e,s]}),t}async function Yt(e){let t=n(),r=y("sha256").update(e).digest("hex"),i=(await t.execute({sql:"SELECT user_id, expires_at, used FROM password_reset_tokens WHERE token = ?",args:[r]})).rows[0];return!i||Number(i.used)||new Date(String(i.expires_at))<new Date?null:(await t.execute({sql:"UPDATE password_reset_tokens SET used = 1 WHERE token = ?",args:[r]}),Number(i.user_id))}async function Jt(e){let t=pe();return await n().execute({sql:"UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?",args:[t,e]}),t}async function $t(e){await n().execute({sql:"UPDATE users SET key_active = 0 WHERE id = ?",args:[e]})}async function Kt(){return(await n().execute("SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC")).rows.map(t=>L({...t,api_key:"",key_active:1}))}async function Vt(e){await n().execute({sql:"UPDATE users SET active = 0 WHERE id = ?",args:[e]})}function A(e){return{id:String(e.id),user_id:Number(e.user_id),status:String(e.status),query:String(e.query),options:String(e.options),callback_url:e.callback_url!=null?String(e.callback_url):null,result:e.result!=null?String(e.result):null,error:e.error!=null?String(e.error):null,public_error_json:e.public_error_json!=null?String(e.public_error_json):null,created_at:String(e.created_at),started_at:e.started_at!=null?String(e.started_at):null,completed_at:e.completed_at!=null?String(e.completed_at):null}}function x(e){return e==null?null:Number(e)===1}function v(e){if(e==null)return null;try{return JSON.parse(String(e))}catch{return null}}function ge(e){return{id:String(e.id),job_id:String(e.job_id),user_id:Number(e.user_id),attempt_number:Number(e.attempt_number),max_attempts:Number(e.max_attempts),query:String(e.query),location:e.location!=null?String(e.location):null,max_questions:Number(e.max_questions),status:String(e.status),outcome:e.outcome!=null?String(e.outcome):null,\u006b\u0065\u0072\u006e\u0065\u006c_session_id:e.\u006b\u0065\u0072\u006e\u0065\u006c_session_id!=null?String(e.\u006b\u0065\u0072\u006e\u0065\u006c_session_id):null,question_count:e.question_count!=null?Number(e.question_count):null,duration_ms:e.duration_ms!=null?Number(e.duration_ms):null,error:e.error!=null?String(e.error):null,will_retry:x(e.will_retry),\u006b\u0065\u0072\u006e\u0065\u006c_delete_started:Number(e.\u006b\u0065\u0072\u006e\u0065\u006c_delete_started??0)===1,\u006b\u0065\u0072\u006e\u0065\u006c_delete_succeeded:x(e.\u006b\u0065\u0072\u006e\u0065\u006c_delete_succeeded),\u006b\u0065\u0072\u006e\u0065\u006c_delete_error:e.\u006b\u0065\u0072\u006e\u0065\u006c_delete_error!=null?String(e.\u006b\u0065\u0072\u006e\u0065\u006c_delete_error):null,browser_close_succeeded:x(e.browser_close_succeeded),browser_close_error:e.browser_close_error!=null?String(e.browser_close_error):null,browser_provider:e.browser_provider!=null?String(e.browser_provider):null,provider_session_id:e.provider_session_id!=null?String(e.provider_session_id):null,provider_session_observed_at:e.provider_session_observed_at!=null?String(e.provider_session_observed_at):null,provider_lookup_status:e.provider_lookup_status!=null?String(e.provider_lookup_status):null,provider_status:e.provider_status!=null?String(e.provider_status):null,provider_error_code:e.provider_error_code!=null?String(e.provider_error_code):null,provider_error_message:e.provider_error_message!=null?String(e.provider_error_message):null,provider_reason_source:e.provider_reason_source!=null?String(e.provider_reason_source):null,provider_duration_ms:e.provider_duration_ms!=null?Number(e.provider_duration_ms):null,provider_bandwidth_bytes:e.provider_bandwidth_bytes!=null?Number(e.provider_bandwidth_bytes):null,provider_captcha:x(e.provider_captcha),provider_checked_at:e.provider_checked_at!=null?String(e.provider_checked_at):null,provider_detail:v(e.provider_detail_json),provider_disconnect_observed:x(e.provider_disconnect_observed),provider_disconnect_message:e.provider_disconnect_message!=null?String(e.provider_disconnect_message):null,debug:v(e.debug_json),created_at:String(e.created_at),completed_at:e.completed_at!=null?String(e.completed_at):null}}function I(e){let{public_error_json:t,...r}=e;return{...r,options:JSON.parse(e.options),result:e.result?JSON.parse(e.result):null,publicError:se(t)}}async function zt(e,t,r,s){let i=g();return await ze(i,e,t,r,s),i}async function ze(e,t,r,s,i){if(!/^[a-zA-Z0-9-]{1,100}$/.test(e))throw new Error("job id is invalid");await n().execute({sql:"INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,JSON.stringify(s),i??null]})}async function Qt(e,t,r){let s=g();return await n().execute({sql:"INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))",args:[s,e,t,JSON.stringify(r)]}),s}async function Zt(e,t,r,s){let i=await n().execute({sql:`INSERT INTO jobs (id, user_id, query, options, status, started_at)
|
|
693
796
|
VALUES (?, ?, ?, ?, 'running', datetime('now'))
|
|
694
|
-
ON CONFLICT(id) DO NOTHING`,args:[e,t,r,JSON.stringify(
|
|
797
|
+
ON CONFLICT(id) DO NOTHING`,args:[e,t,r,JSON.stringify(s)]}),a=await be(e,t);if(!a)throw new Error(`getOrCreateRunningJob: row missing immediately after insert/lookup for ${e}`);return{job:a,created:i.rowsAffected===1}}async function er(e){await n().execute({sql:`
|
|
695
798
|
INSERT INTO harvest_attempts (
|
|
696
799
|
id, job_id, user_id, attempt_number, max_attempts, query, location, max_questions, status, created_at
|
|
697
800
|
)
|
|
698
801
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?)
|
|
699
802
|
ON CONFLICT(job_id, attempt_number) DO NOTHING
|
|
700
|
-
`,args:[
|
|
803
|
+
`,args:[g(),e.jobId,e.userId,e.attemptNumber,e.maxAttempts,e.query,e.location,e.maxQuestions,e.startedAt]})}async function tr(e){return(await n().execute({sql:`
|
|
701
804
|
UPDATE harvest_attempts SET
|
|
702
805
|
browser_provider = ?,
|
|
703
806
|
provider_session_id = ?,
|
|
@@ -707,7 +810,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
707
810
|
ELSE 'pending'
|
|
708
811
|
END
|
|
709
812
|
WHERE job_id = ? AND attempt_number = ? AND status = 'running'
|
|
710
|
-
`,args:[e.browserProvider,e.providerSessionId,e.observedAt,e.jobId,e.attemptNumber]})).rowsAffected===1}async function
|
|
813
|
+
`,args:[e.browserProvider,e.providerSessionId,e.observedAt,e.jobId,e.attemptNumber]})).rowsAffected===1}async function rr(e,t,r=new Date().toISOString()){return(await n().execute({sql:`
|
|
711
814
|
UPDATE harvest_attempts SET
|
|
712
815
|
status = 'finished',
|
|
713
816
|
outcome = COALESCE(outcome, 'timeout'),
|
|
@@ -720,7 +823,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
720
823
|
provider_reason_source = COALESCE(provider_reason_source, 'client_runtime'),
|
|
721
824
|
completed_at = COALESCE(completed_at, ?)
|
|
722
825
|
WHERE job_id = ? AND status = 'running'
|
|
723
|
-
`,args:[r,t?1:0,r,e]})).rowsAffected}async function
|
|
826
|
+
`,args:[r,t?1:0,r,e]})).rowsAffected}async function sr(e){await n().execute({sql:`
|
|
724
827
|
UPDATE harvest_attempts SET
|
|
725
828
|
status = 'finished',
|
|
726
829
|
outcome = ?,
|
|
@@ -763,7 +866,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
763
866
|
debug_json = ?,
|
|
764
867
|
completed_at = ?
|
|
765
868
|
WHERE job_id = ? AND attempt_number = ?
|
|
766
|
-
`,args:[e.outcome,e.\u006b\u0065\u0072\u006e\u0065\u006cSessionId,e.questionCount,e.durationMs,e.error,e.willRetry?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteStarted?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteSucceeded==null?null:e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteSucceeded?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteError,e.browserCloseSucceeded==null?null:e.browserCloseSucceeded?1:0,e.browserCloseError,e.browserProvider??null,e.providerSessionId??null,e.providerLookupStatus??null,e.providerStatus??null,e.providerErrorCode??null,e.providerErrorMessage??null,e.providerReasonSource??null,e.providerDurationMs??null,e.providerBandwidthBytes??null,e.providerCaptcha==null?null:e.providerCaptcha?1:0,e.providerCheckedAt??null,e.providerDetail==null?null:
|
|
869
|
+
`,args:[e.outcome,e.\u006b\u0065\u0072\u006e\u0065\u006cSessionId,e.questionCount,e.durationMs,e.error,e.willRetry?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteStarted?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteSucceeded==null?null:e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteSucceeded?1:0,e.\u006b\u0065\u0072\u006e\u0065\u006cDeleteError,e.browserCloseSucceeded==null?null:e.browserCloseSucceeded?1:0,e.browserCloseError,e.browserProvider??null,e.providerSessionId??null,e.providerLookupStatus??null,e.providerStatus??null,e.providerErrorCode??null,e.providerErrorMessage??null,e.providerReasonSource??null,e.providerDurationMs??null,e.providerBandwidthBytes??null,e.providerCaptcha==null?null:e.providerCaptcha?1:0,e.providerCheckedAt??null,e.providerDetail==null?null:Ne(e.providerDetail),e.providerDisconnectObserved==null?null:e.providerDisconnectObserved?1:0,e.providerDisconnectMessage??null,e.debug==null?null:JSON.stringify(e.debug),e.completedAt,e.jobId,e.attemptNumber]})}function Ne(e,t=4096){let r=JSON.stringify(e);return r.length<=t?r:JSON.stringify({truncated:!0})}async function nr(e){return(await n().execute({sql:`
|
|
767
870
|
UPDATE harvest_attempts SET
|
|
768
871
|
provider_lookup_status = ?,
|
|
769
872
|
provider_status = ?,
|
|
@@ -781,20 +884,20 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
781
884
|
AND browser_provider = '\u0062\u0072\u0069\u0067\u0068\u0074_\u0064\u0061\u0074\u0061_browser_api'
|
|
782
885
|
AND provider_session_id = ?
|
|
783
886
|
AND COALESCE(provider_lookup_status, '') <> 'complete'
|
|
784
|
-
`,args:[e.lookupStatus,e.providerStatus??null,e.errorCode??null,e.errorMessage??null,e.reasonSource??null,e.durationMs??null,e.bandwidthBytes??null,e.captcha==null?null:e.captcha?1:0,e.checkedAt,e.detail==null?null:
|
|
887
|
+
`,args:[e.lookupStatus,e.providerStatus??null,e.errorCode??null,e.errorMessage??null,e.reasonSource??null,e.durationMs??null,e.bandwidthBytes??null,e.captcha==null?null:e.captcha?1:0,e.checkedAt,e.detail==null?null:Ne(e.detail),e.attemptId,e.jobId,e.userId,e.providerSessionId]})).rowsAffected===1}async function ir(e=25){let t=Math.max(1,Math.min(100,Math.trunc(e)));return(await n().execute({sql:`
|
|
785
888
|
SELECT * FROM harvest_attempts
|
|
786
889
|
WHERE browser_provider = '\u0062\u0072\u0069\u0067\u0068\u0074_\u0064\u0061\u0074\u0061_browser_api'
|
|
787
890
|
AND provider_session_id IS NOT NULL
|
|
788
891
|
AND provider_lookup_status IN ('pending', 'temporary_error')
|
|
789
892
|
ORDER BY COALESCE(provider_checked_at, created_at) ASC
|
|
790
893
|
LIMIT ?
|
|
791
|
-
`,args:[t]})).rows.map(
|
|
894
|
+
`,args:[t]})).rows.map(s=>ge(s))}async function ar(e,t){return(t==null?await n().execute({sql:"SELECT * FROM harvest_attempts WHERE job_id = ? ORDER BY attempt_number ASC",args:[e]}):await n().execute({sql:`
|
|
792
895
|
SELECT ha.*
|
|
793
896
|
FROM harvest_attempts ha
|
|
794
897
|
INNER JOIN jobs j ON j.id = ha.job_id
|
|
795
898
|
WHERE ha.job_id = ? AND j.user_id = ?
|
|
796
899
|
ORDER BY ha.attempt_number ASC
|
|
797
|
-
`,args:[e,t]})).rows.map(
|
|
900
|
+
`,args:[e,t]})).rows.map(s=>ge(s))}async function be(e,t){let r=n(),s=t?await r.execute({sql:"SELECT * FROM jobs WHERE id = ? AND user_id = ?",args:[e,t]}):await r.execute({sql:"SELECT * FROM jobs WHERE id = ?",args:[e]});return s.rows[0]?I(A(s.rows[0])):void 0}async function or(e){return(await n().execute({sql:"SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50",args:[e]})).rows.map(r=>I(A(r)))}async function ur(e,t){let s=(await n().execute({sql:"SELECT content_hash, content, title, content_bytes, truncated, checked_at FROM page_snapshots WHERE user_id = ? AND url = ?",args:[e,t]})).rows[0];return s?{contentHash:String(s.content_hash),content:String(s.content),title:s.title==null?null:String(s.title),contentBytes:Number(s.content_bytes),truncated:Number(s.truncated)===1,checkedAt:String(s.checked_at)}:null}async function cr(e,t,r){await n().execute({sql:`
|
|
798
901
|
INSERT INTO page_snapshots (user_id, url, content_hash, content, title, content_bytes, truncated, checked_at)
|
|
799
902
|
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
800
903
|
ON CONFLICT(user_id, url) DO UPDATE SET
|
|
@@ -804,56 +907,56 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
804
907
|
content_bytes = excluded.content_bytes,
|
|
805
908
|
truncated = excluded.truncated,
|
|
806
909
|
checked_at = excluded.checked_at
|
|
807
|
-
`,args:[e,t,r.contentHash,r.content,r.title,r.contentBytes,r.truncated?1:0]})}var
|
|
910
|
+
`,args:[e,t,r.contentHash,r.content,r.title,r.contentBytes,r.truncated?1:0]})}var Qe=32*1024;function Ze(e){if(e==null)return null;let t=JSON.stringify(e),r=Buffer.byteLength(t,"utf8");return r<=Qe?t:JSON.stringify({truncated:!0,originalBytes:r,sha256:y("sha256").update(t).digest("hex")})}function et(e){let t=e.result!=null?String(e.result):null;return{id:String(e.id),user_id:Number(e.user_id),source:String(e.source),status:String(e.status),query:String(e.query),location:e.location!=null?String(e.location):null,result_count:e.result_count!=null?Number(e.result_count):null,result:t?JSON.parse(t):null,error:e.error!=null?String(e.error):null,created_at:String(e.created_at)}}async function _r(e){let t=g();return await n().execute({sql:`
|
|
808
911
|
INSERT INTO request_events (id, user_id, source, status, query, location, result_count, result, error)
|
|
809
912
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
810
|
-
`,args:[t,e.userId,e.source,e.status,e.query,e.location??null,e.resultCount??null,
|
|
913
|
+
`,args:[t,e.userId,e.source,e.status,e.query,e.location??null,e.resultCount??null,Ze(e.result),e.error??null]}),t}async function dr(e,t=100){return(await n().execute({sql:`
|
|
811
914
|
SELECT id, user_id, source, status, query, location, result_count, NULL AS result, error, created_at
|
|
812
915
|
FROM request_events
|
|
813
916
|
WHERE user_id = ?
|
|
814
917
|
ORDER BY created_at DESC
|
|
815
918
|
LIMIT ?
|
|
816
|
-
`,args:[e,t]})).rows.map(
|
|
919
|
+
`,args:[e,t]})).rows.map(s=>et(s))}function Le(e){if(e==null)return{};try{let t=JSON.parse(String(e));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function G(e){return{id:String(e.id),user_id:Number(e.user_id),workflow_id:String(e.workflow_id),name:String(e.name),input:Le(e.input_json),cadence:String(e.cadence),cron_expr:e.cron_expr!=null?String(e.cron_expr):null,timezone:String(e.timezone??"UTC"),webhook_url:e.webhook_url!=null?String(e.webhook_url):null,status:String(e.status),next_run_at:e.next_run_at!=null?String(e.next_run_at):null,last_run_at:e.last_run_at!=null?String(e.last_run_at):null,created_at:String(e.created_at),updated_at:String(e.updated_at)}}function Y(e){return{id:String(e.id),user_id:Number(e.user_id),schedule_id:e.schedule_id!=null?String(e.schedule_id):null,workflow_id:String(e.workflow_id),input:Le(e.input_json),status:String(e.status),manifest:e.manifest_json!=null?v(e.manifest_json):null,error_message:e.error_message!=null?String(e.error_message):null,queued_at:String(e.queued_at),started_at:e.started_at!=null?String(e.started_at):null,completed_at:e.completed_at!=null?String(e.completed_at):null,idempotency_key:e.idempotency_key!=null?String(e.idempotency_key):null,step_index:e.step_index!=null?Number(e.step_index):0,total_steps:e.total_steps!=null?Number(e.total_steps):null,step_log:Se(e.step_log_json),webhook_url:e.webhook_url!=null?String(e.webhook_url):null}}function Se(e){if(e==null)return[];try{let t=JSON.parse(String(e));return Array.isArray(t)?t:[]}catch{return[]}}function Re(e){return{id:String(e.id),run_id:String(e.run_id),user_id:Number(e.user_id),kind:String(e.kind),label:String(e.label),path:String(e.path),content_type:String(e.content_type),bytes:e.bytes!=null?Number(e.bytes):null,rows_count:e.rows_count!=null?Number(e.rows_count):null,content:e.content!=null?String(e.content):null,created_at:String(e.created_at)}}async function lr(e){let t=g();await n().execute({sql:`
|
|
817
920
|
INSERT INTO workflow_schedules (
|
|
818
921
|
id, user_id, workflow_id, name, input_json, cadence, timezone, webhook_url, next_run_at
|
|
819
922
|
)
|
|
820
923
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
821
|
-
`,args:[t,e.userId,e.workflowId,e.name,JSON.stringify(e.workflowInput),e.cadence,e.timezone??"UTC",e.webhookUrl??null,e.nextRunAt??null]});let r=await
|
|
924
|
+
`,args:[t,e.userId,e.workflowId,e.name,JSON.stringify(e.workflowInput),e.cadence,e.timezone??"UTC",e.webhookUrl??null,e.nextRunAt??null]});let r=await B(t,e.userId);if(!r)throw new Error("Failed to create workflow schedule");return r}async function B(e,t){let r=t==null?await n().execute({sql:"SELECT * FROM workflow_schedules WHERE id = ? AND status != ? LIMIT 1",args:[e,"deleted"]}):await n().execute({sql:"SELECT * FROM workflow_schedules WHERE id = ? AND user_id = ? AND status != ? LIMIT 1",args:[e,t,"deleted"]});return r.rows[0]?G(r.rows[0]):null}async function Er(e){return(await n().execute({sql:"SELECT * FROM workflow_schedules WHERE user_id = ? AND status != 'deleted' ORDER BY created_at DESC LIMIT 100",args:[e]})).rows.map(r=>G(r))}async function Tr(e){let t=[],r=[];return e.name!==void 0&&(t.push("name = ?"),r.push(e.name)),e.status!==void 0&&(t.push("status = ?"),r.push(e.status)),e.workflowInput!==void 0&&(t.push("input_json = ?"),r.push(JSON.stringify(e.workflowInput))),e.cadence!==void 0&&(t.push("cadence = ?"),r.push(e.cadence)),e.timezone!==void 0&&(t.push("timezone = ?"),r.push(e.timezone)),e.webhookUrl!==void 0&&(t.push("webhook_url = ?"),r.push(e.webhookUrl)),e.nextRunAt!==void 0&&(t.push("next_run_at = ?"),r.push(e.nextRunAt)),t.length===0||(t.push("updated_at = datetime('now')"),await n().execute({sql:`UPDATE workflow_schedules SET ${t.join(", ")} WHERE id = ? AND user_id = ? AND status != 'deleted'`,args:[...r,e.id,e.userId]})),B(e.id,e.userId)}async function pr(e,t){return(await n().execute({sql:"UPDATE workflow_schedules SET status = 'deleted', updated_at = datetime('now') WHERE id = ? AND user_id = ? AND status != 'deleted'",args:[e,t]})).rowsAffected>0}async function mr(e,t=10){return(await n().execute({sql:`
|
|
822
925
|
SELECT * FROM workflow_schedules
|
|
823
926
|
WHERE status = 'active' AND next_run_at IS NOT NULL AND next_run_at <= ?
|
|
824
927
|
ORDER BY next_run_at ASC
|
|
825
928
|
LIMIT ?
|
|
826
|
-
`,args:[e,t]})).rows.map(
|
|
929
|
+
`,args:[e,t]})).rows.map(s=>G(s))}async function gr(e,t,r){await n().execute({sql:"UPDATE workflow_schedules SET last_run_at = ?, next_run_at = ?, updated_at = datetime('now') WHERE id = ?",args:[t,r,e]})}async function Nr(e){let t=e.id??g(),r=[t,e.userId,e.scheduleId??null,e.workflowId,JSON.stringify(e.workflowInput),e.idempotencyKey??null,e.webhookUrl??null,e.totalSteps??null];try{await n().execute({sql:`
|
|
827
930
|
INSERT INTO workflow_runs (id, user_id, schedule_id, workflow_id, input_json, idempotency_key, webhook_url, total_steps)
|
|
828
931
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
829
|
-
`,args:r})}catch(i){if(e.idempotencyKey){let a=await
|
|
932
|
+
`,args:r})}catch(i){if(e.idempotencyKey){let a=await rt(e.idempotencyKey);if(a)return a}throw i}let s=await tt(t,e.userId);if(!s)throw new Error("Failed to create workflow run");return s}async function tt(e,t){let r=t==null?await n().execute({sql:"SELECT * FROM workflow_runs WHERE id = ? LIMIT 1",args:[e]}):await n().execute({sql:"SELECT * FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1",args:[e,t]});return r.rows[0]?Y(r.rows[0]):null}async function rt(e){let t=await n().execute({sql:"SELECT * FROM workflow_runs WHERE idempotency_key = ? LIMIT 1",args:[e]});return t.rows[0]?Y(t.rows[0]):null}async function br(e,t=50){return(await n().execute({sql:"SELECT * FROM workflow_runs WHERE user_id = ? ORDER BY queued_at DESC LIMIT ?",args:[e,t]})).rows.map(s=>Y(s))}async function Lr(e){await n().execute({sql:"UPDATE workflow_runs SET status = 'running', started_at = COALESCE(started_at, datetime('now')) WHERE id = ?",args:[e]})}async function Sr(e,t,r){await n().execute({sql:"UPDATE workflow_runs SET status = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,JSON.stringify(r),e]})}async function Rr(e,t,r){await n().execute({sql:"UPDATE workflow_runs SET status = 'failed', error_message = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,r==null?null:JSON.stringify(r),e]})}function st(e){return e==="html_report"?"text/html; charset=utf-8":e==="csv"?"text/csv; charset=utf-8":e==="json"?"application/json; charset=utf-8":"text/plain; charset=utf-8"}async function Ae(e,t,r){await n().execute({sql:`
|
|
830
933
|
INSERT INTO workflow_artifacts (id, run_id, user_id, kind, label, path, content_type, bytes, rows_count, content)
|
|
831
934
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
832
|
-
`,args:[
|
|
935
|
+
`,args:[g(),e,t,r.kind,r.label,r.path,st(r.kind),r.bytes??null,r.rows??null,r.content??null]})}async function Ar(e,t,r){await n().execute({sql:"DELETE FROM workflow_artifacts WHERE run_id = ? AND user_id = ?",args:[e,t]});for(let s of r)await Ae(e,t,s);return we(e,t)}async function wr(e,t,r){for(let s of r)await Ae(e,t,s);return we(e,t)}async function fr(e,t){let s=(t==null?await n().execute({sql:"SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? LIMIT 1",args:[e]}):await n().execute({sql:"SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1",args:[e,t]})).rows[0];return s?{stepIndex:s.step_index!=null?Number(s.step_index):0,totalSteps:s.total_steps!=null?Number(s.total_steps):null,state:s.step_state_json!=null?v(s.step_state_json):null,stepLog:Se(s.step_log_json)}:null}async function Or(e,t){let r=[...t.appendLog,t.logEntry];await n().execute({sql:"UPDATE workflow_runs SET status = ?, step_index = ?, total_steps = ?, step_state_json = ?, step_log_json = ?, started_at = COALESCE(started_at, datetime('now')) WHERE id = ?",args:[t.status,t.stepIndex,t.totalSteps,JSON.stringify(t.state??null),JSON.stringify(r),e]})}async function we(e,t){return(await n().execute({sql:"SELECT * FROM workflow_artifacts WHERE run_id = ? AND user_id = ? ORDER BY created_at ASC",args:[e,t]})).rows.map(s=>Re(s))}async function hr(e,t){let r=await n().execute({sql:"SELECT * FROM workflow_artifacts WHERE id = ? AND user_id = ? LIMIT 1",args:[e,t]});return r.rows[0]?Re(r.rows[0]):null}async function xr(e){await n().execute({sql:`
|
|
833
936
|
INSERT INTO workflow_webhook_deliveries (
|
|
834
937
|
id, run_id, schedule_id, user_id, webhook_url, status, attempts, last_error, delivered_at
|
|
835
938
|
)
|
|
836
939
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
837
|
-
`,args:[
|
|
940
|
+
`,args:[g(),e.runId,e.scheduleId??null,e.userId,e.webhookUrl,e.status,e.attempts,e.lastError??null,e.status==="delivered"?new Date().toISOString():null]})}async function yr(){let e=await n().execute("SELECT COUNT(*) as n FROM users WHERE active = 1");return Number(e.rows[0]?.n??0)}async function Ir(e){let t=await n().execute({sql:"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND (status = 'pending' OR (status = 'running' AND started_at > datetime('now', '-10 minutes')))",args:[e]});return Number(t.rows[0]?.n??0)}async function Cr(){let e=n(),t=await e.execute(`
|
|
838
941
|
SELECT * FROM jobs
|
|
839
942
|
WHERE status = 'pending'
|
|
840
943
|
AND COALESCE(json_extract(options, '$.executionOwner'), 'cron') NOT IN ('direct', 'inngest')
|
|
841
944
|
ORDER BY created_at
|
|
842
945
|
LIMIT 1
|
|
843
|
-
`),r=t.rows[0]?
|
|
946
|
+
`),r=t.rows[0]?A(t.rows[0]):void 0;return r?(await e.execute({sql:"UPDATE jobs SET status = 'running', started_at = datetime('now') WHERE id = ? AND status = 'pending'",args:[r.id]})).rowsAffected===0?void 0:{...r,status:"running"}:void 0}var J=45e3;async function kr(e){let t=n(),r=await be(e);if(!r||r.options.executionOwner!=="direct"||r.options.serpOnly===!0)return;let s=Math.max(0,Number(r.options.paaRecoveryCount??0));if(r.status!=="running"){if(r.status==="pending"){let i=g(),a=new Date().toISOString(),u=new Date(Date.now()+J).toISOString();return(await t.execute({sql:`UPDATE jobs
|
|
844
947
|
SET status = 'running', started_at = datetime('now'), error = NULL,
|
|
845
948
|
options = json_set(options,
|
|
846
949
|
'$.paaLeaseToken', ?,
|
|
847
950
|
'$.paaLeaseExpiresAt', ?,
|
|
848
951
|
'$.paaLastDispatchAt', ?)
|
|
849
|
-
WHERE id = ? AND status = 'pending'`,args:[i,
|
|
850
|
-
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),
|
|
851
|
-
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,e,t]})).rowsAffected===1}async function
|
|
952
|
+
WHERE id = ? AND status = 'pending'`,args:[i,u,a,e]})).rowsAffected===0?void 0:{job:{...r,status:"running",started_at:a,options:{...r.options,paaLeaseToken:i,paaLeaseExpiresAt:u,paaLastDispatchAt:a}},leaseToken:i,recoveryCount:s}}return{job:r,leaseToken:null,recoveryCount:s}}}async function Ur(e,t){return(await n().execute({sql:"UPDATE jobs SET result = ? WHERE id = ? AND status = 'running'",args:[JSON.stringify(t),e]})).rowsAffected===1}async function Dr(e,t,r){let s=new Date(Date.now()+J).toISOString();return(await n().execute({sql:`UPDATE jobs SET result = ?, options = json_set(options, '$.paaLeaseExpiresAt', ?)
|
|
953
|
+
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),s,e,t]})).rowsAffected===1}async function vr(e,t){let r=new Date(Date.now()+J).toISOString();return(await n().execute({sql:`UPDATE jobs SET options = json_set(options, '$.paaLeaseExpiresAt', ?)
|
|
954
|
+
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,e,t]})).rowsAffected===1}async function Xr(e,t,r){return(await n().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL,
|
|
852
955
|
completed_at = datetime('now'),
|
|
853
956
|
options = json_set(options, '$.paaLeaseExpiresAt', NULL)
|
|
854
|
-
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),e,t]})).rowsAffected===1}async function
|
|
957
|
+
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),e,t]})).rowsAffected===1}async function Mr(e,t,r,s){return(await n().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now'),
|
|
855
958
|
options = json_set(options, '$.paaLeaseExpiresAt', NULL)
|
|
856
|
-
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,
|
|
959
|
+
WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,R(s),e,t]})).rowsAffected===1}async function Fr(e,t){let r=new Date().toISOString();return(await n().execute({sql:`UPDATE jobs SET status = 'pending', started_at = NULL,
|
|
857
960
|
result = CASE WHEN json_valid(result) THEN json_set(result,
|
|
858
961
|
'$.progress.lifecycle.discoveryStatus', 'interrupted',
|
|
859
962
|
'$.progress.lifecycle.completionReason', 'The previous worker lease expired; the same job is recovering automatically.',
|
|
@@ -869,28 +972,28 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
869
972
|
datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
|
|
870
973
|
OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
|
|
871
974
|
)
|
|
872
|
-
AND COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) < ?`,args:[r,e,Math.max(0,Math.trunc(t))]})).rowsAffected===1}async function
|
|
975
|
+
AND COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) < ?`,args:[r,e,Math.max(0,Math.trunc(t))]})).rowsAffected===1}async function Pr(e,t){return(await n().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL, completed_at = datetime('now')
|
|
873
976
|
WHERE id = ? AND status = 'running'
|
|
874
977
|
AND (
|
|
875
978
|
datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
|
|
876
979
|
OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
|
|
877
|
-
)`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function
|
|
980
|
+
)`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function Hr(e,t,r){return(await n().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now')
|
|
878
981
|
WHERE id = ? AND status = 'running'
|
|
879
982
|
AND (
|
|
880
983
|
datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
|
|
881
984
|
OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
|
|
882
|
-
)`,args:[t,
|
|
985
|
+
)`,args:[t,R(r),e]})).rowsAffected===1}async function qr(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await n().execute({sql:`SELECT * FROM jobs
|
|
883
986
|
WHERE status IN ('done', 'failed', 'cancelled')
|
|
884
987
|
AND json_extract(options, '$.executionOwner') IN ('direct', 'inngest')
|
|
885
988
|
AND json_extract(options, '$.billingDebitKey') IS NOT NULL
|
|
886
989
|
AND json_extract(options, '$.billingSettledAt') IS NULL
|
|
887
990
|
ORDER BY completed_at DESC
|
|
888
|
-
LIMIT ?`,args:[t]})).rows.map(
|
|
991
|
+
LIMIT ?`,args:[t]})).rows.map(s=>I(A(s)))}async function Wr(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await n().execute({sql:`SELECT * FROM jobs
|
|
889
992
|
WHERE status = 'running'
|
|
890
993
|
AND started_at <= datetime('now', '-15 minutes')
|
|
891
994
|
AND json_extract(options, '$.executionOwner') IN ('direct', 'inngest')
|
|
892
995
|
ORDER BY started_at
|
|
893
|
-
LIMIT ?`,args:[t]})).rows.map(
|
|
996
|
+
LIMIT ?`,args:[t]})).rows.map(s=>I(A(s)))}async function jr(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await n().execute({sql:`SELECT * FROM jobs
|
|
894
997
|
WHERE json_extract(options, '$.executionOwner') = 'direct'
|
|
895
998
|
AND json_extract(options, '$.serpOnly') IS NOT 1
|
|
896
999
|
AND (
|
|
@@ -903,11 +1006,11 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
903
1006
|
AND datetime(COALESCE(json_extract(options, '$.paaLastDispatchAt'), '1970-01-01')) <= datetime('now', '-30 seconds'))
|
|
904
1007
|
)
|
|
905
1008
|
ORDER BY COALESCE(started_at, created_at)
|
|
906
|
-
LIMIT ?`,args:[t]})).rows.map(
|
|
1009
|
+
LIMIT ?`,args:[t]})).rows.map(s=>I(A(s)))}async function Br(e,t,r=new Date().toISOString()){if(!Number.isSafeInteger(t)||t<0)throw new Error("job billed amount must be a non-negative integer");return(await n().execute({sql:`UPDATE jobs
|
|
907
1010
|
SET options = json_set(options, '$.billingSettledAt', ?, '$.billedMc', ?)
|
|
908
|
-
WHERE id = ?`,args:[r,t,e]})).rowsAffected===1}async function
|
|
909
|
-
WHERE id = ? AND status = 'running'`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function
|
|
910
|
-
WHERE id = ? AND status = 'running'`,args:[t,
|
|
1011
|
+
WHERE id = ?`,args:[r,t,e]})).rowsAffected===1}async function Gr(e,t){await n().execute({sql:"UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL, completed_at = datetime('now') WHERE id = ?",args:[JSON.stringify(t),e]})}async function Yr(e,t){return(await n().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL, completed_at = datetime('now')
|
|
1012
|
+
WHERE id = ? AND status = 'running'`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function Jr(e,t,r){await n().execute({sql:"UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,R(r),e]})}async function $r(e,t,r){return(await n().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now')
|
|
1013
|
+
WHERE id = ? AND status = 'running'`,args:[t,R(r),e]})).rowsAffected===1}async function Kr(e,t,r){await n().execute({sql:"UPDATE jobs SET status = 'cancelled', error = ?, public_error_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,R(r),e]})}function $(e){return{id:String(e.id),user_id:Number(e.user_id),status:String(e.status),entity_name:String(e.entity_name),entity_type:String(e.entity_type),url:e.url!=null?String(e.url):null,request:String(e.request),result:e.result!=null?String(e.result):null,error:e.error!=null?String(e.error):null,created_at:String(e.created_at),updated_at:String(e.updated_at)}}function nt(e){return{id:Number(e.id),job_id:String(e.job_id),phase:String(e.phase),output:String(e.output),created_at:String(e.created_at)}}async function Vr(e,t,r,s,i,a){await n().execute({sql:"INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)",args:[e,t,r,s,a??null,JSON.stringify(i)]})}async function zr(){let t=await n().execute("UPDATE kpo_jobs SET status='running', updated_at=datetime('now') WHERE id = (SELECT id FROM kpo_jobs WHERE status='pending' ORDER BY created_at ASC LIMIT 1) RETURNING *");return t.rowsAffected===0?null:t.rows[0]?$(t.rows[0]):null}async function Qr(e){let t=await n().execute({sql:"SELECT * FROM kpo_jobs WHERE id = ?",args:[e]});return t.rows[0]?$(t.rows[0]):null}async function Zr(e,t){await n().execute({sql:"UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?",args:[t,e]})}async function es(e,t){await n().execute({sql:"UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?",args:[JSON.stringify(t),e]})}async function ts(e,t){await n().execute({sql:"UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?",args:[t,e]})}async function rs(e,t,r){await n().execute({sql:"INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)",args:[e,t,JSON.stringify(r)]})}async function ss(e){return(await n().execute({sql:"SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC",args:[e]})).rows.map(r=>nt(r))}async function ns(e){return(e!=null?await n().execute({sql:"SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50",args:[e]}):await n().execute("SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50")).rows.map(r=>$(r))}async function is(e){let t=await n().execute({sql:"SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1",args:[e]});return t.rows[0]?L(t.rows[0]):void 0}async function as(e,t){await n().execute({sql:"UPDATE users SET stripe_customer_id = ? WHERE id = ?",args:[t,e]})}async function os(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("extra concurrency slots must be a non-negative safe integer");await it(e,Math.ceil(t/2))}async function it(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("concurrency pack quantity must be a non-negative safe integer");await n().execute({sql:"UPDATE users SET concurrency_pack_quantity = ?, extra_concurrency_slots = ? WHERE id = ?",args:[t,t*2,e]})}async function us(e,t){await n().execute({sql:"UPDATE users SET concurrency_stripe_sub_id = ? WHERE id = ?",args:[t,e]})}async function cs(e,t,r,s){await n().execute({sql:"UPDATE users SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ? WHERE id = ?",args:[t,Math.max(0,Math.round(r)),s,e]})}function at(e){return{user_id:Number(e.user_id),stripe_subscription_id:e.stripe_subscription_id!=null?String(e.stripe_subscription_id):null,stripe_subscription_item_id:e.stripe_subscription_item_id!=null?String(e.stripe_subscription_item_id):null,price_id:e.price_id!=null?String(e.price_id):null,quantity:Math.max(0,Number(e.quantity??0)),status:["pending","synced","blocked","error"].includes(String(e.status))?String(e.status):"error",last_error_code:e.last_error_code!=null?String(e.last_error_code):null,checked_at:String(e.checked_at),synced_at:e.synced_at!=null?String(e.synced_at):null,updated_at:String(e.updated_at)}}async function ot(e){let r=(await n().execute({sql:"SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",args:[e]})).rows[0];return r?at(r):null}async function _s(e){let t=Math.max(0,Math.round(e.quantity));await n().execute({sql:`INSERT INTO connected_account_billing
|
|
911
1014
|
(user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
|
|
912
1015
|
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
|
|
913
1016
|
ON CONFLICT(user_id) DO UPDATE SET
|
|
@@ -919,14 +1022,14 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
919
1022
|
last_error_code = excluded.last_error_code,
|
|
920
1023
|
checked_at = datetime('now'),
|
|
921
1024
|
synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
|
|
922
|
-
updated_at = datetime('now')`,args:[e.userId,e.stripeSubscriptionId,e.stripeSubscriptionItemId,e.priceId,t,e.status,e.lastErrorCode??null,e.synced===!0?1:0,e.synced===!0?1:0]});let r=await
|
|
1025
|
+
updated_at = datetime('now')`,args:[e.userId,e.stripeSubscriptionId,e.stripeSubscriptionItemId,e.priceId,t,e.status,e.lastErrorCode??null,e.synced===!0?1:0,e.synced===!0?1:0]});let r=await ot(e.userId);if(!r)throw new Error("connected account billing state was not persisted");return r}async function ds(e=60,t=20){let r=`-${Math.max(1,Math.round(e))} minutes`;return(await n().execute({sql:`SELECT u.*
|
|
923
1026
|
FROM users u
|
|
924
1027
|
LEFT JOIN connected_account_billing b ON b.user_id = u.id
|
|
925
1028
|
WHERE u.active = 1
|
|
926
1029
|
AND u.subscription_id IS NOT NULL
|
|
927
1030
|
AND (b.checked_at IS NULL OR b.checked_at <= datetime('now', ?))
|
|
928
1031
|
ORDER BY COALESCE(b.checked_at, u.created_at) ASC
|
|
929
|
-
LIMIT ?`,args:[r,Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(i=>
|
|
1032
|
+
LIMIT ?`,args:[r,Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(i=>L(i))}async function ls(e,t,r){await n().execute({sql:"UPDATE users SET memory_plan = ?, memory_subscription_id = ? WHERE id = ?",args:[t,r,e]})}async function Es(e){let r=(await n().execute({sql:"SELECT memory_plan, memory_subscription_id FROM users WHERE id = ?",args:[e]})).rows[0];return{plan:r?.memory_plan!=null?String(r.memory_plan):"free",memory_subscription_id:r?.memory_subscription_id!=null?String(r.memory_subscription_id):null}}async function Ts(e){let r=(await n().execute({sql:"SELECT memory_provisioned FROM users WHERE id = ?",args:[e]})).rows[0];return Number(r?.memory_provisioned??0)===1}async function ps(e,t){await n().execute({sql:"UPDATE users SET memory_provisioned = ? WHERE id = ?",args:[t?1:0,e]})}async function ms(e){let t=await n().execute({sql:"SELECT * FROM users WHERE memory_subscription_id = ? AND active = 1",args:[e]});return t.rows[0]?L(t.rows[0]):void 0}var X="+3 months";async function w(e){return(await n().execute({sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at)
|
|
930
1033
|
SELECT ?, legacy.balance_mc, legacy.balance_mc, 'migration', datetime('now', ?)
|
|
931
1034
|
FROM (
|
|
932
1035
|
SELECT COALESCE(SUM(amount_mc), 0) AS balance_mc
|
|
@@ -940,44 +1043,44 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
940
1043
|
)
|
|
941
1044
|
AND NOT EXISTS (
|
|
942
1045
|
SELECT 1 FROM credit_lots WHERE user_id = ?
|
|
943
|
-
)`,args:[e,
|
|
1046
|
+
)`,args:[e,X,e,e,C,e]})).rowsAffected}async function gs(){let e=n(),t=await e.execute({sql:`SELECT id
|
|
944
1047
|
FROM credit_lots
|
|
945
1048
|
WHERE remaining_mc > 0
|
|
946
1049
|
AND never_expires = 0
|
|
947
1050
|
AND expires_at <= datetime('now')
|
|
948
|
-
LIMIT 500`});if(t.rows.length===0)return{expired_lots:0,expired_mc:0};let r=[],
|
|
1051
|
+
LIMIT 500`});if(t.rows.length===0)return{expired_lots:0,expired_mc:0};let r=[],s=[];for(let _ of t.rows){let d=Number(_.id),E=g();r.push({token:E,insertResultIndex:s.length}),s.push({sql:`INSERT OR IGNORE INTO credit_lot_expirations (lot_id, user_id, amount_mc, claim_token)
|
|
949
1052
|
SELECT id, user_id, remaining_mc, ?
|
|
950
1053
|
FROM credit_lots
|
|
951
1054
|
WHERE id = ?
|
|
952
1055
|
AND remaining_mc > 0
|
|
953
1056
|
AND never_expires = 0
|
|
954
|
-
AND expires_at <= datetime('now')`,args:[
|
|
1057
|
+
AND expires_at <= datetime('now')`,args:[E,d]}),s.push({sql:`UPDATE credit_lots
|
|
955
1058
|
SET remaining_mc = 0
|
|
956
1059
|
WHERE id = ?
|
|
957
1060
|
AND EXISTS (
|
|
958
1061
|
SELECT 1 FROM credit_lot_expirations
|
|
959
1062
|
WHERE lot_id = ? AND claim_token = ?
|
|
960
|
-
)`,args:[d,d,
|
|
1063
|
+
)`,args:[d,d,E]}),s.push({sql:`INSERT INTO ledger (user_id, amount_mc, operation, description)
|
|
961
1064
|
SELECT user_id, -amount_mc, 'credit_expired', 'credits expired'
|
|
962
1065
|
FROM credit_lot_expirations
|
|
963
|
-
WHERE lot_id = ? AND claim_token = ?`,args:[d,
|
|
1066
|
+
WHERE lot_id = ? AND claim_token = ?`,args:[d,E]})}let i=await e.batch(s,"write"),a=r.filter(_=>i[_.insertResultIndex]?.rowsAffected===1).map(_=>_.token);if(a.length===0)return{expired_lots:0,expired_mc:0};let u=a.map(()=>"?").join(", "),c=await e.execute({sql:`SELECT user_id, amount_mc
|
|
964
1067
|
FROM credit_lot_expirations
|
|
965
|
-
WHERE claim_token IN (${
|
|
1068
|
+
WHERE claim_token IN (${u})`,args:a}),o=new Set(c.rows.map(_=>Number(_.user_id)));for(let _ of o)await fe(_);return{expired_lots:c.rows.length,expired_mc:c.rows.reduce((_,d)=>_+Number(d.amount_mc),0)}}async function Ns(){let e=n(),t=await e.execute({sql:`SELECT DISTINCT u.id AS user_id
|
|
966
1069
|
FROM users u
|
|
967
1070
|
JOIN ledger l ON l.user_id = u.id
|
|
968
1071
|
WHERE l.created_at < ?
|
|
969
|
-
AND NOT EXISTS (SELECT 1 FROM credit_lots WHERE user_id = u.id)`,args:[
|
|
1072
|
+
AND NOT EXISTS (SELECT 1 FROM credit_lots WHERE user_id = u.id)`,args:[C]}),r=0,s=0;for(let i of t.rows){let a=Number(i.user_id);if(await w(a)===0)continue;let c=await e.execute({sql:`SELECT amount_mc FROM credit_lots
|
|
970
1073
|
WHERE user_id = ? AND source = 'migration'
|
|
971
|
-
ORDER BY id DESC LIMIT 1`,args:[a]}),
|
|
1074
|
+
ORDER BY id DESC LIMIT 1`,args:[a]}),o=Number(c.rows[0]?.amount_mc??0);await fe(a),r+=1,s+=o}return{migrated:r,total_mc:s}}async function bs(e,t,r,s,i,a){let u=n();await w(e);let c=a?.neverExpires;if(c===void 0&&t>0){let E=await u.execute({sql:"SELECT subscription_tier FROM users WHERE id = ? LIMIT 1",args:[e]});c=String(E.rows[0]?.subscription_tier??"").startsWith("sonic")}let o=c?"9999-12-31 23:59:59":null;if(i){let E=[{sql:"INSERT OR IGNORE INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,s??null,i]}];t>0&&E.push({sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
|
|
972
1075
|
SELECT ?, ?, ?, ?, ?, COALESCE(?, datetime('now', ?)), ?
|
|
973
|
-
WHERE changes() = 1`,args:[e,t,t,r,i,
|
|
1076
|
+
WHERE changes() = 1`,args:[e,t,t,r,i,o,X,c?1:0]}),E.push({sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND changes() = 1",args:[t,e]}),await u.batch(E,"write");let l=await u.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(l.rows[0]?.balance_mc??0)}let _=[];t>0&&_.push({sql:"INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires) VALUES (?, ?, ?, ?, ?, COALESCE(?, datetime('now', ?)), ?)",args:[e,t,t,r,null,o,X,c?1:0]}),_.push({sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?",args:[t,e]}),_.push({sql:"INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,s??null,null]}),await u.batch(_,"write");let d=await u.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(d.rows[0]?.balance_mc??0)}async function Ls(e,t,r,s,i,a){if(!Number.isSafeInteger(t)||t<=0)throw new Error("idempotent credit amount must be a positive integer");if(!i||i.length>500)throw new Error("idempotent credit key is required and must be bounded");let u=n();await w(e);let c=a?.neverExpires;if(c===void 0){let m=await u.execute({sql:"SELECT subscription_tier FROM users WHERE id = ? LIMIT 1",args:[e]});c=String(m.rows[0]?.subscription_tier??"").startsWith("sonic")}let o=c?"9999-12-31 23:59:59":null,_=async()=>{let m=await u.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(m.rows[0]?.balance_mc??0)},d;try{d=await u.batch([{sql:"INSERT OR IGNORE INTO billing_credits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')",args:[i,e,t,r,s]},{sql:"UPDATE billing_credits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed'",args:[i,e,t,r,s]},{sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
|
|
974
1077
|
SELECT user_id, amount_mc, amount_mc, operation, NULL, COALESCE(?, datetime('now', ?)), ?
|
|
975
1078
|
FROM billing_credits
|
|
976
|
-
WHERE idempotency_key = ? AND status = 'applying'`,args:[
|
|
1079
|
+
WHERE idempotency_key = ? AND status = 'applying'`,args:[o,X,c?1:0,i]},{sql:`UPDATE users
|
|
977
1080
|
SET balance_mc = balance_mc + COALESCE((
|
|
978
1081
|
SELECT amount_mc FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'
|
|
979
1082
|
), 0)
|
|
980
|
-
WHERE id = ?`,args:[i,e]},{sql:"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'",args:[i]},{sql:"UPDATE billing_credits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[i]}],"write")}catch(
|
|
1083
|
+
WHERE id = ?`,args:[i,e]},{sql:"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'",args:[i]},{sql:"UPDATE billing_credits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[i]}],"write")}catch(m){try{let T=(await u.execute({sql:"SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",args:[i]})).rows[0];if(T&&Number(T.user_id)===e&&Number(T.amount_mc)===t&&String(T.operation)===r&&String(T.description)===s)return{ok:!0,balance_mc:await _(),duplicate:!0}}catch{}throw m}let l=(await u.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_credits WHERE idempotency_key = ? LIMIT 1",args:[i]})).rows[0];if(!l||Number(l.user_id)!==e||Number(l.amount_mc)!==t||String(l.operation)!==r||String(l.description)!==s)throw new Error("idempotent credit key conflicts with another credit");return{ok:String(l.status)==="applied",balance_mc:await _(),duplicate:d[1]?.rowsAffected===0&&String(l.status)==="applied"}}async function Ss(e,t){return(await n().execute({sql:"SELECT 1 FROM ledger WHERE user_id = ? AND operation = ? LIMIT 1",args:[e,t]})).rows.length>0}async function Rs(e,t){return(await n().execute({sql:"INSERT OR IGNORE INTO free_credit_refreshes (user_id, month) VALUES (?, ?)",args:[e,t]})).rowsAffected>0}async function As(e,t,r,s){let i=await ut(e,t,r,s??"",`debit:${g()}`);return{ok:i.ok,balance_mc:i.balance_mc}}async function ut(e,t,r,s,i){if(!Number.isSafeInteger(t)||t<=0)throw new Error("idempotent debit amount must be a positive integer");if(!i||i.length>500)throw new Error("idempotent debit key is required and must be bounded");let a=n();await w(e);let u;try{u=await a.batch([{sql:"UPDATE billing_debits SET amount_mc = ? WHERE idempotency_key = ? AND user_id = ? AND operation = ? AND description = ? AND status = 'claimed'",args:[t,i,e,r,s]},{sql:"INSERT OR IGNORE INTO billing_debits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')",args:[i,e,t,r,s]},{sql:`UPDATE users
|
|
981
1084
|
SET balance_mc = balance_mc - ?
|
|
982
1085
|
WHERE id = ?
|
|
983
1086
|
AND balance_mc >= ?
|
|
@@ -986,7 +1089,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
986
1089
|
WHERE user_id = ?
|
|
987
1090
|
AND remaining_mc > 0
|
|
988
1091
|
AND (never_expires = 1 OR expires_at > datetime('now'))) >= ?
|
|
989
|
-
AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed')`,args:[t,e,t,e,t,i,e,t,r,
|
|
1092
|
+
AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed')`,args:[t,e,t,e,t,i,e,t,r,s]},{sql:"UPDATE billing_debits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed' AND changes() = 1",args:[i,e,t,r,s]},{sql:`INSERT OR IGNORE INTO billing_debit_allocations
|
|
990
1093
|
(debit_idempotency_key, lot_id, user_id, amount_mc, expires_at)
|
|
991
1094
|
WITH ordered AS MATERIALIZED (
|
|
992
1095
|
SELECT id,
|
|
@@ -1013,7 +1116,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1013
1116
|
WHERE id IN (
|
|
1014
1117
|
SELECT lot_id FROM billing_debit_allocations WHERE debit_idempotency_key = ?
|
|
1015
1118
|
)
|
|
1016
|
-
AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,args:[i,i,i]},{sql:"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, -amount_mc, operation, description FROM billing_debits WHERE idempotency_key = ? AND status = 'applying'",args:[i]},{sql:"UPDATE billing_debits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[i]}],"write")}catch(d){try{let l=(await a.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",args:[i]})).rows[0];if(l&&Number(l.user_id)===e&&Number(l.amount_mc)===t&&String(l.operation)===r&&String(l.description)===
|
|
1119
|
+
AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,args:[i,i,i]},{sql:"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, -amount_mc, operation, description FROM billing_debits WHERE idempotency_key = ? AND status = 'applying'",args:[i]},{sql:"UPDATE billing_debits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[i]}],"write")}catch(d){try{let l=(await a.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",args:[i]})).rows[0];if(l&&Number(l.user_id)===e&&Number(l.amount_mc)===t&&String(l.operation)===r&&String(l.description)===s){let m=await a.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return{ok:!0,balance_mc:Number(m.rows[0]?.balance_mc??0),duplicate:!0}}}catch{}throw d}let o=(await a.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? LIMIT 1",args:[i]})).rows[0];if(!o||Number(o.user_id)!==e||Number(o.amount_mc)!==t||String(o.operation)!==r||String(o.description)!==s)throw new Error("idempotent debit key conflicts with another debit");let _=await a.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return{ok:String(o.status)==="applied",balance_mc:Number(_.rows[0]?.balance_mc??0),duplicate:u[2]?.rowsAffected===0&&String(o.status)==="applied"}}async function ws(e,t,r,s,i,a){if(!Number.isSafeInteger(r)||r<0)throw new Error("idempotent debit settlement amount must be a non-negative integer");if(!t||t.length>500)throw new Error("idempotent debit settlement key is required and must be bounded");if(!s||!a)throw new Error("idempotent debit settlement operation and source are required");let u=n();await w(e);let c=`debit-settlement:${y("sha256").update(t).digest("hex")}`,o="wallet_hold_settlement",_=g(),d=JSON.stringify({debitIdempotencyKey:t,refundOperation:s,description:i}),E=async()=>{let T=(await u.execute({sql:`SELECT e.user_id,
|
|
1017
1120
|
e.status,
|
|
1018
1121
|
e.amount_mc AS final_amount_mc,
|
|
1019
1122
|
e.billing_class,
|
|
@@ -1025,7 +1128,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1025
1128
|
WHERE e.idempotency_key = ?
|
|
1026
1129
|
AND d.user_id = e.user_id
|
|
1027
1130
|
AND d.status = 'applied'
|
|
1028
|
-
LIMIT 1`,args:[t,
|
|
1131
|
+
LIMIT 1`,args:[t,c]})).rows[0];if(!T)return null;if(Number(T.user_id)!==e||String(T.billing_class)!==o||String(T.source_surface)!==a)throw new Error("idempotent debit settlement key conflicts with another settlement");if(String(T.status)!=="settled")return null;let F=Number(T.held_amount_mc),S=Number(T.final_amount_mc);if(!Number.isSafeInteger(F)||!Number.isSafeInteger(S)||S<0||S>F)throw new Error("idempotent debit settlement receipt is invalid");let Oe=F-S,K=await u.execute({sql:`WITH ordered AS (
|
|
1029
1132
|
SELECT a.amount_mc,
|
|
1030
1133
|
a.expires_at,
|
|
1031
1134
|
COALESCE(SUM(a.amount_mc) OVER (
|
|
@@ -1041,9 +1144,9 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1041
1144
|
)
|
|
1042
1145
|
SELECT (SELECT COUNT(*) FROM ordered) AS allocation_count,
|
|
1043
1146
|
COALESCE(SUM(CASE WHEN expires_at > ? THEN refund_mc ELSE 0 END), 0) AS credited_refund_mc
|
|
1044
|
-
FROM refunds`,args:[t,
|
|
1147
|
+
FROM refunds`,args:[t,S,String(T.settled_at)]}),V=Number(K.rows[0]?.allocation_count??0)>0?Number(K.rows[0]?.credited_refund_mc??0):0,he=await u.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return{balance_mc:Number(he.rows[0]?.balance_mc??0),final_amount_mc:S,refund_mc:V,expired_refund_mc:Oe-V,duplicate:!0}},l;try{l=await u.batch([{sql:`INSERT OR IGNORE INTO billing_events
|
|
1045
1148
|
(id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)
|
|
1046
|
-
VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,args:[
|
|
1149
|
+
VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,args:[_,e,c,o,a,r,d]},{sql:`WITH ordered AS (
|
|
1047
1150
|
SELECT a.lot_id,
|
|
1048
1151
|
a.amount_mc,
|
|
1049
1152
|
a.expires_at,
|
|
@@ -1084,14 +1187,14 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1084
1187
|
AND d.user_id = e.user_id
|
|
1085
1188
|
AND d.status = 'applied'
|
|
1086
1189
|
AND e.amount_mc BETWEEN 0 AND d.amount_mc
|
|
1087
|
-
)`,args:[t,r,e,t,
|
|
1190
|
+
)`,args:[t,r,e,t,c,e,o,a]},{sql:`UPDATE billing_events
|
|
1088
1191
|
SET status = 'applying'
|
|
1089
1192
|
WHERE idempotency_key = ?
|
|
1090
1193
|
AND user_id = ?
|
|
1091
1194
|
AND billing_class = ?
|
|
1092
1195
|
AND source_surface = ?
|
|
1093
1196
|
AND status = 'settling'
|
|
1094
|
-
AND changes() = 1`,args:[
|
|
1197
|
+
AND changes() = 1`,args:[c,e,o,a]},{sql:`WITH ordered AS (
|
|
1095
1198
|
SELECT a.lot_id,
|
|
1096
1199
|
a.amount_mc,
|
|
1097
1200
|
a.expires_at,
|
|
@@ -1113,7 +1216,7 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1113
1216
|
SELECT lot_id FROM refunds
|
|
1114
1217
|
WHERE refund_mc > 0 AND expires_at > datetime('now')
|
|
1115
1218
|
)
|
|
1116
|
-
AND EXISTS (SELECT 1 FROM billing_events WHERE idempotency_key = ? AND status = 'applying')`,args:[t,r,
|
|
1219
|
+
AND EXISTS (SELECT 1 FROM billing_events WHERE idempotency_key = ? AND status = 'applying')`,args:[t,r,c]},{sql:`WITH ordered AS (
|
|
1117
1220
|
SELECT a.amount_mc,
|
|
1118
1221
|
a.expires_at,
|
|
1119
1222
|
COALESCE(SUM(a.amount_mc) OVER (
|
|
@@ -1146,20 +1249,20 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1146
1249
|
?,
|
|
1147
1250
|
?
|
|
1148
1251
|
FROM credited
|
|
1149
|
-
WHERE refund_mc > 0`,args:[t,r,t,
|
|
1252
|
+
WHERE refund_mc > 0`,args:[t,r,t,c,s,i]},{sql:`UPDATE billing_events
|
|
1150
1253
|
SET status = 'settled', settled_at = datetime('now')
|
|
1151
1254
|
WHERE idempotency_key = ?
|
|
1152
|
-
AND status = 'applying'`,args:[
|
|
1255
|
+
AND status = 'applying'`,args:[c]}],"write")}catch(N){try{let T=await E();if(T)return T}catch{}throw N}let m=await E();if(!m)throw new Error("idempotent debit settlement did not reach a durable settled state");return{...m,duplicate:l[0]?.rowsAffected===0}}async function fs(e,t=50){return(await n().execute({sql:"SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",args:[e,t]})).rows.map(s=>({id:Number(s.id),user_id:Number(s.user_id),amount_mc:Number(s.amount_mc),operation:String(s.operation),description:s.description!=null?String(s.description):null,stripe_pi:s.stripe_pi!=null?String(s.stripe_pi):null,created_at:String(s.created_at)}))}async function Os(e,t,r){let s=n(),i=r.refund.map(()=>"?").join(", "),a=r.grant.map(()=>"?").join(", "),u=r.purchase.map(()=>"?").join(", "),c=await s.execute({sql:`SELECT
|
|
1153
1256
|
COALESCE(SUM(CASE WHEN amount_mc < 0 AND operation NOT IN (${i}) THEN -amount_mc ELSE 0 END), 0) AS spent_mc,
|
|
1154
1257
|
COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${i}) THEN amount_mc ELSE 0 END), 0) AS refunded_mc,
|
|
1155
1258
|
COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${a}) THEN amount_mc ELSE 0 END), 0) AS granted_mc,
|
|
1156
|
-
COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${
|
|
1259
|
+
COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${u}) THEN amount_mc ELSE 0 END), 0) AS purchased_mc,
|
|
1157
1260
|
COALESCE(SUM(amount_mc), 0) AS net_mc
|
|
1158
|
-
FROM ledger WHERE user_id = ? AND created_at >= ?`,args:[...r.refund,...r.refund,...r.grant,...r.purchase,e,t]}),
|
|
1261
|
+
FROM ledger WHERE user_id = ? AND created_at >= ?`,args:[...r.refund,...r.refund,...r.grant,...r.purchase,e,t]}),o=await s.execute({sql:`SELECT operation, -SUM(amount_mc) AS mc, COUNT(*) AS count
|
|
1159
1262
|
FROM ledger
|
|
1160
1263
|
WHERE user_id = ? AND created_at >= ? AND amount_mc < 0 AND operation NOT IN (${i})
|
|
1161
1264
|
GROUP BY operation
|
|
1162
|
-
ORDER BY mc DESC, operation ASC`,args:[e,t,...r.refund]}),c
|
|
1265
|
+
ORDER BY mc DESC, operation ASC`,args:[e,t,...r.refund]}),_=c.rows[0];return{spent_mc:Number(_?.spent_mc??0),refunded_mc:Number(_?.refunded_mc??0),granted_mc:Number(_?.granted_mc??0),purchased_mc:Number(_?.purchased_mc??0),net_mc:Number(_?.net_mc??0),by_operation:o.rows.map(d=>({operation:String(d.operation),mc:Number(d.mc),count:Number(d.count)}))}}async function hs(e){return(await n().execute({sql:"SELECT 1 FROM ledger WHERE stripe_pi = ? LIMIT 1",args:[e]})).rows.length>0}async function fe(e,t){let r=n();for(let i=0;i<8;i+=1){let u=(await r.execute({sql:`SELECT
|
|
1163
1266
|
u.balance_mc AS cached_balance_mc,
|
|
1164
1267
|
COUNT(l.id) AS lot_count,
|
|
1165
1268
|
COALESCE(SUM(CASE
|
|
@@ -1169,4 +1272,4 @@ import{createClient as Ce}from"@libsql/client/http";import{createHash as A,rando
|
|
|
1169
1272
|
FROM users u
|
|
1170
1273
|
LEFT JOIN credit_lots l ON l.user_id = u.id
|
|
1171
1274
|
WHERE u.id = ?
|
|
1172
|
-
GROUP BY u.id, u.balance_mc`,args:[e]})).rows[0];if(!
|
|
1275
|
+
GROUP BY u.id, u.balance_mc`,args:[e]})).rows[0];if(!u)return 0;if(Number(u.lot_count??0)===0&&(await w(Number(e)),(await r.execute({sql:"SELECT 1 FROM credit_lots WHERE user_id = ? LIMIT 1",args:[e]})).rows.length>0))continue;let c=Number(u.cached_balance_mc??0),o=Number(u.projected_balance_mc??0);if(c===o||(await r.execute({sql:"UPDATE users SET balance_mc = ? WHERE id = ? AND balance_mc = ?",args:[o,e,c]})).rowsAffected===1)return o}let s=await r.execute({sql:"SELECT balance_mc FROM users WHERE id = ? LIMIT 1",args:[e]});return Number(s.rows[0]?.balance_mc??0)}var xs=p.object({id:p.string(),user_id:p.number(),status:p.string().default("pending"),client_domain:p.string(),session_path:p.string(),request:p.string(),result:p.string().nullable(),error:p.string().nullable(),created_at:p.string(),updated_at:p.string()}),ys=p.object({id:p.number(),job_id:p.string(),phase:p.string(),completed_at:p.string(),output_summary:p.string()});async function Is(e){return(await n().execute({sql:"SELECT id, subject, sent_at, opened_at FROM inbox_messages WHERE user_id = ? AND deleted_at IS NULL ORDER BY sent_at DESC, id DESC",args:[e]})).rows.map(r=>({id:String(r.id),subject:String(r.subject),sent_at:String(r.sent_at),opened_at:r.opened_at!=null?String(r.opened_at):null}))}async function Cs(e,t){let s=(await n().execute({sql:"SELECT id, subject, body_md, source, sent_at, opened_at FROM inbox_messages WHERE id = ? AND user_id = ? AND deleted_at IS NULL",args:[t,e]})).rows[0];if(s)return{id:String(s.id),subject:String(s.subject),body_md:String(s.body_md),source:String(s.source),sent_at:String(s.sent_at),opened_at:s.opened_at!=null?String(s.opened_at):null}}async function ks(e,t){let r=n();await r.execute({sql:"UPDATE inbox_messages SET opened_at = datetime('now') WHERE id = ? AND user_id = ? AND deleted_at IS NULL AND opened_at IS NULL",args:[t,e]});let i=(await r.execute({sql:"SELECT opened_at FROM inbox_messages WHERE id = ? AND user_id = ? AND deleted_at IS NULL",args:[t,e]})).rows[0];return i&&i.opened_at!=null?String(i.opened_at):null}async function Us(e,t){return(await n().execute({sql:"UPDATE inbox_messages SET deleted_at = datetime('now') WHERE id = ? AND user_id = ? AND deleted_at IS NULL",args:[t,e]})).rowsAffected>0}async function Ds(e,t,r,s="operator"){let i=g();return await n().execute({sql:"INSERT INTO inbox_messages (id, user_id, subject, body_md, source) VALUES (?, ?, ?, ?, ?)",args:[i,e,t,r,s]}),i}async function vs(){return(await n().execute("SELECT id FROM users WHERE active = 1")).rows.map(t=>Number(t.id))}export{dt as a,xe as b,ye as c,R as d,se as e,P as f,Fe as g,Pe as h,ie as i,z as j,Q as k,Z as l,ee as m,te as n,re as o,C as p,q,_e as r,n as s,Lt as t,St as u,Rt as v,At as w,wt as x,ft as y,Ot as z,ht as A,Ve as B,xt as C,yt as D,It as E,Ct as F,kt as G,Ut as H,Dt as I,pe as J,me as K,vt as L,Xt as M,Mt as N,Ft as O,Pt as P,Ht as Q,qt as R,Wt as S,jt as T,Bt as U,Gt as V,Yt as W,Jt as X,$t as Y,Kt as Z,Vt as _,zt as $,ze as aa,Qt as ba,Zt as ca,er as da,tr as ea,rr as fa,sr as ga,nr as ha,ir as ia,ar as ja,be as ka,or as la,ur as ma,cr as na,_r as oa,dr as pa,lr as qa,B as ra,Er as sa,Tr as ta,pr as ua,mr as va,gr as wa,Nr as xa,tt as ya,rt as za,br as Aa,Lr as Ba,Sr as Ca,Rr as Da,Ar as Ea,wr as Fa,fr as Ga,Or as Ha,we as Ia,hr as Ja,xr as Ka,yr as La,Ir as Ma,Cr as Na,kr as Oa,Ur as Pa,Dr as Qa,vr as Ra,Xr as Sa,Mr as Ta,Fr as Ua,Pr as Va,Hr as Wa,qr as Xa,Wr as Ya,jr as Za,Br as _a,Gr as $a,Yr as ab,Jr as bb,$r as cb,Kr as db,Vr as eb,zr as fb,Qr as gb,Zr as hb,es as ib,ts as jb,rs as kb,ss as lb,ns as mb,is as nb,as as ob,os as pb,it as qb,us as rb,cs as sb,ot as tb,_s as ub,ds as vb,ls as wb,Es as xb,Ts as yb,ps as zb,ms as Ab,X as Bb,gs as Cb,Ns as Db,bs as Eb,Ls as Fb,Ss as Gb,Rs as Hb,As as Ib,ut as Jb,ws as Kb,fs as Lb,Os as Mb,hs as Nb,fe as Ob,xs as Pb,ys as Qb,Is as Rb,Cs as Sb,ks as Tb,Us as Ub,Ds as Vb,vs as Wb};
|