better-supabase 0.7.0 → 0.8.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 +6 -2
- package/dist/{access-sql-B0dF-SoF.js → access-sql-CrSbK1q-.js} +1 -1
- package/dist/{adapter-B-tL8hqg.js → adapter-CN45ibbV.js} +663 -157
- package/dist/{agents-CVHGd7It.d.ts → agents-BY_LQXtQ.d.ts} +4 -4
- package/dist/{ai-cache-CBWs1W0k.js → ai-cache-Dfgk8nBT.js} +2 -1
- package/dist/{ai-cache-DaDu_G5L.d.ts → ai-cache-DrUMMgp6.d.ts} +4 -4
- package/dist/{ai-chat-B24UI962.js → ai-chat-CVLRWbtS.js} +4 -3
- package/dist/{ai-chat-7amvXjmQ.d.ts → ai-chat-DXkMy74O.d.ts} +5 -5
- package/dist/{ai-files-CSPNswUY.js → ai-files-CioLu5du.js} +5 -4
- package/dist/ai-message-DfaP8sis.js +14 -0
- package/dist/ai-sdk/agents/index.d.ts +4 -4
- package/dist/ai-sdk/batches/index.d.ts +4 -4
- package/dist/ai-sdk/batches/index.js +4 -4
- package/dist/ai-sdk/cache/index.d.ts +2 -2
- package/dist/ai-sdk/cache/index.js +2 -2
- package/dist/ai-sdk/chat/index.d.ts +1 -1
- package/dist/ai-sdk/chat/index.js +8 -20
- package/dist/ai-sdk/embeddings/index.d.ts +2 -2
- package/dist/ai-sdk/files/index.d.ts +3 -3
- package/dist/ai-sdk/files/index.js +4 -4
- package/dist/ai-sdk/index.d.ts +37 -9
- package/dist/ai-sdk/index.js +3 -2
- package/dist/ai-sdk/mcp/index.d.ts +43 -6
- package/dist/ai-sdk/mcp/index.js +36 -7
- package/dist/ai-sdk/memory/index.d.ts +2 -2
- package/dist/ai-sdk/memory/index.js +1 -1
- package/dist/ai-sdk/workflow/index.d.ts +6 -6
- package/dist/ai-sdk/workflow/index.js +13 -24
- package/dist/{ai-tasks-Cd1HMFvB.d.ts → ai-tasks-BRQ6iQLc.d.ts} +4 -4
- package/dist/{announcements-ZRGnScWI.d.ts → announcements-Bouy0Ski.d.ts} +3 -3
- package/dist/{announcements-CiyOs-xP.js → announcements-p_BFofiR.js} +1 -1
- package/dist/api-Dnf2q-Ht.d.ts +370 -0
- package/dist/api-DtHFC037.js +676 -0
- package/dist/{api-keys-CIjghYjV.d.ts → api-keys-C8BlF3L2.d.ts} +4 -4
- package/dist/arazzo/index.d.ts +255 -0
- package/dist/arazzo/index.js +622 -0
- package/dist/{assistant-DwdpDH1q.d.ts → assistant-DBlFDZsi.d.ts} +5 -5
- package/dist/astro/index.d.ts +7 -7
- package/dist/astro/index.js +7 -7
- package/dist/asyncapi/index.d.ts +103 -0
- package/dist/asyncapi/index.js +569 -0
- package/dist/authorizer-ayxUDXXP.js +126 -0
- package/dist/authorizer-eiOjo-3l.d.ts +73 -0
- package/dist/better-result/index.d.ts +118 -0
- package/dist/better-result/index.js +64 -0
- package/dist/{bind-l4kAvYSv.js → bind-ButSzrHe.js} +1 -1
- package/dist/{bind-Bu_ZNa3W.d.ts → bind-CQ5mPyX1.d.ts} +3 -3
- package/dist/{block-events-CrT8r7Fe.js → block-events-DTxU5tfr.js} +18 -3
- package/dist/{block-fields-GFkxl4dP.js → block-fields-CroxUuF2.js} +3 -3
- package/dist/{block-fields-SJ6fEf3o.d.ts → block-fields-D1pfrcIY.d.ts} +2 -2
- package/dist/{block-helpers-CKtbFcRv.d.ts → block-helpers-C0Xj8War.d.ts} +2 -2
- package/dist/{block-helpers-DWHxHsEQ.js → block-helpers-Cy3pB96I.js} +41 -9
- package/dist/{block-hooks-BmjMyXTa.js → block-hooks-BPNMIlLl.js} +3 -3
- package/dist/{block-hooks-plbZ0AbJ.d.ts → block-hooks-DaOVC7SC.d.ts} +3 -3
- package/dist/{block-transport-BLr-ZgLc.js → block-transport-bAKh-dGc.js} +28 -2
- package/dist/{block-transport-DvaoPuZ9.d.ts → block-transport-inD4fFq-.d.ts} +24 -2
- package/dist/blocks/agents/index.d.ts +3 -3
- package/dist/blocks/agents/index.js +5 -4
- package/dist/blocks/ai-cache/index.d.ts +3 -3
- package/dist/blocks/ai-cache/index.js +3 -3
- package/dist/blocks/ai-chat/index.d.ts +9 -4
- package/dist/blocks/ai-chat/index.js +5 -5
- package/dist/blocks/ai-chat/react/index.d.ts +3 -3
- package/dist/blocks/ai-chat/react/index.js +5 -5
- package/dist/blocks/ai-files/index.d.ts +3 -3
- package/dist/blocks/ai-files/index.js +3 -3
- package/dist/blocks/ai-providers/index.d.ts +3 -3
- package/dist/blocks/ai-providers/index.js +8 -7
- package/dist/blocks/ai-tasks/index.d.ts +3 -3
- package/dist/blocks/ai-tasks/index.js +7 -6
- package/dist/blocks/announcements/index.d.ts +3 -3
- package/dist/blocks/announcements/index.js +3 -3
- package/dist/blocks/announcements/react/index.d.ts +3 -3
- package/dist/blocks/announcements/react/index.js +3 -3
- package/dist/blocks/api-keys/index.d.ts +5 -5
- package/dist/blocks/api-keys/index.js +8 -8
- package/dist/blocks/attachments/index.d.ts +12 -8
- package/dist/blocks/attachments/index.js +22 -13
- package/dist/blocks/audit/index.d.ts +21 -10
- package/dist/blocks/audit/index.js +30 -11
- package/dist/blocks/billing/index.d.ts +11 -9
- package/dist/blocks/billing/index.js +15 -15
- package/dist/blocks/comments/index.d.ts +8 -8
- package/dist/blocks/comments/index.js +8 -7
- package/dist/blocks/connectors/index.d.ts +3 -3
- package/dist/blocks/connectors/index.js +7 -6
- package/dist/blocks/data-lifecycle/index.d.ts +12 -8
- package/dist/blocks/data-lifecycle/index.js +13 -8
- package/dist/blocks/entitlements/index.d.ts +3 -3
- package/dist/blocks/entitlements/index.js +1 -1
- package/dist/blocks/flags/index.d.ts +4 -4
- package/dist/blocks/flags/index.js +4 -3
- package/dist/blocks/inbox/index.d.ts +3 -3
- package/dist/blocks/inbox/index.js +4 -5
- package/dist/blocks/inbox/react/index.d.ts +1 -1
- package/dist/blocks/inbox/react/index.js +1 -1
- package/dist/blocks/inbox/react/server.d.ts +1 -1
- package/dist/blocks/index.d.ts +10 -10
- package/dist/blocks/index.js +6 -6
- package/dist/blocks/jobs/index.d.ts +48 -32
- package/dist/blocks/jobs/index.js +44 -287
- package/dist/blocks/knowledge/index.d.ts +3 -3
- package/dist/blocks/knowledge/index.js +3 -3
- package/dist/blocks/memory/index.d.ts +4 -4
- package/dist/blocks/memory/index.js +7 -6
- package/dist/blocks/notifications/index.d.ts +11 -9
- package/dist/blocks/notifications/index.js +20 -25
- package/dist/blocks/notifications/react/index.d.ts +1 -1
- package/dist/blocks/notifications/react/index.js +1 -1
- package/dist/blocks/onboarding/index.d.ts +3 -3
- package/dist/blocks/onboarding/index.js +3 -3
- package/dist/blocks/onboarding/react/index.d.ts +2 -2
- package/dist/blocks/onboarding/react/index.js +2 -2
- package/dist/blocks/onboarding/react/server.d.ts +1 -1
- package/dist/blocks/organizations/index.d.ts +9 -9
- package/dist/blocks/organizations/index.js +11 -18
- package/dist/blocks/outbox/index.d.ts +2 -2
- package/dist/blocks/outbox/index.js +16 -25
- package/dist/blocks/profiles/index.d.ts +7 -7
- package/dist/blocks/profiles/index.js +7 -6
- package/dist/blocks/push/index.d.ts +4 -4
- package/dist/blocks/push/index.js +3 -3
- package/dist/blocks/settings/index.d.ts +4 -4
- package/dist/blocks/settings/index.js +7 -6
- package/dist/blocks/sso/index.d.ts +5 -5
- package/dist/blocks/sso/index.js +6 -5
- package/dist/blocks/usage/index.d.ts +3 -3
- package/dist/blocks/usage/index.js +5 -4
- package/dist/blocks/waitlist/index.d.ts +5 -5
- package/dist/blocks/waitlist/index.js +5 -4
- package/dist/blocks/webhooks/index.d.ts +22 -13
- package/dist/blocks/webhooks/index.js +41 -70
- package/dist/blocks/workflow-builder/index.d.ts +4 -4
- package/dist/blocks/workflow-builder/index.js +3 -3
- package/dist/blocks/workflow-builder/react/index.d.ts +4 -4
- package/dist/blocks/workflow-builder/react/index.js +4 -4
- package/dist/blocks/workflows/index.d.ts +3 -3
- package/dist/blocks/workflows/index.js +3 -3
- package/dist/blocks/workflows/react/index.d.ts +3 -3
- package/dist/blocks/workflows/react/index.js +3 -3
- package/dist/build-hPwk3_X5.js +1296 -0
- package/dist/{build-BXIzE6Xv.js → build-meLuYGri.js} +3 -3
- package/dist/casing-DdFWEtqr.js +16 -0
- package/dist/chat-sdk/index.d.ts +6 -6
- package/dist/chat-sdk/index.js +5 -4
- package/dist/chat-sdk/react/index.d.ts +1 -1
- package/dist/chat-sdk/react/server.d.ts +1 -1
- package/dist/{claims-DDBQT6BK.js → claims-0KZVT804.js} +1 -1
- package/dist/cli/add-1dc5wM5k.js +69 -0
- package/dist/cli/arazzo-1.0-BalU61-_.js +532 -0
- package/dist/cli/arazzo-1.1-Bnfasbun.js +705 -0
- package/dist/cli/asyncapi-3.0.0-C78dBjR6.js +5593 -0
- package/dist/cli/asyncapi-3.1.0-g-7aIfV1.js +5833 -0
- package/dist/cli/bin.js +4 -4
- package/dist/cli/cache-CIfA-jBV.js +483 -0
- package/dist/cli/{codemod-Bl_tQpHn.js → codemod-JVlTQQG2.js} +4 -4
- package/dist/cli/{command-CK14fP91.js → command-DV-StG1x.js} +5 -0
- package/dist/cli/{config-DU7EJImt.js → config-C2Pz2_4A.js} +7 -4
- package/dist/cli/config-DpbF8ML3.js +2 -0
- package/dist/cli/{config-NiPxAmUF.js → config-M-WzL9O2.js} +116 -18
- package/dist/cli/{db-DOBPt660.js → db-BkcfOlyB.js} +2 -1
- package/dist/cli/{diff-SjkO5rNr.js → diff-D1jGdb3Y.js} +1 -1
- package/dist/cli/{doctor-CVdnLahA.js → doctor-VcDtuusV.js} +317 -222
- package/dist/cli/emit-BHHfwUoT.js +1136 -0
- package/dist/cli/{env-qXgyMV6r.js → env-B--LMhEe.js} +13 -10
- package/dist/cli/env-vzBG90Um.js +2 -0
- package/dist/cli/{env-CJLbUVPB.js → env-zHJtMIBo.js} +11 -3
- package/dist/cli/{errors-4v4DTtda.js → errors-CqTLXe2M.js} +18 -2
- package/dist/cli/{fastest-levenshtein-CFN-lNDv.js → fastest-levenshtein-CF7BCi9l.js} +1 -1
- package/dist/cli/files-BVupXPIC.js +34 -0
- package/dist/cli/gen-DmVj3u4L.js +322 -0
- package/dist/cli/{init-Cr6arjXU.js → glue-C1UE8gQN.js} +16 -313
- package/dist/cli/index.d.ts +59 -31
- package/dist/cli/index.js +8 -6
- package/dist/cli/init-CVZ3s5kl.js +258 -0
- package/dist/cli/{introspect-Bzuv1HHj.js → introspect-BToGNfm-.js} +14 -14
- package/dist/cli/{io-DI1mTdSA.js → io-CFzJVJ68.js} +5 -1
- package/dist/cli/{keys-BGCnnb-s.js → keys-CJhtIxJ3.js} +4 -4
- package/dist/cli/metadata-DSRKCaf-.js +133 -0
- package/dist/cli/names-D6LrQnOv.js +6 -0
- package/dist/cli/openapi-3.0-t7xwkuRH.js +928 -0
- package/dist/cli/openapi-3.1-CVze7tBS.js +870 -0
- package/dist/cli/openapi-3.2-CFoMYM01.js +1031 -0
- package/dist/cli/{openapi-BlRJ18s3.js → openapi-DWa4Ir1q.js} +8 -14
- package/dist/cli/prompts-BdeVmVpg.js +2 -0
- package/dist/cli/{prompts-BNcNfAve.js → prompts-DZJB1nTw.js} +6 -6
- package/dist/cli/{rolldown-runtime-BPOCksWG.js → rolldown-runtime-CMFfr-1z.js} +3 -1
- package/dist/cli/run-DA6_lg3w.js +389 -0
- package/dist/cli/run-DuN1MX-7.js +705 -0
- package/dist/cli/scaffold-5DenHSxF.js +292 -0
- package/dist/cli/scaffold-CwQUMFRb.js +2 -0
- package/dist/cli/{seed-DQzqT7LP.js → seed-CzBmrGON.js} +14 -18
- package/dist/cli/seed-DOmit9Hq.js +2 -0
- package/dist/cli/{skills-QQs4q68J.js → skills-CnO8QFS7.js} +7 -6
- package/dist/cli/snapshot-Bstk6ulj.js +287 -0
- package/dist/cli/source-args-BYfq6imV.js +31 -0
- package/dist/cli/spec-CzosTYm3.js +7438 -0
- package/dist/cli/spec-T_0Bcyw7.js +2 -0
- package/dist/cli/{sql-B8csQPyn.js → sql-BKOOQAX3.js} +240 -218
- package/dist/cli/sql-DJrNDxQm.js +3 -0
- package/dist/cli/{style-C0NFdJRL.js → style-CB3vxgh0.js} +3 -3
- package/dist/cli/typegen-Demi5-VQ.js +486 -0
- package/dist/cli/validate-Ca13ktgz.js +7522 -0
- package/dist/cli/{version-Bp4fyubI.js → version-B4k0aUxS.js} +1 -1
- package/dist/client/index.d.ts +4 -3
- package/dist/client/index.js +60 -3
- package/dist/client/native/index.d.ts +3 -3
- package/dist/client/native/index.js +3 -3
- package/dist/{client-CI0Z2zWX.js → client-wxeTQFtB.js} +36 -3
- package/dist/{clone-BfgmLU90.js → clone-dN9XOL1K.js} +9 -1
- package/dist/{cloud-event-BnmD5fb9.js → cloud-event-CTh9Sr5w.js} +16 -2
- package/dist/{cloud-event-C1JCc1bH.d.ts → cloud-event-Dune0KI7.d.ts} +16 -2
- package/dist/{compiler-DY_wkj4R.d.ts → compiler-V1qvcST1.d.ts} +2 -2
- package/dist/{compose-BIBNgAnq.js → compose-BHej1acw.js} +3 -3
- package/dist/{composite-CpHLEo-G.d.ts → composite-CWAJTRBF.d.ts} +6 -6
- package/dist/{composite-C6I8ZyaF.js → composite-zTe0TCqj.js} +4 -4
- package/dist/config/index.d.ts +3 -4
- package/dist/config/index.js +4 -4
- package/dist/config-DlKvtt95.js +540 -0
- package/dist/{connector-Dv8kK0MO.d.ts → connector-CySQEFcW.d.ts} +1 -1
- package/dist/{connectors-CrpknU0P.d.ts → connectors--PGhmiu0.d.ts} +4 -4
- package/dist/{context-BCrWe7ow.d.ts → context-DsV2SueA.d.ts} +11 -2
- package/dist/context-nPDvQ-Zl.js +41 -0
- package/dist/credentials/index.d.ts +5 -5
- package/dist/credentials/index.js +9 -17
- package/dist/{cron-DVPUg5k3.js → cron-D25Ukpxy.js} +1 -1
- package/dist/cron-route-D2AN9SA1.js +24 -0
- package/dist/{csv-DCK-PT1n.js → csv-BZnHA1Py.js} +1 -1
- package/dist/{define-B-TVjC25.js → define-CFQcvM20.js} +263 -229
- package/dist/{define-CJJkkr1-.d.ts → define-DmcfoNXB.d.ts} +136 -111
- package/dist/edge/index.d.ts +66 -13
- package/dist/edge/index.js +65 -39
- package/dist/elysia/index.d.ts +1 -1
- package/dist/elysia/index.js +1 -1
- package/dist/{embeddings-C9KGQnXt.d.ts → embeddings-B4vhS-dY.d.ts} +1 -1
- package/dist/{ended-XjeGQ5XW.d.ts → ended-CcvcRqEd.d.ts} +7 -1
- package/dist/{entitlements-DRl6ZJUm.js → entitlements-BYFf0Cdh.js} +2 -2
- package/dist/{entitlements-BKSVGtrG.d.ts → entitlements-Ds8qoYZ-.d.ts} +1 -1
- package/dist/env/index.d.ts +1 -1
- package/dist/env/index.js +9 -2
- package/dist/{errors-B11Duf73.js → errors-2M6CKMP4.js} +5 -0
- package/dist/{errors-DLjJHPLK.js → errors-rZ7VIqp6.js} +2 -2
- package/dist/{escape-like-ImTK0ps_.d.ts → escape-like-DDWoHzh_.d.ts} +1 -1
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +5 -11
- package/dist/{eve-gT2Fpjut.d.ts → eve-CoAIcCkq.d.ts} +2 -2
- package/dist/events/index.d.ts +4 -4
- package/dist/events/index.js +7 -2
- package/dist/{events-DWDhx77e.d.ts → events-D1BOCll8.d.ts} +6 -4
- package/dist/{executor-Bq28caBo.js → executor-B-fD7DLt.js} +52 -35
- package/dist/executor-B_Hfobee.js +162 -0
- package/dist/{executor-BVBFM86G.d.ts → executor-CpxLPCiz.d.ts} +1 -1
- package/dist/{executor-CaWigTyR.d.ts → executor-DfEtf0cq.d.ts} +43 -27
- package/dist/{executor-DvsBbNBS.d.ts → executor-Drzu3dBK.d.ts} +2 -2
- package/dist/{executor-DbvExCFC.d.ts → executor-ngjKL_Pe.d.ts} +2 -2
- package/dist/expo/index.d.ts +4 -4
- package/dist/expo/index.js +6 -6
- package/dist/expo-sqlite/index.d.ts +3 -3
- package/dist/expo-sqlite/index.js +2 -2
- package/dist/extra-CGQ2_q3S.js +10 -0
- package/dist/{field-errors-BFOa7WhB.d.ts → field-errors-DIe9XYWL.d.ts} +17 -7
- package/dist/{field-errors-VF1yb8yI.js → field-errors-DZVRWRkf.js} +1 -1
- package/dist/format-DTXdu8ya.d.ts +70 -0
- package/dist/{framework-KIPvRQJU.d.ts → framework-148t1m3C.d.ts} +5 -5
- package/dist/{framework-BDCpJI7o.js → framework-3ey3QmrO.js} +2 -2
- package/dist/function-contract-BcZenXMO.d.ts +8 -0
- package/dist/{gateway-C1ssRHnf.d.ts → gateway-S9vicgeo.d.ts} +4 -4
- package/dist/{guards-BnzDvFrz.js → guards-6F2bCtQR.js} +3 -1
- package/dist/h3/index.d.ts +1 -1
- package/dist/h3/index.js +1 -1
- package/dist/h3/v1/index.d.ts +1 -1
- package/dist/h3/v1/index.js +1 -1
- package/dist/{message-CaKlqdsX.js → history-GH1HJWQV.js} +65 -13
- package/dist/hmac-DCud0myt.js +22 -0
- package/dist/hono/index.d.ts +19 -14
- package/dist/hono/index.js +21 -16
- package/dist/{hooks-1BTCJsiw.js → hooks-Bhza9Hnr.js} +68 -100
- package/dist/http-sbmBe5d2.js +25 -0
- package/dist/{inbox-i7E1ppo6.d.ts → inbox-Cd1KCEBL.d.ts} +3 -3
- package/dist/index-B72Rvv_j.d.ts +89 -0
- package/dist/{index-DunNRdnT.d.ts → index-BCHR4Ucl.d.ts} +4 -4
- package/dist/{index-CLg87OBW.d.ts → index-BUK7jUzR.d.ts} +6 -0
- package/dist/{config-D44Y7PAv.d.ts → index-Ba1SI-uO.d.ts} +625 -278
- package/dist/{index-D48jVS-N.d.ts → index-BfxOaOYs.d.ts} +4 -5
- package/dist/{index-ClmjK_o-.d.ts → index-CicIyqOe.d.ts} +4 -4
- package/dist/{index-ACiNPN4Z.d.ts → index-D69h4QqD.d.ts} +1 -1
- package/dist/index-DG2SUXDQ.d.ts +58 -0
- package/dist/{index-Bepi-Hgg.d.ts → index-DoNVLHOU.d.ts} +3 -3
- package/dist/{index-CdZKQF98.d.ts → index-R274XFlg.d.ts} +1 -1
- package/dist/{index-CewnYSpv.d.ts → index-_4R4b01v.d.ts} +8 -18
- package/dist/index.d.ts +89 -14
- package/dist/index.js +12 -11
- package/dist/{json-schema-CvER4kgT.js → json-schema-CT4tvrkA.js} +148 -102
- package/dist/{kit-Fm-Fv7q5.js → kit-BhHr9T9v.js} +36 -10
- package/dist/{kit-CkNzZUqv.d.ts → kit-W99LCPBS.d.ts} +13 -7
- package/dist/{knowledge-BXBbT0Tr.d.ts → knowledge-CAReFpRA.d.ts} +5 -5
- package/dist/{knowledge-CDmiMLew.js → knowledge-DnHtb64A.js} +4 -3
- package/dist/list/index.d.ts +1 -2
- package/dist/{list-query-BRt-zDF7.d.ts → list-query-D3JQeEL7.d.ts} +4 -4
- package/dist/mcp/index.d.ts +49 -19
- package/dist/mcp/index.js +158 -94
- package/dist/mcp/sdk/index.d.ts +38 -6
- package/dist/mcp/sdk/index.js +88 -6
- package/dist/{message-CfDoR0Y6.d.ts → message-B0fNJgq8.d.ts} +1 -2
- package/dist/{messages-B9-5887d.js → messages-CJoHHCWT.js} +4 -205
- package/dist/{mfa-Daak8ukG.d.ts → mfa-SZc9YYyb.d.ts} +1 -1
- package/dist/model-OgBxvk3N.d.ts +300 -0
- package/dist/model-messages-Cxw_Up4f.js +206 -0
- package/dist/model-messages-D4GZllOD.d.ts +34 -0
- package/dist/modules-DksjZyMW.js +77 -0
- package/dist/nestjs/index.d.ts +2 -2
- package/dist/nestjs/index.js +1 -1
- package/dist/next/index.d.ts +105 -55
- package/dist/next/index.js +204 -170
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +1 -1
- package/dist/{onboarding-BZGHYJks.d.ts → onboarding-LZvlK1P4.d.ts} +3 -3
- package/dist/openapi/index.d.ts +4 -2
- package/dist/openapi/index.js +21 -343
- package/dist/optional-peer-D1pi-skw.js +24 -0
- package/dist/orpc/index.d.ts +8 -8
- package/dist/orpc/index.js +21 -20
- package/dist/otel/index.d.ts +3 -3
- package/dist/otel/index.js +7 -7
- package/dist/{outbox-Cq7pPYvl.d.ts → outbox-DZfAvfzC.d.ts} +6 -6
- package/dist/overlay/index.d.ts +233 -0
- package/dist/overlay/index.js +554 -0
- package/dist/{plugin-FGY8UhYm.d.ts → plugin-CPK2HsbY.d.ts} +42 -3
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/actor/index.js +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +1 -1
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +9 -2
- package/dist/plugins/tenant/index.d.ts +7 -2
- package/dist/plugins/tenant/index.js +16 -2
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +8 -1
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +4 -4
- package/dist/{pool-CAu2nLrL.d.ts → pool-gkhbGp2E.d.ts} +2 -2
- package/dist/postgres/index.d.ts +4 -4
- package/dist/postgres/index.js +2 -1
- package/dist/{postgrest-BLVdEnfe.d.ts → postgrest-BUqLag2Q.d.ts} +1 -1
- package/dist/powersync/index.d.ts +5 -5
- package/dist/powersync/index.js +4 -4
- package/dist/powersync/react/index.d.ts +3 -3
- package/dist/powersync/react/index.js +1 -1
- package/dist/{problem-BreOl8b8.js → problem-CJvFeGu7.js} +13 -2
- package/dist/{problem-DD8LZFjc.d.ts → problem-TvGdxO1t.d.ts} +7 -1
- package/dist/{provider-Di3sRutW.js → provider-DxRTex4x.js} +1 -1
- package/dist/{provider-D-UEMvSD.d.ts → provider-ib9wzUDz.d.ts} +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +1 -1
- package/dist/{query-BQ-NaZon.js → query-Ctz-qKRx.js} +1 -1
- package/dist/rate-limit-Bw7HgmFj.js +260 -0
- package/dist/react/index.d.ts +9 -9
- package/dist/react/index.js +4 -4
- package/dist/react/native/index.d.ts +1 -1
- package/dist/react/native/index.js +2 -2
- package/dist/react/server.d.ts +10 -10
- package/dist/react/server.js +3 -3
- package/dist/react/session.d.ts +2 -2
- package/dist/{react-F80mF4-t.js → react-Bmpo59D7.js} +4 -4
- package/dist/react-router/index.d.ts +6 -6
- package/dist/react-router/index.js +9 -11
- package/dist/{read-set-DuKWC9Mj.js → read-set-DAeWhqPf.js} +2 -2
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +30 -36
- package/dist/{refusal-DhSU2Vvr.d.ts → refusal-CFI8-5nf.d.ts} +4 -1
- package/dist/{refusal-CeJWxmkH.js → refusal-K78A1Vmq.js} +5 -5
- package/dist/{resolve-tJZY3FEa.js → resolve-Bvgf3ut_.js} +131 -42
- package/dist/{resolve-ChTYeTEl.d.ts → resolve-MxXVWyhd.d.ts} +12 -8
- package/dist/resource--vTrBe6I.d.ts +103 -0
- package/dist/resource-BqNqlYg9.js +91 -0
- package/dist/resource-DA811u_4.js +463 -0
- package/dist/resource-execute-rbKvuCsY.d.ts +12 -0
- package/dist/resource-schemas-BJ518zFH.js +79 -0
- package/dist/resource-types-CqB6GWGT.d.ts +141 -0
- package/dist/resources-BVYPnuLo.js +56 -0
- package/dist/resources-DZSk8tXJ.d.ts +7 -0
- package/dist/{respond-DprrgyXX.d.ts → respond-Cw-zanCm.d.ts} +3 -3
- package/dist/{respond-BUgsXNgk.js → respond-volM3ByS.js} +4 -4
- package/dist/{result-D7y2fT87.d.ts → result-BchM1XZn.d.ts} +7 -1
- package/dist/{result-DzUbqgdW.js → result-DEqHCMKP.js} +1 -1
- package/dist/server/index.d.ts +81 -19
- package/dist/server/index.js +16 -14
- package/dist/{server-ZSJ9rCcV.d.ts → server-types-CrAmZ8jv.d.ts} +110 -36
- package/dist/{shared-CxWmcyqB.js → shared-DHjhfRdV.js} +2 -15
- package/dist/shared-DTneRoxi.js +14 -0
- package/dist/{sign-in-Cb1mwv6X.js → sign-in-D_yoP0PF.js} +1 -1
- package/dist/signatures-BRS_p3WD.d.ts +34 -0
- package/dist/signatures-DsWv_bYR.js +103 -0
- package/dist/{signers-4y62_VIi.d.ts → signers-cpdmZYrv.d.ts} +1 -1
- package/dist/solid/index.d.ts +9 -18
- package/dist/solid/index.js +2 -2
- package/dist/solid-start/index.d.ts +6 -6
- package/dist/solid-start/index.js +7 -7
- package/dist/spec/index.d.ts +134 -0
- package/dist/spec/index.js +353 -0
- package/dist/{spec-pins-C89s6DyW.d.ts → spec-pins-CwzDY25w.d.ts} +18 -1
- package/dist/{spec-pins--j6mTok4.js → spec-pins-DsLfUH7l.js} +14 -1
- package/dist/sql/index.d.ts +145 -119
- package/dist/sql/index.js +19921 -19798
- package/dist/{executor-De1Of3m3.js → sql-CwuqaQ4Y.js} +34 -169
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +3 -2
- package/dist/{standard-BK0cqJ7y.js → standard-DkzZUneg.js} +2 -2
- package/dist/{standard-CYsm7xpT.d.ts → standard-lmkbOtPB.d.ts} +1 -1
- package/dist/{stats-D_AD9GGu.js → stats-DhU78Ctf.js} +8 -4
- package/dist/storage/index.d.ts +4 -6
- package/dist/storage/index.js +16 -17
- package/dist/{bucket-Cl46E3jS.d.ts → storage-api-CB7Zl1Ci.d.ts} +243 -8
- package/dist/{store-CGjgWu5n.d.ts → store-DsIYEBnv.d.ts} +1 -1
- package/dist/streams/index.d.ts +4 -4
- package/dist/streams/index.js +5 -4
- package/dist/streams/redis/index.d.ts +1 -1
- package/dist/streams/redis/index.js +5 -11
- package/dist/{stripe-BMj5LtsT.js → stripe-v-Y2lmop.js} +2 -8
- package/dist/supabase-mcp-C6tMoEjn.d.ts +43 -0
- package/dist/supabase-mcp-Cpn5A4i-.js +28 -0
- package/dist/{support-DWwzVf-O.js → support-D1pFFouF.js} +71 -35
- package/dist/{support-view-CVOJJLpc.d.ts → support-view-scqMgtOo.d.ts} +1 -1
- package/dist/svelte/index.d.ts +8 -17
- package/dist/svelte/index.js +2 -2
- package/dist/sveltekit/index.d.ts +7 -7
- package/dist/sveltekit/index.js +10 -12
- package/dist/table-schema-htM_SbCW.js +286 -0
- package/dist/{tags-Cx-LnIQP.d.ts → tags-BO9LjtWG.d.ts} +1 -1
- package/dist/tanstack-db/index.d.ts +2 -2
- package/dist/tanstack-db/supabase/index.d.ts +52 -0
- package/dist/tanstack-db/supabase/index.js +97 -0
- package/dist/tanstack-start/index.d.ts +6 -6
- package/dist/tanstack-start/index.js +8 -12
- package/dist/{tee-CoEppCER.js → tee-Cb98Vn3b.js} +1 -1
- package/dist/{template--jDkl3UP.js → template-lPVh-EwW.js} +11 -2
- package/dist/{temporal-required-BVvW7SYJ.js → temporal-required-BtynQtM-.js} +1 -1
- package/dist/{tenant-BxNFk9bx.d.ts → tenant-CM368LXb.d.ts} +2 -2
- package/dist/testing/index.d.ts +249 -118
- package/dist/testing/index.js +1418 -870
- package/dist/ttl-cache-XzZ-Q2eX.js +29 -0
- package/dist/types-BRTSpOIh.js +5 -0
- package/dist/{types-C05bxW-W.d.ts → types-Bb_4-vdW.d.ts} +4 -4
- package/dist/{types-AYOvcyep.d.ts → types-CD6yCDx3.d.ts} +5 -5
- package/dist/{types-6rocye-y.d.ts → types-CbOUc5IH.d.ts} +4 -4
- package/dist/{types-6BdhkmZe.d.ts → types-CoE5Zwy4.d.ts} +8 -0
- package/dist/{types-DGs5L0VN.d.ts → types-DSJ3-m9X.d.ts} +4 -4
- package/dist/{queue-CLJurhdy.d.ts → types-D__3EN0q.d.ts} +17 -19
- package/dist/{usage-CrvrL2vw.d.ts → usage-D0NNUFfr.d.ts} +3 -3
- package/dist/{vault-wlbBhqm7.d.ts → vault-BpmApxxW.d.ts} +3 -3
- package/dist/vercel-connect/index.d.ts +1 -1
- package/dist/vercel-connect/index.js +5 -11
- package/dist/{verify-DPuWngD4.d.ts → verify-C91Wyp40.d.ts} +5 -35
- package/dist/verify-CJbYfADA.js +124 -0
- package/dist/{version--AolB5ej.js → version-Cq8gfDbd.js} +1 -1
- package/dist/{view-BeFcRV6G.js → view-D5ryq3gz.js} +1 -1
- package/dist/{view-CF7ihnkg.d.ts → view-TpXEUcVN.d.ts} +3 -3
- package/dist/vue/index.d.ts +9 -18
- package/dist/vue/index.js +2 -2
- package/dist/{watch-Xuxrvxq-.js → watch-BcvtAHOd.js} +2 -2
- package/dist/{watch-DI34G86j.d.ts → watch-COzRWvVN.d.ts} +23 -9
- package/dist/{wire-DtZQ4sdE.js → wire-Df3EWkG8.js} +2 -2
- package/dist/{workflow-builder-DJ1oHn8g.js → workflow-builder-CHqHpAVt.js} +6 -5
- package/dist/workflow-sdk/builder/index.d.ts +3 -3
- package/dist/workflow-sdk/index.d.ts +1 -1
- package/dist/workflow-sdk/world/index.d.ts +23 -17
- package/dist/workflow-sdk/world/index.js +59 -76
- package/dist/{workflow-sdk-B3yLD6Br.d.ts → workflow-sdk-DTZyrvNo.d.ts} +3 -3
- package/dist/{workflow-sdk-world-D-prir6O.js → workflow-sdk-world-CMV1ahFW.js} +528 -526
- package/dist/{workflows-B0j9WKPu.d.ts → workflows-CcpCBfIe.d.ts} +3 -3
- package/dist/{workflows-wu1WWBn7.js → workflows-IvxzG2cl.js} +5 -4
- package/package.json +54 -3
- package/schemas/config-v1.json +243 -0
- package/schemas/doctor-report-v1.json +4 -1
- package/schemas/spec-manifest-v1.json +95 -0
- package/skills/better-supabase/SKILL.md +8 -4
- package/skills/better-supabase/references/troubleshooting.md +20 -20
- package/skills/better-supabase-api/references/expo.md +6 -3
- package/skills/better-supabase-api/references/powersync.md +2 -1
- package/skills/better-supabase-auth/SKILL.md +8 -4
- package/dist/cli/config-B9HkL8nF.js +0 -2
- package/dist/cli/gen-AIf4MREI.js +0 -3100
- package/dist/cli/gen-DSrahEdX.js +0 -2
- package/dist/cli/prompts-BTYQGunc.js +0 -2
- package/dist/cli/run-BO052mki.js +0 -361
- package/dist/config-Dn7F5k9A.js +0 -202
- package/dist/history-BZih6SFP.js +0 -64
- package/dist/index-BkWT_uBO.d.ts +0 -51
- package/dist/index-Bxih3l0u.d.ts +0 -81
- package/dist/index-CaxrMD1A.d.ts +0 -1
- package/dist/index-DQh4VCc9.d.ts +0 -98
- package/dist/keys-4MnkWTTh.js +0 -35
- package/dist/messages-DYP0sUTe.d.ts +0 -59
- package/dist/modules-dxrVK3ID.js +0 -228
- package/dist/path-Cv__-OTj.d.ts +0 -17
- package/dist/resource-BsvzCMlX.js +0 -212
- package/dist/resource-IPS5xi9q.d.ts +0 -74
- package/dist/template-CwO38Do8.d.ts +0 -6
- package/dist/verify-DL41hbvY.js +0 -232
- package/dist/cli/{read-sets-CETOSFx4.js → read-sets-DjJrm8Wj.js} +1 -1
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import { r as errorText } from "./errors-CqTLXe2M.js";
|
|
2
|
+
import { a as writeIfChanged, i as sameText, n as importPath, t as display } from "./io-CFzJVJ68.js";
|
|
3
|
+
import { i as writeAtomic, n as cachedDatabaseTypes, t as CACHE_DIR } from "./cache-CIfA-jBV.js";
|
|
4
|
+
import { t as byCodePoint } from "./compare-BmIWVEui.js";
|
|
5
|
+
import { d as typegenVersion, f as typegenVersionNotice, i as oxfmtInstalled, l as stabilizeMetadata, r as generateDatabaseTypes } from "./typegen-Demi5-VQ.js";
|
|
6
|
+
import { a as buildModel, c as toCatalog, i as metaPaths, n as emitMeta, o as generatorModel, r as emitModule } from "./emit-BHHfwUoT.js";
|
|
7
|
+
import { a as entitlementsMode, c as unsafeKey, i as entitlementRequirements, n as accessProviderMode, o as moduleKeyProblems, r as accessRequirements, s as providerLabel, t as compiledReadSets } from "./read-sets-DjJrm8Wj.js";
|
|
8
|
+
import { p as tomlGet } from "./supabase-toml-B-bIukQF.js";
|
|
9
|
+
import { t as loadSnapshot } from "./snapshot-Bstk6ulj.js";
|
|
10
|
+
import { t as fileDiff } from "./diff-D1jGdb3Y.js";
|
|
11
|
+
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { readFile, rm } from "node:fs/promises";
|
|
14
|
+
import { SQL_MODULES, moduleLayout, modulePermissionKeys, renderModules, resolveModules, sameModuleFile } from "better-supabase/sql";
|
|
15
|
+
//#region src/cli/gen/render.ts
|
|
16
|
+
/**
|
|
17
|
+
* Renders every generated file for a snapshot, without touching disk:
|
|
18
|
+
* `database.types.ts` (what `supabase gen types` prints), the main module,
|
|
19
|
+
* then each configured generator's files.
|
|
20
|
+
*/
|
|
21
|
+
async function renderFiles(config, snapshot, options = {}) {
|
|
22
|
+
return (await render(config, snapshot, options)).files;
|
|
23
|
+
}
|
|
24
|
+
async function render(config, snapshot, options = {}) {
|
|
25
|
+
const model = buildModel(snapshot, config);
|
|
26
|
+
const runtime = options.runtimeImport ? { runtimeImport: options.runtimeImport } : {};
|
|
27
|
+
const output = resolve(config.root, config.output);
|
|
28
|
+
const typegenOptions = {
|
|
29
|
+
schemas: config.schemas,
|
|
30
|
+
postgrestVersion: config.postgrestVersion
|
|
31
|
+
};
|
|
32
|
+
const databaseTypes = await cachedDatabaseTypes(config.root, [
|
|
33
|
+
typegenOptions,
|
|
34
|
+
await oxfmtInstalled(),
|
|
35
|
+
snapshot.generator
|
|
36
|
+
], () => generateDatabaseTypes(snapshot.generator, typegenOptions));
|
|
37
|
+
const metaFiles = metaPaths(config.output);
|
|
38
|
+
const main = emitModule(model, {
|
|
39
|
+
databaseTypesImport: importPath(output, resolve(config.root, config.databaseTypesOutput)),
|
|
40
|
+
importPathFor: (from) => importPath(output, resolve(config.root, from)),
|
|
41
|
+
metaImport: `./${basename(metaFiles.js)}`,
|
|
42
|
+
...options.inlineMeta ? { inlineMeta: true } : {},
|
|
43
|
+
...runtime
|
|
44
|
+
});
|
|
45
|
+
const meta = emitMeta(model, {
|
|
46
|
+
types: `./${basename(metaFiles.dts)}`,
|
|
47
|
+
...runtime
|
|
48
|
+
});
|
|
49
|
+
const files = [
|
|
50
|
+
{
|
|
51
|
+
path: config.databaseTypesOutput,
|
|
52
|
+
contents: databaseTypes
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
path: config.output,
|
|
56
|
+
contents: main
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: metaFiles.js,
|
|
60
|
+
contents: meta.js
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
path: metaFiles.dts,
|
|
64
|
+
contents: meta.dts
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
const owners = new Map(files.map((file) => [resolve(config.root, file.path), "gen"]));
|
|
68
|
+
let view;
|
|
69
|
+
for (const generator of config.generators) {
|
|
70
|
+
let extra;
|
|
71
|
+
try {
|
|
72
|
+
extra = await generator.generate({
|
|
73
|
+
meta: model.meta,
|
|
74
|
+
introspection: model.introspection,
|
|
75
|
+
extras: snapshot.extras,
|
|
76
|
+
config,
|
|
77
|
+
output,
|
|
78
|
+
importPath: (from, to) => importPath(resolve(config.root, from), resolve(config.root, to)),
|
|
79
|
+
model: view ??= generatorModel(model),
|
|
80
|
+
...runtime
|
|
81
|
+
});
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
throw new Error(`generator "${generator.name}" failed: ${errorText(cause)}`, { cause });
|
|
84
|
+
}
|
|
85
|
+
for (const file of extra) {
|
|
86
|
+
const path = resolve(config.root, file.path);
|
|
87
|
+
const inside = relative(config.root, path);
|
|
88
|
+
if (inside === "" || inside.startsWith("..") || isAbsolute(inside)) throw new Error(`generator "${generator.name}" wrote ${file.path}, which is outside the project root`);
|
|
89
|
+
const owner = owners.get(path);
|
|
90
|
+
if (owner !== void 0) throw new Error(`generator "${generator.name}" wrote ${file.path}, which ${owner === "gen" ? "better-supabase gen writes" : `generator "${owner}" also writes`}`);
|
|
91
|
+
owners.set(path, generator.name);
|
|
92
|
+
files.push(file);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
files,
|
|
97
|
+
warnings: model.warnings
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/cli/doctor/shared.ts
|
|
102
|
+
const catalogs = /* @__PURE__ */ new WeakMap();
|
|
103
|
+
/** The snapshot joined per table and function, computed once per snapshot. */
|
|
104
|
+
function catalogOf(context) {
|
|
105
|
+
let catalog = catalogs.get(context.snapshot);
|
|
106
|
+
if (!catalog) {
|
|
107
|
+
catalog = toCatalog(context.snapshot);
|
|
108
|
+
catalogs.set(context.snapshot, catalog);
|
|
109
|
+
}
|
|
110
|
+
return catalog;
|
|
111
|
+
}
|
|
112
|
+
/** `[api] schemas` from `config.toml`, the schemas PostgREST serves, or `config.schemas` without one. */
|
|
113
|
+
function exposedSchemas(context) {
|
|
114
|
+
const listed = context.configToml ? tomlGet(context.configToml.document, ["api", "schemas"]) : void 0;
|
|
115
|
+
return Array.isArray(listed) && listed.every((entry) => typeof entry === "string") ? listed : context.config.schemas;
|
|
116
|
+
}
|
|
117
|
+
const exposed = (context) => {
|
|
118
|
+
const schemas = exposedSchemas(context);
|
|
119
|
+
return catalogOf(context).tables.filter((table) => schemas.includes(table.schema));
|
|
120
|
+
};
|
|
121
|
+
const qualified = (table) => `${table.schema}.${table.name}`;
|
|
122
|
+
const tableObject = (table) => ({
|
|
123
|
+
kind: "table",
|
|
124
|
+
schema: table.schema,
|
|
125
|
+
name: table.name
|
|
126
|
+
});
|
|
127
|
+
const policyObject = (table, policy) => ({
|
|
128
|
+
kind: "policy",
|
|
129
|
+
schema: table.schema,
|
|
130
|
+
name: policy.name,
|
|
131
|
+
table: table.name
|
|
132
|
+
});
|
|
133
|
+
const splitTexts = /* @__PURE__ */ new Map();
|
|
134
|
+
const SPLIT_CACHE_SIZE = 64;
|
|
135
|
+
/** `text` split into lines; doctor locates many objects in the same SQL files. */
|
|
136
|
+
function linesOf(text) {
|
|
137
|
+
let lines = splitTexts.get(text);
|
|
138
|
+
if (!lines) {
|
|
139
|
+
if (splitTexts.size >= SPLIT_CACHE_SIZE) splitTexts.clear();
|
|
140
|
+
lines = text.split("\n");
|
|
141
|
+
splitTexts.set(text, lines);
|
|
142
|
+
}
|
|
143
|
+
return lines;
|
|
144
|
+
}
|
|
145
|
+
/** Line number (1-based) of the first line matching `pattern`. */
|
|
146
|
+
function lineOf(text, pattern) {
|
|
147
|
+
const index = linesOf(text).findIndex((line) => pattern.test(line));
|
|
148
|
+
return index === -1 ? void 0 : index + 1;
|
|
149
|
+
}
|
|
150
|
+
/** The keys of `authorization.permissions`, for `api-keys` scopes `"catalog"`. */
|
|
151
|
+
const permissionCatalogKeys = (context) => context.config.authorization?.permissions?.map((entry) => entry.key);
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/cli/doctor/authorization.ts
|
|
154
|
+
const TEMPLATE_EFFECT = {
|
|
155
|
+
memberIds: "their membership checks read the tenant module's memberships table instead of the provider",
|
|
156
|
+
idsWithFor: "their checks of another user's tenant permissions (can_user, member_can) raise 0A000 (ACCESS_CALLER_ONLY) or are skipped",
|
|
157
|
+
isPlatformFor: "their checks of another user's platform permissions (platform_can) answer false or are skipped",
|
|
158
|
+
canAssignFor: "better_supabase.can_assign_as is not written and the inviter's right to assign the role is not checked again when the invitation is accepted"
|
|
159
|
+
};
|
|
160
|
+
/** The optional templates installed modules call that the provider doesn't set. */
|
|
161
|
+
function missingTemplates(context, provider) {
|
|
162
|
+
const installed = new Set(context.config.sql.moduleNames);
|
|
163
|
+
const callers = /* @__PURE__ */ new Map();
|
|
164
|
+
for (const module of Object.values(SQL_MODULES)) {
|
|
165
|
+
if (!installed.has(module.name)) continue;
|
|
166
|
+
for (const name of module.providerFunctions ?? []) {
|
|
167
|
+
if (provider.functions[name] !== void 0) continue;
|
|
168
|
+
if (name === "canAssignFor" && context.config.sql.modules.access?.functions?.canAssignFor) continue;
|
|
169
|
+
callers.set(name, [...callers.get(name) ?? [], module.name]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...callers].map(([name, modules]) => ({
|
|
173
|
+
severity: "warning",
|
|
174
|
+
message: `The ${modules.join(", ")} ${modules.length > 1 ? "modules call" : "module calls"} authorization.functions.${name}, which ${providerLabel(provider)} doesn't set, so under the provider model ${TEMPLATE_EFFECT[name] ?? "those calls fail"}. Use a provider that sets ${name}${name === "canAssignFor" ? ", or set sql.modules.access.functions.canAssignFor" : ""}.`,
|
|
175
|
+
target: `authorization.functions.${name}`
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
/** Tables whose generated policies call the provider's functions by role and scope only. */
|
|
179
|
+
const POLICY_TABLES = /* @__PURE__ */ new Set(["storage.objects", "realtime.messages"]);
|
|
180
|
+
const POLICY = /create\s+policy\s+("(?:[^"]|"")+"|[\w$]+)\s+on\s+((?:"[^"]+"|\w+)\s*\.\s*(?:"[^"]+"|\w+))([\s\S]*?);/gi;
|
|
181
|
+
const unquote = (name) => name.replaceAll(/^"|"$/g, "").replaceAll("\"\"", "\"");
|
|
182
|
+
const escapeRegExp = (text) => text.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
183
|
+
/**
|
|
184
|
+
* A pattern for a filled template: `{permission}` captures the key literal
|
|
185
|
+
* (optionally passed by name), `{scope}` captures the scope name, and
|
|
186
|
+
* whitespace may differ.
|
|
187
|
+
*/
|
|
188
|
+
function templatePattern(template) {
|
|
189
|
+
const source = template.split(/(\{\w+\})/).map((part) => {
|
|
190
|
+
if (part === "{permission}") return String.raw`(?:\w+\s*=>\s*)?'((?:[^']|'')*)'`;
|
|
191
|
+
if (part === "{scope}") return "([a-z][a-z0-9_]*)";
|
|
192
|
+
if (/^\{\w+\}$/.test(part)) return String.raw`[\s\S]*?`;
|
|
193
|
+
return escapeRegExp(part.trim()).replaceAll(/\s+/g, String.raw`\s*`);
|
|
194
|
+
}).filter((part) => part !== "").join(String.raw`\s*`);
|
|
195
|
+
return new RegExp(source, "gi");
|
|
196
|
+
}
|
|
197
|
+
/** The keys and scopes a policy body passes to the provider's `idsWith` and `isPlatform`. */
|
|
198
|
+
function providerCalls(provider, body) {
|
|
199
|
+
const calls = [];
|
|
200
|
+
const { idsWith, isPlatform } = provider.functions;
|
|
201
|
+
const groups = (template) => [...template.matchAll(/\{(permission|scope)\}/g)].map((match) => match[1]);
|
|
202
|
+
const idsGroups = groups(idsWith);
|
|
203
|
+
for (const match of body.matchAll(templatePattern(idsWith))) {
|
|
204
|
+
const key = match[idsGroups.indexOf("permission") + 1];
|
|
205
|
+
if (key === void 0) continue;
|
|
206
|
+
const scopeIndex = idsGroups.indexOf("scope");
|
|
207
|
+
calls.push({
|
|
208
|
+
key: key.replaceAll("''", "'"),
|
|
209
|
+
scope: scopeIndex === -1 ? provider.tenantScope : match[scopeIndex + 1]
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
const platformGroups = groups(isPlatform);
|
|
213
|
+
for (const match of body.matchAll(templatePattern(isPlatform))) {
|
|
214
|
+
const key = match[platformGroups.indexOf("permission") + 1];
|
|
215
|
+
if (key !== void 0) calls.push({
|
|
216
|
+
key: key.replaceAll("''", "'"),
|
|
217
|
+
scope: "platform"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return calls;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Policies better-supabase generates (`bs_` names) on `storage.objects` and
|
|
224
|
+
* `realtime.messages` that call the provider's templates, with the keys
|
|
225
|
+
* they pass. The first file that declares a policy wins, matching the
|
|
226
|
+
* `sqlFiles` order (declarative schemas, then the newest migration).
|
|
227
|
+
*/
|
|
228
|
+
function providerPolicies(provider, files) {
|
|
229
|
+
const seen = /* @__PURE__ */ new Set();
|
|
230
|
+
const found = [];
|
|
231
|
+
for (const file of files) for (const match of file.text.matchAll(POLICY)) {
|
|
232
|
+
const name = unquote(match[1]);
|
|
233
|
+
const table = match[2].split(".").map((part) => unquote(part.trim())).join(".");
|
|
234
|
+
if (!POLICY_TABLES.has(table) || !name.startsWith("bs_")) continue;
|
|
235
|
+
const id = `${table}.${name}`;
|
|
236
|
+
if (seen.has(id)) continue;
|
|
237
|
+
seen.add(id);
|
|
238
|
+
const calls = providerCalls(provider, match[3]);
|
|
239
|
+
if (calls.length === 0) continue;
|
|
240
|
+
found.push({
|
|
241
|
+
name,
|
|
242
|
+
table,
|
|
243
|
+
calls,
|
|
244
|
+
file: file.path,
|
|
245
|
+
line: file.text.slice(0, match.index).split("\n").length
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return found;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* `buckets` entries whose access policy reaches the provider's functions:
|
|
252
|
+
* through `sql` templates, or through the access contract under the
|
|
253
|
+
* `provider` model. Their keys and scope (the tenant scope for `tenant`).
|
|
254
|
+
*/
|
|
255
|
+
function providerBucketKeys(config) {
|
|
256
|
+
const provider = config.authorization;
|
|
257
|
+
if (!provider) return [];
|
|
258
|
+
const providerModel = config.sql.modules.access?.model === "provider";
|
|
259
|
+
return Object.entries(config.buckets).flatMap(([bucket, entry]) => {
|
|
260
|
+
const policy = entry.policy;
|
|
261
|
+
if (policy === void 0 || typeof policy === "string") return [];
|
|
262
|
+
if (policy.sql === void 0 && !providerModel) return [];
|
|
263
|
+
const scope = policy.scope ?? "tenant";
|
|
264
|
+
return [{
|
|
265
|
+
bucket,
|
|
266
|
+
keys: [...new Set(Object.values(policy.access).filter((key) => typeof key === "string"))],
|
|
267
|
+
scope: scope === "tenant" ? provider.tenantScope : scope
|
|
268
|
+
}];
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Why a policy's scope can't work with the provider: a scope it doesn't
|
|
273
|
+
* declare, or a key it grants only at other scopes.
|
|
274
|
+
*/
|
|
275
|
+
function placementProblems(provider, call, where) {
|
|
276
|
+
if (call.scope === "platform") return [];
|
|
277
|
+
const label = providerLabel(provider);
|
|
278
|
+
if (!provider.scopes.some((scope) => scope.name === call.scope)) return [`${where} uses scope "${call.scope}", which ${label} doesn't declare. Use one of ${provider.scopes.map((scope) => scope.name).join(", ") || "its scopes"}.`];
|
|
279
|
+
const declared = provider.permissions?.find((permission) => permission.key === call.key)?.scopes;
|
|
280
|
+
if (!declared || declared.length === 0 || declared.includes(call.scope)) return [];
|
|
281
|
+
return [`${where} checks "${call.key}" at scope "${call.scope}", but ${label} grants it at ${declared.map((scope) => `"${scope}"`).join(", ")}, so its SQL functions never grant it there. Use scope "${declared[0]}".`];
|
|
282
|
+
}
|
|
283
|
+
/** The provider's memberships for the `entitlements` SQL module, when that mode is valid. */
|
|
284
|
+
function entitlementsModule(context) {
|
|
285
|
+
const mode = entitlementsMode(context.config);
|
|
286
|
+
return mode.kind === "provider" ? mode.provider : void 0;
|
|
287
|
+
}
|
|
288
|
+
/** The provider for the `provider` access model, when `sql.modules` installs `access`. */
|
|
289
|
+
function accessModule(context) {
|
|
290
|
+
if (!resolveModules(context.config.sql.moduleNames, {}).some((module) => module.name === "access")) return { kind: "off" };
|
|
291
|
+
return accessProviderMode(context.config);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Checks a function a template calls against `authorization.requires` and
|
|
295
|
+
* the database: listed, executable by `role`, and present when the
|
|
296
|
+
* snapshot read its schema. `undefined` when it passes.
|
|
297
|
+
*/
|
|
298
|
+
function requirementProblem(context, provider, requirement, caller) {
|
|
299
|
+
const label = providerLabel(provider);
|
|
300
|
+
const listed = (provider.requires ?? []).filter((entry) => entry.function === requirement.function);
|
|
301
|
+
if (provider.requires !== void 0 && listed.length === 0) return { message: `${caller} calls ${requirement.function}, but ${label} doesn't list it in authorization.requires. Use a provider version that lists every function its templates call.` };
|
|
302
|
+
if (listed.length > 0 && !listed.some((entry) => entry.role === requirement.role)) return { message: `${label} says only ${listed.map((entry) => entry.role).join(", ")} may execute ${requirement.function}, but ${caller} calls it as ${requirement.role}. Grant it execute to ${requirement.role} through the provider.` };
|
|
303
|
+
const schema = requirement.function.slice(0, requirement.function.indexOf("."));
|
|
304
|
+
if (!context.snapshot.schemas.includes(schema)) return void 0;
|
|
305
|
+
return catalogOf(context).functions.some((fn) => `${fn.schema}.${fn.name}` === requirement.function) ? void 0 : { message: `${requirement.function} is not in the database, but ${caller} calls it. Apply the provider's migration before the SQL modules.` };
|
|
306
|
+
}
|
|
307
|
+
const AUTHORIZATION_RULES = [
|
|
308
|
+
{
|
|
309
|
+
code: "BS214",
|
|
310
|
+
severity: "error",
|
|
311
|
+
title: "Permission the provider's SQL functions don't fully answer in a Storage or Realtime policy",
|
|
312
|
+
description: "An access policy on a bucket or topic that reaches the authorization provider's functions (through `sql` templates, or through the access contract under the `provider` model) decides by role and scope only. A permission whose `authorization.permissions` entry isn't `sqlComplete: true` would grant every object or topic in the scope, so it is reported, and so is a key the provider doesn't list or every key when it lists no permissions. A scope the provider doesn't declare, and a key checked at another scope than its entry's `scopes`, are reported too. Doctor reads the keys from `buckets` in the config and from the generated `bs_` policies on `storage.objects` and `realtime.messages`. It also warns when a bucket's `sql` templates differ from the provider's `idsWith` and `isPlatform`, since a copy goes stale when the provider changes; `sql: \"provider\"` renders them at `gen` time instead.",
|
|
313
|
+
check: (context) => {
|
|
314
|
+
const provider = context.config.authorization;
|
|
315
|
+
if (!provider) return [];
|
|
316
|
+
const findings = [];
|
|
317
|
+
for (const { bucket, keys, scope } of providerBucketKeys(context.config)) for (const key of keys) {
|
|
318
|
+
for (const message of placementProblems(provider, {
|
|
319
|
+
key,
|
|
320
|
+
scope
|
|
321
|
+
}, `buckets.${bucket} ("${key}")`)) findings.push({
|
|
322
|
+
message,
|
|
323
|
+
target: `buckets.${bucket}:${key}`
|
|
324
|
+
});
|
|
325
|
+
const problem = unsafeKey(provider, key);
|
|
326
|
+
if (!problem) continue;
|
|
327
|
+
findings.push({
|
|
328
|
+
message: `buckets.${bucket} uses "${key}", which ${problem.reason}. The bucket policy could grant every object in the scope. ${problem.fix}`,
|
|
329
|
+
target: `buckets.${bucket}:${key}`
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
for (const [bucket, entry] of Object.entries(context.config.buckets)) {
|
|
333
|
+
const sql = typeof entry.policy === "object" ? entry.policy.sql : void 0;
|
|
334
|
+
if (typeof sql !== "object") continue;
|
|
335
|
+
const stale = ["idsWith", "isPlatform"].filter((name) => sql[name] !== provider.functions[name]);
|
|
336
|
+
if (stale.length === 0) continue;
|
|
337
|
+
findings.push({
|
|
338
|
+
severity: "warning",
|
|
339
|
+
message: `buckets.${bucket} copies sql templates that differ from ${providerLabel(provider)}'s ${stale.join(" and ")}. Set sql: "provider" so gen renders the provider's templates.`,
|
|
340
|
+
target: `buckets.${bucket}.policy.sql`
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
for (const policy of providerPolicies(provider, context.sqlFiles ?? [])) {
|
|
344
|
+
const seen = /* @__PURE__ */ new Set();
|
|
345
|
+
for (const call of policy.calls) for (const message of placementProblems(provider, call, `Policy "${policy.name}" on ${policy.table}`)) {
|
|
346
|
+
if (seen.has(message)) continue;
|
|
347
|
+
seen.add(message);
|
|
348
|
+
findings.push({
|
|
349
|
+
message,
|
|
350
|
+
target: `${policy.table}.${policy.name}:${call.key}`,
|
|
351
|
+
location: {
|
|
352
|
+
file: policy.file,
|
|
353
|
+
line: policy.line
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
for (const key of new Set(policy.calls.map((call) => call.key))) {
|
|
358
|
+
const problem = unsafeKey(provider, key);
|
|
359
|
+
if (!problem) continue;
|
|
360
|
+
findings.push({
|
|
361
|
+
message: `Policy "${policy.name}" on ${policy.table} passes "${key}" to the provider's SQL functions, but it ${problem.reason}. ${problem.fix}`,
|
|
362
|
+
target: `${policy.table}.${policy.name}:${key}`,
|
|
363
|
+
location: {
|
|
364
|
+
file: policy.file,
|
|
365
|
+
line: policy.line
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return findings;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
code: "BS408",
|
|
375
|
+
severity: "warning",
|
|
376
|
+
title: "The entitlements module can't read the provider's memberships",
|
|
377
|
+
description: "With `entitlements.memberships: 'provider'` (the default with `authorization`), the `entitlements` SQL module reads memberships through the provider's `memberIds` template (in `has_entitlement`, as `authenticated`) and `memberIdsFor` (in `feature_claims`, which the provider's hook calls as `supabase_auth_admin`). Doctor warns when the provider can't back that mode (no tenant scope, an id type the module doesn't render, no `memberIds` or `memberIdsFor`), when its hook doesn't fill `claims.features` from `better_supabase.feature_claims` (unless `entitlements.claim` is `false`), when no membership table covers the tenant scope, and when a function those templates call is missing from `authorization.requires`, not executable by that role, or not in the database.",
|
|
378
|
+
check: (context) => {
|
|
379
|
+
if (!context.config.sql.moduleNames.includes("entitlements")) return [];
|
|
380
|
+
const mode = entitlementsMode(context.config);
|
|
381
|
+
if (mode.kind === "tenant") return [];
|
|
382
|
+
if (mode.kind === "invalid") return [{
|
|
383
|
+
message: mode.problem,
|
|
384
|
+
target: "entitlements.memberships"
|
|
385
|
+
}];
|
|
386
|
+
const provider = context.config.authorization;
|
|
387
|
+
const label = providerLabel(provider);
|
|
388
|
+
const findings = [];
|
|
389
|
+
const features = context.config.claims.features;
|
|
390
|
+
const hook = provider.tokenHook;
|
|
391
|
+
if (hook && context.config.entitlements.claim !== false) {
|
|
392
|
+
const claims = hook.registeredClaims ?? [];
|
|
393
|
+
const featureClaim = claims.find((claim) => claim.name === features);
|
|
394
|
+
if (featureClaim?.function !== "better_supabase.feature_claims") {
|
|
395
|
+
const registered = claims.find((claim) => claim.function === "better_supabase.feature_claims");
|
|
396
|
+
findings.push({
|
|
397
|
+
message: registered ? `The hook of ${label} fills the "${registered.name}" claim from better_supabase.feature_claims, but claims.features is "${features}", so hasEntitlement() never finds the plan features. Set claims.features to "${registered.name}".` : featureClaim ? `The hook of ${label} fills the "${features}" claim from ${featureClaim.function}, not better_supabase.feature_claims, so the claim doesn't carry the entitlements module's features. Point the provider's "${features}" claim at better_supabase.feature_claims.` : `The hook of ${label} has no "${features}" claim, so it never writes the plan features hasEntitlement() reads. Have it fill "${features}" from better_supabase.feature_claims, or set \`entitlements.claim: false\` to keep the features out of the token and check them in SQL with has_entitlement.`,
|
|
398
|
+
target: `claims.${features}`
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const scope = mode.provider.scope;
|
|
403
|
+
if (provider.memberships === void 0) findings.push({
|
|
404
|
+
severity: "info",
|
|
405
|
+
message: `${label} lists no authorization.memberships, so entitlement_members() can't tell which tables hold "${scope}" memberships and plan changes never invalidate sessions.`,
|
|
406
|
+
target: "authorization.memberships"
|
|
407
|
+
});
|
|
408
|
+
else if (mode.provider.memberships.length === 0) findings.push({
|
|
409
|
+
message: `${label} maps no membership table to scope "${scope}", so entitlement_members() finds no users and plan changes never invalidate sessions. Add a membership table for "${scope}" to the provider.`,
|
|
410
|
+
target: "authorization.memberships"
|
|
411
|
+
});
|
|
412
|
+
for (const requirement of entitlementRequirements(mode.provider)) {
|
|
413
|
+
const problem = requirementProblem(context, provider, requirement, requirement.caller);
|
|
414
|
+
if (!problem) continue;
|
|
415
|
+
findings.push({
|
|
416
|
+
...requirement.kind === "member-for" && problem.message.includes("may execute") ? { severity: "info" } : {},
|
|
417
|
+
message: problem.message,
|
|
418
|
+
target: requirement.function
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return findings;
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
code: "BS409",
|
|
426
|
+
severity: "warning",
|
|
427
|
+
title: "The authorization provider and the better-supabase config disagree",
|
|
428
|
+
description: "The provider's hook writes the active tenant to `tokenHook.tenantClaim`, and its tenant scope is `tenantScope`. Doctor warns when `claims.tenant` names another claim (the tenant plugin, guards and RLS would read a claim the hook never writes), notes when `claims.scope` is neither `tenant` nor the provider's tenant scope, notes when the provider sets `suspension` or `roleSources` but `sql.modules.access.model` is not `provider` (only that model reads them), and reports the `problems` the provider found while it was built.",
|
|
429
|
+
check: (context) => {
|
|
430
|
+
const provider = context.config.authorization;
|
|
431
|
+
if (!provider) return [];
|
|
432
|
+
const label = providerLabel(provider);
|
|
433
|
+
const findings = (provider.problems ?? []).map((problem) => ({
|
|
434
|
+
message: `${label}: ${problem}`,
|
|
435
|
+
target: "authorization"
|
|
436
|
+
}));
|
|
437
|
+
const tenantClaim = provider.tokenHook?.tenantClaim;
|
|
438
|
+
if (tenantClaim !== void 0 && tenantClaim !== context.config.claims.tenant) findings.push({
|
|
439
|
+
message: `The hook of ${label} writes the active tenant to "${tenantClaim}", but claims.tenant is "${context.config.claims.tenant}". Set claims.tenant to "${tenantClaim}", or change the provider's tenant claim.`,
|
|
440
|
+
target: "claims.tenant"
|
|
441
|
+
});
|
|
442
|
+
const scope = context.config.claims.scope;
|
|
443
|
+
if (scope !== "tenant" && scope !== provider.tenantScope) findings.push({
|
|
444
|
+
severity: "info",
|
|
445
|
+
message: `claims.scope is "${scope}", but the tenant scope of ${label} is "${provider.tenantScope}". The tenant module's membership claims then use another scope than the provider's. Set claims.scope to "${provider.tenantScope}" (or "tenant").`,
|
|
446
|
+
target: "claims.scope"
|
|
447
|
+
});
|
|
448
|
+
const model = context.config.sql.modules.access?.model ?? "roles";
|
|
449
|
+
const unread = [...provider.suspension ? ["suspension"] : [], ...provider.roleSources && provider.roleSources.length > 0 ? ["roleSources"] : []];
|
|
450
|
+
if (model !== "provider" && unread.length > 0) findings.push({
|
|
451
|
+
severity: "info",
|
|
452
|
+
message: `${label} sets ${unread.join(" and ")}, but sql.modules.access.model is "${model}", so the SQL modules ignore ${unread.length > 1 ? "them" : "it"}. Set sql.modules.access.model to "provider" to use them, or set sql.modules.access.disabled yourself.`,
|
|
453
|
+
target: `authorization.${unread[0]}`
|
|
454
|
+
});
|
|
455
|
+
return findings;
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
code: "BS411",
|
|
460
|
+
severity: "error",
|
|
461
|
+
title: "The provider access model can't use the authorization provider",
|
|
462
|
+
description: "With `sql.modules.access.model: 'provider'`, the `access` module fills the provider's `idsWith` template for tenant checks and `isPlatform` for platform checks, at its tenant scope. Doctor reports a config without `authorization`, a tenant scope or id type the module can't render, a function those templates call that `authorization.requires` doesn't list, doesn't let `authenticated` execute or the database lacks, and every module permission key (from `modulePermissionKeys`) the provider doesn't mark `sqlComplete: true`, since its functions are trusted with role and scope only. It warns when neither `sql.modules.access.functions.canAssign` nor the provider's `canAssign` is set, because only the service role then assigns roles, and when an installed module calls a template the provider doesn't set (`idsWithFor` and `isPlatformFor` to check another user, `canAssignFor` to recheck an inviter).",
|
|
463
|
+
check: (context) => {
|
|
464
|
+
const mode = accessModule(context);
|
|
465
|
+
if (mode.kind === "off") return [];
|
|
466
|
+
if (mode.kind === "invalid") return [{
|
|
467
|
+
message: mode.problem,
|
|
468
|
+
target: "authorization"
|
|
469
|
+
}];
|
|
470
|
+
const provider = context.config.authorization;
|
|
471
|
+
const findings = [];
|
|
472
|
+
for (const requirement of accessRequirements(mode.access)) {
|
|
473
|
+
const problem = requirementProblem(context, provider, requirement, "the provider access model (can() and the SQL modules)");
|
|
474
|
+
if (problem) findings.push({
|
|
475
|
+
message: problem.message,
|
|
476
|
+
target: requirement.function
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
findings.push(...moduleKeyProblems(provider, modulePermissionKeys(context.config.sql.modules, context.config.sql.moduleNames)));
|
|
480
|
+
if ((context.config.sql.modules.access?.functions?.canAssign ?? provider.functions.canAssign) === void 0) findings.push({
|
|
481
|
+
severity: "warning",
|
|
482
|
+
message: `Neither sql.modules.access.functions.canAssign nor ${providerLabel(provider)} sets canAssign, so under the provider model only the service role assigns roles: the memberships guard refuses every membership a member adds or changes. Use a provider that sets authorization.functions.canAssign, or set sql.modules.access.functions.canAssign.`,
|
|
483
|
+
target: "sql.modules.access.functions.canAssign"
|
|
484
|
+
});
|
|
485
|
+
findings.push(...missingTemplates(context, provider));
|
|
486
|
+
return findings;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
];
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region src/cli/gen/manifest.ts
|
|
492
|
+
/** The files the last `gen` wrote, so the next one can remove those it no longer writes. */
|
|
493
|
+
const MANIFEST = `${CACHE_DIR}/gen-manifest.json`;
|
|
494
|
+
const manifestPath = (root, path) => relative(root, resolve(root, path)).split(sep).join("/");
|
|
495
|
+
async function readManifest(root) {
|
|
496
|
+
const parsed = await readFile(resolve(root, MANIFEST), "utf8").then((text) => JSON.parse(text)).catch(() => void 0);
|
|
497
|
+
if (typeof parsed !== "object" || parsed === null || !("version" in parsed) || parsed.version !== 1 || !("files" in parsed) || !Array.isArray(parsed.files)) return [];
|
|
498
|
+
return parsed.files.filter((path) => typeof path === "string");
|
|
499
|
+
}
|
|
500
|
+
/** Files in the last manifest that this run doesn't generate and that still exist inside the root. */
|
|
501
|
+
async function leftoverFiles(root, generated) {
|
|
502
|
+
const current = new Set(generated);
|
|
503
|
+
return (await readManifest(root)).filter((path) => {
|
|
504
|
+
const inside = relative(root, resolve(root, path));
|
|
505
|
+
return !current.has(path) && inside !== "" && !inside.startsWith("..") && !isAbsolute(inside) && existsSync(resolve(root, path));
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
const writeManifest = (root, files) => writeAtomic(resolve(root, MANIFEST), `${JSON.stringify({
|
|
509
|
+
version: 1,
|
|
510
|
+
files: [...files].sort()
|
|
511
|
+
}, null, 2)}\n`).catch(() => void 0);
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/cli/introspect/from-metadata.ts
|
|
514
|
+
/** What the extras query set reads that a `GeneratorMetadata` document leaves out. */
|
|
515
|
+
const METADATA_GAPS = [
|
|
516
|
+
"multi-column unique keys and checks",
|
|
517
|
+
"foreign key actions",
|
|
518
|
+
"indexes",
|
|
519
|
+
"triggers",
|
|
520
|
+
"policies",
|
|
521
|
+
"grants",
|
|
522
|
+
"storage buckets",
|
|
523
|
+
"the realtime publication"
|
|
524
|
+
];
|
|
525
|
+
/** `METADATA_GAPS` as one phrase for notices. */
|
|
526
|
+
const METADATA_GAPS_TEXT = `${METADATA_GAPS.slice(0, -1).join(", ")} or ${METADATA_GAPS.at(-1) ?? ""}`;
|
|
527
|
+
/**
|
|
528
|
+
* Builds a snapshot from a `GeneratorMetadata` document alone, for
|
|
529
|
+
* `gen --metadata`. Relations come from `relationships`, single-column unique
|
|
530
|
+
* keys from `is_unique` and single-column checks from `check`, named as
|
|
531
|
+
* Postgres names them by default (`<table>_<column>_key`, `<table>_<column>_check`).
|
|
532
|
+
* Everything else the extras carry is empty, and `extras.fromMetadata` says so.
|
|
533
|
+
*/
|
|
534
|
+
function fromMetadata(document) {
|
|
535
|
+
const { metadata } = stabilizeMetadata(document);
|
|
536
|
+
const tableIds = new Set(metadata.tables.map((table) => table.id));
|
|
537
|
+
const tables = [
|
|
538
|
+
...metadata.tables,
|
|
539
|
+
...metadata.foreignTables,
|
|
540
|
+
...metadata.views,
|
|
541
|
+
...metadata.materializedViews
|
|
542
|
+
].map((relation) => {
|
|
543
|
+
const columns = metadata.columns.filter((column) => column.table_id === relation.id).sort((a, b) => a.ordinal_position - b.ordinal_position);
|
|
544
|
+
const own = tableIds.has(relation.id);
|
|
545
|
+
return {
|
|
546
|
+
id: relation.id,
|
|
547
|
+
schema: relation.schema,
|
|
548
|
+
name: relation.name,
|
|
549
|
+
primaryKey: [],
|
|
550
|
+
uniques: own ? columns.filter((column) => column.is_unique).map((column) => ({
|
|
551
|
+
name: `${relation.name}_${column.name}_key`,
|
|
552
|
+
columns: [column.name]
|
|
553
|
+
})) : [],
|
|
554
|
+
foreignKeys: own ? metadata.relationships.filter((fk) => fk.schema === relation.schema && fk.relation === relation.name).map((fk) => ({
|
|
555
|
+
name: fk.foreign_key_name,
|
|
556
|
+
onDelete: "no action",
|
|
557
|
+
onUpdate: "no action"
|
|
558
|
+
})).sort((a, b) => byCodePoint(a.name, b.name)) : [],
|
|
559
|
+
checks: own ? columns.flatMap((column) => column.check === null ? [] : [{
|
|
560
|
+
name: `${relation.name}_${column.name}_check`,
|
|
561
|
+
definition: `CHECK ((${column.check}))`
|
|
562
|
+
}]) : [],
|
|
563
|
+
indexes: [],
|
|
564
|
+
policies: [],
|
|
565
|
+
triggers: [],
|
|
566
|
+
grants: []
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
return {
|
|
570
|
+
version: 2,
|
|
571
|
+
schemas: [...new Set(metadata.schemas.map((schema) => schema.name))].sort(byCodePoint),
|
|
572
|
+
generator: metadata,
|
|
573
|
+
extras: {
|
|
574
|
+
tables,
|
|
575
|
+
buckets: [],
|
|
576
|
+
realtime: [],
|
|
577
|
+
fromMetadata: true
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region src/cli/gen/notices.ts
|
|
583
|
+
const UNFORMATTED = "oxfmt is not installed, so database.types.ts is not formatted like `supabase gen types` output. Install it: pnpm add -D oxfmt. @supabase/postgrest-typegen pins oxfmt 0.66.0 as its peer; to allow a newer one, add it to peerDependencyRules.allowedVersions in pnpm-workspace.yaml, keyed \"@supabase/postgrest-typegen>oxfmt\".";
|
|
584
|
+
const METADATA_ONLY = `Notice: the schema came from a GeneratorMetadata document, which has no ${METADATA_GAPS_TEXT}. Unique keys and checks cover single columns, relations have no onDelete, and columns a before-insert trigger fills stay required on insert. Run \`better-supabase gen\` against the database for the full model.`;
|
|
585
|
+
/** The formatter, typegen version and metadata-only notices and the config warnings, one line each. */
|
|
586
|
+
async function generationNotices(snapshot, warnings) {
|
|
587
|
+
const typegen = typegenVersionNotice(await typegenVersion());
|
|
588
|
+
return [
|
|
589
|
+
...await oxfmtInstalled() ? [] : [UNFORMATTED],
|
|
590
|
+
...typegen ? [typegen] : [],
|
|
591
|
+
...snapshot.extras.fromMetadata ? [METADATA_ONLY] : [],
|
|
592
|
+
...warnings.map((warning) => `Warning: ${warning}`)
|
|
593
|
+
];
|
|
594
|
+
}
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/cli/gen/run.ts
|
|
597
|
+
/** The `read-sets` SQL module file for `config.readSets`, if any are configured. */
|
|
598
|
+
async function readSetFile(config) {
|
|
599
|
+
if (config.readSets.length === 0) return void 0;
|
|
600
|
+
const readSets = await compiledReadSets(config);
|
|
601
|
+
return renderModules(["read-sets"], moduleLayout(config, config.sql.testsDir, readSets))[0];
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* `buckets` keys that reach the authorization provider's functions but
|
|
605
|
+
* aren't marked `sqlComplete: true` in `authorization.permissions`, as
|
|
606
|
+
* error lines.
|
|
607
|
+
*/
|
|
608
|
+
function rowConditionedBuckets(config) {
|
|
609
|
+
const provider = config.authorization;
|
|
610
|
+
if (!provider) return [];
|
|
611
|
+
return providerBucketKeys(config).flatMap(({ bucket, keys }) => keys.flatMap((key) => {
|
|
612
|
+
const problem = unsafeKey(provider, key);
|
|
613
|
+
return problem ? [` buckets.${bucket}: "${key}" ${problem.reason}. ${problem.fix}`] : [];
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
/** The exit `gen` stops with when bucket keys aren't fully answered by the provider's SQL functions. */
|
|
617
|
+
function refuseRowConditionedBuckets(config) {
|
|
618
|
+
const refused = rowConditionedBuckets(config);
|
|
619
|
+
return refused.length === 0 || !config.authorization ? void 0 : {
|
|
620
|
+
code: 1,
|
|
621
|
+
error: `The SQL functions of ${providerLabel(config.authorization)} decide by role and scope only, so these bucket policies could grant every object in the scope:\n${refused.join("\n")}\nSee doctor BS214.`
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
async function runGen(options) {
|
|
625
|
+
const { config } = options;
|
|
626
|
+
const refused = refuseRowConditionedBuckets(config);
|
|
627
|
+
if (refused) return refused;
|
|
628
|
+
const snapshot = options.snapshot ?? await loadSnapshot(config, options.env, options);
|
|
629
|
+
const { files, warnings } = await render(config, snapshot);
|
|
630
|
+
const pathsOf = (readSets) => [...files, ...readSets ? [readSets] : []].map((file) => manifestPath(config.root, file.path));
|
|
631
|
+
const notice = (await generationNotices(snapshot, warnings)).map((line) => `\n${line}`).join("");
|
|
632
|
+
const readSetsOf = () => options.isolated ? Promise.resolve(void 0) : readSetFile(config);
|
|
633
|
+
const leftoversOf = (generated) => options.isolated ? Promise.resolve([]) : leftoverFiles(config.root, generated);
|
|
634
|
+
if (options.check) {
|
|
635
|
+
const readSets = await readSetsOf();
|
|
636
|
+
const generated = pathsOf(readSets);
|
|
637
|
+
const leftovers = await leftoversOf(generated);
|
|
638
|
+
const stale = [];
|
|
639
|
+
const diffs = [];
|
|
640
|
+
const compare = (file, current) => {
|
|
641
|
+
const shown = display(config.root, file.path);
|
|
642
|
+
stale.push(shown);
|
|
643
|
+
diffs.push(fileDiff(shown, current, file.contents, options.paint));
|
|
644
|
+
};
|
|
645
|
+
for (const file of files) {
|
|
646
|
+
const path = resolve(config.root, file.path);
|
|
647
|
+
const current = existsSync(path) ? await readFile(path, "utf8") : void 0;
|
|
648
|
+
if (!sameText(current, file.contents)) compare(file, current);
|
|
649
|
+
}
|
|
650
|
+
if (readSets) {
|
|
651
|
+
const path = resolve(config.root, readSets.path);
|
|
652
|
+
const current = existsSync(path) ? await readFile(path, "utf8") : void 0;
|
|
653
|
+
if (!sameModuleFile(current, readSets.contents)) compare(readSets, current);
|
|
654
|
+
}
|
|
655
|
+
if (stale.length > 0 || leftovers.length > 0) {
|
|
656
|
+
const lines = [...stale.map((path) => ` ${path}`), ...leftovers.map((path) => ` ${path} (no longer generated)`)];
|
|
657
|
+
return {
|
|
658
|
+
code: 1,
|
|
659
|
+
output: `${diffs.join("\n\n")}${notice}`,
|
|
660
|
+
error: `Generated files are out of date:\n${lines.join("\n")}\nRun \`better-supabase gen\`.`,
|
|
661
|
+
data: {
|
|
662
|
+
upToDate: false,
|
|
663
|
+
stale,
|
|
664
|
+
leftovers,
|
|
665
|
+
warnings
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
code: 0,
|
|
671
|
+
output: `Generated files are up to date (${generated.length}).${notice}`,
|
|
672
|
+
data: {
|
|
673
|
+
upToDate: true,
|
|
674
|
+
stale: [],
|
|
675
|
+
leftovers: [],
|
|
676
|
+
warnings
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
const written = [];
|
|
681
|
+
for (const file of files) if (await writeIfChanged(resolve(config.root, file.path), file.contents)) written.push(display(config.root, file.path));
|
|
682
|
+
const readSets = await readSetsOf();
|
|
683
|
+
if (readSets && await writeIfChanged(resolve(config.root, readSets.path), readSets.contents)) written.push(display(config.root, readSets.path));
|
|
684
|
+
const generated = pathsOf(readSets);
|
|
685
|
+
const removed = [];
|
|
686
|
+
for (const path of await leftoversOf(generated)) {
|
|
687
|
+
await rm(resolve(config.root, path), { force: true });
|
|
688
|
+
removed.push(path);
|
|
689
|
+
}
|
|
690
|
+
if (!options.isolated) await writeManifest(config.root, generated);
|
|
691
|
+
const tables = snapshot.extras.tables.filter((table) => config.schemas.includes(table.schema)).length;
|
|
692
|
+
const changes = [...written.map((path) => ` ${path}`), ...removed.map((path) => ` ${path} (removed, no longer generated)`)];
|
|
693
|
+
return {
|
|
694
|
+
code: 0,
|
|
695
|
+
output: `${changes.length === 0 ? `No changes (${tables} tables).` : `Generated ${tables} tables:\n${changes.join("\n")}`}${notice}`,
|
|
696
|
+
data: {
|
|
697
|
+
tables,
|
|
698
|
+
written,
|
|
699
|
+
removed,
|
|
700
|
+
warnings
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
//#endregion
|
|
705
|
+
export { tableObject as _, METADATA_GAPS_TEXT as a, accessModule as c, exposed as d, exposedSchemas as f, qualified as g, policyObject as h, generationNotices as i, entitlementsModule as l, permissionCatalogKeys as m, runGen as n, fromMetadata as o, lineOf as p, UNFORMATTED as r, AUTHORIZATION_RULES as s, refuseRowConditionedBuckets as t, catalogOf as u, render as v, renderFiles as y };
|