@shipfox/client-integrations 6.0.1 → 6.0.2

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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 51 files with swc (94.41ms)
2
+ Successfully compiled: 51 files with swc (83.75ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @shipfox/client-integrations
2
2
 
3
+ ## 6.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 102c5f4: Isolates private browser state and React Query data across authenticated principal transitions.
8
+ - Updated dependencies [4b85404]
9
+ - Updated dependencies [102c5f4]
10
+ - @shipfox/api-integration-core-dto@9.0.2
11
+ - @shipfox/api-integration-gitea-dto@9.0.2
12
+ - @shipfox/api-integration-github-dto@9.0.2
13
+ - @shipfox/api-integration-linear-dto@9.0.2
14
+ - @shipfox/api-integration-sentry-dto@9.0.2
15
+ - @shipfox/api-integration-slack-dto@9.0.2
16
+ - @shipfox/api-integration-webhook-dto@9.0.2
17
+ - @shipfox/react-ui@0.3.7
18
+ - @shipfox/client-ui@6.0.2
19
+ - @shipfox/client-shell@6.0.2
20
+ - @shipfox/client-auth@6.0.2
21
+ - @shipfox/integration-icons@0.2.2
22
+
3
23
  ## 6.0.1
4
24
 
5
25
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../src/linear-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;AAEhF,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAUnD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,sBAAsB,GAAG,SAAS,CAWpC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAmEjF"}
1
+ {"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../src/linear-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;AAEhF,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAanD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,sBAAsB,GAAG,SAAS,CAWpC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAmEjF"}
@@ -4,7 +4,10 @@ export const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id
4
4
  const linearInstallWorkspaceStorageKey = {
5
5
  key: LINEAR_INSTALL_WORKSPACE_KEY,
6
6
  lifetime: 'session',
7
- principalScope: 'workspace',
7
+ // This is a one-shot navigation hint, not private workspace state. It must
8
+ // remain readable on an OAuth callback before workspace hydration completes;
9
+ // the callback state and returned connection are the authoritative checks.
10
+ principalScope: 'global',
8
11
  serialize: (workspaceId)=>workspaceId,
9
12
  parse: (value)=>value || undefined
10
13
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/linear-callback.ts"],"sourcesContent":["import type {LinearCallbackQueryDto} from '@shipfox/api-integration-linear-dto';\nimport {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id';\n\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst linearInstallWorkspaceStorageKey = {\n key: LINEAR_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n principalScope: 'workspace',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\n\nexport function saveLinearInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n installWorkspaceStorage(storage).write(workspaceId);\n}\n\nexport function readLinearInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\n\nexport function clearLinearInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, linearInstallWorkspaceStorageKey);\n}\n\nexport function parseLinearCallbackQuery(\n search: Record<string, unknown>,\n): LinearCallbackQueryDto | undefined {\n const state = stringParam(search.state);\n if (!state) return undefined;\n\n const code = stringParam(search.code);\n if (code) return {code, state};\n\n const error = stringParam(search.error);\n if (!error) return undefined;\n const errorDescription = stringParam(search.error_description);\n return errorDescription ? {error, error_description: errorDescription, state} : {error, state};\n}\n\nexport function serializeLinearCallbackQuery(query: LinearCallbackQueryDto): string {\n const params = new URLSearchParams();\n if ('code' in query) params.set('code', query.code);\n else {\n params.set('error', query.error);\n if (query.error_description) params.set('error_description', query.error_description);\n }\n params.set('state', query.state);\n return params.toString();\n}\n\nexport type LinearCallbackFailure = {\n title?: string;\n message: string;\n startOver: boolean;\n signIn: boolean;\n};\n\nexport function classifyLinearCallbackError(error: unknown): LinearCallbackFailure {\n if (error instanceof ApiError) {\n if (error.code === 'invalid-linear-install-state') {\n return {\n title: 'Linear install link expired',\n message: 'Linear install link expired. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.code === 'linear-install-state-actor-mismatch' || error.code === 'unauthorized') {\n return {\n title: 'Different Shipfox account',\n message: 'Different Shipfox account. Sign in with the account that started this install.',\n startOver: true,\n signIn: true,\n };\n }\n if (\n error.code === 'linear-installation-already-linked' ||\n error.code === 'linear-connection-already-linked'\n ) {\n return {\n title: 'Linear already linked',\n message: 'This Linear organization is already linked to another workspace.',\n startOver: false,\n signIn: false,\n };\n }\n if (\n error.code === 'linear-authorization-scope-mismatch' ||\n error.code === 'linear-oauth-callback-error'\n ) {\n return {\n title: 'Linear permissions needed',\n message:\n 'Linear did not authorize the permissions Shipfox needs. Review the Linear consent and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.status === 0 || error.code === 'network-error') {\n return {\n message: 'Could not reach Shipfox. Check your connection and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (\n error.status >= 500 ||\n error.status === 429 ||\n error.code === 'provider-unavailable' ||\n error.code === 'timeout' ||\n error.code === 'rate-limited'\n ) {\n return {\n message: 'Linear is temporarily unavailable. Start a new install when it is available.',\n startOver: true,\n signIn: false,\n };\n }\n }\n return {\n message: 'Could not complete the Linear install. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n}\n\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","createTypedBrowserStorage","LINEAR_INSTALL_WORKSPACE_KEY","linearInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveLinearInstallWorkspace","storage","installWorkspaceStorage","write","readLinearInstallWorkspace","read","clearLinearInstallWorkspace","remove","parseLinearCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeLinearCallbackQuery","query","params","URLSearchParams","set","toString","classifyLinearCallbackError","title","message","startOver","signIn","status","length"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/linear-callback.ts"],"sourcesContent":["import type {LinearCallbackQueryDto} from '@shipfox/api-integration-linear-dto';\nimport {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id';\n\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst linearInstallWorkspaceStorageKey = {\n key: LINEAR_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n // This is a one-shot navigation hint, not private workspace state. It must\n // remain readable on an OAuth callback before workspace hydration completes;\n // the callback state and returned connection are the authoritative checks.\n principalScope: 'global',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\n\nexport function saveLinearInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n installWorkspaceStorage(storage).write(workspaceId);\n}\n\nexport function readLinearInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\n\nexport function clearLinearInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, linearInstallWorkspaceStorageKey);\n}\n\nexport function parseLinearCallbackQuery(\n search: Record<string, unknown>,\n): LinearCallbackQueryDto | undefined {\n const state = stringParam(search.state);\n if (!state) return undefined;\n\n const code = stringParam(search.code);\n if (code) return {code, state};\n\n const error = stringParam(search.error);\n if (!error) return undefined;\n const errorDescription = stringParam(search.error_description);\n return errorDescription ? {error, error_description: errorDescription, state} : {error, state};\n}\n\nexport function serializeLinearCallbackQuery(query: LinearCallbackQueryDto): string {\n const params = new URLSearchParams();\n if ('code' in query) params.set('code', query.code);\n else {\n params.set('error', query.error);\n if (query.error_description) params.set('error_description', query.error_description);\n }\n params.set('state', query.state);\n return params.toString();\n}\n\nexport type LinearCallbackFailure = {\n title?: string;\n message: string;\n startOver: boolean;\n signIn: boolean;\n};\n\nexport function classifyLinearCallbackError(error: unknown): LinearCallbackFailure {\n if (error instanceof ApiError) {\n if (error.code === 'invalid-linear-install-state') {\n return {\n title: 'Linear install link expired',\n message: 'Linear install link expired. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.code === 'linear-install-state-actor-mismatch' || error.code === 'unauthorized') {\n return {\n title: 'Different Shipfox account',\n message: 'Different Shipfox account. Sign in with the account that started this install.',\n startOver: true,\n signIn: true,\n };\n }\n if (\n error.code === 'linear-installation-already-linked' ||\n error.code === 'linear-connection-already-linked'\n ) {\n return {\n title: 'Linear already linked',\n message: 'This Linear organization is already linked to another workspace.',\n startOver: false,\n signIn: false,\n };\n }\n if (\n error.code === 'linear-authorization-scope-mismatch' ||\n error.code === 'linear-oauth-callback-error'\n ) {\n return {\n title: 'Linear permissions needed',\n message:\n 'Linear did not authorize the permissions Shipfox needs. Review the Linear consent and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.status === 0 || error.code === 'network-error') {\n return {\n message: 'Could not reach Shipfox. Check your connection and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (\n error.status >= 500 ||\n error.status === 429 ||\n error.code === 'provider-unavailable' ||\n error.code === 'timeout' ||\n error.code === 'rate-limited'\n ) {\n return {\n message: 'Linear is temporarily unavailable. Start a new install when it is available.',\n startOver: true,\n signIn: false,\n };\n }\n }\n return {\n message: 'Could not complete the Linear install. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n}\n\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","createTypedBrowserStorage","LINEAR_INSTALL_WORKSPACE_KEY","linearInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveLinearInstallWorkspace","storage","installWorkspaceStorage","write","readLinearInstallWorkspace","read","clearLinearInstallWorkspace","remove","parseLinearCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeLinearCallbackQuery","query","params","URLSearchParams","set","toString","classifyLinearCallbackError","title","message","startOver","signIn","status","length"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../src/sentry-callback.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAanD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,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,CAiD/E"}
1
+ {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../src/sentry-callback.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAenD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,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,CAiD/E"}
@@ -4,7 +4,9 @@ export const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id
4
4
  const sentryInstallWorkspaceStorageKey = {
5
5
  key: SENTRY_INSTALL_WORKSPACE_KEY,
6
6
  lifetime: 'session',
7
- principalScope: 'workspace',
7
+ // This is a one-shot navigation hint, not private workspace state. The
8
+ // callback validates it against the authenticated workspace list before use.
9
+ principalScope: 'global',
8
10
  serialize: (workspaceId)=>workspaceId,
9
11
  parse: (value)=>value || undefined
10
12
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sentry-callback.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id';\n\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst sentryInstallWorkspaceStorageKey = {\n key: SENTRY_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n principalScope: 'workspace',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\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 installWorkspaceStorage(storage).write(workspaceId);\n}\n\nexport function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\n\nexport function clearSentryInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, sentryInstallWorkspaceStorageKey);\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 (the client transport reports 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 return {\n kind: 'terminal',\n message:\n 'Sentry did not accept this install. Start the install again from workspace settings.',\n startOver: true,\n };\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","createTypedBrowserStorage","SENTRY_INSTALL_WORKSPACE_KEY","sentryInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveSentryInstallWorkspace","storage","installWorkspaceStorage","write","readSentryInstallWorkspace","read","clearSentryInstallWorkspace","remove","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","retry_after_seconds","Number","isFinite"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/sentry-callback.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id';\n\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst sentryInstallWorkspaceStorageKey = {\n key: SENTRY_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n // This is a one-shot navigation hint, not private workspace state. The\n // callback validates it against the authenticated workspace list before use.\n principalScope: 'global',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\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 installWorkspaceStorage(storage).write(workspaceId);\n}\n\nexport function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\n\nexport function clearSentryInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, sentryInstallWorkspaceStorageKey);\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 (the client transport reports 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 return {\n kind: 'terminal',\n message:\n 'Sentry did not accept this install. Start the install again from workspace settings.',\n startOver: true,\n };\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","createTypedBrowserStorage","SENTRY_INSTALL_WORKSPACE_KEY","sentryInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveSentryInstallWorkspace","storage","installWorkspaceStorage","write","readSentryInstallWorkspace","read","clearSentryInstallWorkspace","remove","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","retry_after_seconds","Number","isFinite"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"slack-callback.d.ts","sourceRoot":"","sources":["../src/slack-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,2BAA2B,uCAAuC,CAAC;AAChF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAUnD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE9F;AACD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEvF;AACD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE1E;AAMD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,qBAAqB,GAAG,SAAS,CASnC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAShF;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AACF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA4E/E"}
1
+ {"version":3,"file":"slack-callback.d.ts","sourceRoot":"","sources":["../src/slack-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,2BAA2B,uCAAuC,CAAC;AAChF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAanD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE9F;AACD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEvF;AACD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE1E;AAMD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,qBAAqB,GAAG,SAAS,CASnC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAShF;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AACF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA4E/E"}
@@ -4,7 +4,10 @@ export const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';
4
4
  const slackInstallWorkspaceStorageKey = {
5
5
  key: SLACK_INSTALL_WORKSPACE_KEY,
6
6
  lifetime: 'session',
7
- principalScope: 'workspace',
7
+ // This is a one-shot navigation hint, not private workspace state. It must
8
+ // remain readable on an OAuth callback before workspace hydration completes;
9
+ // the callback state and returned connection are the authoritative checks.
10
+ principalScope: 'global',
8
11
  serialize: (workspaceId)=>workspaceId,
9
12
  parse: (value)=>value || undefined
10
13
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/slack-callback.ts"],"sourcesContent":["import type {SlackCallbackQueryDto} from '@shipfox/api-integration-slack-dto';\nimport {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst slackInstallWorkspaceStorageKey = {\n key: SLACK_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n principalScope: 'workspace',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\n\nexport function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n installWorkspaceStorage(storage).write(workspaceId);\n}\nexport function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\nexport function clearSlackInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, slackInstallWorkspaceStorageKey);\n}\n\nexport function parseSlackCallbackQuery(\n search: Record<string, unknown>,\n): SlackCallbackQueryDto | undefined {\n const state = stringParam(search.state);\n if (!state) return undefined;\n const code = stringParam(search.code);\n if (code) return {code, state};\n const error = stringParam(search.error);\n if (!error) return undefined;\n const errorDescription = stringParam(search.error_description);\n return errorDescription ? {error, error_description: errorDescription, state} : {error, state};\n}\n\nexport function serializeSlackCallbackQuery(query: SlackCallbackQueryDto): string {\n const params = new URLSearchParams();\n if ('code' in query) params.set('code', query.code);\n else {\n params.set('error', query.error);\n if (query.error_description) params.set('error_description', query.error_description);\n }\n params.set('state', query.state);\n return params.toString();\n}\n\nexport type SlackCallbackFailure = {\n title: string;\n message: string;\n startOver: boolean;\n signIn: boolean;\n};\nexport function classifySlackCallbackError(error: unknown): SlackCallbackFailure {\n if (error instanceof ApiError) {\n if (error.code === 'invalid-slack-install-state')\n return failure(\n 'Slack install link expired',\n 'Slack install link expired. Start again from workspace settings.',\n true,\n );\n if (error.code === 'slack-install-state-actor-mismatch' || error.code === 'unauthorized')\n return {\n ...failure(\n 'Different Shipfox account',\n 'Different Shipfox account. Sign in with the account that started this install.',\n true,\n ),\n signIn: true,\n };\n if (['not-found', 'forbidden', 'workspace-inactive'].includes(error.code))\n return failure(\n 'Workspace access changed',\n 'You no longer have access to this workspace. Return to Shipfox to continue.',\n false,\n );\n if (\n [\n 'slack-installation-already-linked',\n 'slack-connection-already-linked',\n 'slug-conflict',\n ].includes(error.code)\n )\n return failure(\n 'Slack already linked',\n 'This Slack workspace is already linked and cannot be installed again here.',\n false,\n );\n if (\n [\n 'slack-authorization-scope-mismatch',\n 'slack-oauth-callback-error',\n 'access-denied',\n ].includes(error.code)\n )\n return failure(\n 'Slack permissions needed',\n 'Slack did not authorize the permissions Shipfox needs. Review the consent and start again.',\n true,\n );\n if (\n error.code === 'slack-enterprise-install-unsupported' ||\n error.code === 'slack-token-rotation-unsupported'\n )\n return failure(\n 'Slack install unsupported',\n 'This Slack installation is not supported. Return to Shipfox to continue.',\n false,\n );\n if (error.status === 0 || error.code === 'network-error')\n return failure('Could not reach Shipfox', 'Check your connection and start again.', true);\n if (\n error.status >= 500 ||\n error.status === 429 ||\n ['rate-limited', 'timeout', 'provider-unavailable', 'malformed-provider-response'].includes(\n error.code,\n )\n )\n return failure(\n 'Slack is temporarily unavailable',\n 'Start a new install when Slack is available.',\n true,\n );\n }\n return failure(\n 'Slack install could not be completed',\n 'Could not complete the Slack install. Start again from workspace settings.',\n true,\n );\n}\nfunction failure(title: string, message: string, startOver: boolean): SlackCallbackFailure {\n return {title, message, startOver, signIn: false};\n}\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","createTypedBrowserStorage","SLACK_INSTALL_WORKSPACE_KEY","slackInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveSlackInstallWorkspace","storage","installWorkspaceStorage","write","readSlackInstallWorkspace","read","clearSlackInstallWorkspace","remove","parseSlackCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeSlackCallbackQuery","query","params","URLSearchParams","set","toString","classifySlackCallbackError","failure","signIn","includes","status","title","message","startOver","length"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/slack-callback.ts"],"sourcesContent":["import type {SlackCallbackQueryDto} from '@shipfox/api-integration-slack-dto';\nimport {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst slackInstallWorkspaceStorageKey = {\n key: SLACK_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n // This is a one-shot navigation hint, not private workspace state. It must\n // remain readable on an OAuth callback before workspace hydration completes;\n // the callback state and returned connection are the authoritative checks.\n principalScope: 'global',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\n\nexport function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n installWorkspaceStorage(storage).write(workspaceId);\n}\nexport function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\nexport function clearSlackInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, slackInstallWorkspaceStorageKey);\n}\n\nexport function parseSlackCallbackQuery(\n search: Record<string, unknown>,\n): SlackCallbackQueryDto | undefined {\n const state = stringParam(search.state);\n if (!state) return undefined;\n const code = stringParam(search.code);\n if (code) return {code, state};\n const error = stringParam(search.error);\n if (!error) return undefined;\n const errorDescription = stringParam(search.error_description);\n return errorDescription ? {error, error_description: errorDescription, state} : {error, state};\n}\n\nexport function serializeSlackCallbackQuery(query: SlackCallbackQueryDto): string {\n const params = new URLSearchParams();\n if ('code' in query) params.set('code', query.code);\n else {\n params.set('error', query.error);\n if (query.error_description) params.set('error_description', query.error_description);\n }\n params.set('state', query.state);\n return params.toString();\n}\n\nexport type SlackCallbackFailure = {\n title: string;\n message: string;\n startOver: boolean;\n signIn: boolean;\n};\nexport function classifySlackCallbackError(error: unknown): SlackCallbackFailure {\n if (error instanceof ApiError) {\n if (error.code === 'invalid-slack-install-state')\n return failure(\n 'Slack install link expired',\n 'Slack install link expired. Start again from workspace settings.',\n true,\n );\n if (error.code === 'slack-install-state-actor-mismatch' || error.code === 'unauthorized')\n return {\n ...failure(\n 'Different Shipfox account',\n 'Different Shipfox account. Sign in with the account that started this install.',\n true,\n ),\n signIn: true,\n };\n if (['not-found', 'forbidden', 'workspace-inactive'].includes(error.code))\n return failure(\n 'Workspace access changed',\n 'You no longer have access to this workspace. Return to Shipfox to continue.',\n false,\n );\n if (\n [\n 'slack-installation-already-linked',\n 'slack-connection-already-linked',\n 'slug-conflict',\n ].includes(error.code)\n )\n return failure(\n 'Slack already linked',\n 'This Slack workspace is already linked and cannot be installed again here.',\n false,\n );\n if (\n [\n 'slack-authorization-scope-mismatch',\n 'slack-oauth-callback-error',\n 'access-denied',\n ].includes(error.code)\n )\n return failure(\n 'Slack permissions needed',\n 'Slack did not authorize the permissions Shipfox needs. Review the consent and start again.',\n true,\n );\n if (\n error.code === 'slack-enterprise-install-unsupported' ||\n error.code === 'slack-token-rotation-unsupported'\n )\n return failure(\n 'Slack install unsupported',\n 'This Slack installation is not supported. Return to Shipfox to continue.',\n false,\n );\n if (error.status === 0 || error.code === 'network-error')\n return failure('Could not reach Shipfox', 'Check your connection and start again.', true);\n if (\n error.status >= 500 ||\n error.status === 429 ||\n ['rate-limited', 'timeout', 'provider-unavailable', 'malformed-provider-response'].includes(\n error.code,\n )\n )\n return failure(\n 'Slack is temporarily unavailable',\n 'Start a new install when Slack is available.',\n true,\n );\n }\n return failure(\n 'Slack install could not be completed',\n 'Could not complete the Slack install. Start again from workspace settings.',\n true,\n );\n}\nfunction failure(title: string, message: string, startOver: boolean): SlackCallbackFailure {\n return {title, message, startOver, signIn: false};\n}\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","createTypedBrowserStorage","SLACK_INSTALL_WORKSPACE_KEY","slackInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveSlackInstallWorkspace","storage","installWorkspaceStorage","write","readSlackInstallWorkspace","read","clearSlackInstallWorkspace","remove","parseSlackCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeSlackCallbackQuery","query","params","URLSearchParams","set","toString","classifySlackCallbackError","failure","signIn","includes","status","title","message","startOver","length"],"mappings":"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"}