@keboola/api-client 39.0.0 → 41.0.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/AGENTS.md +121 -100
- package/API-SURFACE.md +27 -2
- package/CHANGELOG.md +129 -0
- package/README.md +3 -3
- package/dist/{ManagementClientAuthError-FcfMJNhr.cjs → ManagementClientAuthError-DE7lNGZQ.cjs} +2 -2
- package/dist/{ManagementClientAuthError-FcfMJNhr.cjs.map → ManagementClientAuthError-DE7lNGZQ.cjs.map} +1 -1
- package/dist/{ManagementClientAuthError-Cr9IOsnJ.js → ManagementClientAuthError-H30lti_Q.js} +2 -2
- package/dist/{ManagementClientAuthError-Cr9IOsnJ.js.map → ManagementClientAuthError-H30lti_Q.js.map} +1 -1
- package/dist/{ValidationError-CLVJCCwH.cjs → ValidationError-D65QU0uq.cjs} +10 -16
- package/dist/ValidationError-D65QU0uq.cjs.map +1 -0
- package/dist/ValidationError-DjhtNMfZ.js +42 -0
- package/dist/ValidationError-DjhtNMfZ.js.map +1 -0
- package/dist/ai/index.cjs +2 -2
- package/dist/ai/index.d.cts +2 -2
- package/dist/ai/index.d.ts +2 -2
- package/dist/ai/index.js +2 -2
- package/dist/ai/types.d.cts +1 -1
- package/dist/ai/types.d.ts +1 -1
- package/dist/{appLogs-C_zP0lpv.cjs → appLogs-B9zFJIuy.cjs} +2 -2
- package/dist/{appLogs-C_zP0lpv.cjs.map → appLogs-B9zFJIuy.cjs.map} +1 -1
- package/dist/{appLogs-Cl7Q_MR9.js → appLogs-BBkkUOW1.js} +2 -2
- package/dist/{appLogs-Cl7Q_MR9.js.map → appLogs-BBkkUOW1.js.map} +1 -1
- package/dist/{appLogs-Ch7DYpzx.d.ts → appLogs-BS6ViVGR.d.ts} +2 -2
- package/dist/{appLogs-D5A13Y0y.d.cts → appLogs-C9YlJ2g8.d.cts} +2 -2
- package/dist/apps/index.cjs +30 -56
- package/dist/apps/index.cjs.map +1 -1
- package/dist/apps/index.d.cts +8 -13
- package/dist/apps/index.d.ts +8 -13
- package/dist/apps/index.js +30 -56
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/types.d.cts +2 -2
- package/dist/apps/types.d.ts +2 -2
- package/dist/assets/index.cjs +1 -1
- package/dist/assets/index.d.cts +1 -1
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/index.js +1 -1
- package/dist/auth/index.cjs +8 -4
- package/dist/auth/index.d.cts +2 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +2 -2
- package/dist/auth/types.d.cts +2 -2
- package/dist/auth/types.d.ts +2 -2
- package/dist/{auth-DLWnl18b.cjs → auth-Dm0F2rOh.cjs} +2 -2
- package/dist/{auth-DLWnl18b.cjs.map → auth-Dm0F2rOh.cjs.map} +1 -1
- package/dist/{auth-BjmkFOgd.js → auth-DworfoPy.js} +2 -2
- package/dist/{auth-BjmkFOgd.js.map → auth-DworfoPy.js.map} +1 -1
- package/dist/{authClient-8yU1last.cjs → authClient-BmuUo0J4.cjs} +141 -48
- package/dist/authClient-BmuUo0J4.cjs.map +1 -0
- package/dist/{authClient-CTHNs6Tq.d.cts → authClient-CHXWJ9x3.d.ts} +109 -76
- package/dist/{authClient-BTo4Afm6.js → authClient-CgcDGd30.js} +108 -39
- package/dist/authClient-CgcDGd30.js.map +1 -0
- package/dist/{authClient-D8i3JTM-.d.ts → authClient-vEt_7_uk.d.cts} +109 -76
- package/dist/billing/index.cjs +2 -2
- package/dist/billing/index.d.cts +1 -1
- package/dist/billing/index.d.ts +1 -1
- package/dist/billing/index.js +2 -2
- package/dist/billing/types.d.cts +1 -1
- package/dist/billing/types.d.ts +1 -1
- package/dist/businessWorkspaceApi/index.cjs +1 -1
- package/dist/businessWorkspaceApi/index.d.cts +1 -1
- package/dist/businessWorkspaceApi/index.d.ts +1 -1
- package/dist/businessWorkspaceApi/index.js +1 -1
- package/dist/businessWorkspaceApi/types.d.cts +1 -1
- package/dist/businessWorkspaceApi/types.d.ts +1 -1
- package/dist/{businessWorkspaceApi-wyBdbpIJ.cjs → businessWorkspaceApi-6tCDdpnY.cjs} +3 -3
- package/dist/{businessWorkspaceApi-wyBdbpIJ.cjs.map → businessWorkspaceApi-6tCDdpnY.cjs.map} +1 -1
- package/dist/{businessWorkspaceApi-BD-iUj07.js → businessWorkspaceApi-BLUdefRF.js} +3 -3
- package/dist/{businessWorkspaceApi-BD-iUj07.js.map → businessWorkspaceApi-BLUdefRF.js.map} +1 -1
- package/dist/{businessWorkspaceApiClient-CRM1U8b-.d.cts → businessWorkspaceApiClient-BHDJlHFo.d.cts} +3 -3
- package/dist/{businessWorkspaceApiClient-C2WyWN3b.d.ts → businessWorkspaceApiClient-DI-z1VIH.d.ts} +3 -3
- package/dist/chat/index.cjs +7 -3
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +2 -2
- package/dist/chat/index.d.ts +2 -2
- package/dist/chat/index.js +7 -3
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/types.cjs +1 -3
- package/dist/chat/types.cjs.map +1 -1
- package/dist/chat/types.d.cts +2 -2
- package/dist/chat/types.d.ts +2 -2
- package/dist/chat/types.js +1 -3
- package/dist/chat/types.js.map +1 -1
- package/dist/{componentId-vJz-kWs6.js → componentId-BJLfnaGc.js} +6 -2
- package/dist/componentId-BJLfnaGc.js.map +1 -0
- package/dist/{componentId-DkyQ8PM0.cjs → componentId-_IKXxICq.cjs} +6 -2
- package/dist/componentId-_IKXxICq.cjs.map +1 -0
- package/dist/{concurrent-CH9fbNr3.js → concurrent-BacWxPwi.js} +5 -7
- package/dist/concurrent-BacWxPwi.js.map +1 -0
- package/dist/{concurrent-tbiIa0UL.cjs → concurrent-CdVfR1N5.cjs} +5 -7
- package/dist/concurrent-CdVfR1N5.cjs.map +1 -0
- package/dist/{configurations-CyFF9Dtu.cjs → configurations-BTRHVSRq.cjs} +20 -53
- package/dist/configurations-BTRHVSRq.cjs.map +1 -0
- package/dist/{configurations-VtNE0nrT.js → configurations-CF3xz7RB.js} +14 -22
- package/dist/configurations-CF3xz7RB.js.map +1 -0
- package/dist/{configurations-BVvcimzo.js → configurations-Chg_JwJ1.js} +20 -53
- package/dist/configurations-Chg_JwJ1.js.map +1 -0
- package/dist/{configurations-zkNjr73Q.cjs → configurations-VlfQ_37Y.cjs} +14 -22
- package/dist/configurations-VlfQ_37Y.cjs.map +1 -0
- package/dist/constants/index.cjs +1 -1
- package/dist/constants/index.d.cts +1 -1
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/{createFetchClient-BlgPbjFj.cjs → createFetchClient-Cwp8max9.cjs} +11 -4
- package/dist/createFetchClient-Cwp8max9.cjs.map +1 -0
- package/dist/{createFetchClient-C-cadpBx.js → createFetchClient-DXHQK63w.js} +11 -4
- package/dist/createFetchClient-DXHQK63w.js.map +1 -0
- package/dist/{createGenericFetchClient-Bnf1QK9W.js → createGenericFetchClient-Dg1rgzmG.js} +2 -2
- package/dist/{createGenericFetchClient-Bnf1QK9W.js.map → createGenericFetchClient-Dg1rgzmG.js.map} +1 -1
- package/dist/{createGenericFetchClient-lcozzwk2.cjs → createGenericFetchClient-Dl9cqTvR.cjs} +2 -2
- package/dist/{createGenericFetchClient-lcozzwk2.cjs.map → createGenericFetchClient-Dl9cqTvR.cjs.map} +1 -1
- package/dist/{createOpenapiFetchClient-C3UmDXpe.cjs → createOpenapiFetchClient-BYqW-9tm.cjs} +2 -2
- package/dist/{createOpenapiFetchClient-C3UmDXpe.cjs.map → createOpenapiFetchClient-BYqW-9tm.cjs.map} +1 -1
- package/dist/{createOpenapiFetchClient-COaPeeQ0.js → createOpenapiFetchClient-DZUOVupy.js} +2 -2
- package/dist/{createOpenapiFetchClient-COaPeeQ0.js.map → createOpenapiFetchClient-DZUOVupy.js.map} +1 -1
- package/dist/dataScience/index.cjs +2 -2
- package/dist/dataScience/index.d.cts +2 -2
- package/dist/dataScience/index.d.ts +2 -2
- package/dist/dataScience/index.js +2 -2
- package/dist/dataScience/types.d.cts +2 -2
- package/dist/dataScience/types.d.ts +2 -2
- package/dist/{dataScience-D-hyc4GY.cjs → dataScience-B5QxZnvW.cjs} +5 -5
- package/dist/{dataScience-D-hyc4GY.cjs.map → dataScience-B5QxZnvW.cjs.map} +1 -1
- package/dist/{dataScience-B_BmnB1m.js → dataScience-OXcwdk1p.js} +5 -5
- package/dist/{dataScience-B_BmnB1m.js.map → dataScience-OXcwdk1p.js.map} +1 -1
- package/dist/dedupe-Cky1gSUu.js +21 -0
- package/dist/dedupe-Cky1gSUu.js.map +1 -0
- package/dist/dedupe-OSnnst2u.cjs +26 -0
- package/dist/dedupe-OSnnst2u.cjs.map +1 -0
- package/dist/developerPortal/index.cjs +1 -1
- package/dist/developerPortal/index.d.cts +2 -2
- package/dist/developerPortal/index.d.ts +2 -2
- package/dist/developerPortal/index.js +1 -1
- package/dist/developerPortal/types.d.cts +1 -1
- package/dist/developerPortal/types.d.ts +1 -1
- package/dist/{developerPortalClient-yHFTkKPb.js → developerPortalClient-B1QZ1rYi.js} +6 -8
- package/dist/{developerPortalClient-yHFTkKPb.js.map → developerPortalClient-B1QZ1rYi.js.map} +1 -1
- package/dist/{developerPortalClient-CutPqoAa.cjs → developerPortalClient-CLPpBksD.cjs} +6 -8
- package/dist/{developerPortalClient-CutPqoAa.cjs.map → developerPortalClient-CLPpBksD.cjs.map} +1 -1
- package/dist/domain/permissions/index.cjs +9 -30
- package/dist/domain/permissions/index.cjs.map +1 -1
- package/dist/domain/permissions/index.d.cts +29 -102
- package/dist/domain/permissions/index.d.ts +29 -102
- package/dist/domain/permissions/index.js +9 -30
- package/dist/domain/permissions/index.js.map +1 -1
- package/dist/domain/storageTable/index.d.cts +1 -1
- package/dist/domain/storageTable/index.d.ts +1 -1
- package/dist/editor/index.cjs +2 -2
- package/dist/editor/index.cjs.map +1 -1
- package/dist/editor/index.d.cts +2 -2
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.js +2 -2
- package/dist/editor/index.js.map +1 -1
- package/dist/editor/types.d.cts +1 -1
- package/dist/editor/types.d.ts +1 -1
- package/dist/encryption/index.cjs +2 -22
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +1 -1
- package/dist/encryption/index.d.ts +1 -1
- package/dist/encryption/index.js +2 -22
- package/dist/encryption/index.js.map +1 -1
- package/dist/encryption/types.d.cts +1 -1
- package/dist/encryption/types.d.ts +1 -1
- package/dist/import/index.cjs +1 -1
- package/dist/import/index.d.cts +3 -3
- package/dist/import/index.d.ts +3 -3
- package/dist/import/index.js +1 -1
- package/dist/{import-7YyyD_U_.cjs → import-CNk89mqy.cjs} +3 -3
- package/dist/{import-7YyyD_U_.cjs.map → import-CNk89mqy.cjs.map} +1 -1
- package/dist/{import-xliONVib.js → import-DiMQCzLa.js} +3 -3
- package/dist/{import-xliONVib.js.map → import-DiMQCzLa.js.map} +1 -1
- package/dist/{index-1r_CskjU.d.ts → index--ZpnE3YT.d.cts} +3 -3
- package/dist/{index-DBklbaTp.d.cts → index-B5w7WlaU.d.cts} +5 -1
- package/dist/{index-DBklbaTp.d.ts → index-B5w7WlaU.d.ts} +5 -1
- package/dist/{index-C77vTm2K.d.ts → index-BDZvaRud.d.ts} +6 -6
- package/dist/{index-KuQ79Mnz.d.ts → index-BVhHt5lx.d.cts} +18 -26
- package/dist/{index-NC1dXRTD.d.ts → index-BfqkgPGI.d.cts} +7 -13
- package/dist/{index-NC1dXRTD.d.cts → index-BfqkgPGI.d.ts} +7 -13
- package/dist/{index-V7XKUQm9.d.cts → index-BgR5zcjJ.d.ts} +18 -26
- package/dist/{index-BK-qHT39.d.ts → index-ByRpNVcs.d.cts} +83 -17
- package/dist/{index-pXN4LWUX.d.ts → index-C11JSxBY.d.ts} +8 -11
- package/dist/{index-GjcJTCbz.d.cts → index-C4TNAA8-.d.cts} +8 -11
- package/dist/{index-BMqLPS6S.d.ts → index-CXcWpZQ2.d.cts} +4 -12
- package/dist/{index-BMqLPS6S.d.cts → index-CXcWpZQ2.d.ts} +4 -12
- package/dist/{index-GfKzh267.d.ts → index-CaaTOANF.d.ts} +395 -297
- package/dist/{index-Dx52hNLP.d.cts → index-CcMHJcuP.d.cts} +3 -3
- package/dist/{index-DVw4Av4b.d.cts → index-Cvbjp3jx.d.cts} +6 -6
- package/dist/{index-DppA4k3b.d.cts → index-Cz0MXhzs.d.cts} +5 -5
- package/dist/{index-6Xe81qN3.d.ts → index-D-VMgpGc.d.ts} +5 -5
- package/dist/{index-DWf6FXOr.d.cts → index-D7-o-oGa.d.ts} +3 -3
- package/dist/{index-CWoEnDJ_.d.cts → index-DCRKjihs.d.ts} +83 -17
- package/dist/{kaiAgentClient-CHpULyeG.d.cts → index-DNThyEej.d.cts} +14 -5
- package/dist/{index-DXSmFkol.d.cts → index-DOAZmWvh.d.cts} +6 -6
- package/dist/{index-BuegqrS1.d.ts → index-D_-4uQF8.d.ts} +3 -3
- package/dist/index-DbLFwXgb.d.cts +116 -0
- package/dist/{index-De5qUWhp.d.ts → index-DnQTuc6t.d.ts} +6 -6
- package/dist/{kaiAgentClient-B5L4gbQ1.d.ts → index-Qb7X6-ph.d.ts} +14 -5
- package/dist/{index-CHXnJQ9s.d.cts → index-cWp_V6K5.d.cts} +395 -297
- package/dist/index-rwK7GcB2.d.ts +116 -0
- package/dist/index.cjs +1498 -901
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +1497 -902
- package/dist/index.js.map +1 -1
- package/dist/isLoginPageRedirect-XPwAOKK3.js +14 -0
- package/dist/isLoginPageRedirect-XPwAOKK3.js.map +1 -0
- package/dist/isLoginPageRedirect-jQHiLrG7.cjs +19 -0
- package/dist/isLoginPageRedirect-jQHiLrG7.cjs.map +1 -0
- package/dist/{jobStatus-0_Y3Sr4i.cjs → jobStatus-BMlsyXWI.cjs} +19 -45
- package/dist/jobStatus-BMlsyXWI.cjs.map +1 -0
- package/dist/{jobStatus-PSZJMTiA.js → jobStatus-CfRtsIpv.js} +19 -45
- package/dist/jobStatus-CfRtsIpv.js.map +1 -0
- package/dist/kaiAgent/index.cjs +1 -1
- package/dist/kaiAgent/index.d.cts +3 -2
- package/dist/kaiAgent/index.d.ts +3 -2
- package/dist/kaiAgent/index.js +1 -1
- package/dist/kaiAgent/types.d.cts +2 -2
- package/dist/kaiAgent/types.d.ts +2 -2
- package/dist/{kaiAgent-CcT2oNip.js → kaiAgent-Dp20HLTh.js} +32 -11
- package/dist/kaiAgent-Dp20HLTh.js.map +1 -0
- package/dist/{kaiAgent-DaB6UCsh.cjs → kaiAgent-QNoeVKPs.cjs} +32 -11
- package/dist/kaiAgent-QNoeVKPs.cjs.map +1 -0
- package/dist/kaiMemory-CClSPInt.cjs +289 -0
- package/dist/kaiMemory-CClSPInt.cjs.map +1 -0
- package/dist/kaiMemory-D0Y_16U4.js +260 -0
- package/dist/kaiMemory-D0Y_16U4.js.map +1 -0
- package/dist/management/index.cjs +1 -1
- package/dist/management/index.d.cts +1 -1
- package/dist/management/index.d.ts +1 -1
- package/dist/management/index.js +1 -1
- package/dist/management/types.d.cts +2 -2
- package/dist/management/types.d.ts +2 -2
- package/dist/{management-DeZiQSlw.cjs → management-CCY0IYnq.cjs} +59 -24
- package/dist/management-CCY0IYnq.cjs.map +1 -0
- package/dist/{management-ckdnu1dA.js → management-DvmPiG64.js} +59 -24
- package/dist/management-DvmPiG64.js.map +1 -0
- package/dist/metastore/index.cjs +1 -1
- package/dist/metastore/index.d.cts +1 -1
- package/dist/metastore/index.d.ts +1 -1
- package/dist/metastore/index.js +1 -1
- package/dist/metastore/types.d.cts +2 -2
- package/dist/metastore/types.d.ts +2 -2
- package/dist/{metastore-B0PolCjQ.js → metastore-BycJxe7A.js} +14 -8
- package/dist/metastore-BycJxe7A.js.map +1 -0
- package/dist/{metastore-9AGrwjmN.cjs → metastore-XtWciCZT.cjs} +14 -8
- package/dist/metastore-XtWciCZT.cjs.map +1 -0
- package/dist/microsoftGraph/index.cjs +2 -2
- package/dist/microsoftGraph/index.d.cts +2 -2
- package/dist/microsoftGraph/index.d.ts +2 -2
- package/dist/microsoftGraph/index.js +2 -2
- package/dist/microsoftGraph/types.d.cts +1 -1
- package/dist/microsoftGraph/types.d.ts +1 -1
- package/dist/middleware-CBwrtgQd.js +34 -0
- package/dist/middleware-CBwrtgQd.js.map +1 -0
- package/dist/middleware-IjYx3d5u.cjs +45 -0
- package/dist/middleware-IjYx3d5u.cjs.map +1 -0
- package/dist/notifications/index.cjs +4 -4
- package/dist/notifications/index.d.cts +2 -2
- package/dist/notifications/index.d.ts +2 -2
- package/dist/notifications/index.js +4 -4
- package/dist/notifications/types.d.cts +1 -1
- package/dist/notifications/types.d.ts +1 -1
- package/dist/oauth/index.cjs +1 -1
- package/dist/oauth/index.d.cts +2 -2
- package/dist/oauth/index.d.ts +2 -2
- package/dist/oauth/index.js +1 -1
- package/dist/oauth/types.d.cts +1 -1
- package/dist/oauth/types.d.ts +1 -1
- package/dist/{oauth-DNtv4Ihb.cjs → oauth--2p8jkJf.cjs} +5 -5
- package/dist/{oauth-DNtv4Ihb.cjs.map → oauth--2p8jkJf.cjs.map} +1 -1
- package/dist/{oauth-BnHVf4cB.js → oauth-l5zE39cD.js} +5 -5
- package/dist/{oauth-BnHVf4cB.js.map → oauth-l5zE39cD.js.map} +1 -1
- package/dist/omnisearch/index.cjs +6 -14
- package/dist/omnisearch/index.cjs.map +1 -1
- package/dist/omnisearch/index.d.cts +6 -14
- package/dist/omnisearch/index.d.ts +6 -14
- package/dist/omnisearch/index.js +6 -14
- package/dist/omnisearch/index.js.map +1 -1
- package/dist/omnisearch/types.d.cts +1 -1
- package/dist/omnisearch/types.d.ts +1 -1
- package/dist/{project-DCHOxRWd.d.ts → project-B15gStbM.d.ts} +2 -2
- package/dist/{project-BRpzv3yE.d.cts → project-cHKuGvqJ.d.cts} +2 -2
- package/dist/queryService/index.cjs +1 -1
- package/dist/queryService/index.d.cts +1 -1
- package/dist/queryService/index.d.ts +1 -1
- package/dist/queryService/index.js +1 -1
- package/dist/queryService/types.d.cts +1 -1
- package/dist/queryService/types.d.ts +1 -1
- package/dist/{queryService-j3XZ2mX6.js → queryService-CCOYbRbw.js} +6 -6
- package/dist/{queryService-j3XZ2mX6.js.map → queryService-CCOYbRbw.js.map} +1 -1
- package/dist/{queryService-BVwD6Vmc.cjs → queryService-CQzclHdL.cjs} +6 -6
- package/dist/{queryService-BVwD6Vmc.cjs.map → queryService-CQzclHdL.cjs.map} +1 -1
- package/dist/queue/index.cjs +1 -1
- package/dist/queue/index.d.cts +3 -3
- package/dist/queue/index.d.ts +3 -3
- package/dist/queue/index.js +1 -1
- package/dist/queue/types.d.cts +1 -1
- package/dist/queue/types.d.ts +1 -1
- package/dist/{queue-Dagl0sdP.js → queue-Cowbg6bR.js} +8 -11
- package/dist/queue-Cowbg6bR.js.map +1 -0
- package/dist/{queue-e3rMWbmK.cjs → queue-UG_BWKIC.cjs} +8 -11
- package/dist/queue-UG_BWKIC.cjs.map +1 -0
- package/dist/scheduler/index.cjs +4 -4
- package/dist/scheduler/index.d.cts +2 -2
- package/dist/scheduler/index.d.ts +2 -2
- package/dist/scheduler/index.js +4 -4
- package/dist/scheduler/types.d.cts +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/sdk/configurations/index.cjs +1 -1
- package/dist/sdk/configurations/index.d.cts +11 -11
- package/dist/sdk/configurations/index.d.ts +11 -11
- package/dist/sdk/configurations/index.js +1 -1
- package/dist/sdk/developerPortal/index.cjs +10 -18
- package/dist/sdk/developerPortal/index.cjs.map +1 -1
- package/dist/sdk/developerPortal/index.d.cts +7 -15
- package/dist/sdk/developerPortal/index.d.ts +7 -15
- package/dist/sdk/developerPortal/index.js +7 -15
- package/dist/sdk/developerPortal/index.js.map +1 -1
- package/dist/sdk/kaiMemory/index.cjs +7 -0
- package/dist/sdk/kaiMemory/index.d.cts +41 -0
- package/dist/sdk/kaiMemory/index.d.ts +41 -0
- package/dist/sdk/kaiMemory/index.js +2 -0
- package/dist/sdk/kaiMemory/types.cjs +0 -0
- package/dist/sdk/kaiMemory/types.d.cts +2 -0
- package/dist/sdk/kaiMemory/types.d.ts +2 -0
- package/dist/sdk/kaiMemory/types.js +1 -0
- package/dist/sdk/queryService/index.cjs +2 -2
- package/dist/sdk/queryService/index.d.cts +1 -1
- package/dist/sdk/queryService/index.d.ts +1 -1
- package/dist/sdk/queryService/index.js +2 -2
- package/dist/sdk/semanticLayer/index.cjs +1 -1
- package/dist/sdk/semanticLayer/index.d.cts +16 -40
- package/dist/sdk/semanticLayer/index.d.ts +16 -40
- package/dist/sdk/semanticLayer/index.js +1 -1
- package/dist/sdk/semanticLayer/types.d.cts +1 -1
- package/dist/sdk/semanticLayer/types.d.ts +1 -1
- package/dist/sdk/storage/index.cjs +0 -5
- package/dist/sdk/storage/index.cjs.map +1 -1
- package/dist/sdk/storage/index.d.cts +2 -2
- package/dist/sdk/storage/index.d.ts +2 -2
- package/dist/sdk/storage/index.js +1 -5
- package/dist/sdk/storage/index.js.map +1 -1
- package/dist/sdk/tag/index.cjs +1 -1
- package/dist/sdk/tag/index.d.cts +3 -3
- package/dist/sdk/tag/index.d.ts +3 -3
- package/dist/sdk/tag/index.js +1 -1
- package/dist/sdk/tag/types.d.cts +1 -1
- package/dist/sdk/tag/types.d.ts +1 -1
- package/dist/sdk/vault/index.cjs +1 -1
- package/dist/sdk/vault/index.d.cts +1 -1
- package/dist/sdk/vault/index.d.ts +1 -1
- package/dist/sdk/vault/index.js +1 -1
- package/dist/{semanticLayer-9uTaU7UT.cjs → semanticLayer-CKDVkU56.cjs} +62 -183
- package/dist/semanticLayer-CKDVkU56.cjs.map +1 -0
- package/dist/{semanticLayer-6_gtFxDu.js → semanticLayer-pYy082Ju.js} +62 -183
- package/dist/semanticLayer-pYy082Ju.js.map +1 -0
- package/dist/status/index.cjs +1 -1
- package/dist/status/index.d.cts +1 -1
- package/dist/status/index.d.ts +1 -1
- package/dist/status/index.js +1 -1
- package/dist/storage/index.cjs +5 -5
- package/dist/storage/index.d.cts +2 -2
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +5 -5
- package/dist/storage/types.d.cts +2 -2
- package/dist/storage/types.d.ts +2 -2
- package/dist/{storage-Ob1LUIDq.js → storage-CZQHZcXG.js} +109 -302
- package/dist/storage-CZQHZcXG.js.map +1 -0
- package/dist/{storage-HFUdEy7P.cjs → storage-JinL1y1s.cjs} +109 -302
- package/dist/storage-JinL1y1s.cjs.map +1 -0
- package/dist/stream/index.cjs +3 -3
- package/dist/stream/index.d.cts +2 -2
- package/dist/stream/index.d.ts +2 -2
- package/dist/stream/index.js +3 -3
- package/dist/stream/types.d.cts +1 -1
- package/dist/stream/types.d.ts +1 -1
- package/dist/syncActions/index.cjs +1 -1
- package/dist/syncActions/index.d.cts +1 -1
- package/dist/syncActions/index.d.ts +1 -1
- package/dist/syncActions/index.js +1 -1
- package/dist/syncActions/types.d.cts +2 -2
- package/dist/syncActions/types.d.ts +2 -2
- package/dist/{syncActions-B3NS5a0g.js → syncActions-ChNWuhYA.js} +260 -111
- package/dist/syncActions-ChNWuhYA.js.map +1 -0
- package/dist/{syncActions-D2DhY6pP.cjs → syncActions-mFlF16cl.cjs} +260 -111
- package/dist/syncActions-mFlF16cl.cjs.map +1 -0
- package/dist/{tag-CsoOZWB_.cjs → tag-DHS_soa7.cjs} +2 -2
- package/dist/{tag-CsoOZWB_.cjs.map → tag-DHS_soa7.cjs.map} +1 -1
- package/dist/{tag-DCsoewjL.js → tag-hh-1nhNr.js} +2 -2
- package/dist/{tag-DCsoewjL.js.map → tag-hh-1nhNr.js.map} +1 -1
- package/dist/telemetry/index.cjs +7 -11
- package/dist/telemetry/index.cjs.map +1 -1
- package/dist/telemetry/index.d.cts +5 -9
- package/dist/telemetry/index.d.ts +5 -9
- package/dist/telemetry/index.js +7 -11
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/types.d.cts +1 -1
- package/dist/telemetry/types.d.ts +1 -1
- package/dist/{types-DqxIx9Pi.d.cts → types-6kuMW4JW.d.cts} +2 -2
- package/dist/{types-Ct0YPy2w.d.ts → types-B2w2TtSA.d.ts} +3 -3
- package/dist/{types-M46WuBSm.d.cts → types-B61aCifg.d.cts} +2 -2
- package/dist/{types-COL1kVEB.d.cts → types-B8N_k79M.d.cts} +2 -2
- package/dist/{types-Jw10xuOW.d.cts → types-BIECJYun.d.ts} +171 -44
- package/dist/{types-DZk3XHiN.d.cts → types-BLNB882w.d.cts} +3 -3
- package/dist/{types-BEe50G2r.d.cts → types-BPy5Ozee.d.cts} +2 -2
- package/dist/{types-gi4Eay19.d.ts → types-BS4jwuMi.d.ts} +7 -15
- package/dist/{types-BrYkBQo4.d.ts → types-BTK7Bv3n.d.cts} +171 -44
- package/dist/{types-D6UmMa6E.d.ts → types-BZDV8DZk.d.ts} +3 -3
- package/dist/{types-Cz83RiVu.d.cts → types-B_iLJlp4.d.ts} +255 -12
- package/dist/{types-CmF3pR2n.d.cts → types-BiMahEK8.d.cts} +91 -237
- package/dist/{types-BH5LLeJy.d.ts → types-Bl03Cg2n.d.cts} +255 -12
- package/dist/{types-bGQb4Jtl.d.cts → types-ByLH46Dt.d.cts} +13 -8
- package/dist/{types-g3vU7hcg.d.cts → types-Bydx6ga_.d.cts} +2 -2
- package/dist/{types-B95uNQ9o.d.ts → types-CKmTZSPG.d.ts} +91 -237
- package/dist/{types-D_j2Uq5X.d.cts → types-CMzQSHAQ.d.cts} +3 -3
- package/dist/{types-N3EChotg.d.cts → types-CP_LBmw3.d.cts} +2 -2
- package/dist/{types-CW6lCqrC.d.cts → types-CQFOCQzk.d.ts} +230 -43
- package/dist/{types-zZBxJ0P-.d.ts → types-CWyXTMwy.d.ts} +2 -2
- package/dist/{types-CPB-eF82.d.ts → types-CZJlLCbA.d.ts} +2 -2
- package/dist/{types-l9o4gFsI.d.ts → types-CZXPu3gn.d.ts} +2 -2
- package/dist/{types-BztF4HeB.d.ts → types-CaOtNjSZ.d.ts} +6 -13
- package/dist/{types-BTWIPEiU.d.ts → types-CfwqVkEA.d.ts} +2 -2
- package/dist/types-Cg14ncdV.d.ts +63 -0
- package/dist/{types-BsxLGDoC.d.cts → types-CoJVrRAD.d.ts} +2 -2
- package/dist/{types-DYzJ6K2e.d.ts → types-CqwHP7mN.d.cts} +230 -43
- package/dist/{types-CKiW3n6v.d.ts → types-D3wcs4tp.d.ts} +2 -2
- package/dist/{types-DOyNe7oe.d.cts → types-DDOa07rU.d.cts} +7 -15
- package/dist/{types-BW2BXUB4.d.cts → types-DE9hgr7i.d.cts} +2 -2
- package/dist/{types-BOqD3Hqy.d.cts → types-DEzW1CfI.d.cts} +3 -3
- package/dist/{types-OaD-cbbl.d.ts → types-DIiOMV-t.d.ts} +3 -3
- package/dist/{types-B-P4vQe0.d.cts → types-DKc1gmd9.d.ts} +857 -131
- package/dist/{types-BrPlv-NS.d.ts → types-DRCMBt89.d.ts} +3 -3
- package/dist/{types-BQvv4RaI.d.cts → types-DUMal2E-.d.cts} +5 -8
- package/dist/types-DWiLcrxt.d.cts +63 -0
- package/dist/{types-DNxOHnZI.d.cts → types-Dd31Lw4S.d.cts} +2 -2
- package/dist/{types-Dmu-8g4O.d.ts → types-DekYAlA-.d.ts} +3 -3
- package/dist/{types-B2U9cFcx.d.ts → types-DgJeO9Nd.d.ts} +2 -2
- package/dist/{types-nrviNdzZ.d.ts → types-DicSaNwq.d.ts} +2 -2
- package/dist/{types-Crhy0c9m.d.cts → types-DinIJ1D_.d.cts} +40 -11
- package/dist/{types-DhDNS-5B.d.ts → types-DlQw6FCi.d.ts} +13 -8
- package/dist/{types-_RhBCj4_.d.cts → types-DoZtE9tz.d.cts} +3 -3
- package/dist/{types-Bxhh2SJr.d.ts → types-DrvKfXXd.d.cts} +2 -2
- package/dist/{types-DPl8pZsY.d.ts → types-Dtuvhm3K.d.cts} +857 -131
- package/dist/{types-CcrvrAJX.d.ts → types-DwVSj-Da.d.ts} +5 -8
- package/dist/{types-C2vefB4D.d.ts → types-FwRmjsNN.d.ts} +10 -34
- package/dist/{types-C0_2KZ-A.d.ts → types-RbrfiY8T.d.ts} +2 -2
- package/dist/{types-DfvdbRPL.d.ts → types-WZBztugL.d.ts} +2 -2
- package/dist/{types-52irAt99.d.cts → types-YHUEArkR.d.cts} +2 -2
- package/dist/{types-fX4gbxfV.d.cts → types-bc-4z_gc.d.cts} +10 -34
- package/dist/{types-3eUgOA7I.d.cts → types-ePSqbXRr.d.cts} +6 -13
- package/dist/{types-DrqKICcK.d.ts → types-n4PTnLii.d.ts} +40 -11
- package/dist/{types-DXskkNp8.d.cts → types-pvQzbgNE.d.cts} +3 -3
- package/dist/{types-DajzGOKB.d.ts → types-q-1r_9d1.d.ts} +2 -2
- package/dist/{types-D8Rrz51O.d.cts → types-tKkeLa2B.d.cts} +2 -2
- package/dist/{validateConnectionStatus-828HV8WL.js → validateConnectionStatus-BEGu8wXT.js} +6 -10
- package/dist/validateConnectionStatus-BEGu8wXT.js.map +1 -0
- package/dist/{validateConnectionStatus-CvBKyeDO.cjs → validateConnectionStatus-BEYkQpu9.cjs} +6 -10
- package/dist/validateConnectionStatus-BEYkQpu9.cjs.map +1 -0
- package/dist/vault/index.cjs +4 -4
- package/dist/vault/index.d.cts +1 -1
- package/dist/vault/index.d.ts +1 -1
- package/dist/vault/index.js +4 -4
- package/dist/vault/types.d.cts +1 -1
- package/dist/vault/types.d.ts +1 -1
- package/package.json +32 -4
- package/surface/apps.md +3 -3
- package/surface/auth.md +13 -6
- package/surface/chat.md +6 -6
- package/surface/kaiAgent.md +9 -8
- package/surface/management.md +14 -5
- package/surface/metastore.md +2 -2
- package/surface/queue.md +3 -3
- package/surface/sdk.md +13 -3
- package/surface/storage.md +6 -6
- package/surface/syncActions.md +16 -0
- package/dist/ValidationError-BekhphAq.js +0 -48
- package/dist/ValidationError-BekhphAq.js.map +0 -1
- package/dist/ValidationError-CLVJCCwH.cjs.map +0 -1
- package/dist/authClient-8yU1last.cjs.map +0 -1
- package/dist/authClient-BTo4Afm6.js.map +0 -1
- package/dist/componentId-DkyQ8PM0.cjs.map +0 -1
- package/dist/componentId-vJz-kWs6.js.map +0 -1
- package/dist/concurrent-CH9fbNr3.js.map +0 -1
- package/dist/concurrent-tbiIa0UL.cjs.map +0 -1
- package/dist/configurations-BVvcimzo.js.map +0 -1
- package/dist/configurations-CyFF9Dtu.cjs.map +0 -1
- package/dist/configurations-VtNE0nrT.js.map +0 -1
- package/dist/configurations-zkNjr73Q.cjs.map +0 -1
- package/dist/createFetchClient-BlgPbjFj.cjs.map +0 -1
- package/dist/createFetchClient-C-cadpBx.js.map +0 -1
- package/dist/credential-CAulvfZ-.cjs +0 -47
- package/dist/credential-CAulvfZ-.cjs.map +0 -1
- package/dist/credential-DWXbo4IX.js +0 -36
- package/dist/credential-DWXbo4IX.js.map +0 -1
- package/dist/dedupe-CiskLkGR.cjs +0 -30
- package/dist/dedupe-CiskLkGR.cjs.map +0 -1
- package/dist/dedupe-DYIHgZoG.js +0 -25
- package/dist/dedupe-DYIHgZoG.js.map +0 -1
- package/dist/index-3LUk2rpD.d.ts +0 -65
- package/dist/index-CW3Pj7DY.d.cts +0 -65
- package/dist/isLoginPageRedirect-Cm6Pn8Pj.cjs +0 -25
- package/dist/isLoginPageRedirect-Cm6Pn8Pj.cjs.map +0 -1
- package/dist/isLoginPageRedirect-hyPqyWNX.js +0 -20
- package/dist/isLoginPageRedirect-hyPqyWNX.js.map +0 -1
- package/dist/jobStatus-0_Y3Sr4i.cjs.map +0 -1
- package/dist/jobStatus-PSZJMTiA.js.map +0 -1
- package/dist/kaiAgent-CcT2oNip.js.map +0 -1
- package/dist/kaiAgent-DaB6UCsh.cjs.map +0 -1
- package/dist/management-DeZiQSlw.cjs.map +0 -1
- package/dist/management-ckdnu1dA.js.map +0 -1
- package/dist/metastore-9AGrwjmN.cjs.map +0 -1
- package/dist/metastore-B0PolCjQ.js.map +0 -1
- package/dist/queue-Dagl0sdP.js.map +0 -1
- package/dist/queue-e3rMWbmK.cjs.map +0 -1
- package/dist/semanticLayer-6_gtFxDu.js.map +0 -1
- package/dist/semanticLayer-9uTaU7UT.cjs.map +0 -1
- package/dist/storage-HFUdEy7P.cjs.map +0 -1
- package/dist/storage-Ob1LUIDq.js.map +0 -1
- package/dist/syncActions-B3NS5a0g.js.map +0 -1
- package/dist/syncActions-D2DhY6pP.cjs.map +0 -1
- package/dist/validateConnectionStatus-828HV8WL.js.map +0 -1
- package/dist/validateConnectionStatus-CvBKyeDO.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
3
3
|
const require_project = require("./project-BvFi19K_.cjs");
|
|
4
|
-
const require_jobStatus = require("./jobStatus-
|
|
5
|
-
const require_queue = require("./queue-
|
|
6
|
-
const require_concurrent = require("./concurrent-
|
|
7
|
-
const require_dedupe = require("./dedupe-
|
|
4
|
+
const require_jobStatus = require("./jobStatus-BMlsyXWI.cjs");
|
|
5
|
+
const require_queue = require("./queue-UG_BWKIC.cjs");
|
|
6
|
+
const require_concurrent = require("./concurrent-CdVfR1N5.cjs");
|
|
7
|
+
const require_dedupe = require("./dedupe-OSnnst2u.cjs");
|
|
8
8
|
const require_store = require("./store-xS-Cu_VS.cjs");
|
|
9
|
-
const require_storage = require("./storage-
|
|
10
|
-
const require_tag = require("./tag-
|
|
9
|
+
const require_storage = require("./storage-JinL1y1s.cjs");
|
|
10
|
+
const require_tag = require("./tag-DHS_soa7.cjs");
|
|
11
11
|
const require_generateUUID = require("./generateUUID-C6qKBPIv.cjs");
|
|
12
12
|
const require_isPlainObject = require("./isPlainObject-BB3iy87C.cjs");
|
|
13
|
-
const require_configurations = require("./configurations-
|
|
13
|
+
const require_configurations = require("./configurations-VlfQ_37Y.cjs");
|
|
14
14
|
const require_UserError = require("./UserError-CSGO1qII.cjs");
|
|
15
|
-
const require_createFetchClient = require("./createFetchClient-
|
|
16
|
-
const require_validateConnectionStatus = require("./validateConnectionStatus-
|
|
17
|
-
const require_ManagementClientAuthError = require("./ManagementClientAuthError-
|
|
15
|
+
const require_createFetchClient = require("./createFetchClient-Cwp8max9.cjs");
|
|
16
|
+
const require_validateConnectionStatus = require("./validateConnectionStatus-BEYkQpu9.cjs");
|
|
17
|
+
const require_ManagementClientAuthError = require("./ManagementClientAuthError-DE7lNGZQ.cjs");
|
|
18
18
|
const require_QueryJobTimeoutError = require("./QueryJobTimeoutError-CVNPdCWX.cjs");
|
|
19
19
|
const require_SyncActionError = require("./SyncActionError-BG0zwchi.cjs");
|
|
20
|
-
const require_ValidationError = require("./ValidationError-
|
|
21
|
-
const require_createGenericFetchClient = require("./createGenericFetchClient-
|
|
22
|
-
const require_import = require("./import-
|
|
23
|
-
const require_authClient = require("./authClient-
|
|
24
|
-
const require_management = require("./management-
|
|
20
|
+
const require_ValidationError = require("./ValidationError-D65QU0uq.cjs");
|
|
21
|
+
const require_createGenericFetchClient = require("./createGenericFetchClient-Dl9cqTvR.cjs");
|
|
22
|
+
const require_import = require("./import-CNk89mqy.cjs");
|
|
23
|
+
const require_authClient = require("./authClient-BmuUo0J4.cjs");
|
|
24
|
+
const require_management = require("./management-CCY0IYnq.cjs");
|
|
25
25
|
const require_constants = require("./constants-C6xku_Vx.cjs");
|
|
26
26
|
const require_storageTable = require("./storageTable-C9crh4Ef.cjs");
|
|
27
|
-
const
|
|
27
|
+
const require_kaiMemory = require("./kaiMemory-CClSPInt.cjs");
|
|
28
|
+
const require_semanticLayer = require("./semanticLayer-CKDVkU56.cjs");
|
|
28
29
|
const require_sdk_storage_index = require("./sdk/storage/index.cjs");
|
|
29
30
|
const require_sdk_vault_index = require("./sdk/vault/index.cjs");
|
|
30
31
|
const require_ai_index = require("./ai/index.cjs");
|
|
31
|
-
const require_appLogs = require("./appLogs-
|
|
32
|
+
const require_appLogs = require("./appLogs-B9zFJIuy.cjs");
|
|
32
33
|
const require_apps_index = require("./apps/index.cjs");
|
|
33
34
|
const require_billing_index = require("./billing/index.cjs");
|
|
34
|
-
const require_businessWorkspaceApi = require("./businessWorkspaceApi-
|
|
35
|
+
const require_businessWorkspaceApi = require("./businessWorkspaceApi-6tCDdpnY.cjs");
|
|
35
36
|
const require_chat_index = require("./chat/index.cjs");
|
|
36
|
-
const require_dataScience = require("./dataScience-
|
|
37
|
+
const require_dataScience = require("./dataScience-B5QxZnvW.cjs");
|
|
37
38
|
const require_editor_index = require("./editor/index.cjs");
|
|
38
39
|
const require_encryption_index = require("./encryption/index.cjs");
|
|
39
|
-
const require_kaiAgent = require("./kaiAgent-
|
|
40
|
-
const require_metastore = require("./metastore-
|
|
40
|
+
const require_kaiAgent = require("./kaiAgent-QNoeVKPs.cjs");
|
|
41
|
+
const require_metastore = require("./metastore-XtWciCZT.cjs");
|
|
41
42
|
const require_notifications_index = require("./notifications/index.cjs");
|
|
42
|
-
const require_oauth = require("./oauth
|
|
43
|
+
const require_oauth = require("./oauth--2p8jkJf.cjs");
|
|
43
44
|
const require_omnisearch_index = require("./omnisearch/index.cjs");
|
|
44
|
-
const require_queryService = require("./queryService-
|
|
45
|
+
const require_queryService = require("./queryService-CQzclHdL.cjs");
|
|
45
46
|
const require_scheduler_index = require("./scheduler/index.cjs");
|
|
46
47
|
const require_stream_index = require("./stream/index.cjs");
|
|
47
|
-
const require_syncActions = require("./syncActions-
|
|
48
|
+
const require_syncActions = require("./syncActions-mFlF16cl.cjs");
|
|
48
49
|
const require_telemetry_index = require("./telemetry/index.cjs");
|
|
49
50
|
const require_vault_index = require("./vault/index.cjs");
|
|
50
|
-
const
|
|
51
|
+
const require_middleware = require("./middleware-IjYx3d5u.cjs");
|
|
51
52
|
const require_sdk_queryService_index = require("./sdk/queryService/index.cjs");
|
|
52
53
|
let zod = require("zod");
|
|
53
54
|
zod = require_rolldown_runtime.__toESM(zod, 1);
|
|
@@ -56,22 +57,29 @@ function assert(value, message) {
|
|
|
56
57
|
if (value == null) throw new Error(message);
|
|
57
58
|
}
|
|
58
59
|
//#endregion
|
|
60
|
+
//#region src/utils/ensure.ts
|
|
61
|
+
/**
|
|
62
|
+
* Read a key, or build its value and keep it.
|
|
63
|
+
*
|
|
64
|
+
* `Map.prototype.getOrInsertComputed` is this natively — swap to it once it clears the app's
|
|
65
|
+
* `baseline-widely-available` build target. Vite transpiles syntax, not methods, so adopting it
|
|
66
|
+
* early fails in the browser rather than at build.
|
|
67
|
+
*/
|
|
68
|
+
const ensure = (map, key, build) => {
|
|
69
|
+
const existing = map.get(key);
|
|
70
|
+
if (existing !== void 0) return existing;
|
|
71
|
+
const created = build();
|
|
72
|
+
map.set(key, created);
|
|
73
|
+
return created;
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
59
76
|
//#region src/utils/async/coalesce.ts
|
|
60
77
|
/**
|
|
61
78
|
* Coalesces concurrent triggers onto a single, never-parallel run of `task`.
|
|
62
|
-
* Triggers fired while a run is busy collapse into one trailing run, and each
|
|
63
|
-
* resolves with the first run that started after it — so it never sees stale state.
|
|
64
|
-
*
|
|
65
|
-
* Example — A triggers, then B and C trigger while A's run is still going:
|
|
66
|
-
* 1. trigger A → run #1 starts
|
|
67
|
-
* 2. trigger B, C → run #1 busy, so they wait
|
|
68
|
-
* 3. run #1 done → A resolves
|
|
69
|
-
* 4. run #2 starts → covers B and C together (one trailing run, not two)
|
|
70
|
-
* 5. run #2 done → B and C resolve
|
|
71
79
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
80
|
+
* - Triggers fired while a run is busy collapse into one trailing run.
|
|
81
|
+
* - Each trigger resolves with the first run started after it, so it never sees stale state.
|
|
82
|
+
* - The returned promise rejects with whatever `task` throws.
|
|
75
83
|
*/
|
|
76
84
|
const coalesce = (task) => {
|
|
77
85
|
let running = false;
|
|
@@ -391,8 +399,7 @@ const createProjectRefreshMiddleware = (shouldRefresh, requestRefresh) => (next)
|
|
|
391
399
|
return response;
|
|
392
400
|
};
|
|
393
401
|
/**
|
|
394
|
-
* The live project store
|
|
395
|
-
* token-mutating request or an explicit re-scope. Its `middleware` drives the auto re-verify.
|
|
402
|
+
* The live project store, re-verified (coalesced) after a token-mutating request or a re-scope.
|
|
396
403
|
*/
|
|
397
404
|
const createLiveProject = ({ initialToken, verify }) => {
|
|
398
405
|
const store = require_store.createStore(require_project.createProject({ sapiToken: initialToken }));
|
|
@@ -415,24 +422,30 @@ const createLiveProject = ({ initialToken, verify }) => {
|
|
|
415
422
|
const KeboolaErrorType = {
|
|
416
423
|
Unconfigured: "unconfigured",
|
|
417
424
|
NotAuthenticated: "not-authenticated",
|
|
425
|
+
SignInEnded: "sign-in-ended",
|
|
418
426
|
AlreadyAuthenticated: "already-authenticated",
|
|
419
427
|
NoStorageToken: "no-storage-token",
|
|
420
428
|
NoManagementToken: "no-management-token",
|
|
421
429
|
MissingProject: "missing-project",
|
|
422
430
|
NotBearer: "not-bearer",
|
|
423
431
|
NotReady: "not-ready",
|
|
424
|
-
InvalidBuildTransition: "invalid-build-transition"
|
|
432
|
+
InvalidBuildTransition: "invalid-build-transition",
|
|
433
|
+
StackActive: "stack-active",
|
|
434
|
+
CredentialKindMismatch: "credential-kind-mismatch"
|
|
425
435
|
};
|
|
426
436
|
const MESSAGES = {
|
|
427
437
|
[KeboolaErrorType.Unconfigured]: "The session is not configured — set a baseUrl first.",
|
|
428
438
|
[KeboolaErrorType.NotAuthenticated]: "The session is not authenticated — sign in first.",
|
|
439
|
+
[KeboolaErrorType.SignInEnded]: "The sign-in this operation started under has ended — its result belongs to nobody.",
|
|
429
440
|
[KeboolaErrorType.AlreadyAuthenticated]: "The session is already authenticated — reset before authenticating again.",
|
|
430
441
|
[KeboolaErrorType.NoStorageToken]: "No storage token — authenticate the storage realm first.",
|
|
431
442
|
[KeboolaErrorType.NoManagementToken]: "No management token — authenticate the management realm first.",
|
|
432
443
|
[KeboolaErrorType.MissingProject]: "A bearer storage session requires a project — call setProject(projectId) first.",
|
|
433
444
|
[KeboolaErrorType.NotBearer]: "This operation requires a bearer (OAuth) session.",
|
|
434
445
|
[KeboolaErrorType.NotReady]: "The client is not ready — authenticate the storage realm first.",
|
|
435
|
-
[KeboolaErrorType.InvalidBuildTransition]: "Invalid storage build transition — the build goes idle → initializing → ready | failed."
|
|
446
|
+
[KeboolaErrorType.InvalidBuildTransition]: "Invalid storage build transition — the build goes idle → initializing → ready | failed.",
|
|
447
|
+
[KeboolaErrorType.StackActive]: "The active stack cannot be closed — select another first.",
|
|
448
|
+
[KeboolaErrorType.CredentialKindMismatch]: "This client renews the other credential kind — log in the way its `credentials` mode expects."
|
|
436
449
|
};
|
|
437
450
|
var KeboolaError = class KeboolaError extends Error {
|
|
438
451
|
type;
|
|
@@ -449,168 +462,6 @@ function assert$1(value, type) {
|
|
|
449
462
|
if (value == null) throw new KeboolaError(type);
|
|
450
463
|
}
|
|
451
464
|
//#endregion
|
|
452
|
-
//#region src/middlewares/loggingMiddleware.ts
|
|
453
|
-
const CORRELATION_ID_HEADER = "X-Correlation-ID";
|
|
454
|
-
const ALWAYS_REDACTED_HEADERS = [
|
|
455
|
-
require_createFetchClient.KeboolaHttpHeader.MANAGEMENT_API_TOKEN,
|
|
456
|
-
require_createFetchClient.KeboolaHttpHeader.STORAGE_API_TOKEN,
|
|
457
|
-
"authorization",
|
|
458
|
-
"cookie",
|
|
459
|
-
"set-cookie"
|
|
460
|
-
];
|
|
461
|
-
const ALWAYS_REDACTED_QUERY_PARAMS = /* @__PURE__ */ new Set([
|
|
462
|
-
"token",
|
|
463
|
-
"apikey",
|
|
464
|
-
"api_key",
|
|
465
|
-
"access_token",
|
|
466
|
-
"refresh_token",
|
|
467
|
-
"code",
|
|
468
|
-
"state",
|
|
469
|
-
"signature",
|
|
470
|
-
"sig",
|
|
471
|
-
"x-amz-signature",
|
|
472
|
-
"x-amz-credential",
|
|
473
|
-
"x-amz-security-token",
|
|
474
|
-
"password",
|
|
475
|
-
"secret"
|
|
476
|
-
]);
|
|
477
|
-
const REDACTED = "[REDACTED]";
|
|
478
|
-
const getRedactedUrl = (rawUrl) => {
|
|
479
|
-
const url = new URL(rawUrl);
|
|
480
|
-
if (url.username) url.username = REDACTED;
|
|
481
|
-
if (url.password) url.password = REDACTED;
|
|
482
|
-
url.searchParams.forEach((_value, key) => {
|
|
483
|
-
if (ALWAYS_REDACTED_QUERY_PARAMS.has(key.toLowerCase())) url.searchParams.set(key, REDACTED);
|
|
484
|
-
});
|
|
485
|
-
return url.toString();
|
|
486
|
-
};
|
|
487
|
-
const ensureCorrelationId = (request) => {
|
|
488
|
-
const correlationId = request.headers.get(CORRELATION_ID_HEADER) ?? require_generateUUID.generateUUID();
|
|
489
|
-
request.headers.set(CORRELATION_ID_HEADER, correlationId);
|
|
490
|
-
return correlationId;
|
|
491
|
-
};
|
|
492
|
-
const getRedactedHeaders = (headers, redacted) => {
|
|
493
|
-
const redactedSet = new Set([...ALWAYS_REDACTED_HEADERS, ...redacted].map((h) => h.toLowerCase()));
|
|
494
|
-
const result = {};
|
|
495
|
-
headers.forEach((value, name) => {
|
|
496
|
-
result[name] = redactedSet.has(name.toLowerCase()) ? REDACTED : value;
|
|
497
|
-
});
|
|
498
|
-
return result;
|
|
499
|
-
};
|
|
500
|
-
const getRequestLog = (request, redactedHeaders) => ({
|
|
501
|
-
url: getRedactedUrl(request.url),
|
|
502
|
-
headers: getRedactedHeaders(request.headers, redactedHeaders)
|
|
503
|
-
});
|
|
504
|
-
const getResponseLog = (response, redactedHeaders) => ({
|
|
505
|
-
status: response.status,
|
|
506
|
-
statusText: response.statusText,
|
|
507
|
-
headers: getRedactedHeaders(response.headers, redactedHeaders)
|
|
508
|
-
});
|
|
509
|
-
const redactApiError = (error, redactedHeaders) => new require_createFetchClient.ApiError({
|
|
510
|
-
request: new Request(getRedactedUrl(error.request.url), {
|
|
511
|
-
method: error.request.method,
|
|
512
|
-
headers: getRedactedHeaders(error.request.headers, redactedHeaders)
|
|
513
|
-
}),
|
|
514
|
-
response: new Response(null, {
|
|
515
|
-
status: error.response.status,
|
|
516
|
-
statusText: error.response.statusText,
|
|
517
|
-
headers: getRedactedHeaders(error.response.headers, redactedHeaders)
|
|
518
|
-
}),
|
|
519
|
-
data: error.data
|
|
520
|
-
});
|
|
521
|
-
const formatLog = (level, log) => {
|
|
522
|
-
const parts = [
|
|
523
|
-
`[${level.toUpperCase()}]`,
|
|
524
|
-
`${Math.round(log.durationMs)}ms`,
|
|
525
|
-
log.method
|
|
526
|
-
];
|
|
527
|
-
parts.push(log.request.url);
|
|
528
|
-
if ("response" in log) parts.push(String(log.response.status));
|
|
529
|
-
return parts.join(" ");
|
|
530
|
-
};
|
|
531
|
-
const createSafeLogger = (logger, level = "error-only") => {
|
|
532
|
-
return {
|
|
533
|
-
info: (log) => {
|
|
534
|
-
if (level !== "all") return;
|
|
535
|
-
try {
|
|
536
|
-
if (logger?.info) {
|
|
537
|
-
logger.info(log);
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
console.info(formatLog("info", log));
|
|
541
|
-
} catch {}
|
|
542
|
-
},
|
|
543
|
-
error: (log) => {
|
|
544
|
-
try {
|
|
545
|
-
if (logger?.error) {
|
|
546
|
-
logger.error(log);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
console.error(formatLog("error", log));
|
|
550
|
-
} catch {}
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
};
|
|
554
|
-
const createLoggingMiddleware = (options = {}) => {
|
|
555
|
-
const safeLogger = createSafeLogger(options.logger, options.level);
|
|
556
|
-
const redactHeaders = options.redactHeaders ?? [];
|
|
557
|
-
return (next) => async (fetchRequest) => {
|
|
558
|
-
const correlationId = ensureCorrelationId(fetchRequest.request);
|
|
559
|
-
const start = performance.now();
|
|
560
|
-
const method = fetchRequest.request.method.toLowerCase();
|
|
561
|
-
const requestLog = getRequestLog(fetchRequest.request, redactHeaders);
|
|
562
|
-
try {
|
|
563
|
-
const result = await next(fetchRequest);
|
|
564
|
-
safeLogger.info({
|
|
565
|
-
correlationId,
|
|
566
|
-
method,
|
|
567
|
-
durationMs: performance.now() - start,
|
|
568
|
-
request: requestLog,
|
|
569
|
-
response: getResponseLog(result.response, redactHeaders)
|
|
570
|
-
});
|
|
571
|
-
return result;
|
|
572
|
-
} catch (error) {
|
|
573
|
-
if (error instanceof Error) safeLogger.error({
|
|
574
|
-
correlationId,
|
|
575
|
-
method,
|
|
576
|
-
durationMs: performance.now() - start,
|
|
577
|
-
request: requestLog,
|
|
578
|
-
error: require_createFetchClient.isApiError(error) ? redactApiError(error, redactHeaders) : error
|
|
579
|
-
});
|
|
580
|
-
throw error;
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
};
|
|
584
|
-
//#endregion
|
|
585
|
-
//#region src/middlewares/retryMiddleware.ts
|
|
586
|
-
const DEFAULT_MAX_RETRIES = 5;
|
|
587
|
-
const DEFAULT_RETRYABLE_STATUSES = [
|
|
588
|
-
require_createFetchClient.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
589
|
-
require_createFetchClient.HttpStatus.BAD_GATEWAY,
|
|
590
|
-
require_createFetchClient.HttpStatus.SERVICE_UNAVAILABLE,
|
|
591
|
-
require_createFetchClient.HttpStatus.GATEWAY_TIMEOUT
|
|
592
|
-
];
|
|
593
|
-
const DEFAULT_BACKOFF_FACTOR = .3;
|
|
594
|
-
const defaultShouldRetry = (error) => error instanceof require_createFetchClient.ApiError && DEFAULT_RETRYABLE_STATUSES.includes(error.response.status);
|
|
595
|
-
const createRetryMiddleware = (options) => {
|
|
596
|
-
const { maxRetries = DEFAULT_MAX_RETRIES, backoffFactor = DEFAULT_BACKOFF_FACTOR, shouldRetry = defaultShouldRetry } = options ?? {};
|
|
597
|
-
if (!Number.isInteger(maxRetries) || maxRetries < 0) throw new RangeError("maxRetries must be a non-negative integer");
|
|
598
|
-
if (!Number.isFinite(backoffFactor) || backoffFactor < 0) throw new RangeError("backoffFactor must be a finite non-negative number");
|
|
599
|
-
return (next) => async (request) => {
|
|
600
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
601
|
-
return await next({
|
|
602
|
-
...request,
|
|
603
|
-
request: request.request.clone()
|
|
604
|
-
});
|
|
605
|
-
} catch (error) {
|
|
606
|
-
if (attempt === maxRetries || !shouldRetry(error, attempt)) throw error;
|
|
607
|
-
const delayMs = backoffFactor * 1e3 * Math.pow(2, attempt);
|
|
608
|
-
await require_jobStatus.delay(delayMs, request.request.signal ?? void 0);
|
|
609
|
-
}
|
|
610
|
-
throw new Error("unreachable");
|
|
611
|
-
};
|
|
612
|
-
};
|
|
613
|
-
//#endregion
|
|
614
465
|
//#region src/serviceClientRegistry/utils.ts
|
|
615
466
|
const isBuiltInProperty = (property) => typeof property === "symbol" || property === "then" || property === "constructor" || property === "toString" || property === "valueOf";
|
|
616
467
|
/**
|
|
@@ -699,146 +550,40 @@ const createMiddlewareFor = (middlewares) => {
|
|
|
699
550
|
};
|
|
700
551
|
};
|
|
701
552
|
//#endregion
|
|
702
|
-
//#region src/keboola/
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
issuedAt: Date.now()
|
|
710
|
-
});
|
|
711
|
-
const createConfig$1 = (initial = INITIAL) => {
|
|
712
|
-
const store = require_store.createStore(initial);
|
|
713
|
-
let expiredNotified = false;
|
|
714
|
-
let epoch = 0;
|
|
553
|
+
//#region src/keboola/legacy/createConfig.ts
|
|
554
|
+
const createConfig = () => {
|
|
555
|
+
const store = require_store.createStore({
|
|
556
|
+
baseUrl: null,
|
|
557
|
+
sapiToken: null,
|
|
558
|
+
managementToken: null
|
|
559
|
+
});
|
|
715
560
|
const setBaseUrl = (baseUrl) => {
|
|
716
|
-
if (store.get().status !== "anonymous") throw new KeboolaError(KeboolaErrorType.AlreadyAuthenticated);
|
|
717
561
|
store.set({
|
|
718
|
-
|
|
562
|
+
...store.get(),
|
|
719
563
|
baseUrl
|
|
720
564
|
});
|
|
721
565
|
};
|
|
722
566
|
const authenticate = (tokens) => {
|
|
723
|
-
const
|
|
724
|
-
if (state.status !== "anonymous") throw new KeboolaError(KeboolaErrorType.AlreadyAuthenticated);
|
|
725
|
-
if (!state.baseUrl) throw new KeboolaError(KeboolaErrorType.Unconfigured);
|
|
726
|
-
expiredNotified = false;
|
|
727
|
-
epoch += 1;
|
|
728
|
-
store.set({
|
|
729
|
-
status: "authenticated",
|
|
730
|
-
baseUrl: state.baseUrl,
|
|
731
|
-
credential: stamp(tokens)
|
|
732
|
-
});
|
|
733
|
-
};
|
|
734
|
-
const setProjectId = (projectId) => {
|
|
735
|
-
const state = store.get();
|
|
736
|
-
if (state.status === "anonymous") throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
737
|
-
store.set({
|
|
738
|
-
status: "scoped",
|
|
739
|
-
baseUrl: state.baseUrl,
|
|
740
|
-
credential: state.credential,
|
|
741
|
-
projectId
|
|
742
|
-
});
|
|
743
|
-
};
|
|
744
|
-
/**
|
|
745
|
-
* Drop the project scope, keeping the credential — the inverse of `setProjectId`, for leaving a
|
|
746
|
-
* project without signing out. The project clients are keyed on the scope, so they tear down and
|
|
747
|
-
* the storage build returns to `idle`; the session stays usable for `auth` and `management`.
|
|
748
|
-
* No-op unless currently scoped.
|
|
749
|
-
*/
|
|
750
|
-
const clearProjectId = () => {
|
|
751
|
-
const state = store.get();
|
|
752
|
-
if (state.status !== "scoped") return;
|
|
753
|
-
store.set({
|
|
754
|
-
status: "authenticated",
|
|
755
|
-
baseUrl: state.baseUrl,
|
|
756
|
-
credential: state.credential
|
|
757
|
-
});
|
|
758
|
-
};
|
|
759
|
-
/** Drop the credential (sign out); keeps the configured baseUrl. */
|
|
760
|
-
const reset = () => {
|
|
761
|
-
epoch += 1;
|
|
762
|
-
store.set({
|
|
763
|
-
status: "anonymous",
|
|
764
|
-
baseUrl: store.get().baseUrl
|
|
765
|
-
});
|
|
766
|
-
};
|
|
767
|
-
/** Apply a token refresh: replace the credential (fresh tokens, new issue time); no-op before auth. */
|
|
768
|
-
const applyRefresh = (tokens) => {
|
|
769
|
-
const state = store.get();
|
|
770
|
-
if (state.status === "anonymous") return;
|
|
771
|
-
store.set({
|
|
772
|
-
...state,
|
|
773
|
-
credential: stamp(tokens)
|
|
774
|
-
});
|
|
775
|
-
};
|
|
776
|
-
/**
|
|
777
|
-
* Dev/test lever: backdate the current token's issue time by a full lifetime so it reads as
|
|
778
|
-
* expired, making the refresh policy rotate it on the next request. No-op before auth.
|
|
779
|
-
*/
|
|
780
|
-
const forceExpire = () => {
|
|
781
|
-
const state = store.get();
|
|
782
|
-
if (state.status === "anonymous") return;
|
|
783
|
-
const issuedAt = Date.now() - state.credential.expiresIn * 1e3;
|
|
567
|
+
const current = store.get();
|
|
784
568
|
store.set({
|
|
785
|
-
...
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
issuedAt
|
|
789
|
-
}
|
|
569
|
+
...current,
|
|
570
|
+
sapiToken: tokens.sapiToken ?? current.sapiToken,
|
|
571
|
+
managementToken: tokens.managementToken ?? current.managementToken
|
|
790
572
|
});
|
|
791
573
|
};
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (state.status === "anonymous") throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
799
|
-
return state.credential;
|
|
800
|
-
};
|
|
801
|
-
const getBaseUrl = () => store.get().baseUrl;
|
|
802
|
-
/**
|
|
803
|
-
* Which sign-in this is — a counter, bumped by `authenticate` and `reset`.
|
|
804
|
-
*
|
|
805
|
-
* - For anything long-lived that must not outlive the credential it was built against: capture it,
|
|
806
|
-
* compare it later. A rotation does not move it; only a new session or a sign-out does.
|
|
807
|
-
* - Distinct from `notifyExpired`'s latch, which answers "report this death once" rather than
|
|
808
|
-
* "which session are we in".
|
|
809
|
-
*/
|
|
810
|
-
const getEpoch = () => epoch;
|
|
811
|
-
/**
|
|
812
|
-
* Session-expiry latch (fire-once):
|
|
813
|
-
* - why: concurrent 401s all report expiry, but the app must sign out once; and the credential /
|
|
814
|
-
* auth middleware are long-lived singletons, so the latch can't live in them — it must reset
|
|
815
|
-
* per session.
|
|
816
|
-
* - how: returns `true` only for the first call since the last `authenticate` (which re-arms it),
|
|
817
|
-
* so the caller runs its sign-out exactly once per session death.
|
|
818
|
-
*/
|
|
819
|
-
const notifyExpired = () => {
|
|
820
|
-
if (expiredNotified) return false;
|
|
821
|
-
expiredNotified = true;
|
|
822
|
-
return true;
|
|
823
|
-
};
|
|
574
|
+
const getStorageToken = () => store.get().sapiToken ?? "";
|
|
575
|
+
const setStorageToken = (sapiToken) => store.set({
|
|
576
|
+
...store.get(),
|
|
577
|
+
sapiToken
|
|
578
|
+
});
|
|
579
|
+
/** Current state, for diagnostics. */
|
|
824
580
|
const debug = () => store.get();
|
|
825
581
|
return {
|
|
826
|
-
store
|
|
827
|
-
get: store.get,
|
|
828
|
-
subscribe: store.subscribe,
|
|
829
|
-
dispose: store.dispose
|
|
830
|
-
},
|
|
582
|
+
store,
|
|
831
583
|
setBaseUrl,
|
|
832
584
|
authenticate,
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
reset,
|
|
836
|
-
applyRefresh,
|
|
837
|
-
forceExpire,
|
|
838
|
-
requireCredential,
|
|
839
|
-
getBaseUrl,
|
|
840
|
-
getEpoch,
|
|
841
|
-
notifyExpired,
|
|
585
|
+
getStorageToken,
|
|
586
|
+
setStorageToken,
|
|
842
587
|
debug
|
|
843
588
|
};
|
|
844
589
|
};
|
|
@@ -979,6 +724,7 @@ const createVariablesSdk = ({ storageClient }) => {
|
|
|
979
724
|
};
|
|
980
725
|
//#endregion
|
|
981
726
|
//#region src/keboola/createServiceClients.ts
|
|
727
|
+
const withPath = (serviceBaseUrl, path) => typeof serviceBaseUrl === "string" ? `${serviceBaseUrl}${path}` : async () => `${await serviceBaseUrl()}${path}`;
|
|
982
728
|
const createServiceClients = ({ baseUrl, auth, getServiceBaseUrl, middlewareFor, attachTokenMiddleware }) => createServiceClientRegistry()({
|
|
983
729
|
getServiceBaseUrl,
|
|
984
730
|
onUnavailable: ({ serviceId, path, clientName }) => {
|
|
@@ -1004,7 +750,7 @@ const createServiceClients = ({ baseUrl, auth, getServiceBaseUrl, middlewareFor,
|
|
|
1004
750
|
serviceId: "data-science",
|
|
1005
751
|
clientName: "apps",
|
|
1006
752
|
clientFn: (serviceBaseUrl) => require_apps_index.createAppsClient({
|
|
1007
|
-
baseUrl:
|
|
753
|
+
baseUrl: withPath(serviceBaseUrl, "/v2"),
|
|
1008
754
|
middlewares: middlewareFor("apps", [attachTokenMiddleware]),
|
|
1009
755
|
auth
|
|
1010
756
|
})
|
|
@@ -1076,7 +822,7 @@ const createServiceClients = ({ baseUrl, auth, getServiceBaseUrl, middlewareFor,
|
|
|
1076
822
|
serviceId: "ai",
|
|
1077
823
|
clientName: "omnisearch",
|
|
1078
824
|
clientFn: (serviceBaseUrl) => require_omnisearch_index.createOmnisearchClient({
|
|
1079
|
-
baseUrl:
|
|
825
|
+
baseUrl: withPath(serviceBaseUrl, "/omnisearch"),
|
|
1080
826
|
middlewares: middlewareFor("omnisearch", [attachTokenMiddleware]),
|
|
1081
827
|
auth
|
|
1082
828
|
})
|
|
@@ -1155,35 +901,26 @@ const createServiceClients = ({ baseUrl, auth, getServiceBaseUrl, middlewareFor,
|
|
|
1155
901
|
})
|
|
1156
902
|
}).build();
|
|
1157
903
|
//#endregion
|
|
1158
|
-
//#region src/keboola/
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
* before it clones the request for the 401 replay. Bearer has no static token header, so this
|
|
1165
|
-
* combined `attach` rides on every client — including the operational storage and the bootstrap reader.
|
|
1166
|
-
*/
|
|
1167
|
-
const createProjectClients$1 = async ({ baseUrl, projectId, authMiddleware, middlewareFor, clientServiceUrls }) => {
|
|
904
|
+
//#region src/keboola/legacy/createProjectClients.ts
|
|
905
|
+
const createAttachStorageToken = (getToken) => (next) => (request) => {
|
|
906
|
+
request.request.headers.set(require_createFetchClient.KeboolaHttpHeader.STORAGE_API_TOKEN, getToken());
|
|
907
|
+
return next(request);
|
|
908
|
+
};
|
|
909
|
+
const createProjectClients = async ({ baseUrl, getStorageToken, onStorageTokenRefreshed, middlewareFor, clientServiceUrls }) => {
|
|
1168
910
|
const auth = {
|
|
1169
|
-
type: "
|
|
1170
|
-
token:
|
|
1171
|
-
projectId
|
|
1172
|
-
};
|
|
1173
|
-
const projectIdMiddleware = (next) => (request) => {
|
|
1174
|
-
request.request.headers.set(require_createFetchClient.KeboolaHttpHeader.PROJECT_ID, String(projectId));
|
|
1175
|
-
return next(request);
|
|
911
|
+
type: "sapi-token",
|
|
912
|
+
token: getStorageToken()
|
|
1176
913
|
};
|
|
1177
|
-
const attach = (
|
|
1178
|
-
const
|
|
914
|
+
const attach = createAttachStorageToken(getStorageToken);
|
|
915
|
+
const _storage = require_storage.createStorageClient({
|
|
1179
916
|
baseUrl,
|
|
1180
917
|
auth,
|
|
1181
918
|
middlewares: [attach]
|
|
1182
919
|
});
|
|
1183
920
|
const [stackInfo, sapiToken, devBranches] = await Promise.all([
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
921
|
+
_storage.getStackInfo({ exclude: "componentDetails" }),
|
|
922
|
+
_storage.tokens.verify(),
|
|
923
|
+
_storage.branches.getDevBranches()
|
|
1187
924
|
]);
|
|
1188
925
|
const stack = createStack({
|
|
1189
926
|
stackInfo,
|
|
@@ -1192,7 +929,7 @@ const createProjectClients$1 = async ({ baseUrl, projectId, authMiddleware, midd
|
|
|
1192
929
|
const defaultBranch = createDefaultBranch({ devBranches });
|
|
1193
930
|
const liveProject = createLiveProject({
|
|
1194
931
|
initialToken: sapiToken,
|
|
1195
|
-
verify: () =>
|
|
932
|
+
verify: () => _storage.tokens.verify()
|
|
1196
933
|
});
|
|
1197
934
|
const storageTableStore = createDerivedStore({
|
|
1198
935
|
source: liveProject.store,
|
|
@@ -1217,552 +954,8 @@ const createProjectClients$1 = async ({ baseUrl, projectId, authMiddleware, midd
|
|
|
1217
954
|
const storage = require_storage.createStorageClient({
|
|
1218
955
|
baseUrl,
|
|
1219
956
|
auth,
|
|
1220
|
-
middlewares: middlewareFor("storage", [
|
|
1221
|
-
|
|
1222
|
-
const { client: services, meta: serviceMeta } = createServiceClients({
|
|
1223
|
-
baseUrl,
|
|
1224
|
-
auth,
|
|
1225
|
-
getServiceBaseUrl: context.stack.getServiceUrl,
|
|
1226
|
-
middlewareFor,
|
|
1227
|
-
attachTokenMiddleware: attach
|
|
1228
|
-
});
|
|
1229
|
-
const sdk = {
|
|
1230
|
-
configuration: require_configurations.createConfigurationSdk({
|
|
1231
|
-
storageClient: storage,
|
|
1232
|
-
encryptionClient: services.encryption,
|
|
1233
|
-
context
|
|
1234
|
-
}),
|
|
1235
|
-
tag: require_tag.createTagSdk({ metastoreClient: services.metastore }),
|
|
1236
|
-
semanticLayer: require_semanticLayer.createSemanticLayerSdk({ metastoreClient: services.metastore }),
|
|
1237
|
-
storage: require_sdk_storage_index.createStorageSdk({ storageClient: storage }),
|
|
1238
|
-
vault: require_sdk_vault_index.createVaultSdk({
|
|
1239
|
-
vaultClient: services.vault,
|
|
1240
|
-
encryptionClient: services.encryption
|
|
1241
|
-
}),
|
|
1242
|
-
variables: createVariablesSdk({ storageClient: storage })
|
|
1243
|
-
};
|
|
1244
|
-
return {
|
|
1245
|
-
context,
|
|
1246
|
-
serviceMeta,
|
|
1247
|
-
storage,
|
|
1248
|
-
...services,
|
|
1249
|
-
sdk
|
|
1250
|
-
};
|
|
1251
|
-
};
|
|
1252
|
-
//#endregion
|
|
1253
|
-
//#region src/keboola/bearer/middleware.ts
|
|
1254
|
-
/**
|
|
1255
|
-
* Bearer auth for every outgoing request:
|
|
1256
|
-
* - Stamps the current access token (from the credential) as the `Authorization` header.
|
|
1257
|
-
* - On a 401, force-refreshes the token and replays the request once.
|
|
1258
|
-
* - If the replay still 401s, the fresh token is rejected → marks the session expired.
|
|
1259
|
-
*
|
|
1260
|
-
* When the session is anonymous (`getAccessToken` → `null`) the request is passed through untouched
|
|
1261
|
-
* with no 401 dance — so pre-auth calls on the auth client (`login`, `refresh`) work and a failed
|
|
1262
|
-
* login is never mistaken for session expiry.
|
|
1263
|
-
*
|
|
1264
|
-
* Holds no token and never touches config — all token and refresh state lives in the credential.
|
|
1265
|
-
*/
|
|
1266
|
-
const createBearerAuthMiddleware = (credential) => {
|
|
1267
|
-
return (next) => async (request) => {
|
|
1268
|
-
const token = await credential.getAccessToken({ forceRefresh: false });
|
|
1269
|
-
if (token === null) return next(request);
|
|
1270
|
-
const retryRequest = request.request.clone();
|
|
1271
|
-
request.request.headers.set(require_createFetchClient.HttpHeader.AUTHORIZATION, `Bearer ${token}`);
|
|
1272
|
-
try {
|
|
1273
|
-
return await next(request);
|
|
1274
|
-
} catch (error) {
|
|
1275
|
-
if (!require_credential.isUnauthorized(error)) throw error;
|
|
1276
|
-
const refreshed = await credential.getAccessToken({ forceRefresh: true });
|
|
1277
|
-
if (refreshed === null) throw error;
|
|
1278
|
-
retryRequest.headers.set(require_createFetchClient.HttpHeader.AUTHORIZATION, `Bearer ${refreshed}`);
|
|
1279
|
-
try {
|
|
1280
|
-
return await next({
|
|
1281
|
-
...request,
|
|
1282
|
-
request: retryRequest
|
|
1283
|
-
});
|
|
1284
|
-
} catch (retryError) {
|
|
1285
|
-
if (require_credential.isUnauthorized(retryError)) credential.notifyExpired();
|
|
1286
|
-
throw retryError;
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
};
|
|
1290
|
-
};
|
|
1291
|
-
//#endregion
|
|
1292
|
-
//#region src/keboola/bearer/persistor.ts
|
|
1293
|
-
const SESSION_KEY = "keboola:bearer-session";
|
|
1294
|
-
const persistedSessionSchema = zod.object({
|
|
1295
|
-
baseUrl: zod.string().nullable(),
|
|
1296
|
-
projectId: zod.number().nullable(),
|
|
1297
|
-
refreshToken: zod.string().nullable()
|
|
1298
|
-
});
|
|
1299
|
-
const EMPTY_PERSISTED_SESSION = {
|
|
1300
|
-
baseUrl: null,
|
|
1301
|
-
projectId: null,
|
|
1302
|
-
refreshToken: null
|
|
1303
|
-
};
|
|
1304
|
-
const createSessionPersistor = (storage) => createPersistor({
|
|
1305
|
-
storage,
|
|
1306
|
-
key: SESSION_KEY,
|
|
1307
|
-
schema: persistedSessionSchema,
|
|
1308
|
-
defaultValue: EMPTY_PERSISTED_SESSION
|
|
1309
|
-
});
|
|
1310
|
-
//#endregion
|
|
1311
|
-
//#region src/keboola/bearer/keboola.ts
|
|
1312
|
-
const createKeboola = (options = {}) => {
|
|
1313
|
-
const persistor = createSessionPersistor(options.storage);
|
|
1314
|
-
const config = createConfig$1();
|
|
1315
|
-
const middlewareFor = createMiddlewareFor(options.middlewares);
|
|
1316
|
-
const retryContention = createRetryMiddleware({ shouldRetry: (error) => error instanceof require_createFetchClient.ApiError && error.response.status === require_createFetchClient.HttpStatus.SERVICE_UNAVAILABLE });
|
|
1317
|
-
const authClientBare = createDerivedStore({
|
|
1318
|
-
source: config.store,
|
|
1319
|
-
dependsOn: (state) => state.baseUrl,
|
|
1320
|
-
build: (baseUrl) => baseUrl ? require_authClient.createAuthClient({
|
|
1321
|
-
baseUrl,
|
|
1322
|
-
middlewares: middlewareFor("auth", [retryContention])
|
|
1323
|
-
}) : null
|
|
1324
|
-
});
|
|
1325
|
-
const refresh = async () => {
|
|
1326
|
-
const client = authClientBare.get();
|
|
1327
|
-
assert$1(client, KeboolaErrorType.NotReady);
|
|
1328
|
-
const epoch = config.getEpoch();
|
|
1329
|
-
const refreshed = await client.token.refresh(config.requireCredential().refreshToken).catch((error) => {
|
|
1330
|
-
if (config.getEpoch() !== epoch) throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
1331
|
-
throw error;
|
|
1332
|
-
});
|
|
1333
|
-
if (config.getEpoch() !== epoch) throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
1334
|
-
config.applyRefresh(refreshed);
|
|
1335
|
-
return refreshed.accessToken;
|
|
1336
|
-
};
|
|
1337
|
-
const getToken = () => {
|
|
1338
|
-
const state = config.store.get();
|
|
1339
|
-
if (state.status === "anonymous") return null;
|
|
1340
|
-
const { accessToken, issuedAt, expiresIn } = state.credential;
|
|
1341
|
-
return {
|
|
1342
|
-
accessToken,
|
|
1343
|
-
timing: {
|
|
1344
|
-
issuedAt,
|
|
1345
|
-
expiresIn
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
};
|
|
1349
|
-
/**
|
|
1350
|
-
* The session's state, once it is known to carry a credential.
|
|
1351
|
-
*
|
|
1352
|
-
* - Returns the state rather than nothing, so the caller reads `baseUrl` and `credential` off a
|
|
1353
|
-
* narrowed union instead of re-checking the status to get at them.
|
|
1354
|
-
* - `NotAuthenticated`, the same refusal `requireScopedProject` opens with. The two differ only in
|
|
1355
|
-
* how far they go: this one stops at "somebody is signed in", which is all a request naming its
|
|
1356
|
-
* own project needs.
|
|
1357
|
-
*/
|
|
1358
|
-
const requireAuthenticated = () => {
|
|
1359
|
-
const state = config.store.get();
|
|
1360
|
-
if (state.status === "anonymous") throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
1361
|
-
return state;
|
|
1362
|
-
};
|
|
1363
|
-
/**
|
|
1364
|
-
* The project this session is scoped to, right now.
|
|
1365
|
-
*
|
|
1366
|
-
* - Read at every point that stamps a project, never snapshotted across an `await`: the scope moves
|
|
1367
|
-
* with `setProject`/`clearProject`, and neither ends the session, so it is not in the epoch and
|
|
1368
|
-
* `assertSameSession` cannot see it change.
|
|
1369
|
-
* - `NotReady` rather than `NotAuthenticated` when merely unscoped — the credential is fine, the
|
|
1370
|
-
* project is what is missing. A credential alone does not authorize a project request: kai-agent
|
|
1371
|
-
* reads the project from the header, not the token, and cannot attribute a call that omits it.
|
|
1372
|
-
*/
|
|
1373
|
-
const requireScopedProject = () => {
|
|
1374
|
-
const state = requireAuthenticated();
|
|
1375
|
-
if (state.status !== "scoped") throw new KeboolaError(KeboolaErrorType.NotReady);
|
|
1376
|
-
return state.projectId;
|
|
1377
|
-
};
|
|
1378
|
-
/**
|
|
1379
|
-
* The project client sets `forProject` has handed out, keyed by project id.
|
|
1380
|
-
*
|
|
1381
|
-
* - Not keyed by stack: `setBaseUrl` throws once authenticated and `signOut` empties this, so one
|
|
1382
|
-
* entry can never belong to a different `baseUrl` than the live session.
|
|
1383
|
-
* - A `lazy` per key, not a bare promise: concurrent callers share one bootstrap, later ones replay
|
|
1384
|
-
* the resolved set, and a rejected build clears only its own slot — so a build orphaned by a
|
|
1385
|
-
* sign-out cannot evict the entry that replaced it.
|
|
1386
|
-
*/
|
|
1387
|
-
const extraProjects = /* @__PURE__ */ new Map();
|
|
1388
|
-
const signOut = () => {
|
|
1389
|
-
config.reset();
|
|
1390
|
-
credential.abandonRefresh();
|
|
1391
|
-
extraProjects.clear();
|
|
1392
|
-
persistor.clear();
|
|
1393
|
-
};
|
|
1394
|
-
const onSessionExpired = () => {
|
|
1395
|
-
if (config.notifyExpired()) signOut();
|
|
1396
|
-
};
|
|
1397
|
-
const credential = require_credential.createBearerCredential({
|
|
1398
|
-
refresh,
|
|
1399
|
-
getToken,
|
|
1400
|
-
onSessionExpired
|
|
1401
|
-
});
|
|
1402
|
-
const sessionGuard = (epoch) => () => {
|
|
1403
|
-
if (config.getEpoch() !== epoch) throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
1404
|
-
};
|
|
1405
|
-
/**
|
|
1406
|
-
* The auth middleware, refusing to carry a request across a change of session.
|
|
1407
|
-
*
|
|
1408
|
-
* - The middleware holds no token — it reads the credential live — so anything built on it acts as
|
|
1409
|
-
* whoever is signed in *at request time*, not whoever built it. Rebuilding a store on sign-out
|
|
1410
|
-
* only stops new reads; a request already in flight completes as the next user, still stamped
|
|
1411
|
-
* with the old project. `config`'s epoch moves on `authenticate` and `reset` and never on a
|
|
1412
|
-
* rotation, so it is exactly "which sign-in is this".
|
|
1413
|
-
* - `epochFor` says which sign-in a request answers to, resolved per request:
|
|
1414
|
-
* 1. a client set pins to the sign-in that *built* it (`pinToSession`) — a set the caller still
|
|
1415
|
-
* holds must not ride the session that replaced it;
|
|
1416
|
-
* 2. `api.auth` pins to the sign-in the request *started* on, because its store is keyed on
|
|
1417
|
-
* `baseUrl` alone: it is built once, while anonymous, so a build-time pin would freeze at that
|
|
1418
|
-
* epoch and refuse every authenticated call.
|
|
1419
|
-
* - The guard covers every credential read, not only the wire request, so it wraps the credential
|
|
1420
|
-
* rather than sitting beside it. The 401 path rotates the token by calling the credential
|
|
1421
|
-
* directly: a straggler from a dead session would otherwise spend the *live* session's
|
|
1422
|
-
* single-use refresh token, and sign that session out if the rotation itself came back 401.
|
|
1423
|
-
* - `expiresSession` decides whether a 401 that survives a rotation means the session is dead.
|
|
1424
|
-
* True for this session's own realms; false where the 401 may be about the project instead of
|
|
1425
|
-
* the credential (see `forProject`).
|
|
1426
|
-
* - `inner` is for middleware that **re-invokes `next`** — a retry. Composed outside the pin it
|
|
1427
|
-
* would re-enter it per attempt, and with a per-request `epochFor` each attempt would re-pin to
|
|
1428
|
-
* whoever is signed in by then: a 503 backoff is exactly long enough for a sign-out to land, and
|
|
1429
|
-
* the retry then goes out as the next user. Inside, one epoch covers every attempt and each
|
|
1430
|
-
* attempt still passes the guard. Anything new that retries belongs here, not in the array.
|
|
1431
|
-
*/
|
|
1432
|
-
const createPinnedAuthMiddleware = (epochFor, { expiresSession = true, inner = [] } = {}) => {
|
|
1433
|
-
return (next) => (request) => {
|
|
1434
|
-
const assertSameSession = sessionGuard(epochFor());
|
|
1435
|
-
const pinnedCredential = {
|
|
1436
|
-
...credential,
|
|
1437
|
-
getAccessToken: async (options) => {
|
|
1438
|
-
assertSameSession();
|
|
1439
|
-
return credential.getAccessToken(options);
|
|
1440
|
-
},
|
|
1441
|
-
notifyExpired: expiresSession ? credential.notifyExpired : () => {}
|
|
1442
|
-
};
|
|
1443
|
-
const guarded = (wire) => (repeated) => {
|
|
1444
|
-
assertSameSession();
|
|
1445
|
-
return wire(repeated);
|
|
1446
|
-
};
|
|
1447
|
-
const chain = inner.reduceRight((acc, middleware) => middleware(acc), guarded(next));
|
|
1448
|
-
return createBearerAuthMiddleware(pinnedCredential)(chain)(request);
|
|
1449
|
-
};
|
|
1450
|
-
};
|
|
1451
|
-
/**
|
|
1452
|
-
* Pin something long-lived to the sign-in live right now.
|
|
1453
|
-
*
|
|
1454
|
-
* - `assertSameSession` is for whatever no middleware chain covers: a bootstrap whose every
|
|
1455
|
-
* request passed the guard before the sign-out resolves anyway and must not be handed back, and
|
|
1456
|
-
* `getAuthHeaders`, which emits the credential by hand.
|
|
1457
|
-
*/
|
|
1458
|
-
const pinToSession = (options) => {
|
|
1459
|
-
const epoch = config.getEpoch();
|
|
1460
|
-
return {
|
|
1461
|
-
assertSameSession: sessionGuard(epoch),
|
|
1462
|
-
pinnedAuthMiddleware: createPinnedAuthMiddleware(() => epoch, options)
|
|
1463
|
-
};
|
|
1464
|
-
};
|
|
1465
|
-
const authClient = createDerivedStore({
|
|
1466
|
-
source: config.store,
|
|
1467
|
-
dependsOn: (state) => state.baseUrl,
|
|
1468
|
-
build: (baseUrl) => baseUrl ? require_authClient.createAuthClient({
|
|
1469
|
-
baseUrl,
|
|
1470
|
-
middlewares: middlewareFor("auth", [createPinnedAuthMiddleware(config.getEpoch, { inner: [retryContention] })])
|
|
1471
|
-
}) : null
|
|
1472
|
-
});
|
|
1473
|
-
const management = createDerivedStore({
|
|
1474
|
-
source: config.store,
|
|
1475
|
-
dependsOn: (state) => state.status === "anonymous" ? null : state.baseUrl,
|
|
1476
|
-
build: (baseUrl) => baseUrl ? require_management.createManagementClient({
|
|
1477
|
-
baseUrl,
|
|
1478
|
-
auth: {
|
|
1479
|
-
type: "bearer",
|
|
1480
|
-
token: ""
|
|
1481
|
-
},
|
|
1482
|
-
middlewares: middlewareFor("management", [pinToSession().pinnedAuthMiddleware])
|
|
1483
|
-
}) : null
|
|
1484
|
-
});
|
|
1485
|
-
const projectClientsAsync = createDerivedAsyncStore({
|
|
1486
|
-
source: config.store,
|
|
1487
|
-
dependsOn: (state) => state.status === "scoped" ? {
|
|
1488
|
-
baseUrl: state.baseUrl,
|
|
1489
|
-
projectId: state.projectId
|
|
1490
|
-
} : null,
|
|
1491
|
-
build: ({ baseUrl, projectId }) => {
|
|
1492
|
-
return createProjectClients$1({
|
|
1493
|
-
baseUrl,
|
|
1494
|
-
authMiddleware: pinToSession().pinnedAuthMiddleware,
|
|
1495
|
-
projectId,
|
|
1496
|
-
middlewareFor,
|
|
1497
|
-
clientServiceUrls: options.clientServiceUrls
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
});
|
|
1501
|
-
const forProject = async (projectId) => {
|
|
1502
|
-
const state = requireAuthenticated();
|
|
1503
|
-
const cached = extraProjects.get(projectId);
|
|
1504
|
-
if (cached) return cached();
|
|
1505
|
-
const { assertSameSession, pinnedAuthMiddleware } = pinToSession({ expiresSession: false });
|
|
1506
|
-
const build = require_jobStatus.lazy(async () => {
|
|
1507
|
-
const clients = await createProjectClients$1({
|
|
1508
|
-
baseUrl: state.baseUrl,
|
|
1509
|
-
authMiddleware: pinnedAuthMiddleware,
|
|
1510
|
-
projectId,
|
|
1511
|
-
middlewareFor,
|
|
1512
|
-
clientServiceUrls: options.clientServiceUrls
|
|
1513
|
-
});
|
|
1514
|
-
assertSameSession();
|
|
1515
|
-
return clients;
|
|
1516
|
-
});
|
|
1517
|
-
extraProjects.set(projectId, build);
|
|
1518
|
-
return build();
|
|
1519
|
-
};
|
|
1520
|
-
if (options.baseUrl) config.setBaseUrl(options.baseUrl);
|
|
1521
|
-
config.store.subscribe((state) => {
|
|
1522
|
-
if (state.status === "anonymous") return;
|
|
1523
|
-
persistor.persist({
|
|
1524
|
-
baseUrl: state.baseUrl,
|
|
1525
|
-
projectId: state.status === "scoped" ? state.projectId : null,
|
|
1526
|
-
refreshToken: state.credential.refreshToken
|
|
1527
|
-
});
|
|
1528
|
-
});
|
|
1529
|
-
const debug = () => ({
|
|
1530
|
-
type: "Bearer",
|
|
1531
|
-
config: config.debug(),
|
|
1532
|
-
authReady: authClient.get() !== null,
|
|
1533
|
-
managementReady: management.get() !== null,
|
|
1534
|
-
storage: projectClientsAsync.store.get().state
|
|
1535
|
-
});
|
|
1536
|
-
const session = {
|
|
1537
|
-
hasPersistedValue: persistor.hasPersistedValue,
|
|
1538
|
-
setBaseUrl: config.setBaseUrl,
|
|
1539
|
-
isStorageReady: () => projectClientsAsync.store.get().state === "ready",
|
|
1540
|
-
authenticate: (tokens) => config.authenticate(tokens),
|
|
1541
|
-
setProject: config.setProjectId,
|
|
1542
|
-
setProjectAsync: async (projectId) => {
|
|
1543
|
-
config.setProjectId(projectId);
|
|
1544
|
-
await projectClientsAsync.settled();
|
|
1545
|
-
},
|
|
1546
|
-
/**
|
|
1547
|
-
* Leave the current project but stay signed in — the storage build returns to `idle`, so a
|
|
1548
|
-
* consumer gating on it falls back to its project picker. The persisted session is rewritten with
|
|
1549
|
-
* `projectId: null` by the subscription above, so a reload lands on the picker rather than
|
|
1550
|
-
* re-entering the project. Use `reset` to sign out instead.
|
|
1551
|
-
*/
|
|
1552
|
-
clearProject: config.clearProjectId,
|
|
1553
|
-
/**
|
|
1554
|
-
* The clients for one project, off this session — for reaching a second project without leaving
|
|
1555
|
-
* the first.
|
|
1556
|
-
*
|
|
1557
|
-
* - `setProjectAsync` *moves* the session (and `api`) to another project; this *adds* one. The
|
|
1558
|
-
* returned set is the caller's to hold and never appears on `api`.
|
|
1559
|
-
* - Every set shares this session's auth middleware, so one rotation serves all of them. A second
|
|
1560
|
-
* `createKeboola()` does not: both hold the same single-use refresh token, and the first to
|
|
1561
|
-
* rotate invalidates the other's, whose next refresh 401s and signs it out.
|
|
1562
|
-
* - Independent of the scope, including for the scoped project itself — asking for that one
|
|
1563
|
-
* builds a second set with its own `liveProject`, so prefer `api.*` there.
|
|
1564
|
-
*/
|
|
1565
|
-
forProject,
|
|
1566
|
-
reset: signOut,
|
|
1567
|
-
/**
|
|
1568
|
-
* Dev/test only: mark the current access token as expired so the bearer credential rotates it on
|
|
1569
|
-
* the next request. Pair with any authenticated call (e.g. `api.storage.tokens.verify()`) to force
|
|
1570
|
-
* the rotation immediately and observe the new credential.
|
|
1571
|
-
*/
|
|
1572
|
-
debugForceRefresh: config.forceExpire,
|
|
1573
|
-
/**
|
|
1574
|
-
* The headers that authorize one off-client request, resolved fresh each call.
|
|
1575
|
-
*
|
|
1576
|
-
* - For a transport this package does not own — the chat SSE stream, whose `fetch` the AI SDK
|
|
1577
|
-
* holds. Anything with a service client on `api` should use that instead.
|
|
1578
|
-
* - Returns the assembled bag, never the bare token: no caller handles the credential as a
|
|
1579
|
-
* string, and `X-KBC-ProjectId` is composed here rather than in every consuming app.
|
|
1580
|
-
* - Resolved per call, through the credential — so a near-expiry token rotates and a revoked
|
|
1581
|
-
* one is never re-sent. A snapshot held by the caller would do neither.
|
|
1582
|
-
* - Throws rather than emitting a half-formed `Bearer null`: a session with no credential has
|
|
1583
|
-
* nothing to authorize with.
|
|
1584
|
-
* - Pinned to the sign-in it was called on, like every client here. This is the one
|
|
1585
|
-
* credential-emitting surface with no middleware chain for the guard to sit in, so the check
|
|
1586
|
-
* is written out around the `await` that rotates the token.
|
|
1587
|
-
*
|
|
1588
|
-
* `projectId` names the project the headers authorize, for a caller reaching a project the
|
|
1589
|
-
* session is not scoped to:
|
|
1590
|
-
*
|
|
1591
|
-
* - **Omitted**, the session's own scope answers — unchanged, and re-read *after* the rotation
|
|
1592
|
-
* rather than carried across it. The epoch the pin compares moves on a sign-in and a sign-out
|
|
1593
|
-
* only, so a project switch mid-rotation is invisible to it.
|
|
1594
|
-
* - **Given**, it is what the header says, and the scope is not consulted at all. A caller that
|
|
1595
|
-
* names a project has said everything the request needs: the credential is user-level and the
|
|
1596
|
-
* project is a header on top of it, so an authenticated session suffices where an unnamed call
|
|
1597
|
-
* requires a scoped one. Nothing about the session can change the answer, so there is nothing
|
|
1598
|
-
* to re-read after the `await`.
|
|
1599
|
-
* - **Not checked against an access set**, because this session cannot acquire one:
|
|
1600
|
-
* `forProject` — the only other surface taking a caller-supplied project id — records that
|
|
1601
|
-
* connection collapses every bearer failure into a single `invalid_token` 401, so "no access
|
|
1602
|
-
* to this project" and "revoked token" are the same response. Whether the caller may reach a
|
|
1603
|
-
* project is the caller's own knowledge, from `token.introspect`. What is guaranteed here is
|
|
1604
|
-
* that the header names what was asked for and never something else — which is the failure
|
|
1605
|
-
* worth preventing: a turn sent somewhere the transcript above it did not come from.
|
|
1606
|
-
*/
|
|
1607
|
-
getAuthHeaders: async (projectId) => {
|
|
1608
|
-
const { assertSameSession } = pinToSession();
|
|
1609
|
-
if (projectId === void 0) requireScopedProject();
|
|
1610
|
-
else requireAuthenticated();
|
|
1611
|
-
const accessToken = await credential.getAccessToken();
|
|
1612
|
-
assertSameSession();
|
|
1613
|
-
const authorizedProjectId = projectId ?? requireScopedProject();
|
|
1614
|
-
assert$1(accessToken, KeboolaErrorType.NotAuthenticated);
|
|
1615
|
-
return {
|
|
1616
|
-
[require_createFetchClient.HttpHeader.AUTHORIZATION]: `Bearer ${accessToken}`,
|
|
1617
|
-
[require_createFetchClient.KeboolaHttpHeader.PROJECT_ID]: String(authorizedProjectId)
|
|
1618
|
-
};
|
|
1619
|
-
},
|
|
1620
|
-
restore: async () => {
|
|
1621
|
-
const persisted = persistor.hydrate();
|
|
1622
|
-
if (!persisted.refreshToken || !persisted.baseUrl) return { status: "empty" };
|
|
1623
|
-
const configured = config.getBaseUrl();
|
|
1624
|
-
if (configured && configured !== persisted.baseUrl) {
|
|
1625
|
-
persistor.clear();
|
|
1626
|
-
return { status: "empty" };
|
|
1627
|
-
}
|
|
1628
|
-
if (!configured) config.setBaseUrl(persisted.baseUrl);
|
|
1629
|
-
const client = authClientBare.get();
|
|
1630
|
-
assert$1(client, KeboolaErrorType.NotReady);
|
|
1631
|
-
const startedOn = config.getEpoch();
|
|
1632
|
-
try {
|
|
1633
|
-
const refreshed = await client.token.refresh(persisted.refreshToken);
|
|
1634
|
-
if (config.getEpoch() !== startedOn) return { status: "empty" };
|
|
1635
|
-
config.authenticate(refreshed);
|
|
1636
|
-
if (persisted.projectId !== null) {
|
|
1637
|
-
config.setProjectId(persisted.projectId);
|
|
1638
|
-
await projectClientsAsync.settled();
|
|
1639
|
-
}
|
|
1640
|
-
return { status: "restored" };
|
|
1641
|
-
} catch (err) {
|
|
1642
|
-
if (config.getEpoch() !== startedOn) return { status: "empty" };
|
|
1643
|
-
if (require_credential.isUnauthorized(err)) {
|
|
1644
|
-
persistor.clear();
|
|
1645
|
-
return { status: "expired" };
|
|
1646
|
-
}
|
|
1647
|
-
throw err;
|
|
1648
|
-
}
|
|
1649
|
-
},
|
|
1650
|
-
subscribe: (listener) => {
|
|
1651
|
-
const unsubscribeConfig = config.store.subscribe(() => listener(debug()));
|
|
1652
|
-
const unsubscribeBuild = projectClientsAsync.store.subscribe(() => listener(debug()));
|
|
1653
|
-
return () => {
|
|
1654
|
-
unsubscribeConfig();
|
|
1655
|
-
unsubscribeBuild();
|
|
1656
|
-
};
|
|
1657
|
-
},
|
|
1658
|
-
debug
|
|
1659
|
-
};
|
|
1660
|
-
return {
|
|
1661
|
-
api: createApi({
|
|
1662
|
-
getAuthClient: () => authClient.get(),
|
|
1663
|
-
getManagementClient: () => management.get(),
|
|
1664
|
-
getProjectClients: () => {
|
|
1665
|
-
const state = projectClientsAsync.store.get();
|
|
1666
|
-
return state.state === "ready" ? state.value : null;
|
|
1667
|
-
}
|
|
1668
|
-
}),
|
|
1669
|
-
session
|
|
1670
|
-
};
|
|
1671
|
-
};
|
|
1672
|
-
//#endregion
|
|
1673
|
-
//#region src/keboola/legacy/createConfig.ts
|
|
1674
|
-
const createConfig = () => {
|
|
1675
|
-
const store = require_store.createStore({
|
|
1676
|
-
baseUrl: null,
|
|
1677
|
-
sapiToken: null,
|
|
1678
|
-
managementToken: null
|
|
1679
|
-
});
|
|
1680
|
-
const setBaseUrl = (baseUrl) => {
|
|
1681
|
-
store.set({
|
|
1682
|
-
...store.get(),
|
|
1683
|
-
baseUrl
|
|
1684
|
-
});
|
|
1685
|
-
};
|
|
1686
|
-
const authenticate = (tokens) => {
|
|
1687
|
-
const current = store.get();
|
|
1688
|
-
store.set({
|
|
1689
|
-
...current,
|
|
1690
|
-
sapiToken: tokens.sapiToken ?? current.sapiToken,
|
|
1691
|
-
managementToken: tokens.managementToken ?? current.managementToken
|
|
1692
|
-
});
|
|
1693
|
-
};
|
|
1694
|
-
const getStorageToken = () => store.get().sapiToken ?? "";
|
|
1695
|
-
const setStorageToken = (sapiToken) => store.set({
|
|
1696
|
-
...store.get(),
|
|
1697
|
-
sapiToken
|
|
1698
|
-
});
|
|
1699
|
-
/** Current state, for diagnostics. */
|
|
1700
|
-
const debug = () => store.get();
|
|
1701
|
-
return {
|
|
1702
|
-
store,
|
|
1703
|
-
setBaseUrl,
|
|
1704
|
-
authenticate,
|
|
1705
|
-
getStorageToken,
|
|
1706
|
-
setStorageToken,
|
|
1707
|
-
debug
|
|
1708
|
-
};
|
|
1709
|
-
};
|
|
1710
|
-
//#endregion
|
|
1711
|
-
//#region src/keboola/legacy/createProjectClients.ts
|
|
1712
|
-
const createAttachStorageToken = (getToken) => (next) => (request) => {
|
|
1713
|
-
request.request.headers.set(require_createFetchClient.KeboolaHttpHeader.STORAGE_API_TOKEN, getToken());
|
|
1714
|
-
return next(request);
|
|
1715
|
-
};
|
|
1716
|
-
const createProjectClients = async ({ baseUrl, getStorageToken, onStorageTokenRefreshed, middlewareFor, clientServiceUrls }) => {
|
|
1717
|
-
const auth = {
|
|
1718
|
-
type: "sapi-token",
|
|
1719
|
-
token: getStorageToken()
|
|
1720
|
-
};
|
|
1721
|
-
const attach = createAttachStorageToken(getStorageToken);
|
|
1722
|
-
const _storage = require_storage.createStorageClient({
|
|
1723
|
-
baseUrl,
|
|
1724
|
-
auth,
|
|
1725
|
-
middlewares: [attach]
|
|
1726
|
-
});
|
|
1727
|
-
const [stackInfo, sapiToken, devBranches] = await Promise.all([
|
|
1728
|
-
_storage.getStackInfo({ exclude: "componentDetails" }),
|
|
1729
|
-
_storage.tokens.verify(),
|
|
1730
|
-
_storage.branches.getDevBranches()
|
|
1731
|
-
]);
|
|
1732
|
-
const stack = createStack({
|
|
1733
|
-
stackInfo,
|
|
1734
|
-
clientServiceUrls
|
|
1735
|
-
});
|
|
1736
|
-
const defaultBranch = createDefaultBranch({ devBranches });
|
|
1737
|
-
const liveProject = createLiveProject({
|
|
1738
|
-
initialToken: sapiToken,
|
|
1739
|
-
verify: () => _storage.tokens.verify()
|
|
1740
|
-
});
|
|
1741
|
-
const storageTableStore = createDerivedStore({
|
|
1742
|
-
source: liveProject.store,
|
|
1743
|
-
build: require_storageTable.createStorageTable,
|
|
1744
|
-
dependsOn: (project) => ({
|
|
1745
|
-
useLegacyBucketPrefix: !project.hasFeature(require_constants.ProjectFeature.DisableLegacyBucketPrefix),
|
|
1746
|
-
hasProtectedDefaultBranch: project.hasFeature(require_constants.ProjectFeature.SoxProtectedDefaultBranch),
|
|
1747
|
-
hasStorageBranches: project.hasFeature(require_constants.ProjectFeature.StorageBranches)
|
|
1748
|
-
})
|
|
1749
|
-
});
|
|
1750
|
-
const context = {
|
|
1751
|
-
baseUrl: require_createFetchClient.normalizeBaseUrl(baseUrl),
|
|
1752
|
-
stack,
|
|
1753
|
-
defaultBranch,
|
|
1754
|
-
get project() {
|
|
1755
|
-
return liveProject.getProject();
|
|
1756
|
-
},
|
|
1757
|
-
get st() {
|
|
1758
|
-
return storageTableStore.get();
|
|
1759
|
-
}
|
|
1760
|
-
};
|
|
1761
|
-
const storage = require_storage.createStorageClient({
|
|
1762
|
-
baseUrl,
|
|
1763
|
-
auth,
|
|
1764
|
-
middlewares: middlewareFor("storage", [liveProject.middleware]),
|
|
1765
|
-
onRefreshToken: onStorageTokenRefreshed
|
|
957
|
+
middlewares: middlewareFor("storage", [liveProject.middleware]),
|
|
958
|
+
onRefreshToken: onStorageTokenRefreshed
|
|
1766
959
|
});
|
|
1767
960
|
const { client: services, meta: serviceMeta } = createServiceClients({
|
|
1768
961
|
baseUrl,
|
|
@@ -1779,6 +972,7 @@ const createProjectClients = async ({ baseUrl, getStorageToken, onStorageTokenRe
|
|
|
1779
972
|
}),
|
|
1780
973
|
tag: require_tag.createTagSdk({ metastoreClient: services.metastore }),
|
|
1781
974
|
semanticLayer: require_semanticLayer.createSemanticLayerSdk({ metastoreClient: services.metastore }),
|
|
975
|
+
kaiMemory: require_kaiMemory.createKaiMemorySdk({ metastoreClient: services.metastore }),
|
|
1782
976
|
storage: require_sdk_storage_index.createStorageSdk({ storageClient: storage }),
|
|
1783
977
|
vault: require_sdk_vault_index.createVaultSdk({
|
|
1784
978
|
vaultClient: services.vault,
|
|
@@ -1876,6 +1070,1407 @@ const createLegacyKeboola = (options = {}) => {
|
|
|
1876
1070
|
};
|
|
1877
1071
|
};
|
|
1878
1072
|
//#endregion
|
|
1073
|
+
//#region src/keboola/bearer/credentials/renewal.ts
|
|
1074
|
+
const renewalGrant = (credential) => credential.type === "token" ? {
|
|
1075
|
+
type: "token",
|
|
1076
|
+
refreshToken: credential.refreshToken
|
|
1077
|
+
} : {
|
|
1078
|
+
type: "cookie",
|
|
1079
|
+
sessionId: credential.sessionId
|
|
1080
|
+
};
|
|
1081
|
+
const renew = async (client, grant) => {
|
|
1082
|
+
if (grant.type === "cookie") {
|
|
1083
|
+
const { accessToken, expiresIn, sessionId } = await client.token.refreshWithSession({ sessionId: grant.sessionId });
|
|
1084
|
+
return {
|
|
1085
|
+
type: "cookie",
|
|
1086
|
+
accessToken,
|
|
1087
|
+
expiresIn,
|
|
1088
|
+
sessionId
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const { accessToken, expiresIn, refreshToken } = await client.token.refreshWithToken({ refreshToken: grant.refreshToken });
|
|
1092
|
+
if (!refreshToken) throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
1093
|
+
return {
|
|
1094
|
+
type: "token",
|
|
1095
|
+
accessToken,
|
|
1096
|
+
expiresIn,
|
|
1097
|
+
refreshToken
|
|
1098
|
+
};
|
|
1099
|
+
};
|
|
1100
|
+
//#endregion
|
|
1101
|
+
//#region src/keboola/bearer/credentials/credentials.ts
|
|
1102
|
+
const DEFAULT_REFRESH_THRESHOLD = .9;
|
|
1103
|
+
const signInEnded = () => new KeboolaError(KeboolaErrorType.SignInEnded);
|
|
1104
|
+
const createCredentials = ({ state, persistor, middlewares, mode = "token", refreshThreshold = DEFAULT_REFRESH_THRESHOLD, onSignedOut }) => {
|
|
1105
|
+
const rotationMap = /* @__PURE__ */ new Map();
|
|
1106
|
+
const generations = /* @__PURE__ */ new Map();
|
|
1107
|
+
const generationOf = (baseUrl) => generations.get(baseUrl) ?? 0;
|
|
1108
|
+
const refresh = async (baseUrl, grant) => {
|
|
1109
|
+
const client = require_authClient.createAuthClient({
|
|
1110
|
+
baseUrl,
|
|
1111
|
+
middlewares
|
|
1112
|
+
});
|
|
1113
|
+
const epochBefore = require_jobStatus.Result.unwrap(state.auth.getEpoch(baseUrl));
|
|
1114
|
+
const generationBefore = generationOf(baseUrl);
|
|
1115
|
+
const refreshed = await require_jobStatus.Result.tryCatchAsync(() => renew(client, grant));
|
|
1116
|
+
return require_jobStatus.Result.unwrapOr(state.auth.getEpoch(baseUrl), -1) === epochBefore && generationOf(baseUrl) === generationBefore ? refreshed : require_jobStatus.Result.failure(signInEnded());
|
|
1117
|
+
};
|
|
1118
|
+
/**
|
|
1119
|
+
* A stack's teardown, from the credential's side.
|
|
1120
|
+
*
|
|
1121
|
+
* - Bumps the generation, so an in-flight rotation can no longer write.
|
|
1122
|
+
* - Drops the deduped entry, so nothing joins that rotation.
|
|
1123
|
+
*/
|
|
1124
|
+
const forget = (baseUrl) => {
|
|
1125
|
+
generations.set(baseUrl, generationOf(baseUrl) + 1);
|
|
1126
|
+
rotationMap.delete(baseUrl);
|
|
1127
|
+
};
|
|
1128
|
+
/**
|
|
1129
|
+
* The rotation, and the one place a credential is declared dead.
|
|
1130
|
+
*
|
|
1131
|
+
* - A refused refresh token or session is unambiguous.
|
|
1132
|
+
* - The triggering 401 is not (an unreachable project answers the same), so never sign out on a replay.
|
|
1133
|
+
*/
|
|
1134
|
+
const rotateOnce = async (baseUrl) => {
|
|
1135
|
+
const credential = state.auth.requireCredentialOf(baseUrl, mode);
|
|
1136
|
+
if (!credential.success) return credential;
|
|
1137
|
+
const refreshed = await refresh(baseUrl, renewalGrant(credential.data));
|
|
1138
|
+
if (!refreshed.success) {
|
|
1139
|
+
if (require_middleware.isUnauthorized(refreshed.error)) expire(baseUrl);
|
|
1140
|
+
return refreshed;
|
|
1141
|
+
}
|
|
1142
|
+
return require_jobStatus.Result.map(state.auth.applyRefresh(baseUrl, refreshed.data), () => refreshed.data.accessToken);
|
|
1143
|
+
};
|
|
1144
|
+
const rotate = (baseUrl) => ensure(rotationMap, baseUrl, () => require_dedupe.dedupe(() => rotateOnce(baseUrl)))();
|
|
1145
|
+
const restoreStack = async (baseUrl, grant) => {
|
|
1146
|
+
if (!state.auth.startRestore(baseUrl).success) return "unreachable";
|
|
1147
|
+
const refreshed = await refresh(baseUrl, grant);
|
|
1148
|
+
if (!refreshed.success) {
|
|
1149
|
+
state.auth.cancelRestore(baseUrl);
|
|
1150
|
+
return require_middleware.isUnauthorized(refreshed.error) ? "refused" : "unreachable";
|
|
1151
|
+
}
|
|
1152
|
+
state.auth.authenticate(baseUrl, refreshed.data);
|
|
1153
|
+
return "restored";
|
|
1154
|
+
};
|
|
1155
|
+
const restoreProjects = (baseUrl, projects) => {
|
|
1156
|
+
for (const { projectId } of projects) state.projects.open(baseUrl, projectId);
|
|
1157
|
+
};
|
|
1158
|
+
/**
|
|
1159
|
+
* Every persisted stack back into the table, with whatever credential it can hand back.
|
|
1160
|
+
*
|
|
1161
|
+
* - Never writes the scope. `lastActive` is what was scoped when the session was stored, for the
|
|
1162
|
+
* caller to select.
|
|
1163
|
+
* - `status` reports what became of `lastActive`'s credential; the client reads it to decide
|
|
1164
|
+
* when persistence may start.
|
|
1165
|
+
*/
|
|
1166
|
+
const restore = async () => {
|
|
1167
|
+
const hydrated = persistor.hydrate();
|
|
1168
|
+
const stacks = hydrated.stacks.map((stack) => stack.credential && stack.credential.type !== "static" && stack.credential.type !== mode ? {
|
|
1169
|
+
...stack,
|
|
1170
|
+
credential: null
|
|
1171
|
+
} : stack);
|
|
1172
|
+
const { current } = hydrated;
|
|
1173
|
+
const scoped = stacks.find(({ baseUrl }) => baseUrl === current?.baseUrl);
|
|
1174
|
+
const restored = stacks.map(async ({ baseUrl, credential, projects }) => {
|
|
1175
|
+
state.stacks.ensure(baseUrl);
|
|
1176
|
+
if (!credential) return "unreachable";
|
|
1177
|
+
if (require_jobStatus.Result.unwrapOr(state.auth.getEpoch(baseUrl), 0) > 0) return "skipped";
|
|
1178
|
+
const outcome = credential.type === "static" ? state.auth.authenticate(baseUrl, credential).success ? "restored" : "refused" : await restoreStack(baseUrl, credential);
|
|
1179
|
+
if (outcome === "restored") restoreProjects(baseUrl, projects);
|
|
1180
|
+
return outcome;
|
|
1181
|
+
});
|
|
1182
|
+
const results = await Promise.all(restored);
|
|
1183
|
+
const outcome = scoped?.credential ? results[stacks.indexOf(scoped)] : void 0;
|
|
1184
|
+
return {
|
|
1185
|
+
lastActive: current,
|
|
1186
|
+
status: outcome === void 0 || outcome === "skipped" ? "empty" : outcome === "restored" ? "restored" : outcome === "unreachable" ? "unreachable" : "expired"
|
|
1187
|
+
};
|
|
1188
|
+
};
|
|
1189
|
+
const isStale = (baseUrl) => {
|
|
1190
|
+
const ageing = require_jobStatus.Result.map(state.auth.requireCredentialOf(baseUrl, mode), ({ issuedAt, expiresIn }) => Date.now() >= issuedAt + expiresIn * 1e3 * refreshThreshold);
|
|
1191
|
+
return require_jobStatus.Result.unwrapOr(ageing, false);
|
|
1192
|
+
};
|
|
1193
|
+
const signOut = (baseUrl) => {
|
|
1194
|
+
rotationMap.delete(baseUrl);
|
|
1195
|
+
const signedOut = state.auth.signOut(baseUrl);
|
|
1196
|
+
onSignedOut?.(baseUrl);
|
|
1197
|
+
return signedOut;
|
|
1198
|
+
};
|
|
1199
|
+
const expire = (baseUrl) => {
|
|
1200
|
+
if (state.auth.notifyExpired(baseUrl)) signOut(baseUrl);
|
|
1201
|
+
};
|
|
1202
|
+
const canRefresh = (baseUrl) => state.auth.requireCredentialOf(baseUrl, mode).success;
|
|
1203
|
+
const middleware = (baseUrl) => require_middleware.createBearerAuthMiddleware({ getAccessToken: async ({ forceRefresh = false } = {}) => {
|
|
1204
|
+
if (forceRefresh) return canRefresh(baseUrl) ? require_jobStatus.Result.unwrap(await rotate(baseUrl)) : null;
|
|
1205
|
+
if (!isStale(baseUrl)) return state.auth.getAccessToken(baseUrl);
|
|
1206
|
+
return require_jobStatus.Result.unwrapOr(await rotate(baseUrl), state.auth.getAccessToken(baseUrl));
|
|
1207
|
+
} });
|
|
1208
|
+
return {
|
|
1209
|
+
middleware,
|
|
1210
|
+
rotate,
|
|
1211
|
+
restore,
|
|
1212
|
+
signOut,
|
|
1213
|
+
forget
|
|
1214
|
+
};
|
|
1215
|
+
};
|
|
1216
|
+
//#endregion
|
|
1217
|
+
//#region src/keboola/bearer/state/factories.ts
|
|
1218
|
+
/**
|
|
1219
|
+
* Everything the table constructs: the entries themselves, and the failures reading them produces.
|
|
1220
|
+
*
|
|
1221
|
+
* Nothing here touches the store — a factory takes what it is told and returns a value, so the
|
|
1222
|
+
* namespaces the tables build on can be read without following a dependency.
|
|
1223
|
+
*/
|
|
1224
|
+
const projectEntryId = (baseUrl, projectId) => `${baseUrl}#${projectId}`;
|
|
1225
|
+
/** What every project entry of one stack shares — the one thing a per-stack teardown matches on. */
|
|
1226
|
+
const projectEntryPrefix = (baseUrl) => `${baseUrl}#`;
|
|
1227
|
+
const isUnderStack = (baseUrl) => (id) => id.startsWith(projectEntryPrefix(baseUrl));
|
|
1228
|
+
const anonymousAuth = () => ({ status: "anonymous" });
|
|
1229
|
+
const restoringAuth = () => ({ status: "restoring" });
|
|
1230
|
+
const authenticatedAuth = (credential) => ({
|
|
1231
|
+
status: "authenticated",
|
|
1232
|
+
credential
|
|
1233
|
+
});
|
|
1234
|
+
const bearerCredential = (input) => input.type === "static" ? input : {
|
|
1235
|
+
...input,
|
|
1236
|
+
issuedAt: Date.now()
|
|
1237
|
+
};
|
|
1238
|
+
const expiredCredential = (credential) => ({
|
|
1239
|
+
...credential,
|
|
1240
|
+
issuedAt: Date.now() - credential.expiresIn * 1e3
|
|
1241
|
+
});
|
|
1242
|
+
const stack = (baseUrl) => ({
|
|
1243
|
+
baseUrl,
|
|
1244
|
+
context: "idle",
|
|
1245
|
+
epoch: 0,
|
|
1246
|
+
auth: anonymousAuth()
|
|
1247
|
+
});
|
|
1248
|
+
const project = (baseUrl, projectId) => ({
|
|
1249
|
+
id: projectEntryId(baseUrl, projectId),
|
|
1250
|
+
baseUrl,
|
|
1251
|
+
projectId,
|
|
1252
|
+
context: "idle"
|
|
1253
|
+
});
|
|
1254
|
+
const stackAddress = (baseUrl) => ({
|
|
1255
|
+
baseUrl,
|
|
1256
|
+
projectId: null
|
|
1257
|
+
});
|
|
1258
|
+
const projectAddress = (baseUrl, projectId) => ({
|
|
1259
|
+
baseUrl,
|
|
1260
|
+
projectId
|
|
1261
|
+
});
|
|
1262
|
+
const table = (defaultBaseUrl) => ({
|
|
1263
|
+
active: stackAddress(defaultBaseUrl),
|
|
1264
|
+
stacks: { [defaultBaseUrl]: stack(defaultBaseUrl) },
|
|
1265
|
+
projects: {}
|
|
1266
|
+
});
|
|
1267
|
+
const Entry = {
|
|
1268
|
+
projectEntryId,
|
|
1269
|
+
isUnderStack,
|
|
1270
|
+
stack,
|
|
1271
|
+
project,
|
|
1272
|
+
stackAddress,
|
|
1273
|
+
projectAddress,
|
|
1274
|
+
table,
|
|
1275
|
+
anonymousAuth,
|
|
1276
|
+
restoringAuth,
|
|
1277
|
+
authenticatedAuth,
|
|
1278
|
+
bearerCredential,
|
|
1279
|
+
expiredCredential
|
|
1280
|
+
};
|
|
1281
|
+
/**
|
|
1282
|
+
* Every failure the table can produce.
|
|
1283
|
+
*
|
|
1284
|
+
* - The typed ones carry a `KeboolaErrorType` a consumer branches on; the rest are plain `Error`s
|
|
1285
|
+
* whose message names the entry, because nothing branches on them.
|
|
1286
|
+
*/
|
|
1287
|
+
const Errors = {
|
|
1288
|
+
notAuthenticated: () => new KeboolaError(KeboolaErrorType.NotAuthenticated),
|
|
1289
|
+
stackActive: () => new KeboolaError(KeboolaErrorType.StackActive),
|
|
1290
|
+
/** A stack that was never opened — nothing has been configured for it. */
|
|
1291
|
+
unconfigured: () => new KeboolaError(KeboolaErrorType.Unconfigured),
|
|
1292
|
+
alreadyAuthenticated: () => new KeboolaError(KeboolaErrorType.AlreadyAuthenticated),
|
|
1293
|
+
notReady: () => new KeboolaError(KeboolaErrorType.NotReady),
|
|
1294
|
+
alreadyRestoring: (baseUrl) => /* @__PURE__ */ new Error(`Stack "${baseUrl}" is already being restored`),
|
|
1295
|
+
notRestoring: (baseUrl) => /* @__PURE__ */ new Error(`Stack "${baseUrl}" is not being restored`),
|
|
1296
|
+
unexpectedCredentialKind: (baseUrl, kind) => /* @__PURE__ */ new Error(`Stack "${baseUrl}" holds a ${kind} credential`),
|
|
1297
|
+
notRotatingCredential: (baseUrl) => /* @__PURE__ */ new Error(`Stack "${baseUrl}" holds a static credential`),
|
|
1298
|
+
credentialKindChanged: (from, to) => /* @__PURE__ */ new Error(`A ${from} credential cannot become ${to}`),
|
|
1299
|
+
missingProject: (id) => /* @__PURE__ */ new Error(`No project "${id}"`)
|
|
1300
|
+
};
|
|
1301
|
+
const authenticated = (auth) => auth.status === "authenticated" ? require_jobStatus.Result.success(auth.credential) : require_jobStatus.Result.failure(Errors.notAuthenticated());
|
|
1302
|
+
//#endregion
|
|
1303
|
+
//#region src/keboola/bearer/state/auth.ts
|
|
1304
|
+
/**
|
|
1305
|
+
* Fire-once per sign-in: the first report of a dead credential returns `true`, the rest `false`,
|
|
1306
|
+
* and `authenticate` re-arms it. Held beside the table because it is not state anyone restores.
|
|
1307
|
+
*/
|
|
1308
|
+
const createExpiryLatch = () => {
|
|
1309
|
+
const notified = /* @__PURE__ */ new Map();
|
|
1310
|
+
return {
|
|
1311
|
+
arm: (baseUrl) => notified.set(baseUrl, false),
|
|
1312
|
+
forget: (baseUrl) => notified.delete(baseUrl),
|
|
1313
|
+
report: (baseUrl) => {
|
|
1314
|
+
if (notified.get(baseUrl)) return false;
|
|
1315
|
+
notified.set(baseUrl, true);
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
};
|
|
1320
|
+
/**
|
|
1321
|
+
* The stack's credential and its sign-in pin.
|
|
1322
|
+
*
|
|
1323
|
+
* - `epoch` moves on `authenticate` and `signOut` and never on a rotation, so it is exactly "which
|
|
1324
|
+
* sign-in is this" — what an in-flight exchange compares itself against.
|
|
1325
|
+
*/
|
|
1326
|
+
const createAuth = ({ stacks, projects, expiry }) => {
|
|
1327
|
+
const requireUnauthenticated = (baseUrl) => require_jobStatus.Result.andThen(stacks.get(baseUrl), (stack) => stack.auth.status === "authenticated" ? require_jobStatus.Result.failure(Errors.alreadyAuthenticated()) : require_jobStatus.Result.success(stack));
|
|
1328
|
+
const requireAnonymous = (baseUrl) => require_jobStatus.Result.andThen(requireUnauthenticated(baseUrl), (stack) => stack.auth.status === "restoring" ? require_jobStatus.Result.failure(Errors.alreadyRestoring(baseUrl)) : require_jobStatus.Result.success(stack));
|
|
1329
|
+
const requireRestoring = (baseUrl) => require_jobStatus.Result.andThen(stacks.get(baseUrl), (stack) => stack.auth.status === "restoring" ? require_jobStatus.Result.success(stack) : require_jobStatus.Result.failure(Errors.notRestoring(baseUrl)));
|
|
1330
|
+
const requireRotatingCredential = (baseUrl) => require_jobStatus.Result.andThen(stacks.requireCredential(baseUrl), (credential) => credential.type === "static" ? require_jobStatus.Result.failure(Errors.notRotatingCredential(baseUrl)) : require_jobStatus.Result.success(credential));
|
|
1331
|
+
const requireCredentialOf = (baseUrl, mode) => require_jobStatus.Result.andThen(requireRotatingCredential(baseUrl), (credential) => credential.type === mode ? require_jobStatus.Result.success(credential) : require_jobStatus.Result.failure(Errors.unexpectedCredentialKind(baseUrl, credential.type)));
|
|
1332
|
+
const startRestore = (baseUrl) => require_jobStatus.Result.map(requireAnonymous(baseUrl), (stack) => stacks.set({
|
|
1333
|
+
...stack,
|
|
1334
|
+
auth: Entry.restoringAuth()
|
|
1335
|
+
}));
|
|
1336
|
+
const cancelRestore = (baseUrl) => require_jobStatus.Result.map(requireRestoring(baseUrl), (stack) => stacks.set({
|
|
1337
|
+
...stack,
|
|
1338
|
+
auth: Entry.anonymousAuth()
|
|
1339
|
+
}));
|
|
1340
|
+
const authenticate = (baseUrl, input) => require_jobStatus.Result.map(requireUnauthenticated(baseUrl), (stack) => {
|
|
1341
|
+
expiry.arm(baseUrl);
|
|
1342
|
+
return stacks.set({
|
|
1343
|
+
...stack,
|
|
1344
|
+
epoch: stack.epoch + 1,
|
|
1345
|
+
auth: Entry.authenticatedAuth(Entry.bearerCredential(input))
|
|
1346
|
+
});
|
|
1347
|
+
});
|
|
1348
|
+
const applyRefresh = (baseUrl, input) => require_jobStatus.Result.andThen(requireRotatingCredential(baseUrl), (credential) => credential.type !== input.type ? require_jobStatus.Result.failure(Errors.credentialKindChanged(credential.type, input.type)) : require_jobStatus.Result.map(stacks.get(baseUrl), (stack) => stacks.set({
|
|
1349
|
+
...stack,
|
|
1350
|
+
auth: Entry.authenticatedAuth(Entry.bearerCredential(input))
|
|
1351
|
+
})));
|
|
1352
|
+
const forceExpire = (baseUrl) => require_jobStatus.Result.andThen(requireRotatingCredential(baseUrl), (credential) => require_jobStatus.Result.map(stacks.get(baseUrl), (stack) => stacks.set({
|
|
1353
|
+
...stack,
|
|
1354
|
+
auth: Entry.authenticatedAuth(Entry.expiredCredential(credential))
|
|
1355
|
+
})));
|
|
1356
|
+
const signOut = (baseUrl) => require_jobStatus.Result.map(stacks.get(baseUrl), (stack) => {
|
|
1357
|
+
projects.closeAll(baseUrl);
|
|
1358
|
+
return stacks.set({
|
|
1359
|
+
...stack,
|
|
1360
|
+
epoch: stack.epoch + 1,
|
|
1361
|
+
auth: Entry.anonymousAuth()
|
|
1362
|
+
});
|
|
1363
|
+
});
|
|
1364
|
+
const getEpoch = (baseUrl) => require_jobStatus.Result.map(stacks.get(baseUrl), (stack) => stack.epoch);
|
|
1365
|
+
const getAccessToken = (baseUrl) => require_jobStatus.Result.unwrapOr(require_jobStatus.Result.map(stacks.requireCredential(baseUrl), ({ accessToken }) => accessToken), null);
|
|
1366
|
+
return {
|
|
1367
|
+
startRestore,
|
|
1368
|
+
cancelRestore,
|
|
1369
|
+
authenticate,
|
|
1370
|
+
applyRefresh,
|
|
1371
|
+
forceExpire,
|
|
1372
|
+
signOut,
|
|
1373
|
+
notifyExpired: expiry.report,
|
|
1374
|
+
requireCredential: stacks.requireCredential,
|
|
1375
|
+
requireRotatingCredential,
|
|
1376
|
+
requireCredentialOf,
|
|
1377
|
+
getEpoch,
|
|
1378
|
+
getAccessToken
|
|
1379
|
+
};
|
|
1380
|
+
};
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/keboola/bearer/state/projects.ts
|
|
1383
|
+
/** The projects open under a stack, and what authorizes a request to one. */
|
|
1384
|
+
const createProjects = ({ store, stacks }) => {
|
|
1385
|
+
const has = (id) => id in store.get().projects;
|
|
1386
|
+
const get = (id) => {
|
|
1387
|
+
const project = store.get().projects[id];
|
|
1388
|
+
return project ? require_jobStatus.Result.success(project) : require_jobStatus.Result.failure(Errors.missingProject(id));
|
|
1389
|
+
};
|
|
1390
|
+
const list = (baseUrl) => Object.values(store.get().projects).filter((project) => project.baseUrl === baseUrl);
|
|
1391
|
+
/** Every open project with this id, whatever stack it sits on — a project number is not unique. */
|
|
1392
|
+
const findAll = (projectId) => Object.values(store.get().projects).filter((project) => project.projectId === projectId);
|
|
1393
|
+
const set = (project) => {
|
|
1394
|
+
const state = store.get();
|
|
1395
|
+
store.set({
|
|
1396
|
+
...state,
|
|
1397
|
+
projects: {
|
|
1398
|
+
...state.projects,
|
|
1399
|
+
[project.id]: project
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
return project;
|
|
1403
|
+
};
|
|
1404
|
+
const update = (id, updater) => require_jobStatus.Result.map(get(id), (project) => set(updater(project)));
|
|
1405
|
+
const removeMany = (ids, nextActive) => {
|
|
1406
|
+
const state = store.get();
|
|
1407
|
+
const projects = { ...state.projects };
|
|
1408
|
+
for (const id of ids) delete projects[id];
|
|
1409
|
+
store.set({
|
|
1410
|
+
...state,
|
|
1411
|
+
active: nextActive,
|
|
1412
|
+
projects
|
|
1413
|
+
});
|
|
1414
|
+
};
|
|
1415
|
+
const requireCredential = (id) => require_jobStatus.Result.andThen(get(id), (project) => stacks.requireCredential(project.baseUrl));
|
|
1416
|
+
const open = (baseUrl, projectId) => {
|
|
1417
|
+
const id = Entry.projectEntryId(baseUrl, projectId);
|
|
1418
|
+
if (has(id)) return get(id);
|
|
1419
|
+
return require_jobStatus.Result.map(stacks.requireCredential(baseUrl), () => set(Entry.project(baseUrl, projectId)));
|
|
1420
|
+
};
|
|
1421
|
+
const close = (id) => require_jobStatus.Result.tap(get(id), ({ baseUrl, projectId }) => {
|
|
1422
|
+
const { active } = store.get();
|
|
1423
|
+
const isScoped = active.baseUrl === baseUrl && active.projectId === projectId;
|
|
1424
|
+
removeMany([id], isScoped ? Entry.stackAddress(baseUrl) : active);
|
|
1425
|
+
});
|
|
1426
|
+
/** Every project under a stack, with the scope falling back to the stack itself. */
|
|
1427
|
+
const closeAll = (baseUrl) => {
|
|
1428
|
+
const ids = list(baseUrl).map((project) => project.id);
|
|
1429
|
+
const { active } = store.get();
|
|
1430
|
+
removeMany(ids, active.baseUrl === baseUrl ? Entry.stackAddress(baseUrl) : active);
|
|
1431
|
+
};
|
|
1432
|
+
const updateContext = (id, context) => update(id, (project) => ({
|
|
1433
|
+
...project,
|
|
1434
|
+
context
|
|
1435
|
+
}));
|
|
1436
|
+
return {
|
|
1437
|
+
has,
|
|
1438
|
+
get,
|
|
1439
|
+
list,
|
|
1440
|
+
findAll,
|
|
1441
|
+
set,
|
|
1442
|
+
update,
|
|
1443
|
+
open,
|
|
1444
|
+
close,
|
|
1445
|
+
closeAll,
|
|
1446
|
+
updateContext,
|
|
1447
|
+
requireCredential
|
|
1448
|
+
};
|
|
1449
|
+
};
|
|
1450
|
+
//#endregion
|
|
1451
|
+
//#region src/keboola/bearer/state/scope.ts
|
|
1452
|
+
/**
|
|
1453
|
+
* What `api.*` addresses: the `active` slot, and the address in it resolved against the tables it
|
|
1454
|
+
* points into.
|
|
1455
|
+
*
|
|
1456
|
+
* The tables never write here — they read `active` through the pure helpers in `address.ts`, so
|
|
1457
|
+
* that this can depend on them rather than the other way round.
|
|
1458
|
+
*/
|
|
1459
|
+
const createScope = ({ store, stacks, projects }) => {
|
|
1460
|
+
const get = () => store.get().active;
|
|
1461
|
+
const isSame = (a, b) => a === b || a.baseUrl === b.baseUrl && a.projectId === b.projectId;
|
|
1462
|
+
const set = (active) => {
|
|
1463
|
+
if (isSame(store.get().active, active)) return;
|
|
1464
|
+
store.set({
|
|
1465
|
+
...store.get(),
|
|
1466
|
+
active
|
|
1467
|
+
});
|
|
1468
|
+
};
|
|
1469
|
+
const locate = (active) => {
|
|
1470
|
+
const stack = stacks.get(active.baseUrl);
|
|
1471
|
+
if (!stack.success) return null;
|
|
1472
|
+
if (active.projectId === null) return {
|
|
1473
|
+
stack: stack.data,
|
|
1474
|
+
project: null
|
|
1475
|
+
};
|
|
1476
|
+
const project = projects.get(Entry.projectEntryId(active.baseUrl, active.projectId));
|
|
1477
|
+
if (!project.success) return null;
|
|
1478
|
+
return {
|
|
1479
|
+
stack: stack.data,
|
|
1480
|
+
project: project.data
|
|
1481
|
+
};
|
|
1482
|
+
};
|
|
1483
|
+
const getActive = () => {
|
|
1484
|
+
const location = locate(get());
|
|
1485
|
+
if (!location) throw new Error(`The scope names no row: ${JSON.stringify(get())}`);
|
|
1486
|
+
return location;
|
|
1487
|
+
};
|
|
1488
|
+
const getActiveStack = () => getActive().stack;
|
|
1489
|
+
const activateStack = (baseUrl) => require_jobStatus.Result.map(stacks.get(baseUrl), (stack) => {
|
|
1490
|
+
const next = Entry.stackAddress(stack.baseUrl);
|
|
1491
|
+
set(next);
|
|
1492
|
+
return next;
|
|
1493
|
+
});
|
|
1494
|
+
const activateProject = (baseUrl, projectId) => require_jobStatus.Result.map(projects.requireCredential(Entry.projectEntryId(baseUrl, projectId)), () => {
|
|
1495
|
+
const next = Entry.projectAddress(baseUrl, projectId);
|
|
1496
|
+
set(next);
|
|
1497
|
+
return next;
|
|
1498
|
+
});
|
|
1499
|
+
const selectStack = (baseUrl) => {
|
|
1500
|
+
const stack = stacks.ensure(baseUrl);
|
|
1501
|
+
return require_jobStatus.Result.map(activateStack(stack.baseUrl), () => stack);
|
|
1502
|
+
};
|
|
1503
|
+
const selectProject = (projectId) => {
|
|
1504
|
+
const { baseUrl } = getActiveStack();
|
|
1505
|
+
const project = projects.open(baseUrl, projectId);
|
|
1506
|
+
if (!project.success) return project;
|
|
1507
|
+
return require_jobStatus.Result.map(activateProject(baseUrl, projectId), () => project.data);
|
|
1508
|
+
};
|
|
1509
|
+
const deactivate = () => {
|
|
1510
|
+
const active = get();
|
|
1511
|
+
if (active.projectId === null) return;
|
|
1512
|
+
set(Entry.stackAddress(active.baseUrl));
|
|
1513
|
+
};
|
|
1514
|
+
const requireActiveProject = () => {
|
|
1515
|
+
const { project } = getActive();
|
|
1516
|
+
return project ? require_jobStatus.Result.success(project) : require_jobStatus.Result.failure(Errors.notReady());
|
|
1517
|
+
};
|
|
1518
|
+
return {
|
|
1519
|
+
get,
|
|
1520
|
+
getActive,
|
|
1521
|
+
activateStack,
|
|
1522
|
+
activateProject,
|
|
1523
|
+
selectStack,
|
|
1524
|
+
selectProject,
|
|
1525
|
+
deactivate,
|
|
1526
|
+
getActiveStack,
|
|
1527
|
+
requireActiveProject
|
|
1528
|
+
};
|
|
1529
|
+
};
|
|
1530
|
+
//#endregion
|
|
1531
|
+
//#region src/keboola/bearer/state/stacks.ts
|
|
1532
|
+
/** One entry per stack: the entries, and the verbs that open and close them. */
|
|
1533
|
+
const createStacks = ({ store, forgetExpiry }) => {
|
|
1534
|
+
const has = (baseUrl) => baseUrl in store.get().stacks;
|
|
1535
|
+
const get = (baseUrl) => {
|
|
1536
|
+
const stack = store.get().stacks[baseUrl];
|
|
1537
|
+
return stack ? require_jobStatus.Result.success(stack) : require_jobStatus.Result.failure(Errors.unconfigured());
|
|
1538
|
+
};
|
|
1539
|
+
const list = () => Object.values(store.get().stacks);
|
|
1540
|
+
const set = (stack) => {
|
|
1541
|
+
const state = store.get();
|
|
1542
|
+
store.set({
|
|
1543
|
+
...state,
|
|
1544
|
+
stacks: {
|
|
1545
|
+
...state.stacks,
|
|
1546
|
+
[stack.baseUrl]: stack
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
return stack;
|
|
1550
|
+
};
|
|
1551
|
+
const update = (baseUrl, updater) => require_jobStatus.Result.map(get(baseUrl), (stack) => set(updater(stack)));
|
|
1552
|
+
const requireCredential = (baseUrl) => require_jobStatus.Result.andThen(get(baseUrl), (stack) => authenticated(stack.auth));
|
|
1553
|
+
const ensure = (baseUrl) => {
|
|
1554
|
+
const existing = get(baseUrl);
|
|
1555
|
+
if (existing.success) return existing.data;
|
|
1556
|
+
return set(Entry.stack(baseUrl));
|
|
1557
|
+
};
|
|
1558
|
+
const close = (baseUrl) => {
|
|
1559
|
+
const stack = get(baseUrl);
|
|
1560
|
+
if (!stack.success) return stack;
|
|
1561
|
+
const state = store.get();
|
|
1562
|
+
if (state.active.baseUrl === baseUrl) return require_jobStatus.Result.failure(Errors.stackActive());
|
|
1563
|
+
const stacks = { ...state.stacks };
|
|
1564
|
+
delete stacks[baseUrl];
|
|
1565
|
+
const projects = Object.fromEntries(Object.entries(state.projects).filter(([, project]) => project.baseUrl !== baseUrl));
|
|
1566
|
+
store.set({
|
|
1567
|
+
active: state.active,
|
|
1568
|
+
stacks,
|
|
1569
|
+
projects
|
|
1570
|
+
});
|
|
1571
|
+
forgetExpiry(baseUrl);
|
|
1572
|
+
return stack;
|
|
1573
|
+
};
|
|
1574
|
+
const updateContext = (baseUrl, context) => update(baseUrl, (stack) => ({
|
|
1575
|
+
...stack,
|
|
1576
|
+
context
|
|
1577
|
+
}));
|
|
1578
|
+
return {
|
|
1579
|
+
has,
|
|
1580
|
+
get,
|
|
1581
|
+
list,
|
|
1582
|
+
set,
|
|
1583
|
+
update,
|
|
1584
|
+
ensure,
|
|
1585
|
+
close,
|
|
1586
|
+
updateContext,
|
|
1587
|
+
requireCredential
|
|
1588
|
+
};
|
|
1589
|
+
};
|
|
1590
|
+
//#endregion
|
|
1591
|
+
//#region src/keboola/bearer/state/state.ts
|
|
1592
|
+
/**
|
|
1593
|
+
* The stack table and the rules over it, one namespace per concern.
|
|
1594
|
+
*
|
|
1595
|
+
* - Built in dependency order: a project reads its stack's credential, and the scope resolves
|
|
1596
|
+
* the active address against both tables.
|
|
1597
|
+
*/
|
|
1598
|
+
const createState = (defaultBaseUrl) => {
|
|
1599
|
+
const store = require_store.createStore(Entry.table(defaultBaseUrl));
|
|
1600
|
+
const expiry = createExpiryLatch();
|
|
1601
|
+
const stacks = createStacks({
|
|
1602
|
+
store,
|
|
1603
|
+
forgetExpiry: expiry.forget
|
|
1604
|
+
});
|
|
1605
|
+
const projects = createProjects({
|
|
1606
|
+
store,
|
|
1607
|
+
stacks
|
|
1608
|
+
});
|
|
1609
|
+
const scope = createScope({
|
|
1610
|
+
store,
|
|
1611
|
+
stacks,
|
|
1612
|
+
projects
|
|
1613
|
+
});
|
|
1614
|
+
const auth = createAuth({
|
|
1615
|
+
stacks,
|
|
1616
|
+
projects,
|
|
1617
|
+
expiry
|
|
1618
|
+
});
|
|
1619
|
+
return {
|
|
1620
|
+
store: store.asReadable(),
|
|
1621
|
+
stacks,
|
|
1622
|
+
projects,
|
|
1623
|
+
auth,
|
|
1624
|
+
scope
|
|
1625
|
+
};
|
|
1626
|
+
};
|
|
1627
|
+
//#endregion
|
|
1628
|
+
//#region src/keboola/bearer/context/resource.ts
|
|
1629
|
+
/**
|
|
1630
|
+
* Owns fetching one async value per key — a caller names what it needs and never orchestrates the
|
|
1631
|
+
* request. Backs the stack and project contexts.
|
|
1632
|
+
*
|
|
1633
|
+
* - One fetch per key: concurrent askers share it, a landed value is never refetched.
|
|
1634
|
+
* - A failure is held, not thrown away — nothing here needs a rejection handler.
|
|
1635
|
+
* - Status lives here and nowhere else; a consumer that has to mirror it subscribes.
|
|
1636
|
+
*/
|
|
1637
|
+
const createResource = ({ key, fetch }) => {
|
|
1638
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1639
|
+
const changes = require_store.createEmitter();
|
|
1640
|
+
const set = (id, entry) => {
|
|
1641
|
+
entries.set(id, entry);
|
|
1642
|
+
changes.emit({
|
|
1643
|
+
key: id,
|
|
1644
|
+
status: entry.status
|
|
1645
|
+
});
|
|
1646
|
+
};
|
|
1647
|
+
const run = (id, input) => {
|
|
1648
|
+
const settle = (next) => {
|
|
1649
|
+
if (entries.get(id) === entry) set(id, {
|
|
1650
|
+
input,
|
|
1651
|
+
...next
|
|
1652
|
+
});
|
|
1653
|
+
return next;
|
|
1654
|
+
};
|
|
1655
|
+
const attempt = async () => await fetch(input);
|
|
1656
|
+
const entry = {
|
|
1657
|
+
input,
|
|
1658
|
+
status: "pending",
|
|
1659
|
+
settled: attempt().then((value) => settle({
|
|
1660
|
+
status: "ready",
|
|
1661
|
+
value
|
|
1662
|
+
}), (error) => settle({
|
|
1663
|
+
status: "errored",
|
|
1664
|
+
error
|
|
1665
|
+
}))
|
|
1666
|
+
};
|
|
1667
|
+
set(id, entry);
|
|
1668
|
+
return entry;
|
|
1669
|
+
};
|
|
1670
|
+
const get = (id) => {
|
|
1671
|
+
const entry = entries.get(id);
|
|
1672
|
+
return entry?.status === "ready" ? entry.value : null;
|
|
1673
|
+
};
|
|
1674
|
+
const getAsync = async (input) => {
|
|
1675
|
+
const id = key(input);
|
|
1676
|
+
const cached = entries.get(id);
|
|
1677
|
+
const entry = cached === void 0 || cached.status === "errored" ? run(id, input) : cached;
|
|
1678
|
+
const settled = entry.status === "pending" ? await entry.settled : entry;
|
|
1679
|
+
if (settled.status === "errored") throw settled.error;
|
|
1680
|
+
return settled.value;
|
|
1681
|
+
};
|
|
1682
|
+
const forget = (matches) => {
|
|
1683
|
+
for (const id of entries.keys()) if (matches(id)) entries.delete(id);
|
|
1684
|
+
};
|
|
1685
|
+
const prefetch = (input) => {
|
|
1686
|
+
const id = key(input);
|
|
1687
|
+
if (!entries.has(id)) run(id, input);
|
|
1688
|
+
};
|
|
1689
|
+
const retry = (id) => {
|
|
1690
|
+
const entry = entries.get(id);
|
|
1691
|
+
if (entry !== void 0) run(id, entry.input);
|
|
1692
|
+
};
|
|
1693
|
+
return {
|
|
1694
|
+
get,
|
|
1695
|
+
getAsync,
|
|
1696
|
+
prefetch,
|
|
1697
|
+
retry,
|
|
1698
|
+
forget,
|
|
1699
|
+
subscribe: changes.subscribe
|
|
1700
|
+
};
|
|
1701
|
+
};
|
|
1702
|
+
//#endregion
|
|
1703
|
+
//#region src/keboola/bearer/context/project.ts
|
|
1704
|
+
const createProjectContext = ({ state, credentials, stackContext }) => {
|
|
1705
|
+
const resource = createResource({
|
|
1706
|
+
key: (project) => project.id,
|
|
1707
|
+
fetch: async ({ baseUrl, projectId }) => {
|
|
1708
|
+
const withProjectId = (next) => (request) => {
|
|
1709
|
+
request.request.headers.set(require_createFetchClient.KeboolaHttpHeader.PROJECT_ID, String(projectId));
|
|
1710
|
+
return next(request);
|
|
1711
|
+
};
|
|
1712
|
+
const client = require_storage.createStorageClient({
|
|
1713
|
+
baseUrl,
|
|
1714
|
+
auth: {
|
|
1715
|
+
type: "bearer",
|
|
1716
|
+
token: "",
|
|
1717
|
+
projectId
|
|
1718
|
+
},
|
|
1719
|
+
middlewares: [(next) => withProjectId(credentials.middleware(baseUrl)(next))]
|
|
1720
|
+
});
|
|
1721
|
+
const [token, devBranches] = await Promise.all([
|
|
1722
|
+
client.tokens.verify(),
|
|
1723
|
+
client.branches.getDevBranches(),
|
|
1724
|
+
stackContext.getAsync(baseUrl)
|
|
1725
|
+
]);
|
|
1726
|
+
return {
|
|
1727
|
+
token,
|
|
1728
|
+
defaultBranch: createDefaultBranch({ devBranches })
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
resource.subscribe(({ key, status }) => {
|
|
1733
|
+
state.projects.updateContext(key, status);
|
|
1734
|
+
});
|
|
1735
|
+
const subscriber = ({ projects }) => {
|
|
1736
|
+
for (const project of Object.values(projects)) resource.prefetch(project);
|
|
1737
|
+
};
|
|
1738
|
+
/**
|
|
1739
|
+
* Dropped with the sign-in that fetched them: a verified token is what a credential answered
|
|
1740
|
+
* for, so it belongs to that credential and not to the project id it was keyed by.
|
|
1741
|
+
*/
|
|
1742
|
+
const forgetUnder = (baseUrl) => resource.forget(Entry.isUnderStack(baseUrl));
|
|
1743
|
+
/** One project, closed on purpose. */
|
|
1744
|
+
const forget = (id) => resource.forget((key) => key === id);
|
|
1745
|
+
return {
|
|
1746
|
+
subscriber,
|
|
1747
|
+
get: resource.get,
|
|
1748
|
+
getAsync: resource.getAsync,
|
|
1749
|
+
retry: resource.retry,
|
|
1750
|
+
forget,
|
|
1751
|
+
forgetUnder
|
|
1752
|
+
};
|
|
1753
|
+
};
|
|
1754
|
+
//#endregion
|
|
1755
|
+
//#region src/keboola/bearer/context/stack.ts
|
|
1756
|
+
const createStackContext = (state) => {
|
|
1757
|
+
const resource = createResource({
|
|
1758
|
+
key: (baseUrl) => baseUrl,
|
|
1759
|
+
fetch: (baseUrl) => require_storage.createStorageClient({
|
|
1760
|
+
baseUrl,
|
|
1761
|
+
auth: {
|
|
1762
|
+
type: "sapi-token",
|
|
1763
|
+
token: ""
|
|
1764
|
+
}
|
|
1765
|
+
}).getStackInfo({ exclude: "componentDetails" })
|
|
1766
|
+
});
|
|
1767
|
+
resource.subscribe(({ key, status }) => {
|
|
1768
|
+
state.stacks.updateContext(key, status);
|
|
1769
|
+
});
|
|
1770
|
+
const subscriber = ({ stacks }) => {
|
|
1771
|
+
for (const { baseUrl } of Object.values(stacks)) resource.prefetch(baseUrl);
|
|
1772
|
+
};
|
|
1773
|
+
/** Dropped with the stack itself — a stack that comes back re-reads its index. */
|
|
1774
|
+
const forget = (baseUrl) => resource.forget((key) => key === baseUrl);
|
|
1775
|
+
return {
|
|
1776
|
+
subscriber,
|
|
1777
|
+
get: resource.get,
|
|
1778
|
+
getAsync: resource.getAsync,
|
|
1779
|
+
retry: resource.retry,
|
|
1780
|
+
forget
|
|
1781
|
+
};
|
|
1782
|
+
};
|
|
1783
|
+
//#endregion
|
|
1784
|
+
//#region src/keboola/bearer/perProject.ts
|
|
1785
|
+
const once = (build) => {
|
|
1786
|
+
let value = null;
|
|
1787
|
+
return () => value ??= build();
|
|
1788
|
+
};
|
|
1789
|
+
/**
|
|
1790
|
+
* Everything one project derives, and the one call that drops it.
|
|
1791
|
+
*
|
|
1792
|
+
* Every field belongs to the credential that built it — a client set carries its middleware chain,
|
|
1793
|
+
* the storage client holds a `liveProject` seeded with its token, a fetched context *is* what it
|
|
1794
|
+
* answered for `tokens.verify` — so the record dies with the sign-in, and `forgetUnder` is the
|
|
1795
|
+
* only thing that ends one.
|
|
1796
|
+
*/
|
|
1797
|
+
const createPerProject = ({ state, credentials, middlewareFor, stack, clientServiceUrls, buildApi }) => {
|
|
1798
|
+
const context = createProjectContext({
|
|
1799
|
+
state,
|
|
1800
|
+
credentials,
|
|
1801
|
+
stackContext: stack.context
|
|
1802
|
+
});
|
|
1803
|
+
const attachFor = ({ baseUrl, projectId }) => {
|
|
1804
|
+
const withProjectId = (next) => (request) => {
|
|
1805
|
+
request.request.headers.set(require_createFetchClient.KeboolaHttpHeader.PROJECT_ID, String(projectId));
|
|
1806
|
+
return next(request);
|
|
1807
|
+
};
|
|
1808
|
+
return (next) => withProjectId(credentials.middleware(baseUrl)(next));
|
|
1809
|
+
};
|
|
1810
|
+
const buildClients = ({ baseUrl, projectId }, attach) => createServiceClients({
|
|
1811
|
+
baseUrl,
|
|
1812
|
+
auth: {
|
|
1813
|
+
type: "bearer",
|
|
1814
|
+
token: "",
|
|
1815
|
+
projectId
|
|
1816
|
+
},
|
|
1817
|
+
getServiceBaseUrl: (serviceId) => async () => {
|
|
1818
|
+
const stackInfo = await stack.context.getAsync(baseUrl);
|
|
1819
|
+
const serviceUrl = createStack({
|
|
1820
|
+
stackInfo,
|
|
1821
|
+
clientServiceUrls
|
|
1822
|
+
}).getServiceUrl(serviceId);
|
|
1823
|
+
assert$1(serviceUrl, KeboolaErrorType.NotReady);
|
|
1824
|
+
return serviceUrl;
|
|
1825
|
+
},
|
|
1826
|
+
middlewareFor,
|
|
1827
|
+
attachTokenMiddleware: attach
|
|
1828
|
+
});
|
|
1829
|
+
/**
|
|
1830
|
+
* `api.storage` and `api.context`, built together: both read one live token — the storage client
|
|
1831
|
+
* carries its re-verify middleware, the context reports the project it re-verifies.
|
|
1832
|
+
*
|
|
1833
|
+
* - Both fetches it reads are already running. `NotReady` covers the window before they land,
|
|
1834
|
+
* which the table reports as `project.context`.
|
|
1835
|
+
* - `storage` is gated on the stack index too, though it addresses the project's own `baseUrl`
|
|
1836
|
+
* and does not need it — that is the gate as it stands, not a requirement.
|
|
1837
|
+
*/
|
|
1838
|
+
const buildLive = (project, attach) => {
|
|
1839
|
+
const stackInfo = stack.context.get(project.baseUrl);
|
|
1840
|
+
assert$1(stackInfo, KeboolaErrorType.NotReady);
|
|
1841
|
+
const fetched = context.get(project.id);
|
|
1842
|
+
assert$1(fetched, KeboolaErrorType.NotReady);
|
|
1843
|
+
const { baseUrl, projectId } = project;
|
|
1844
|
+
const auth = {
|
|
1845
|
+
type: "bearer",
|
|
1846
|
+
token: "",
|
|
1847
|
+
projectId
|
|
1848
|
+
};
|
|
1849
|
+
const reader = require_storage.createStorageClient({
|
|
1850
|
+
baseUrl,
|
|
1851
|
+
auth,
|
|
1852
|
+
middlewares: [attach]
|
|
1853
|
+
});
|
|
1854
|
+
const liveProject = createLiveProject({
|
|
1855
|
+
initialToken: fetched.token,
|
|
1856
|
+
verify: () => reader.tokens.verify()
|
|
1857
|
+
});
|
|
1858
|
+
const storageTableStore = createDerivedStore({
|
|
1859
|
+
source: liveProject.store,
|
|
1860
|
+
build: require_storageTable.createStorageTable,
|
|
1861
|
+
dependsOn: (live) => ({
|
|
1862
|
+
useLegacyBucketPrefix: !live.hasFeature(require_constants.ProjectFeature.DisableLegacyBucketPrefix),
|
|
1863
|
+
hasProtectedDefaultBranch: live.hasFeature(require_constants.ProjectFeature.SoxProtectedDefaultBranch),
|
|
1864
|
+
hasStorageBranches: live.hasFeature(require_constants.ProjectFeature.StorageBranches)
|
|
1865
|
+
})
|
|
1866
|
+
});
|
|
1867
|
+
return {
|
|
1868
|
+
storage: require_storage.createStorageClient({
|
|
1869
|
+
baseUrl,
|
|
1870
|
+
auth,
|
|
1871
|
+
middlewares: middlewareFor("storage", [attach, liveProject.middleware])
|
|
1872
|
+
}),
|
|
1873
|
+
context: {
|
|
1874
|
+
baseUrl,
|
|
1875
|
+
stack: createStack({
|
|
1876
|
+
stackInfo,
|
|
1877
|
+
clientServiceUrls
|
|
1878
|
+
}),
|
|
1879
|
+
defaultBranch: fetched.defaultBranch,
|
|
1880
|
+
get project() {
|
|
1881
|
+
return liveProject.getProject();
|
|
1882
|
+
},
|
|
1883
|
+
get st() {
|
|
1884
|
+
return storageTableStore.get();
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
};
|
|
1889
|
+
const build = (project) => {
|
|
1890
|
+
const attach = attachFor(project);
|
|
1891
|
+
const live = once(() => buildLive(project, attach));
|
|
1892
|
+
const clients = once(() => buildClients(project, attach));
|
|
1893
|
+
return {
|
|
1894
|
+
api: buildApi(project),
|
|
1895
|
+
get clients() {
|
|
1896
|
+
return clients();
|
|
1897
|
+
},
|
|
1898
|
+
get storage() {
|
|
1899
|
+
return live().storage;
|
|
1900
|
+
},
|
|
1901
|
+
get context() {
|
|
1902
|
+
return live().context;
|
|
1903
|
+
}
|
|
1904
|
+
};
|
|
1905
|
+
};
|
|
1906
|
+
const derived = /* @__PURE__ */ new Map();
|
|
1907
|
+
const get = (project) => ensure(derived, project.id, () => build(project));
|
|
1908
|
+
/** One project, closed on purpose — a handle for it stops answering from the next read. */
|
|
1909
|
+
const forget = (id) => {
|
|
1910
|
+
derived.delete(id);
|
|
1911
|
+
context.forget(id);
|
|
1912
|
+
};
|
|
1913
|
+
const forgetUnder = (baseUrl) => {
|
|
1914
|
+
const isUnder = Entry.isUnderStack(baseUrl);
|
|
1915
|
+
for (const id of derived.keys()) if (isUnder(id)) derived.delete(id);
|
|
1916
|
+
context.forgetUnder(baseUrl);
|
|
1917
|
+
};
|
|
1918
|
+
return {
|
|
1919
|
+
context,
|
|
1920
|
+
get,
|
|
1921
|
+
forget,
|
|
1922
|
+
forgetUnder
|
|
1923
|
+
};
|
|
1924
|
+
};
|
|
1925
|
+
//#endregion
|
|
1926
|
+
//#region src/keboola/bearer/persistor.ts
|
|
1927
|
+
/**
|
|
1928
|
+
* Where a bearer session is persisted, under whichever adapter the client was given.
|
|
1929
|
+
*
|
|
1930
|
+
* Exported because a consumer sometimes has to drop a session a client cannot reach: the adapter is
|
|
1931
|
+
* chosen at construction, so a client built without one holds no handle to the storage its own
|
|
1932
|
+
* session is sitting in. `AppAuthStrategy` in kbc-ui is that case.
|
|
1933
|
+
*/
|
|
1934
|
+
const SESSION_STORAGE_KEY = "keboola:session";
|
|
1935
|
+
/**
|
|
1936
|
+
* What survives a reload, per credential kind.
|
|
1937
|
+
*
|
|
1938
|
+
* - `token`: the refresh token. It rotates, is single-use and is server-revocable, so a leaked
|
|
1939
|
+
* snapshot has limited and revocable value. The access token is never written.
|
|
1940
|
+
* - `static`: the token itself — a long-lived PAT the user pasted, which nothing can re-mint.
|
|
1941
|
+
* - `cookie`: the session id. The session cookie is the durable half and the browser keeps it;
|
|
1942
|
+
* the id alone renews nothing.
|
|
1943
|
+
*/
|
|
1944
|
+
const persistedCredentialSchema = zod.discriminatedUnion("type", [
|
|
1945
|
+
zod.object({
|
|
1946
|
+
type: zod.literal("token"),
|
|
1947
|
+
refreshToken: zod.string()
|
|
1948
|
+
}),
|
|
1949
|
+
zod.object({
|
|
1950
|
+
type: zod.literal("cookie"),
|
|
1951
|
+
sessionId: zod.string()
|
|
1952
|
+
}),
|
|
1953
|
+
zod.object({
|
|
1954
|
+
type: zod.literal("static"),
|
|
1955
|
+
accessToken: zod.string()
|
|
1956
|
+
})
|
|
1957
|
+
]);
|
|
1958
|
+
/** A record per project rather than a bare id, so a project can grow a field without a migration. */
|
|
1959
|
+
const persistedProjectSchema = zod.object({ projectId: zod.number() });
|
|
1960
|
+
const persistedSessionSchema = zod.object({
|
|
1961
|
+
current: zod.object({
|
|
1962
|
+
baseUrl: zod.string(),
|
|
1963
|
+
projectId: zod.number().nullable()
|
|
1964
|
+
}).nullable(),
|
|
1965
|
+
stacks: zod.array(zod.object({
|
|
1966
|
+
baseUrl: zod.string(),
|
|
1967
|
+
credential: persistedCredentialSchema.nullable(),
|
|
1968
|
+
projects: zod.array(persistedProjectSchema)
|
|
1969
|
+
}))
|
|
1970
|
+
});
|
|
1971
|
+
const EMPTY_PERSISTED_SESSION = {
|
|
1972
|
+
current: null,
|
|
1973
|
+
stacks: []
|
|
1974
|
+
};
|
|
1975
|
+
const durableCredential = (stack) => {
|
|
1976
|
+
if (stack.auth.status !== "authenticated") return null;
|
|
1977
|
+
const { credential } = stack.auth;
|
|
1978
|
+
if (credential.type === "token") return {
|
|
1979
|
+
type: "token",
|
|
1980
|
+
refreshToken: credential.refreshToken
|
|
1981
|
+
};
|
|
1982
|
+
if (credential.type === "cookie") return {
|
|
1983
|
+
type: "cookie",
|
|
1984
|
+
sessionId: credential.sessionId
|
|
1985
|
+
};
|
|
1986
|
+
return {
|
|
1987
|
+
type: "static",
|
|
1988
|
+
accessToken: credential.accessToken
|
|
1989
|
+
};
|
|
1990
|
+
};
|
|
1991
|
+
const createSessionPersistor = (storage) => {
|
|
1992
|
+
const persistor = createPersistor({
|
|
1993
|
+
storage,
|
|
1994
|
+
key: SESSION_STORAGE_KEY,
|
|
1995
|
+
schema: persistedSessionSchema,
|
|
1996
|
+
defaultValue: EMPTY_PERSISTED_SESSION
|
|
1997
|
+
});
|
|
1998
|
+
const persist = ({ active, stacks, projects }) => persistor.persist({
|
|
1999
|
+
current: {
|
|
2000
|
+
baseUrl: active.baseUrl,
|
|
2001
|
+
projectId: active.projectId
|
|
2002
|
+
},
|
|
2003
|
+
stacks: Object.values(stacks).map((stack) => ({
|
|
2004
|
+
baseUrl: stack.baseUrl,
|
|
2005
|
+
credential: durableCredential(stack),
|
|
2006
|
+
projects: Object.values(projects).filter((project) => project.baseUrl === stack.baseUrl).map(({ projectId }) => ({ projectId }))
|
|
2007
|
+
}))
|
|
2008
|
+
});
|
|
2009
|
+
return {
|
|
2010
|
+
...persistor,
|
|
2011
|
+
persist
|
|
2012
|
+
};
|
|
2013
|
+
};
|
|
2014
|
+
//#endregion
|
|
2015
|
+
//#region src/keboola/bearer/perStack.ts
|
|
2016
|
+
/**
|
|
2017
|
+
* Everything one stack derives, and the one call that drops it.
|
|
2018
|
+
*
|
|
2019
|
+
* Outlives a sign-out, unlike anything in `perProject`: both clients read the credential live per
|
|
2020
|
+
* request, so neither belongs to one sign-in, and the stack index is public.
|
|
2021
|
+
*/
|
|
2022
|
+
const createPerStack = ({ state, credentials, middlewareFor }) => {
|
|
2023
|
+
const context = createStackContext(state);
|
|
2024
|
+
const derived = /* @__PURE__ */ new Map();
|
|
2025
|
+
const get = (baseUrl) => ensure(derived, baseUrl, () => ({
|
|
2026
|
+
auth: require_authClient.createAuthClient({
|
|
2027
|
+
baseUrl,
|
|
2028
|
+
middlewares: middlewareFor("auth", [credentials.middleware(baseUrl)])
|
|
2029
|
+
}),
|
|
2030
|
+
management: require_management.createManagementClient({
|
|
2031
|
+
baseUrl,
|
|
2032
|
+
auth: {
|
|
2033
|
+
type: "bearer",
|
|
2034
|
+
token: ""
|
|
2035
|
+
},
|
|
2036
|
+
middlewares: middlewareFor("management", [credentials.middleware(baseUrl)])
|
|
2037
|
+
})
|
|
2038
|
+
}));
|
|
2039
|
+
const forget = (baseUrl) => {
|
|
2040
|
+
derived.delete(baseUrl);
|
|
2041
|
+
context.forget(baseUrl);
|
|
2042
|
+
};
|
|
2043
|
+
return {
|
|
2044
|
+
context,
|
|
2045
|
+
get,
|
|
2046
|
+
forget
|
|
2047
|
+
};
|
|
2048
|
+
};
|
|
2049
|
+
//#endregion
|
|
2050
|
+
//#region src/keboola/bearer/keboola.ts
|
|
2051
|
+
/**
|
|
2052
|
+
* The bearer client: the session table, its two scopes, and the surface they back.
|
|
2053
|
+
*
|
|
2054
|
+
* - Credential-derived state is owned by the scope whose end ends it (`perStack`, `perProject`).
|
|
2055
|
+
*/
|
|
2056
|
+
const createKeboola = ({ defaultBaseUrl, storage, middlewares, clientServiceUrls, credentials: mode = "token" }) => {
|
|
2057
|
+
const state = createState(require_createFetchClient.normalizeBaseUrl(defaultBaseUrl));
|
|
2058
|
+
const persistor = createSessionPersistor(storage);
|
|
2059
|
+
const middlewareFor = createMiddlewareFor(middlewares);
|
|
2060
|
+
const credentials = createCredentials({
|
|
2061
|
+
state,
|
|
2062
|
+
persistor,
|
|
2063
|
+
middlewares: middlewareFor("auth"),
|
|
2064
|
+
mode,
|
|
2065
|
+
onSignedOut: (baseUrl) => perProject.forgetUnder(baseUrl)
|
|
2066
|
+
});
|
|
2067
|
+
const perStack = createPerStack({
|
|
2068
|
+
state,
|
|
2069
|
+
credentials,
|
|
2070
|
+
middlewareFor
|
|
2071
|
+
});
|
|
2072
|
+
const perProject = createPerProject({
|
|
2073
|
+
state,
|
|
2074
|
+
credentials,
|
|
2075
|
+
middlewareFor,
|
|
2076
|
+
stack: perStack,
|
|
2077
|
+
clientServiceUrls,
|
|
2078
|
+
buildApi: (project) => createApiFor({
|
|
2079
|
+
stackUrl: () => project.baseUrl,
|
|
2080
|
+
project: () => require_jobStatus.Result.unwrap(state.projects.get(project.id)),
|
|
2081
|
+
pinned: true
|
|
2082
|
+
})
|
|
2083
|
+
});
|
|
2084
|
+
state.store.subscribe(perStack.context.subscriber);
|
|
2085
|
+
perStack.context.subscriber(state.store.get());
|
|
2086
|
+
state.store.subscribe(perProject.context.subscriber);
|
|
2087
|
+
const selectStack = (baseUrl) => require_jobStatus.Result.unwrap(state.scope.selectStack(require_createFetchClient.normalizeBaseUrl(baseUrl)));
|
|
2088
|
+
const selectProject = (projectId) => require_jobStatus.Result.unwrap(state.scope.selectProject(projectId));
|
|
2089
|
+
/** The scoped stack, or a named one — normalised to key like `select`. */
|
|
2090
|
+
const requireStackUrl = (baseUrl) => baseUrl === void 0 ? state.scope.getActiveStack().baseUrl : require_createFetchClient.normalizeBaseUrl(baseUrl);
|
|
2091
|
+
/** The scoped project, or the row a table id names — on whichever stack that is. */
|
|
2092
|
+
const requireProjectEntry = (id) => id === void 0 ? require_jobStatus.Result.unwrap(state.scope.requireActiveProject()) : require_jobStatus.Result.unwrap(state.projects.get(id));
|
|
2093
|
+
/**
|
|
2094
|
+
* The scoped project, or a named open one.
|
|
2095
|
+
*
|
|
2096
|
+
* - An id is ambiguous across stacks: the active stack decides, else the single one open elsewhere.
|
|
2097
|
+
*/
|
|
2098
|
+
const requireProject = (projectId) => {
|
|
2099
|
+
if (projectId === void 0) return require_jobStatus.Result.unwrap(state.scope.requireActiveProject());
|
|
2100
|
+
const scoped = state.projects.get(Entry.projectEntryId(requireStackUrl(), projectId));
|
|
2101
|
+
if (scoped.success) return scoped.data;
|
|
2102
|
+
const [only, ...rest] = state.projects.findAll(projectId);
|
|
2103
|
+
return only !== void 0 && rest.length === 0 ? only : require_jobStatus.Result.unwrap(scoped);
|
|
2104
|
+
};
|
|
2105
|
+
/**
|
|
2106
|
+
* Auth headers for a non-fetch transport (the chat stream).
|
|
2107
|
+
*
|
|
2108
|
+
* - A read, not a rotation: no middleware recovers a 401 there; the fetch clients keep it fresh.
|
|
2109
|
+
*/
|
|
2110
|
+
const getAuthHeaders = (projectId) => {
|
|
2111
|
+
const project = requireProject(projectId);
|
|
2112
|
+
const credential = require_jobStatus.Result.unwrap(state.auth.requireCredential(project.baseUrl));
|
|
2113
|
+
return {
|
|
2114
|
+
[require_createFetchClient.HttpHeader.AUTHORIZATION]: `Bearer ${credential.accessToken}`,
|
|
2115
|
+
[require_createFetchClient.KeboolaHttpHeader.PROJECT_ID]: String(project.projectId)
|
|
2116
|
+
};
|
|
2117
|
+
};
|
|
2118
|
+
const authenticate = (input) => {
|
|
2119
|
+
const stack = state.scope.getActiveStack();
|
|
2120
|
+
if (input.type !== "static" && input.type !== mode) throw new KeboolaError(KeboolaErrorType.CredentialKindMismatch);
|
|
2121
|
+
if (input.type !== "token") return require_jobStatus.Result.unwrap(state.auth.authenticate(stack.baseUrl, input));
|
|
2122
|
+
const { refreshToken } = input;
|
|
2123
|
+
if (!refreshToken) throw new KeboolaError(KeboolaErrorType.NotAuthenticated);
|
|
2124
|
+
return require_jobStatus.Result.unwrap(state.auth.authenticate(stack.baseUrl, {
|
|
2125
|
+
...input,
|
|
2126
|
+
refreshToken
|
|
2127
|
+
}));
|
|
2128
|
+
};
|
|
2129
|
+
/**
|
|
2130
|
+
* The flat product surface: `auth` and `management` per stack, the rest per project.
|
|
2131
|
+
*
|
|
2132
|
+
* - Both resolvers run per property read: that re-points the active handle and makes a pinned
|
|
2133
|
+
* one throw once its project is gone.
|
|
2134
|
+
*/
|
|
2135
|
+
const createApiFor = (resolve) => new Proxy({}, { get: (_target, property) => {
|
|
2136
|
+
if (isBuiltInProperty(property)) return void 0;
|
|
2137
|
+
if (property === "auth" || property === "management") {
|
|
2138
|
+
if (resolve.pinned) resolve.project();
|
|
2139
|
+
return perStack.get(resolve.stackUrl())[property];
|
|
2140
|
+
}
|
|
2141
|
+
const derived = perProject.get(resolve.project());
|
|
2142
|
+
if (property === "context") return derived.context;
|
|
2143
|
+
if (property === "storage") return derived.storage;
|
|
2144
|
+
const { client, meta } = derived.clients;
|
|
2145
|
+
if (property === "serviceMeta") return meta;
|
|
2146
|
+
return client[property];
|
|
2147
|
+
} });
|
|
2148
|
+
const activeApi = createApiFor({
|
|
2149
|
+
stackUrl: () => state.scope.getActiveStack().baseUrl,
|
|
2150
|
+
project: () => require_jobStatus.Result.unwrap(state.scope.requireActiveProject())
|
|
2151
|
+
});
|
|
2152
|
+
/** The scoped project's surface. Re-points when the scope moves. */
|
|
2153
|
+
const getActiveApi = () => activeApi;
|
|
2154
|
+
/**
|
|
2155
|
+
* One project's surface, on the active stack — one credential serves all of them.
|
|
2156
|
+
*
|
|
2157
|
+
* - Pinned: never re-points; stops once the project closes (`session.project.close`, sign-out).
|
|
2158
|
+
* - Memoised per project: a React `key` or a hook dep compares handles by identity.
|
|
2159
|
+
*/
|
|
2160
|
+
const getApi = (projectId) => {
|
|
2161
|
+
const { baseUrl } = state.scope.getActiveStack();
|
|
2162
|
+
const project = Entry.project(baseUrl, projectId);
|
|
2163
|
+
require_jobStatus.Result.unwrapOr(state.projects.open(baseUrl, projectId), null);
|
|
2164
|
+
return perProject.get(project).api;
|
|
2165
|
+
};
|
|
2166
|
+
/** The scope's sign-in, or a named stack's — and everything that sign-in derived. */
|
|
2167
|
+
const signOut = (baseUrl) => {
|
|
2168
|
+
const target = requireStackUrl(baseUrl);
|
|
2169
|
+
return require_jobStatus.Result.unwrap(credentials.signOut(target));
|
|
2170
|
+
};
|
|
2171
|
+
/**
|
|
2172
|
+
* `input` replaces the active stack's sign-in, whatever it holds.
|
|
2173
|
+
*
|
|
2174
|
+
* - Signs out first, always: a sign-out moves the epoch even on an anonymous stack, so a stored
|
|
2175
|
+
* session still being restored cannot land on top of this one.
|
|
2176
|
+
*/
|
|
2177
|
+
const replace = (input) => {
|
|
2178
|
+
signOut();
|
|
2179
|
+
return authenticate(input);
|
|
2180
|
+
};
|
|
2181
|
+
/**
|
|
2182
|
+
* A server-injected `token` credential, adopted whatever this client's mode.
|
|
2183
|
+
*
|
|
2184
|
+
* @deprecated Temporary, for the agent-project claim page only: Connection injects an API-mode
|
|
2185
|
+
* session through `UiConfig.programmaticAuth`. Remove together with that injection.
|
|
2186
|
+
*
|
|
2187
|
+
* - Replaces the active stack's sign-in, like `replace`.
|
|
2188
|
+
* - A cookie-mode client never rotates it, so it lasts as long as its access token.
|
|
2189
|
+
*/
|
|
2190
|
+
const adoptInjected = (input) => {
|
|
2191
|
+
signOut();
|
|
2192
|
+
return require_jobStatus.Result.unwrap(state.auth.authenticate(getActiveStack().baseUrl, input));
|
|
2193
|
+
};
|
|
2194
|
+
/**
|
|
2195
|
+
* Leave one project: its row and everything built for it; its handles stop answering.
|
|
2196
|
+
*
|
|
2197
|
+
* - Counterpart of `getApi(projectId)`; without it the project is held and persisted until sign-out.
|
|
2198
|
+
*/
|
|
2199
|
+
const closeProject = (projectId) => {
|
|
2200
|
+
const id = Entry.projectEntryId(requireStackUrl(), projectId);
|
|
2201
|
+
const closed = require_jobStatus.Result.unwrapOr(state.projects.close(id), null);
|
|
2202
|
+
if (closed === null) return null;
|
|
2203
|
+
perProject.forget(id);
|
|
2204
|
+
return closed;
|
|
2205
|
+
};
|
|
2206
|
+
const closeStack = (baseUrl) => {
|
|
2207
|
+
const target = require_createFetchClient.normalizeBaseUrl(baseUrl);
|
|
2208
|
+
if (state.scope.get().baseUrl === target) throw new KeboolaError(KeboolaErrorType.StackActive);
|
|
2209
|
+
const closed = require_jobStatus.Result.unwrapOr(state.stacks.close(target), null);
|
|
2210
|
+
if (closed === null) return null;
|
|
2211
|
+
perProject.forgetUnder(target);
|
|
2212
|
+
perStack.forget(target);
|
|
2213
|
+
credentials.forget(target);
|
|
2214
|
+
return closed;
|
|
2215
|
+
};
|
|
2216
|
+
let persisting = false;
|
|
2217
|
+
/**
|
|
2218
|
+
* Persistence starts only once the restore has settled.
|
|
2219
|
+
*
|
|
2220
|
+
* - Mid-hydration an unexchanged stack is `anonymous`; `durableCredential` would erase the
|
|
2221
|
+
* refresh token the exchange is about to spend.
|
|
2222
|
+
* - Writes the settled table once: the subscription fires only on the next change.
|
|
2223
|
+
*/
|
|
2224
|
+
const enablePersistence = () => {
|
|
2225
|
+
if (persisting) return;
|
|
2226
|
+
persisting = true;
|
|
2227
|
+
persistor.persist(state.store.get());
|
|
2228
|
+
state.store.subscribe(persistor.persist);
|
|
2229
|
+
};
|
|
2230
|
+
/**
|
|
2231
|
+
* The same, deferred until a sign-in.
|
|
2232
|
+
*
|
|
2233
|
+
* - After an unreachable restore the table is anonymous but the stored token is still good;
|
|
2234
|
+
* writing earlier would drop it.
|
|
2235
|
+
*/
|
|
2236
|
+
const enablePersistenceOnSignIn = () => {
|
|
2237
|
+
const stop = state.store.subscribe((next) => {
|
|
2238
|
+
if (!Object.values(next.stacks).some(({ auth }) => auth.status === "authenticated")) return;
|
|
2239
|
+
stop();
|
|
2240
|
+
enablePersistence();
|
|
2241
|
+
});
|
|
2242
|
+
};
|
|
2243
|
+
const restore = require_jobStatus.lazy(() => credentials.restore().then((restored) => {
|
|
2244
|
+
if (restored.status === "unreachable") enablePersistenceOnSignIn();
|
|
2245
|
+
else enablePersistence();
|
|
2246
|
+
return restored;
|
|
2247
|
+
}, (error) => {
|
|
2248
|
+
enablePersistenceOnSignIn();
|
|
2249
|
+
throw error;
|
|
2250
|
+
}));
|
|
2251
|
+
const getActiveStack = state.scope.getActiveStack;
|
|
2252
|
+
const isAuthenticated = () => getActiveStack().auth.status === "authenticated";
|
|
2253
|
+
/**
|
|
2254
|
+
* Picks up where the stored session left off, once.
|
|
2255
|
+
*
|
|
2256
|
+
* 1. A stored stack becomes active again, with its project if its credential came back.
|
|
2257
|
+
* 2. Nothing stored, or the restore failed: `defaultBaseUrl` stays active; a failure rethrows.
|
|
2258
|
+
* 3. The caller got there first (a select, sign-in or sign-out): its scope and sign-ins stand.
|
|
2259
|
+
*
|
|
2260
|
+
* - Resolves with the stored credential's fate — only this tells `expired` from `empty`.
|
|
2261
|
+
* - Other stacks still come back, persistence still starts; repeat calls share one promise.
|
|
2262
|
+
*/
|
|
2263
|
+
const resume = require_jobStatus.lazy(() => restore().then(({ lastActive, status }) => {
|
|
2264
|
+
if (!lastActive || callerScoped) return status;
|
|
2265
|
+
const stack = selectStack(lastActive.baseUrl);
|
|
2266
|
+
if (lastActive.projectId !== null && stack.auth.status === "authenticated") selectProject(lastActive.projectId);
|
|
2267
|
+
return status;
|
|
2268
|
+
}));
|
|
2269
|
+
let callerScoped = false;
|
|
2270
|
+
const byCaller = (verb) => (...args) => {
|
|
2271
|
+
callerScoped = true;
|
|
2272
|
+
return verb(...args);
|
|
2273
|
+
};
|
|
2274
|
+
return {
|
|
2275
|
+
getActiveApi,
|
|
2276
|
+
getApi,
|
|
2277
|
+
session: {
|
|
2278
|
+
getState: state.store.get,
|
|
2279
|
+
subscribe: state.store.subscribe,
|
|
2280
|
+
resume,
|
|
2281
|
+
stack: {
|
|
2282
|
+
getActive: getActiveStack,
|
|
2283
|
+
select: byCaller(selectStack),
|
|
2284
|
+
close: closeStack,
|
|
2285
|
+
retryContext: perStack.context.retry,
|
|
2286
|
+
getContext: (baseUrl) => perStack.context.get(requireStackUrl(baseUrl)),
|
|
2287
|
+
getContextAsync: (baseUrl) => perStack.context.getAsync(requireStackUrl(baseUrl))
|
|
2288
|
+
},
|
|
2289
|
+
project: {
|
|
2290
|
+
select: byCaller(selectProject),
|
|
2291
|
+
close: closeProject,
|
|
2292
|
+
deactivate: state.scope.deactivate,
|
|
2293
|
+
retryContext: perProject.context.retry,
|
|
2294
|
+
getContext: (id) => perProject.context.get(id ?? requireProjectEntry().id),
|
|
2295
|
+
getContextAsync: (id) => perProject.context.getAsync(requireProjectEntry(id))
|
|
2296
|
+
},
|
|
2297
|
+
auth: {
|
|
2298
|
+
authenticate: byCaller(authenticate),
|
|
2299
|
+
replace: byCaller(replace),
|
|
2300
|
+
adoptInjected: byCaller(adoptInjected),
|
|
2301
|
+
signOut: byCaller(signOut),
|
|
2302
|
+
/** Whether the active stack holds a credential — `restoring` is not one yet. */
|
|
2303
|
+
isAuthenticated,
|
|
2304
|
+
isAuthenticatedAsync: () => resume().then(isAuthenticated),
|
|
2305
|
+
rotate: (baseUrl) => credentials.rotate(baseUrl).then(require_jobStatus.Result.unwrap),
|
|
2306
|
+
getHeaders: getAuthHeaders
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
};
|
|
2311
|
+
//#endregion
|
|
2312
|
+
//#region src/middlewares/loggingMiddleware.ts
|
|
2313
|
+
const CORRELATION_ID_HEADER = "X-Correlation-ID";
|
|
2314
|
+
const ALWAYS_REDACTED_HEADERS = [
|
|
2315
|
+
require_createFetchClient.KeboolaHttpHeader.MANAGEMENT_API_TOKEN,
|
|
2316
|
+
require_createFetchClient.KeboolaHttpHeader.STORAGE_API_TOKEN,
|
|
2317
|
+
"authorization",
|
|
2318
|
+
"cookie",
|
|
2319
|
+
"set-cookie"
|
|
2320
|
+
];
|
|
2321
|
+
const ALWAYS_REDACTED_QUERY_PARAMS = /* @__PURE__ */ new Set([
|
|
2322
|
+
"token",
|
|
2323
|
+
"apikey",
|
|
2324
|
+
"api_key",
|
|
2325
|
+
"access_token",
|
|
2326
|
+
"refresh_token",
|
|
2327
|
+
"code",
|
|
2328
|
+
"state",
|
|
2329
|
+
"signature",
|
|
2330
|
+
"sig",
|
|
2331
|
+
"x-amz-signature",
|
|
2332
|
+
"x-amz-credential",
|
|
2333
|
+
"x-amz-security-token",
|
|
2334
|
+
"password",
|
|
2335
|
+
"secret"
|
|
2336
|
+
]);
|
|
2337
|
+
const REDACTED = "[REDACTED]";
|
|
2338
|
+
const getRedactedUrl = (rawUrl) => {
|
|
2339
|
+
const url = new URL(rawUrl);
|
|
2340
|
+
if (url.username) url.username = REDACTED;
|
|
2341
|
+
if (url.password) url.password = REDACTED;
|
|
2342
|
+
url.searchParams.forEach((_value, key) => {
|
|
2343
|
+
if (ALWAYS_REDACTED_QUERY_PARAMS.has(key.toLowerCase())) url.searchParams.set(key, REDACTED);
|
|
2344
|
+
});
|
|
2345
|
+
return url.toString();
|
|
2346
|
+
};
|
|
2347
|
+
const ensureCorrelationId = (request) => {
|
|
2348
|
+
const correlationId = request.headers.get(CORRELATION_ID_HEADER) ?? require_generateUUID.generateUUID();
|
|
2349
|
+
request.headers.set(CORRELATION_ID_HEADER, correlationId);
|
|
2350
|
+
return correlationId;
|
|
2351
|
+
};
|
|
2352
|
+
const getRedactedHeaders = (headers, redacted) => {
|
|
2353
|
+
const redactedSet = new Set([...ALWAYS_REDACTED_HEADERS, ...redacted].map((h) => h.toLowerCase()));
|
|
2354
|
+
const result = {};
|
|
2355
|
+
headers.forEach((value, name) => {
|
|
2356
|
+
result[name] = redactedSet.has(name.toLowerCase()) ? REDACTED : value;
|
|
2357
|
+
});
|
|
2358
|
+
return result;
|
|
2359
|
+
};
|
|
2360
|
+
const getRequestLog = (request, redactedHeaders) => ({
|
|
2361
|
+
url: getRedactedUrl(request.url),
|
|
2362
|
+
headers: getRedactedHeaders(request.headers, redactedHeaders)
|
|
2363
|
+
});
|
|
2364
|
+
const getResponseLog = (response, redactedHeaders) => ({
|
|
2365
|
+
status: response.status,
|
|
2366
|
+
statusText: response.statusText,
|
|
2367
|
+
headers: getRedactedHeaders(response.headers, redactedHeaders)
|
|
2368
|
+
});
|
|
2369
|
+
const redactApiError = (error, redactedHeaders) => new require_createFetchClient.ApiError({
|
|
2370
|
+
request: new Request(getRedactedUrl(error.request.url), {
|
|
2371
|
+
method: error.request.method,
|
|
2372
|
+
headers: getRedactedHeaders(error.request.headers, redactedHeaders)
|
|
2373
|
+
}),
|
|
2374
|
+
response: new Response(null, {
|
|
2375
|
+
status: error.response.status,
|
|
2376
|
+
statusText: error.response.statusText,
|
|
2377
|
+
headers: getRedactedHeaders(error.response.headers, redactedHeaders)
|
|
2378
|
+
}),
|
|
2379
|
+
data: error.data
|
|
2380
|
+
});
|
|
2381
|
+
const formatLog = (level, log) => {
|
|
2382
|
+
const parts = [
|
|
2383
|
+
`[${level.toUpperCase()}]`,
|
|
2384
|
+
`${Math.round(log.durationMs)}ms`,
|
|
2385
|
+
log.method
|
|
2386
|
+
];
|
|
2387
|
+
parts.push(log.request.url);
|
|
2388
|
+
if ("response" in log) parts.push(String(log.response.status));
|
|
2389
|
+
return parts.join(" ");
|
|
2390
|
+
};
|
|
2391
|
+
const createSafeLogger = (logger, level = "error-only") => {
|
|
2392
|
+
return {
|
|
2393
|
+
info: (log) => {
|
|
2394
|
+
if (level !== "all") return;
|
|
2395
|
+
try {
|
|
2396
|
+
if (logger?.info) {
|
|
2397
|
+
logger.info(log);
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
console.info(formatLog("info", log));
|
|
2401
|
+
} catch {}
|
|
2402
|
+
},
|
|
2403
|
+
error: (log) => {
|
|
2404
|
+
try {
|
|
2405
|
+
if (logger?.error) {
|
|
2406
|
+
logger.error(log);
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
console.error(formatLog("error", log));
|
|
2410
|
+
} catch {}
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
};
|
|
2414
|
+
const createLoggingMiddleware = (options = {}) => {
|
|
2415
|
+
const safeLogger = createSafeLogger(options.logger, options.level);
|
|
2416
|
+
const redactHeaders = options.redactHeaders ?? [];
|
|
2417
|
+
return (next) => async (fetchRequest) => {
|
|
2418
|
+
const correlationId = ensureCorrelationId(fetchRequest.request);
|
|
2419
|
+
const start = performance.now();
|
|
2420
|
+
const method = fetchRequest.request.method.toLowerCase();
|
|
2421
|
+
const requestLog = getRequestLog(fetchRequest.request, redactHeaders);
|
|
2422
|
+
try {
|
|
2423
|
+
const result = await next(fetchRequest);
|
|
2424
|
+
safeLogger.info({
|
|
2425
|
+
correlationId,
|
|
2426
|
+
method,
|
|
2427
|
+
durationMs: performance.now() - start,
|
|
2428
|
+
request: requestLog,
|
|
2429
|
+
response: getResponseLog(result.response, redactHeaders)
|
|
2430
|
+
});
|
|
2431
|
+
return result;
|
|
2432
|
+
} catch (error) {
|
|
2433
|
+
if (error instanceof Error) safeLogger.error({
|
|
2434
|
+
correlationId,
|
|
2435
|
+
method,
|
|
2436
|
+
durationMs: performance.now() - start,
|
|
2437
|
+
request: requestLog,
|
|
2438
|
+
error: require_createFetchClient.isApiError(error) ? redactApiError(error, redactHeaders) : error
|
|
2439
|
+
});
|
|
2440
|
+
throw error;
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
};
|
|
2444
|
+
//#endregion
|
|
2445
|
+
//#region src/middlewares/retryMiddleware.ts
|
|
2446
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
2447
|
+
const DEFAULT_RETRYABLE_STATUSES = [
|
|
2448
|
+
require_createFetchClient.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
2449
|
+
require_createFetchClient.HttpStatus.BAD_GATEWAY,
|
|
2450
|
+
require_createFetchClient.HttpStatus.SERVICE_UNAVAILABLE,
|
|
2451
|
+
require_createFetchClient.HttpStatus.GATEWAY_TIMEOUT
|
|
2452
|
+
];
|
|
2453
|
+
const DEFAULT_BACKOFF_FACTOR = .3;
|
|
2454
|
+
const defaultShouldRetry = (error) => error instanceof require_createFetchClient.ApiError && DEFAULT_RETRYABLE_STATUSES.includes(error.response.status);
|
|
2455
|
+
const createRetryMiddleware = (options) => {
|
|
2456
|
+
const { maxRetries = DEFAULT_MAX_RETRIES, backoffFactor = DEFAULT_BACKOFF_FACTOR, shouldRetry = defaultShouldRetry } = options ?? {};
|
|
2457
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0) throw new RangeError("maxRetries must be a non-negative integer");
|
|
2458
|
+
if (!Number.isFinite(backoffFactor) || backoffFactor < 0) throw new RangeError("backoffFactor must be a finite non-negative number");
|
|
2459
|
+
return (next) => async (request) => {
|
|
2460
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
2461
|
+
return await next({
|
|
2462
|
+
...request,
|
|
2463
|
+
request: request.request.clone()
|
|
2464
|
+
});
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
if (attempt === maxRetries || !shouldRetry(error, attempt)) throw error;
|
|
2467
|
+
const delayMs = backoffFactor * 1e3 * Math.pow(2, attempt);
|
|
2468
|
+
await require_jobStatus.delay(delayMs, request.request.signal ?? void 0);
|
|
2469
|
+
}
|
|
2470
|
+
throw new Error("unreachable");
|
|
2471
|
+
};
|
|
2472
|
+
};
|
|
2473
|
+
//#endregion
|
|
1879
2474
|
exports.ApiError = require_createFetchClient.ApiError;
|
|
1880
2475
|
exports.HtmlResponseError = require_validateConnectionStatus.HtmlResponseError;
|
|
1881
2476
|
exports.HttpContentType = require_createFetchClient.HttpContentType;
|
|
@@ -1887,6 +2482,7 @@ exports.ManagementClientAuthError = require_ManagementClientAuthError.Management
|
|
|
1887
2482
|
exports.QueryJobError = require_QueryJobTimeoutError.QueryJobError;
|
|
1888
2483
|
exports.QueryJobTimeoutError = require_QueryJobTimeoutError.QueryJobTimeoutError;
|
|
1889
2484
|
exports.Result = require_jobStatus.Result;
|
|
2485
|
+
exports.SESSION_STORAGE_KEY = SESSION_STORAGE_KEY;
|
|
1890
2486
|
exports.ServiceUnavailableError = require_storage.ServiceUnavailableError;
|
|
1891
2487
|
exports.SyncActionError = require_SyncActionError.SyncActionError;
|
|
1892
2488
|
exports.UserError = require_UserError.UserError;
|
|
@@ -1917,6 +2513,7 @@ exports.dedupe = require_dedupe.dedupe;
|
|
|
1917
2513
|
exports.deepMerge = deepMerge;
|
|
1918
2514
|
exports.delay = require_jobStatus.delay;
|
|
1919
2515
|
exports.emitter = require_jobStatus.emitter;
|
|
2516
|
+
exports.ensure = ensure;
|
|
1920
2517
|
exports.failureOf = require_jobStatus.failureOf;
|
|
1921
2518
|
exports.formatValidationIssues = require_ValidationError.formatValidationIssues;
|
|
1922
2519
|
exports.generateUUID = require_generateUUID.generateUUID;
|