better-supabase 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -41
- package/dist/access-sql-B0dF-SoF.js +181 -0
- package/dist/actor-CQTlc1Hh.d.ts +43 -0
- package/dist/adapter-Bc1o3zyy.js +1106 -0
- package/dist/agents-CVHGd7It.d.ts +94 -0
- package/dist/ai-cache-CBWs1W0k.js +80 -0
- package/dist/ai-cache-DaDu_G5L.d.ts +64 -0
- package/dist/ai-chat-7amvXjmQ.d.ts +590 -0
- package/dist/ai-chat-B24UI962.js +743 -0
- package/dist/ai-files-CSPNswUY.js +319 -0
- package/dist/ai-sdk/agents/index.d.ts +74 -0
- package/dist/ai-sdk/agents/index.js +159 -0
- package/dist/ai-sdk/batches/index.d.ts +74 -0
- package/dist/ai-sdk/batches/index.js +180 -0
- package/dist/ai-sdk/cache/index.d.ts +38 -0
- package/dist/ai-sdk/cache/index.js +127 -0
- package/dist/ai-sdk/chat/index.d.ts +2 -0
- package/dist/ai-sdk/chat/index.js +246 -0
- package/dist/ai-sdk/embeddings/index.d.ts +3 -0
- package/dist/ai-sdk/embeddings/index.js +2 -0
- package/dist/ai-sdk/files/index.d.ts +61 -0
- package/dist/ai-sdk/files/index.js +111 -0
- package/dist/ai-sdk/index.d.ts +145 -0
- package/dist/ai-sdk/index.js +174 -0
- package/dist/ai-sdk/mcp/index.d.ts +102 -0
- package/dist/ai-sdk/mcp/index.js +362 -0
- package/dist/ai-sdk/memory/index.d.ts +67 -0
- package/dist/ai-sdk/memory/index.js +175 -0
- package/dist/ai-sdk/react/index.d.ts +2 -0
- package/dist/ai-sdk/react/index.js +3 -0
- package/dist/ai-sdk/react/server.d.ts +6 -0
- package/dist/ai-sdk/react/server.js +7 -0
- package/dist/ai-sdk/workflow/index.d.ts +249 -0
- package/dist/ai-sdk/workflow/index.js +575 -0
- package/dist/ai-sdk/workflow/react/index.d.ts +20 -0
- package/dist/ai-sdk/workflow/react/index.js +66 -0
- package/dist/ai-sdk/workflow/react/server.d.ts +10 -0
- package/dist/ai-sdk/workflow/react/server.js +12 -0
- package/dist/ai-tasks-Cd1HMFvB.d.ts +131 -0
- package/dist/announcements-CiyOs-xP.js +107 -0
- package/dist/announcements-ZRGnScWI.d.ts +72 -0
- package/dist/api-keys-rCGtKaZA.d.ts +156 -0
- package/dist/assistant-DwdpDH1q.d.ts +147 -0
- package/dist/astro/index.d.ts +86 -0
- package/dist/astro/index.js +93 -0
- package/dist/attributes-5dYul0u0.js +13 -0
- package/dist/auth-link-BjFPqwLz.d.ts +97 -0
- package/dist/auth-link-DrDX9KOT.js +114 -0
- package/dist/authorization-BFCqamMs.d.ts +172 -0
- package/dist/before-route-CnR0C0Jr.js +30 -0
- package/dist/bind-Bu_ZNa3W.d.ts +62 -0
- package/dist/bind-l4kAvYSv.js +97 -0
- package/dist/block-events-CrT8r7Fe.js +76 -0
- package/dist/block-fields-GFkxl4dP.js +49 -0
- package/dist/block-fields-SJ6fEf3o.d.ts +31 -0
- package/dist/block-helpers-CKtbFcRv.d.ts +37 -0
- package/dist/block-helpers-DWHxHsEQ.js +190 -0
- package/dist/block-hooks-BmjMyXTa.js +114 -0
- package/dist/block-hooks-plbZ0AbJ.d.ts +107 -0
- package/dist/{kit-transport-DJlHQUBU.js → block-transport-BLr-ZgLc.js} +11 -7
- package/dist/block-transport-DvaoPuZ9.d.ts +48 -0
- package/dist/blocks/agents/index.d.ts +3 -0
- package/dist/blocks/agents/index.js +123 -0
- package/dist/blocks/ai-cache/index.d.ts +3 -0
- package/dist/blocks/ai-cache/index.js +3 -0
- package/dist/blocks/ai-chat/index.d.ts +31 -0
- package/dist/blocks/ai-chat/index.js +5 -0
- package/dist/blocks/ai-chat/react/index.d.ts +77 -0
- package/dist/blocks/ai-chat/react/index.js +387 -0
- package/dist/blocks/ai-chat/react/server.d.ts +16 -0
- package/dist/blocks/ai-chat/react/server.js +14 -0
- package/dist/blocks/ai-files/index.d.ts +14 -0
- package/dist/blocks/ai-files/index.js +3 -0
- package/dist/blocks/ai-providers/index.d.ts +7 -0
- package/dist/blocks/ai-providers/index.js +211 -0
- package/dist/blocks/ai-tasks/index.d.ts +3 -0
- package/dist/blocks/ai-tasks/index.js +203 -0
- package/dist/blocks/announcements/index.d.ts +3 -0
- package/dist/blocks/announcements/index.js +3 -0
- package/dist/blocks/announcements/react/index.d.ts +31 -0
- package/dist/blocks/announcements/react/index.js +86 -0
- package/dist/blocks/announcements/react/server.d.ts +6 -0
- package/dist/blocks/announcements/react/server.js +7 -0
- package/dist/blocks/api-keys/index.d.ts +35 -0
- package/dist/blocks/api-keys/index.js +334 -0
- package/dist/blocks/attachments/index.d.ts +210 -0
- package/dist/blocks/attachments/index.js +222 -0
- package/dist/blocks/audit/index.d.ts +366 -0
- package/dist/blocks/audit/index.js +714 -0
- package/dist/blocks/billing/index.d.ts +265 -0
- package/dist/blocks/billing/index.js +400 -0
- package/dist/blocks/comments/index.d.ts +200 -0
- package/dist/blocks/comments/index.js +207 -0
- package/dist/blocks/connectors/index.d.ts +3 -0
- package/dist/blocks/connectors/index.js +164 -0
- package/dist/blocks/data-lifecycle/index.d.ts +251 -0
- package/dist/blocks/data-lifecycle/index.js +353 -0
- package/dist/blocks/entitlements/index.d.ts +14 -0
- package/dist/blocks/entitlements/index.js +25 -0
- package/dist/blocks/flags/index.d.ts +226 -0
- package/dist/blocks/flags/index.js +282 -0
- package/dist/blocks/inbox/index.d.ts +6 -0
- package/dist/blocks/inbox/index.js +499 -0
- package/dist/blocks/inbox/react/index.d.ts +2 -0
- package/dist/blocks/inbox/react/index.js +225 -0
- package/dist/blocks/inbox/react/server.d.ts +10 -0
- package/dist/blocks/inbox/react/server.js +12 -0
- package/dist/blocks/index.d.ts +65 -0
- package/dist/blocks/index.js +77 -0
- package/dist/blocks/jobs/index.d.ts +297 -0
- package/dist/blocks/jobs/index.js +1051 -0
- package/dist/blocks/knowledge/index.d.ts +3 -0
- package/dist/blocks/knowledge/index.js +3 -0
- package/dist/blocks/memory/index.d.ts +8 -0
- package/dist/blocks/memory/index.js +289 -0
- package/dist/blocks/notifications/index.d.ts +297 -0
- package/dist/blocks/notifications/index.js +425 -0
- package/dist/blocks/notifications/react/index.d.ts +44 -0
- package/dist/blocks/notifications/react/index.js +67 -0
- package/dist/blocks/notifications/react/server.d.ts +6 -0
- package/dist/blocks/notifications/react/server.js +7 -0
- package/dist/blocks/onboarding/index.d.ts +3 -0
- package/dist/blocks/onboarding/index.js +64 -0
- package/dist/blocks/onboarding/react/index.d.ts +28 -0
- package/dist/blocks/onboarding/react/index.js +78 -0
- package/dist/blocks/onboarding/react/server.d.ts +7 -0
- package/dist/blocks/onboarding/react/server.js +7 -0
- package/dist/blocks/organizations/index.d.ts +241 -0
- package/dist/blocks/organizations/index.js +400 -0
- package/dist/blocks/outbox/index.d.ts +3 -0
- package/dist/blocks/outbox/index.js +266 -0
- package/dist/blocks/profiles/index.d.ts +40 -0
- package/dist/blocks/profiles/index.js +28 -0
- package/dist/blocks/push/index.d.ts +185 -0
- package/dist/blocks/push/index.js +258 -0
- package/dist/blocks/settings/index.d.ts +89 -0
- package/dist/blocks/settings/index.js +104 -0
- package/dist/blocks/sso/index.d.ts +198 -0
- package/dist/blocks/sso/index.js +1297 -0
- package/dist/blocks/usage/index.d.ts +4 -0
- package/dist/blocks/usage/index.js +233 -0
- package/dist/blocks/waitlist/index.d.ts +117 -0
- package/dist/blocks/waitlist/index.js +132 -0
- package/dist/blocks/webhooks/index.d.ts +304 -0
- package/dist/{webhooks → blocks/webhooks}/index.js +468 -148
- package/dist/blocks/workflow-builder/index.d.ts +21 -0
- package/dist/blocks/workflow-builder/index.js +5 -0
- package/dist/blocks/workflow-builder/react/index.d.ts +47 -0
- package/dist/blocks/workflow-builder/react/index.js +119 -0
- package/dist/blocks/workflow-builder/react/server.d.ts +8 -0
- package/dist/blocks/workflow-builder/react/server.js +11 -0
- package/dist/blocks/workflows/index.d.ts +3 -0
- package/dist/blocks/workflows/index.js +3 -0
- package/dist/blocks/workflows/react/index.d.ts +47 -0
- package/dist/blocks/workflows/react/index.js +137 -0
- package/dist/blocks/workflows/react/server.d.ts +8 -0
- package/dist/blocks/workflows/react/server.js +11 -0
- package/dist/{index-SaTsEfGP.d.ts → bucket-Cl46E3jS.d.ts} +134 -91
- package/dist/{simplify-_0oUKqCd.js → build-BXIzE6Xv.js} +222 -124
- package/dist/{channel-FQW-CnGR.d.ts → channel-_2Mx1Oyu.d.ts} +2 -2
- package/dist/channels-CkgQ_X60.js +114 -0
- package/dist/chat-sdk/index.d.ts +303 -0
- package/dist/chat-sdk/index.js +733 -0
- package/dist/chat-sdk/react/index.d.ts +2 -0
- package/dist/chat-sdk/react/index.js +3 -0
- package/dist/chat-sdk/react/server.d.ts +3 -0
- package/dist/chat-sdk/react/server.js +2 -0
- package/dist/claims-DDBQT6BK.js +61 -0
- package/dist/{claims-BAf6c7is.js → claims-PqzCxaTD.js} +39 -2
- package/dist/cli/bin.js +1 -1
- package/dist/cli/{codemod-DqV7612m.js → codemod-Bl_tQpHn.js} +105 -2
- package/dist/cli/config-B9HkL8nF.js +2 -0
- package/dist/cli/config-DU7EJImt.js +49 -0
- package/dist/cli/config-NiPxAmUF.js +1077 -0
- package/dist/cli/{dist-xn4wlboc.js → dist-DRAVaE6w.js} +111 -1
- package/dist/cli/{doctor-L7Z8mYTx.js → doctor-B4JOm_ox.js} +992 -316
- package/dist/cli/{env-DmKv_G4A.js → env-CJLbUVPB.js} +1 -1
- package/dist/cli/{env-jIFUADLY.js → env-qXgyMV6r.js} +2 -2
- package/dist/cli/fresh-import-BeYf9I3x.js +40 -0
- package/dist/cli/gen-BGLb_zou.js +3100 -0
- package/dist/cli/gen-DAH8FM2W.js +2 -0
- package/dist/cli/index.d.ts +44 -5
- package/dist/cli/index.js +7 -7
- package/dist/cli/{init-Saf5-R9T.js → init-BIIwclPg.js} +242 -27
- package/dist/cli/{introspect--H7oGmcd.js → introspect-DNUTuNvb.js} +5 -5
- package/dist/cli/{io-k41_z-jT.js → io-DI1mTdSA.js} +12 -3
- package/dist/cli/{keys-ByKHnvP9.js → keys-BGCnnb-s.js} +8 -6
- package/dist/cli/{ohash-D7bG_EOb.js → ohash-BjQY1cBb.js} +1 -1
- package/dist/cli/{openapi-Cp5Fyta-.js → openapi-BlRJ18s3.js} +2 -2
- package/dist/cli/{project-B_GeXyHh.js → project-DZRaBaY7.js} +11 -6
- package/dist/cli/read-sets-CETOSFx4.js +223 -0
- package/dist/cli/{run-BKR2W_79.js → run-DNWxHynX.js} +33 -23
- package/dist/cli/{seed-CUlqWh-8.js → seed-DQzqT7LP.js} +8 -6
- package/dist/cli/{skills-LJJIhLFR.js → skills-QQs4q68J.js} +3 -2
- package/dist/cli/sql-D0GUtUPp.js +536 -0
- package/dist/cli/{config-CPVpXP5I.js → supabase-toml-B-bIukQF.js} +72 -942
- package/dist/cli/typegen-version-DeFbtzYI.js +8 -0
- package/dist/cli/{version-BDJuO0UR.js → version-hr88OZG0.js} +1 -1
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +11 -71
- package/dist/client/native/index.d.ts +228 -0
- package/dist/client/native/index.js +361 -0
- package/dist/client-CI0Z2zWX.js +277 -0
- package/dist/cloud-event-BnmD5fb9.js +46 -0
- package/dist/cloud-event-C1JCc1bH.d.ts +50 -0
- package/dist/{compiler-BXH_aZ7N.d.ts → compiler-DY_wkj4R.d.ts} +2 -2
- package/dist/compose-BIBNgAnq.js +53 -0
- package/dist/composite-Bt1YK3Ke.d.ts +146 -0
- package/dist/composite-DhE66P5u.js +130 -0
- package/dist/config/index.d.ts +6 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-T_ejwyd3.d.ts → config-D44Y7PAv.d.ts} +406 -205
- package/dist/{config-DreERlKo.js → config-Dn7F5k9A.js} +62 -14
- package/dist/connector-Dv8kK0MO.d.ts +107 -0
- package/dist/connectors-hkLCwyEz.d.ts +120 -0
- package/dist/context-BCrWe7ow.d.ts +237 -0
- package/dist/credentials/index.d.ts +31 -0
- package/dist/credentials/index.js +168 -0
- package/dist/cron-DVPUg5k3.js +145 -0
- package/dist/csv-CPAzEmB6.js +25 -0
- package/dist/db-exception-CnWcYPxL.js +15 -0
- package/dist/{define-cNKzqwL_.js → define-B-TVjC25.js} +784 -227
- package/dist/{define-Bp_BZa5h.d.ts → define-CJJkkr1-.d.ts} +353 -61
- package/dist/edge/index.d.ts +69 -11
- package/dist/edge/index.js +191 -75
- package/dist/elysia/index.d.ts +59 -0
- package/dist/elysia/index.js +62 -0
- package/dist/embeddings-4ZvFzjsU.js +127 -0
- package/dist/embeddings-C9KGQnXt.d.ts +68 -0
- package/dist/{refresh-BTrQ_ZTp.d.ts → ended-jLfvw8h-.d.ts} +20 -2
- package/dist/{entitlements-DUxU4oHe.d.ts → entitlements-DHrY2UDZ.d.ts} +16 -7
- package/dist/entitlements-DRl6ZJUm.js +24 -0
- package/dist/envelope-DqnYGRw7.js +47 -0
- package/dist/{errors-DtwXa6b8.js → errors-B11Duf73.js} +82 -13
- package/dist/errors-DLjJHPLK.js +44 -0
- package/dist/escape-like-DHfx6jRr.js +7 -0
- package/dist/escape-like-ImTK0ps_.d.ts +15 -0
- package/dist/eve/index.d.ts +220 -0
- package/dist/eve/index.js +504 -0
- package/dist/eve-gT2Fpjut.d.ts +151 -0
- package/dist/events/index.d.ts +4 -3
- package/dist/events/index.js +9 -50
- package/dist/events-DWDhx77e.d.ts +549 -0
- package/dist/executor-Bq28caBo.js +718 -0
- package/dist/executor-C-R4pucm.js +83 -0
- package/dist/executor-CaWigTyR.d.ts +148 -0
- package/dist/executor-D14tGopJ.d.ts +36 -0
- package/dist/{executor-Cghdstwu.d.ts → executor-DbvExCFC.d.ts} +7 -1
- package/dist/{sql-Coh6n4cC.js → executor-De1Of3m3.js} +210 -11
- package/dist/executor-DvsBbNBS.d.ts +261 -0
- package/dist/expo/index.d.ts +79 -0
- package/dist/expo/index.js +129 -0
- package/dist/expo-sqlite/index.d.ts +58 -0
- package/dist/expo-sqlite/index.js +104 -0
- package/dist/field-errors-BFOa7WhB.d.ts +99 -0
- package/dist/field-errors-VF1yb8yI.js +13 -0
- package/dist/framework-CONDyPYx.js +51 -0
- package/dist/framework-DuVb4Xa8.d.ts +25 -0
- package/dist/gateway-C1ssRHnf.d.ts +130 -0
- package/dist/graph-BGyLLFLX.js +265 -0
- package/dist/h3/index.d.ts +59 -0
- package/dist/h3/index.js +55 -0
- package/dist/h3/v1/index.d.ts +63 -0
- package/dist/h3/v1/index.js +49 -0
- package/dist/history-BZih6SFP.js +64 -0
- package/dist/hono/index.d.ts +56 -7
- package/dist/hono/index.js +97 -45
- package/dist/{react-C_s7kiNz.js → hooks-1BTCJsiw.js} +104 -133
- package/dist/http-Cv4T29xh.js +40 -0
- package/dist/http-DdYadv-o.js +126 -0
- package/dist/image-url-pIVlO6R1.js +41 -0
- package/dist/impersonation-DcqaTBHx.js +148 -0
- package/dist/inbox-i7E1ppo6.d.ts +384 -0
- package/dist/index-ACiNPN4Z.d.ts +74 -0
- package/dist/{index-DgILJRXj.d.ts → index-Bepi-Hgg.d.ts} +91 -28
- package/dist/index-BkWT_uBO.d.ts +51 -0
- package/dist/index-BpFYMsi0.d.ts +53 -0
- package/dist/{index-C7f8cOBM.d.ts → index-Bxih3l0u.d.ts} +2 -2
- package/dist/index-CaxrMD1A.d.ts +1 -0
- package/dist/index-ClmjK_o-.d.ts +55 -0
- package/dist/index-CtuakVRc.d.ts +267 -0
- package/dist/index-D48jVS-N.d.ts +246 -0
- package/dist/index-DDrYaUcq.d.ts +138 -0
- package/dist/index-DQh4VCc9.d.ts +98 -0
- package/dist/{live-D9W2M5aO.d.ts → index-DunNRdnT.d.ts} +136 -38
- package/dist/index.d.ts +65 -18
- package/dist/index.js +42 -14
- package/dist/invalidate-BBqhpxyB.d.ts +19 -0
- package/dist/json-CEnpTHlZ.js +16 -0
- package/dist/json-schema-CvER4kgT.js +502 -0
- package/dist/keys-4MnkWTTh.js +35 -0
- package/dist/kit-B5tid-q5.d.ts +60 -0
- package/dist/kit-Fm-Fv7q5.js +78 -0
- package/dist/knowledge-BXBbT0Tr.d.ts +176 -0
- package/dist/knowledge-CDmiMLew.js +255 -0
- package/dist/lint/index.d.ts +1 -1
- package/dist/lint/index.js +37 -0
- package/dist/list/index.d.ts +3 -2
- package/dist/list/index.js +1 -444
- package/dist/{index-fag8VJZ2.d.ts → list-query-BRt-zDF7.d.ts} +20 -12
- package/dist/list-query-gl808ac1.js +459 -0
- package/dist/{live-CdJBOc4f.js → live-CTAZQkwl.js} +99 -14
- package/dist/logger-DWVYUmwi.d.ts +18 -0
- package/dist/mcp/index.d.ts +38 -11
- package/dist/mcp/index.js +82 -46
- package/dist/mcp/sdk/index.d.ts +105 -0
- package/dist/mcp/sdk/index.js +126 -0
- package/dist/message-CaKlqdsX.js +219 -0
- package/dist/message-CfDoR0Y6.d.ts +94 -0
- package/dist/messages-B9-5887d.js +715 -0
- package/dist/messages-DYP0sUTe.d.ts +59 -0
- package/dist/{mfa-CusDgPaD.d.ts → mfa-Daak8ukG.d.ts} +8 -2
- package/dist/modules-DpPwzC7E.d.ts +192 -0
- package/dist/modules-dxrVK3ID.js +228 -0
- package/dist/nestjs/index.d.ts +68 -0
- package/dist/nestjs/index.js +164 -0
- package/dist/next/client/index.d.ts +35 -0
- package/dist/next/client/index.js +36 -0
- package/dist/next/image/index.js +3 -24
- package/dist/next/index.d.ts +150 -51
- package/dist/next/index.js +261 -107
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.js +210 -0
- package/dist/node-headers-AO06VhqH.d.ts +24 -0
- package/dist/node-headers-C71zNQHe.js +55 -0
- package/dist/nuxt/index.d.ts +60 -0
- package/dist/nuxt/index.js +64 -0
- package/dist/onboarding-BZGHYJks.d.ts +62 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +3 -3
- package/dist/orpc/index.d.ts +70 -11
- package/dist/orpc/index.js +73 -24
- package/dist/otel/index.d.ts +7 -7
- package/dist/otel/index.js +87 -57
- package/dist/outbox-Cq7pPYvl.d.ts +161 -0
- package/dist/{plugin-o144f322.d.ts → plugin-FGY8UhYm.d.ts} +30 -10
- package/dist/plugin-tfXW1-qO.js +45 -0
- package/dist/plugins/actor/index.d.ts +2 -2
- package/dist/plugins/actor/index.js +3 -3
- package/dist/plugins/rules/index.d.ts +11 -4
- package/dist/plugins/rules/index.js +40 -13
- package/dist/plugins/soft-delete/index.d.ts +5 -4
- package/dist/plugins/soft-delete/index.js +10 -5
- package/dist/plugins/tenant/index.d.ts +2 -2
- package/dist/plugins/tenant/index.js +18 -11
- package/dist/plugins/timestamps/index.d.ts +5 -3
- package/dist/plugins/timestamps/index.js +6 -4
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +10 -6
- package/dist/{pool-C9voM4Kw.d.ts → pool-CAu2nLrL.d.ts} +2 -2
- package/dist/postgres/index.d.ts +4 -4
- package/dist/postgres/index.js +1 -2
- package/dist/{postgrest-CruYKAvV.d.ts → postgrest-BLVdEnfe.d.ts} +13 -3
- package/dist/powersync/index.d.ts +43 -0
- package/dist/powersync/index.js +199 -0
- package/dist/powersync/react/index.d.ts +87 -0
- package/dist/powersync/react/index.js +108 -0
- package/dist/{problem-BW1KhzSG.js → problem-BreOl8b8.js} +14 -3
- package/dist/{problem-Blxe8S31.d.ts → problem-DD8LZFjc.d.ts} +21 -3
- package/dist/provider-BH5UWZJ6.d.ts +113 -0
- package/dist/provider-Di3sRutW.js +57 -0
- package/dist/query/index.d.ts +4 -2
- package/dist/query/index.js +4 -3
- package/dist/{query-CCbo44lG.js → query-BQ-NaZon.js} +141 -24
- package/dist/{queue-BYUCjr59.d.ts → queue-CLJurhdy.d.ts} +258 -83
- package/dist/react/index.d.ts +11 -7
- package/dist/react/index.js +7 -3
- package/dist/react/native/index.d.ts +116 -0
- package/dist/react/native/index.js +130 -0
- package/dist/react/server.d.ts +28 -10
- package/dist/react/server.js +22 -5
- package/dist/react/session.d.ts +2 -2
- package/dist/react-4ZqkP8da.js +79 -0
- package/dist/react-F80mF4-t.js +409 -0
- package/dist/react-router/index.d.ts +83 -0
- package/dist/react-router/index.js +89 -0
- package/dist/react-topic-seAWjL3_.js +38 -0
- package/dist/{read-set-DQFt1N_Y.js → read-set-DuKWC9Mj.js} +67 -25
- package/dist/realtime/index.d.ts +4 -5
- package/dist/realtime/index.js +144 -82
- package/dist/refusal-CeJWxmkH.js +61 -0
- package/dist/refusal-D1rgbzNq.d.ts +15 -0
- package/dist/replicas-CwrYp5Zj.js +94 -0
- package/dist/{view-Bq64ki8-.js → resolve-D_IcnvFd.js} +239 -289
- package/dist/{resolve-C15TJq77.d.ts → resolve-KW8nCeL6.d.ts} +29 -45
- package/dist/{resource-CRxxwkQq.js → resource-BsvzCMlX.js} +4 -4
- package/dist/{resource-D8iqWMr-.d.ts → resource-D6Uc7Sk7.d.ts} +5 -5
- package/dist/respond-BUgsXNgk.js +91 -0
- package/dist/respond-DoBijysA.d.ts +93 -0
- package/dist/result-D7y2fT87.d.ts +282 -0
- package/dist/{result-BKiliPY7.js → result-DzUbqgdW.js} +27 -2
- package/dist/run-state-CPo-1X2B.d.ts +12 -0
- package/dist/run-state-CgBKzYMW.js +13 -0
- package/dist/search-CUWCRYKL.js +9 -0
- package/dist/server/index.d.ts +143 -21
- package/dist/server/index.js +46 -14
- package/dist/{respond-D2OQl62-.d.ts → server-CeVeeXwz.d.ts} +225 -80
- package/dist/session-ChiXDZdZ.d.ts +72 -0
- package/dist/shared-CBxosKK3.js +75 -0
- package/dist/{shared-D1dRUrAg.js → shared-CxWmcyqB.js} +2 -1
- package/dist/shared-DDSjEGDR.d.ts +11 -0
- package/dist/sign-in-BCV5S2WZ.d.ts +49 -0
- package/dist/sign-in-Cb1mwv6X.js +86 -0
- package/dist/{signers-Cc6r98SW.d.ts → signers-4y62_VIi.d.ts} +13 -9
- package/dist/simplify-DDO0j0LI.js +83 -0
- package/dist/solid/index.d.ts +131 -0
- package/dist/solid/index.js +344 -0
- package/dist/solid-start/index.d.ts +75 -0
- package/dist/solid-start/index.js +58 -0
- package/dist/{spec-pins-C3oc-Tdp.js → spec-pins--j6mTok4.js} +8 -1
- package/dist/{spec-pins-CqorWDnO.d.ts → spec-pins-C89s6DyW.d.ts} +7 -0
- package/dist/sql/index.d.ts +330 -194
- package/dist/sql/index.js +34901 -6489
- package/dist/ssr/index.d.ts +6 -5
- package/dist/ssr/index.js +3 -3
- package/dist/{standard-D7r6dMUu.js → standard-BK0cqJ7y.js} +4 -3
- package/dist/{standard-J4YgMOLm.d.ts → standard-CYsm7xpT.d.ts} +1 -1
- package/dist/stats-D_AD9GGu.js +1123 -0
- package/dist/storage/index.d.ts +5 -5
- package/dist/storage/index.js +1027 -368
- package/dist/store-BdH5ruDx.js +66 -0
- package/dist/store-CGjgWu5n.d.ts +68 -0
- package/dist/streams/index.d.ts +76 -0
- package/dist/streams/index.js +77 -0
- package/dist/streams/redis/index.d.ts +52 -0
- package/dist/streams/redis/index.js +185 -0
- package/dist/stripe-BMj5LtsT.js +38 -0
- package/dist/stripe-D04h-QBT.d.ts +136 -0
- package/dist/{support-Dk6vdmEw.js → support-BivNqSpK.js} +35 -12
- package/dist/{support-view-Da5peKFg.d.ts → support-view-33Oi07TT.d.ts} +1 -1
- package/dist/svelte/index.d.ts +114 -0
- package/dist/svelte/index.js +344 -0
- package/dist/sveltekit/index.d.ts +116 -0
- package/dist/sveltekit/index.js +106 -0
- package/dist/tags-B99_U9Pp.js +39 -0
- package/dist/tags-Cx-LnIQP.d.ts +30 -0
- package/dist/tanstack-db/index.d.ts +71 -0
- package/dist/tanstack-db/index.js +72 -0
- package/dist/tanstack-start/index.d.ts +75 -0
- package/dist/tanstack-start/index.js +95 -0
- package/dist/tee-CoEppCER.js +101 -0
- package/dist/{template-TxrGI5jR.js → template--jDkl3UP.js} +34 -13
- package/dist/template-CwO38Do8.d.ts +6 -0
- package/dist/temporal-BJGitm-h.js +79 -0
- package/dist/{temporal-required-CAt4Dc9Z.js → temporal-required-BVvW7SYJ.js} +3 -2
- package/dist/tenant-I1TEZ_BX.js +27 -0
- package/dist/tenant-icPeuUBN.d.ts +17 -0
- package/dist/testing/index.d.ts +343 -37
- package/dist/testing/index.js +1186 -79
- package/dist/timing-CyTQPubY.js +92 -0
- package/dist/ttl-Hr34gRu8.js +12 -0
- package/dist/{types-Dnf_wRDz.d.ts → types-6BdhkmZe.d.ts} +111 -45
- package/dist/types-BI-0Sl2v.d.ts +359 -0
- package/dist/types-C05bxW-W.d.ts +220 -0
- package/dist/types-DGs5L0VN.d.ts +192 -0
- package/dist/types-TaiEeBGA.d.ts +156 -0
- package/dist/usage-CrvrL2vw.d.ts +177 -0
- package/dist/user-change-XzvgafU4.js +130 -0
- package/dist/vault-CSO7ss5C.d.ts +50 -0
- package/dist/vercel-connect/index.d.ts +47 -0
- package/dist/vercel-connect/index.js +119 -0
- package/dist/{verify-Ce49ETPz.js → verify-DL41hbvY.js} +85 -13
- package/dist/verify-DPuWngD4.d.ts +172 -0
- package/dist/{version-DM_Roffb.js → version-Bvv6aLjZ.js} +1 -1
- package/dist/view-BeFcRV6G.js +99 -0
- package/dist/{view-BEf0diC5.d.ts → view-DbxLNu8L.d.ts} +35 -32
- package/dist/vue/index.d.ts +141 -0
- package/dist/vue/index.js +362 -0
- package/dist/watch-DI34G86j.d.ts +60 -0
- package/dist/watch-Xuxrvxq-.js +122 -0
- package/dist/{wire-D2dT86vi.js → wire-DtZQ4sdE.js} +15 -5
- package/dist/workflow-builder-DJ1oHn8g.js +398 -0
- package/dist/workflow-sdk/builder/index.d.ts +85 -0
- package/dist/workflow-sdk/builder/index.js +250 -0
- package/dist/workflow-sdk/index.d.ts +14 -0
- package/dist/workflow-sdk/index.js +3 -0
- package/dist/workflow-sdk/world/index.d.ts +107 -0
- package/dist/workflow-sdk/world/index.js +702 -0
- package/dist/workflow-sdk-B3yLD6Br.d.ts +98 -0
- package/dist/workflow-sdk-C2WJ_G2O.js +173 -0
- package/dist/workflow-sdk-world-D-prir6O.js +1397 -0
- package/dist/workflows-B0j9WKPu.d.ts +185 -0
- package/dist/workflows-wu1WWBn7.js +216 -0
- package/package.json +470 -29
- package/schemas/ai-message-v1.json +258 -0
- package/schemas/config-v1.json +1074 -140
- package/schemas/doctor-report-v1.json +21 -1
- package/schemas/snapshot-v2.json +8 -0
- package/skills/better-supabase/SKILL.md +41 -12
- package/skills/better-supabase/references/frontend.md +96 -0
- package/skills/better-supabase/references/plugins.md +10 -6
- package/skills/better-supabase/references/troubleshooting.md +2 -1
- package/skills/better-supabase-api/SKILL.md +76 -22
- package/skills/better-supabase-api/references/adapters.md +207 -0
- package/skills/better-supabase-api/references/ai.md +125 -0
- package/skills/better-supabase-api/references/expo.md +65 -0
- package/skills/better-supabase-api/references/powersync.md +35 -0
- package/skills/better-supabase-api/references/streams-credentials.md +105 -0
- package/skills/better-supabase-auth/SKILL.md +62 -18
- package/skills/better-supabase-auth/references/authorization-providers.md +51 -0
- package/skills/better-supabase-testing/SKILL.md +3 -1
- package/dist/cli/config-dtoekM0v.js +0 -2
- package/dist/cli/gen-BJc9vdyu.js +0 -2
- package/dist/cli/gen-C2osRcr5.js +0 -2204
- package/dist/cli/read-sets-BfMQcKy8.js +0 -289
- package/dist/cli/sql-DzPbfngx.js +0 -348
- package/dist/entitlements-CTEBm7IB.js +0 -20
- package/dist/events-aIO7vsIk.d.ts +0 -230
- package/dist/executor-BunPOO-k.d.ts +0 -402
- package/dist/executor-XycESQPH.js +0 -94
- package/dist/impersonation-VfaGc4Q_.js +0 -41
- package/dist/index-BGdgBPdf.d.ts +0 -161
- package/dist/index-DOESKG27.d.ts +0 -100
- package/dist/index-DmXSxbLi.d.ts +0 -62
- package/dist/index-Dxn2a5cw.d.ts +0 -50
- package/dist/jobs/index.d.ts +0 -226
- package/dist/jobs/index.js +0 -1007
- package/dist/json-schema-m1fiwKgN.js +0 -208
- package/dist/kit-events-CTFDfSwb.js +0 -60
- package/dist/kit-transport-B82k1djG.d.ts +0 -33
- package/dist/kits-CGLLrNPd.js +0 -157
- package/dist/notifications/index.d.ts +0 -162
- package/dist/notifications/index.js +0 -264
- package/dist/orgs/index.d.ts +0 -132
- package/dist/orgs/index.js +0 -245
- package/dist/permdock-sql-C2Inc6Xc.d.ts +0 -29
- package/dist/permdock-sql-DgOfeixi.js +0 -51
- package/dist/plugin-DwARMuQp.js +0 -59
- package/dist/server-Csx2sRjg.js +0 -655
- package/dist/stats-Bqob3WqT.js +0 -592
- package/dist/support-DT-OhSxD.d.ts +0 -88
- package/dist/template-mbvlysOo.d.ts +0 -6
- package/dist/temporal-CVlIV2DZ.js +0 -40
- package/dist/user-change-CHFEUro1.js +0 -49
- package/dist/webhooks/index.d.ts +0 -285
- package/skills/better-supabase-auth/references/permdock.md +0 -100
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
import { t as DbException } from "../../db-exception-CnWcYPxL.js";
|
|
2
|
+
import { n as dbError } from "../../errors-B11Duf73.js";
|
|
3
|
+
import { s as nowInstant } from "../../temporal-BJGitm-h.js";
|
|
4
|
+
import { t as temporal } from "../../temporal-required-BVvW7SYJ.js";
|
|
5
|
+
import { a as ok, r as err, s as toDbError, t as AsyncResult } from "../../result-DzUbqgdW.js";
|
|
6
|
+
import { n as allTenantsContext } from "../../plugin-tfXW1-qO.js";
|
|
7
|
+
import { n as validate } from "../../standard-BK0cqJ7y.js";
|
|
8
|
+
import { a as problemResponse } from "../../problem-BreOl8b8.js";
|
|
9
|
+
import { o as tenantFrom, s as tenantPathsFor } from "../../claims-PqzCxaTD.js";
|
|
10
|
+
import { n as nextCronRun, t as assertCron } from "../../cron-DVPUg5k3.js";
|
|
11
|
+
import { A as workerId, C as run, E as sleep, c as errorText, d as isRecord, g as pageOf, i as blockCall, k as toInstant, n as applyTemporal, v as readBodyCapped, w as seconds } from "../../block-helpers-DWHxHsEQ.js";
|
|
12
|
+
import { d as verifyWebhook, l as verifySharedSecret } from "../../verify-DL41hbvY.js";
|
|
13
|
+
import { n as withContext, t as unwrap } from "../../envelope-DqnYGRw7.js";
|
|
14
|
+
//#region src/blocks/jobs/dev.ts
|
|
15
|
+
function devDrainSecret(env, name = "CRON_SECRET") {
|
|
16
|
+
const current = env[name];
|
|
17
|
+
if (current) return current;
|
|
18
|
+
const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32));
|
|
19
|
+
const secret = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20
|
+
env[name] = secret;
|
|
21
|
+
return secret;
|
|
22
|
+
}
|
|
23
|
+
function devDrain(options) {
|
|
24
|
+
if (!options.secret) throw new TypeError("devDrain needs the drain route's secret, such as devDrainSecret(process.env)");
|
|
25
|
+
const every = options.every ?? 6e4;
|
|
26
|
+
if (!Number.isFinite(every) || every <= 0) throw new TypeError("devDrain: every must be a positive number of ms");
|
|
27
|
+
const doFetch = options.fetch ?? fetch;
|
|
28
|
+
const report = options.onError ?? ((message) => {
|
|
29
|
+
console.warn(message);
|
|
30
|
+
});
|
|
31
|
+
let running = false;
|
|
32
|
+
const tick = async () => {
|
|
33
|
+
if (running || options.signal?.aborted) return;
|
|
34
|
+
running = true;
|
|
35
|
+
try {
|
|
36
|
+
const response = await doFetch(options.url, { headers: { authorization: `Bearer ${options.secret}` } });
|
|
37
|
+
await response.body?.cancel();
|
|
38
|
+
if (!response.ok) report(`[jobs] dev drain answered ${String(response.status)}`);
|
|
39
|
+
} catch (cause) {
|
|
40
|
+
report(`[jobs] dev drain failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
41
|
+
} finally {
|
|
42
|
+
running = false;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const timer = setInterval(() => {
|
|
46
|
+
tick();
|
|
47
|
+
}, every);
|
|
48
|
+
const stop = () => {
|
|
49
|
+
clearInterval(timer);
|
|
50
|
+
};
|
|
51
|
+
options.signal?.addEventListener("abort", stop, { once: true });
|
|
52
|
+
return {
|
|
53
|
+
tick,
|
|
54
|
+
stop
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/blocks/jobs/jobs.ts
|
|
59
|
+
async function sha256(text) {
|
|
60
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
61
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
62
|
+
}
|
|
63
|
+
const AUTH_COOKIE = /^sb-.+-auth-token(?:\.\d+)?$/;
|
|
64
|
+
/** A hash of the credentials the request carries, or `''` without any. */
|
|
65
|
+
async function callerScope(request) {
|
|
66
|
+
const authorization = request.headers.get("authorization");
|
|
67
|
+
const cookies = (request.headers.get("cookie") ?? "").split(";").map((pair) => pair.trim()).filter((pair) => AUTH_COOKIE.test(pair.split("=", 1)[0] ?? "")).sort();
|
|
68
|
+
const credential = authorization ?? cookies.join(";");
|
|
69
|
+
return credential === "" ? "" : `caller:${await sha256(credential)}`;
|
|
70
|
+
}
|
|
71
|
+
/** HTTP idempotency keys (IETF `Idempotency-Key` header) over the `idempotency` SQL module. */
|
|
72
|
+
function createIdempotency(sql, options = {}) {
|
|
73
|
+
applyTemporal(options);
|
|
74
|
+
const headerName = options.header ?? "idempotency-key";
|
|
75
|
+
const format = options.problem;
|
|
76
|
+
const ttl = seconds(options.ttl ?? "24 hours");
|
|
77
|
+
const lock = seconds(options.lock ?? "1 minute");
|
|
78
|
+
const begin = (key, fingerprint, scope = "") => run(async () => {
|
|
79
|
+
const [row] = await sql.queryRaw("select * from better_supabase.begin_idempotent($1, $2, $3, $4::interval, $5::interval)", [
|
|
80
|
+
scope,
|
|
81
|
+
key,
|
|
82
|
+
fingerprint,
|
|
83
|
+
ttl,
|
|
84
|
+
lock
|
|
85
|
+
]);
|
|
86
|
+
return {
|
|
87
|
+
state: row.state,
|
|
88
|
+
status: row.status_code,
|
|
89
|
+
body: row.response,
|
|
90
|
+
holder: row.holder ?? void 0
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
const complete = (key, holder, status, body, scope = "") => run(async () => {
|
|
94
|
+
const [row] = await sql.queryRaw("select better_supabase.complete_idempotent($1, $2, $3, $4, $5) as done", [
|
|
95
|
+
scope,
|
|
96
|
+
key,
|
|
97
|
+
holder,
|
|
98
|
+
status,
|
|
99
|
+
JSON.stringify(body)
|
|
100
|
+
]);
|
|
101
|
+
return row?.done === true;
|
|
102
|
+
});
|
|
103
|
+
const release = (key, holder, scope = "") => run(async () => {
|
|
104
|
+
const [row] = await sql.queryRaw("select better_supabase.release_idempotent($1, $2, $3) as done", [
|
|
105
|
+
scope,
|
|
106
|
+
key,
|
|
107
|
+
holder
|
|
108
|
+
]);
|
|
109
|
+
return row?.done === true;
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
begin,
|
|
113
|
+
complete,
|
|
114
|
+
release,
|
|
115
|
+
async handle(request, handler) {
|
|
116
|
+
const key = request.headers.get(headerName);
|
|
117
|
+
const instance = new URL(request.url).pathname;
|
|
118
|
+
if (!key) return options.required ? problemResponse(dbError("invalid_request", `The ${headerName} header is required`, { code: "IDEMPOTENCY_KEY_MISSING" }), {
|
|
119
|
+
instance,
|
|
120
|
+
format
|
|
121
|
+
}) : handler(request);
|
|
122
|
+
if (key.length > 255) return problemResponse(dbError("invalid_request", `The ${headerName} header is too long`, { code: "IDEMPOTENCY_KEY_INVALID" }), {
|
|
123
|
+
instance,
|
|
124
|
+
format
|
|
125
|
+
});
|
|
126
|
+
const scope = typeof options.scope === "function" ? options.scope(request) : options.scope ?? await callerScope(request);
|
|
127
|
+
const body = await request.clone().text();
|
|
128
|
+
const fingerprint = await sha256(`${request.method} ${instance}\n${body}`);
|
|
129
|
+
const started = await begin(key, fingerprint, scope);
|
|
130
|
+
if (!started.ok) return problemResponse(started.error, {
|
|
131
|
+
instance,
|
|
132
|
+
format
|
|
133
|
+
});
|
|
134
|
+
const { state, holder = "" } = started.data;
|
|
135
|
+
switch (state) {
|
|
136
|
+
case "replay": {
|
|
137
|
+
const stored = started.data.body;
|
|
138
|
+
return new Response(stored.body, {
|
|
139
|
+
status: started.data.status ?? 200,
|
|
140
|
+
headers: {
|
|
141
|
+
...stored.contentType ? { "content-type": stored.contentType } : {},
|
|
142
|
+
"idempotency-replayed": "true"
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
case "running": return problemResponse(dbError("conflict", "A request with this idempotency key is still running", { code: "IDEMPOTENCY_KEY_IN_USE" }), {
|
|
147
|
+
instance,
|
|
148
|
+
format,
|
|
149
|
+
headers: { "retry-after": "1" }
|
|
150
|
+
});
|
|
151
|
+
case "mismatch": return problemResponse(dbError("validation", "This idempotency key was used for a different request", {
|
|
152
|
+
code: "IDEMPOTENCY_KEY_REUSED",
|
|
153
|
+
issues: [{
|
|
154
|
+
message: "Use a new key for a new request",
|
|
155
|
+
path: [headerName]
|
|
156
|
+
}]
|
|
157
|
+
}), {
|
|
158
|
+
instance,
|
|
159
|
+
format
|
|
160
|
+
});
|
|
161
|
+
case "started": break;
|
|
162
|
+
default: throw new TypeError(`Unknown idempotency state ${String(state)}`);
|
|
163
|
+
}
|
|
164
|
+
let response;
|
|
165
|
+
try {
|
|
166
|
+
response = await handler(request);
|
|
167
|
+
} catch (cause) {
|
|
168
|
+
await release(key, holder, scope);
|
|
169
|
+
throw cause;
|
|
170
|
+
}
|
|
171
|
+
if (response.status >= 500) {
|
|
172
|
+
await release(key, holder, scope);
|
|
173
|
+
return response;
|
|
174
|
+
}
|
|
175
|
+
const stored = {
|
|
176
|
+
body: await response.clone().text(),
|
|
177
|
+
contentType: response.headers.get("content-type")
|
|
178
|
+
};
|
|
179
|
+
await complete(key, holder, response.status, stored, scope);
|
|
180
|
+
return response;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Runs `fn` while holding the lease on `key`, one holder at a time, and
|
|
186
|
+
* releases it afterwards, also when `fn` throws. `{ acquired: false }` while
|
|
187
|
+
* another holder has it. A lease that expires mid-run is taken by the next
|
|
188
|
+
* caller; call `lease.extend()` for long work.
|
|
189
|
+
*/
|
|
190
|
+
function withLease(sql, key, fn, options = {}) {
|
|
191
|
+
const scope = options.scope ?? "";
|
|
192
|
+
const ttl = options.seconds ?? 60;
|
|
193
|
+
return run(async () => {
|
|
194
|
+
const [row] = await sql.queryRaw("select better_supabase.acquire_lease($1, $2, $3) as holder", [
|
|
195
|
+
key,
|
|
196
|
+
ttl,
|
|
197
|
+
scope
|
|
198
|
+
]);
|
|
199
|
+
const holder = row?.holder;
|
|
200
|
+
if (!holder) return { acquired: false };
|
|
201
|
+
const lease = {
|
|
202
|
+
holder,
|
|
203
|
+
extend: (next = ttl) => run(async () => {
|
|
204
|
+
const [extended] = await sql.queryRaw("select better_supabase.extend_lease($1, $2, $3, $4) as done", [
|
|
205
|
+
key,
|
|
206
|
+
holder,
|
|
207
|
+
next,
|
|
208
|
+
scope
|
|
209
|
+
]);
|
|
210
|
+
return extended?.done === true;
|
|
211
|
+
})
|
|
212
|
+
};
|
|
213
|
+
try {
|
|
214
|
+
return {
|
|
215
|
+
acquired: true,
|
|
216
|
+
value: await fn(lease)
|
|
217
|
+
};
|
|
218
|
+
} finally {
|
|
219
|
+
await sql.queryRaw("select better_supabase.release_lease($1, $2, $3)", [
|
|
220
|
+
key,
|
|
221
|
+
holder,
|
|
222
|
+
scope
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function defaultType(payload) {
|
|
228
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
229
|
+
const type = payload.type;
|
|
230
|
+
return typeof type === "string" ? type : null;
|
|
231
|
+
}
|
|
232
|
+
/** Store-then-process webhooks: acknowledge fast, process with retries, never twice. */
|
|
233
|
+
function createWebhookInbox(sql, options) {
|
|
234
|
+
applyTemporal(options);
|
|
235
|
+
const worker = options.worker ?? workerId();
|
|
236
|
+
const format = options.problem;
|
|
237
|
+
const maxAttempts = options.maxAttempts ?? 8;
|
|
238
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) throw new TypeError(`createWebhookInbox maxAttempts must be a positive integer, not ${String(maxAttempts)}`);
|
|
239
|
+
const maxBodyBytes = options.maxBodyBytes ?? 1048576;
|
|
240
|
+
if (!Number.isInteger(maxBodyBytes) || maxBodyBytes < 1) throw new TypeError(`createWebhookInbox maxBodyBytes must be a positive integer, not ${String(maxBodyBytes)}`);
|
|
241
|
+
const verified = async (request, body) => {
|
|
242
|
+
if (options.verify) return options.verify(request, body);
|
|
243
|
+
const result = await verifyWebhook({
|
|
244
|
+
headers: request.headers,
|
|
245
|
+
body
|
|
246
|
+
}, options.secrets ?? []);
|
|
247
|
+
return result.ok ? ok({
|
|
248
|
+
id: result.data.id,
|
|
249
|
+
payload: result.data.payload
|
|
250
|
+
}) : result;
|
|
251
|
+
};
|
|
252
|
+
const save = (event) => run(async () => {
|
|
253
|
+
const [row] = await sql.queryRaw("select * from better_supabase.receive_webhook($1, $2, $3, $4, $5, $6, $7)", [
|
|
254
|
+
options.source,
|
|
255
|
+
event.id,
|
|
256
|
+
event.type === void 0 ? (options.typeOf ?? defaultType)(event.payload) : event.type,
|
|
257
|
+
JSON.stringify(event.payload),
|
|
258
|
+
JSON.stringify(event.headers ?? {}),
|
|
259
|
+
(event.tenant === void 0 ? options.tenantOf?.(event.payload) : event.tenant) ?? null,
|
|
260
|
+
maxAttempts
|
|
261
|
+
]);
|
|
262
|
+
return {
|
|
263
|
+
id: Number(row.id),
|
|
264
|
+
duplicate: row.duplicate
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
return {
|
|
268
|
+
store: save,
|
|
269
|
+
list: (listOptions) => run(async () => {
|
|
270
|
+
return (await sql.queryRaw("select * from better_supabase.list_webhooks($1, $2, $3, $4)", [
|
|
271
|
+
listOptions.tenant,
|
|
272
|
+
options.source,
|
|
273
|
+
listOptions.status ?? null,
|
|
274
|
+
listOptions.limit ?? 100
|
|
275
|
+
])).map((row) => ({
|
|
276
|
+
id: Number(row.id),
|
|
277
|
+
messageId: row.message_id,
|
|
278
|
+
type: row.event_type,
|
|
279
|
+
status: row.status ?? "pending",
|
|
280
|
+
attempts: row.attempts,
|
|
281
|
+
maxAttempts: row.max_attempts ?? maxAttempts,
|
|
282
|
+
lastError: row.last_error ?? null,
|
|
283
|
+
tenant: row.tenant ?? null,
|
|
284
|
+
receivedAt: toInstant(row.received_at),
|
|
285
|
+
processedAt: row.processed_at === null || row.processed_at === void 0 ? null : toInstant(row.processed_at)
|
|
286
|
+
}));
|
|
287
|
+
}),
|
|
288
|
+
purge: (purgeOptions = {}) => run(async () => {
|
|
289
|
+
const [row] = await sql.queryRaw("select better_supabase.purge_webhooks($1::interval, $2, $3, $4) as purged", [
|
|
290
|
+
seconds(purgeOptions.olderThan ?? "30 days"),
|
|
291
|
+
purgeOptions.includeDead ?? false,
|
|
292
|
+
purgeOptions.batch ?? 1e4,
|
|
293
|
+
purgeOptions.tenant ?? null
|
|
294
|
+
]);
|
|
295
|
+
return row?.purged ?? 0;
|
|
296
|
+
}),
|
|
297
|
+
async receive(request) {
|
|
298
|
+
const instance = new URL(request.url).pathname;
|
|
299
|
+
if (request.method !== "POST") return new Response(null, {
|
|
300
|
+
status: 405,
|
|
301
|
+
headers: { allow: "POST" }
|
|
302
|
+
});
|
|
303
|
+
if (!options.verify && !options.secrets) throw new TypeError(`The inbox for "${options.source}" has no \`secrets\` or \`verify\`, so it only stores events through \`store\``);
|
|
304
|
+
const body = await readBodyCapped(request.clone(), maxBodyBytes);
|
|
305
|
+
if (body === void 0) return problemResponse({
|
|
306
|
+
...dbError("invalid_input", "The body is too large", { code: "WEBHOOK_TOO_LARGE" }),
|
|
307
|
+
status: 413
|
|
308
|
+
}, {
|
|
309
|
+
instance,
|
|
310
|
+
format
|
|
311
|
+
});
|
|
312
|
+
const message = await verified(request, body);
|
|
313
|
+
if (!message.ok) return problemResponse(message.error, {
|
|
314
|
+
instance,
|
|
315
|
+
format
|
|
316
|
+
});
|
|
317
|
+
const headers = Object.fromEntries((options.keepHeaders ?? []).flatMap((name) => {
|
|
318
|
+
const value = request.headers.get(name);
|
|
319
|
+
return value === null ? [] : [[name.toLowerCase(), value]];
|
|
320
|
+
}));
|
|
321
|
+
const stored = await save({
|
|
322
|
+
id: message.data.id,
|
|
323
|
+
payload: message.data.payload,
|
|
324
|
+
headers,
|
|
325
|
+
...message.data.tenant === void 0 ? {} : { tenant: message.data.tenant }
|
|
326
|
+
});
|
|
327
|
+
if (!stored.ok) return problemResponse(stored.error, {
|
|
328
|
+
instance,
|
|
329
|
+
format
|
|
330
|
+
});
|
|
331
|
+
return Response.json(stored.data, { status: stored.data.duplicate ? 200 : 202 });
|
|
332
|
+
},
|
|
333
|
+
async process(handler, processOptions = {}) {
|
|
334
|
+
let succeeded = 0;
|
|
335
|
+
let failed = 0;
|
|
336
|
+
const lease = seconds(processOptions.lease ?? 300);
|
|
337
|
+
const deadline = processOptions.budgetMs === void 0 ? void 0 : Date.now() + processOptions.budgetMs;
|
|
338
|
+
for (;;) {
|
|
339
|
+
if (deadline !== void 0 && Date.now() >= deadline) return {
|
|
340
|
+
succeeded,
|
|
341
|
+
failed
|
|
342
|
+
};
|
|
343
|
+
const rows = await sql.queryRaw("select * from better_supabase.claim_webhooks($1, $2, $3, $4::interval)", [
|
|
344
|
+
options.source,
|
|
345
|
+
worker,
|
|
346
|
+
processOptions.batch ?? 10,
|
|
347
|
+
lease
|
|
348
|
+
]);
|
|
349
|
+
if (rows.length === 0) return {
|
|
350
|
+
succeeded,
|
|
351
|
+
failed
|
|
352
|
+
};
|
|
353
|
+
for (const row of rows) {
|
|
354
|
+
const id = Number(row.id);
|
|
355
|
+
const attempt = row.attempts;
|
|
356
|
+
const extend = async () => {
|
|
357
|
+
const [held] = await sql.queryRaw("select better_supabase.extend_webhook($1, $2, $3, $4::interval) as lease", [
|
|
358
|
+
id,
|
|
359
|
+
worker,
|
|
360
|
+
attempt,
|
|
361
|
+
lease
|
|
362
|
+
]);
|
|
363
|
+
return held?.lease ?? void 0;
|
|
364
|
+
};
|
|
365
|
+
const leaseSeconds = await extend();
|
|
366
|
+
if (leaseSeconds === void 0) continue;
|
|
367
|
+
const message = {
|
|
368
|
+
id,
|
|
369
|
+
source: row.source,
|
|
370
|
+
messageId: row.message_id,
|
|
371
|
+
type: row.event_type,
|
|
372
|
+
payload: row.payload,
|
|
373
|
+
headers: row.headers,
|
|
374
|
+
attempts: row.attempts,
|
|
375
|
+
maxAttempts: row.max_attempts ?? maxAttempts,
|
|
376
|
+
receivedAt: toInstant(row.received_at),
|
|
377
|
+
tenant: row.tenant ?? null,
|
|
378
|
+
progress: row.checkpoint ?? {},
|
|
379
|
+
async checkpoint(fields) {
|
|
380
|
+
const [saved] = await sql.queryRaw("select better_supabase.checkpoint_webhook($1, $2, $3, $4) as saved", [
|
|
381
|
+
id,
|
|
382
|
+
worker,
|
|
383
|
+
JSON.stringify(fields),
|
|
384
|
+
attempt
|
|
385
|
+
]);
|
|
386
|
+
return saved?.saved ?? false;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
const heartbeat = setInterval(() => void extend().catch(() => void 0), Math.max(1e3, leaseSeconds * 500));
|
|
390
|
+
try {
|
|
391
|
+
const outcome = await handler(message);
|
|
392
|
+
if (typeof outcome === "object" && outcome !== null && "ok" in outcome && outcome.ok === false) throw new Error(errorText(outcome.error ?? "The handler failed"));
|
|
393
|
+
const [done] = await sql.queryRaw("select better_supabase.complete_webhook($1, $2, $3) as done", [
|
|
394
|
+
message.id,
|
|
395
|
+
worker,
|
|
396
|
+
attempt
|
|
397
|
+
]);
|
|
398
|
+
if (done?.done !== false) succeeded += 1;
|
|
399
|
+
} catch (cause) {
|
|
400
|
+
await sql.queryRaw("select better_supabase.fail_webhook($1, $2, $3, null, $4)", [
|
|
401
|
+
message.id,
|
|
402
|
+
worker,
|
|
403
|
+
errorText(cause),
|
|
404
|
+
attempt
|
|
405
|
+
]);
|
|
406
|
+
failed += 1;
|
|
407
|
+
} finally {
|
|
408
|
+
clearInterval(heartbeat);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/blocks/jobs/backends.ts
|
|
417
|
+
const instantOrNull = (value) => value === null ? null : toInstant(value);
|
|
418
|
+
/** Schedule times are stored to the millisecond, the precision `Date` reads back. */
|
|
419
|
+
const millis = (instant) => instant.round({
|
|
420
|
+
smallestUnit: "millisecond",
|
|
421
|
+
roundingMode: "floor"
|
|
422
|
+
});
|
|
423
|
+
/** The first run of a schedule written without one, or undefined for a cron the drain can't read. */
|
|
424
|
+
function firstRun(row) {
|
|
425
|
+
try {
|
|
426
|
+
return millis(nextCronRun(row.schedule, row.timezone, row.first_after ? toInstant(row.first_after) : nowInstant()));
|
|
427
|
+
} catch {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The `jobs` SQL module over a service SQL connection. The same
|
|
433
|
+
* functions back both `sql.modules.jobs.options.backend` values (pgmq and table)
|
|
434
|
+
* and both schedulers.
|
|
435
|
+
*/
|
|
436
|
+
function sqlQueueBackend(sql) {
|
|
437
|
+
return {
|
|
438
|
+
apiVersion: 1,
|
|
439
|
+
name: "sql",
|
|
440
|
+
leases: true,
|
|
441
|
+
async send(queue, payload, delay, maxAttempts, dedupeKey, dedupe) {
|
|
442
|
+
const [row] = await sql.queryRaw("select better_supabase.enqueue_job($1, $2, $3, $4, $5, $6) as id", [
|
|
443
|
+
queue,
|
|
444
|
+
JSON.stringify(payload ?? {}),
|
|
445
|
+
delay,
|
|
446
|
+
maxAttempts,
|
|
447
|
+
dedupeKey ?? null,
|
|
448
|
+
dedupe !== "waiting"
|
|
449
|
+
]);
|
|
450
|
+
return Number(row.id);
|
|
451
|
+
},
|
|
452
|
+
read: (queue, lease, batch) => sql.queryRaw("select * from better_supabase.claim_jobs($1, $2, $3)", [
|
|
453
|
+
queue,
|
|
454
|
+
lease,
|
|
455
|
+
batch
|
|
456
|
+
]),
|
|
457
|
+
async complete(job) {
|
|
458
|
+
const [row] = await sql.queryRaw("select better_supabase.complete_job($1, $2, $3) as done", [
|
|
459
|
+
job.queue,
|
|
460
|
+
job.id,
|
|
461
|
+
job.attempts
|
|
462
|
+
]);
|
|
463
|
+
return row?.done ?? false;
|
|
464
|
+
},
|
|
465
|
+
async fail(job, error, retryIn) {
|
|
466
|
+
const [row] = await sql.queryRaw("select better_supabase.fail_job($1, $2, $3, $4, $5) as status", [
|
|
467
|
+
job.queue,
|
|
468
|
+
job.id,
|
|
469
|
+
job.attempts,
|
|
470
|
+
error,
|
|
471
|
+
retryIn ?? null
|
|
472
|
+
]);
|
|
473
|
+
return row?.status ?? null;
|
|
474
|
+
},
|
|
475
|
+
async extend(job, lease) {
|
|
476
|
+
const [row] = await sql.queryRaw("select better_supabase.extend_job_lease($1, $2, $3, $4) as extended", [
|
|
477
|
+
job.queue,
|
|
478
|
+
job.id,
|
|
479
|
+
job.attempts,
|
|
480
|
+
lease
|
|
481
|
+
]);
|
|
482
|
+
return row?.extended ?? false;
|
|
483
|
+
},
|
|
484
|
+
async replay(queue, id) {
|
|
485
|
+
const [row] = await sql.queryRaw("select better_supabase.replay_dead_job($1, $2) as id", [queue, id]);
|
|
486
|
+
return row?.id === null || row?.id === void 0 ? null : Number(row.id);
|
|
487
|
+
},
|
|
488
|
+
async stats(queue) {
|
|
489
|
+
const [row] = await sql.queryRaw("select * from better_supabase.job_queue_stats($1)", [queue]);
|
|
490
|
+
return {
|
|
491
|
+
ready: Number(row?.ready ?? 0),
|
|
492
|
+
inFlight: Number(row?.in_flight ?? 0),
|
|
493
|
+
delayed: Number(row?.delayed ?? 0),
|
|
494
|
+
dead: Number(row?.dead ?? 0),
|
|
495
|
+
oldestAgeSeconds: row?.oldest_age_seconds === null || row?.oldest_age_seconds === void 0 ? null : Number(row.oldest_age_seconds)
|
|
496
|
+
};
|
|
497
|
+
},
|
|
498
|
+
listDead: (queue, limit, before) => sql.queryRaw("select * from better_supabase.list_dead_jobs($1, $2, $3)", [
|
|
499
|
+
queue,
|
|
500
|
+
limit,
|
|
501
|
+
before ?? null
|
|
502
|
+
]),
|
|
503
|
+
async retryDead(queue, ids, limit) {
|
|
504
|
+
const [row] = await sql.queryRaw("select better_supabase.retry_dead_jobs($1, $2, $3) as retried", [
|
|
505
|
+
queue,
|
|
506
|
+
ids === void 0 ? null : [...ids],
|
|
507
|
+
limit
|
|
508
|
+
]);
|
|
509
|
+
return row?.retried ?? 0;
|
|
510
|
+
},
|
|
511
|
+
async schedule(name, cron, queue, payload, timeZone, nextRun, tenant) {
|
|
512
|
+
await sql.queryRaw("select better_supabase.schedule_job($1, $2, $3, $4, $5, $6, $7)", [
|
|
513
|
+
name,
|
|
514
|
+
cron,
|
|
515
|
+
queue,
|
|
516
|
+
JSON.stringify(payload ?? {}),
|
|
517
|
+
timeZone,
|
|
518
|
+
nextRun.toString(),
|
|
519
|
+
tenant ?? null
|
|
520
|
+
]);
|
|
521
|
+
},
|
|
522
|
+
async listSchedules(filter) {
|
|
523
|
+
return (await sql.queryRaw("select * from better_supabase.list_schedules($1, $2)", [filter.prefix ?? null, filter.tenant ?? null])).map((row) => ({
|
|
524
|
+
name: row.job_name,
|
|
525
|
+
cron: row.schedule,
|
|
526
|
+
timeZone: row.timezone,
|
|
527
|
+
queue: row.queue,
|
|
528
|
+
tenant: row.tenant,
|
|
529
|
+
nextRun: instantOrNull(row.next_run),
|
|
530
|
+
lastRun: instantOrNull(row.last_run),
|
|
531
|
+
leasedUntil: instantOrNull(row.locked_until),
|
|
532
|
+
createdAt: instantOrNull(row.created_at)
|
|
533
|
+
}));
|
|
534
|
+
},
|
|
535
|
+
async unscheduleTenant(tenant) {
|
|
536
|
+
const [row] = await sql.queryRaw("select better_supabase.unschedule_tenant($1) as removed", [tenant]);
|
|
537
|
+
return row?.removed ?? 0;
|
|
538
|
+
},
|
|
539
|
+
async unschedule(name) {
|
|
540
|
+
const [row] = await sql.queryRaw("select better_supabase.unschedule_job($1) as done", [name]);
|
|
541
|
+
return row?.done ?? false;
|
|
542
|
+
},
|
|
543
|
+
async dueSchedules(lease, batch) {
|
|
544
|
+
const rows = await sql.queryRaw("select * from better_supabase.claim_due_schedules($1, $2)", [lease, batch]);
|
|
545
|
+
const due = [];
|
|
546
|
+
for (const row of rows) {
|
|
547
|
+
const schedule = {
|
|
548
|
+
name: row.job_name,
|
|
549
|
+
cron: row.schedule,
|
|
550
|
+
timeZone: row.timezone,
|
|
551
|
+
queue: row.queue,
|
|
552
|
+
payload: row.payload
|
|
553
|
+
};
|
|
554
|
+
if (row.next_run !== null) {
|
|
555
|
+
due.push({
|
|
556
|
+
...schedule,
|
|
557
|
+
nextRun: toInstant(row.next_run)
|
|
558
|
+
});
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const first = firstRun(row);
|
|
562
|
+
if (first === void 0) continue;
|
|
563
|
+
if (temporal().Instant.compare(first, nowInstant()) <= 0) due.push({
|
|
564
|
+
...schedule,
|
|
565
|
+
nextRun: first
|
|
566
|
+
});
|
|
567
|
+
else await sql.queryRaw("select better_supabase.advance_schedule($1, null, $2)", [row.job_name, first.toString()]);
|
|
568
|
+
}
|
|
569
|
+
return due;
|
|
570
|
+
},
|
|
571
|
+
async advanceSchedule(name, ran, next) {
|
|
572
|
+
const [row] = await sql.queryRaw("select better_supabase.advance_schedule($1, $2, $3) as advanced", [
|
|
573
|
+
name,
|
|
574
|
+
ran.toString(),
|
|
575
|
+
next.toString()
|
|
576
|
+
]);
|
|
577
|
+
return row?.advanced ?? false;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
/** Throws for a feature the transport doesn't support. */
|
|
582
|
+
function sqlOnly(feature) {
|
|
583
|
+
throw new DbException(dbError("invalid_request", `${feature} needs a SQL connection; pgmq_public (PostgREST) does not support it`));
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Supabase's `pgmq_public` RPCs (Integrations, Queues, "Expose Queues via
|
|
587
|
+
* PostgREST"). No lease check on complete, no retry delay, no deduplication
|
|
588
|
+
* and no schedules: a failed job reappears when its lease ends.
|
|
589
|
+
*/
|
|
590
|
+
function pgmqPublicBackend(client) {
|
|
591
|
+
const untyped = client;
|
|
592
|
+
const call = async (fn, args) => {
|
|
593
|
+
const { data, error } = await untyped.schema("pgmq_public").rpc(fn, args);
|
|
594
|
+
if (error) {
|
|
595
|
+
const message = typeof error === "object" && "message" in error ? String(error.message) : String(error);
|
|
596
|
+
throw new Error(`pgmq_public.${fn}: ${message}`, { cause: error });
|
|
597
|
+
}
|
|
598
|
+
return data;
|
|
599
|
+
};
|
|
600
|
+
const archive = (job) => call("archive", {
|
|
601
|
+
queue_name: job.queue,
|
|
602
|
+
message_id: job.id
|
|
603
|
+
});
|
|
604
|
+
return {
|
|
605
|
+
apiVersion: 1,
|
|
606
|
+
name: "pgmq_public",
|
|
607
|
+
leases: false,
|
|
608
|
+
async send(queue, payload, delay, maxAttempts, dedupeKey) {
|
|
609
|
+
if (dedupeKey !== void 0) sqlOnly("dedupeKey");
|
|
610
|
+
const ids = await call("send", {
|
|
611
|
+
queue_name: queue,
|
|
612
|
+
message: {
|
|
613
|
+
payload: payload ?? {},
|
|
614
|
+
max_attempts: maxAttempts
|
|
615
|
+
},
|
|
616
|
+
sleep_seconds: delay
|
|
617
|
+
});
|
|
618
|
+
return Number(ids[0]);
|
|
619
|
+
},
|
|
620
|
+
async read(queue, lease, batch) {
|
|
621
|
+
const rows = await call("read", {
|
|
622
|
+
queue_name: queue,
|
|
623
|
+
sleep_seconds: lease,
|
|
624
|
+
n: batch
|
|
625
|
+
});
|
|
626
|
+
const live = [];
|
|
627
|
+
for (const row of rows) if (row.read_ct > (row.message?.max_attempts ?? 5)) await call("archive", {
|
|
628
|
+
queue_name: queue,
|
|
629
|
+
message_id: row.msg_id
|
|
630
|
+
});
|
|
631
|
+
else live.push(row);
|
|
632
|
+
return live.map((row) => ({
|
|
633
|
+
id: row.msg_id,
|
|
634
|
+
attempts: row.read_ct,
|
|
635
|
+
enqueued_at: row.enqueued_at,
|
|
636
|
+
visible_until: row.vt,
|
|
637
|
+
message: row.message
|
|
638
|
+
}));
|
|
639
|
+
},
|
|
640
|
+
complete: archive,
|
|
641
|
+
async fail(job) {
|
|
642
|
+
if (job.attempts < job.maxAttempts) return "queued";
|
|
643
|
+
await archive(job);
|
|
644
|
+
return "dead";
|
|
645
|
+
},
|
|
646
|
+
extend: () => Promise.resolve(false),
|
|
647
|
+
schedule: () => sqlOnly("schedule"),
|
|
648
|
+
unschedule: () => sqlOnly("unschedule")
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region src/blocks/jobs/queue.ts
|
|
653
|
+
/** Transient claim errors in a row a lane retries, with backoff, before the loop throws. */
|
|
654
|
+
const CLAIM_RETRIES = 3;
|
|
655
|
+
const TRANSIENT = /* @__PURE__ */ new Set([
|
|
656
|
+
"network",
|
|
657
|
+
"timeout",
|
|
658
|
+
"serialization",
|
|
659
|
+
"rate_limited"
|
|
660
|
+
]);
|
|
661
|
+
function toJob(queue, row) {
|
|
662
|
+
const message = row.message ?? {};
|
|
663
|
+
const { payload, context } = unwrap(message.payload);
|
|
664
|
+
return {
|
|
665
|
+
id: Number(row.id),
|
|
666
|
+
queue,
|
|
667
|
+
payload,
|
|
668
|
+
context,
|
|
669
|
+
attempts: row.attempts,
|
|
670
|
+
maxAttempts: message.max_attempts ?? 5,
|
|
671
|
+
enqueuedAt: toInstant(row.enqueued_at),
|
|
672
|
+
visibleUntil: toInstant(row.visible_until),
|
|
673
|
+
lastError: message.last_error ?? null
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
const QUEUE_NAME = /^[a-z_][a-z0-9_]{0,46}$/;
|
|
677
|
+
function isBackend(source) {
|
|
678
|
+
return "apiVersion" in source;
|
|
679
|
+
}
|
|
680
|
+
function isSqlClient(source) {
|
|
681
|
+
return typeof source.queryRaw === "function";
|
|
682
|
+
}
|
|
683
|
+
function backendOf(source) {
|
|
684
|
+
if (isBackend(source)) return source;
|
|
685
|
+
return isSqlClient(source) ? sqlQueueBackend(source) : pgmqPublicBackend(source);
|
|
686
|
+
}
|
|
687
|
+
const later = (a, b) => temporal().Instant.compare(a, b) >= 0 ? a : b;
|
|
688
|
+
/**
|
|
689
|
+
* Typed jobs on Supabase Queues (pgmq), over the `jobs` SQL module.
|
|
690
|
+
*
|
|
691
|
+
* Pass a service SQL connection (`postgres.admin`, `ctx.sql`) for the full
|
|
692
|
+
* feature set, a service-role Supabase client to go through the
|
|
693
|
+
* `pgmq_public` RPCs where no direct connection exists (edge functions), or
|
|
694
|
+
* any `QueueBackend`. Queue names are lowercase letters, digits and
|
|
695
|
+
* underscores.
|
|
696
|
+
*/
|
|
697
|
+
function createJobs(source, queues, options = {}) {
|
|
698
|
+
applyTemporal(options);
|
|
699
|
+
for (const name of Object.keys(queues)) if (!QUEUE_NAME.test(name)) throw new TypeError(`Queue "${name}": pgmq queue names are lowercase letters, digits and underscores (at most 47)`);
|
|
700
|
+
const transport = backendOf(source);
|
|
701
|
+
const schemaOf = (queue) => {
|
|
702
|
+
const schema = queues[queue];
|
|
703
|
+
if (!schema) throw new TypeError(`Unknown queue "${queue}". Queues: ${Object.keys(queues).join(", ")}`);
|
|
704
|
+
return schema;
|
|
705
|
+
};
|
|
706
|
+
const complete = (job) => run(() => transport.complete(job));
|
|
707
|
+
const fail = (job, error, failOptions = {}) => run(() => transport.fail(job, errorText(error), failOptions.retryIn));
|
|
708
|
+
const claim = (queue, claimOptions = {}) => run(async () => {
|
|
709
|
+
schemaOf(queue);
|
|
710
|
+
return (await transport.read(queue, claimOptions.lease ?? 300, claimOptions.batch ?? 1)).map((row) => toJob(queue, row));
|
|
711
|
+
});
|
|
712
|
+
/**
|
|
713
|
+
* Extends the lease of every job in `held` at half the lease, so jobs
|
|
714
|
+
* waiting their turn in a claimed batch don't expire before they run.
|
|
715
|
+
*/
|
|
716
|
+
const heartbeat = (held, lease) => transport.leases ? setInterval(() => {
|
|
717
|
+
for (const job of held) run(() => transport.extend(job, lease));
|
|
718
|
+
}, Math.max(1e3, lease * 1e3 / 2)) : void 0;
|
|
719
|
+
const handle = async (job, handler, workOptions) => {
|
|
720
|
+
const controller = new AbortController();
|
|
721
|
+
try {
|
|
722
|
+
const payload = await validate(schemaOf(job.queue), job.payload, "payload");
|
|
723
|
+
if (!payload.ok) {
|
|
724
|
+
await fail(job, payload.error.message);
|
|
725
|
+
workOptions.onError?.(payload.error, job);
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
const context = workOptions.allTenants && job.context.tenant === void 0 ? allTenantsContext(job.context) : job.context;
|
|
729
|
+
const outcome = await handler(payload.data, {
|
|
730
|
+
...job,
|
|
731
|
+
payload: payload.data,
|
|
732
|
+
context
|
|
733
|
+
}, controller.signal);
|
|
734
|
+
if (typeof outcome === "object" && outcome !== null && "ok" in outcome && outcome.ok === false && "error" in outcome) throw outcome.error;
|
|
735
|
+
await complete(job);
|
|
736
|
+
return true;
|
|
737
|
+
} catch (cause) {
|
|
738
|
+
controller.abort();
|
|
739
|
+
await fail(job, cause);
|
|
740
|
+
workOptions.onError?.(typeof cause === "object" && cause !== null && "kind" in cause ? cause : toDbError(cause), job);
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
const loop = async (queue, handler, workOptions, forever, deadline) => {
|
|
745
|
+
const concurrency = Math.max(1, workOptions.concurrency ?? 1);
|
|
746
|
+
const lease = workOptions.lease ?? 300;
|
|
747
|
+
const firstWait = workOptions.pollInterval ?? 1e3;
|
|
748
|
+
const maxWait = Math.max(firstWait, workOptions.maxPollInterval ?? 3e4);
|
|
749
|
+
let succeeded = 0;
|
|
750
|
+
let failed = 0;
|
|
751
|
+
const stop = new AbortController();
|
|
752
|
+
const signal = workOptions.signal ? AbortSignal.any([workOptions.signal, stop.signal]) : stop.signal;
|
|
753
|
+
const lanes = Array.from({ length: concurrency }, async () => {
|
|
754
|
+
let wait = firstWait;
|
|
755
|
+
let misses = 0;
|
|
756
|
+
while (!signal.aborted) {
|
|
757
|
+
if (deadline !== void 0 && Date.now() >= deadline) return;
|
|
758
|
+
const claimed = await claim(queue, {
|
|
759
|
+
...workOptions,
|
|
760
|
+
batch: workOptions.batch ?? 1
|
|
761
|
+
});
|
|
762
|
+
if (!claimed.ok) {
|
|
763
|
+
misses += 1;
|
|
764
|
+
if (misses > CLAIM_RETRIES || !TRANSIENT.has(claimed.error.kind)) {
|
|
765
|
+
stop.abort();
|
|
766
|
+
throw new TypeError(claimed.error.message, { cause: claimed.error });
|
|
767
|
+
}
|
|
768
|
+
const left = deadline === void 0 ? Infinity : deadline - Date.now();
|
|
769
|
+
await sleep(Math.min(firstWait * 2 ** (misses - 1), left), signal);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
misses = 0;
|
|
773
|
+
if (claimed.data.length === 0) {
|
|
774
|
+
if (!forever) return;
|
|
775
|
+
await sleep(wait, signal);
|
|
776
|
+
wait = Math.min(maxWait, wait * 2);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
wait = firstWait;
|
|
780
|
+
const held = new Set(claimed.data);
|
|
781
|
+
const timer = heartbeat(held, lease);
|
|
782
|
+
try {
|
|
783
|
+
for (const job of claimed.data) {
|
|
784
|
+
if (await handle(job, handler, workOptions)) succeeded += 1;
|
|
785
|
+
else failed += 1;
|
|
786
|
+
held.delete(job);
|
|
787
|
+
}
|
|
788
|
+
} finally {
|
|
789
|
+
clearInterval(timer);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
const settled = await Promise.allSettled(lanes);
|
|
794
|
+
for (const lane of settled) if (lane.status === "rejected") throw lane.reason;
|
|
795
|
+
return {
|
|
796
|
+
succeeded,
|
|
797
|
+
failed
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
const runSchedules = async (scheduleOptions) => {
|
|
801
|
+
if (!transport.dueSchedules || !transport.advanceSchedule) return 0;
|
|
802
|
+
const due = await transport.dueSchedules(scheduleOptions.lease ?? 60, scheduleOptions.batch ?? 100);
|
|
803
|
+
let ran = 0;
|
|
804
|
+
for (const schedule of due) {
|
|
805
|
+
await transport.send(schedule.queue, schedule.payload, 0, 5, `schedule:${schedule.name}:${schedule.nextRun.toString()}`);
|
|
806
|
+
const next = millis(nextCronRun(schedule.cron, schedule.timeZone, later(nowInstant(), schedule.nextRun)));
|
|
807
|
+
if (await transport.advanceSchedule(schedule.name, schedule.nextRun, next)) ran += 1;
|
|
808
|
+
}
|
|
809
|
+
return ran;
|
|
810
|
+
};
|
|
811
|
+
const prepare = async (name, cron, queue, payload, scheduleOptions, tenantDefault) => {
|
|
812
|
+
const valid = await validate(schemaOf(queue), payload, "payload");
|
|
813
|
+
if (!valid.ok) return valid;
|
|
814
|
+
const timeZone = scheduleOptions.timeZone ?? "UTC";
|
|
815
|
+
try {
|
|
816
|
+
nextCronRun(cron, timeZone, nowInstant());
|
|
817
|
+
} catch (cause) {
|
|
818
|
+
return err(dbError("validation", errorText(cause), { issues: [{
|
|
819
|
+
message: errorText(cause),
|
|
820
|
+
path: [name, "cron"]
|
|
821
|
+
}] }));
|
|
822
|
+
}
|
|
823
|
+
return ok({
|
|
824
|
+
name,
|
|
825
|
+
cron,
|
|
826
|
+
queue,
|
|
827
|
+
payload: withContext(valid.data, scheduleOptions.context),
|
|
828
|
+
timeZone,
|
|
829
|
+
tenant: scheduleOptions.tenant ?? (scheduleOptions.context ? tenantFrom(scheduleOptions.context, tenantPathsFor(void 0)) : void 0) ?? tenantDefault
|
|
830
|
+
});
|
|
831
|
+
};
|
|
832
|
+
const write = (prepared) => transport.schedule(prepared.name, prepared.cron, prepared.queue, prepared.payload, prepared.timeZone, millis(nextCronRun(prepared.cron, prepared.timeZone, nowInstant())), prepared.tenant);
|
|
833
|
+
return {
|
|
834
|
+
enqueue(queue, payload, enqueueOptions = {}) {
|
|
835
|
+
return AsyncResult.from(async () => {
|
|
836
|
+
const valid = await validate(schemaOf(queue), payload, "payload");
|
|
837
|
+
if (!valid.ok) return valid;
|
|
838
|
+
const delay = enqueueOptions.delay ?? (enqueueOptions.runAt ? Math.max(0, Math.ceil((enqueueOptions.runAt.epochMilliseconds - nowInstant().epochMilliseconds) / 1e3)) : 0);
|
|
839
|
+
return run(() => transport.send(queue, withContext(valid.data, enqueueOptions.context), delay, enqueueOptions.maxAttempts ?? 5, enqueueOptions.dedupeKey, enqueueOptions.dedupe));
|
|
840
|
+
});
|
|
841
|
+
},
|
|
842
|
+
claim,
|
|
843
|
+
complete,
|
|
844
|
+
fail,
|
|
845
|
+
extend: (job, lease) => run(() => transport.extend(job, lease)),
|
|
846
|
+
stats: (names) => run(async () => {
|
|
847
|
+
const stats = (queue) => transport.stats?.(queue) ?? sqlOnly("stats");
|
|
848
|
+
const list = names ?? Object.keys(queues);
|
|
849
|
+
const entries = await Promise.all(list.map(async (queue) => {
|
|
850
|
+
schemaOf(queue);
|
|
851
|
+
return [queue, await stats(queue)];
|
|
852
|
+
}));
|
|
853
|
+
return Object.fromEntries(entries);
|
|
854
|
+
}),
|
|
855
|
+
listDead: (queue, listOptions = {}) => run(async () => {
|
|
856
|
+
if (!transport.listDead) sqlOnly("listDead");
|
|
857
|
+
schemaOf(queue);
|
|
858
|
+
return (await transport.listDead(queue, listOptions.limit ?? 100, pageOf(listOptions).cursor)).map((row) => {
|
|
859
|
+
const message = row.message ?? {};
|
|
860
|
+
const { payload, context } = unwrap(message.payload);
|
|
861
|
+
return {
|
|
862
|
+
id: Number(row.id),
|
|
863
|
+
queue,
|
|
864
|
+
payload,
|
|
865
|
+
context,
|
|
866
|
+
attempts: row.attempts,
|
|
867
|
+
maxAttempts: message.max_attempts ?? 5,
|
|
868
|
+
lastError: message.last_error ?? null,
|
|
869
|
+
enqueuedAt: toInstant(row.enqueued_at),
|
|
870
|
+
diedAt: row.died_at === null ? null : toInstant(row.died_at)
|
|
871
|
+
};
|
|
872
|
+
});
|
|
873
|
+
}),
|
|
874
|
+
retryDead: (queue, retryOptions = {}) => run(async () => {
|
|
875
|
+
if (!transport.retryDead) sqlOnly("retryDead");
|
|
876
|
+
schemaOf(queue);
|
|
877
|
+
return transport.retryDead(queue, retryOptions.ids, retryOptions.limit ?? 1e3);
|
|
878
|
+
}),
|
|
879
|
+
replay: (queue, id) => run(() => {
|
|
880
|
+
if (!transport.replay) sqlOnly("replay");
|
|
881
|
+
return transport.replay(queue, id);
|
|
882
|
+
}),
|
|
883
|
+
schedule(name, cron, queue, payload, scheduleOptions = {}) {
|
|
884
|
+
return AsyncResult.from(async () => {
|
|
885
|
+
const prepared = await prepare(name, cron, queue, payload, scheduleOptions);
|
|
886
|
+
if (!prepared.ok) return prepared;
|
|
887
|
+
return run(() => write(prepared.data));
|
|
888
|
+
});
|
|
889
|
+
},
|
|
890
|
+
unschedule: (name) => run(() => transport.unschedule(name)),
|
|
891
|
+
ensureSchedules(definitions, ensureOptions) {
|
|
892
|
+
return AsyncResult.from(async () => {
|
|
893
|
+
const { prefix, tenant } = ensureOptions;
|
|
894
|
+
if (prefix.length === 0) return err(dbError("invalid_request", "ensureSchedules needs a non-empty prefix"));
|
|
895
|
+
const names = /* @__PURE__ */ new Set();
|
|
896
|
+
const prepared = [];
|
|
897
|
+
for (const definition of definitions) {
|
|
898
|
+
if (!definition.name.startsWith(prefix) || names.has(definition.name)) return err(dbError("invalid_request", names.has(definition.name) ? `Schedule "${definition.name}" is defined twice` : `Schedule "${definition.name}" does not start with "${prefix}"`));
|
|
899
|
+
names.add(definition.name);
|
|
900
|
+
const ready = await prepare(definition.name, definition.cron, definition.queue, definition.payload, definition, tenant);
|
|
901
|
+
if (!ready.ok) return ready;
|
|
902
|
+
prepared.push(ready.data);
|
|
903
|
+
}
|
|
904
|
+
return run(async () => {
|
|
905
|
+
if (!transport.listSchedules) sqlOnly("ensureSchedules");
|
|
906
|
+
const existing = await transport.listSchedules({
|
|
907
|
+
prefix,
|
|
908
|
+
...tenant === void 0 ? {} : { tenant }
|
|
909
|
+
});
|
|
910
|
+
for (const schedule of prepared) await write(schedule);
|
|
911
|
+
const removed = [];
|
|
912
|
+
for (const schedule of existing) {
|
|
913
|
+
if (names.has(schedule.name)) continue;
|
|
914
|
+
if (await transport.unschedule(schedule.name)) removed.push(schedule.name);
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
scheduled: [...names],
|
|
918
|
+
removed
|
|
919
|
+
};
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
},
|
|
923
|
+
listSchedules: (filter = {}) => run(async () => {
|
|
924
|
+
if (!transport.listSchedules) sqlOnly("listSchedules");
|
|
925
|
+
return [...await transport.listSchedules(filter)];
|
|
926
|
+
}),
|
|
927
|
+
unscheduleAll: (filter) => run(() => {
|
|
928
|
+
if (!transport.unscheduleTenant) sqlOnly("unscheduleAll");
|
|
929
|
+
return transport.unscheduleTenant(filter.tenant);
|
|
930
|
+
}),
|
|
931
|
+
runSchedules: (scheduleOptions = {}) => run(() => runSchedules(scheduleOptions)),
|
|
932
|
+
drain: (queue, handler, drainOptions = {}) => loop(queue, handler, drainOptions, false, drainOptions.budgetMs === void 0 ? void 0 : Date.now() + drainOptions.budgetMs),
|
|
933
|
+
work: (queue, handler, workOptions = {}) => loop(queue, handler, workOptions, true),
|
|
934
|
+
drainRoute(routeOptions) {
|
|
935
|
+
const secret = routeOptions.secret;
|
|
936
|
+
if (!secret) throw new TypeError("drainRoute needs a secret, such as process.env.CRON_SECRET");
|
|
937
|
+
for (const queue of Object.keys(routeOptions.handlers)) schemaOf(queue);
|
|
938
|
+
return async (request) => {
|
|
939
|
+
const instance = new URL(request.url).pathname;
|
|
940
|
+
if (request.method !== "GET" && request.method !== "POST") return new Response(null, {
|
|
941
|
+
status: 405,
|
|
942
|
+
headers: { allow: "GET, POST" }
|
|
943
|
+
});
|
|
944
|
+
if (!verifySharedSecret(request, secret)) return problemResponse(dbError("unauthorized", "The drain route needs its bearer secret"), {
|
|
945
|
+
instance,
|
|
946
|
+
format: routeOptions.problem
|
|
947
|
+
});
|
|
948
|
+
const deadline = Date.now() + (routeOptions.budgetMs ?? 5e4);
|
|
949
|
+
const monitor = routeOptions.monitor;
|
|
950
|
+
let errors = 0;
|
|
951
|
+
const report = (cause) => {
|
|
952
|
+
errors += 1;
|
|
953
|
+
routeOptions.onError?.(toDbError(cause));
|
|
954
|
+
};
|
|
955
|
+
const watch = async (fn) => {
|
|
956
|
+
try {
|
|
957
|
+
return await fn();
|
|
958
|
+
} catch (cause) {
|
|
959
|
+
routeOptions.onError?.(toDbError(cause));
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
const started = monitor?.onStart ? await watch(() => monitor.onStart?.(request)) : void 0;
|
|
964
|
+
let scheduled = 0;
|
|
965
|
+
if (routeOptions.schedules ?? true) {
|
|
966
|
+
const ran = await run(() => runSchedules({}));
|
|
967
|
+
if (ran.ok) scheduled = ran.data;
|
|
968
|
+
else {
|
|
969
|
+
errors += 1;
|
|
970
|
+
routeOptions.onError?.(ran.error);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
const results = {};
|
|
974
|
+
for (const [queue, handler] of Object.entries(routeOptions.handlers)) {
|
|
975
|
+
if (handler === void 0) continue;
|
|
976
|
+
if (Date.now() >= deadline) break;
|
|
977
|
+
try {
|
|
978
|
+
results[queue] = await loop(queue, handler, routeOptions, false, deadline);
|
|
979
|
+
} catch (cause) {
|
|
980
|
+
report(cause);
|
|
981
|
+
results[queue] = {
|
|
982
|
+
succeeded: 0,
|
|
983
|
+
failed: 0
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
const result = {
|
|
988
|
+
schedules: scheduled,
|
|
989
|
+
queues: results,
|
|
990
|
+
budgetExhausted: Date.now() >= deadline,
|
|
991
|
+
errors
|
|
992
|
+
};
|
|
993
|
+
if (monitor?.onFinish) await watch(() => monitor.onFinish?.(result, started));
|
|
994
|
+
return Response.json(result);
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
//#endregion
|
|
1000
|
+
//#region src/blocks/jobs/rate-limit.ts
|
|
1001
|
+
const isTransport = (source) => "call" in source;
|
|
1002
|
+
/**
|
|
1003
|
+
* Fixed-window limits for keys that aren't claims, over the `rate-limit`
|
|
1004
|
+
* module's counters. Pass a service SQL connection (`ctx.postgresAdmin`,
|
|
1005
|
+
* `postgres.admin`), or a service-role `BlockTransport` such as
|
|
1006
|
+
* `rpcTransport(adminClient)`.
|
|
1007
|
+
*/
|
|
1008
|
+
function createRateLimit(source, options = {}) {
|
|
1009
|
+
applyTemporal(options);
|
|
1010
|
+
const hit = isTransport(source) ? overTransport(blockCall(source, void 0, options.mappers)) : overSql(source);
|
|
1011
|
+
return { check: (scope, key, rule) => {
|
|
1012
|
+
if (rule !== void 0 && (!Number.isInteger(rule.max) || rule.max < 1)) return AsyncResult.err(dbError("invalid_input", "A rate limit's max must be a positive integer"));
|
|
1013
|
+
return hit(scope, key, rule?.max ?? null, rule === void 0 ? null : seconds(rule.period));
|
|
1014
|
+
} };
|
|
1015
|
+
}
|
|
1016
|
+
const overTransport = (call) => (scope, key, max, period) => call("check_rate_limit", {
|
|
1017
|
+
scope,
|
|
1018
|
+
key,
|
|
1019
|
+
max_requests: max ?? void 0,
|
|
1020
|
+
period: period ?? void 0
|
|
1021
|
+
}, (value) => {
|
|
1022
|
+
const row = isRecord(value) ? value : {};
|
|
1023
|
+
return {
|
|
1024
|
+
allowed: row["allowed"] === true,
|
|
1025
|
+
remaining: Number(row["remaining"] ?? 0),
|
|
1026
|
+
retryAfter: Number(row["retry_after"] ?? 0)
|
|
1027
|
+
};
|
|
1028
|
+
});
|
|
1029
|
+
const overSql = (sql) => (scope, key, max, period) => run(async () => {
|
|
1030
|
+
const [row] = await sql.queryRaw("select * from better_supabase.hit_rate_limit($1, $2, $3, $4::interval)", [
|
|
1031
|
+
scope,
|
|
1032
|
+
key,
|
|
1033
|
+
max,
|
|
1034
|
+
period
|
|
1035
|
+
]);
|
|
1036
|
+
return {
|
|
1037
|
+
allowed: row?.allowed ?? false,
|
|
1038
|
+
remaining: row?.remaining ?? 0,
|
|
1039
|
+
retryAfter: row?.retry_after ?? 0
|
|
1040
|
+
};
|
|
1041
|
+
});
|
|
1042
|
+
/** A `problem+json` 429 with `Retry-After`, for a refused `check`. */
|
|
1043
|
+
function rateLimited(decision, options = {}) {
|
|
1044
|
+
const retryAfter = Math.max(1, Math.ceil(decision.retryAfter));
|
|
1045
|
+
return problemResponse(dbError("rate_limited", options.detail ?? `Too many requests. Retry after ${retryAfter} seconds.`, { retryAfter }), {
|
|
1046
|
+
...options.instance === void 0 ? {} : { instance: options.instance },
|
|
1047
|
+
format: options.problem
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
//#endregion
|
|
1051
|
+
export { assertCron, createIdempotency, createJobs, createRateLimit, createWebhookInbox, devDrain, devDrainSecret, nextCronRun, pgmqPublicBackend, rateLimited, sqlQueueBackend, withLease };
|