@shipfox/client-secrets 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.
Files changed (114) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/.storybook/preview.css +4 -0
  3. package/.storybook/preview.tsx +56 -0
  4. package/.turbo/turbo-build.log +2 -0
  5. package/.turbo/turbo-type$colon$emit.log +1 -0
  6. package/.turbo/turbo-type.log +1 -0
  7. package/CHANGELOG.md +102 -0
  8. package/LICENSE +21 -0
  9. package/dist/components/copy-key.d.ts +8 -0
  10. package/dist/components/copy-key.d.ts.map +1 -0
  11. package/dist/components/copy-key.js +21 -0
  12. package/dist/components/copy-key.js.map +1 -0
  13. package/dist/components/delete-entry-dialog.d.ts +9 -0
  14. package/dist/components/delete-entry-dialog.d.ts.map +1 -0
  15. package/dist/components/delete-entry-dialog.js +66 -0
  16. package/dist/components/delete-entry-dialog.js.map +1 -0
  17. package/dist/components/form-errors.d.ts +17 -0
  18. package/dist/components/form-errors.d.ts.map +1 -0
  19. package/dist/components/form-errors.js +81 -0
  20. package/dist/components/form-errors.js.map +1 -0
  21. package/dist/components/form-shell.d.ts +14 -0
  22. package/dist/components/form-shell.d.ts.map +1 -0
  23. package/dist/components/form-shell.js +28 -0
  24. package/dist/components/form-shell.js.map +1 -0
  25. package/dist/components/secret-form.d.ts +10 -0
  26. package/dist/components/secret-form.d.ts.map +1 -0
  27. package/dist/components/secret-form.js +176 -0
  28. package/dist/components/secret-form.js.map +1 -0
  29. package/dist/components/secret-form.stories.d.ts +9 -0
  30. package/dist/components/secret-form.stories.d.ts.map +1 -0
  31. package/dist/components/secret-form.stories.js +60 -0
  32. package/dist/components/secret-form.stories.js.map +1 -0
  33. package/dist/components/store-key.d.ts +20 -0
  34. package/dist/components/store-key.d.ts.map +1 -0
  35. package/dist/components/store-key.js +25 -0
  36. package/dist/components/store-key.js.map +1 -0
  37. package/dist/components/store-section-shell.d.ts +10 -0
  38. package/dist/components/store-section-shell.d.ts.map +1 -0
  39. package/dist/components/store-section-shell.js +40 -0
  40. package/dist/components/store-section-shell.js.map +1 -0
  41. package/dist/components/variable-form.d.ts +12 -0
  42. package/dist/components/variable-form.d.ts.map +1 -0
  43. package/dist/components/variable-form.js +186 -0
  44. package/dist/components/variable-form.js.map +1 -0
  45. package/dist/components/variable-form.stories.d.ts +9 -0
  46. package/dist/components/variable-form.stories.d.ts.map +1 -0
  47. package/dist/components/variable-form.stories.js +61 -0
  48. package/dist/components/variable-form.stories.js.map +1 -0
  49. package/dist/components/workspace-secrets-section.d.ts +4 -0
  50. package/dist/components/workspace-secrets-section.d.ts.map +1 -0
  51. package/dist/components/workspace-secrets-section.js +251 -0
  52. package/dist/components/workspace-secrets-section.js.map +1 -0
  53. package/dist/components/workspace-secrets-section.stories.d.ts +13 -0
  54. package/dist/components/workspace-secrets-section.stories.d.ts.map +1 -0
  55. package/dist/components/workspace-secrets-section.stories.js +140 -0
  56. package/dist/components/workspace-secrets-section.stories.js.map +1 -0
  57. package/dist/components/workspace-variables-section.d.ts +4 -0
  58. package/dist/components/workspace-variables-section.d.ts.map +1 -0
  59. package/dist/components/workspace-variables-section.js +255 -0
  60. package/dist/components/workspace-variables-section.js.map +1 -0
  61. package/dist/components/workspace-variables-section.stories.d.ts +11 -0
  62. package/dist/components/workspace-variables-section.stories.d.ts.map +1 -0
  63. package/dist/components/workspace-variables-section.stories.js +127 -0
  64. package/dist/components/workspace-variables-section.stories.js.map +1 -0
  65. package/dist/hooks/api/create-store-api.d.ts +56 -0
  66. package/dist/hooks/api/create-store-api.d.ts.map +1 -0
  67. package/dist/hooks/api/create-store-api.js +95 -0
  68. package/dist/hooks/api/create-store-api.js.map +1 -0
  69. package/dist/hooks/api/secrets.d.ts +61 -0
  70. package/dist/hooks/api/secrets.d.ts.map +1 -0
  71. package/dist/hooks/api/secrets.js +15 -0
  72. package/dist/hooks/api/secrets.js.map +1 -0
  73. package/dist/hooks/api/variables.d.ts +88 -0
  74. package/dist/hooks/api/variables.d.ts.map +1 -0
  75. package/dist/hooks/api/variables.js +52 -0
  76. package/dist/hooks/api/variables.js.map +1 -0
  77. package/dist/index.d.ts +6 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +7 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/tsconfig.test.tsbuildinfo +1 -0
  82. package/package.json +89 -0
  83. package/src/components/copy-key.test.ts +44 -0
  84. package/src/components/copy-key.ts +21 -0
  85. package/src/components/delete-entry-dialog.tsx +57 -0
  86. package/src/components/form-errors.test.ts +67 -0
  87. package/src/components/form-errors.ts +59 -0
  88. package/src/components/form-shell.tsx +31 -0
  89. package/src/components/secret-form.stories.tsx +58 -0
  90. package/src/components/secret-form.test.tsx +37 -0
  91. package/src/components/secret-form.tsx +170 -0
  92. package/src/components/store-key.test.ts +53 -0
  93. package/src/components/store-key.ts +36 -0
  94. package/src/components/store-section-shell.tsx +32 -0
  95. package/src/components/variable-form.stories.tsx +58 -0
  96. package/src/components/variable-form.test.tsx +60 -0
  97. package/src/components/variable-form.tsx +182 -0
  98. package/src/components/workspace-secrets-section.stories.tsx +128 -0
  99. package/src/components/workspace-secrets-section.tsx +231 -0
  100. package/src/components/workspace-variables-section.stories.tsx +121 -0
  101. package/src/components/workspace-variables-section.tsx +238 -0
  102. package/src/hooks/api/create-store-api.ts +114 -0
  103. package/src/hooks/api/secrets.test.ts +157 -0
  104. package/src/hooks/api/secrets.ts +26 -0
  105. package/src/hooks/api/variables.ts +61 -0
  106. package/src/index.ts +5 -0
  107. package/test/fixtures/secrets.ts +59 -0
  108. package/test/setup.ts +3 -0
  109. package/tsconfig.build.json +9 -0
  110. package/tsconfig.build.tsbuildinfo +1 -0
  111. package/tsconfig.json +3 -0
  112. package/tsconfig.test.json +8 -0
  113. package/vercel.json +8 -0
  114. package/vitest.config.ts +75 -0
@@ -0,0 +1 @@
1
+ export {default} from '../../.storybook/main.ts';
@@ -0,0 +1,4 @@
1
+ @import "@shipfox/react-ui/index.css";
2
+
3
+ @source "../src";
4
+ @source "../../../shared/react/ui/src";
@@ -0,0 +1,56 @@
1
+ import './preview.css';
2
+ import {ThemeProvider} from '@shipfox/react-ui/theme';
3
+ import type {Decorator, Preview} from '@storybook/react';
4
+
5
+ if (typeof document !== 'undefined' && document.fonts) {
6
+ void Promise.all([
7
+ document.fonts.load("16px 'Inter'"),
8
+ document.fonts.load("italic 16px 'Inter'"),
9
+ document.fonts.load("16px 'Commit Mono'"),
10
+ document.fonts.load("bold 16px 'Commit Mono'"),
11
+ ]);
12
+ }
13
+
14
+ const withTheme: Decorator = (Story, context) => {
15
+ const theme = context.globals.theme;
16
+ return (
17
+ <ThemeProvider key={theme} defaultTheme={theme} storageKey={`shipfox-theme-${theme}`}>
18
+ <Story />
19
+ </ThemeProvider>
20
+ );
21
+ };
22
+
23
+ const preview: Preview = {
24
+ decorators: [withTheme],
25
+ parameters: {
26
+ argos: {
27
+ // Dual-theme coverage lives in @shipfox/react-ui (the theming source of truth);
28
+ // feature packages capture only the primary dark theme to limit Argos spend.
29
+ modes: {
30
+ dark: {theme: 'dark'},
31
+ },
32
+ fitToContent: false,
33
+ },
34
+ options: {
35
+ storySort: {method: 'alphabetical'},
36
+ },
37
+ },
38
+ globalTypes: {
39
+ theme: {
40
+ name: 'Theme',
41
+ description: 'Global theme for components',
42
+ defaultValue: 'dark',
43
+ toolbar: {
44
+ icon: 'sun',
45
+ items: [
46
+ {value: 'light', icon: 'sun', title: 'Light'},
47
+ {value: 'dark', icon: 'moon', title: 'Dark'},
48
+ {value: 'system', icon: 'info', title: 'System'},
49
+ ],
50
+ showName: true,
51
+ },
52
+ },
53
+ },
54
+ };
55
+
56
+ export default preview;
@@ -0,0 +1,2 @@
1
+ $ shipfox-swc
2
+ Successfully compiled: 18 files with swc (53.26ms)
@@ -0,0 +1 @@
1
+ $ shipfox-tsc-emit
@@ -0,0 +1 @@
1
+ $ shipfox-tsc-check
package/CHANGELOG.md ADDED
@@ -0,0 +1,102 @@
1
+ # @shipfox/client-secrets
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3d064b8: Publishes the client runtime closure with shell, feature, route, Vite, and testing contracts.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [3d064b8]
12
+ - @shipfox/client-api@0.2.0
13
+ - @shipfox/client-ui@0.2.0
14
+
15
+ ## 0.1.2
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [c18d624]
20
+ - @shipfox/react-ui@0.3.1
21
+ - @shipfox/client-ui@0.1.2
22
+
23
+ ## 0.1.1
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [1b0d344]
28
+ - @shipfox/api-secrets-dto@2.0.0
29
+ - @shipfox/client-ui@0.1.1
30
+ - @shipfox/client-api@0.0.1
31
+ - @shipfox/react-ui@0.3.0
32
+
33
+ ## 0.1.0
34
+
35
+ ### Minor Changes
36
+
37
+ - e51d464: Add the Secrets & Variables workspace settings UI (S1b).
38
+ - New `@shipfox/client-secrets` package: transport + React Query hooks (a shared
39
+ `createStoreApi` factory), a write-only secret form and a readable variable form
40
+ (TanStack Form + Zod, multiline `Textarea` values, live short-value / sensitive-name
41
+ advisories), and the workspace secrets/variables sections (single-call list, masked
42
+ secret values, copy-name, delete with blast-radius warning).
43
+ - `@shipfox/client-workspace-settings`: new Secrets and Variables settings pages and nav
44
+ entries.
45
+ - `@shipfox/api-secrets-dto`: export `SECRETS_MAX_LIST_LIMIT` and raise the list `limit`
46
+ cap so the settings UI can fetch the whole bounded set in one request; the variable
47
+ list item now carries `value_truncated`.
48
+ - `@shipfox/api-secrets`: the variable list returns a bounded single-line preview of each
49
+ value (the full value is read via `GET /variables/:key` when editing) so a single-call
50
+ list cannot materialize very large responses; startup fails if `SECRETS_MAX_PER_WORKSPACE`
51
+ exceeds the list limit.
52
+ - `@shipfox/client-router`: register the `/workspaces/$wid/settings/secrets` and
53
+ `/variables` routes.
54
+
55
+ ### Patch Changes
56
+
57
+ - Updated dependencies [43d7996]
58
+ - Updated dependencies [14e0bea]
59
+ - Updated dependencies [3b45d86]
60
+ - Updated dependencies [9018f0b]
61
+ - Updated dependencies [7fdfd72]
62
+ - Updated dependencies [2a3193f]
63
+ - Updated dependencies [f104ff2]
64
+ - Updated dependencies [7341569]
65
+ - Updated dependencies [e4c6abf]
66
+ - Updated dependencies [5d0676a]
67
+ - Updated dependencies [a35c2dc]
68
+ - Updated dependencies [58f7aef]
69
+ - Updated dependencies [5264a22]
70
+ - Updated dependencies [9674879]
71
+ - Updated dependencies [225c9a5]
72
+ - Updated dependencies [24f131b]
73
+ - Updated dependencies [bb2a7bc]
74
+ - Updated dependencies [f66f606]
75
+ - Updated dependencies [5eb06d0]
76
+ - Updated dependencies [4e13e5f]
77
+ - Updated dependencies [e92150d]
78
+ - Updated dependencies [8037501]
79
+ - Updated dependencies [0fb6018]
80
+ - Updated dependencies [c27a1ed]
81
+ - Updated dependencies [e51d464]
82
+ - Updated dependencies [b8e49ff]
83
+ - Updated dependencies [8037501]
84
+ - Updated dependencies [6c0da64]
85
+ - Updated dependencies [07f8ff8]
86
+ - Updated dependencies [e457582]
87
+ - Updated dependencies [8b5c905]
88
+ - Updated dependencies [f849131]
89
+ - Updated dependencies [94bdcc5]
90
+ - Updated dependencies [a34c8ea]
91
+ - Updated dependencies [27770eb]
92
+ - Updated dependencies [8ac4bf4]
93
+ - Updated dependencies [3a0be6b]
94
+ - Updated dependencies [3ddde91]
95
+ - Updated dependencies [d42baf4]
96
+ - Updated dependencies [8037501]
97
+ - Updated dependencies [54bb8a3]
98
+ - Updated dependencies [f711e18]
99
+ - @shipfox/react-ui@0.3.0
100
+ - @shipfox/api-secrets-dto@0.1.0
101
+ - @shipfox/client-api@0.0.1
102
+ - @shipfox/client-ui@0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shipfox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copies a bare key name (e.g. `MY_TOKEN`) to the clipboard. Guarded because
3
+ * `navigator.clipboard` is undefined in insecure contexts and older browsers —
4
+ * a missing API or a rejected write surfaces a toast instead of an unhandled
5
+ * rejection.
6
+ */
7
+ export declare function copyKeyName(key: string): Promise<void>;
8
+ //# sourceMappingURL=copy-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-key.d.ts","sourceRoot":"","sources":["../../src/components/copy-key.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5D"}
@@ -0,0 +1,21 @@
1
+ import { toast } from '@shipfox/react-ui/toast';
2
+ /**
3
+ * Copies a bare key name (e.g. `MY_TOKEN`) to the clipboard. Guarded because
4
+ * `navigator.clipboard` is undefined in insecure contexts and older browsers —
5
+ * a missing API or a rejected write surfaces a toast instead of an unhandled
6
+ * rejection.
7
+ */ export async function copyKeyName(key) {
8
+ const clipboard = typeof navigator === 'undefined' ? undefined : navigator.clipboard;
9
+ if (!clipboard?.writeText) {
10
+ toast.error('Clipboard is not available in this browser.');
11
+ return;
12
+ }
13
+ try {
14
+ await clipboard.writeText(key);
15
+ toast.success(`Copied ${key}`);
16
+ } catch {
17
+ toast.error('Could not copy to the clipboard.');
18
+ }
19
+ }
20
+
21
+ //# sourceMappingURL=copy-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/copy-key.ts"],"sourcesContent":["import {toast} from '@shipfox/react-ui/toast';\n\n/**\n * Copies a bare key name (e.g. `MY_TOKEN`) to the clipboard. Guarded because\n * `navigator.clipboard` is undefined in insecure contexts and older browsers —\n * a missing API or a rejected write surfaces a toast instead of an unhandled\n * rejection.\n */\nexport async function copyKeyName(key: string): Promise<void> {\n const clipboard = typeof navigator === 'undefined' ? undefined : navigator.clipboard;\n if (!clipboard?.writeText) {\n toast.error('Clipboard is not available in this browser.');\n return;\n }\n try {\n await clipboard.writeText(key);\n toast.success(`Copied ${key}`);\n } catch {\n toast.error('Could not copy to the clipboard.');\n }\n}\n"],"names":["toast","copyKeyName","key","clipboard","navigator","undefined","writeText","error","success"],"mappings":"AAAA,SAAQA,KAAK,QAAO,0BAA0B;AAE9C;;;;;CAKC,GACD,OAAO,eAAeC,YAAYC,GAAW;IAC3C,MAAMC,YAAY,OAAOC,cAAc,cAAcC,YAAYD,UAAUD,SAAS;IACpF,IAAI,CAACA,WAAWG,WAAW;QACzBN,MAAMO,KAAK,CAAC;QACZ;IACF;IACA,IAAI;QACF,MAAMJ,UAAUG,SAAS,CAACJ;QAC1BF,MAAMQ,OAAO,CAAC,CAAC,OAAO,EAAEN,KAAK;IAC/B,EAAE,OAAM;QACNF,MAAMO,KAAK,CAAC;IACd;AACF"}
@@ -0,0 +1,9 @@
1
+ export declare function DeleteEntryDialog({ open, onOpenChange, entryKey, isLoading, errorMessage, onConfirm, }: {
2
+ open: boolean;
3
+ onOpenChange: (next: boolean) => void;
4
+ entryKey: string;
5
+ isLoading: boolean;
6
+ errorMessage?: string | undefined;
7
+ onConfirm: () => void;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=delete-entry-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-entry-dialog.d.ts","sourceRoot":"","sources":["../../src/components/delete-entry-dialog.tsx"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,+BA8BA"}
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@shipfox/react-ui/button';
3
+ import { Callout } from '@shipfox/react-ui/callout';
4
+ import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
5
+ import { Text } from '@shipfox/react-ui/typography';
6
+ export function DeleteEntryDialog({ open, onOpenChange, entryKey, isLoading, errorMessage, onConfirm }) {
7
+ return /*#__PURE__*/ _jsx(Modal, {
8
+ open: open,
9
+ onOpenChange: onOpenChange,
10
+ children: /*#__PURE__*/ _jsxs(ModalContent, {
11
+ children: [
12
+ /*#__PURE__*/ _jsx(ModalHeader, {
13
+ children: /*#__PURE__*/ _jsxs(ModalTitle, {
14
+ children: [
15
+ "Delete ",
16
+ /*#__PURE__*/ _jsx("strong", {
17
+ children: entryKey
18
+ }),
19
+ "?"
20
+ ]
21
+ })
22
+ }),
23
+ /*#__PURE__*/ _jsxs(ModalBody, {
24
+ className: "gap-16",
25
+ children: [
26
+ /*#__PURE__*/ _jsxs(Text, {
27
+ size: "sm",
28
+ children: [
29
+ "Workflows that reference ",
30
+ /*#__PURE__*/ _jsx("strong", {
31
+ children: entryKey
32
+ }),
33
+ " will fail at their next run."
34
+ ]
35
+ }),
36
+ errorMessage ? /*#__PURE__*/ _jsx(Callout, {
37
+ role: "alert",
38
+ type: "error",
39
+ children: /*#__PURE__*/ _jsx(Text, {
40
+ size: "sm",
41
+ children: errorMessage
42
+ })
43
+ }) : null
44
+ ]
45
+ }),
46
+ /*#__PURE__*/ _jsxs(ModalFooter, {
47
+ children: [
48
+ /*#__PURE__*/ _jsx(Button, {
49
+ variant: "secondary",
50
+ onClick: ()=>onOpenChange(false),
51
+ children: "Cancel"
52
+ }),
53
+ /*#__PURE__*/ _jsx(Button, {
54
+ variant: "danger",
55
+ isLoading: isLoading,
56
+ onClick: onConfirm,
57
+ children: "Delete"
58
+ })
59
+ ]
60
+ })
61
+ ]
62
+ })
63
+ });
64
+ }
65
+
66
+ //# sourceMappingURL=delete-entry-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/delete-entry-dialog.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {Text} from '@shipfox/react-ui/typography';\n\nexport function DeleteEntryDialog({\n open,\n onOpenChange,\n entryKey,\n isLoading,\n errorMessage,\n onConfirm,\n}: {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n entryKey: string;\n isLoading: boolean;\n errorMessage?: string | undefined;\n onConfirm: () => void;\n}) {\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent>\n <ModalHeader>\n <ModalTitle>\n Delete <strong>{entryKey}</strong>?\n </ModalTitle>\n </ModalHeader>\n <ModalBody className=\"gap-16\">\n <Text size=\"sm\">\n Workflows that reference <strong>{entryKey}</strong> will fail at their next run.\n </Text>\n {errorMessage ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{errorMessage}</Text>\n </Callout>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button variant=\"danger\" isLoading={isLoading} onClick={onConfirm}>\n Delete\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n );\n}\n"],"names":["Button","Callout","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Text","DeleteEntryDialog","open","onOpenChange","entryKey","isLoading","errorMessage","onConfirm","strong","className","size","role","type","variant","onClick"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SACEC,KAAK,EACLC,SAAS,EACTC,YAAY,EACZC,WAAW,EACXC,WAAW,EACXC,UAAU,QACL,0BAA0B;AACjC,SAAQC,IAAI,QAAO,+BAA+B;AAElD,OAAO,SAASC,kBAAkB,EAChCC,IAAI,EACJC,YAAY,EACZC,QAAQ,EACRC,SAAS,EACTC,YAAY,EACZC,SAAS,EAQV;IACC,qBACE,KAACb;QAAMQ,MAAMA;QAAMC,cAAcA;kBAC/B,cAAA,MAACP;;8BACC,KAACE;8BACC,cAAA,MAACC;;4BAAW;0CACH,KAACS;0CAAQJ;;4BAAkB;;;;8BAGtC,MAACT;oBAAUc,WAAU;;sCACnB,MAACT;4BAAKU,MAAK;;gCAAK;8CACW,KAACF;8CAAQJ;;gCAAkB;;;wBAErDE,6BACC,KAACb;4BAAQkB,MAAK;4BAAQC,MAAK;sCACzB,cAAA,KAACZ;gCAAKU,MAAK;0CAAMJ;;6BAEjB;;;8BAEN,MAACT;;sCACC,KAACL;4BAAOqB,SAAQ;4BAAYC,SAAS,IAAMX,aAAa;sCAAQ;;sCAGhE,KAACX;4BAAOqB,SAAQ;4BAASR,WAAWA;4BAAWS,SAASP;sCAAW;;;;;;;AAO7E"}
@@ -0,0 +1,17 @@
1
+ export type SecretsFormField = 'key';
2
+ /**
3
+ * Classifies a management error into either a field-level message (routed to
4
+ * the `key` field via `errorMap.onServer`) or a form-level message (rendered
5
+ * in an `<Callout role="alert"`). Shared by the secret and variable forms because both hit
6
+ * the same management routes / `ClientError` codes.
7
+ */
8
+ export type SecretsFormErrorMapping = {
9
+ kind: 'field';
10
+ field: SecretsFormField;
11
+ message: string;
12
+ } | {
13
+ kind: 'form';
14
+ message: string;
15
+ };
16
+ export declare function secretsErrorToFormError(error: unknown): SecretsFormErrorMapping;
17
+ //# sourceMappingURL=form-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-errors.d.ts","sourceRoot":"","sources":["../../src/components/form-errors.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAErC;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAC/B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GACzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAC;AAEpC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CA4C/E"}
@@ -0,0 +1,81 @@
1
+ import { ApiError } from '@shipfox/client-api';
2
+ export function secretsErrorToFormError(error) {
3
+ if (error instanceof ApiError) {
4
+ switch(error.code){
5
+ case 'invalid-key':
6
+ return {
7
+ kind: 'field',
8
+ field: 'key',
9
+ message: 'Invalid name. Use uppercase letters, digits and underscores; start with a letter or underscore.'
10
+ };
11
+ case 'duplicate-key':
12
+ return {
13
+ kind: 'field',
14
+ field: 'key',
15
+ message: 'A key with this name already exists.'
16
+ };
17
+ case 'invalid-namespace':
18
+ return {
19
+ kind: 'form',
20
+ message: 'Invalid namespace.'
21
+ };
22
+ case 'value-too-large':
23
+ return {
24
+ kind: 'form',
25
+ message: 'This value is too large to store.'
26
+ };
27
+ case 'workspace-secret-cap-exceeded':
28
+ return {
29
+ kind: 'form',
30
+ message: 'This workspace has reached its secrets and variables limit.'
31
+ };
32
+ case 'batch-scope-mismatch':
33
+ return {
34
+ kind: 'form',
35
+ message: 'All entries in a batch must share the same scope.'
36
+ };
37
+ case 'secret-not-found':
38
+ return {
39
+ kind: 'form',
40
+ message: 'This secret no longer exists.'
41
+ };
42
+ case 'variable-not-found':
43
+ return {
44
+ kind: 'form',
45
+ message: 'This variable no longer exists.'
46
+ };
47
+ case 'unknown-secret-store':
48
+ return {
49
+ kind: 'form',
50
+ message: 'Unknown secret store.'
51
+ };
52
+ case 'project-not-found':
53
+ return {
54
+ kind: 'form',
55
+ message: 'This project no longer exists.'
56
+ };
57
+ case 'forbidden':
58
+ return {
59
+ kind: 'form',
60
+ message: 'You need the workspace admin role to manage secrets and variables.'
61
+ };
62
+ default:
63
+ return {
64
+ kind: 'form',
65
+ message: error.message
66
+ };
67
+ }
68
+ }
69
+ if (error instanceof Error) {
70
+ return {
71
+ kind: 'form',
72
+ message: error.message
73
+ };
74
+ }
75
+ return {
76
+ kind: 'form',
77
+ message: 'Something went wrong. Try again.'
78
+ };
79
+ }
80
+
81
+ //# sourceMappingURL=form-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/form-errors.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\n\nexport type SecretsFormField = 'key';\n\n/**\n * Classifies a management error into either a field-level message (routed to\n * the `key` field via `errorMap.onServer`) or a form-level message (rendered\n * in an `<Callout role=\"alert\"`). Shared by the secret and variable forms because both hit\n * the same management routes / `ClientError` codes.\n */\nexport type SecretsFormErrorMapping =\n | {kind: 'field'; field: SecretsFormField; message: string}\n | {kind: 'form'; message: string};\n\nexport function secretsErrorToFormError(error: unknown): SecretsFormErrorMapping {\n if (error instanceof ApiError) {\n switch (error.code) {\n case 'invalid-key':\n return {\n kind: 'field',\n field: 'key',\n message:\n 'Invalid name. Use uppercase letters, digits and underscores; start with a letter or underscore.',\n };\n case 'duplicate-key':\n return {kind: 'field', field: 'key', message: 'A key with this name already exists.'};\n case 'invalid-namespace':\n return {kind: 'form', message: 'Invalid namespace.'};\n case 'value-too-large':\n return {kind: 'form', message: 'This value is too large to store.'};\n case 'workspace-secret-cap-exceeded':\n return {\n kind: 'form',\n message: 'This workspace has reached its secrets and variables limit.',\n };\n case 'batch-scope-mismatch':\n return {kind: 'form', message: 'All entries in a batch must share the same scope.'};\n case 'secret-not-found':\n return {kind: 'form', message: 'This secret no longer exists.'};\n case 'variable-not-found':\n return {kind: 'form', message: 'This variable no longer exists.'};\n case 'unknown-secret-store':\n return {kind: 'form', message: 'Unknown secret store.'};\n case 'project-not-found':\n return {kind: 'form', message: 'This project no longer exists.'};\n case 'forbidden':\n return {\n kind: 'form',\n message: 'You need the workspace admin role to manage secrets and variables.',\n };\n default:\n return {kind: 'form', message: error.message};\n }\n }\n if (error instanceof Error) {\n return {kind: 'form', message: error.message};\n }\n return {kind: 'form', message: 'Something went wrong. Try again.'};\n}\n"],"names":["ApiError","secretsErrorToFormError","error","code","kind","field","message","Error"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAc7C,OAAO,SAASC,wBAAwBC,KAAc;IACpD,IAAIA,iBAAiBF,UAAU;QAC7B,OAAQE,MAAMC,IAAI;YAChB,KAAK;gBACH,OAAO;oBACLC,MAAM;oBACNC,OAAO;oBACPC,SACE;gBACJ;YACF,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAASC,OAAO;oBAAOC,SAAS;gBAAsC;YACtF,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAoB;YACrD,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAmC;YACpE,KAAK;gBACH,OAAO;oBACLF,MAAM;oBACNE,SAAS;gBACX;YACF,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAmD;YACpF,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAA+B;YAChE,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAiC;YAClE,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAuB;YACxD,KAAK;gBACH,OAAO;oBAACF,MAAM;oBAAQE,SAAS;gBAAgC;YACjE,KAAK;gBACH,OAAO;oBACLF,MAAM;oBACNE,SAAS;gBACX;YACF;gBACE,OAAO;oBAACF,MAAM;oBAAQE,SAASJ,MAAMI,OAAO;gBAAA;QAChD;IACF;IACA,IAAIJ,iBAAiBK,OAAO;QAC1B,OAAO;YAACH,MAAM;YAAQE,SAASJ,MAAMI,OAAO;QAAA;IAC9C;IACA,OAAO;QAACF,MAAM;QAAQE,SAAS;IAAkC;AACnE"}
@@ -0,0 +1,14 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Modal-agnostic body/footer layout matching `ModalBody`/`ModalFooter` so the
4
+ * forms render the same inside a `ModalContent` and standalone (stories, RTL
5
+ * tests) without a Modal context.
6
+ */
7
+ export declare function FormBody({ children, className }: {
8
+ children: ReactNode;
9
+ className?: string;
10
+ }): import("react").JSX.Element;
11
+ export declare function FormFooter({ children }: {
12
+ children: ReactNode;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=form-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-shell.d.ts","sourceRoot":"","sources":["../../src/components/form-shell.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAC,QAAQ,EAAE,SAAS,EAAC,EAAE;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAC,+BAWxF;AAED,wBAAgB,UAAU,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAC,+BAS3D"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '@shipfox/react-ui/utils';
3
+ /**
4
+ * Modal-agnostic body/footer layout matching `ModalBody`/`ModalFooter` so the
5
+ * forms render the same inside a `ModalContent` and standalone (stories, RTL
6
+ * tests) without a Modal context.
7
+ */ export function FormBody({ children, className }) {
8
+ return /*#__PURE__*/ _jsx("div", {
9
+ className: cn('flex w-full flex-col items-start gap-16 bg-background-neutral-base px-24 pt-16 pb-24', className),
10
+ children: children
11
+ });
12
+ }
13
+ export function FormFooter({ children }) {
14
+ return /*#__PURE__*/ _jsxs("div", {
15
+ className: "flex w-full shrink-0 flex-col",
16
+ children: [
17
+ /*#__PURE__*/ _jsx("div", {
18
+ className: "h-[1px] w-full bg-border-neutral-strong"
19
+ }),
20
+ /*#__PURE__*/ _jsx("div", {
21
+ className: "flex w-full items-center justify-end gap-16 bg-background-neutral-base px-24 py-16",
22
+ children: children
23
+ })
24
+ ]
25
+ });
26
+ }
27
+
28
+ //# sourceMappingURL=form-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/form-shell.tsx"],"sourcesContent":["import {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\n\n/**\n * Modal-agnostic body/footer layout matching `ModalBody`/`ModalFooter` so the\n * forms render the same inside a `ModalContent` and standalone (stories, RTL\n * tests) without a Modal context.\n */\nexport function FormBody({children, className}: {children: ReactNode; className?: string}) {\n return (\n <div\n className={cn(\n 'flex w-full flex-col items-start gap-16 bg-background-neutral-base px-24 pt-16 pb-24',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nexport function FormFooter({children}: {children: ReactNode}) {\n return (\n <div className=\"flex w-full shrink-0 flex-col\">\n <div className=\"h-[1px] w-full bg-border-neutral-strong\" />\n <div className=\"flex w-full items-center justify-end gap-16 bg-background-neutral-base px-24 py-16\">\n {children}\n </div>\n </div>\n );\n}\n"],"names":["cn","FormBody","children","className","div","FormFooter"],"mappings":";AAAA,SAAQA,EAAE,QAAO,0BAA0B;AAG3C;;;;CAIC,GACD,OAAO,SAASC,SAAS,EAACC,QAAQ,EAAEC,SAAS,EAA4C;IACvF,qBACE,KAACC;QACCD,WAAWH,GACT,wFACAG;kBAGDD;;AAGP;AAEA,OAAO,SAASG,WAAW,EAACH,QAAQ,EAAwB;IAC1D,qBACE,MAACE;QAAID,WAAU;;0BACb,KAACC;gBAAID,WAAU;;0BACf,KAACC;gBAAID,WAAU;0BACZD;;;;AAIT"}
@@ -0,0 +1,10 @@
1
+ export declare const SECRET_FORM_ID = "secret-form";
2
+ export declare function SecretForm({ workspaceId, mode, existingKey, reservedKeys, onSaved, onCancel, }: {
3
+ workspaceId: string;
4
+ mode: 'create' | 'edit';
5
+ existingKey?: string | undefined;
6
+ reservedKeys?: readonly string[] | undefined;
7
+ onSaved: () => void;
8
+ onCancel: () => void;
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=secret-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-form.d.ts","sourceRoot":"","sources":["../../src/components/secret-form.tsx"],"names":[],"mappings":"AAiBA,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAM5C,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,IAAI,EACJ,WAAW,EACX,YAAiB,EACjB,OAAO,EACP,QAAQ,GACT,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,+BAoIA"}