@shipfox/client-integrations 0.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/dist/components/callback-status-shell.d.ts +9 -0
  4. package/dist/components/callback-status-shell.d.ts.map +1 -0
  5. package/dist/components/callback-status-shell.js +81 -0
  6. package/dist/components/callback-status-shell.js.map +1 -0
  7. package/dist/components/integration-gallery.stories.d.ts.map +1 -1
  8. package/dist/components/integration-gallery.stories.js +28 -0
  9. package/dist/components/integration-gallery.stories.js.map +1 -1
  10. package/dist/components/redirect-install-page.d.ts +2 -1
  11. package/dist/components/redirect-install-page.d.ts.map +1 -1
  12. package/dist/components/redirect-install-page.js +4 -2
  13. package/dist/components/redirect-install-page.js.map +1 -1
  14. package/dist/feature.d.ts +8 -0
  15. package/dist/feature.d.ts.map +1 -1
  16. package/dist/feature.js +10 -0
  17. package/dist/feature.js.map +1 -1
  18. package/dist/hooks/api/integrations.d.ts +41 -0
  19. package/dist/hooks/api/integrations.d.ts.map +1 -1
  20. package/dist/hooks/api/integrations.js +24 -0
  21. package/dist/hooks/api/integrations.js.map +1 -1
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +3 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/pages/linear-callback-page.d.ts.map +1 -1
  27. package/dist/pages/linear-callback-page.js +11 -78
  28. package/dist/pages/linear-callback-page.js.map +1 -1
  29. package/dist/pages/linear-install-page.d.ts.map +1 -1
  30. package/dist/pages/linear-install-page.js +1 -0
  31. package/dist/pages/linear-install-page.js.map +1 -1
  32. package/dist/pages/sentry-install-page.d.ts.map +1 -1
  33. package/dist/pages/sentry-install-page.js +1 -0
  34. package/dist/pages/sentry-install-page.js.map +1 -1
  35. package/dist/pages/slack-callback-page.d.ts +2 -0
  36. package/dist/pages/slack-callback-page.d.ts.map +1 -0
  37. package/dist/pages/slack-callback-page.js +114 -0
  38. package/dist/pages/slack-callback-page.js.map +1 -0
  39. package/dist/pages/slack-install-page.d.ts +2 -0
  40. package/dist/pages/slack-install-page.d.ts.map +1 -0
  41. package/dist/pages/slack-install-page.js +19 -0
  42. package/dist/pages/slack-install-page.js.map +1 -0
  43. package/dist/provider-catalog.d.ts +1 -1
  44. package/dist/provider-catalog.d.ts.map +1 -1
  45. package/dist/provider-catalog.js +5 -0
  46. package/dist/provider-catalog.js.map +1 -1
  47. package/dist/routes/slack-callback.d.ts +6 -0
  48. package/dist/routes/slack-callback.d.ts.map +1 -0
  49. package/dist/routes/slack-callback.js +7 -0
  50. package/dist/routes/slack-callback.js.map +1 -0
  51. package/dist/routes/slack.d.ts +6 -0
  52. package/dist/routes/slack.d.ts.map +1 -0
  53. package/dist/routes/slack.js +7 -0
  54. package/dist/routes/slack.js.map +1 -0
  55. package/dist/slack-callback.d.ts +17 -0
  56. package/dist/slack-callback.d.ts.map +1 -0
  57. package/dist/slack-callback.js +99 -0
  58. package/dist/slack-callback.js.map +1 -0
  59. package/dist/tsconfig.test.tsbuildinfo +1 -1
  60. package/package.json +26 -48
  61. package/src/components/callback-status-shell.tsx +73 -0
  62. package/src/components/integration-gallery.stories.tsx +18 -0
  63. package/src/components/redirect-install-page.tsx +3 -1
  64. package/src/feature.ts +10 -0
  65. package/src/hooks/api/integrations.test.ts +30 -0
  66. package/src/hooks/api/integrations.ts +35 -0
  67. package/src/index.ts +3 -0
  68. package/src/pages/linear-callback-page.tsx +11 -52
  69. package/src/pages/linear-install-page.tsx +1 -0
  70. package/src/pages/sentry-install-page.tsx +1 -0
  71. package/src/pages/slack-callback-page.tsx +120 -0
  72. package/src/pages/slack-install-page.tsx +22 -0
  73. package/src/provider-catalog.ts +7 -1
  74. package/src/routes/slack-callback.tsx +3 -0
  75. package/src/routes/slack.tsx +3 -0
  76. package/src/slack-callback.test.ts +32 -0
  77. package/src/slack-callback.ts +141 -0
  78. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,7 @@
1
+ import { defineRoute } from '@shipfox/client-shell/runtime';
2
+ import { SlackInstallPage } from '#pages/slack-install-page.js';
3
+ export default defineRoute({
4
+ component: SlackInstallPage
5
+ });
6
+
7
+ //# sourceMappingURL=slack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/routes/slack.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SlackInstallPage} from '#pages/slack-install-page.js';\nexport default defineRoute({component: SlackInstallPage});\n"],"names":["defineRoute","SlackInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,gBAAgB,QAAO,+BAA+B;AAC9D,eAAeD,YAAY;IAACE,WAAWD;AAAgB,GAAG"}
@@ -0,0 +1,17 @@
1
+ import type { SlackCallbackQueryDto } from '@shipfox/api-integration-slack-dto';
2
+ export declare const SLACK_INSTALL_WORKSPACE_KEY = "shipfox.slack-install.workspace-id";
3
+ type WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
4
+ export declare function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void;
5
+ export declare function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined;
6
+ export declare function clearSlackInstallWorkspace(storage: WorkspaceStorage): void;
7
+ export declare function parseSlackCallbackQuery(search: Record<string, unknown>): SlackCallbackQueryDto | undefined;
8
+ export declare function serializeSlackCallbackQuery(query: SlackCallbackQueryDto): string;
9
+ export type SlackCallbackFailure = {
10
+ title: string;
11
+ message: string;
12
+ startOver: boolean;
13
+ signIn: boolean;
14
+ };
15
+ export declare function classifySlackCallbackError(error: unknown): SlackCallbackFailure;
16
+ export {};
17
+ //# sourceMappingURL=slack-callback.d.ts.map
@@ -0,0 +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;AAG9E,eAAO,MAAM,2BAA2B,uCAAuC,CAAC;AAChF,KAAK,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;AAE5E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAM9F;AACD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAMvF;AACD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAM1E;AAED,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"}
@@ -0,0 +1,99 @@
1
+ import { ApiError } from '@shipfox/client-api';
2
+ export const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';
3
+ export function saveSlackInstallWorkspace(storage, workspaceId) {
4
+ try {
5
+ storage.setItem(SLACK_INSTALL_WORKSPACE_KEY, workspaceId);
6
+ } catch {
7
+ // Storage only supports recovery navigation.
8
+ }
9
+ }
10
+ export function readSlackInstallWorkspace(storage) {
11
+ try {
12
+ return storage.getItem(SLACK_INSTALL_WORKSPACE_KEY) ?? undefined;
13
+ } catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ export function clearSlackInstallWorkspace(storage) {
18
+ try {
19
+ storage.removeItem(SLACK_INSTALL_WORKSPACE_KEY);
20
+ } catch {
21
+ // A stale recovery hint cannot affect the API callback.
22
+ }
23
+ }
24
+ export function parseSlackCallbackQuery(search) {
25
+ const state = stringParam(search.state);
26
+ if (!state) return undefined;
27
+ const code = stringParam(search.code);
28
+ if (code) return {
29
+ code,
30
+ state
31
+ };
32
+ const error = stringParam(search.error);
33
+ if (!error) return undefined;
34
+ const errorDescription = stringParam(search.error_description);
35
+ return errorDescription ? {
36
+ error,
37
+ error_description: errorDescription,
38
+ state
39
+ } : {
40
+ error,
41
+ state
42
+ };
43
+ }
44
+ export function serializeSlackCallbackQuery(query) {
45
+ const params = new URLSearchParams();
46
+ if ('code' in query) params.set('code', query.code);
47
+ else {
48
+ params.set('error', query.error);
49
+ if (query.error_description) params.set('error_description', query.error_description);
50
+ }
51
+ params.set('state', query.state);
52
+ return params.toString();
53
+ }
54
+ export function classifySlackCallbackError(error) {
55
+ if (error instanceof ApiError) {
56
+ if (error.code === 'invalid-slack-install-state') return failure('Slack install link expired', 'Slack install link expired. Start again from workspace settings.', true);
57
+ if (error.code === 'slack-install-state-actor-mismatch' || error.code === 'unauthorized') return {
58
+ ...failure('Different Shipfox account', 'Different Shipfox account. Sign in with the account that started this install.', true),
59
+ signIn: true
60
+ };
61
+ if ([
62
+ 'not-found',
63
+ 'forbidden',
64
+ 'workspace-inactive'
65
+ ].includes(error.code)) return failure('Workspace access changed', 'You no longer have access to this workspace. Return to Shipfox to continue.', false);
66
+ if ([
67
+ 'slack-installation-already-linked',
68
+ 'slack-connection-already-linked',
69
+ 'slug-conflict'
70
+ ].includes(error.code)) return failure('Slack already linked', 'This Slack workspace is already linked and cannot be installed again here.', false);
71
+ if ([
72
+ 'slack-authorization-scope-mismatch',
73
+ 'slack-oauth-callback-error',
74
+ 'access-denied'
75
+ ].includes(error.code)) return failure('Slack permissions needed', 'Slack did not authorize the permissions Shipfox needs. Review the consent and start again.', true);
76
+ if (error.code === 'slack-enterprise-install-unsupported' || error.code === 'slack-token-rotation-unsupported') return failure('Slack install unsupported', 'This Slack installation is not supported. Return to Shipfox to continue.', false);
77
+ if (error.status === 0 || error.code === 'network-error') return failure('Could not reach Shipfox', 'Check your connection and start again.', true);
78
+ if (error.status >= 500 || error.status === 429 || [
79
+ 'rate-limited',
80
+ 'timeout',
81
+ 'provider-unavailable',
82
+ 'malformed-provider-response'
83
+ ].includes(error.code)) return failure('Slack is temporarily unavailable', 'Start a new install when Slack is available.', true);
84
+ }
85
+ return failure('Slack install could not be completed', 'Could not complete the Slack install. Start again from workspace settings.', true);
86
+ }
87
+ function failure(title, message, startOver) {
88
+ return {
89
+ title,
90
+ message,
91
+ startOver,
92
+ signIn: false
93
+ };
94
+ }
95
+ function stringParam(value) {
96
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
97
+ }
98
+
99
+ //# sourceMappingURL=slack-callback.js.map
@@ -0,0 +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';\n\nexport const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';\ntype WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;\n\nexport function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n try {\n storage.setItem(SLACK_INSTALL_WORKSPACE_KEY, workspaceId);\n } catch {\n // Storage only supports recovery navigation.\n }\n}\nexport function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n try {\n return storage.getItem(SLACK_INSTALL_WORKSPACE_KEY) ?? undefined;\n } catch {\n return undefined;\n }\n}\nexport function clearSlackInstallWorkspace(storage: WorkspaceStorage): void {\n try {\n storage.removeItem(SLACK_INSTALL_WORKSPACE_KEY);\n } catch {\n // A stale recovery hint cannot affect the API callback.\n }\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","SLACK_INSTALL_WORKSPACE_KEY","saveSlackInstallWorkspace","storage","workspaceId","setItem","readSlackInstallWorkspace","getItem","undefined","clearSlackInstallWorkspace","removeItem","parseSlackCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeSlackCallbackQuery","query","params","URLSearchParams","set","toString","classifySlackCallbackError","failure","signIn","includes","status","title","message","startOver","value","length"],"mappings":"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"}