@shipfox/client-integrations 0.2.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/.storybook/main.ts +1 -0
- package/.storybook/preview.css +4 -0
- package/.storybook/preview.tsx +56 -0
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +207 -0
- package/LICENSE +21 -0
- package/dist/components/connection-picker.d.ts +7 -0
- package/dist/components/connection-picker.d.ts.map +1 -0
- package/dist/components/connection-picker.js +98 -0
- package/dist/components/connection-picker.js.map +1 -0
- package/dist/components/installed-integrations-section.d.ts +14 -0
- package/dist/components/installed-integrations-section.d.ts.map +1 -0
- package/dist/components/installed-integrations-section.js +203 -0
- package/dist/components/installed-integrations-section.js.map +1 -0
- package/dist/components/integration-delete-confirm-modal.d.ts +10 -0
- package/dist/components/integration-delete-confirm-modal.d.ts.map +1 -0
- package/dist/components/integration-delete-confirm-modal.js +61 -0
- package/dist/components/integration-delete-confirm-modal.js.map +1 -0
- package/dist/components/integration-gallery-for-workspace.d.ts +9 -0
- package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -0
- package/dist/components/integration-gallery-for-workspace.js +169 -0
- package/dist/components/integration-gallery-for-workspace.js.map +1 -0
- package/dist/components/integration-gallery.d.ts +8 -0
- package/dist/components/integration-gallery.d.ts.map +1 -0
- package/dist/components/integration-gallery.js +26 -0
- package/dist/components/integration-gallery.js.map +1 -0
- package/dist/components/integration-gallery.stories.d.ts +26 -0
- package/dist/components/integration-gallery.stories.d.ts.map +1 -0
- package/dist/components/integration-gallery.stories.js +335 -0
- package/dist/components/integration-gallery.stories.js.map +1 -0
- package/dist/components/integration-usage-events.d.ts +4 -0
- package/dist/components/integration-usage-events.d.ts.map +1 -0
- package/dist/components/integration-usage-events.js +127 -0
- package/dist/components/integration-usage-events.js.map +1 -0
- package/dist/components/integration-usage-modal.d.ts +16 -0
- package/dist/components/integration-usage-modal.d.ts.map +1 -0
- package/dist/components/integration-usage-modal.js +184 -0
- package/dist/components/integration-usage-modal.js.map +1 -0
- package/dist/components/provider-grid.d.ts +14 -0
- package/dist/components/provider-grid.d.ts.map +1 -0
- package/dist/components/provider-grid.js +152 -0
- package/dist/components/provider-grid.js.map +1 -0
- package/dist/components/redirect-install-page.d.ts +19 -0
- package/dist/components/redirect-install-page.d.ts.map +1 -0
- package/dist/components/redirect-install-page.js +68 -0
- package/dist/components/redirect-install-page.js.map +1 -0
- package/dist/components/repository-picker.d.ts +15 -0
- package/dist/components/repository-picker.d.ts.map +1 -0
- package/dist/components/repository-picker.js +78 -0
- package/dist/components/repository-picker.js.map +1 -0
- package/dist/components/webhook/copyable-value.d.ts +10 -0
- package/dist/components/webhook/copyable-value.d.ts.map +1 -0
- package/dist/components/webhook/copyable-value.js +65 -0
- package/dist/components/webhook/copyable-value.js.map +1 -0
- package/dist/components/webhook/webhook-create-modal.d.ts +10 -0
- package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -0
- package/dist/components/webhook/webhook-create-modal.js +233 -0
- package/dist/components/webhook/webhook-create-modal.js.map +1 -0
- package/dist/components/webhook/webhook-form-errors.d.ts +11 -0
- package/dist/components/webhook/webhook-form-errors.d.ts.map +1 -0
- package/dist/components/webhook/webhook-form-errors.js +27 -0
- package/dist/components/webhook/webhook-form-errors.js.map +1 -0
- package/dist/components/webhook/webhook-modals.stories.d.ts +25 -0
- package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -0
- package/dist/components/webhook/webhook-modals.stories.js +222 -0
- package/dist/components/webhook/webhook-modals.stories.js.map +1 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.d.ts +2 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.d.ts.map +1 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.js +20 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.js.map +1 -0
- package/dist/components/webhook/webhook-usage-details.d.ts +7 -0
- package/dist/components/webhook/webhook-usage-details.d.ts.map +1 -0
- package/dist/components/webhook/webhook-usage-details.js +49 -0
- package/dist/components/webhook/webhook-usage-details.js.map +1 -0
- package/dist/connection-status-badge.d.ts +12 -0
- package/dist/connection-status-badge.d.ts.map +1 -0
- package/dist/connection-status-badge.js +34 -0
- package/dist/connection-status-badge.js.map +1 -0
- package/dist/feature.d.ts +37 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/feature.js +48 -0
- package/dist/feature.js.map +1 -0
- package/dist/hooks/api/integrations.d.ts +265 -0
- package/dist/hooks/api/integrations.d.ts.map +1 -0
- package/dist/hooks/api/integrations.js +229 -0
- package/dist/hooks/api/integrations.js.map +1 -0
- package/dist/hooks/api/webhook-connections.d.ts +91 -0
- package/dist/hooks/api/webhook-connections.d.ts.map +1 -0
- package/dist/hooks/api/webhook-connections.js +97 -0
- package/dist/hooks/api/webhook-connections.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/integration-icon.d.ts +17 -0
- package/dist/integration-icon.d.ts.map +1 -0
- package/dist/integration-icon.js +22 -0
- package/dist/integration-icon.js.map +1 -0
- package/dist/linear-callback.d.ts +17 -0
- package/dist/linear-callback.d.ts.map +1 -0
- package/dist/linear-callback.js +113 -0
- package/dist/linear-callback.js.map +1 -0
- package/dist/pages/gitea-form-errors.d.ts +11 -0
- package/dist/pages/gitea-form-errors.d.ts.map +1 -0
- package/dist/pages/gitea-form-errors.js +35 -0
- package/dist/pages/gitea-form-errors.js.map +1 -0
- package/dist/pages/gitea-install-page.d.ts +2 -0
- package/dist/pages/gitea-install-page.d.ts.map +1 -0
- package/dist/pages/gitea-install-page.js +139 -0
- package/dist/pages/gitea-install-page.js.map +1 -0
- package/dist/pages/github-install-page.d.ts +2 -0
- package/dist/pages/github-install-page.d.ts.map +1 -0
- package/dist/pages/github-install-page.js +11 -0
- package/dist/pages/github-install-page.js.map +1 -0
- package/dist/pages/linear-callback-page.d.ts +2 -0
- package/dist/pages/linear-callback-page.d.ts.map +1 -0
- package/dist/pages/linear-callback-page.js +192 -0
- package/dist/pages/linear-callback-page.js.map +1 -0
- package/dist/pages/linear-install-page.d.ts +2 -0
- package/dist/pages/linear-install-page.d.ts.map +1 -0
- package/dist/pages/linear-install-page.js +24 -0
- package/dist/pages/linear-install-page.js.map +1 -0
- package/dist/pages/sentry-callback-page.d.ts +2 -0
- package/dist/pages/sentry-callback-page.d.ts.map +1 -0
- package/dist/pages/sentry-callback-page.js +256 -0
- package/dist/pages/sentry-callback-page.js.map +1 -0
- package/dist/pages/sentry-install-page.d.ts +2 -0
- package/dist/pages/sentry-install-page.d.ts.map +1 -0
- package/dist/pages/sentry-install-page.js +17 -0
- package/dist/pages/sentry-install-page.js.map +1 -0
- package/dist/pages/source-control-onboarding-page.d.ts +2 -0
- package/dist/pages/source-control-onboarding-page.d.ts.map +1 -0
- package/dist/pages/source-control-onboarding-page.js +37 -0
- package/dist/pages/source-control-onboarding-page.js.map +1 -0
- package/dist/provider-catalog.d.ts +15 -0
- package/dist/provider-catalog.d.ts.map +1 -0
- package/dist/provider-catalog.js +28 -0
- package/dist/provider-catalog.js.map +1 -0
- package/dist/routes/gitea.d.ts +6 -0
- package/dist/routes/gitea.d.ts.map +1 -0
- package/dist/routes/gitea.js +7 -0
- package/dist/routes/gitea.js.map +1 -0
- package/dist/routes/github-callback.d.ts +6 -0
- package/dist/routes/github-callback.d.ts.map +1 -0
- package/dist/routes/github-callback.js +129 -0
- package/dist/routes/github-callback.js.map +1 -0
- package/dist/routes/github.d.ts +6 -0
- package/dist/routes/github.d.ts.map +1 -0
- package/dist/routes/github.js +7 -0
- package/dist/routes/github.js.map +1 -0
- package/dist/routes/integrations.d.ts +6 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +7 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/linear-callback.d.ts +6 -0
- package/dist/routes/linear-callback.d.ts.map +1 -0
- package/dist/routes/linear-callback.js +7 -0
- package/dist/routes/linear-callback.js.map +1 -0
- package/dist/routes/linear.d.ts +6 -0
- package/dist/routes/linear.d.ts.map +1 -0
- package/dist/routes/linear.js +7 -0
- package/dist/routes/linear.js.map +1 -0
- package/dist/routes/sentry-callback.d.ts +6 -0
- package/dist/routes/sentry-callback.d.ts.map +1 -0
- package/dist/routes/sentry-callback.js +7 -0
- package/dist/routes/sentry-callback.js.map +1 -0
- package/dist/routes/sentry.d.ts +6 -0
- package/dist/routes/sentry.d.ts.map +1 -0
- package/dist/routes/sentry.js +7 -0
- package/dist/routes/sentry.js.map +1 -0
- package/dist/sentry-callback.d.ts +35 -0
- package/dist/sentry-callback.d.ts.map +1 -0
- package/dist/sentry-callback.js +132 -0
- package/dist/sentry-callback.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +120 -0
- package/src/components/connection-picker.tsx +89 -0
- package/src/components/installed-integrations-section.tsx +198 -0
- package/src/components/integration-delete-confirm-modal.tsx +62 -0
- package/src/components/integration-gallery-for-workspace.tsx +193 -0
- package/src/components/integration-gallery.stories.tsx +302 -0
- package/src/components/integration-gallery.test.tsx +692 -0
- package/src/components/integration-gallery.tsx +49 -0
- package/src/components/integration-usage-events.test.ts +80 -0
- package/src/components/integration-usage-events.ts +116 -0
- package/src/components/integration-usage-modal.tsx +178 -0
- package/src/components/provider-grid.tsx +163 -0
- package/src/components/redirect-install-page.test.tsx +98 -0
- package/src/components/redirect-install-page.tsx +68 -0
- package/src/components/repository-picker.tsx +102 -0
- package/src/components/webhook/copyable-value.test.tsx +41 -0
- package/src/components/webhook/copyable-value.tsx +72 -0
- package/src/components/webhook/webhook-create-modal.tsx +247 -0
- package/src/components/webhook/webhook-form-errors.test.ts +45 -0
- package/src/components/webhook/webhook-form-errors.ts +32 -0
- package/src/components/webhook/webhook-modals.stories.tsx +225 -0
- package/src/components/webhook/webhook-public-endpoint-alert.tsx +15 -0
- package/src/components/webhook/webhook-usage-details.tsx +44 -0
- package/src/connection-status-badge.tsx +38 -0
- package/src/feature.ts +47 -0
- package/src/hooks/api/integrations.test.ts +105 -0
- package/src/hooks/api/integrations.ts +287 -0
- package/src/hooks/api/webhook-connections.ts +115 -0
- package/src/index.ts +16 -0
- package/src/integration-icon.react.test.tsx +40 -0
- package/src/integration-icon.test.ts +19 -0
- package/src/integration-icon.tsx +26 -0
- package/src/linear-callback.test.ts +227 -0
- package/src/linear-callback.ts +136 -0
- package/src/pages/gitea-form-errors.test.ts +55 -0
- package/src/pages/gitea-form-errors.ts +36 -0
- package/src/pages/gitea-install-page.tsx +108 -0
- package/src/pages/github-install-page.tsx +11 -0
- package/src/pages/linear-callback-page.test.tsx +157 -0
- package/src/pages/linear-callback-page.tsx +175 -0
- package/src/pages/linear-install-page.tsx +26 -0
- package/src/pages/sentry-callback-page.test.tsx +205 -0
- package/src/pages/sentry-callback-page.tsx +238 -0
- package/src/pages/sentry-install-page.tsx +17 -0
- package/src/pages/source-control-onboarding-page.test.tsx +46 -0
- package/src/pages/source-control-onboarding-page.tsx +26 -0
- package/src/provider-catalog.test.ts +42 -0
- package/src/provider-catalog.ts +49 -0
- package/src/routes/gitea.tsx +4 -0
- package/src/routes/github-callback.tsx +114 -0
- package/src/routes/github.tsx +4 -0
- package/src/routes/integrations.tsx +4 -0
- package/src/routes/linear-callback.tsx +4 -0
- package/src/routes/linear.tsx +4 -0
- package/src/routes/sentry-callback.tsx +4 -0
- package/src/routes/sentry.tsx +4 -0
- package/src/sentry-callback.test.ts +239 -0
- package/src/sentry-callback.ts +142 -0
- package/test/render.tsx +111 -0
- package/test/setup.ts +3 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vercel.json +8 -0
- package/vitest.config.ts +75 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pages/source-control-onboarding-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {ProviderGrid} from '#components/provider-grid.js';\nimport {useIntegrationProvidersQuery} from '#hooks/api/integrations.js';\n\nexport function SourceControlOnboardingPage() {\n const workspace = useActiveWorkspace();\n const providersQuery = useIntegrationProvidersQuery({capability: 'source_control'});\n\n return (\n <div className=\"mx-auto flex w-full max-w-[640px] flex-col gap-20\">\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h1\">Install source control</Header>\n <Text size=\"md\" className=\"text-foreground-neutral-muted\">\n Shipfox needs a source control integration to import your repositories.\n </Text>\n </header>\n\n <ProviderGrid\n providersQuery={providersQuery}\n workspaceId={workspace.id}\n emptyMessage=\"Enable at least one source-control provider in the application settings.\"\n />\n </div>\n );\n}\n"],"names":["useActiveWorkspace","Header","Text","ProviderGrid","useIntegrationProvidersQuery","SourceControlOnboardingPage","workspace","providersQuery","capability","div","className","header","variant","size","workspaceId","id","emptyMessage"],"mappings":";AAAA,SAAQA,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAC1D,SAAQC,YAAY,QAAO,+BAA+B;AAC1D,SAAQC,4BAA4B,QAAO,6BAA6B;AAExE,OAAO,SAASC;IACd,MAAMC,YAAYN;IAClB,MAAMO,iBAAiBH,6BAA6B;QAACI,YAAY;IAAgB;IAEjF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACC;gBAAOD,WAAU;;kCAChB,KAACT;wBAAOW,SAAQ;kCAAK;;kCACrB,KAACV;wBAAKW,MAAK;wBAAKH,WAAU;kCAAgC;;;;0BAK5D,KAACP;gBACCI,gBAAgBA;gBAChBO,aAAaR,UAAUS,EAAE;gBACzBC,cAAa;;;;AAIrB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IconName } from '@shipfox/react-ui/icon';
|
|
2
|
+
export type ProviderSetupPath = '/workspaces/$wid/integrations/github' | '/workspaces/$wid/integrations/gitea' | '/workspaces/$wid/integrations/sentry' | '/workspaces/$wid/integrations/linear';
|
|
3
|
+
interface RouteProviderCatalogEntry {
|
|
4
|
+
kind: 'redirect-install' | 'direct-connect';
|
|
5
|
+
iconName: IconName;
|
|
6
|
+
setupPath: ProviderSetupPath;
|
|
7
|
+
}
|
|
8
|
+
interface ModalProviderCatalogEntry {
|
|
9
|
+
kind: 'modal-connect';
|
|
10
|
+
iconName: IconName;
|
|
11
|
+
}
|
|
12
|
+
export type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;
|
|
13
|
+
export declare const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=provider-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-catalog.d.ts","sourceRoot":"","sources":["../src/provider-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAIrD,MAAM,MAAM,iBAAiB,GACzB,sCAAsC,GACtC,qCAAqC,GACrC,sCAAsC,GACtC,sCAAsC,CAAC;AAE3C,UAAU,yBAAyB;IACjC,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,CAAC;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,yBAAyB,CAAC;AAEzF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAyBjE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const PROVIDER_CATALOG = {
|
|
2
|
+
github: {
|
|
3
|
+
kind: 'redirect-install',
|
|
4
|
+
iconName: 'github',
|
|
5
|
+
setupPath: '/workspaces/$wid/integrations/github'
|
|
6
|
+
},
|
|
7
|
+
sentry: {
|
|
8
|
+
kind: 'redirect-install',
|
|
9
|
+
iconName: 'sentry',
|
|
10
|
+
setupPath: '/workspaces/$wid/integrations/sentry'
|
|
11
|
+
},
|
|
12
|
+
linear: {
|
|
13
|
+
kind: 'redirect-install',
|
|
14
|
+
iconName: 'linear',
|
|
15
|
+
setupPath: '/workspaces/$wid/integrations/linear'
|
|
16
|
+
},
|
|
17
|
+
gitea: {
|
|
18
|
+
kind: 'direct-connect',
|
|
19
|
+
iconName: 'gitea',
|
|
20
|
+
setupPath: '/workspaces/$wid/integrations/gitea'
|
|
21
|
+
},
|
|
22
|
+
webhook: {
|
|
23
|
+
kind: 'modal-connect',
|
|
24
|
+
iconName: 'webhookLine'
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=provider-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider-catalog.ts"],"sourcesContent":["import type {IconName} from '@shipfox/react-ui/icon';\n\n// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed\n// against TanStack Router's route tree.\nexport type ProviderSetupPath =\n | '/workspaces/$wid/integrations/github'\n | '/workspaces/$wid/integrations/gitea'\n | '/workspaces/$wid/integrations/sentry'\n | '/workspaces/$wid/integrations/linear';\n\ninterface RouteProviderCatalogEntry {\n kind: 'redirect-install' | 'direct-connect';\n iconName: IconName;\n setupPath: ProviderSetupPath;\n}\n\ninterface ModalProviderCatalogEntry {\n kind: 'modal-connect';\n iconName: IconName;\n}\n\nexport type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {\n github: {\n kind: 'redirect-install',\n iconName: 'github',\n setupPath: '/workspaces/$wid/integrations/github',\n },\n sentry: {\n kind: 'redirect-install',\n iconName: 'sentry',\n setupPath: '/workspaces/$wid/integrations/sentry',\n },\n linear: {\n kind: 'redirect-install',\n iconName: 'linear',\n setupPath: '/workspaces/$wid/integrations/linear',\n },\n gitea: {\n kind: 'direct-connect',\n iconName: 'gitea',\n setupPath: '/workspaces/$wid/integrations/gitea',\n },\n webhook: {\n kind: 'modal-connect',\n iconName: 'webhookLine',\n },\n};\n"],"names":["PROVIDER_CATALOG","github","kind","iconName","setupPath","sentry","linear","gitea","webhook"],"mappings":"AAuBA,OAAO,MAAMA,mBAAyD;IACpEC,QAAQ;QACNC,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAC,QAAQ;QACNH,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAE,QAAQ;QACNJ,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAG,OAAO;QACLL,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAI,SAAS;QACPN,MAAM;QACNC,UAAU;IACZ;AACF,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitea.d.ts","sourceRoot":"","sources":["../../src/routes/gitea.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,8BAA8B,CAAC;;;;AAE9D,wBAA0D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/gitea.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {GiteaInstallPage} from '#pages/gitea-install-page.js';\n\nexport default defineRoute({component: GiteaInstallPage});\n"],"names":["defineRoute","GiteaInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,gBAAgB,QAAO,+BAA+B;AAE9D,eAAeD,YAAY;IAACE,WAAWD;AAAgB,GAAG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
|
|
2
|
+
readonly component: typeof GithubCallbackRoute;
|
|
3
|
+
}>;
|
|
4
|
+
export default _default;
|
|
5
|
+
declare function GithubCallbackRoute(): import("react").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=github-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-callback.d.ts","sourceRoot":"","sources":["../../src/routes/github-callback.tsx"],"names":[],"mappings":";;;AAkBA,wBAA6D;AAE7D,iBAAS,mBAAmB,gCAqD3B"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ApiError, apiRequest } from '@shipfox/client-api';
|
|
3
|
+
import { useRefreshAuth } from '@shipfox/client-auth';
|
|
4
|
+
import { defineRoute } from '@shipfox/client-shell/runtime';
|
|
5
|
+
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
6
|
+
import { toast } from '@shipfox/react-ui/toast';
|
|
7
|
+
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
8
|
+
import { useEffect } from 'react';
|
|
9
|
+
const callbackRequests = new Map();
|
|
10
|
+
const toastedCallbacks = new Set();
|
|
11
|
+
export default defineRoute({
|
|
12
|
+
component: GithubCallbackRoute
|
|
13
|
+
});
|
|
14
|
+
function GithubCallbackRoute() {
|
|
15
|
+
const search = useSearch({
|
|
16
|
+
strict: false
|
|
17
|
+
});
|
|
18
|
+
const navigate = useNavigate();
|
|
19
|
+
const refreshAuth = useRefreshAuth();
|
|
20
|
+
useEffect(()=>{
|
|
21
|
+
const missing = missingCallbackParams(search);
|
|
22
|
+
if (missing.length > 0) {
|
|
23
|
+
toast.error(`GitHub callback is missing: ${missing.join(', ')}. ${missing.includes('code') ? 'Enable "Request user authorization (OAuth) during installation" on the Shipfox GitHub App.' : ''}`);
|
|
24
|
+
navigate({
|
|
25
|
+
to: '/',
|
|
26
|
+
replace: true
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const params = githubCallbackParams(search);
|
|
31
|
+
if (!params) {
|
|
32
|
+
toast.error('GitHub callback is missing required parameters.');
|
|
33
|
+
navigate({
|
|
34
|
+
to: '/',
|
|
35
|
+
replace: true
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let disposed = false;
|
|
40
|
+
const key = encodeCallbackQuery(params);
|
|
41
|
+
const request = callbackRequests.get(key) ?? refreshAuth().then(async (session)=>{
|
|
42
|
+
await apiRequest(`/integrations/github/callback/api?${key}`, {
|
|
43
|
+
headers: {
|
|
44
|
+
authorization: `Bearer ${session.token}`
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
callbackRequests.set(key, request);
|
|
49
|
+
request.then(()=>{
|
|
50
|
+
if (disposed) return;
|
|
51
|
+
if (!toastedCallbacks.has(key)) {
|
|
52
|
+
toastedCallbacks.add(key);
|
|
53
|
+
toast.success('GitHub installed.');
|
|
54
|
+
}
|
|
55
|
+
navigate({
|
|
56
|
+
to: '/',
|
|
57
|
+
replace: true
|
|
58
|
+
});
|
|
59
|
+
}).catch((error)=>{
|
|
60
|
+
if (disposed) return;
|
|
61
|
+
if (!toastedCallbacks.has(key)) {
|
|
62
|
+
toastedCallbacks.add(key);
|
|
63
|
+
toast.error(githubCallbackErrorMessage(error));
|
|
64
|
+
}
|
|
65
|
+
navigate({
|
|
66
|
+
to: '/',
|
|
67
|
+
replace: true
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return ()=>{
|
|
71
|
+
disposed = true;
|
|
72
|
+
};
|
|
73
|
+
}, [
|
|
74
|
+
navigate,
|
|
75
|
+
refreshAuth,
|
|
76
|
+
search
|
|
77
|
+
]);
|
|
78
|
+
return /*#__PURE__*/ _jsx(FullPageLoader, {});
|
|
79
|
+
}
|
|
80
|
+
function missingCallbackParams(search) {
|
|
81
|
+
const missing = [];
|
|
82
|
+
if (!stringParam(search.code)) missing.push('code');
|
|
83
|
+
if (!numberParam(search.installation_id)) missing.push('installation_id');
|
|
84
|
+
if (!stringParam(search.state)) missing.push('state');
|
|
85
|
+
return missing;
|
|
86
|
+
}
|
|
87
|
+
function githubCallbackParams(search) {
|
|
88
|
+
const code = stringParam(search.code);
|
|
89
|
+
const installationId = numberParam(search.installation_id);
|
|
90
|
+
const state = stringParam(search.state);
|
|
91
|
+
if (!code || installationId === undefined || !state) return undefined;
|
|
92
|
+
const setupAction = stringParam(search.setup_action);
|
|
93
|
+
return setupAction ? {
|
|
94
|
+
code,
|
|
95
|
+
installationId,
|
|
96
|
+
state,
|
|
97
|
+
setupAction
|
|
98
|
+
} : {
|
|
99
|
+
code,
|
|
100
|
+
installationId,
|
|
101
|
+
state
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function encodeCallbackQuery(params) {
|
|
105
|
+
const search = new URLSearchParams();
|
|
106
|
+
search.set('code', params.code);
|
|
107
|
+
search.set('installation_id', params.installationId.toString());
|
|
108
|
+
search.set('state', params.state);
|
|
109
|
+
if (params.setupAction) search.set('setup_action', params.setupAction);
|
|
110
|
+
return search.toString();
|
|
111
|
+
}
|
|
112
|
+
function stringParam(value) {
|
|
113
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
114
|
+
}
|
|
115
|
+
function numberParam(value) {
|
|
116
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
117
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
118
|
+
const parsed = Number(value);
|
|
119
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
function githubCallbackErrorMessage(error) {
|
|
124
|
+
if (error instanceof ApiError) return error.message;
|
|
125
|
+
if (error instanceof Error) return error.message;
|
|
126
|
+
return 'Could not install GitHub.';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=github-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/github-callback.tsx"],"sourcesContent":["import {ApiError, apiRequest} from '@shipfox/client-api';\nimport {useRefreshAuth} from '@shipfox/client-auth';\nimport {defineRoute} from '@shipfox/client-shell/runtime';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate, useSearch} from '@tanstack/react-router';\nimport {useEffect} from 'react';\n\ninterface GithubCallbackParams {\n code: string;\n installationId: number;\n state: string;\n setupAction?: string;\n}\n\nconst callbackRequests = new Map<string, Promise<void>>();\nconst toastedCallbacks = new Set<string>();\n\nexport default defineRoute({component: GithubCallbackRoute});\n\nfunction GithubCallbackRoute() {\n const search = useSearch({strict: false});\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n\n useEffect(() => {\n const missing = missingCallbackParams(search);\n if (missing.length > 0) {\n toast.error(\n `GitHub callback is missing: ${missing.join(', ')}. ${missing.includes('code') ? 'Enable \"Request user authorization (OAuth) during installation\" on the Shipfox GitHub App.' : ''}`,\n );\n navigate({to: '/', replace: true});\n return;\n }\n const params = githubCallbackParams(search);\n if (!params) {\n toast.error('GitHub callback is missing required parameters.');\n navigate({to: '/', replace: true});\n return;\n }\n let disposed = false;\n const key = encodeCallbackQuery(params);\n const request =\n callbackRequests.get(key) ??\n refreshAuth().then(async (session) => {\n await apiRequest(`/integrations/github/callback/api?${key}`, {\n headers: {authorization: `Bearer ${session.token}`},\n });\n });\n callbackRequests.set(key, request);\n request\n .then(() => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('GitHub installed.');\n }\n navigate({to: '/', replace: true});\n })\n .catch((error: unknown) => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.error(githubCallbackErrorMessage(error));\n }\n navigate({to: '/', replace: true});\n });\n return () => {\n disposed = true;\n };\n }, [navigate, refreshAuth, search]);\n\n return <FullPageLoader />;\n}\n\nfunction missingCallbackParams(search: Record<string, unknown>): string[] {\n const missing: string[] = [];\n if (!stringParam(search.code)) missing.push('code');\n if (!numberParam(search.installation_id)) missing.push('installation_id');\n if (!stringParam(search.state)) missing.push('state');\n return missing;\n}\nfunction githubCallbackParams(search: Record<string, unknown>): GithubCallbackParams | undefined {\n const code = stringParam(search.code);\n const installationId = numberParam(search.installation_id);\n const state = stringParam(search.state);\n if (!code || installationId === undefined || !state) return undefined;\n const setupAction = stringParam(search.setup_action);\n return setupAction ? {code, installationId, state, setupAction} : {code, installationId, state};\n}\nfunction encodeCallbackQuery(params: GithubCallbackParams): string {\n const search = new URLSearchParams();\n search.set('code', params.code);\n search.set('installation_id', params.installationId.toString());\n search.set('state', params.state);\n if (params.setupAction) search.set('setup_action', params.setupAction);\n return search.toString();\n}\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\nfunction numberParam(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.length > 0) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n}\nfunction githubCallbackErrorMessage(error: unknown): string {\n if (error instanceof ApiError) return error.message;\n if (error instanceof Error) return error.message;\n return 'Could not install GitHub.';\n}\n"],"names":["ApiError","apiRequest","useRefreshAuth","defineRoute","FullPageLoader","toast","useNavigate","useSearch","useEffect","callbackRequests","Map","toastedCallbacks","Set","component","GithubCallbackRoute","search","strict","navigate","refreshAuth","missing","missingCallbackParams","length","error","join","includes","to","replace","params","githubCallbackParams","disposed","key","encodeCallbackQuery","request","get","then","session","headers","authorization","token","set","has","add","success","catch","githubCallbackErrorMessage","stringParam","code","push","numberParam","installation_id","state","installationId","undefined","setupAction","setup_action","URLSearchParams","toString","value","Number","isFinite","parsed","message","Error"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/routes/github.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;;;;AAEhE,wBAA2D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/github.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {GithubInstallPage} from '#pages/github-install-page.js';\n\nexport default defineRoute({component: GithubInstallPage});\n"],"names":["defineRoute","GithubInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAEhE,eAAeD,YAAY;IAACE,WAAWD;AAAiB,GAAG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SourceControlOnboardingPage } from '#pages/source-control-onboarding-page.js';
|
|
2
|
+
declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
|
|
3
|
+
readonly component: typeof SourceControlOnboardingPage;
|
|
4
|
+
}>;
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=integrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/routes/integrations.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,2BAA2B,EAAC,MAAM,0CAA0C,CAAC;;;;AAErF,wBAAqE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineRoute } from '@shipfox/client-shell/runtime';
|
|
2
|
+
import { SourceControlOnboardingPage } from '#pages/source-control-onboarding-page.js';
|
|
3
|
+
export default defineRoute({
|
|
4
|
+
component: SourceControlOnboardingPage
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=integrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/integrations.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SourceControlOnboardingPage} from '#pages/source-control-onboarding-page.js';\n\nexport default defineRoute({component: SourceControlOnboardingPage});\n"],"names":["defineRoute","SourceControlOnboardingPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,2BAA2B,QAAO,2CAA2C;AAErF,eAAeD,YAAY;IAACE,WAAWD;AAA2B,GAAG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { LinearCallbackPage } from '#pages/linear-callback-page.js';
|
|
2
|
+
declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
|
|
3
|
+
readonly component: typeof LinearCallbackPage;
|
|
4
|
+
}>;
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=linear-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../../src/routes/linear-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;;;;AAElE,wBAA4D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/linear-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {LinearCallbackPage} from '#pages/linear-callback-page.js';\n\nexport default defineRoute({component: LinearCallbackPage});\n"],"names":["defineRoute","LinearCallbackPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAElE,eAAeD,YAAY;IAACE,WAAWD;AAAkB,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/routes/linear.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;;;;AAEhE,wBAA2D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/linear.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {LinearInstallPage} from '#pages/linear-install-page.js';\n\nexport default defineRoute({component: LinearInstallPage});\n"],"names":["defineRoute","LinearInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAEhE,eAAeD,YAAY;IAACE,WAAWD;AAAiB,GAAG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SentryCallbackPage } from '#pages/sentry-callback-page.js';
|
|
2
|
+
declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
|
|
3
|
+
readonly component: typeof SentryCallbackPage;
|
|
4
|
+
}>;
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=sentry-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../../src/routes/sentry-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;;;;AAElE,wBAA4D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/sentry-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SentryCallbackPage} from '#pages/sentry-callback-page.js';\n\nexport default defineRoute({component: SentryCallbackPage});\n"],"names":["defineRoute","SentryCallbackPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAElE,eAAeD,YAAY;IAACE,WAAWD;AAAkB,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/routes/sentry.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;;;;AAEhE,wBAA2D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/sentry.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SentryInstallPage} from '#pages/sentry-install-page.js';\n\nexport default defineRoute({component: SentryInstallPage});\n"],"names":["defineRoute","SentryInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAEhE,eAAeD,YAAY;IAACE,WAAWD;AAAiB,GAAG"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const SENTRY_INSTALL_WORKSPACE_KEY = "shipfox.sentry-install.workspace-id";
|
|
2
|
+
type WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
3
|
+
export declare function saveSentryInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void;
|
|
4
|
+
export declare function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined;
|
|
5
|
+
export declare function clearSentryInstallWorkspace(storage: WorkspaceStorage): void;
|
|
6
|
+
export interface SentryCallbackParams {
|
|
7
|
+
code: string;
|
|
8
|
+
installationId: string;
|
|
9
|
+
/** Display-only: rendered in the confirm copy, never sent to the API. */
|
|
10
|
+
orgSlug?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare function parseSentryCallbackParams(search: Record<string, unknown>): SentryCallbackParams | undefined;
|
|
13
|
+
export interface SentryWorkspaceOption {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
export type SentryWorkspacePreselection = {
|
|
18
|
+
kind: 'pick';
|
|
19
|
+
preselectedId: string | undefined;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'none';
|
|
22
|
+
};
|
|
23
|
+
export declare function preselectSentryWorkspace(storedId: string | undefined, workspaces: SentryWorkspaceOption[]): SentryWorkspacePreselection;
|
|
24
|
+
export type SentryConnectFailure = {
|
|
25
|
+
kind: 'retryable';
|
|
26
|
+
message: string;
|
|
27
|
+
retryAfterSeconds?: number | undefined;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'terminal';
|
|
30
|
+
message: string;
|
|
31
|
+
startOver: boolean;
|
|
32
|
+
};
|
|
33
|
+
export declare function classifySentryConnectError(error: unknown): SentryConnectFailure;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=sentry-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../src/sentry-callback.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;AAK5E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAM/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAMxF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAM3E;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,oBAAoB,GAAG,SAAS,CAQlC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GACjD;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAC;AAKnB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,qBAAqB,EAAE,GAClC,2BAA2B,CAO7B;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAC5E;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CAAC;AAE5D,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA8C/E"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ApiError } from '@shipfox/client-api';
|
|
2
|
+
export const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id';
|
|
3
|
+
// Storage helpers swallow failures (quota, private mode, disabled storage):
|
|
4
|
+
// the handoff is an optimization, never a requirement — the callback always
|
|
5
|
+
// asks for explicit confirmation and only uses the stored id to pre-select.
|
|
6
|
+
export function saveSentryInstallWorkspace(storage, workspaceId) {
|
|
7
|
+
try {
|
|
8
|
+
storage.setItem(SENTRY_INSTALL_WORKSPACE_KEY, workspaceId);
|
|
9
|
+
} catch {
|
|
10
|
+
// The callback falls back to its sole-workspace pre-selection.
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function readSentryInstallWorkspace(storage) {
|
|
14
|
+
try {
|
|
15
|
+
return storage.getItem(SENTRY_INSTALL_WORKSPACE_KEY) ?? undefined;
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function clearSentryInstallWorkspace(storage) {
|
|
21
|
+
try {
|
|
22
|
+
storage.removeItem(SENTRY_INSTALL_WORKSPACE_KEY);
|
|
23
|
+
} catch {
|
|
24
|
+
// A tab-scoped leftover key is harmless: it only pre-selects.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function parseSentryCallbackParams(search) {
|
|
28
|
+
const code = stringParam(search.code);
|
|
29
|
+
// Sentry documents camelCase params; accept snake_case defensively.
|
|
30
|
+
const installationId = stringParam(search.installationId) ?? stringParam(search.installation_id);
|
|
31
|
+
if (!code || !installationId) return undefined;
|
|
32
|
+
const orgSlug = stringParam(search.orgSlug) ?? stringParam(search.org_slug);
|
|
33
|
+
return {
|
|
34
|
+
code,
|
|
35
|
+
installationId,
|
|
36
|
+
orgSlug
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Sentry's redirect carries no state token, so the callback can never prove it
|
|
40
|
+
// belongs to an install the user started — installation always requires an
|
|
41
|
+
// explicit click. The stored id (and a sole workspace) only pre-select.
|
|
42
|
+
export function preselectSentryWorkspace(storedId, workspaces) {
|
|
43
|
+
if (workspaces.length === 0) return {
|
|
44
|
+
kind: 'none'
|
|
45
|
+
};
|
|
46
|
+
if (storedId && workspaces.some((workspace)=>workspace.id === storedId)) {
|
|
47
|
+
return {
|
|
48
|
+
kind: 'pick',
|
|
49
|
+
preselectedId: storedId
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (workspaces.length === 1) return {
|
|
53
|
+
kind: 'pick',
|
|
54
|
+
preselectedId: workspaces[0]?.id
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
kind: 'pick',
|
|
58
|
+
preselectedId: undefined
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function classifySentryConnectError(error) {
|
|
62
|
+
if (error instanceof ApiError) {
|
|
63
|
+
if (error.code === 'sentry-installation-already-linked') {
|
|
64
|
+
return {
|
|
65
|
+
kind: 'terminal',
|
|
66
|
+
message: 'This Sentry org is already installed in another workspace.',
|
|
67
|
+
// Retrying or starting over would fail identically.
|
|
68
|
+
startOver: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (error.code === 'sentry-verification-in-progress') {
|
|
72
|
+
// A concurrent signed webhook is still verifying the install. The grant code
|
|
73
|
+
// is untouched, so the existing backoff re-calls and finds the verified row.
|
|
74
|
+
return {
|
|
75
|
+
kind: 'retryable',
|
|
76
|
+
message: 'Finishing Sentry verification. This only takes a moment.',
|
|
77
|
+
retryAfterSeconds: retryAfterSeconds(error.details)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (error.code === 'rate-limited') {
|
|
81
|
+
return {
|
|
82
|
+
kind: 'retryable',
|
|
83
|
+
message: 'Sentry is rate limiting requests. Try again in a moment.',
|
|
84
|
+
retryAfterSeconds: retryAfterSeconds(error.details)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (error.code === 'timeout' || error.code === 'provider-unavailable') {
|
|
88
|
+
return {
|
|
89
|
+
kind: 'retryable',
|
|
90
|
+
message: 'Sentry is unreachable. Try again.'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (error.status === 0 || error.code === 'network-error') {
|
|
94
|
+
// A network failure (apiRequest throws status 0) never reached the
|
|
95
|
+
// server, so the grant code is untouched and a plain Retry can recover.
|
|
96
|
+
// Must precede the status < 500 branch, which would otherwise treat it as
|
|
97
|
+
// a spent code and force a full restart.
|
|
98
|
+
return {
|
|
99
|
+
kind: 'retryable',
|
|
100
|
+
message: 'Could not reach Shipfox. Check your connection and try again.'
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (error.status < 500) {
|
|
104
|
+
// 4xx (access-denied, malformed-provider-response, validation): the grant
|
|
105
|
+
// code is spent or rejected — only a fresh install can recover.
|
|
106
|
+
return {
|
|
107
|
+
kind: 'terminal',
|
|
108
|
+
message: error.message,
|
|
109
|
+
startOver: true
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
kind: 'retryable',
|
|
115
|
+
message: 'Could not install Sentry. Try again.'
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function retryAfterSeconds(details) {
|
|
119
|
+
// client-api's toApiError stores the whole response body as ApiError.details,
|
|
120
|
+
// so the structured payload lives one level deeper at details.details (mirrors
|
|
121
|
+
// project-error.ts apiDetails()).
|
|
122
|
+
if (typeof details !== 'object' || details === null) return undefined;
|
|
123
|
+
const inner = details.details;
|
|
124
|
+
if (typeof inner !== 'object' || inner === null) return undefined;
|
|
125
|
+
const value = inner.retry_after_seconds;
|
|
126
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
127
|
+
}
|
|
128
|
+
function stringParam(value) {
|
|
129
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=sentry-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sentry-callback.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\n\nexport const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id';\n\ntype WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;\n\n// Storage helpers swallow failures (quota, private mode, disabled storage):\n// the handoff is an optimization, never a requirement — the callback always\n// asks for explicit confirmation and only uses the stored id to pre-select.\nexport function saveSentryInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n try {\n storage.setItem(SENTRY_INSTALL_WORKSPACE_KEY, workspaceId);\n } catch {\n // The callback falls back to its sole-workspace pre-selection.\n }\n}\n\nexport function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n try {\n return storage.getItem(SENTRY_INSTALL_WORKSPACE_KEY) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function clearSentryInstallWorkspace(storage: WorkspaceStorage): void {\n try {\n storage.removeItem(SENTRY_INSTALL_WORKSPACE_KEY);\n } catch {\n // A tab-scoped leftover key is harmless: it only pre-selects.\n }\n}\n\nexport interface SentryCallbackParams {\n code: string;\n installationId: string;\n /** Display-only: rendered in the confirm copy, never sent to the API. */\n orgSlug?: string | undefined;\n}\n\nexport function parseSentryCallbackParams(\n search: Record<string, unknown>,\n): SentryCallbackParams | undefined {\n const code = stringParam(search.code);\n // Sentry documents camelCase params; accept snake_case defensively.\n const installationId = stringParam(search.installationId) ?? stringParam(search.installation_id);\n if (!code || !installationId) return undefined;\n\n const orgSlug = stringParam(search.orgSlug) ?? stringParam(search.org_slug);\n return {code, installationId, orgSlug};\n}\n\nexport interface SentryWorkspaceOption {\n id: string;\n name: string;\n}\n\nexport type SentryWorkspacePreselection =\n | {kind: 'pick'; preselectedId: string | undefined}\n | {kind: 'none'};\n\n// Sentry's redirect carries no state token, so the callback can never prove it\n// belongs to an install the user started — installation always requires an\n// explicit click. The stored id (and a sole workspace) only pre-select.\nexport function preselectSentryWorkspace(\n storedId: string | undefined,\n workspaces: SentryWorkspaceOption[],\n): SentryWorkspacePreselection {\n if (workspaces.length === 0) return {kind: 'none'};\n if (storedId && workspaces.some((workspace) => workspace.id === storedId)) {\n return {kind: 'pick', preselectedId: storedId};\n }\n if (workspaces.length === 1) return {kind: 'pick', preselectedId: workspaces[0]?.id};\n return {kind: 'pick', preselectedId: undefined};\n}\n\nexport type SentryConnectFailure =\n | {kind: 'retryable'; message: string; retryAfterSeconds?: number | undefined}\n | {kind: 'terminal'; message: string; startOver: boolean};\n\nexport function classifySentryConnectError(error: unknown): SentryConnectFailure {\n if (error instanceof ApiError) {\n if (error.code === 'sentry-installation-already-linked') {\n return {\n kind: 'terminal',\n message: 'This Sentry org is already installed in another workspace.',\n // Retrying or starting over would fail identically.\n startOver: false,\n };\n }\n if (error.code === 'sentry-verification-in-progress') {\n // A concurrent signed webhook is still verifying the install. The grant code\n // is untouched, so the existing backoff re-calls and finds the verified row.\n return {\n kind: 'retryable',\n message: 'Finishing Sentry verification. This only takes a moment.',\n retryAfterSeconds: retryAfterSeconds(error.details),\n };\n }\n if (error.code === 'rate-limited') {\n return {\n kind: 'retryable',\n message: 'Sentry is rate limiting requests. Try again in a moment.',\n retryAfterSeconds: retryAfterSeconds(error.details),\n };\n }\n if (error.code === 'timeout' || error.code === 'provider-unavailable') {\n return {kind: 'retryable', message: 'Sentry is unreachable. Try again.'};\n }\n if (error.status === 0 || error.code === 'network-error') {\n // A network failure (apiRequest throws status 0) never reached the\n // server, so the grant code is untouched and a plain Retry can recover.\n // Must precede the status < 500 branch, which would otherwise treat it as\n // a spent code and force a full restart.\n return {\n kind: 'retryable',\n message: 'Could not reach Shipfox. Check your connection and try again.',\n };\n }\n if (error.status < 500) {\n // 4xx (access-denied, malformed-provider-response, validation): the grant\n // code is spent or rejected — only a fresh install can recover.\n return {kind: 'terminal', message: error.message, startOver: true};\n }\n }\n return {kind: 'retryable', message: 'Could not install Sentry. Try again.'};\n}\n\nfunction retryAfterSeconds(details: unknown): number | undefined {\n // client-api's toApiError stores the whole response body as ApiError.details,\n // so the structured payload lives one level deeper at details.details (mirrors\n // project-error.ts apiDetails()).\n if (typeof details !== 'object' || details === null) return undefined;\n const inner = (details as Record<string, unknown>).details;\n if (typeof inner !== 'object' || inner === null) return undefined;\n const value = (inner as Record<string, unknown>).retry_after_seconds;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","SENTRY_INSTALL_WORKSPACE_KEY","saveSentryInstallWorkspace","storage","workspaceId","setItem","readSentryInstallWorkspace","getItem","undefined","clearSentryInstallWorkspace","removeItem","parseSentryCallbackParams","search","code","stringParam","installationId","installation_id","orgSlug","org_slug","preselectSentryWorkspace","storedId","workspaces","length","kind","some","workspace","id","preselectedId","classifySentryConnectError","error","message","startOver","retryAfterSeconds","details","status","inner","value","retry_after_seconds","Number","isFinite"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAE7C,OAAO,MAAMC,+BAA+B,sCAAsC;AAIlF,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,OAAO,SAASC,2BAA2BC,OAAyB,EAAEC,WAAmB;IACvF,IAAI;QACFD,QAAQE,OAAO,CAACJ,8BAA8BG;IAChD,EAAE,OAAM;IACN,+DAA+D;IACjE;AACF;AAEA,OAAO,SAASE,2BAA2BH,OAAyB;IAClE,IAAI;QACF,OAAOA,QAAQI,OAAO,CAACN,iCAAiCO;IAC1D,EAAE,OAAM;QACN,OAAOA;IACT;AACF;AAEA,OAAO,SAASC,4BAA4BN,OAAyB;IACnE,IAAI;QACFA,QAAQO,UAAU,CAACT;IACrB,EAAE,OAAM;IACN,8DAA8D;IAChE;AACF;AASA,OAAO,SAASU,0BACdC,MAA+B;IAE/B,MAAMC,OAAOC,YAAYF,OAAOC,IAAI;IACpC,oEAAoE;IACpE,MAAME,iBAAiBD,YAAYF,OAAOG,cAAc,KAAKD,YAAYF,OAAOI,eAAe;IAC/F,IAAI,CAACH,QAAQ,CAACE,gBAAgB,OAAOP;IAErC,MAAMS,UAAUH,YAAYF,OAAOK,OAAO,KAAKH,YAAYF,OAAOM,QAAQ;IAC1E,OAAO;QAACL;QAAME;QAAgBE;IAAO;AACvC;AAWA,+EAA+E;AAC/E,2EAA2E;AAC3E,wEAAwE;AACxE,OAAO,SAASE,yBACdC,QAA4B,EAC5BC,UAAmC;IAEnC,IAAIA,WAAWC,MAAM,KAAK,GAAG,OAAO;QAACC,MAAM;IAAM;IACjD,IAAIH,YAAYC,WAAWG,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKN,WAAW;QACzE,OAAO;YAACG,MAAM;YAAQI,eAAeP;QAAQ;IAC/C;IACA,IAAIC,WAAWC,MAAM,KAAK,GAAG,OAAO;QAACC,MAAM;QAAQI,eAAeN,UAAU,CAAC,EAAE,EAAEK;IAAE;IACnF,OAAO;QAACH,MAAM;QAAQI,eAAenB;IAAS;AAChD;AAMA,OAAO,SAASoB,2BAA2BC,KAAc;IACvD,IAAIA,iBAAiB7B,UAAU;QAC7B,IAAI6B,MAAMhB,IAAI,KAAK,sCAAsC;YACvD,OAAO;gBACLU,MAAM;gBACNO,SAAS;gBACT,oDAAoD;gBACpDC,WAAW;YACb;QACF;QACA,IAAIF,MAAMhB,IAAI,KAAK,mCAAmC;YACpD,6EAA6E;YAC7E,6EAA6E;YAC7E,OAAO;gBACLU,MAAM;gBACNO,SAAS;gBACTE,mBAAmBA,kBAAkBH,MAAMI,OAAO;YACpD;QACF;QACA,IAAIJ,MAAMhB,IAAI,KAAK,gBAAgB;YACjC,OAAO;gBACLU,MAAM;gBACNO,SAAS;gBACTE,mBAAmBA,kBAAkBH,MAAMI,OAAO;YACpD;QACF;QACA,IAAIJ,MAAMhB,IAAI,KAAK,aAAagB,MAAMhB,IAAI,KAAK,wBAAwB;YACrE,OAAO;gBAACU,MAAM;gBAAaO,SAAS;YAAmC;QACzE;QACA,IAAID,MAAMK,MAAM,KAAK,KAAKL,MAAMhB,IAAI,KAAK,iBAAiB;YACxD,mEAAmE;YACnE,wEAAwE;YACxE,0EAA0E;YAC1E,yCAAyC;YACzC,OAAO;gBACLU,MAAM;gBACNO,SAAS;YACX;QACF;QACA,IAAID,MAAMK,MAAM,GAAG,KAAK;YACtB,0EAA0E;YAC1E,gEAAgE;YAChE,OAAO;gBAACX,MAAM;gBAAYO,SAASD,MAAMC,OAAO;gBAAEC,WAAW;YAAI;QACnE;IACF;IACA,OAAO;QAACR,MAAM;QAAaO,SAAS;IAAsC;AAC5E;AAEA,SAASE,kBAAkBC,OAAgB;IACzC,8EAA8E;IAC9E,+EAA+E;IAC/E,kCAAkC;IAClC,IAAI,OAAOA,YAAY,YAAYA,YAAY,MAAM,OAAOzB;IAC5D,MAAM2B,QAAQ,AAACF,QAAoCA,OAAO;IAC1D,IAAI,OAAOE,UAAU,YAAYA,UAAU,MAAM,OAAO3B;IACxD,MAAM4B,QAAQ,AAACD,MAAkCE,mBAAmB;IACpE,OAAO,OAAOD,UAAU,YAAYE,OAAOC,QAAQ,CAACH,SAASA,QAAQ5B;AACvE;AAEA,SAASM,YAAYsB,KAAc;IACjC,OAAO,OAAOA,UAAU,YAAYA,MAAMd,MAAM,GAAG,IAAIc,QAAQ5B;AACjE"}
|