mcp-scraper 0.95.2 → 0.96.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +67 -1
  2. package/README.md +21 -3
  3. package/dist/actions-R4EJDLRO.js +1 -0
  4. package/dist/artifacts-4ZDNP2JW.js +1 -0
  5. package/dist/artifacts-N62OPIT5.js +1 -0
  6. package/dist/artifacts-PSADD5OC.js +1 -0
  7. package/dist/artifacts-RUR3OHAK.js +1 -0
  8. package/dist/bin/api-server.js +1 -1
  9. package/dist/bin/mcp-scraper-cli.js +22 -22
  10. package/dist/bin/mcp-scraper-core.js +1 -1
  11. package/dist/bin/mcp-scraper-install.js +1 -1
  12. package/dist/bin/mcp-stdio-server.js +1 -1
  13. package/dist/bin/paa-harvest.js +1 -1
  14. package/dist/blob-store-GIH26EDO.js +1 -0
  15. package/dist/bundle-FCXZMIRB.js +1 -0
  16. package/dist/chunk-24YDQ3EO.js +5 -0
  17. package/dist/chunk-3F7AFM53.js +1 -0
  18. package/dist/chunk-4HZXATHB.js +27 -0
  19. package/dist/chunk-55ZANTHL.js +3 -0
  20. package/dist/chunk-5BKSKLHX.js +1 -0
  21. package/dist/chunk-5LYINW7Z.js +8 -0
  22. package/dist/chunk-63VWBMYR.js +3 -0
  23. package/dist/chunk-6BD75VKS.js +218 -0
  24. package/dist/chunk-6N5EC7ZO.js +1 -0
  25. package/dist/chunk-7POHNTVW.js +1 -0
  26. package/dist/chunk-7SR7MEU5.js +62 -0
  27. package/dist/chunk-7WPCGRD7.js +182 -0
  28. package/dist/chunk-B3JRL45T.js +1 -0
  29. package/dist/chunk-B4ZKPLSP.js +1 -0
  30. package/dist/chunk-BPKTUCV7.js +12 -0
  31. package/dist/chunk-C7MGJUTU.js +1 -0
  32. package/dist/{chunk-W2BVJ7S2.js → chunk-CNSVZTTT.js} +4 -4
  33. package/dist/chunk-CORWWDHL.js +1 -0
  34. package/dist/chunk-CVQUHVHX.js +1 -0
  35. package/dist/chunk-FB4KBB5W.js +16 -0
  36. package/dist/{chunk-Y3Q4FIKK.js → chunk-H6MODA7W.js} +199 -84
  37. package/dist/chunk-HCK6PLM3.js +3 -0
  38. package/dist/chunk-HNNYXMSI.js +1 -0
  39. package/dist/chunk-HUNSEGUN.js +57 -0
  40. package/dist/{chunk-MC4W6TZT.js → chunk-HVQZRQRJ.js} +41 -8
  41. package/dist/chunk-IFPION5D.js +121 -0
  42. package/dist/chunk-IY6ZUUY7.js +173 -0
  43. package/dist/chunk-JHT6A4U6.js +11 -0
  44. package/dist/chunk-JMZIDHYQ.js +2 -0
  45. package/dist/chunk-KXJCGV3H.js +1 -0
  46. package/dist/chunk-MVMWPKRC.js +2 -0
  47. package/dist/chunk-NALXKTUU.js +1 -0
  48. package/dist/chunk-NNOX3FYG.js +198 -0
  49. package/dist/chunk-OILOHQNL.js +2 -0
  50. package/dist/chunk-OLYPLX5C.js +1 -0
  51. package/dist/chunk-QER2ELZP.js +1 -0
  52. package/dist/chunk-RDHGQ2WZ.js +1 -0
  53. package/dist/chunk-RIHI3KH7.js +955 -0
  54. package/dist/chunk-RN4VG3VH.js +4 -0
  55. package/dist/chunk-RYX4FJVH.js +1 -0
  56. package/dist/chunk-SDQW7IJN.js +1 -0
  57. package/dist/{chunk-ENZUJEIM.js → chunk-SE4VKLEC.js} +1 -1
  58. package/dist/chunk-SEZETSB3.js +1 -0
  59. package/dist/chunk-SFUOSJ3A.js +159 -0
  60. package/dist/chunk-UIOSADPB.js +1718 -0
  61. package/dist/{chunk-ZH75KWBI.js → chunk-UNEJYZ4T.js} +1 -1
  62. package/dist/chunk-UXNR3K6I.js +1 -0
  63. package/dist/{chunk-VREXSMC6.js → chunk-VVSHZV5S.js} +5 -5
  64. package/dist/chunk-WNUXMPVX.js +1 -0
  65. package/dist/chunk-WP3QS4CJ.js +1 -0
  66. package/dist/chunk-ZTMQEDIB.js +41 -0
  67. package/dist/contracts-GUXGFCVV.js +1 -0
  68. package/dist/core-schema-423BSTE6.js +936 -0
  69. package/dist/dispatch-P7IY75LH.js +1 -0
  70. package/dist/index.cjs +103 -213
  71. package/dist/index.d.cts +184 -169
  72. package/dist/index.d.ts +184 -169
  73. package/dist/index.js +1 -1
  74. package/dist/media-artifacts-67CJWI7O.js +1 -0
  75. package/dist/metering-ZH2P62IC.js +1 -0
  76. package/dist/migrations-3AKEC5QE.js +1 -0
  77. package/dist/queue-AC4XKD6Z.js +1 -0
  78. package/dist/reconciliation-UZUZ6B74.js +1 -0
  79. package/dist/report-artifact-offload-6OOY2PTM.js +1 -0
  80. package/dist/repository-4EC5EXVN.js +1 -0
  81. package/dist/{location-data-repository-A2QZVP7O.js → repository-CYXVMYAI.js} +1 -1
  82. package/dist/server-DXZ27KQO.js +5825 -0
  83. package/dist/settlement-6CS4JZTY.js +1 -0
  84. package/dist/worker-ATRD6VKM.js +1 -0
  85. package/package.json +2 -2
  86. package/dist/analytics-repository-DZZKDZL6.js +0 -1
  87. package/dist/chunk-6DRKDSXY.js +0 -182
  88. package/dist/chunk-6ND5AFV5.js +0 -27
  89. package/dist/chunk-7XDBGQDY.js +0 -6
  90. package/dist/chunk-7YOXSMHG.js +0 -1565
  91. package/dist/chunk-BLVBUMH5.js +0 -21
  92. package/dist/chunk-EDQ57S4P.js +0 -1
  93. package/dist/chunk-GLTECY3U.js +0 -1282
  94. package/dist/chunk-GPSPSJBJ.js +0 -1
  95. package/dist/chunk-HE45FFBU.js +0 -1
  96. package/dist/chunk-HUV2WTRW.js +0 -1
  97. package/dist/chunk-IXJQHXDC.js +0 -2595
  98. package/dist/chunk-K3D2S36Q.js +0 -16
  99. package/dist/chunk-KFOFU3LA.js +0 -1
  100. package/dist/chunk-LMWU7VPZ.js +0 -63
  101. package/dist/chunk-O4LUUIYG.js +0 -4
  102. package/dist/chunk-OLSW4AN4.js +0 -19
  103. package/dist/chunk-RLFCJGHA.js +0 -100
  104. package/dist/chunk-T4VTJNDZ.js +0 -84
  105. package/dist/chunk-TMB56NCA.js +0 -1
  106. package/dist/chunk-VXOXB3IB.js +0 -1
  107. package/dist/chunk-WO3N5FH2.js +0 -5
  108. package/dist/chunk-WO46LTBW.js +0 -1
  109. package/dist/chunk-XPEJB4BZ.js +0 -1
  110. package/dist/db-CDEZHBRN.js +0 -1
  111. package/dist/extract-bundle-SXC6EWJR.js +0 -26
  112. package/dist/gmail-service-EWR76XE7.js +0 -1
  113. package/dist/lead-list-enrichment-repository-SIWGXWQV.js +0 -1
  114. package/dist/operation-metering-UJ53YX7S.js +0 -1
  115. package/dist/server-QVEKDIDQ.js +0 -5014
  116. package/dist/site-extract-repository-23ND4N77.js +0 -1
  117. package/dist/stripe-event-worker-LPXTITVC.js +0 -1
  118. package/dist/worker-EDN2TJH3.js +0 -1
  119. /package/dist/{chunk-YGBTTW5D.js → chunk-2IATUJO6.js} +0 -0
  120. /package/dist/{chunk-6DTXIZY2.js → chunk-4ZVO6GX4.js} +0 -0
  121. /package/dist/{chunk-XG6GCEUE.js → chunk-U4WIJZCX.js} +0 -0
@@ -1,1565 +0,0 @@
1
- import{createClient as Ve}from"@libsql/client/http";import{createHash as v,randomBytes as j,randomUUID as N,scryptSync as Ne,timingSafeEqual as ze}from"crypto";import{z as p}from"zod";var k="2026-06-26 21:58:03";var Lt="Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.",ve="This operation is temporarily unavailable. Please retry later.",ke=["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","directory_job_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"],Xe=["request_aborted","captcha","location_mismatch","connection","timeout","extraction","concurrency_limit","unauthorized","forbidden","invalid_request","not_found","rate_limit","billing","service_unavailable"];function O(e){return e?JSON.stringify(e):null}function ae(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"||!oe.has(r.error_code))return null;let n=Ge({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 s=_e(r.message).trim().slice(0,1e3);s&&!/https?:\/\//i.test(s)&&(n.message=s)}return n}catch{return null}}var oe=new Set(ke),Fe=new Set(Xe),Me=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"]),Pe=12,He=500;function W(e){return typeof e=="string"&&oe.has(e)?e:"service_unavailable"}function je(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>=Pe)break;if(Me.has(r))if(typeof n=="string"){if((r==="upgrade_url"||r==="topup_url")&&!qe(n))continue;let s=_e(n).slice(0,He);s&&(t[r]=s)}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 qe(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 We(e){let t=W(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";case"directory_job_not_found":return"not_found";default:return t}}function Be(e){switch(W(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. Check retryable before attempting it again.";case"directory_job_not_found":return"This directory job was not found for the current account.";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 ve}}function Ge(e){let t=W(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,s=typeof e.errorType=="string"&&Fe.has(e.errorType)?e.errorType:void 0,a=je(e.details);return{error_code:t,error_type:s??We(t),message:Be(t),retryable:e.retryable===!0,...r?{retry_after_seconds:r}:{},...n?{charge_status:n}:{},...a?{details:a}:{}}}function _e(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 ee=class extends Error{constructor(r){super(`CAPTCHA detected. ${r}`);this.instructions=r}instructions;name="CaptchaError"},te=class extends Error{constructor(r,n){super(r);this.cause=n}cause;name="ExtractionError"},re=class extends Error{constructor(r){super(`Google Maps search did not reach a complete result state (${r})`);this.pageState=r}pageState;name="MapsSearchIncompletePageError"},ne=class extends Error{name="RequestAbortedError";constructor(t="Request aborted before harvest completed"){super(t)}},se=class extends Error{name="PaaInteractionInterruptedError"},ie=class extends Error{name="LocationMismatchError";constructor(t="Google returned results for a different location than requested"){super(t)}};var I=["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"],x=["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"],ue={oauth_clients:"oauth_clients_legacy_raw",oauth_codes:"oauth_codes_legacy_raw",oauth_tokens:"oauth_tokens_legacy_raw"},L="strftime('%Y-%m-%dT%H:%M:%fZ','now')";async function y(e,t,r=!1){try{await e.execute(t)}catch(n){let s=n.code;if(!r||s!=="SQL_PARSE_ERROR")throw n}}var Ye=[`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 (${L})
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 (${L}),
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 (${L}),
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 (${L}),
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 (${L}),
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 (${L}),
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 (${L}),
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 (${L}),
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)"],ce=["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'`],B=[`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 (${L}),
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)`],X=[...Ye,...ce,...B];async function Ke(e){if(await y(e,"PRAGMA foreign_keys = ON"),await y(e,"PRAGMA busy_timeout = 5000",!0),(await e.execute("SELECT name FROM pragma_table_info('oauth_codes')")).rows.some(_=>String(_.name)==="code"))throw new Error("Legacy raw OAuth tables require the explicit maintenance-mode coexistence migration");let n=(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,s=n?.rows[0]?.schema_version==null?void 0:Number(n.rows[0].schema_version);if(s!==void 0&&(!Number.isSafeInteger(s)||s<1||s>3))throw new Error("OAuth store schema version cannot be migrated by this runtime");let a=s===void 0?X:s===1?[...ce,...B]:s===2?B:[];for(let _ of a)await e.execute(_);if(s!==void 0&&s<3&&(await e.execute({sql:`UPDATE oauth_store_metadata SET schema_version=?,updated_at=${L} WHERE id=1 AND schema_version=?`,args:[3,s]})).rowsAffected!==1)throw new Error("OAuth store schema version changed during migration");await y(e,"PRAGMA optimize",!0)}async function de(e){await y(e,"PRAGMA foreign_keys = ON"),await y(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(_=>String(_.name)==="code_digest");return await Ke(e),a?"current":"created"}if((await e.execute({sql:"SELECT name FROM sqlite_master WHERE type='table' AND name IN (?,?,?)",args:Object.values(ue)})).rows.length>0)throw new Error("Legacy OAuth archive names already exist; refusing ambiguous migration");let s=await e.transaction("write");try{for(let[a,_]of Object.entries(ue))(await s.execute({sql:"SELECT 1 present FROM sqlite_master WHERE type='table' AND name=?",args:[a]})).rows.length>0&&await s.execute({sql:`ALTER TABLE ${a} RENAME TO ${_}`,args:[]});for(let a of X)await s.execute({sql:a,args:[]});await s.commit()}finally{s.close()}return"archived_legacy"}var $e={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 Ee(e){let t=await e.execute({sql:`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${I.map(()=>"?").join(",")})`,args:[...I]}),r=await e.execute({sql:`SELECT name FROM sqlite_master WHERE type = 'index' AND name IN (${x.map(()=>"?").join(",")})`,args:[...x]}),n=new Set(t.rows.map(o=>String(o.name))),s=new Set(r.rows.map(o=>String(o.name))),a=await e.execute("PRAGMA foreign_keys"),_=[],u=o=>o.replace(/\bIF\s+NOT\s+EXISTS\b/gi,"").replace(/\s+/g," ").trim().toLowerCase();for(let o of I){if(!n.has(o))continue;let d=(await e.execute(`PRAGMA table_info('${o}')`)).rows.map(g=>String(g.name));JSON.stringify(d)!==JSON.stringify($e[o])&&_.push(`${o}:columns`);let l=await e.execute({sql:"SELECT sql FROM sqlite_master WHERE type='table' AND name=?",args:[o]}),E=String(l.rows[0]?.sql??""),m=X.find(g=>u(g).startsWith(`create table ${o} `));(!m||u(E)!==u(m))&&_.push(`${o}:definition`)}for(let o of x){if(!s.has(o))continue;let d=(await e.execute(`PRAGMA index_info('${o}')`)).rows.map(g=>g.name==null?null:String(g.name));JSON.stringify(d)!==JSON.stringify(Je[o])&&_.push(`${o}:columns`);let l=await e.execute({sql:"SELECT sql FROM sqlite_master WHERE type='index' AND name=?",args:[o]}),E=String(l.rows[0]?.sql??""),m=X.find(g=>u(g).startsWith(`create index ${o} `));(!m||u(E)!==u(m))&&_.push(`${o}:definition`)}return{ok:I.every(o=>n.has(o))&&x.every(o=>s.has(o))&&Number(a.rows[0]?.foreign_keys??0)===1&&_.length===0,missingTables:I.filter(o=>!n.has(o)),missingIndexes:x.filter(o=>!s.has(o)),foreignKeysEnabled:Number(a.rows[0]?.foreign_keys??0)===1,problems:_}}var Qe=process.env.TURSO_DATABASE_URL??"file:./paa-api.db",Ze=process.env.TURSO_AUTH_TOKEN,M=null,U="2026-09-25.1",le="data-2026-07-28-site-extract-counters.1",Te=["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","operation_attempts","operation_billing_links","operation_coverage_defects","operation_runs","maps_place_runs","maps_place_attempts","maps_place_run_events","maps_place_reviews","maps_place_images","page_snapshots","password_reset_tokens","rate_limits","request_events","provider_reconciliation_runs","provider_account_windows","provider_usage_receipts","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"],pe=["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","operation_attempts_provider_status","operation_attempts_run_number","operation_billing_links_run","operation_coverage_defects_created","operation_runs_idempotency_scope","operation_runs_status_started","operation_runs_tool_started","operation_runs_user_started","maps_place_runs_owner_key","maps_place_runs_status_lease","maps_place_attempts_run_number","maps_place_reviews_order","maps_place_images_order","page_snapshots_user_checked_at","prt_user_id","request_events_created_at","request_events_user_created_at","provider_reconciliation_runs_provider_started","provider_account_windows_lookup","provider_usage_receipts_attempt","provider_usage_receipts_pending","provider_usage_receipts_provider_source","scheduled_run_view_links_expiry","scheduled_run_view_links_owner_run","stripe_events_due","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"],me=["credit_lots_bounds_insert","credit_lots_bounds_update","credit_lots_one_migration_insert","credit_lots_one_migration_update"],et={maps_place_runs:["request_fingerprint","field_states_json","core_json","checkpoint_revision","current_attempt_id","current_fence_token","lease_expires_at","billing_state"],maps_place_attempts:["attempt_number","resume_key_hash","fence_token","hold_key","held_amount_mc","final_amount_mc","billing_state"],maps_place_reviews:["review_key","record_json"],maps_place_images:["media_key","record_json"],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"],provider_usage_receipts:["provider_product","billing_tier","billable_event","measurement_source","rate_numerator","rate_denominator","rate_policy_version","rate_effective_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","claimed_at","completed_at","event_type","last_error","last_error_code","next_attempt_at","received_at","status","stripe_created_at","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"]},b="provider_usage_receipts",f="provider_usage_receipts_measured_v1",ge="cost_state IN ('pending', 'estimated', 'measured', 'actual', 'unavailable')";function Le(e=b){if(e!==b&&e!==f)throw new Error(`Unsupported provider receipt table name: ${e}`);return`
113
- CREATE TABLE IF NOT EXISTS ${e} (
114
- id TEXT PRIMARY KEY,
115
- attempt_id TEXT NOT NULL REFERENCES operation_attempts(id) ON DELETE CASCADE,
116
- provider TEXT NOT NULL,
117
- account_scope_hash TEXT NOT NULL,
118
- method TEXT NOT NULL,
119
- provider_source_key TEXT,
120
- provider_source_key_hash TEXT,
121
- cost_state TEXT NOT NULL DEFAULT 'pending'
122
- CHECK (cost_state IN ('pending', 'estimated', 'measured', 'actual', 'unavailable')),
123
- provider_product TEXT,
124
- billing_tier TEXT,
125
- billable_event TEXT,
126
- measurement_source TEXT,
127
- rate_numerator INTEGER CHECK (rate_numerator IS NULL OR rate_numerator >= 0),
128
- rate_denominator INTEGER CHECK (rate_denominator IS NULL OR rate_denominator > 0),
129
- rate_policy_version TEXT,
130
- rate_effective_at TEXT,
131
- units REAL CHECK (units IS NULL OR units >= 0),
132
- unit_type TEXT,
133
- amount_usd_nanos INTEGER CHECK (amount_usd_nanos IS NULL OR amount_usd_nanos >= 0),
134
- currency TEXT NOT NULL DEFAULT 'USD',
135
- rate_source TEXT,
136
- zero_cost_reason TEXT,
137
- provider_status TEXT,
138
- provider_duration_ms INTEGER CHECK (provider_duration_ms IS NULL OR provider_duration_ms >= 0),
139
- provider_captcha INTEGER CHECK (provider_captcha IS NULL OR provider_captcha IN (0, 1)),
140
- error_code TEXT,
141
- error_message TEXT,
142
- checked_at TEXT,
143
- finalized_at TEXT,
144
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
145
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
146
- CHECK (provider_source_key IS NULL OR provider_source_key_hash IS NOT NULL),
147
- CHECK (cost_state <> 'pending' OR (amount_usd_nanos IS NULL AND finalized_at IS NULL)),
148
- CHECK (cost_state <> 'actual' OR (amount_usd_nanos IS NOT NULL AND finalized_at IS NOT NULL
149
- AND (amount_usd_nanos > 0 OR zero_cost_reason IS NOT NULL))),
150
- CHECK (cost_state NOT IN ('estimated', 'measured', 'unavailable') OR finalized_at IS NOT NULL),
151
- CHECK (cost_state <> 'measured' OR (amount_usd_nanos IS NOT NULL
152
- AND (amount_usd_nanos > 0 OR zero_cost_reason IS NOT NULL)))
153
- )
154
- `}async function be(e){let t=await e.execute({sql:"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",args:[b]});return t.rows[0]?.sql==null?null:String(t.rows[0].sql)}function Se(e){return e==null||e.includes(ge)}async function tt(e){let t=await be(e);if(Se(t))return"not_needed";if((await e.execute({sql:"SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1",args:[f]})).rows.length>0)throw new Error(`Refusing provider receipt migration because ${f} already exists`);let s=(await e.execute(`PRAGMA table_info(${b})`)).rows.map(a=>String(a.name)).map(a=>`"${a.replaceAll('"','""')}"`).join(", ");if(!s)throw new Error("Provider receipt migration found no source columns");return await e.batch([Le(f),`INSERT INTO ${f} (${s}) SELECT ${s} FROM ${b}`,`DROP TABLE ${b}`,`ALTER TABLE ${f} RENAME TO ${b}`,"CREATE INDEX provider_usage_receipts_attempt ON provider_usage_receipts(attempt_id, created_at)","CREATE INDEX provider_usage_receipts_pending ON provider_usage_receipts(cost_state, checked_at, created_at) WHERE cost_state = 'pending'","CREATE UNIQUE INDEX provider_usage_receipts_provider_source ON provider_usage_receipts(provider, account_scope_hash, method, provider_source_key_hash) WHERE provider_source_key_hash IS NOT NULL"],"write"),"rebuilt"}function F(e){return e.map(t=>`'${t.replaceAll("'","''")}'`).join(", ")}async function P(e){let t=[`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN (${F(Te)})) = ${Te.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name IN (${F(pe)})) = ${pe.length}`,`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name IN (${F(me)})) = ${me.length}`,...Object.entries(et).map(([n,s])=>`(SELECT COUNT(*) FROM pragma_table_info('${n}') WHERE name IN (${F(s)})) = ${s.length}`),`(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = '${b}' AND instr(sql, '${ge.replaceAll("'","''")}') > 0) = 1`],r=await e.execute(`SELECT ${t.join(" AND ")} AS ready`);return Number(r.rows[0]?.ready??0)===1}async function It(e=i()){let t=!1;try{t=(await e.execute({sql:"SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",args:[U]})).rows.length===1}catch(n){let s=n instanceof Error?n.message.toLowerCase():String(n).toLowerCase();if(!s.includes("no such table")||!s.includes("schema_migrations"))throw n}let r=t?await P(e):!1;return{coreSchemaVersion:U,migrationApplied:t,schemaReady:r}}async function rt(e){await e.execute(`
155
- UPDATE site_extract_jobs SET
156
- attempted_urls = done_urls,
157
- successful_urls = (
158
- SELECT COUNT(*) FROM site_extract_pages
159
- WHERE job_id = site_extract_jobs.id
160
- AND CASE
161
- WHEN json_valid(page) = 0 THEN 0
162
- WHEN json_extract(page, '$.extractionStatus') IS NOT NULL
163
- THEN json_extract(page, '$.extractionStatus') = 'successful'
164
- ELSE COALESCE(
165
- json_extract(page, '$.status') >= 200
166
- AND json_extract(page, '$.status') < 300
167
- AND json_extract(page, '$.wordCount') > 0,
168
- 0
169
- )
170
- END
171
- ),
172
- failed_urls = MAX(0, done_urls - (
173
- SELECT COUNT(*) FROM site_extract_pages
174
- WHERE job_id = site_extract_jobs.id
175
- AND CASE
176
- WHEN json_valid(page) = 0 THEN 0
177
- WHEN json_extract(page, '$.extractionStatus') IS NOT NULL
178
- THEN json_extract(page, '$.extractionStatus') = 'successful'
179
- ELSE COALESCE(
180
- json_extract(page, '$.status') >= 200
181
- AND json_extract(page, '$.status') < 300
182
- AND json_extract(page, '$.wordCount') > 0,
183
- 0
184
- )
185
- END
186
- ))
187
- WHERE done_urls > 0
188
- AND attempted_urls = 0
189
- AND successful_urls = 0
190
- AND failed_urls = 0
191
- `)}async function G(e){(await e.execute({sql:"SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",args:[le]})).rows.length>0||(await rt(e),await e.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[le]}))}function i(){return M||(M=Ve({url:Qe,authToken:Ze})),M}function xt(e){M=e}async function yt(e=i()){if(process.env.OAUTH_MAINTENANCE_MODE!=="true")throw new Error("Secured main OAuth schema migration requires OAUTH_MAINTENANCE_MODE=true");await de(e)}async function Ct(e=i()){return Ee(e)}async function nt(e={}){let t=i(),r;try{r=await t.execute({sql:"SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1",args:[U]})}catch(s){let a=s instanceof Error?s.message.toLowerCase():String(s).toLowerCase();if(!a.includes("no such table")||!a.includes("schema_migrations"))throw s;await t.execute(`
192
- CREATE TABLE IF NOT EXISTS schema_migrations (
193
- version TEXT PRIMARY KEY,
194
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
195
- )
196
- `),r={rows:[]}}if(r.rows.length>0){if(!e.verifyCurrentSchema)return;if(await P(t)){e.runDataRepairs&&await G(t);return}}let n=await be(t);if(!Se(n))throw new Error("Provider receipt schema requires the explicit operator migration: run npm run db:migrate before starting the new application release");if(await P(t)){await G(t),await t.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[U]});return}await t.execute(`
197
- CREATE TABLE IF NOT EXISTS users (
198
- id INTEGER PRIMARY KEY AUTOINCREMENT,
199
- email TEXT UNIQUE NOT NULL,
200
- name TEXT,
201
- api_key TEXT UNIQUE NOT NULL,
202
- password_hash TEXT,
203
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
204
- active INTEGER NOT NULL DEFAULT 1
205
- )
206
- `),await t.execute(`
207
- CREATE TABLE IF NOT EXISTS jobs (
208
- id TEXT PRIMARY KEY,
209
- user_id INTEGER NOT NULL REFERENCES users(id),
210
- status TEXT NOT NULL DEFAULT 'pending',
211
- query TEXT NOT NULL,
212
- options TEXT NOT NULL,
213
- callback_url TEXT,
214
- result TEXT,
215
- error TEXT,
216
- public_error_json TEXT,
217
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
218
- started_at TEXT,
219
- completed_at TEXT
220
- )
221
- `),await t.execute("CREATE INDEX IF NOT EXISTS jobs_user_id ON jobs(user_id)"),await t.execute("CREATE INDEX IF NOT EXISTS jobs_status ON jobs(status)");try{await t.execute("ALTER TABLE jobs ADD COLUMN public_error_json TEXT")}catch{}await t.execute("CREATE INDEX IF NOT EXISTS jobs_created_at_status_user ON jobs(created_at, status, user_id)"),await t.execute("CREATE INDEX IF NOT EXISTS jobs_user_created_at_status ON jobs(user_id, created_at, status)"),await t.execute(`
222
- CREATE TABLE IF NOT EXISTS harvest_attempts (
223
- id TEXT PRIMARY KEY,
224
- job_id TEXT NOT NULL REFERENCES jobs(id),
225
- user_id INTEGER NOT NULL REFERENCES users(id),
226
- attempt_number INTEGER NOT NULL,
227
- max_attempts INTEGER NOT NULL,
228
- query TEXT NOT NULL,
229
- location TEXT,
230
- max_questions INTEGER NOT NULL,
231
- status TEXT NOT NULL,
232
- outcome TEXT,
233
- \u006b\u0065\u0072\u006e\u0065\u006c_session_id TEXT,
234
- question_count INTEGER,
235
- duration_ms INTEGER,
236
- error TEXT,
237
- will_retry INTEGER,
238
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_started INTEGER NOT NULL DEFAULT 0,
239
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_succeeded INTEGER,
240
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_error TEXT,
241
- browser_close_succeeded INTEGER,
242
- browser_close_error TEXT,
243
- debug_json TEXT,
244
- browser_provider TEXT,
245
- provider_session_id TEXT,
246
- provider_session_observed_at TEXT,
247
- provider_lookup_status TEXT,
248
- provider_status TEXT,
249
- provider_error_code TEXT,
250
- provider_error_message TEXT,
251
- provider_reason_source TEXT,
252
- provider_duration_ms INTEGER,
253
- provider_bandwidth_bytes INTEGER,
254
- provider_captcha INTEGER,
255
- provider_checked_at TEXT,
256
- provider_detail_json TEXT,
257
- provider_disconnect_observed INTEGER,
258
- provider_disconnect_message TEXT,
259
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
260
- completed_at TEXT,
261
- UNIQUE(job_id, attempt_number)
262
- )
263
- `),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(`
264
- CREATE TABLE IF NOT EXISTS kpo_jobs (
265
- id TEXT PRIMARY KEY,
266
- user_id INTEGER NOT NULL REFERENCES users(id),
267
- status TEXT NOT NULL,
268
- entity_name TEXT NOT NULL,
269
- entity_type TEXT NOT NULL,
270
- url TEXT,
271
- request TEXT NOT NULL,
272
- result TEXT,
273
- error TEXT,
274
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
275
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
276
- )
277
- `),await t.execute(`
278
- CREATE TABLE IF NOT EXISTS kpo_phase_log (
279
- id INTEGER PRIMARY KEY AUTOINCREMENT,
280
- job_id TEXT NOT NULL REFERENCES kpo_jobs(id),
281
- phase TEXT NOT NULL,
282
- output TEXT NOT NULL,
283
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
284
- )
285
- `),await t.execute("CREATE INDEX IF NOT EXISTS kpo_jobs_user_id ON kpo_jobs(user_id)"),await t.execute("CREATE INDEX IF NOT EXISTS kpo_jobs_status ON kpo_jobs(status)"),await t.execute("CREATE INDEX IF NOT EXISTS kpo_phase_log_job_id ON kpo_phase_log(job_id)");try{await t.execute("ALTER TABLE users ADD COLUMN stripe_customer_id TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN balance_mc INTEGER NOT NULL DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN extra_concurrency_slots INTEGER NOT NULL DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN concurrency_stripe_sub_id TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN concurrency_pack_quantity INTEGER NOT NULL DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN subscription_concurrency INTEGER NOT NULL DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN subscription_tier TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN subscription_id TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN memory_key TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN memory_identity TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN memory_plan TEXT NOT NULL DEFAULT 'free'")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN memory_subscription_id TEXT")}catch{}try{await t.execute("ALTER TABLE users ADD COLUMN memory_provisioned INTEGER NOT NULL DEFAULT 0")}catch{}await t.execute(`
286
- CREATE TABLE IF NOT EXISTS concurrency_locks (
287
- id TEXT PRIMARY KEY,
288
- user_id INTEGER NOT NULL REFERENCES users(id),
289
- operation TEXT NOT NULL,
290
- status TEXT NOT NULL DEFAULT 'active',
291
- acquired_at TEXT NOT NULL DEFAULT (datetime('now')),
292
- expires_at TEXT NOT NULL,
293
- released_at TEXT,
294
- metadata TEXT
295
- )
296
- `),await t.execute("CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)"),await t.execute("CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)"),await t.execute("CREATE INDEX IF NOT EXISTS concurrency_locks_active_user ON concurrency_locks(user_id, expires_at) WHERE status = 'active'"),await t.execute(`
297
- CREATE TABLE IF NOT EXISTS ledger (
298
- id INTEGER PRIMARY KEY AUTOINCREMENT,
299
- user_id INTEGER NOT NULL REFERENCES users(id),
300
- amount_mc INTEGER NOT NULL,
301
- operation TEXT NOT NULL,
302
- description TEXT,
303
- stripe_pi TEXT,
304
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
305
- )
306
- `),await t.execute("CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)"),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS ledger_stripe_pi_unique ON ledger(stripe_pi) WHERE stripe_pi IS NOT NULL"),await t.execute(`
307
- CREATE TABLE IF NOT EXISTS credit_lots (
308
- id INTEGER PRIMARY KEY AUTOINCREMENT,
309
- user_id INTEGER NOT NULL REFERENCES users(id),
310
- amount_mc INTEGER NOT NULL,
311
- remaining_mc INTEGER NOT NULL,
312
- source TEXT NOT NULL,
313
- stripe_pi TEXT,
314
- granted_at TEXT NOT NULL DEFAULT (datetime('now')),
315
- expires_at TEXT NOT NULL,
316
- never_expires INTEGER NOT NULL DEFAULT 0
317
- )
318
- `);try{await t.execute("ALTER TABLE credit_lots ADD COLUMN never_expires INTEGER NOT NULL DEFAULT 0")}catch{}await t.execute("CREATE INDEX IF NOT EXISTS credit_lots_user_active ON credit_lots(user_id, expires_at, remaining_mc)"),await t.execute(`
319
- CREATE TABLE IF NOT EXISTS credit_lot_expirations (
320
- lot_id INTEGER PRIMARY KEY REFERENCES credit_lots(id),
321
- user_id INTEGER NOT NULL REFERENCES users(id),
322
- amount_mc INTEGER NOT NULL,
323
- claim_token TEXT NOT NULL UNIQUE,
324
- expired_at TEXT NOT NULL DEFAULT (datetime('now'))
325
- )
326
- `),await t.execute(`
327
- CREATE TRIGGER IF NOT EXISTS credit_lots_bounds_insert
328
- BEFORE INSERT ON credit_lots
329
- WHEN NEW.amount_mc < 0
330
- OR NEW.remaining_mc < 0
331
- OR NEW.remaining_mc > NEW.amount_mc
332
- BEGIN
333
- SELECT RAISE(ABORT, 'invalid credit lot bounds');
334
- END
335
- `),await t.execute(`
336
- CREATE TRIGGER IF NOT EXISTS credit_lots_bounds_update
337
- BEFORE UPDATE OF amount_mc, remaining_mc ON credit_lots
338
- WHEN NEW.amount_mc < 0
339
- OR NEW.remaining_mc < 0
340
- OR NEW.remaining_mc > NEW.amount_mc
341
- BEGIN
342
- SELECT RAISE(ABORT, 'invalid credit lot bounds');
343
- END
344
- `),await t.execute(`
345
- CREATE TRIGGER IF NOT EXISTS credit_lots_one_migration_insert
346
- BEFORE INSERT ON credit_lots
347
- WHEN NEW.source = 'migration'
348
- AND EXISTS (
349
- SELECT 1 FROM credit_lots
350
- WHERE user_id = NEW.user_id AND source = 'migration'
351
- )
352
- BEGIN
353
- SELECT RAISE(ABORT, 'duplicate migration lot');
354
- END
355
- `),await t.execute(`
356
- CREATE TRIGGER IF NOT EXISTS credit_lots_one_migration_update
357
- BEFORE UPDATE OF source, user_id ON credit_lots
358
- WHEN NEW.source = 'migration'
359
- AND EXISTS (
360
- SELECT 1 FROM credit_lots
361
- WHERE user_id = NEW.user_id
362
- AND source = 'migration'
363
- AND id != OLD.id
364
- )
365
- BEGIN
366
- SELECT RAISE(ABORT, 'duplicate migration lot');
367
- END
368
- `),await t.execute(`
369
- CREATE TABLE IF NOT EXISTS credit_lot_repairs (
370
- repair_id TEXT PRIMARY KEY,
371
- user_id INTEGER NOT NULL REFERENCES users(id),
372
- report_sha256 TEXT NOT NULL,
373
- classification TEXT NOT NULL,
374
- before_json TEXT NOT NULL,
375
- plan_json TEXT NOT NULL,
376
- after_json TEXT,
377
- status TEXT NOT NULL DEFAULT 'claimed',
378
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
379
- applied_at TEXT
380
- )
381
- `),await t.execute("CREATE INDEX IF NOT EXISTS credit_lot_repairs_user_applied ON credit_lot_repairs(user_id, applied_at)"),await t.execute(`
382
- CREATE TABLE IF NOT EXISTS free_credit_refreshes (
383
- user_id INTEGER NOT NULL REFERENCES users(id),
384
- month TEXT NOT NULL,
385
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
386
- PRIMARY KEY (user_id, month)
387
- )
388
- `),await t.execute(`
389
- CREATE TABLE IF NOT EXISTS billing_authorizations (
390
- id TEXT PRIMARY KEY,
391
- user_id INTEGER NOT NULL REFERENCES users(id),
392
- idempotency_key TEXT NOT NULL UNIQUE,
393
- billing_class TEXT NOT NULL,
394
- status TEXT NOT NULL,
395
- minimum_amount_mc INTEGER NOT NULL,
396
- maximum_amount_mc INTEGER NOT NULL,
397
- token_hash TEXT NOT NULL,
398
- metadata TEXT,
399
- expires_at TEXT NOT NULL,
400
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
401
- settled_at TEXT
402
- )
403
- `),await t.execute("CREATE INDEX IF NOT EXISTS billing_authorizations_user_status ON billing_authorizations(user_id, status, expires_at)"),await t.execute(`
404
- CREATE TABLE IF NOT EXISTS billing_events (
405
- id TEXT PRIMARY KEY,
406
- user_id INTEGER NOT NULL REFERENCES users(id),
407
- authorization_id TEXT REFERENCES billing_authorizations(id),
408
- idempotency_key TEXT NOT NULL UNIQUE,
409
- billing_class TEXT NOT NULL,
410
- source_surface TEXT NOT NULL,
411
- status TEXT NOT NULL,
412
- amount_mc INTEGER NOT NULL DEFAULT 0,
413
- raw_cost_usd_nanos INTEGER,
414
- multiplier_bps INTEGER,
415
- input_tokens INTEGER,
416
- output_tokens INTEGER,
417
- cached_tokens INTEGER,
418
- model_steps INTEGER,
419
- metadata TEXT,
420
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
421
- settled_at TEXT
422
- )
423
- `),await t.execute("CREATE INDEX IF NOT EXISTS billing_events_user_created_at ON billing_events(user_id, created_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS billing_events_authorization ON billing_events(authorization_id)"),await t.execute(`
424
- CREATE TABLE IF NOT EXISTS billing_debits (
425
- idempotency_key TEXT PRIMARY KEY,
426
- user_id INTEGER NOT NULL REFERENCES users(id),
427
- amount_mc INTEGER NOT NULL,
428
- operation TEXT NOT NULL,
429
- description TEXT,
430
- status TEXT NOT NULL DEFAULT 'claimed',
431
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
432
- applied_at TEXT
433
- )
434
- `),await t.execute("CREATE INDEX IF NOT EXISTS billing_debits_user_status ON billing_debits(user_id, status)"),await t.execute(`
435
- CREATE TABLE IF NOT EXISTS billing_debit_allocations (
436
- debit_idempotency_key TEXT NOT NULL REFERENCES billing_debits(idempotency_key),
437
- lot_id INTEGER NOT NULL REFERENCES credit_lots(id),
438
- user_id INTEGER NOT NULL REFERENCES users(id),
439
- amount_mc INTEGER NOT NULL,
440
- expires_at TEXT NOT NULL,
441
- PRIMARY KEY (debit_idempotency_key, lot_id)
442
- )
443
- `),await t.execute(`
444
- CREATE TABLE IF NOT EXISTS connected_account_billing (
445
- user_id INTEGER PRIMARY KEY REFERENCES users(id),
446
- stripe_subscription_id TEXT,
447
- stripe_subscription_item_id TEXT,
448
- price_id TEXT,
449
- quantity INTEGER NOT NULL DEFAULT 0,
450
- status TEXT NOT NULL DEFAULT 'pending',
451
- last_error_code TEXT,
452
- checked_at TEXT NOT NULL DEFAULT (datetime('now')),
453
- synced_at TEXT,
454
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
455
- )
456
- `),await t.execute(`
457
- CREATE TABLE IF NOT EXISTS connected_account_charges (
458
- user_id INTEGER NOT NULL,
459
- connection_id TEXT NOT NULL,
460
- period_start TEXT NOT NULL,
461
- amount_mc INTEGER NOT NULL,
462
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
463
- PRIMARY KEY (user_id, connection_id, period_start)
464
- )
465
- `),await t.execute(`
466
- CREATE TABLE IF NOT EXISTS connected_account_charge_transfers (
467
- id INTEGER PRIMARY KEY AUTOINCREMENT,
468
- user_id INTEGER NOT NULL,
469
- period_start TEXT NOT NULL,
470
- from_connection_id TEXT NOT NULL,
471
- to_connection_id TEXT NOT NULL,
472
- provider_config_key TEXT NOT NULL,
473
- transferred_at TEXT NOT NULL DEFAULT (datetime('now')),
474
- UNIQUE (user_id, period_start, from_connection_id),
475
- CHECK (from_connection_id <> to_connection_id)
476
- )
477
- `),await t.execute("CREATE INDEX IF NOT EXISTS connected_account_charge_transfers_destination ON connected_account_charge_transfers(user_id, period_start, to_connection_id, transferred_at DESC)"),await t.execute(`
478
- CREATE TABLE IF NOT EXISTS billing_credits (
479
- idempotency_key TEXT PRIMARY KEY,
480
- user_id INTEGER NOT NULL REFERENCES users(id),
481
- amount_mc INTEGER NOT NULL,
482
- operation TEXT NOT NULL,
483
- description TEXT,
484
- status TEXT NOT NULL DEFAULT 'claimed',
485
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
486
- applied_at TEXT
487
- )
488
- `),await t.execute("CREATE INDEX IF NOT EXISTS billing_credits_user_status ON billing_credits(user_id, status)"),await t.execute(`
489
- CREATE TABLE IF NOT EXISTS operation_runs (
490
- id TEXT PRIMARY KEY,
491
- user_id INTEGER REFERENCES users(id),
492
- owner_scope_hash TEXT NOT NULL,
493
- tool TEXT NOT NULL,
494
- idempotency_scope_hash TEXT,
495
- request_fingerprint TEXT,
496
- job_id TEXT,
497
- request_id TEXT,
498
- deployment_commit TEXT,
499
- normalized_flags_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(normalized_flags_json)),
500
- status TEXT NOT NULL DEFAULT 'running'
501
- CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled', 'timed_out', 'interrupted', 'unknown')),
502
- failure_class TEXT,
503
- started_at TEXT NOT NULL,
504
- ended_at TEXT,
505
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
506
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
507
- CHECK ((idempotency_scope_hash IS NULL AND request_fingerprint IS NULL)
508
- OR (idempotency_scope_hash IS NOT NULL AND request_fingerprint IS NOT NULL)),
509
- CHECK ((status = 'running' AND ended_at IS NULL) OR (status <> 'running' AND ended_at IS NOT NULL))
510
- )
511
- `),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS operation_runs_idempotency_scope ON operation_runs(idempotency_scope_hash) WHERE idempotency_scope_hash IS NOT NULL"),await t.execute("CREATE INDEX IF NOT EXISTS operation_runs_user_started ON operation_runs(user_id, started_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS operation_runs_tool_started ON operation_runs(tool, started_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS operation_runs_status_started ON operation_runs(status, started_at)"),await t.execute(`
512
- CREATE TABLE IF NOT EXISTS maps_place_runs (
513
- id TEXT PRIMARY KEY,
514
- user_id INTEGER NOT NULL REFERENCES users(id),
515
- idempotency_key_hash TEXT NOT NULL,
516
- request_fingerprint TEXT NOT NULL,
517
- declaration_json TEXT NOT NULL CHECK (json_valid(declaration_json)),
518
- status TEXT NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','running','interrupted','complete','partial','failed','cancelled')),
519
- billing_state TEXT NOT NULL DEFAULT 'unreserved' CHECK (billing_state IN ('unreserved','held','settling','settled','refunded','pending_reconciliation')),
520
- field_states_json TEXT NOT NULL CHECK (json_valid(field_states_json)),
521
- core_json TEXT CHECK (core_json IS NULL OR json_valid(core_json)),
522
- verified INTEGER NOT NULL DEFAULT 0 CHECK (verified IN (0,1)),
523
- checkpoint_revision INTEGER NOT NULL DEFAULT 0 CHECK (checkpoint_revision >= 0),
524
- current_attempt_id TEXT,
525
- current_fence_token TEXT,
526
- lease_expires_at TEXT,
527
- operation_run_id TEXT,
528
- concurrency_lock_id TEXT,
529
- error_code TEXT,
530
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
531
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
532
- UNIQUE(user_id, idempotency_key_hash)
533
- )
534
- `),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS maps_place_runs_owner_key ON maps_place_runs(user_id,idempotency_key_hash)"),await t.execute("CREATE INDEX IF NOT EXISTS maps_place_runs_status_lease ON maps_place_runs(status,lease_expires_at)"),await t.execute(`
535
- CREATE TABLE IF NOT EXISTS maps_place_attempts (
536
- id TEXT PRIMARY KEY,
537
- run_id TEXT NOT NULL REFERENCES maps_place_runs(id) ON DELETE CASCADE,
538
- attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
539
- resume_key_hash TEXT,
540
- fence_token TEXT NOT NULL,
541
- status TEXT NOT NULL CHECK (status IN ('running','complete','partial','failed','interrupted','cancelled')),
542
- lease_expires_at TEXT NOT NULL,
543
- hold_key TEXT NOT NULL,
544
- held_amount_mc INTEGER NOT NULL DEFAULT 0,
545
- final_amount_mc INTEGER,
546
- billing_state TEXT NOT NULL DEFAULT 'unreserved',
547
- started_at TEXT NOT NULL DEFAULT (datetime('now')),
548
- ended_at TEXT,
549
- UNIQUE(run_id,attempt_number),
550
- UNIQUE(run_id,resume_key_hash)
551
- )
552
- `),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS maps_place_attempts_run_number ON maps_place_attempts(run_id,attempt_number)"),await t.execute(`
553
- CREATE TABLE IF NOT EXISTS maps_place_run_events (
554
- id TEXT PRIMARY KEY,
555
- run_id TEXT NOT NULL REFERENCES maps_place_runs(id) ON DELETE CASCADE,
556
- attempt_id TEXT,
557
- revision INTEGER NOT NULL,
558
- event_type TEXT NOT NULL,
559
- details_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(details_json)),
560
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
561
- )
562
- `),await t.execute(`
563
- CREATE TABLE IF NOT EXISTS maps_place_reviews (
564
- run_id TEXT NOT NULL REFERENCES maps_place_runs(id) ON DELETE CASCADE,
565
- review_key TEXT NOT NULL,
566
- ordinal INTEGER NOT NULL,
567
- record_json TEXT NOT NULL CHECK (json_valid(record_json)),
568
- PRIMARY KEY(run_id,review_key)
569
- )
570
- `),await t.execute("CREATE INDEX IF NOT EXISTS maps_place_reviews_order ON maps_place_reviews(run_id,ordinal,review_key)"),await t.execute(`
571
- CREATE TABLE IF NOT EXISTS maps_place_images (
572
- run_id TEXT NOT NULL REFERENCES maps_place_runs(id) ON DELETE CASCADE,
573
- media_key TEXT NOT NULL,
574
- ordinal INTEGER NOT NULL,
575
- record_json TEXT NOT NULL CHECK (json_valid(record_json)),
576
- PRIMARY KEY(run_id,media_key)
577
- )
578
- `),await t.execute("CREATE INDEX IF NOT EXISTS maps_place_images_order ON maps_place_images(run_id,ordinal,media_key)"),await t.execute(`
579
- CREATE TABLE IF NOT EXISTS operation_attempts (
580
- id TEXT PRIMARY KEY,
581
- run_id TEXT NOT NULL REFERENCES operation_runs(id) ON DELETE CASCADE,
582
- parent_attempt_id TEXT,
583
- attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
584
- edge_kind TEXT CHECK (edge_kind IN ('next_phase', 'parallel_child', 'same_method_retry', 'provider_fallback', 'durable_resume', 'reconciliation')),
585
- retry_kind TEXT CHECK (retry_kind IN ('same_method', 'provider_fallback', 'durable_resume', 'reconciliation')),
586
- retry_reason TEXT,
587
- retry_owner TEXT,
588
- method TEXT NOT NULL,
589
- provider TEXT,
590
- normalized_flags_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(normalized_flags_json)),
591
- status TEXT NOT NULL DEFAULT 'running'
592
- CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled', 'timed_out', 'interrupted', 'unknown')),
593
- error_code TEXT,
594
- error_message TEXT,
595
- started_at TEXT NOT NULL,
596
- ended_at TEXT,
597
- duration_ms INTEGER CHECK (duration_ms IS NULL OR duration_ms >= 0),
598
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
599
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
600
- UNIQUE (id, run_id),
601
- FOREIGN KEY (parent_attempt_id, run_id) REFERENCES operation_attempts(id, run_id) ON DELETE RESTRICT,
602
- CHECK ((attempt_number = 1 AND parent_attempt_id IS NULL AND edge_kind IS NULL AND retry_kind IS NULL)
603
- OR (attempt_number > 1 AND parent_attempt_id IS NOT NULL AND edge_kind IS NOT NULL)),
604
- CHECK (retry_kind IS NULL OR edge_kind IN ('same_method_retry', 'provider_fallback', 'durable_resume', 'reconciliation')),
605
- CHECK ((status = 'running' AND ended_at IS NULL AND duration_ms IS NULL)
606
- OR (status <> 'running' AND ended_at IS NOT NULL AND duration_ms IS NOT NULL))
607
- )
608
- `),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS operation_attempts_run_number ON operation_attempts(run_id, attempt_number)"),await t.execute("CREATE INDEX IF NOT EXISTS operation_attempts_provider_status ON operation_attempts(provider, status, started_at)"),await t.execute(Le()),await t.execute("CREATE INDEX IF NOT EXISTS provider_usage_receipts_attempt ON provider_usage_receipts(attempt_id, created_at)"),await t.execute("CREATE INDEX IF NOT EXISTS provider_usage_receipts_pending ON provider_usage_receipts(cost_state, checked_at, created_at) WHERE cost_state = 'pending'"),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS provider_usage_receipts_provider_source ON provider_usage_receipts(provider, account_scope_hash, method, provider_source_key_hash) WHERE provider_source_key_hash IS NOT NULL"),await t.execute(`
609
- CREATE TABLE IF NOT EXISTS provider_reconciliation_runs (
610
- id TEXT PRIMARY KEY,
611
- provider TEXT NOT NULL,
612
- status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
613
- checked_count INTEGER NOT NULL DEFAULT 0 CHECK (checked_count >= 0),
614
- completed_count INTEGER NOT NULL DEFAULT 0 CHECK (completed_count >= 0),
615
- pending_count INTEGER NOT NULL DEFAULT 0 CHECK (pending_count >= 0),
616
- unavailable_count INTEGER NOT NULL DEFAULT 0 CHECK (unavailable_count >= 0),
617
- failed_count INTEGER NOT NULL DEFAULT 0 CHECK (failed_count >= 0),
618
- error_code TEXT,
619
- started_at TEXT NOT NULL,
620
- completed_at TEXT,
621
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
622
- CHECK ((status = 'running' AND completed_at IS NULL) OR (status <> 'running' AND completed_at IS NOT NULL))
623
- )
624
- `),await t.execute("CREATE INDEX IF NOT EXISTS provider_reconciliation_runs_provider_started ON provider_reconciliation_runs(provider, started_at DESC)"),await t.execute(`
625
- CREATE TABLE IF NOT EXISTS provider_account_windows (
626
- id TEXT PRIMARY KEY,
627
- provider TEXT NOT NULL,
628
- provider_product TEXT NOT NULL,
629
- account_scope_hash TEXT NOT NULL,
630
- window_start TEXT NOT NULL,
631
- window_end TEXT NOT NULL,
632
- source TEXT NOT NULL,
633
- source_observed_at TEXT NOT NULL,
634
- account_total_usd_nanos INTEGER NOT NULL CHECK (account_total_usd_nanos >= 0),
635
- actual_usd_nanos INTEGER NOT NULL CHECK (actual_usd_nanos >= 0),
636
- measured_usd_nanos INTEGER NOT NULL CHECK (measured_usd_nanos >= 0),
637
- estimated_usd_nanos INTEGER NOT NULL CHECK (estimated_usd_nanos >= 0),
638
- unattributed_usd_nanos INTEGER NOT NULL,
639
- pending_count INTEGER NOT NULL CHECK (pending_count >= 0),
640
- unavailable_count INTEGER NOT NULL CHECK (unavailable_count >= 0),
641
- receipt_count INTEGER NOT NULL CHECK (receipt_count >= 0),
642
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
643
- CHECK (window_start < window_end),
644
- CHECK (account_total_usd_nanos = actual_usd_nanos + measured_usd_nanos + estimated_usd_nanos + unattributed_usd_nanos),
645
- UNIQUE (provider, provider_product, account_scope_hash, window_start, window_end, source)
646
- )
647
- `),await t.execute("CREATE INDEX IF NOT EXISTS provider_account_windows_lookup ON provider_account_windows(provider, provider_product, window_start, window_end)"),await t.execute(`
648
- CREATE TABLE IF NOT EXISTS operation_coverage_defects (
649
- id TEXT PRIMARY KEY,
650
- tool TEXT NOT NULL,
651
- method TEXT NOT NULL,
652
- phase TEXT NOT NULL,
653
- error_code TEXT NOT NULL,
654
- run_id TEXT,
655
- attempt_id TEXT,
656
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
657
- )
658
- `),await t.execute("CREATE INDEX IF NOT EXISTS operation_coverage_defects_created ON operation_coverage_defects(created_at, tool, method)"),await t.execute(`
659
- CREATE TABLE IF NOT EXISTS operation_billing_links (
660
- run_id TEXT NOT NULL REFERENCES operation_runs(id) ON DELETE CASCADE,
661
- billing_table TEXT NOT NULL CHECK (billing_table IN ('billing_authorizations', 'billing_events', 'billing_debits', 'billing_credits', 'ledger')),
662
- billing_key TEXT NOT NULL,
663
- relation TEXT NOT NULL CHECK (relation IN ('hold', 'settlement', 'debit', 'refund', 'refund_pending')),
664
- amount_mc INTEGER CHECK (amount_mc IS NULL OR amount_mc >= 0),
665
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
666
- PRIMARY KEY (run_id, billing_table, billing_key, relation)
667
- )
668
- `),await t.execute("CREATE INDEX IF NOT EXISTS operation_billing_links_run ON operation_billing_links(run_id, created_at)"),await t.execute(`
669
- CREATE TABLE IF NOT EXISTS admin_credit_adjustments (
670
- reference TEXT PRIMARY KEY,
671
- user_id INTEGER NOT NULL REFERENCES users(id),
672
- direction TEXT NOT NULL,
673
- amount_mc INTEGER NOT NULL,
674
- reason TEXT NOT NULL,
675
- actor TEXT NOT NULL,
676
- status TEXT NOT NULL DEFAULT 'applied',
677
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
678
- updated_at TEXT
679
- )
680
- `),await t.execute("CREATE INDEX IF NOT EXISTS admin_credit_adjustments_user_created ON admin_credit_adjustments(user_id, created_at)"),await t.execute(`
681
- CREATE TABLE IF NOT EXISTS admin_credit_adjustment_events (
682
- id INTEGER PRIMARY KEY AUTOINCREMENT,
683
- reference TEXT NOT NULL REFERENCES admin_credit_adjustments(reference),
684
- seq INTEGER NOT NULL,
685
- kind TEXT NOT NULL,
686
- from_amount_mc INTEGER,
687
- to_amount_mc INTEGER,
688
- from_reason TEXT,
689
- to_reason TEXT,
690
- moved_mc INTEGER NOT NULL DEFAULT 0,
691
- actor TEXT NOT NULL,
692
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
693
- UNIQUE (reference, seq)
694
- )
695
- `),await t.execute("CREATE INDEX IF NOT EXISTS admin_credit_adjustment_events_reference ON admin_credit_adjustment_events(reference)"),await t.execute(`
696
- CREATE TABLE IF NOT EXISTS site_audit_jobs (
697
- id TEXT PRIMARY KEY,
698
- user_id INTEGER,
699
- status TEXT DEFAULT 'pending',
700
- client_domain TEXT,
701
- session_path TEXT,
702
- request TEXT,
703
- result TEXT,
704
- error TEXT,
705
- created_at TEXT DEFAULT (datetime('now')),
706
- updated_at TEXT DEFAULT (datetime('now'))
707
- )
708
- `),await t.execute(`
709
- CREATE TABLE IF NOT EXISTS site_audit_phase_log (
710
- id INTEGER PRIMARY KEY AUTOINCREMENT,
711
- job_id TEXT REFERENCES site_audit_jobs(id),
712
- phase TEXT,
713
- completed_at TEXT DEFAULT (datetime('now')),
714
- output_summary TEXT
715
- )
716
- `),await t.execute("CREATE INDEX IF NOT EXISTS site_audit_jobs_user_id ON site_audit_jobs(user_id)"),await t.execute("CREATE INDEX IF NOT EXISTS site_audit_jobs_status ON site_audit_jobs(status)"),await t.execute("CREATE INDEX IF NOT EXISTS site_audit_phase_log_job_id ON site_audit_phase_log(job_id)"),await t.execute(`
717
- CREATE TABLE IF NOT EXISTS site_extract_jobs (
718
- id TEXT PRIMARY KEY,
719
- user_id INTEGER,
720
- status TEXT DEFAULT 'pending',
721
- start_url TEXT,
722
- options TEXT,
723
- total_urls INTEGER DEFAULT 0,
724
- done_urls INTEGER DEFAULT 0,
725
- attempted_urls INTEGER DEFAULT 0,
726
- successful_urls INTEGER DEFAULT 0,
727
- failed_urls INTEGER DEFAULT 0,
728
- idempotency_key TEXT,
729
- request_fingerprint TEXT,
730
- dispatch_attempts INTEGER DEFAULT 0,
731
- next_dispatch_at TEXT,
732
- dispatch_error TEXT,
733
- settlement_attempts INTEGER DEFAULT 0,
734
- next_settlement_at TEXT,
735
- settlement_error TEXT,
736
- artifacts TEXT,
737
- error TEXT,
738
- public_error_json TEXT,
739
- billed_mc INTEGER,
740
- created_at TEXT DEFAULT (datetime('now')),
741
- updated_at TEXT DEFAULT (datetime('now'))
742
- )
743
- `),await t.execute(`
744
- CREATE TABLE IF NOT EXISTS site_extract_pages (
745
- job_id TEXT,
746
- url TEXT,
747
- page TEXT,
748
- PRIMARY KEY (job_id, url)
749
- )
750
- `),await t.execute("CREATE INDEX IF NOT EXISTS site_extract_jobs_user_id ON site_extract_jobs(user_id)"),await t.execute("CREATE INDEX IF NOT EXISTS site_extract_jobs_status ON site_extract_jobs(status)"),await t.execute("CREATE INDEX IF NOT EXISTS site_extract_pages_job_id ON site_extract_pages(job_id)");try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN attempted_urls INTEGER DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN successful_urls INTEGER DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN failed_urls INTEGER DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN idempotency_key TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN request_fingerprint TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN dispatch_attempts INTEGER DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN next_dispatch_at TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN dispatch_error TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN settlement_attempts INTEGER DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN next_settlement_at TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN settlement_error TEXT")}catch{}try{await t.execute("ALTER TABLE site_extract_jobs ADD COLUMN public_error_json TEXT")}catch{}await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS site_extract_jobs_idempotency ON site_extract_jobs(user_id, idempotency_key) WHERE idempotency_key IS NOT NULL"),await t.execute(`
751
- CREATE TABLE IF NOT EXISTS password_reset_tokens (
752
- token TEXT PRIMARY KEY,
753
- user_id INTEGER NOT NULL REFERENCES users(id),
754
- expires_at TEXT NOT NULL,
755
- used INTEGER NOT NULL DEFAULT 0,
756
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
757
- )
758
- `),await t.execute("CREATE INDEX IF NOT EXISTS prt_user_id ON password_reset_tokens(user_id)"),await t.execute(`
759
- CREATE TABLE IF NOT EXISTS rate_limits (
760
- scope TEXT NOT NULL,
761
- key_hash TEXT NOT NULL,
762
- window_start INTEGER NOT NULL,
763
- count INTEGER NOT NULL DEFAULT 0,
764
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
765
- PRIMARY KEY (scope, key_hash, window_start)
766
- )
767
- `),await t.execute(`
768
- CREATE TABLE IF NOT EXISTS request_events (
769
- id TEXT PRIMARY KEY,
770
- user_id INTEGER NOT NULL REFERENCES users(id),
771
- source TEXT NOT NULL,
772
- status TEXT NOT NULL,
773
- query TEXT NOT NULL,
774
- location TEXT,
775
- result_count INTEGER,
776
- result TEXT,
777
- error TEXT,
778
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
779
- )
780
- `),await t.execute("CREATE INDEX IF NOT EXISTS request_events_user_created_at ON request_events(user_id, created_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS request_events_created_at ON request_events(created_at)"),await t.execute(`
781
- CREATE TABLE IF NOT EXISTS workflow_schedules (
782
- id TEXT PRIMARY KEY,
783
- user_id INTEGER NOT NULL REFERENCES users(id),
784
- workflow_id TEXT NOT NULL,
785
- name TEXT NOT NULL,
786
- input_json TEXT NOT NULL,
787
- cadence TEXT NOT NULL,
788
- cron_expr TEXT,
789
- timezone TEXT NOT NULL DEFAULT 'UTC',
790
- webhook_url TEXT,
791
- status TEXT NOT NULL DEFAULT 'active',
792
- next_run_at TEXT,
793
- last_run_at TEXT,
794
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
795
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
796
- )
797
- `),await t.execute("CREATE INDEX IF NOT EXISTS workflow_schedules_user_status ON workflow_schedules(user_id, status, created_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS workflow_schedules_due ON workflow_schedules(status, next_run_at)"),await t.execute(`
798
- CREATE TABLE IF NOT EXISTS workflow_runs (
799
- id TEXT PRIMARY KEY,
800
- user_id INTEGER NOT NULL REFERENCES users(id),
801
- schedule_id TEXT,
802
- workflow_id TEXT NOT NULL,
803
- input_json TEXT NOT NULL,
804
- status TEXT NOT NULL DEFAULT 'queued',
805
- manifest_json TEXT,
806
- error_message TEXT,
807
- queued_at TEXT NOT NULL DEFAULT (datetime('now')),
808
- started_at TEXT,
809
- completed_at TEXT,
810
- idempotency_key TEXT,
811
- step_index INTEGER NOT NULL DEFAULT 0,
812
- total_steps INTEGER,
813
- step_state_json TEXT,
814
- step_log_json TEXT,
815
- webhook_url TEXT
816
- )
817
- `);try{await t.execute("ALTER TABLE workflow_runs ADD COLUMN step_index INTEGER NOT NULL DEFAULT 0")}catch{}try{await t.execute("ALTER TABLE workflow_runs ADD COLUMN total_steps INTEGER")}catch{}try{await t.execute("ALTER TABLE workflow_runs ADD COLUMN step_state_json TEXT")}catch{}try{await t.execute("ALTER TABLE workflow_runs ADD COLUMN step_log_json TEXT")}catch{}try{await t.execute("ALTER TABLE workflow_runs ADD COLUMN webhook_url TEXT")}catch{}await t.execute("CREATE INDEX IF NOT EXISTS workflow_runs_user_created ON workflow_runs(user_id, queued_at DESC)"),await t.execute("CREATE INDEX IF NOT EXISTS workflow_runs_schedule ON workflow_runs(schedule_id, queued_at DESC)"),await t.execute("CREATE UNIQUE INDEX IF NOT EXISTS workflow_runs_idempotency ON workflow_runs(idempotency_key) WHERE idempotency_key IS NOT NULL"),await t.execute(`
818
- CREATE TABLE IF NOT EXISTS workflow_artifacts (
819
- id TEXT PRIMARY KEY,
820
- run_id TEXT NOT NULL REFERENCES workflow_runs(id),
821
- user_id INTEGER NOT NULL REFERENCES users(id),
822
- kind TEXT NOT NULL,
823
- label TEXT NOT NULL,
824
- path TEXT NOT NULL,
825
- content_type TEXT NOT NULL,
826
- bytes INTEGER,
827
- rows_count INTEGER,
828
- content TEXT,
829
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
830
- )
831
- `);try{await t.execute("ALTER TABLE workflow_artifacts ADD COLUMN content TEXT")}catch{}await t.execute("CREATE INDEX IF NOT EXISTS workflow_artifacts_run ON workflow_artifacts(run_id)"),await t.execute(`
832
- CREATE TABLE IF NOT EXISTS workflow_webhook_deliveries (
833
- id TEXT PRIMARY KEY,
834
- run_id TEXT NOT NULL REFERENCES workflow_runs(id),
835
- schedule_id TEXT,
836
- user_id INTEGER NOT NULL REFERENCES users(id),
837
- webhook_url TEXT NOT NULL,
838
- status TEXT NOT NULL,
839
- attempts INTEGER NOT NULL DEFAULT 0,
840
- last_error TEXT,
841
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
842
- delivered_at TEXT
843
- )
844
- `),await t.execute("CREATE INDEX IF NOT EXISTS workflow_webhook_deliveries_run ON workflow_webhook_deliveries(run_id)"),await t.execute(`
845
- CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
846
- share_id TEXT PRIMARY KEY,
847
- owner_id TEXT NOT NULL,
848
- run_id TEXT NOT NULL,
849
- artifact_id TEXT NOT NULL,
850
- title TEXT NOT NULL,
851
- summary TEXT NOT NULL,
852
- renderer_key TEXT NOT NULL CHECK (renderer_key IN ('editorial_reading_room_v1','personal_authority_v1','personal_authority_v2','newsroom_publisher_v1','blog_article_v1')),
853
- token_hash TEXT NOT NULL UNIQUE,
854
- expires_at TEXT NOT NULL,
855
- revoked_at TEXT,
856
- created_at TEXT NOT NULL
857
- )
858
- `),await t.execute(`
859
- CREATE INDEX IF NOT EXISTS scheduled_run_view_links_owner_run
860
- ON scheduled_run_view_links(owner_id, run_id, created_at DESC)
861
- `),await t.execute(`
862
- CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
863
- ON scheduled_run_view_links(expires_at, revoked_at)
864
- `),await t.execute(`
865
- CREATE TABLE IF NOT EXISTS stripe_events (
866
- event_id TEXT PRIMARY KEY,
867
- status TEXT NOT NULL DEFAULT 'processed',
868
- attempt_count INTEGER NOT NULL DEFAULT 1,
869
- claim_token TEXT,
870
- event_type TEXT,
871
- stripe_created_at INTEGER,
872
- received_at TEXT NOT NULL DEFAULT (datetime('now')),
873
- claimed_at TEXT,
874
- next_attempt_at TEXT,
875
- completed_at TEXT,
876
- last_error TEXT,
877
- last_error_code TEXT,
878
- processed_at TEXT NOT NULL DEFAULT (datetime('now')),
879
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
880
- )
881
- `);for(let s of["status TEXT NOT NULL DEFAULT 'processed'","attempt_count INTEGER NOT NULL DEFAULT 1","claim_token TEXT","event_type TEXT","stripe_created_at INTEGER","received_at TEXT","claimed_at TEXT","next_attempt_at TEXT","completed_at TEXT","last_error TEXT","last_error_code TEXT","updated_at TEXT"])try{await t.execute(`ALTER TABLE stripe_events ADD COLUMN ${s}`)}catch{}await t.execute(`
882
- UPDATE stripe_events
883
- SET updated_at = COALESCE(updated_at, processed_at),
884
- received_at = COALESCE(received_at, processed_at, updated_at, datetime('now')),
885
- claimed_at = CASE WHEN status = 'processing' THEN COALESCE(claimed_at, updated_at, processed_at) ELSE claimed_at END,
886
- next_attempt_at = CASE WHEN status = 'failed' THEN COALESCE(next_attempt_at, datetime('now')) ELSE next_attempt_at END,
887
- completed_at = CASE WHEN status = 'processed' THEN COALESCE(completed_at, processed_at, updated_at) ELSE completed_at END,
888
- status = CASE WHEN status = 'failed' THEN 'retryable' ELSE COALESCE(status, 'processed') END,
889
- attempt_count = CASE WHEN status = 'pending' THEN MAX(0, COALESCE(attempt_count, 0)) ELSE MAX(1, COALESCE(attempt_count, 1)) END
890
- WHERE updated_at IS NULL
891
- OR received_at IS NULL
892
- OR status IS NULL
893
- OR status = 'failed'
894
- OR (status = 'processing' AND claimed_at IS NULL)
895
- OR (status = 'processed' AND completed_at IS NULL)
896
- OR attempt_count IS NULL
897
- OR attempt_count < 0
898
- `),await t.execute(`
899
- CREATE INDEX IF NOT EXISTS stripe_events_due
900
- ON stripe_events(status, next_attempt_at, claimed_at, received_at)
901
- `),await t.execute(`
902
- CREATE TABLE IF NOT EXISTS oauth_clients (
903
- client_id TEXT PRIMARY KEY,
904
- redirect_uris TEXT NOT NULL,
905
- name TEXT,
906
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
907
- )
908
- `),await t.execute(`
909
- CREATE TABLE IF NOT EXISTS oauth_codes (
910
- code TEXT PRIMARY KEY,
911
- client_id TEXT NOT NULL,
912
- redirect_uri TEXT NOT NULL,
913
- code_challenge TEXT NOT NULL,
914
- identity TEXT NOT NULL,
915
- scope TEXT NOT NULL,
916
- resource TEXT,
917
- expires_at TEXT NOT NULL,
918
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
919
- )
920
- `),await t.execute(`
921
- CREATE TABLE IF NOT EXISTS oauth_tokens (
922
- refresh_token TEXT PRIMARY KEY,
923
- client_id TEXT NOT NULL,
924
- identity TEXT NOT NULL,
925
- scope TEXT NOT NULL,
926
- resource TEXT,
927
- expires_at TEXT NOT NULL,
928
- revoked INTEGER NOT NULL DEFAULT 0,
929
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
930
- )
931
- `),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(`
932
- CREATE TABLE IF NOT EXISTS page_snapshots (
933
- user_id INTEGER NOT NULL REFERENCES users(id),
934
- url TEXT NOT NULL,
935
- content_hash TEXT NOT NULL,
936
- content TEXT NOT NULL,
937
- title TEXT,
938
- content_bytes INTEGER NOT NULL,
939
- truncated INTEGER NOT NULL DEFAULT 0,
940
- checked_at TEXT NOT NULL DEFAULT (datetime('now')),
941
- PRIMARY KEY (user_id, url)
942
- )
943
- `),await t.execute("CREATE INDEX IF NOT EXISTS page_snapshots_user_checked_at ON page_snapshots(user_id, checked_at DESC)"),await t.execute(`
944
- CREATE TABLE IF NOT EXISTS inbox_messages (
945
- id TEXT PRIMARY KEY,
946
- user_id INTEGER NOT NULL REFERENCES users(id),
947
- subject TEXT NOT NULL,
948
- body_md TEXT NOT NULL,
949
- source TEXT NOT NULL DEFAULT 'operator',
950
- sent_at TEXT NOT NULL DEFAULT (datetime('now')),
951
- opened_at TEXT,
952
- deleted_at TEXT
953
- )
954
- `),await t.execute("CREATE INDEX IF NOT EXISTS inbox_messages_user_sent_at ON inbox_messages(user_id, sent_at DESC)"),await t.execute(`
955
- CREATE TABLE IF NOT EXISTS workshop_registrations (
956
- id TEXT PRIMARY KEY,
957
- event_slug TEXT NOT NULL,
958
- email TEXT NOT NULL,
959
- first_name TEXT NOT NULL,
960
- last_name TEXT,
961
- status TEXT NOT NULL,
962
- zoom_registrant_id TEXT,
963
- zoom_join_url TEXT,
964
- resend_contact_id TEXT,
965
- confirmation_email_id TEXT,
966
- field_notes_email_id TEXT,
967
- reminder_48h_email_id TEXT,
968
- reminder_24h_email_id TEXT,
969
- reminder_1h_email_id TEXT,
970
- last_error TEXT,
971
- registered_at TEXT NOT NULL DEFAULT (datetime('now')),
972
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
973
- UNIQUE(event_slug, email)
974
- )
975
- `);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(`
976
- CREATE INDEX IF NOT EXISTS workshop_registrations_event_status
977
- ON workshop_registrations(event_slug, status, registered_at)
978
- `),await t.execute(`
979
- CREATE TABLE IF NOT EXISTS browser_profile_labels (
980
- user_id INTEGER NOT NULL REFERENCES users(id),
981
- profile TEXT NOT NULL,
982
- label TEXT NOT NULL,
983
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
984
- updated_at TEXT,
985
- PRIMARY KEY (user_id, profile)
986
- )
987
- `),await t.execute(`
988
- CREATE TABLE IF NOT EXISTS serp_identities (
989
- id TEXT PRIMARY KEY,
990
- user_id INTEGER NOT NULL REFERENCES users(id),
991
- name TEXT NOT NULL,
992
- \u006b\u0065\u0072\u006e\u0065\u006c_profile_name TEXT NOT NULL,
993
- \u006b\u0065\u0072\u006e\u0065\u006c_proxy_id TEXT NOT NULL,
994
- proxy_type TEXT NOT NULL DEFAULT 'isp',
995
- country TEXT NOT NULL DEFAULT 'US',
996
- status TEXT NOT NULL DEFAULT 'ready',
997
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
998
- updated_at TEXT,
999
- last_used_at TEXT,
1000
- UNIQUE(user_id, name)
1001
- )
1002
- `),await t.execute(`
1003
- CREATE UNIQUE INDEX IF NOT EXISTS serp_identities_user_name
1004
- ON serp_identities(user_id, name)
1005
- `),!await P(t))throw new Error("core schema migration finished without all required tables, indexes, and columns");await G(t),await t.execute({sql:"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",args:[U]})}async function Ut(e={}){let t=await tt(i());return await nt(e),{providerUsageReceipts:t}}async function Dt(e,t,r){await i().execute({sql:"INSERT INTO oauth_clients (client_id, redirect_uris, name) VALUES (?, ?, ?)",args:[e,JSON.stringify(t),r]})}async function vt(e){let r=(await i().execute({sql:"SELECT * FROM oauth_clients WHERE client_id = ?",args:[e]})).rows[0];if(!r)return;let n=[];try{let s=JSON.parse(String(r.redirect_uris));Array.isArray(s)&&(n=s.filter(a=>typeof a=="string"))}catch{n=[]}return{client_id:String(r.client_id),redirect_uris:n,name:r.name!=null?String(r.name):null,created_at:String(r.created_at)}}async function kt(e){await i().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 Xt(e){let t=i(),n=(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]}),!!n)return{code:String(n.code),client_id:String(n.client_id),redirect_uri:String(n.redirect_uri),code_challenge:String(n.code_challenge),identity:String(n.identity),scope:String(n.scope),resource:n.resource!=null?String(n.resource):null,expires_at:String(n.expires_at)}}async function st(e){await i().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 Ft(e){let r=(await i().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 Mt(e){await i().execute({sql:"UPDATE oauth_tokens SET revoked = 1 WHERE refresh_token = ?",args:[e]})}async function Pt(e,t){await st(t),await i().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 Ht(e,t,r){return(await i().execute({sql:`INSERT OR IGNORE INTO stripe_events
1006
- (event_id, event_type, stripe_created_at, status, attempt_count, received_at, next_attempt_at, processed_at, updated_at)
1007
- VALUES (?, ?, ?, 'pending', 0, datetime('now'), datetime('now'), datetime('now'), datetime('now'))`,args:[e,t,r]})).rowsAffected===1?"queued":"duplicate"}async function jt(e=50,t=300){let r=Math.max(1,Math.min(500,Math.trunc(e))),n=await i().execute({sql:`SELECT event_id
1008
- FROM stripe_events
1009
- WHERE (status IN ('pending', 'retryable') AND COALESCE(next_attempt_at, received_at) <= datetime('now'))
1010
- OR (status = 'processing' AND claimed_at <= datetime('now', '-' || ? || ' seconds'))
1011
- ORDER BY COALESCE(next_attempt_at, received_at), received_at
1012
- LIMIT ?`,args:[t,r]}),s=[];for(let a of n.rows){let _=String(a.event_id),u=N();if((await i().execute({sql:`UPDATE stripe_events
1013
- SET status = 'processing', attempt_count = attempt_count + 1,
1014
- claim_token = ?, claimed_at = datetime('now'), last_error = NULL,
1015
- last_error_code = NULL, updated_at = datetime('now')
1016
- WHERE event_id = ?
1017
- AND ((status IN ('pending', 'retryable') AND COALESCE(next_attempt_at, received_at) <= datetime('now'))
1018
- OR (status = 'processing' AND claimed_at <= datetime('now', '-' || ? || ' seconds')))`,args:[u,_,t]})).rowsAffected!==1)continue;let c=await i().execute({sql:"SELECT event_type, attempt_count FROM stripe_events WHERE event_id = ? LIMIT 1",args:[_]});s.push({eventId:_,eventType:c.rows[0]?.event_type!=null?String(c.rows[0].event_type):null,attemptCount:Number(c.rows[0]?.attempt_count??1),token:u})}return s}async function qt(e,t){return(await i().execute({sql:`UPDATE stripe_events
1019
- SET status = 'processed',
1020
- claim_token = NULL,
1021
- claimed_at = NULL,
1022
- next_attempt_at = NULL,
1023
- completed_at = datetime('now'),
1024
- last_error = NULL,
1025
- processed_at = datetime('now'),
1026
- updated_at = datetime('now')
1027
- WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[e,t]})).rowsAffected===1}async function Wt(e,t,r,n,s){return(await i().execute({sql:`UPDATE stripe_events
1028
- SET status = 'retryable',
1029
- claim_token = NULL,
1030
- claimed_at = NULL,
1031
- last_error = ?,
1032
- last_error_code = ?,
1033
- next_attempt_at = datetime('now', '+' || ? || ' seconds'),
1034
- updated_at = datetime('now')
1035
- WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[r.slice(0,500),n.slice(0,100),Math.max(0,Math.trunc(s)),e,t]})).rowsAffected===1}async function Bt(e,t,r,n){return(await i().execute({sql:`UPDATE stripe_events
1036
- SET status = 'dead_letter', claim_token = NULL, claimed_at = NULL,
1037
- last_error = ?, last_error_code = ?, next_attempt_at = NULL, updated_at = datetime('now')
1038
- WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[r.slice(0,500),n.slice(0,100),e,t]})).rowsAffected===1}async function Gt(){let t=(await i().execute(`
1039
- SELECT
1040
- SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending,
1041
- SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) AS processing,
1042
- SUM(CASE WHEN status = 'retryable' THEN 1 ELSE 0 END) AS retryable,
1043
- SUM(CASE WHEN status = 'dead_letter' THEN 1 ELSE 0 END) AS dead_letter,
1044
- MIN(CASE WHEN status IN ('pending', 'retryable') THEN COALESCE(next_attempt_at, received_at) END) AS oldest_due_at
1045
- FROM stripe_events
1046
- `)).rows[0];return{pending:Number(t?.pending??0),processing:Number(t?.processing??0),retryable:Number(t?.retryable??0),dead_letter:Number(t?.dead_letter??0),oldest_due_at:t?.oldest_due_at!=null?String(t.oldest_due_at):null}}async function Yt(e){return(await i().execute({sql:"SELECT 1 FROM stripe_events WHERE event_id = ? AND status = 'processed' LIMIT 1",args:[e]})).rows.length>0}function Re(){return"sk_"+j(24).toString("hex")}function Ae(e){let t=j(16).toString("hex"),r=Ne(e,t,64);return`${t}:${r.toString("hex")}`}function Kt(e,t){let[r,n]=t.split(":"),s=Buffer.from(n,"hex"),a=Ne(e,r,64);return ze(s,a)}function S(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 $t(e){let t=await i().execute({sql:"SELECT * FROM users WHERE email = ? AND active = 1",args:[e]});return t.rows[0]?S(t.rows[0]):void 0}async function Jt(e){let t=await i().execute({sql:"SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1",args:[e]});return t.rows[0]?S(t.rows[0]):void 0}async function Vt(e){let t=await i().execute({sql:"SELECT * FROM users WHERE id = ? AND active = 1",args:[e]});return t.rows[0]?S(t.rows[0]):void 0}async function zt(e){let r=(await i().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 Qt(e,t,r){await i().execute({sql:"UPDATE users SET memory_key = ?, memory_identity = ? WHERE id = ?",args:[t,r,e]})}async function Zt(e,t){await i().execute({sql:"UPDATE users SET password_hash = ? WHERE id = ?",args:[Ae(t),e]})}async function er(e){let t=i(),[r,n,s,a,_]=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(n.rows[0]?.n??0),failed:Number(s.rows[0]?.n??0),totalQuestions:Number(a.rows[0]?.n??0),lastUsed:_.rows[0]?.t!=null?String(_.rows[0].t):null}}async function tr(e,t,r,n){let s=i(),a=Re(),_=r??j(6).toString("hex"),u=Ae(_);return{id:(await s.execute({sql:"INSERT INTO users (email, name, api_key, password_hash, stripe_customer_id) VALUES (?, ?, ?, ?, ?)",args:[e,t??null,a,u,n??null]})).lastInsertRowid,email:e,name:t,api_key:a,password:_}}async function rr(e,t,r,n){let s=Math.floor(Date.now()/1e3),a=Math.floor(s/n)*n,_=v("sha256").update(t).digest("hex"),o=await i().execute({sql:`
1047
- INSERT INTO rate_limits (scope, key_hash, window_start, count)
1048
- VALUES (?, ?, ?, 1)
1049
- ON CONFLICT(scope, key_hash, window_start)
1050
- DO UPDATE SET count = count + 1, updated_at = datetime('now')
1051
- RETURNING count
1052
- `,args:[e,_,a]}),c=Number(o.rows[0]?.count);if(!Number.isSafeInteger(c)||c<1)throw new Error("Rate-limit bucket mutation did not return a valid count");return{allowed:c<=r,count:c,remaining:Math.max(0,r-c),resetSeconds:Math.max(1,a+n-s)}}async function nr(e){let t=j(32).toString("hex"),r=v("sha256").update(t).digest("hex"),n=new Date(Date.now()+3600*1e3).toISOString();return await i().execute({sql:"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)",args:[r,e,n]}),t}async function sr(e){let t=i(),r=v("sha256").update(e).digest("hex"),s=(await t.execute({sql:"SELECT user_id, expires_at, used FROM password_reset_tokens WHERE token = ?",args:[r]})).rows[0];return!s||Number(s.used)||new Date(String(s.expires_at))<new Date?null:(await t.execute({sql:"UPDATE password_reset_tokens SET used = 1 WHERE token = ?",args:[r]}),Number(s.user_id))}async function ir(e){let t=Re();return await i().execute({sql:"UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?",args:[t,e]}),t}async function ar(e){await i().execute({sql:"UPDATE users SET key_active = 0 WHERE id = ?",args:[e]})}async function or(){return(await i().execute("SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC")).rows.map(t=>S({...t,api_key:"",key_active:1}))}async function _r(e){await i().execute({sql:"UPDATE users SET active = 0 WHERE id = ?",args:[e]})}function R(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 C(e){return e==null?null:Number(e)===1}function H(e){if(e==null)return null;try{return JSON.parse(String(e))}catch{return null}}function we(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:C(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:C(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:C(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:C(e.provider_captcha),provider_checked_at:e.provider_checked_at!=null?String(e.provider_checked_at):null,provider_detail:H(e.provider_detail_json),provider_disconnect_observed:C(e.provider_disconnect_observed),provider_disconnect_message:e.provider_disconnect_message!=null?String(e.provider_disconnect_message):null,debug:H(e.debug_json),created_at:String(e.created_at),completed_at:e.completed_at!=null?String(e.completed_at):null}}function h(e){let{public_error_json:t,...r}=e;return{...r,options:JSON.parse(e.options),result:e.result?JSON.parse(e.result):null,publicError:ae(t)}}async function ur(e,t,r,n){let s=N();return await it(s,e,t,r,n),s}async function it(e,t,r,n,s){if(!/^[a-zA-Z0-9-]{1,100}$/.test(e))throw new Error("job id is invalid");await i().execute({sql:"INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,JSON.stringify(n),s??null]})}async function cr(e,t,r){let n=N();return await i().execute({sql:"INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))",args:[n,e,t,JSON.stringify(r)]}),n}async function dr(e,t,r,n){let s=await i().execute({sql:`INSERT INTO jobs (id, user_id, query, options, status, started_at)
1053
- VALUES (?, ?, ?, ?, 'running', datetime('now'))
1054
- ON CONFLICT(id) DO NOTHING`,args:[e,t,r,JSON.stringify(n)]}),a=await K(e,t);if(!a)throw new Error(`getOrCreateRunningJob: row missing immediately after insert/lookup for ${e}`);return{job:a,created:s.rowsAffected===1}}async function Er(e){await i().execute({sql:`
1055
- INSERT INTO harvest_attempts (
1056
- id, job_id, user_id, attempt_number, max_attempts, query, location, max_questions, status, created_at
1057
- )
1058
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?)
1059
- ON CONFLICT(job_id, attempt_number) DO NOTHING
1060
- `,args:[N(),e.jobId,e.userId,e.attemptNumber,e.maxAttempts,e.query,e.location,e.maxQuestions,e.startedAt]})}async function lr(e){return(await i().execute({sql:`
1061
- UPDATE harvest_attempts SET
1062
- browser_provider = ?,
1063
- provider_session_id = ?,
1064
- provider_session_observed_at = COALESCE(provider_session_observed_at, ?),
1065
- provider_lookup_status = CASE
1066
- WHEN provider_lookup_status = 'complete' THEN provider_lookup_status
1067
- ELSE 'pending'
1068
- END
1069
- WHERE job_id = ? AND attempt_number = ? AND status = 'running'
1070
- `,args:[e.browserProvider,e.providerSessionId,e.observedAt,e.jobId,e.attemptNumber]})).rowsAffected===1}async function Tr(e,t,r=new Date().toISOString()){return(await i().execute({sql:`
1071
- UPDATE harvest_attempts SET
1072
- status = 'finished',
1073
- outcome = COALESCE(outcome, 'timeout'),
1074
- question_count = COALESCE(question_count, 0),
1075
- duration_ms = COALESCE(duration_ms, MAX(0, CAST((julianday(?) - julianday(created_at)) * 86400000 AS INTEGER))),
1076
- error = COALESCE(error, 'Durable worker lease expired before attempt completion'),
1077
- will_retry = ?,
1078
- provider_error_code = COALESCE(provider_error_code, 'timeout'),
1079
- provider_error_message = COALESCE(provider_error_message, 'Durable worker lease expired before attempt completion'),
1080
- provider_reason_source = COALESCE(provider_reason_source, 'client_runtime'),
1081
- completed_at = COALESCE(completed_at, ?)
1082
- WHERE job_id = ? AND status = 'running'
1083
- `,args:[r,t?1:0,r,e]})).rowsAffected}async function pr(e){await i().execute({sql:`
1084
- UPDATE harvest_attempts SET
1085
- status = 'finished',
1086
- outcome = ?,
1087
- \u006b\u0065\u0072\u006e\u0065\u006c_session_id = ?,
1088
- question_count = ?,
1089
- duration_ms = ?,
1090
- error = ?,
1091
- will_retry = ?,
1092
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_started = ?,
1093
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_succeeded = ?,
1094
- \u006b\u0065\u0072\u006e\u0065\u006c_delete_error = ?,
1095
- browser_close_succeeded = ?,
1096
- browser_close_error = ?,
1097
- browser_provider = COALESCE(browser_provider, ?),
1098
- provider_session_id = COALESCE(provider_session_id, ?),
1099
- provider_lookup_status = CASE
1100
- WHEN provider_lookup_status = 'complete' THEN provider_lookup_status
1101
- ELSE COALESCE(?, provider_lookup_status)
1102
- END,
1103
- provider_status = COALESCE(provider_status, ?),
1104
- provider_error_code = CASE
1105
- WHEN provider_reason_source = 'provider_session_api' THEN provider_error_code
1106
- ELSE COALESCE(?, provider_error_code)
1107
- END,
1108
- provider_error_message = CASE
1109
- WHEN provider_reason_source = 'provider_session_api' THEN provider_error_message
1110
- ELSE COALESCE(?, provider_error_message)
1111
- END,
1112
- provider_reason_source = CASE
1113
- WHEN provider_reason_source = 'provider_session_api' THEN provider_reason_source
1114
- ELSE COALESCE(?, provider_reason_source)
1115
- END,
1116
- provider_duration_ms = COALESCE(provider_duration_ms, ?),
1117
- provider_bandwidth_bytes = COALESCE(provider_bandwidth_bytes, ?),
1118
- provider_captcha = COALESCE(provider_captcha, ?),
1119
- provider_checked_at = COALESCE(provider_checked_at, ?),
1120
- provider_detail_json = COALESCE(provider_detail_json, ?),
1121
- provider_disconnect_observed = COALESCE(provider_disconnect_observed, ?),
1122
- provider_disconnect_message = COALESCE(provider_disconnect_message, ?),
1123
- debug_json = ?,
1124
- completed_at = ?
1125
- WHERE job_id = ? AND attempt_number = ?
1126
- `,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:Oe(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 Oe(e,t=4096){let r=JSON.stringify(e);return r.length<=t?r:JSON.stringify({truncated:!0})}async function mr(e){return(await i().execute({sql:`
1127
- UPDATE harvest_attempts SET
1128
- provider_lookup_status = ?,
1129
- provider_status = ?,
1130
- provider_error_code = ?,
1131
- provider_error_message = ?,
1132
- provider_reason_source = ?,
1133
- provider_duration_ms = ?,
1134
- provider_bandwidth_bytes = ?,
1135
- provider_captcha = ?,
1136
- provider_checked_at = ?,
1137
- provider_detail_json = ?
1138
- WHERE id = ?
1139
- AND job_id = ?
1140
- AND user_id = ?
1141
- AND browser_provider = '\u0062\u0072\u0069\u0067\u0068\u0074_\u0064\u0061\u0074\u0061_browser_api'
1142
- AND provider_session_id = ?
1143
- AND COALESCE(provider_lookup_status, '') <> 'complete'
1144
- `,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:Oe(e.detail),e.attemptId,e.jobId,e.userId,e.providerSessionId]})).rowsAffected===1}async function Nr(e=25){let t=Math.max(1,Math.min(100,Math.trunc(e)));return(await i().execute({sql:`
1145
- SELECT * FROM harvest_attempts
1146
- WHERE browser_provider = '\u0062\u0072\u0069\u0067\u0068\u0074_\u0064\u0061\u0074\u0061_browser_api'
1147
- AND provider_session_id IS NOT NULL
1148
- AND provider_lookup_status IN ('pending', 'temporary_error')
1149
- ORDER BY COALESCE(provider_checked_at, created_at) ASC
1150
- LIMIT ?
1151
- `,args:[t]})).rows.map(n=>we(n))}async function gr(e,t){return(t==null?await i().execute({sql:"SELECT * FROM harvest_attempts WHERE job_id = ? ORDER BY attempt_number ASC",args:[e]}):await i().execute({sql:`
1152
- SELECT ha.*
1153
- FROM harvest_attempts ha
1154
- INNER JOIN jobs j ON j.id = ha.job_id
1155
- WHERE ha.job_id = ? AND j.user_id = ?
1156
- ORDER BY ha.attempt_number ASC
1157
- `,args:[e,t]})).rows.map(n=>we(n))}async function K(e,t){let r=i(),n=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 n.rows[0]?h(R(n.rows[0])):void 0}async function Lr(e,t,r){await i().execute({sql:`UPDATE jobs
1158
- SET status = 'running', started_at = COALESCE(started_at, datetime('now'))
1159
- WHERE id = ? AND status = 'pending'
1160
- AND json_extract(options, '$.executionOwner') = ?
1161
- AND json_extract(options, '$.jobKind') = ?`,args:[e,t,r]});let n=await K(e);if(!(!n||n.options.executionOwner!==t||n.options.jobKind!==r))return n}async function br(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await i().execute({sql:`SELECT * FROM jobs
1162
- WHERE status = 'pending'
1163
- AND json_extract(options, '$.executionOwner') = 'inngest'
1164
- AND json_extract(options, '$.jobKind') = 'extract_url'
1165
- AND json_extract(options, '$.billingDebitKey') IS NOT NULL
1166
- AND created_at <= datetime('now', '-30 seconds')
1167
- AND datetime(COALESCE(json_extract(options, '$.extractUrlLastDispatchAt'), '1970-01-01')) <= datetime('now', '-30 seconds')
1168
- ORDER BY created_at
1169
- LIMIT ?`,args:[t]})).rows.map(n=>h(R(n)))}async function Sr(e,t){return(await i().execute({sql:`UPDATE jobs
1170
- SET options = json_set(options,
1171
- '$.extractUrlDispatchAttempts', COALESCE(json_extract(options, '$.extractUrlDispatchAttempts'), 0) + 1,
1172
- '$.extractUrlLastDispatchAt', ?,
1173
- '$.extractUrlLastDispatchError', ?)
1174
- WHERE id = ? AND status = 'pending'
1175
- AND json_extract(options, '$.executionOwner') = 'inngest'
1176
- AND json_extract(options, '$.jobKind') = 'extract_url'`,args:[new Date().toISOString(),t??null,e]})).rowsAffected===1}async function Rr(e){return(await i().execute({sql:"SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50",args:[e]})).rows.map(r=>h(R(r)))}async function Ar(e,t){let n=(await i().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 n?{contentHash:String(n.content_hash),content:String(n.content),title:n.title==null?null:String(n.title),contentBytes:Number(n.content_bytes),truncated:Number(n.truncated)===1,checkedAt:String(n.checked_at)}:null}async function wr(e,t,r){await i().execute({sql:`
1177
- INSERT INTO page_snapshots (user_id, url, content_hash, content, title, content_bytes, truncated, checked_at)
1178
- VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
1179
- ON CONFLICT(user_id, url) DO UPDATE SET
1180
- content_hash = excluded.content_hash,
1181
- content = excluded.content,
1182
- title = excluded.title,
1183
- content_bytes = excluded.content_bytes,
1184
- truncated = excluded.truncated,
1185
- checked_at = excluded.checked_at
1186
- `,args:[e,t,r.contentHash,r.content,r.title,r.contentBytes,r.truncated?1:0]})}var at=32*1024;function ot(e){if(e==null)return null;let t=JSON.stringify(e),r=Buffer.byteLength(t,"utf8");return r<=at?t:JSON.stringify({truncated:!0,originalBytes:r,sha256:v("sha256").update(t).digest("hex")})}function _t(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 Or(e){let t=N();return await i().execute({sql:`
1187
- INSERT INTO request_events (id, user_id, source, status, query, location, result_count, result, error)
1188
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1189
- `,args:[t,e.userId,e.source,e.status,e.query,e.location??null,e.resultCount??null,ot(e.result),e.error??null]}),t}async function fr(e,t=100){return(await i().execute({sql:`
1190
- SELECT id, user_id, source, status, query, location, result_count, NULL AS result, error, created_at
1191
- FROM request_events
1192
- WHERE user_id = ?
1193
- ORDER BY created_at DESC
1194
- LIMIT ?
1195
- `,args:[e,t]})).rows.map(n=>_t(n))}function fe(e){if(e==null)return{};try{let t=JSON.parse(String(e));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function $(e){return{id:String(e.id),user_id:Number(e.user_id),workflow_id:String(e.workflow_id),name:String(e.name),input:fe(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 J(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:fe(e.input_json),status:String(e.status),manifest:e.manifest_json!=null?H(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:he(e.step_log_json),webhook_url:e.webhook_url!=null?String(e.webhook_url):null}}function he(e){if(e==null)return[];try{let t=JSON.parse(String(e));return Array.isArray(t)?t:[]}catch{return[]}}function Ie(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 hr(e){let t=N();await i().execute({sql:`
1196
- INSERT INTO workflow_schedules (
1197
- id, user_id, workflow_id, name, input_json, cadence, timezone, webhook_url, next_run_at
1198
- )
1199
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1200
- `,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 Y(t,e.userId);if(!r)throw new Error("Failed to create workflow schedule");return r}async function Y(e,t){let r=t==null?await i().execute({sql:"SELECT * FROM workflow_schedules WHERE id = ? AND status != ? LIMIT 1",args:[e,"deleted"]}):await i().execute({sql:"SELECT * FROM workflow_schedules WHERE id = ? AND user_id = ? AND status != ? LIMIT 1",args:[e,t,"deleted"]});return r.rows[0]?$(r.rows[0]):null}async function Ir(e){return(await i().execute({sql:"SELECT * FROM workflow_schedules WHERE user_id = ? AND status != 'deleted' ORDER BY created_at DESC LIMIT 100",args:[e]})).rows.map(r=>$(r))}async function xr(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 i().execute({sql:`UPDATE workflow_schedules SET ${t.join(", ")} WHERE id = ? AND user_id = ? AND status != 'deleted'`,args:[...r,e.id,e.userId]})),Y(e.id,e.userId)}async function yr(e,t){return(await i().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 Cr(e,t=10){return(await i().execute({sql:`
1201
- SELECT * FROM workflow_schedules
1202
- WHERE status = 'active' AND next_run_at IS NOT NULL AND next_run_at <= ?
1203
- ORDER BY next_run_at ASC
1204
- LIMIT ?
1205
- `,args:[e,t]})).rows.map(n=>$(n))}async function Ur(e,t,r){await i().execute({sql:"UPDATE workflow_schedules SET last_run_at = ?, next_run_at = ?, updated_at = datetime('now') WHERE id = ?",args:[t,r,e]})}async function Dr(e){let t=e.id??N(),r=[t,e.userId,e.scheduleId??null,e.workflowId,JSON.stringify(e.workflowInput),e.idempotencyKey??null,e.webhookUrl??null,e.totalSteps??null];try{await i().execute({sql:`
1206
- INSERT INTO workflow_runs (id, user_id, schedule_id, workflow_id, input_json, idempotency_key, webhook_url, total_steps)
1207
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1208
- `,args:r})}catch(s){if(e.idempotencyKey){let a=await ct(e.idempotencyKey);if(a)return a}throw s}let n=await ut(t,e.userId);if(!n)throw new Error("Failed to create workflow run");return n}async function ut(e,t){let r=t==null?await i().execute({sql:"SELECT * FROM workflow_runs WHERE id = ? LIMIT 1",args:[e]}):await i().execute({sql:"SELECT * FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1",args:[e,t]});return r.rows[0]?J(r.rows[0]):null}async function ct(e){let t=await i().execute({sql:"SELECT * FROM workflow_runs WHERE idempotency_key = ? LIMIT 1",args:[e]});return t.rows[0]?J(t.rows[0]):null}async function vr(e,t=50){return(await i().execute({sql:"SELECT * FROM workflow_runs WHERE user_id = ? ORDER BY queued_at DESC LIMIT ?",args:[e,t]})).rows.map(n=>J(n))}async function kr(e){await i().execute({sql:"UPDATE workflow_runs SET status = 'running', started_at = COALESCE(started_at, datetime('now')) WHERE id = ?",args:[e]})}async function Xr(e,t,r){await i().execute({sql:"UPDATE workflow_runs SET status = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,JSON.stringify(r),e]})}async function Fr(e,t,r){await i().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 dt(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 xe(e,t,r){await i().execute({sql:`
1209
- INSERT INTO workflow_artifacts (id, run_id, user_id, kind, label, path, content_type, bytes, rows_count, content)
1210
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1211
- `,args:[N(),e,t,r.kind,r.label,r.path,dt(r.kind),r.bytes??null,r.rows??null,r.content??null]})}async function Mr(e,t,r){await i().execute({sql:"DELETE FROM workflow_artifacts WHERE run_id = ? AND user_id = ?",args:[e,t]});for(let n of r)await xe(e,t,n);return ye(e,t)}async function Pr(e,t,r){for(let n of r)await xe(e,t,n);return ye(e,t)}async function Hr(e,t){let n=(t==null?await i().execute({sql:"SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? LIMIT 1",args:[e]}):await i().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 n?{stepIndex:n.step_index!=null?Number(n.step_index):0,totalSteps:n.total_steps!=null?Number(n.total_steps):null,state:n.step_state_json!=null?H(n.step_state_json):null,stepLog:he(n.step_log_json)}:null}async function jr(e,t){let r=[...t.appendLog,t.logEntry];await i().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 ye(e,t){return(await i().execute({sql:"SELECT * FROM workflow_artifacts WHERE run_id = ? AND user_id = ? ORDER BY created_at ASC",args:[e,t]})).rows.map(n=>Ie(n))}async function qr(e,t){let r=await i().execute({sql:"SELECT * FROM workflow_artifacts WHERE id = ? AND user_id = ? LIMIT 1",args:[e,t]});return r.rows[0]?Ie(r.rows[0]):null}async function Wr(e){await i().execute({sql:`
1212
- INSERT INTO workflow_webhook_deliveries (
1213
- id, run_id, schedule_id, user_id, webhook_url, status, attempts, last_error, delivered_at
1214
- )
1215
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1216
- `,args:[N(),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 Br(){let e=await i().execute("SELECT COUNT(*) as n FROM users WHERE active = 1");return Number(e.rows[0]?.n??0)}async function Gr(e){let t=await i().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 Yr(){let e=i(),t=await e.execute(`
1217
- SELECT * FROM jobs
1218
- WHERE status = 'pending'
1219
- AND COALESCE(json_extract(options, '$.executionOwner'), 'cron') NOT IN ('direct', 'inngest')
1220
- ORDER BY created_at
1221
- LIMIT 1
1222
- `),r=t.rows[0]?R(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 V=45e3;async function Kr(e){let t=i(),r=await K(e);if(!r||r.options.executionOwner!=="direct"||r.options.serpOnly===!0)return;let n=Math.max(0,Number(r.options.paaRecoveryCount??0));if(r.status!=="running"){if(r.status==="pending"){let s=N(),a=new Date().toISOString(),_=new Date(Date.now()+V).toISOString();return(await t.execute({sql:`UPDATE jobs
1223
- SET status = 'running', started_at = datetime('now'), error = NULL,
1224
- options = json_set(options,
1225
- '$.paaLeaseToken', ?,
1226
- '$.paaLeaseExpiresAt', ?,
1227
- '$.paaLastDispatchAt', ?)
1228
- WHERE id = ? AND status = 'pending'`,args:[s,_,a,e]})).rowsAffected===0?void 0:{job:{...r,status:"running",started_at:a,options:{...r.options,paaLeaseToken:s,paaLeaseExpiresAt:_,paaLastDispatchAt:a}},leaseToken:s,recoveryCount:n}}return{job:r,leaseToken:null,recoveryCount:n}}}async function $r(e,t){return(await i().execute({sql:"UPDATE jobs SET result = ? WHERE id = ? AND status = 'running'",args:[JSON.stringify(t),e]})).rowsAffected===1}async function Jr(e,t,r){let n=new Date(Date.now()+V).toISOString();return(await i().execute({sql:`UPDATE jobs SET result = ?, options = json_set(options, '$.paaLeaseExpiresAt', ?)
1229
- WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),n,e,t]})).rowsAffected===1}async function Vr(e,t){let r=new Date(Date.now()+V).toISOString();return(await i().execute({sql:`UPDATE jobs SET options = json_set(options, '$.paaLeaseExpiresAt', ?)
1230
- WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,e,t]})).rowsAffected===1}async function zr(e,t,r){return(await i().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL,
1231
- completed_at = datetime('now'),
1232
- options = json_set(options, '$.paaLeaseExpiresAt', NULL)
1233
- WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[JSON.stringify(r),e,t]})).rowsAffected===1}async function Qr(e,t,r,n){return(await i().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now'),
1234
- options = json_set(options, '$.paaLeaseExpiresAt', NULL)
1235
- WHERE id = ? AND status = 'running' AND json_extract(options, '$.paaLeaseToken') = ?`,args:[r,O(n),e,t]})).rowsAffected===1}async function Zr(e,t){let r=new Date().toISOString();return(await i().execute({sql:`UPDATE jobs SET status = 'pending', started_at = NULL,
1236
- result = CASE WHEN json_valid(result) THEN json_set(result,
1237
- '$.progress.lifecycle.discoveryStatus', 'interrupted',
1238
- '$.progress.lifecycle.completionReason', 'The previous worker lease expired; the same job is recovering automatically.',
1239
- '$.progress.lifecycle.automaticRetries', COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) + 1)
1240
- ELSE result END,
1241
- options = json_set(options,
1242
- '$.paaRecoveryCount', COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) + 1,
1243
- '$.paaLeaseToken', NULL,
1244
- '$.paaLeaseExpiresAt', NULL,
1245
- '$.paaRecoveryQueuedAt', ?)
1246
- WHERE id = ? AND status = 'running'
1247
- AND (
1248
- datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
1249
- OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
1250
- )
1251
- AND COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) < ?`,args:[r,e,Math.max(0,Math.trunc(t))]})).rowsAffected===1}async function en(e,t){return(await i().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL, completed_at = datetime('now')
1252
- WHERE id = ? AND status = 'running'
1253
- AND (
1254
- datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
1255
- OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
1256
- )`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function tn(e,t,r){return(await i().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now')
1257
- WHERE id = ? AND status = 'running'
1258
- AND (
1259
- datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
1260
- OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
1261
- )`,args:[t,O(r),e]})).rowsAffected===1}async function rn(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await i().execute({sql:`SELECT * FROM jobs
1262
- WHERE status IN ('done', 'failed', 'cancelled')
1263
- AND json_extract(options, '$.executionOwner') IN ('direct', 'inngest')
1264
- AND json_extract(options, '$.billingDebitKey') IS NOT NULL
1265
- AND CAST(json_extract(options, '$.billingHoldMc') AS INTEGER) > 0
1266
- AND json_extract(options, '$.billingSettledAt') IS NULL
1267
- ORDER BY completed_at DESC
1268
- LIMIT ?`,args:[t]})).rows.map(n=>h(R(n)))}async function nn(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await i().execute({sql:`SELECT * FROM jobs
1269
- WHERE status = 'running'
1270
- AND started_at <= datetime('now', '-15 minutes')
1271
- AND json_extract(options, '$.executionOwner') IN ('direct', 'inngest')
1272
- ORDER BY started_at
1273
- LIMIT ?`,args:[t]})).rows.map(n=>h(R(n)))}async function sn(e=25){let t=Math.max(1,Math.min(100,Math.floor(e)));return(await i().execute({sql:`SELECT * FROM jobs
1274
- WHERE json_extract(options, '$.executionOwner') = 'direct'
1275
- AND json_extract(options, '$.serpOnly') IS NOT 1
1276
- AND (
1277
- (status = 'running' AND (
1278
- datetime(json_extract(options, '$.paaLeaseExpiresAt')) <= datetime('now')
1279
- OR (json_extract(options, '$.paaLeaseExpiresAt') IS NULL AND started_at <= datetime('now', '-15 minutes'))
1280
- ))
1281
- OR
1282
- (status = 'pending' AND COALESCE(json_extract(options, '$.paaRecoveryCount'), 0) > 0
1283
- AND datetime(COALESCE(json_extract(options, '$.paaLastDispatchAt'), '1970-01-01')) <= datetime('now', '-30 seconds'))
1284
- )
1285
- ORDER BY COALESCE(started_at, created_at)
1286
- LIMIT ?`,args:[t]})).rows.map(n=>h(R(n)))}async function an(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 i().execute({sql:`UPDATE jobs
1287
- SET options = json_set(options, '$.billingSettledAt', ?, '$.billedMc', ?)
1288
- WHERE id = ?`,args:[r,t,e]})).rowsAffected===1}async function on(e,t){await i().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 _n(e,t){return(await i().execute({sql:`UPDATE jobs SET status = 'done', result = ?, error = NULL, public_error_json = NULL, completed_at = datetime('now')
1289
- WHERE id = ? AND status = 'running'`,args:[JSON.stringify(t),e]})).rowsAffected===1}async function un(e,t,r){await i().execute({sql:"UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,O(r),e]})}async function cn(e,t,r){return(await i().execute({sql:`UPDATE jobs SET status = 'failed', error = ?, public_error_json = ?, completed_at = datetime('now')
1290
- WHERE id = ? AND status = 'running'`,args:[t,O(r),e]})).rowsAffected===1}async function dn(e,t,r){await i().execute({sql:"UPDATE jobs SET status = 'cancelled', error = ?, public_error_json = ?, completed_at = datetime('now') WHERE id = ?",args:[t,O(r),e]})}function z(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 Et(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 En(e,t,r,n,s,a){await i().execute({sql:"INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)",args:[e,t,r,n,a??null,JSON.stringify(s)]})}async function ln(){let t=await i().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]?z(t.rows[0]):null}async function Tn(e){let t=await i().execute({sql:"SELECT * FROM kpo_jobs WHERE id = ?",args:[e]});return t.rows[0]?z(t.rows[0]):null}async function pn(e,t){await i().execute({sql:"UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?",args:[t,e]})}async function mn(e,t){await i().execute({sql:"UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?",args:[JSON.stringify(t),e]})}async function Nn(e,t){await i().execute({sql:"UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?",args:[t,e]})}async function gn(e,t,r){await i().execute({sql:"INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)",args:[e,t,JSON.stringify(r)]})}async function Ln(e){return(await i().execute({sql:"SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC",args:[e]})).rows.map(r=>Et(r))}async function bn(e){return(e!=null?await i().execute({sql:"SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50",args:[e]}):await i().execute("SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50")).rows.map(r=>z(r))}async function Sn(e){let t=await i().execute({sql:"SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1",args:[e]});return t.rows[0]?S(t.rows[0]):void 0}async function Rn(e,t){await i().execute({sql:"UPDATE users SET stripe_customer_id = ? WHERE id = ?",args:[t,e]})}async function An(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("extra concurrency slots must be a non-negative safe integer");await lt(e,Math.ceil(t/2))}async function lt(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("concurrency pack quantity must be a non-negative safe integer");await i().execute({sql:"UPDATE users SET concurrency_pack_quantity = ?, extra_concurrency_slots = ? WHERE id = ?",args:[t,t*2,e]})}async function wn(e,t){await i().execute({sql:"UPDATE users SET concurrency_stripe_sub_id = ? WHERE id = ?",args:[t,e]})}async function On(e,t,r,n){await i().execute({sql:"UPDATE users SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ? WHERE id = ?",args:[t,Math.max(0,Math.round(r)),n,e]})}async function fn(e){if(!e.invoiceId||e.invoiceId.length>500)throw new Error("invoice id is required and must be bounded");if(!Number.isSafeInteger(e.creditsMc)||e.creditsMc<=0)throw new Error("subscription credit amount must be a positive integer");await A(e.userId);let t=i(),r=e.neverExpires?"9999-12-31 23:59:59":null,n=await t.batch([{sql:`INSERT OR IGNORE INTO ledger (user_id, amount_mc, operation, description, stripe_pi)
1291
- VALUES (?, ?, 'subscription', ?, ?)`,args:[e.userId,e.creditsMc,e.description,e.invoiceId]},{sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
1292
- SELECT ?, ?, ?, 'subscription', ?, COALESCE(?, datetime('now', ?)), ?
1293
- WHERE changes() = 1`,args:[e.userId,e.creditsMc,e.creditsMc,e.invoiceId,r,D,e.neverExpires?1:0]},{sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND changes() = 1",args:[e.creditsMc,e.userId]},{sql:`UPDATE users
1294
- SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ?,
1295
- memory_plan = CASE WHEN ? IS NOT NULL THEN ? ELSE memory_plan END,
1296
- memory_subscription_id = CASE WHEN ? IS NOT NULL THEN NULL ELSE memory_subscription_id END
1297
- WHERE id = ?
1298
- AND EXISTS (
1299
- SELECT 1 FROM ledger
1300
- WHERE stripe_pi = ? AND user_id = ? AND amount_mc = ? AND operation = 'subscription'
1301
- )`,args:[e.tier,Math.max(0,Math.round(e.concurrency)),e.subscriptionId,e.includedMemoryPlan,e.includedMemoryPlan,e.includedMemoryPlan,e.userId,e.invoiceId,e.userId,e.creditsMc]}],"write"),a=(await t.execute({sql:`SELECT l.user_id, l.amount_mc, l.operation, u.balance_mc
1302
- FROM ledger l JOIN users u ON u.id = l.user_id
1303
- WHERE l.stripe_pi = ? LIMIT 1`,args:[e.invoiceId]})).rows[0];if(!a||Number(a.user_id)!==e.userId||Number(a.amount_mc)!==e.creditsMc||String(a.operation)!=="subscription")throw new Error("invoice idempotency key conflicts with an existing grant");return{granted:n[0]?.rowsAffected===1,balance_mc:Number(a.balance_mc??0)}}function Tt(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 pt(e){let r=(await i().execute({sql:"SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",args:[e]})).rows[0];return r?Tt(r):null}async function hn(e){let t=Math.max(0,Math.round(e.quantity));await i().execute({sql:`INSERT INTO connected_account_billing
1304
- (user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
1305
- VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
1306
- ON CONFLICT(user_id) DO UPDATE SET
1307
- stripe_subscription_id = excluded.stripe_subscription_id,
1308
- stripe_subscription_item_id = excluded.stripe_subscription_item_id,
1309
- price_id = excluded.price_id,
1310
- quantity = excluded.quantity,
1311
- status = excluded.status,
1312
- last_error_code = excluded.last_error_code,
1313
- checked_at = datetime('now'),
1314
- synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
1315
- 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 pt(e.userId);if(!r)throw new Error("connected account billing state was not persisted");return r}async function In(e=60,t=20){let r=`-${Math.max(1,Math.round(e))} minutes`;return(await i().execute({sql:`SELECT u.*
1316
- FROM users u
1317
- LEFT JOIN connected_account_billing b ON b.user_id = u.id
1318
- WHERE u.active = 1
1319
- AND u.subscription_id IS NOT NULL
1320
- AND (b.checked_at IS NULL OR b.checked_at <= datetime('now', ?))
1321
- ORDER BY COALESCE(b.checked_at, u.created_at) ASC
1322
- LIMIT ?`,args:[r,Math.max(1,Math.min(100,Math.round(t)))]})).rows.map(s=>S(s))}async function xn(e,t,r){await i().execute({sql:"UPDATE users SET memory_plan = ?, memory_subscription_id = ? WHERE id = ?",args:[t,r,e]})}async function yn(e){let r=(await i().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 Cn(e){let r=(await i().execute({sql:"SELECT memory_provisioned FROM users WHERE id = ?",args:[e]})).rows[0];return Number(r?.memory_provisioned??0)===1}async function Un(e,t){await i().execute({sql:"UPDATE users SET memory_provisioned = ? WHERE id = ?",args:[t?1:0,e]})}async function Dn(e){let t=await i().execute({sql:"SELECT * FROM users WHERE memory_subscription_id = ? AND active = 1",args:[e]});return t.rows[0]?S(t.rows[0]):void 0}var D="+3 months";async function A(e){return(await i().execute({sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at)
1323
- SELECT ?, legacy.balance_mc, legacy.balance_mc, 'migration', datetime('now', ?)
1324
- FROM (
1325
- SELECT COALESCE(SUM(amount_mc), 0) AS balance_mc
1326
- FROM ledger
1327
- WHERE user_id = ?
1328
- ) AS legacy
1329
- WHERE legacy.balance_mc > 0
1330
- AND EXISTS (
1331
- SELECT 1 FROM ledger
1332
- WHERE user_id = ? AND created_at < ?
1333
- )
1334
- AND NOT EXISTS (
1335
- SELECT 1 FROM credit_lots WHERE user_id = ?
1336
- )`,args:[e,D,e,e,k,e]})).rowsAffected}async function vn(){let e=i(),t=await e.execute({sql:`SELECT id
1337
- FROM credit_lots
1338
- WHERE remaining_mc > 0
1339
- AND never_expires = 0
1340
- AND expires_at <= datetime('now')
1341
- LIMIT 500`});if(t.rows.length===0)return{expired_lots:0,expired_mc:0};let r=[],n=[];for(let c of t.rows){let d=Number(c.id),l=N();r.push({token:l,insertResultIndex:n.length}),n.push({sql:`INSERT OR IGNORE INTO credit_lot_expirations (lot_id, user_id, amount_mc, claim_token)
1342
- SELECT id, user_id, remaining_mc, ?
1343
- FROM credit_lots
1344
- WHERE id = ?
1345
- AND remaining_mc > 0
1346
- AND never_expires = 0
1347
- AND expires_at <= datetime('now')`,args:[l,d]}),n.push({sql:`UPDATE credit_lots
1348
- SET remaining_mc = 0
1349
- WHERE id = ?
1350
- AND EXISTS (
1351
- SELECT 1 FROM credit_lot_expirations
1352
- WHERE lot_id = ? AND claim_token = ?
1353
- )`,args:[d,d,l]}),n.push({sql:`INSERT INTO ledger (user_id, amount_mc, operation, description)
1354
- SELECT user_id, -amount_mc, 'credit_expired', 'credits expired'
1355
- FROM credit_lot_expirations
1356
- WHERE lot_id = ? AND claim_token = ?`,args:[d,l]})}let s=await e.batch(n,"write"),a=r.filter(c=>s[c.insertResultIndex]?.rowsAffected===1).map(c=>c.token);if(a.length===0)return{expired_lots:0,expired_mc:0};let _=a.map(()=>"?").join(", "),u=await e.execute({sql:`SELECT user_id, amount_mc
1357
- FROM credit_lot_expirations
1358
- WHERE claim_token IN (${_})`,args:a}),o=new Set(u.rows.map(c=>Number(c.user_id)));for(let c of o)await Ce(c);return{expired_lots:u.rows.length,expired_mc:u.rows.reduce((c,d)=>c+Number(d.amount_mc),0)}}async function kn(){let e=i(),t=await e.execute({sql:`SELECT DISTINCT u.id AS user_id
1359
- FROM users u
1360
- JOIN ledger l ON l.user_id = u.id
1361
- WHERE l.created_at < ?
1362
- AND NOT EXISTS (SELECT 1 FROM credit_lots WHERE user_id = u.id)`,args:[k]}),r=0,n=0;for(let s of t.rows){let a=Number(s.user_id);if(await A(a)===0)continue;let u=await e.execute({sql:`SELECT amount_mc FROM credit_lots
1363
- WHERE user_id = ? AND source = 'migration'
1364
- ORDER BY id DESC LIMIT 1`,args:[a]}),o=Number(u.rows[0]?.amount_mc??0);await Ce(a),r+=1,n+=o}return{migrated:r,total_mc:n}}async function Xn(e,t,r,n,s,a){let _=i();await A(e);let u=a?.neverExpires;if(u===void 0&&t>0){let l=await _.execute({sql:"SELECT subscription_tier FROM users WHERE id = ? LIMIT 1",args:[e]});u=String(l.rows[0]?.subscription_tier??"").startsWith("sonic")}let o=u?"9999-12-31 23:59:59":null;if(s){let l=[{sql:"INSERT OR IGNORE INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,n??null,s]}];t>0&&l.push({sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
1365
- SELECT ?, ?, ?, ?, ?, COALESCE(?, datetime('now', ?)), ?
1366
- WHERE changes() = 1`,args:[e,t,t,r,s,o,D,u?1:0]}),l.push({sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND changes() = 1",args:[t,e]}),await _.batch(l,"write");let E=await _.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(E.rows[0]?.balance_mc??0)}let c=[];t>0&&c.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,D,u?1:0]}),c.push({sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?",args:[t,e]}),c.push({sql:"INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)",args:[e,t,r,n??null,null]}),await _.batch(c,"write");let d=await _.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(d.rows[0]?.balance_mc??0)}async function Fn(e,t,r,n,s,a){if(!Number.isSafeInteger(t)||t<=0)throw new Error("idempotent credit amount must be a positive integer");if(!s||s.length>500)throw new Error("idempotent credit key is required and must be bounded");let _=i();await A(e);let u=a?.neverExpires;if(u===void 0){let m=await _.execute({sql:"SELECT subscription_tier FROM users WHERE id = ? LIMIT 1",args:[e]});u=String(m.rows[0]?.subscription_tier??"").startsWith("sonic")}let o=u?"9999-12-31 23:59:59":null,c=async()=>{let m=await _.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return Number(m.rows[0]?.balance_mc??0)},d;try{d=await _.batch([{sql:"INSERT OR IGNORE INTO billing_credits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')",args:[s,e,t,r,n]},{sql:"UPDATE billing_credits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed'",args:[s,e,t,r,n]},{sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
1367
- SELECT user_id, amount_mc, amount_mc, operation, NULL, COALESCE(?, datetime('now', ?)), ?
1368
- FROM billing_credits
1369
- WHERE idempotency_key = ? AND status = 'applying'`,args:[o,D,u?1:0,s]},{sql:`UPDATE users
1370
- SET balance_mc = balance_mc + COALESCE((
1371
- SELECT amount_mc FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'
1372
- ), 0)
1373
- WHERE id = ?`,args:[s,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:[s]},{sql:"UPDATE billing_credits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[s]}],"write")}catch(m){try{let T=(await _.execute({sql:"SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",args:[s]})).rows[0];if(T&&Number(T.user_id)===e&&Number(T.amount_mc)===t&&String(T.operation)===r&&String(T.description)===n)return{ok:!0,balance_mc:await c(),duplicate:!0}}catch{}throw m}let E=(await _.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_credits WHERE idempotency_key = ? LIMIT 1",args:[s]})).rows[0];if(!E||Number(E.user_id)!==e||Number(E.amount_mc)!==t||String(E.operation)!==r||String(E.description)!==n)throw new Error("idempotent credit key conflicts with another credit");return{ok:String(E.status)==="applied",balance_mc:await c(),duplicate:d[1]?.rowsAffected===0&&String(E.status)==="applied"}}async function Mn(e,t){return(await i().execute({sql:"SELECT 1 FROM ledger WHERE user_id = ? AND operation = ? LIMIT 1",args:[e,t]})).rows.length>0}async function Pn(e,t){return(await i().execute({sql:"INSERT OR IGNORE INTO free_credit_refreshes (user_id, month) VALUES (?, ?)",args:[e,t]})).rowsAffected>0}async function Hn(e,t,r,n){let s=`debit:${N()}`,a=await mt(e,t,r,n??"",s);return{ok:a.ok,balance_mc:a.balance_mc,billingKey:s}}async function mt(e,t,r,n,s){if(!Number.isSafeInteger(t)||t<=0)throw new Error("idempotent debit amount must be a positive integer");if(!s||s.length>500)throw new Error("idempotent debit key is required and must be bounded");let a=i();await A(e);let _;try{_=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,s,e,r,n]},{sql:"INSERT OR IGNORE INTO billing_debits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')",args:[s,e,t,r,n]},{sql:`UPDATE users
1374
- SET balance_mc = balance_mc - ?
1375
- WHERE id = ?
1376
- AND balance_mc >= ?
1377
- AND (SELECT COALESCE(SUM(remaining_mc), 0)
1378
- FROM credit_lots
1379
- WHERE user_id = ?
1380
- AND remaining_mc > 0
1381
- AND (never_expires = 1 OR expires_at > datetime('now'))) >= ?
1382
- 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,s,e,t,r,n]},{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:[s,e,t,r,n]},{sql:`INSERT OR IGNORE INTO billing_debit_allocations
1383
- (debit_idempotency_key, lot_id, user_id, amount_mc, expires_at)
1384
- WITH ordered AS MATERIALIZED (
1385
- SELECT id,
1386
- remaining_mc,
1387
- expires_at,
1388
- COALESCE(SUM(remaining_mc) OVER (
1389
- ORDER BY never_expires ASC, expires_at ASC, granted_at ASC, id ASC
1390
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
1391
- ), 0) AS prior_mc
1392
- FROM credit_lots
1393
- WHERE user_id = ? AND remaining_mc > 0 AND (never_expires = 1 OR expires_at > datetime('now'))
1394
- ), allocation AS (
1395
- SELECT id, expires_at, MIN(remaining_mc, MAX(0, ? - prior_mc)) AS take_mc
1396
- FROM ordered
1397
- )
1398
- SELECT ?, id, ?, take_mc, expires_at
1399
- FROM allocation
1400
- WHERE take_mc > 0
1401
- AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,args:[e,t,s,e,s]},{sql:`UPDATE credit_lots
1402
- SET remaining_mc = remaining_mc - COALESCE((
1403
- SELECT amount_mc FROM billing_debit_allocations
1404
- WHERE debit_idempotency_key = ? AND lot_id = credit_lots.id
1405
- ), 0)
1406
- WHERE id IN (
1407
- SELECT lot_id FROM billing_debit_allocations WHERE debit_idempotency_key = ?
1408
- )
1409
- AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,args:[s,s,s]},{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:[s]},{sql:"UPDATE billing_debits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",args:[s]}],"write")}catch(d){try{let E=(await a.execute({sql:"SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",args:[s]})).rows[0];if(E&&Number(E.user_id)===e&&Number(E.amount_mc)===t&&String(E.operation)===r&&String(E.description)===n){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:[s]})).rows[0];if(!o||Number(o.user_id)!==e||Number(o.amount_mc)!==t||String(o.operation)!==r||String(o.description)!==n)throw new Error("idempotent debit key conflicts with another debit");let c=await a.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return{ok:String(o.status)==="applied",balance_mc:Number(c.rows[0]?.balance_mc??0),duplicate:_[2]?.rowsAffected===0&&String(o.status)==="applied"}}async function jn(e,t,r,n,s,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(!n||!a)throw new Error("idempotent debit settlement operation and source are required");let _=i();await A(e);let u=`debit-settlement:${v("sha256").update(t).digest("hex")}`,o="wallet_hold_settlement",c=N(),d=JSON.stringify({debitIdempotencyKey:t,refundOperation:n,description:s}),l=async()=>{let T=(await _.execute({sql:`SELECT e.user_id,
1410
- e.status,
1411
- e.amount_mc AS final_amount_mc,
1412
- e.billing_class,
1413
- e.source_surface,
1414
- e.settled_at,
1415
- d.amount_mc AS held_amount_mc
1416
- FROM billing_events e
1417
- JOIN billing_debits d ON d.idempotency_key = ?
1418
- WHERE e.idempotency_key = ?
1419
- AND d.user_id = e.user_id
1420
- AND d.status = 'applied'
1421
- LIMIT 1`,args:[t,u]})).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 q=Number(T.held_amount_mc),w=Number(T.final_amount_mc);if(!Number.isSafeInteger(q)||!Number.isSafeInteger(w)||w<0||w>q)throw new Error("idempotent debit settlement receipt is invalid");let Ue=q-w,Q=await _.execute({sql:`WITH ordered AS (
1422
- SELECT a.amount_mc,
1423
- a.expires_at,
1424
- COALESCE(SUM(a.amount_mc) OVER (
1425
- ORDER BY a.expires_at ASC, a.lot_id ASC
1426
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
1427
- ), 0) AS prior_mc
1428
- FROM billing_debit_allocations a
1429
- WHERE a.debit_idempotency_key = ?
1430
- ), refunds AS (
1431
- SELECT expires_at,
1432
- amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc
1433
- FROM ordered
1434
- )
1435
- SELECT (SELECT COUNT(*) FROM ordered) AS allocation_count,
1436
- COALESCE(SUM(CASE WHEN expires_at > ? THEN refund_mc ELSE 0 END), 0) AS credited_refund_mc
1437
- FROM refunds`,args:[t,w,String(T.settled_at)]}),Z=Number(Q.rows[0]?.allocation_count??0)>0?Number(Q.rows[0]?.credited_refund_mc??0):0,De=await _.execute({sql:"SELECT balance_mc FROM users WHERE id = ?",args:[e]});return{balance_mc:Number(De.rows[0]?.balance_mc??0),final_amount_mc:w,refund_mc:Z,expired_refund_mc:Ue-Z,duplicate:!0}},E;try{E=await _.batch([{sql:`INSERT OR IGNORE INTO billing_events
1438
- (id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)
1439
- VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,args:[c,e,u,o,a,r,d]},{sql:`WITH ordered AS (
1440
- SELECT a.lot_id,
1441
- a.amount_mc,
1442
- a.expires_at,
1443
- COALESCE(SUM(a.amount_mc) OVER (
1444
- ORDER BY a.expires_at ASC, a.lot_id ASC
1445
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
1446
- ), 0) AS prior_mc
1447
- FROM billing_debit_allocations a
1448
- WHERE a.debit_idempotency_key = ?
1449
- ), refunds AS (
1450
- SELECT lot_id,
1451
- expires_at,
1452
- amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc
1453
- FROM ordered
1454
- )
1455
- UPDATE users
1456
- SET balance_mc = balance_mc + CASE
1457
- WHEN EXISTS (SELECT 1 FROM ordered) THEN COALESCE(
1458
- (SELECT SUM(refund_mc) FROM refunds WHERE expires_at > datetime('now')),
1459
- 0
1460
- )
1461
- -- Holds created before allocation provenance cannot be safely
1462
- -- tied back to a source lot. Treat their unused portion as
1463
- -- expired instead of minting spendable credits with a guessed
1464
- -- expiry.
1465
- ELSE 0
1466
- END
1467
- WHERE id = ?
1468
- AND EXISTS (
1469
- SELECT 1
1470
- FROM billing_events e
1471
- JOIN billing_debits d ON d.idempotency_key = ?
1472
- WHERE e.idempotency_key = ?
1473
- AND e.user_id = ?
1474
- AND e.billing_class = ?
1475
- AND e.source_surface = ?
1476
- AND e.status = 'settling'
1477
- AND d.user_id = e.user_id
1478
- AND d.status = 'applied'
1479
- AND e.amount_mc BETWEEN 0 AND d.amount_mc
1480
- )`,args:[t,r,e,t,u,e,o,a]},{sql:`UPDATE billing_events
1481
- SET status = 'applying'
1482
- WHERE idempotency_key = ?
1483
- AND user_id = ?
1484
- AND billing_class = ?
1485
- AND source_surface = ?
1486
- AND status = 'settling'
1487
- AND changes() = 1`,args:[u,e,o,a]},{sql:`WITH ordered AS (
1488
- SELECT a.lot_id,
1489
- a.amount_mc,
1490
- a.expires_at,
1491
- COALESCE(SUM(a.amount_mc) OVER (
1492
- ORDER BY a.expires_at ASC, a.lot_id ASC
1493
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
1494
- ), 0) AS prior_mc
1495
- FROM billing_debit_allocations a
1496
- WHERE a.debit_idempotency_key = ?
1497
- ), refunds AS (
1498
- SELECT lot_id,
1499
- expires_at,
1500
- amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc
1501
- FROM ordered
1502
- )
1503
- UPDATE credit_lots
1504
- SET remaining_mc = remaining_mc + COALESCE((SELECT refund_mc FROM refunds WHERE refunds.lot_id = credit_lots.id), 0)
1505
- WHERE id IN (
1506
- SELECT lot_id FROM refunds
1507
- WHERE refund_mc > 0 AND expires_at > datetime('now')
1508
- )
1509
- AND EXISTS (SELECT 1 FROM billing_events WHERE idempotency_key = ? AND status = 'applying')`,args:[t,r,u]},{sql:`WITH ordered AS (
1510
- SELECT a.amount_mc,
1511
- a.expires_at,
1512
- COALESCE(SUM(a.amount_mc) OVER (
1513
- ORDER BY a.expires_at ASC, a.lot_id ASC
1514
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
1515
- ), 0) AS prior_mc
1516
- FROM billing_debit_allocations a
1517
- WHERE a.debit_idempotency_key = ?
1518
- ), refunds AS (
1519
- SELECT expires_at,
1520
- amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc
1521
- FROM ordered
1522
- ), credited AS (
1523
- SELECT e.user_id,
1524
- CASE
1525
- WHEN EXISTS (SELECT 1 FROM ordered) THEN COALESCE(
1526
- (SELECT SUM(refund_mc) FROM refunds WHERE expires_at > datetime('now')),
1527
- 0
1528
- )
1529
- ELSE 0
1530
- END AS refund_mc
1531
- FROM billing_events e
1532
- JOIN billing_debits d ON d.idempotency_key = ?
1533
- WHERE e.idempotency_key = ?
1534
- AND e.status = 'applying'
1535
- )
1536
- INSERT INTO ledger (user_id, amount_mc, operation, description)
1537
- SELECT user_id,
1538
- refund_mc,
1539
- ?,
1540
- ?
1541
- FROM credited
1542
- WHERE refund_mc > 0`,args:[t,r,t,u,n,s]},{sql:`UPDATE billing_events
1543
- SET status = 'settled', settled_at = datetime('now')
1544
- WHERE idempotency_key = ?
1545
- AND status = 'applying'`,args:[u]}],"write")}catch(g){try{let T=await l();if(T)return T}catch{}throw g}let m=await l();if(!m)throw new Error("idempotent debit settlement did not reach a durable settled state");return{...m,duplicate:E[0]?.rowsAffected===0}}async function qn(e,t=50){return(await i().execute({sql:"SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",args:[e,t]})).rows.map(n=>({id:Number(n.id),user_id:Number(n.user_id),amount_mc:Number(n.amount_mc),operation:String(n.operation),description:n.description!=null?String(n.description):null,stripe_pi:n.stripe_pi!=null?String(n.stripe_pi):null,created_at:String(n.created_at)}))}async function Wn(e,t,r){let n=i(),s=r.refund.map(()=>"?").join(", "),a=r.grant.map(()=>"?").join(", "),_=r.purchase.map(()=>"?").join(", "),u=await n.execute({sql:`SELECT
1546
- COALESCE(SUM(CASE WHEN amount_mc < 0 AND operation NOT IN (${s}) THEN -amount_mc ELSE 0 END), 0) AS spent_mc,
1547
- COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${s}) THEN amount_mc ELSE 0 END), 0) AS refunded_mc,
1548
- COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${a}) THEN amount_mc ELSE 0 END), 0) AS granted_mc,
1549
- COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${_}) THEN amount_mc ELSE 0 END), 0) AS purchased_mc,
1550
- COALESCE(SUM(amount_mc), 0) AS net_mc
1551
- FROM ledger WHERE user_id = ? AND created_at >= ?`,args:[...r.refund,...r.refund,...r.grant,...r.purchase,e,t]}),o=await n.execute({sql:`SELECT operation, -SUM(amount_mc) AS mc, COUNT(*) AS count
1552
- FROM ledger
1553
- WHERE user_id = ? AND created_at >= ? AND amount_mc < 0 AND operation NOT IN (${s})
1554
- GROUP BY operation
1555
- ORDER BY mc DESC, operation ASC`,args:[e,t,...r.refund]}),c=u.rows[0];return{spent_mc:Number(c?.spent_mc??0),refunded_mc:Number(c?.refunded_mc??0),granted_mc:Number(c?.granted_mc??0),purchased_mc:Number(c?.purchased_mc??0),net_mc:Number(c?.net_mc??0),by_operation:o.rows.map(d=>({operation:String(d.operation),mc:Number(d.mc),count:Number(d.count)}))}}async function Bn(e){return(await i().execute({sql:"SELECT 1 FROM ledger WHERE stripe_pi = ? LIMIT 1",args:[e]})).rows.length>0}async function Ce(e,t){let r=i();for(let s=0;s<8;s+=1){let _=(await r.execute({sql:`SELECT
1556
- u.balance_mc AS cached_balance_mc,
1557
- COUNT(l.id) AS lot_count,
1558
- COALESCE(SUM(CASE
1559
- WHEN l.remaining_mc > 0
1560
- AND (l.never_expires = 1 OR l.expires_at > datetime('now'))
1561
- THEN l.remaining_mc ELSE 0 END), 0) AS projected_balance_mc
1562
- FROM users u
1563
- LEFT JOIN credit_lots l ON l.user_id = u.id
1564
- WHERE u.id = ?
1565
- GROUP BY u.id, u.balance_mc`,args:[e]})).rows[0];if(!_)return 0;if(Number(_.lot_count??0)===0&&(await A(Number(e)),(await r.execute({sql:"SELECT 1 FROM credit_lots WHERE user_id = ? LIMIT 1",args:[e]})).rows.length>0))continue;let u=Number(_.cached_balance_mc??0),o=Number(_.projected_balance_mc??0);if(u===o||(await r.execute({sql:"UPDATE users SET balance_mc = ? WHERE id = ? AND balance_mc = ?",args:[o,e,u]})).rowsAffected===1)return o}let n=await r.execute({sql:"SELECT balance_mc FROM users WHERE id = ? LIMIT 1",args:[e]});return Number(n.rows[0]?.balance_mc??0)}var Gn=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()}),Yn=p.object({id:p.number(),job_id:p.string(),phase:p.string(),completed_at:p.string(),output_summary:p.string()});async function Kn(e){return(await i().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 $n(e,t){let n=(await i().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(n)return{id:String(n.id),subject:String(n.subject),body_md:String(n.body_md),source:String(n.source),sent_at:String(n.sent_at),opened_at:n.opened_at!=null?String(n.opened_at):null}}async function Jn(e,t){let r=i();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 s=(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 s&&s.opened_at!=null?String(s.opened_at):null}async function Vn(e,t){return(await i().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 zn(e,t,r,n="operator"){let s=N();return await i().execute({sql:"INSERT INTO inbox_messages (id, user_id, subject, body_md, source) VALUES (?, ?, ?, ?, ?)",args:[s,e,t,r,n]}),s}async function Qn(){return(await i().execute("SELECT id FROM users WHERE active = 1")).rows.map(t=>Number(t.id))}export{Lt as a,ve as b,ke as c,O as d,ae as e,W as f,Be as g,Ge as h,_e as i,ee as j,te as k,re as l,ne as m,se as n,ie as o,k as p,U as q,le as r,It as s,i as t,xt as u,yt as v,Ct as w,nt as x,Ut as y,Dt as z,vt as A,kt as B,Xt as C,st as D,Ft as E,Mt as F,Pt as G,Ht as H,jt as I,qt as J,Wt as K,Bt as L,Gt as M,Yt as N,Re as O,Ae as P,Kt as Q,$t as R,Jt as S,Vt as T,zt as U,Qt as V,Zt as W,er as X,tr as Y,rr as Z,nr as _,sr as $,ir as aa,ar as ba,or as ca,_r as da,ur as ea,it as fa,cr as ga,dr as ha,Er as ia,lr as ja,Tr as ka,pr as la,mr as ma,Nr as na,gr as oa,K as pa,Lr as qa,br as ra,Sr as sa,Rr as ta,Ar as ua,wr as va,Or as wa,fr as xa,hr as ya,Y as za,Ir as Aa,xr as Ba,yr as Ca,Cr as Da,Ur as Ea,Dr as Fa,ut as Ga,ct as Ha,vr as Ia,kr as Ja,Xr as Ka,Fr as La,Mr as Ma,Pr as Na,Hr as Oa,jr as Pa,ye as Qa,qr as Ra,Wr as Sa,Br as Ta,Gr as Ua,Yr as Va,Kr as Wa,$r as Xa,Jr as Ya,Vr as Za,zr as _a,Qr as $a,Zr as ab,en as bb,tn as cb,rn as db,nn as eb,sn as fb,an as gb,on as hb,_n as ib,un as jb,cn as kb,dn as lb,En as mb,ln as nb,Tn as ob,pn as pb,mn as qb,Nn as rb,gn as sb,Ln as tb,bn as ub,Sn as vb,Rn as wb,An as xb,lt as yb,wn as zb,On as Ab,fn as Bb,pt as Cb,hn as Db,In as Eb,xn as Fb,yn as Gb,Cn as Hb,Un as Ib,Dn as Jb,D as Kb,vn as Lb,kn as Mb,Xn as Nb,Fn as Ob,Mn as Pb,Pn as Qb,Hn as Rb,mt as Sb,jn as Tb,qn as Ub,Wn as Vb,Bn as Wb,Ce as Xb,Gn as Yb,Yn as Zb,Kn as _b,$n as $b,Jn as ac,Vn as bc,zn as cc,Qn as dc};