@shipfox/client-secrets 5.0.0 → 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.
Files changed (72) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +49 -0
  3. package/dist/components/form-errors.d.ts.map +1 -1
  4. package/dist/components/form-errors.js +0 -6
  5. package/dist/components/form-errors.js.map +1 -1
  6. package/dist/components/secret-form.d.ts.map +1 -1
  7. package/dist/components/secret-form.js +6 -8
  8. package/dist/components/secret-form.js.map +1 -1
  9. package/dist/components/variable-form.d.ts.map +1 -1
  10. package/dist/components/variable-form.js +6 -8
  11. package/dist/components/variable-form.js.map +1 -1
  12. package/dist/components/workspace-secrets-section.d.ts.map +1 -1
  13. package/dist/components/workspace-secrets-section.js +4 -2
  14. package/dist/components/workspace-secrets-section.js.map +1 -1
  15. package/dist/components/workspace-secrets-section.stories.d.ts.map +1 -1
  16. package/dist/components/workspace-secrets-section.stories.js.map +1 -1
  17. package/dist/components/workspace-variables-section.d.ts.map +1 -1
  18. package/dist/components/workspace-variables-section.js +5 -3
  19. package/dist/components/workspace-variables-section.js.map +1 -1
  20. package/dist/components/workspace-variables-section.stories.d.ts.map +1 -1
  21. package/dist/components/workspace-variables-section.stories.js.map +1 -1
  22. package/dist/core/store.d.ts +54 -0
  23. package/dist/core/store.d.ts.map +1 -0
  24. package/dist/core/store.js +44 -0
  25. package/dist/core/store.js.map +1 -0
  26. package/dist/feature.d.ts +13 -0
  27. package/dist/feature.d.ts.map +1 -1
  28. package/dist/feature.js +17 -16
  29. package/dist/feature.js.map +1 -1
  30. package/dist/hooks/api/create-store-api.d.ts +31 -49
  31. package/dist/hooks/api/create-store-api.d.ts.map +1 -1
  32. package/dist/hooks/api/create-store-api.js +44 -45
  33. package/dist/hooks/api/create-store-api.js.map +1 -1
  34. package/dist/hooks/api/secrets.d.ts +16 -55
  35. package/dist/hooks/api/secrets.d.ts.map +1 -1
  36. package/dist/hooks/api/secrets.js +10 -2
  37. package/dist/hooks/api/secrets.js.map +1 -1
  38. package/dist/hooks/api/store-mapper.d.ts +7 -0
  39. package/dist/hooks/api/store-mapper.d.ts.map +1 -0
  40. package/dist/hooks/api/store-mapper.js +36 -0
  41. package/dist/hooks/api/store-mapper.js.map +1 -0
  42. package/dist/hooks/api/variables.d.ts +25 -78
  43. package/dist/hooks/api/variables.d.ts.map +1 -1
  44. package/dist/hooks/api/variables.js +37 -28
  45. package/dist/hooks/api/variables.js.map +1 -1
  46. package/dist/tsconfig.test.tsbuildinfo +1 -1
  47. package/package.json +9 -10
  48. package/src/components/form-errors.test.ts +2 -2
  49. package/src/components/form-errors.ts +0 -3
  50. package/src/components/secret-form.tsx +15 -5
  51. package/src/components/variable-form.tsx +15 -5
  52. package/src/components/workspace-secrets-section.stories.tsx +1 -2
  53. package/src/components/workspace-secrets-section.tsx +8 -4
  54. package/src/components/workspace-variables-section.stories.tsx +1 -2
  55. package/src/components/workspace-variables-section.tsx +10 -6
  56. package/src/core/store.test.ts +38 -0
  57. package/src/core/store.ts +91 -0
  58. package/src/feature.ts +13 -11
  59. package/src/hooks/api/create-store-api.ts +104 -75
  60. package/src/hooks/api/secrets.test.ts +16 -10
  61. package/src/hooks/api/secrets.ts +11 -14
  62. package/src/hooks/api/store-mapper.test.ts +39 -0
  63. package/src/hooks/api/store-mapper.ts +47 -0
  64. package/src/hooks/api/variables.ts +52 -36
  65. package/tsconfig.build.tsbuildinfo +1 -1
  66. package/vercel.json +5 -0
  67. package/dist/components/store-key.d.ts +0 -20
  68. package/dist/components/store-key.d.ts.map +0 -1
  69. package/dist/components/store-key.js +0 -25
  70. package/dist/components/store-key.js.map +0 -1
  71. package/src/components/store-key.test.ts +0 -53
  72. package/src/components/store-key.ts +0 -36
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 21 files with swc (333.72ms)
2
+ Successfully compiled: 22 files with swc (404.18ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # @shipfox/client-secrets
2
2
 
3
+ ## 6.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4b85404]
8
+ - Updated dependencies [102c5f4]
9
+ - @shipfox/api-secrets-dto@9.0.2
10
+ - @shipfox/react-ui@0.3.7
11
+ - @shipfox/client-ui@6.0.2
12
+ - @shipfox/client-shell@6.0.2
13
+
14
+ ## 6.0.1
15
+
16
+ ### Patch Changes
17
+
18
+ - 475ce59: Republishes all public packages after restoring release authorization.
19
+ - Updated dependencies [8436596]
20
+ - Updated dependencies [475ce59]
21
+ - Updated dependencies [3f8f1cb]
22
+ - @shipfox/api-secrets-dto@9.0.1
23
+ - @shipfox/client-api@6.0.1
24
+ - @shipfox/client-shell@6.0.1
25
+ - @shipfox/client-ui@6.0.1
26
+ - @shipfox/react-ui@0.3.6
27
+
28
+ ## 6.0.0
29
+
30
+ ### Minor Changes
31
+
32
+ - 401b583: Exposes typed feature-owned navigation and settings contributions and enforces coordinator-owned client composition.
33
+
34
+ ### Patch Changes
35
+
36
+ - d784a07: Enforces checked client API responses and removes stale transport compatibility helpers.
37
+ - 83f2710: Converges the secrets and variables client store on a shared domain model and Zod-validated transport, with no behavior change.
38
+ - Updated dependencies [401b583]
39
+ - Updated dependencies [82eda45]
40
+ - Updated dependencies [cd90c19]
41
+ - Updated dependencies [24be269]
42
+ - Updated dependencies [c56c124]
43
+ - Updated dependencies [02974d6]
44
+ - Updated dependencies [4a6d124]
45
+ - Updated dependencies [c02ac42]
46
+ - @shipfox/client-shell@6.0.0
47
+ - @shipfox/client-api@6.0.0
48
+ - @shipfox/api-secrets-dto@9.0.0
49
+ - @shipfox/client-ui@6.0.0
50
+ - @shipfox/react-ui@0.3.5
51
+
3
52
  ## 5.0.0
4
53
 
5
54
  ### Patch Changes
@@ -1 +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"}
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,CAyC/E"}
@@ -66,12 +66,6 @@ export function secretsErrorToFormError(error) {
66
66
  };
67
67
  }
68
68
  }
69
- if (error instanceof Error) {
70
- return {
71
- kind: 'form',
72
- message: error.message
73
- };
74
- }
75
69
  return {
76
70
  kind: 'form',
77
71
  message: 'Something went wrong. Try again.'
@@ -1 +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"}
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 return {kind: 'form', message: 'Something went wrong. Try again.'};\n}\n"],"names":["ApiError","secretsErrorToFormError","error","code","kind","field","message"],"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,OAAO;QAACF,MAAM;QAAQE,SAAS;IAAkC;AACnE"}
@@ -1 +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"}
1
+ {"version":3,"file":"secret-form.d.ts","sourceRoot":"","sources":["../../src/components/secret-form.tsx"],"names":[],"mappings":"AAsBA,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,+BAyIA"}
@@ -1,15 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { isShortSecretValue } from '@shipfox/api-secrets-dto';
3
2
  import { Button, IconButton } from '@shipfox/react-ui/button';
4
3
  import { Callout } from '@shipfox/react-ui/callout';
5
4
  import { FormField, FormFieldInput, FormFieldTextarea, fieldError } from '@shipfox/react-ui/form-field';
6
5
  import { Text } from '@shipfox/react-ui/typography';
7
6
  import { useForm } from '@tanstack/react-form';
8
7
  import { useState } from 'react';
8
+ import { normalizeStoreKey, STORE_KEY_HELP, shouldWarnShortSecretValue, validateNewStoreKey, workspaceStoreScope } from '#core/store.js';
9
9
  import { usePutSecretMutation } from '#hooks/api/secrets.js';
10
10
  import { secretsErrorToFormError } from './form-errors.js';
11
11
  import { FormBody, FormFooter } from './form-shell.js';
12
- import { STORE_KEY_HELP, validateNewStoreKey } from './store-key.js';
13
12
  export const SECRET_FORM_ID = 'secret-form';
14
13
  // Client-side mirror of the server default SECRETS_SHORT_VALUE_WARN_LENGTH. The
15
14
  // advisory is best-effort UX, so drift from a self-hosted override is cosmetic.
@@ -28,10 +27,9 @@ export function SecretForm({ workspaceId, mode, existingKey, reservedKeys = [],
28
27
  try {
29
28
  await putSecret.mutateAsync({
30
29
  workspaceId,
31
- key: value.key,
32
- body: {
33
- value: value.value
34
- }
30
+ key: normalizeStoreKey(value.key),
31
+ value: value.value,
32
+ scope: workspaceStoreScope
35
33
  });
36
34
  onSaved();
37
35
  } catch (error) {
@@ -90,7 +88,7 @@ export function SecretForm({ workspaceId, mode, existingKey, reservedKeys = [],
90
88
  disabled: mode === 'edit',
91
89
  placeholder: "MY_TOKEN",
92
90
  value: field.state.value,
93
- onChange: (event)=>field.handleChange(event.target.value.toUpperCase()),
91
+ onChange: (event)=>field.handleChange(normalizeStoreKey(event.target.value)),
94
92
  onBlur: field.handleBlur
95
93
  })
96
94
  })
@@ -130,7 +128,7 @@ export function SecretForm({ workspaceId, mode, existingKey, reservedKeys = [],
130
128
  })
131
129
  ]
132
130
  }),
133
- isShortSecretValue(field.state.value, SHORT_VALUE_THRESHOLD) ? /*#__PURE__*/ _jsx(Callout, {
131
+ shouldWarnShortSecretValue(field.state.value, SHORT_VALUE_THRESHOLD) ? /*#__PURE__*/ _jsx(Callout, {
134
132
  role: "alert",
135
133
  type: "warning",
136
134
  children: /*#__PURE__*/ _jsx(Text, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/secret-form.tsx"],"sourcesContent":["import {isShortSecretValue} from '@shipfox/api-secrets-dto';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n FormField,\n FormFieldInput,\n FormFieldTextarea,\n fieldError,\n} from '@shipfox/react-ui/form-field';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useState} from 'react';\nimport {usePutSecretMutation} from '#hooks/api/secrets.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {FormBody, FormFooter} from './form-shell.js';\nimport {STORE_KEY_HELP, validateNewStoreKey} from './store-key.js';\n\nexport const SECRET_FORM_ID = 'secret-form';\n\n// Client-side mirror of the server default SECRETS_SHORT_VALUE_WARN_LENGTH. The\n// advisory is best-effort UX, so drift from a self-hosted override is cosmetic.\nconst SHORT_VALUE_THRESHOLD = 12;\n\nexport function SecretForm({\n workspaceId,\n mode,\n existingKey,\n reservedKeys = [],\n onSaved,\n onCancel,\n}: {\n workspaceId: string;\n mode: 'create' | 'edit';\n existingKey?: string | undefined;\n reservedKeys?: readonly string[] | undefined;\n onSaved: () => void;\n onCancel: () => void;\n}) {\n const putSecret = usePutSecretMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [showValue, setShowValue] = useState(false);\n\n const form = useForm({\n defaultValues: {key: existingKey ?? '', value: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await putSecret.mutateAsync({workspaceId, key: value.key, body: {value: value.value}});\n onSaved();\n } catch (error) {\n const mapped = secretsErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n return (\n <>\n <FormBody>\n <form\n id={SECRET_FORM_ID}\n className=\"flex w-full flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"key\"\n validators={{\n onBlur: ({value}) => validateNewStoreKey(value, {mode, reservedKeys, kind: 'secret'}),\n onSubmit: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'secret'}),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"secret-key\"\n description={STORE_KEY_HELP}\n error={fieldError(field)}\n >\n <FormFieldInput\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n disabled={mode === 'edit'}\n placeholder=\"MY_TOKEN\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value.toUpperCase())}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n\n <form.Field\n name=\"value\"\n validators={{\n onBlur: ({value}) => (value.length > 0 ? undefined : 'A value is required.'),\n onSubmit: ({value}) => (value.length > 0 ? undefined : 'A value is required.'),\n }}\n >\n {(field) => (\n <FormField label=\"Value\" id=\"secret-value\" error={fieldError(field)}>\n <div className=\"relative\">\n <FormFieldTextarea\n className={\n showValue ? 'pr-32 font-code' : 'pr-32 font-code [-webkit-text-security:disc]'\n }\n autoComplete=\"off\"\n spellCheck={false}\n rows={3}\n placeholder={\n mode === 'edit'\n ? 'Enter a new value to replace the current one'\n : 'Paste the secret value'\n }\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"xs\"\n className=\"absolute top-6 right-6\"\n icon={showValue ? 'eyeOffLine' : 'eyeLine'}\n aria-label={showValue ? 'Hide value' : 'Show value'}\n onClick={() => setShowValue((prev) => !prev)}\n />\n </div>\n {isShortSecretValue(field.state.value, SHORT_VALUE_THRESHOLD) ? (\n <Callout role=\"alert\" type=\"warning\">\n <Text size=\"sm\">\n Very short secrets can match ordinary log text and redact unrelated output. If\n this value is not sensitive, store it as a Variable instead.\n </Text>\n </Callout>\n ) : null}\n </FormField>\n )}\n </form.Field>\n </form>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{formError}</Text>\n </Callout>\n ) : null}\n </FormBody>\n <FormFooter>\n <Button variant=\"secondary\" type=\"button\" onClick={onCancel}>\n Cancel\n </Button>\n <Button type=\"submit\" form={SECRET_FORM_ID} isLoading={putSecret.isPending}>\n {mode === 'edit' ? 'Update secret' : 'Create secret'}\n </Button>\n </FormFooter>\n </>\n );\n}\n"],"names":["isShortSecretValue","Button","IconButton","Callout","FormField","FormFieldInput","FormFieldTextarea","fieldError","Text","useForm","useState","usePutSecretMutation","secretsErrorToFormError","FormBody","FormFooter","STORE_KEY_HELP","validateNewStoreKey","SECRET_FORM_ID","SHORT_VALUE_THRESHOLD","SecretForm","workspaceId","mode","existingKey","reservedKeys","onSaved","onCancel","putSecret","formError","setFormError","showValue","setShowValue","form","defaultValues","key","value","onSubmit","undefined","mutateAsync","body","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","id","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","label","description","autoComplete","spellCheck","disabled","placeholder","state","onChange","handleChange","target","toUpperCase","handleBlur","length","div","rows","type","variant","size","icon","aria-label","onClick","role","isLoading","isPending"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/secret-form.tsx"],"sourcesContent":["import {Button, IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n FormField,\n FormFieldInput,\n FormFieldTextarea,\n fieldError,\n} from '@shipfox/react-ui/form-field';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useState} from 'react';\nimport {\n normalizeStoreKey,\n STORE_KEY_HELP,\n shouldWarnShortSecretValue,\n validateNewStoreKey,\n workspaceStoreScope,\n} from '#core/store.js';\nimport {usePutSecretMutation} from '#hooks/api/secrets.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {FormBody, FormFooter} from './form-shell.js';\n\nexport const SECRET_FORM_ID = 'secret-form';\n\n// Client-side mirror of the server default SECRETS_SHORT_VALUE_WARN_LENGTH. The\n// advisory is best-effort UX, so drift from a self-hosted override is cosmetic.\nconst SHORT_VALUE_THRESHOLD = 12;\n\nexport function SecretForm({\n workspaceId,\n mode,\n existingKey,\n reservedKeys = [],\n onSaved,\n onCancel,\n}: {\n workspaceId: string;\n mode: 'create' | 'edit';\n existingKey?: string | undefined;\n reservedKeys?: readonly string[] | undefined;\n onSaved: () => void;\n onCancel: () => void;\n}) {\n const putSecret = usePutSecretMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [showValue, setShowValue] = useState(false);\n\n const form = useForm({\n defaultValues: {key: existingKey ?? '', value: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await putSecret.mutateAsync({\n workspaceId,\n key: normalizeStoreKey(value.key),\n value: value.value,\n scope: workspaceStoreScope,\n });\n onSaved();\n } catch (error) {\n const mapped = secretsErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n return (\n <>\n <FormBody>\n <form\n id={SECRET_FORM_ID}\n className=\"flex w-full flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"key\"\n validators={{\n onBlur: ({value}) => validateNewStoreKey(value, {mode, reservedKeys, kind: 'secret'}),\n onSubmit: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'secret'}),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"secret-key\"\n description={STORE_KEY_HELP}\n error={fieldError(field)}\n >\n <FormFieldInput\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n disabled={mode === 'edit'}\n placeholder=\"MY_TOKEN\"\n value={field.state.value}\n onChange={(event) => field.handleChange(normalizeStoreKey(event.target.value))}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n\n <form.Field\n name=\"value\"\n validators={{\n onBlur: ({value}) => (value.length > 0 ? undefined : 'A value is required.'),\n onSubmit: ({value}) => (value.length > 0 ? undefined : 'A value is required.'),\n }}\n >\n {(field) => (\n <FormField label=\"Value\" id=\"secret-value\" error={fieldError(field)}>\n <div className=\"relative\">\n <FormFieldTextarea\n className={\n showValue ? 'pr-32 font-code' : 'pr-32 font-code [-webkit-text-security:disc]'\n }\n autoComplete=\"off\"\n spellCheck={false}\n rows={3}\n placeholder={\n mode === 'edit'\n ? 'Enter a new value to replace the current one'\n : 'Paste the secret value'\n }\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"xs\"\n className=\"absolute top-6 right-6\"\n icon={showValue ? 'eyeOffLine' : 'eyeLine'}\n aria-label={showValue ? 'Hide value' : 'Show value'}\n onClick={() => setShowValue((prev) => !prev)}\n />\n </div>\n {shouldWarnShortSecretValue(field.state.value, SHORT_VALUE_THRESHOLD) ? (\n <Callout role=\"alert\" type=\"warning\">\n <Text size=\"sm\">\n Very short secrets can match ordinary log text and redact unrelated output. If\n this value is not sensitive, store it as a Variable instead.\n </Text>\n </Callout>\n ) : null}\n </FormField>\n )}\n </form.Field>\n </form>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{formError}</Text>\n </Callout>\n ) : null}\n </FormBody>\n <FormFooter>\n <Button variant=\"secondary\" type=\"button\" onClick={onCancel}>\n Cancel\n </Button>\n <Button type=\"submit\" form={SECRET_FORM_ID} isLoading={putSecret.isPending}>\n {mode === 'edit' ? 'Update secret' : 'Create secret'}\n </Button>\n </FormFooter>\n </>\n );\n}\n"],"names":["Button","IconButton","Callout","FormField","FormFieldInput","FormFieldTextarea","fieldError","Text","useForm","useState","normalizeStoreKey","STORE_KEY_HELP","shouldWarnShortSecretValue","validateNewStoreKey","workspaceStoreScope","usePutSecretMutation","secretsErrorToFormError","FormBody","FormFooter","SECRET_FORM_ID","SHORT_VALUE_THRESHOLD","SecretForm","workspaceId","mode","existingKey","reservedKeys","onSaved","onCancel","putSecret","formError","setFormError","showValue","setShowValue","form","defaultValues","key","value","onSubmit","undefined","mutateAsync","scope","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","id","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","label","description","autoComplete","spellCheck","disabled","placeholder","state","onChange","handleChange","target","handleBlur","length","div","rows","type","variant","size","icon","aria-label","onClick","role","isLoading","isPending"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"variable-form.d.ts","sourceRoot":"","sources":["../../src/components/variable-form.tsx"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,sBAA8B,EAC9B,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,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,sBAAsB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,+BAgJA"}
1
+ {"version":3,"file":"variable-form.d.ts","sourceRoot":"","sources":["../../src/components/variable-form.tsx"],"names":[],"mappings":"AAsBA,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,sBAA8B,EAC9B,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,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,sBAAsB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,+BAqJA"}
@@ -1,15 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { isSensitiveSecretName } from '@shipfox/api-secrets-dto';
3
2
  import { Button } from '@shipfox/react-ui/button';
4
3
  import { Callout } from '@shipfox/react-ui/callout';
5
4
  import { FormField, FormFieldInput, FormFieldTextarea, fieldError } from '@shipfox/react-ui/form-field';
6
5
  import { Text } from '@shipfox/react-ui/typography';
7
6
  import { useForm } from '@tanstack/react-form';
8
7
  import { useEffect, useRef, useState } from 'react';
8
+ import { normalizeStoreKey, STORE_KEY_HELP, shouldWarnSensitiveVariableName, validateNewStoreKey, workspaceStoreScope } from '#core/store.js';
9
9
  import { usePutVariableMutation, useVariableQuery } from '#hooks/api/variables.js';
10
10
  import { secretsErrorToFormError } from './form-errors.js';
11
11
  import { FormBody, FormFooter } from './form-shell.js';
12
- import { STORE_KEY_HELP, validateNewStoreKey } from './store-key.js';
13
12
  export const VARIABLE_FORM_ID = 'variable-form';
14
13
  export function VariableForm({ workspaceId, mode, existingKey, existingValue, existingValueTruncated = false, reservedKeys = [], onSaved, onCancel }) {
15
14
  const putVariable = usePutVariableMutation();
@@ -28,10 +27,9 @@ export function VariableForm({ workspaceId, mode, existingKey, existingValue, ex
28
27
  try {
29
28
  await putVariable.mutateAsync({
30
29
  workspaceId,
31
- key: value.key,
32
- body: {
33
- value: value.value
34
- }
30
+ key: normalizeStoreKey(value.key),
31
+ value: value.value,
32
+ scope: workspaceStoreScope
35
33
  });
36
34
  onSaved();
37
35
  } catch (error) {
@@ -107,10 +105,10 @@ export function VariableForm({ workspaceId, mode, existingKey, existingValue, ex
107
105
  disabled: mode === 'edit',
108
106
  placeholder: "LOG_LEVEL",
109
107
  value: field.state.value,
110
- onChange: (event)=>field.handleChange(event.target.value.toUpperCase()),
108
+ onChange: (event)=>field.handleChange(normalizeStoreKey(event.target.value)),
111
109
  onBlur: field.handleBlur
112
110
  }),
113
- isSensitiveSecretName(field.state.value) ? /*#__PURE__*/ _jsx(Callout, {
111
+ shouldWarnSensitiveVariableName(field.state.value) ? /*#__PURE__*/ _jsx(Callout, {
114
112
  role: "alert",
115
113
  type: "warning",
116
114
  children: /*#__PURE__*/ _jsx(Text, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/variable-form.tsx"],"sourcesContent":["import {isSensitiveSecretName} from '@shipfox/api-secrets-dto';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n FormField,\n FormFieldInput,\n FormFieldTextarea,\n fieldError,\n} from '@shipfox/react-ui/form-field';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport {usePutVariableMutation, useVariableQuery} from '#hooks/api/variables.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {FormBody, FormFooter} from './form-shell.js';\nimport {STORE_KEY_HELP, validateNewStoreKey} from './store-key.js';\n\nexport const VARIABLE_FORM_ID = 'variable-form';\n\nexport function VariableForm({\n workspaceId,\n mode,\n existingKey,\n existingValue,\n existingValueTruncated = false,\n reservedKeys = [],\n onSaved,\n onCancel,\n}: {\n workspaceId: string;\n mode: 'create' | 'edit';\n existingKey?: string | undefined;\n existingValue?: string | undefined;\n existingValueTruncated?: boolean | undefined;\n reservedKeys?: readonly string[] | undefined;\n onSaved: () => void;\n onCancel: () => void;\n}) {\n const putVariable = usePutVariableMutation();\n const [formError, setFormError] = useState<string | undefined>();\n\n // The list value is only a preview, so a truncated variable must load its full\n // value before editing to avoid saving the truncated preview back.\n const needsFullValue = mode === 'edit' && existingValueTruncated;\n const fullValueQuery = useVariableQuery(workspaceId, needsFullValue ? existingKey : undefined);\n\n const form = useForm({\n defaultValues: {key: existingKey ?? '', value: existingValue ?? ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await putVariable.mutateAsync({workspaceId, key: value.key, body: {value: value.value}});\n onSaved();\n } catch (error) {\n const mapped = secretsErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n const populatedRef = useRef(false);\n useEffect(() => {\n if (needsFullValue && fullValueQuery.data && !populatedRef.current) {\n populatedRef.current = true;\n form.setFieldValue('value', fullValueQuery.data.value);\n }\n }, [needsFullValue, fullValueQuery.data, form]);\n\n // The full value must be loaded before a save is allowed, otherwise submitting would\n // overwrite the stored value with the truncated preview. Block until it arrives —\n // whether the fetch is still pending or has failed.\n const awaitingFullValue = needsFullValue && fullValueQuery.data === undefined;\n const loadingFullValue = needsFullValue && fullValueQuery.isPending;\n\n return (\n <>\n <FormBody>\n <form\n id={VARIABLE_FORM_ID}\n className=\"flex w-full flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"key\"\n validators={{\n onBlur: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'variable'}),\n onSubmit: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'variable'}),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"variable-key\"\n description={STORE_KEY_HELP}\n error={fieldError(field)}\n >\n <FormFieldInput\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n disabled={mode === 'edit'}\n placeholder=\"LOG_LEVEL\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value.toUpperCase())}\n onBlur={field.handleBlur}\n />\n {isSensitiveSecretName(field.state.value) ? (\n <Callout role=\"alert\" type=\"warning\">\n <Text size=\"sm\" aria-live=\"polite\">\n This looks like it may be sensitive. Variables are stored in plaintext and are\n not redacted from logs. Use a Secret if this value contains private data.\n </Text>\n </Callout>\n ) : null}\n </FormField>\n )}\n </form.Field>\n\n <form.Field name=\"value\">\n {(field) => (\n <FormField\n label=\"Value\"\n id=\"variable-value\"\n error={fieldError(field)}\n description={loadingFullValue ? 'Loading the current value…' : undefined}\n >\n <FormFieldTextarea\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n rows={3}\n disabled={awaitingFullValue}\n placeholder=\"debug\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n </form>\n {awaitingFullValue && fullValueQuery.isError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">Could not load the current value. Close and try again.</Text>\n </Callout>\n ) : null}\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{formError}</Text>\n </Callout>\n ) : null}\n </FormBody>\n <FormFooter>\n <Button variant=\"secondary\" type=\"button\" onClick={onCancel}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={VARIABLE_FORM_ID}\n isLoading={putVariable.isPending}\n disabled={awaitingFullValue}\n >\n {mode === 'edit' ? 'Update variable' : 'Create variable'}\n </Button>\n </FormFooter>\n </>\n );\n}\n"],"names":["isSensitiveSecretName","Button","Callout","FormField","FormFieldInput","FormFieldTextarea","fieldError","Text","useForm","useEffect","useRef","useState","usePutVariableMutation","useVariableQuery","secretsErrorToFormError","FormBody","FormFooter","STORE_KEY_HELP","validateNewStoreKey","VARIABLE_FORM_ID","VariableForm","workspaceId","mode","existingKey","existingValue","existingValueTruncated","reservedKeys","onSaved","onCancel","putVariable","formError","setFormError","needsFullValue","fullValueQuery","undefined","form","defaultValues","key","value","onSubmit","mutateAsync","body","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","populatedRef","data","current","setFieldValue","awaitingFullValue","loadingFullValue","isPending","id","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","label","description","autoComplete","spellCheck","disabled","placeholder","state","onChange","handleChange","target","toUpperCase","handleBlur","role","type","size","aria-live","rows","isError","variant","onClick","isLoading"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/variable-form.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n FormField,\n FormFieldInput,\n FormFieldTextarea,\n fieldError,\n} from '@shipfox/react-ui/form-field';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport {\n normalizeStoreKey,\n STORE_KEY_HELP,\n shouldWarnSensitiveVariableName,\n validateNewStoreKey,\n workspaceStoreScope,\n} from '#core/store.js';\nimport {usePutVariableMutation, useVariableQuery} from '#hooks/api/variables.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {FormBody, FormFooter} from './form-shell.js';\n\nexport const VARIABLE_FORM_ID = 'variable-form';\n\nexport function VariableForm({\n workspaceId,\n mode,\n existingKey,\n existingValue,\n existingValueTruncated = false,\n reservedKeys = [],\n onSaved,\n onCancel,\n}: {\n workspaceId: string;\n mode: 'create' | 'edit';\n existingKey?: string | undefined;\n existingValue?: string | undefined;\n existingValueTruncated?: boolean | undefined;\n reservedKeys?: readonly string[] | undefined;\n onSaved: () => void;\n onCancel: () => void;\n}) {\n const putVariable = usePutVariableMutation();\n const [formError, setFormError] = useState<string | undefined>();\n\n // The list value is only a preview, so a truncated variable must load its full\n // value before editing to avoid saving the truncated preview back.\n const needsFullValue = mode === 'edit' && existingValueTruncated;\n const fullValueQuery = useVariableQuery(workspaceId, needsFullValue ? existingKey : undefined);\n\n const form = useForm({\n defaultValues: {key: existingKey ?? '', value: existingValue ?? ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await putVariable.mutateAsync({\n workspaceId,\n key: normalizeStoreKey(value.key),\n value: value.value,\n scope: workspaceStoreScope,\n });\n onSaved();\n } catch (error) {\n const mapped = secretsErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n const populatedRef = useRef(false);\n useEffect(() => {\n if (needsFullValue && fullValueQuery.data && !populatedRef.current) {\n populatedRef.current = true;\n form.setFieldValue('value', fullValueQuery.data.value);\n }\n }, [needsFullValue, fullValueQuery.data, form]);\n\n // The full value must be loaded before a save is allowed, otherwise submitting would\n // overwrite the stored value with the truncated preview. Block until it arrives —\n // whether the fetch is still pending or has failed.\n const awaitingFullValue = needsFullValue && fullValueQuery.data === undefined;\n const loadingFullValue = needsFullValue && fullValueQuery.isPending;\n\n return (\n <>\n <FormBody>\n <form\n id={VARIABLE_FORM_ID}\n className=\"flex w-full flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"key\"\n validators={{\n onBlur: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'variable'}),\n onSubmit: ({value}) =>\n validateNewStoreKey(value, {mode, reservedKeys, kind: 'variable'}),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"variable-key\"\n description={STORE_KEY_HELP}\n error={fieldError(field)}\n >\n <FormFieldInput\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n disabled={mode === 'edit'}\n placeholder=\"LOG_LEVEL\"\n value={field.state.value}\n onChange={(event) => field.handleChange(normalizeStoreKey(event.target.value))}\n onBlur={field.handleBlur}\n />\n {shouldWarnSensitiveVariableName(field.state.value) ? (\n <Callout role=\"alert\" type=\"warning\">\n <Text size=\"sm\" aria-live=\"polite\">\n This looks like it may be sensitive. Variables are stored in plaintext and are\n not redacted from logs. Use a Secret if this value contains private data.\n </Text>\n </Callout>\n ) : null}\n </FormField>\n )}\n </form.Field>\n\n <form.Field name=\"value\">\n {(field) => (\n <FormField\n label=\"Value\"\n id=\"variable-value\"\n error={fieldError(field)}\n description={loadingFullValue ? 'Loading the current value…' : undefined}\n >\n <FormFieldTextarea\n className=\"font-code\"\n autoComplete=\"off\"\n spellCheck={false}\n rows={3}\n disabled={awaitingFullValue}\n placeholder=\"debug\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n </form>\n {awaitingFullValue && fullValueQuery.isError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">Could not load the current value. Close and try again.</Text>\n </Callout>\n ) : null}\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{formError}</Text>\n </Callout>\n ) : null}\n </FormBody>\n <FormFooter>\n <Button variant=\"secondary\" type=\"button\" onClick={onCancel}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={VARIABLE_FORM_ID}\n isLoading={putVariable.isPending}\n disabled={awaitingFullValue}\n >\n {mode === 'edit' ? 'Update variable' : 'Create variable'}\n </Button>\n </FormFooter>\n </>\n );\n}\n"],"names":["Button","Callout","FormField","FormFieldInput","FormFieldTextarea","fieldError","Text","useForm","useEffect","useRef","useState","normalizeStoreKey","STORE_KEY_HELP","shouldWarnSensitiveVariableName","validateNewStoreKey","workspaceStoreScope","usePutVariableMutation","useVariableQuery","secretsErrorToFormError","FormBody","FormFooter","VARIABLE_FORM_ID","VariableForm","workspaceId","mode","existingKey","existingValue","existingValueTruncated","reservedKeys","onSaved","onCancel","putVariable","formError","setFormError","needsFullValue","fullValueQuery","undefined","form","defaultValues","key","value","onSubmit","mutateAsync","scope","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","populatedRef","data","current","setFieldValue","awaitingFullValue","loadingFullValue","isPending","id","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","label","description","autoComplete","spellCheck","disabled","placeholder","state","onChange","handleChange","target","handleBlur","role","type","size","aria-live","rows","isError","variant","onClick","isLoading"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-secrets-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,uBAAuB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BAsI3E"}
1
+ {"version":3,"file":"workspace-secrets-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,uBAAuB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA0I3E"}
@@ -9,6 +9,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
9
9
  import { toast } from '@shipfox/react-ui/toast';
10
10
  import { Code, Header, Text } from '@shipfox/react-ui/typography';
11
11
  import { useMemo, useState } from 'react';
12
+ import { workspaceStoreScope } from '#core/store.js';
12
13
  import { useDeleteSecretMutation, useSecretsQuery } from '#hooks/api/secrets.js';
13
14
  import { copyKeyName } from './copy-key.js';
14
15
  import { DeleteEntryDialog } from './delete-entry-dialog.js';
@@ -170,7 +171,8 @@ export function WorkspaceSecretsSection({ workspaceId }) {
170
171
  try {
171
172
  await deleteSecret.mutateAsync({
172
173
  workspaceId,
173
- key: deleteKey
174
+ key: deleteKey,
175
+ scope: workspaceStoreScope
174
176
  });
175
177
  toast.success('Secret deleted');
176
178
  closeDelete();
@@ -210,7 +212,7 @@ function SecretRow({ secret, onEdit, onDelete }) {
210
212
  /*#__PURE__*/ _jsx(TableCell, {
211
213
  className: "text-foreground-neutral-muted",
212
214
  children: /*#__PURE__*/ _jsx(RelativeTime, {
213
- value: secret.updated_at
215
+ value: secret.updatedAt
214
216
  })
215
217
  }),
216
218
  /*#__PURE__*/ _jsx(TableCell, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/workspace-secrets-section.tsx"],"sourcesContent":["import type {SecretDto} from '@shipfox/api-secrets-dto';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Modal, ModalContent, ModalHeader, ModalTitle} from '@shipfox/react-ui/modal';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useMemo, useState} from 'react';\nimport {useDeleteSecretMutation, useSecretsQuery} from '#hooks/api/secrets.js';\nimport {copyKeyName} from './copy-key.js';\nimport {DeleteEntryDialog} from './delete-entry-dialog.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {SecretForm} from './secret-form.js';\nimport {StoreRowsSkeleton, StoreSurface} from './store-section-shell.js';\n\nconst SECRETS_DESCRIPTION =\n 'Encrypted, write-only values for sensitive data like API keys, tokens, and passwords.';\nconst EMPTY_SECRETS_DESCRIPTION =\n 'Create a secret to store sensitive values like API keys, tokens, and passwords.';\n\ntype FormState = {mode: 'create'} | {mode: 'edit'; key: string} | null;\n\nexport function WorkspaceSecretsSection({workspaceId}: {workspaceId: string}) {\n const secretsQuery = useSecretsQuery(workspaceId);\n const deleteSecret = useDeleteSecretMutation();\n const [formState, setFormState] = useState<FormState>(null);\n const [deleteKey, setDeleteKey] = useState<string | null>(null);\n const [deleteError, setDeleteError] = useState<string | undefined>();\n\n const secrets = useMemo(\n () => [...(secretsQuery.data ?? [])].sort((a, b) => a.key.localeCompare(b.key)),\n [secretsQuery.data],\n );\n\n function closeDelete() {\n setDeleteKey(null);\n setDeleteError(undefined);\n }\n\n return (\n <RelativeTimeProvider>\n <section className=\"flex flex-col gap-16\" aria-label=\"Secrets\">\n <div className=\"flex items-start justify-between gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Secrets</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {SECRETS_DESCRIPTION}\n </Text>\n </div>\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n </div>\n\n {secretsQuery.isPending ? <StoreRowsSkeleton label=\"Loading secrets\" /> : null}\n\n {secretsQuery.isError && secretsQuery.data === undefined ? (\n <StoreSurface className=\"px-16\">\n <QueryLoadError query={secretsQuery} subject=\"secrets\" />\n </StoreSurface>\n ) : null}\n\n {secretsQuery.data !== undefined && secrets.length === 0 ? (\n <StoreSurface className=\"px-16\">\n <EmptyState\n icon=\"keyLine\"\n title=\"No secrets yet\"\n description={EMPTY_SECRETS_DESCRIPTION}\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n }\n />\n </StoreSurface>\n ) : null}\n\n {secrets.length > 0 ? (\n <StoreSurface>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Value</TableHead>\n <TableHead>Last edited</TableHead>\n <TableHead className=\"w-40 text-right\">\n <span className=\"sr-only\">Actions</span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {secrets.map((secret) => (\n <SecretRow\n key={secret.key}\n secret={secret}\n onEdit={() => setFormState({mode: 'edit', key: secret.key})}\n onDelete={() => setDeleteKey(secret.key)}\n />\n ))}\n </TableBody>\n </Table>\n </StoreSurface>\n ) : null}\n </section>\n\n <Modal\n open={formState !== null}\n onOpenChange={(open) => {\n if (!open) setFormState(null);\n }}\n >\n <ModalContent>\n <ModalHeader>\n <ModalTitle>\n {formState?.mode === 'edit' ? 'Update secret' : 'Create secret'}\n </ModalTitle>\n </ModalHeader>\n {formState ? (\n <SecretForm\n workspaceId={workspaceId}\n mode={formState.mode}\n existingKey={formState.mode === 'edit' ? formState.key : undefined}\n reservedKeys={secrets.map((secret) => secret.key)}\n onSaved={() => {\n const wasEdit = formState.mode === 'edit';\n setFormState(null);\n toast.success(wasEdit ? 'Secret updated' : 'Secret created');\n }}\n onCancel={() => setFormState(null)}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <DeleteEntryDialog\n open={deleteKey !== null}\n onOpenChange={(open) => {\n if (!open) closeDelete();\n }}\n entryKey={deleteKey ?? ''}\n isLoading={deleteSecret.isPending}\n errorMessage={deleteError}\n onConfirm={async () => {\n if (deleteKey === null) return;\n setDeleteError(undefined);\n try {\n await deleteSecret.mutateAsync({workspaceId, key: deleteKey});\n toast.success('Secret deleted');\n closeDelete();\n } catch (error) {\n setDeleteError(secretsErrorToFormError(error).message);\n }\n }}\n />\n </RelativeTimeProvider>\n );\n}\n\nfunction SecretRow({\n secret,\n onEdit,\n onDelete,\n}: {\n secret: SecretDto;\n onEdit: () => void;\n onDelete: () => void;\n}) {\n return (\n <TableRow>\n <TableCell>\n <button\n type=\"button\"\n className=\"inline-flex min-w-0 cursor-pointer rounded-4 border-none bg-transparent p-0 text-left text-foreground-neutral-base outline-none transition-colors hover:text-foreground-highlight-interactive focus-visible:shadow-border-interactive-with-active\"\n aria-label={`Copy secret name ${secret.key}`}\n onClick={() => void copyKeyName(secret.key)}\n >\n <Code as=\"span\" variant=\"paragraph\" className=\"truncate\">\n {secret.key}\n </Code>\n </button>\n </TableCell>\n <TableCell>\n <span\n role=\"img\"\n aria-label=\"Value hidden\"\n className=\"font-code text-foreground-neutral-muted\"\n >\n ••••••••\n </span>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={secret.updated_at} />\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Actions for ${secret.key}`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem icon=\"editLine\" onSelect={onEdit}>\n Edit value\n </DropdownMenuItem>\n <DropdownMenuItem icon=\"deleteBinLine\" onSelect={onDelete}>\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n}\n"],"names":["QueryLoadError","Button","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","EmptyState","Modal","ModalContent","ModalHeader","ModalTitle","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","useDeleteSecretMutation","useSecretsQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","SecretForm","StoreRowsSkeleton","StoreSurface","SECRETS_DESCRIPTION","EMPTY_SECRETS_DESCRIPTION","WorkspaceSecretsSection","workspaceId","secretsQuery","deleteSecret","formState","setFormState","deleteKey","setDeleteKey","deleteError","setDeleteError","secrets","data","sort","a","b","key","localeCompare","closeDelete","undefined","section","className","aria-label","div","variant","size","onClick","mode","isPending","label","isError","query","subject","length","icon","title","description","action","span","map","secret","SecretRow","onEdit","onDelete","open","onOpenChange","existingKey","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","error","message","button","type","as","role","value","updated_at","asChild","align","onSelect"],"mappings":";AACA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,MAAM,EAAEC,UAAU,QAAO,2BAA2B;AAC5D,SACEC,YAAY,EACZC,mBAAmB,EACnBC,gBAAgB,EAChBC,mBAAmB,QACd,kCAAkC;AACzC,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,KAAK,EAAEC,YAAY,EAAEC,WAAW,EAAEC,UAAU,QAAO,0BAA0B;AACrF,SAAQC,YAAY,EAAEC,oBAAoB,QAAO,kCAAkC;AACnF,SACEC,KAAK,EACLC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,WAAW,EACXC,QAAQ,QACH,0BAA0B;AACjC,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,IAAI,EAAEC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAChE,SAAQC,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AACxC,SAAQC,uBAAuB,EAAEC,eAAe,QAAO,wBAAwB;AAC/E,SAAQC,WAAW,QAAO,gBAAgB;AAC1C,SAAQC,iBAAiB,QAAO,2BAA2B;AAC3D,SAAQC,uBAAuB,QAAO,mBAAmB;AACzD,SAAQC,UAAU,QAAO,mBAAmB;AAC5C,SAAQC,iBAAiB,EAAEC,YAAY,QAAO,2BAA2B;AAEzE,MAAMC,sBACJ;AACF,MAAMC,4BACJ;AAIF,OAAO,SAASC,wBAAwB,EAACC,WAAW,EAAwB;IAC1E,MAAMC,eAAeX,gBAAgBU;IACrC,MAAME,eAAeb;IACrB,MAAM,CAACc,WAAWC,aAAa,GAAGhB,SAAoB;IACtD,MAAM,CAACiB,WAAWC,aAAa,GAAGlB,SAAwB;IAC1D,MAAM,CAACmB,aAAaC,eAAe,GAAGpB;IAEtC,MAAMqB,UAAUtB,QACd,IAAM;eAAKc,aAAaS,IAAI,IAAI,EAAE;SAAE,CAACC,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEE,GAAG,CAACC,aAAa,CAACF,EAAEC,GAAG,IAC7E;QAACb,aAAaS,IAAI;KAAC;IAGrB,SAASM;QACPV,aAAa;QACbE,eAAeS;IACjB;IAEA,qBACE,MAACzC;;0BACC,MAAC0C;gBAAQC,WAAU;gBAAuBC,cAAW;;kCACnD,MAACC;wBAAIF,WAAU;;0CACb,MAACE;gCAAIF,WAAU;;kDACb,KAAClC;wCAAOqC,SAAQ;kDAAK;;kDACrB,KAACpC;wCAAKqC,MAAK;wCAAKJ,WAAU;kDACvBtB;;;;0CAGL,KAACjC;gCAAO2D,MAAK;gCAAKC,SAAS,IAAMpB,aAAa;wCAACqB,MAAM;oCAAQ;0CAAI;;;;oBAKlExB,aAAayB,SAAS,iBAAG,KAAC/B;wBAAkBgC,OAAM;yBAAuB;oBAEzE1B,aAAa2B,OAAO,IAAI3B,aAAaS,IAAI,KAAKO,0BAC7C,KAACrB;wBAAauB,WAAU;kCACtB,cAAA,KAACxD;4BAAekE,OAAO5B;4BAAc6B,SAAQ;;yBAE7C;oBAEH7B,aAAaS,IAAI,KAAKO,aAAaR,QAAQsB,MAAM,KAAK,kBACrD,KAACnC;wBAAauB,WAAU;kCACtB,cAAA,KAACjD;4BACC8D,MAAK;4BACLC,OAAM;4BACNC,aAAapC;4BACbqC,sBACE,KAACvE;gCAAO2D,MAAK;gCAAKC,SAAS,IAAMpB,aAAa;wCAACqB,MAAM;oCAAQ;0CAAI;;;yBAMrE;oBAEHhB,QAAQsB,MAAM,GAAG,kBAChB,KAACnC;kCACC,cAAA,MAACnB;;8CACC,KAACI;8CACC,cAAA,MAACC;;0DACC,KAACF;0DAAU;;0DACX,KAACA;0DAAU;;0DACX,KAACA;0DAAU;;0DACX,KAACA;gDAAUuC,WAAU;0DACnB,cAAA,KAACiB;oDAAKjB,WAAU;8DAAU;;;;;;8CAIhC,KAACzC;8CACE+B,QAAQ4B,GAAG,CAAC,CAACC,uBACZ,KAACC;4CAECD,QAAQA;4CACRE,QAAQ,IAAMpC,aAAa;oDAACqB,MAAM;oDAAQX,KAAKwB,OAAOxB,GAAG;gDAAA;4CACzD2B,UAAU,IAAMnC,aAAagC,OAAOxB,GAAG;2CAHlCwB,OAAOxB,GAAG;;;;yBASvB;;;0BAGN,KAAC3C;gBACCuE,MAAMvC,cAAc;gBACpBwC,cAAc,CAACD;oBACb,IAAI,CAACA,MAAMtC,aAAa;gBAC1B;0BAEA,cAAA,MAAChC;;sCACC,KAACC;sCACC,cAAA,KAACC;0CACE6B,WAAWsB,SAAS,SAAS,kBAAkB;;;wBAGnDtB,0BACC,KAACT;4BACCM,aAAaA;4BACbyB,MAAMtB,UAAUsB,IAAI;4BACpBmB,aAAazC,UAAUsB,IAAI,KAAK,SAAStB,UAAUW,GAAG,GAAGG;4BACzD4B,cAAcpC,QAAQ4B,GAAG,CAAC,CAACC,SAAWA,OAAOxB,GAAG;4BAChDgC,SAAS;gCACP,MAAMC,UAAU5C,UAAUsB,IAAI,KAAK;gCACnCrB,aAAa;gCACbrB,MAAMiE,OAAO,CAACD,UAAU,mBAAmB;4BAC7C;4BACAE,UAAU,IAAM7C,aAAa;6BAE7B;;;;0BAIR,KAACZ;gBACCkD,MAAMrC,cAAc;gBACpBsC,cAAc,CAACD;oBACb,IAAI,CAACA,MAAM1B;gBACb;gBACAkC,UAAU7C,aAAa;gBACvB8C,WAAWjD,aAAawB,SAAS;gBACjC0B,cAAc7C;gBACd8C,WAAW;oBACT,IAAIhD,cAAc,MAAM;oBACxBG,eAAeS;oBACf,IAAI;wBACF,MAAMf,aAAaoD,WAAW,CAAC;4BAACtD;4BAAac,KAAKT;wBAAS;wBAC3DtB,MAAMiE,OAAO,CAAC;wBACdhC;oBACF,EAAE,OAAOuC,OAAO;wBACd/C,eAAef,wBAAwB8D,OAAOC,OAAO;oBACvD;gBACF;;;;AAIR;AAEA,SAASjB,UAAU,EACjBD,MAAM,EACNE,MAAM,EACNC,QAAQ,EAKT;IACC,qBACE,MAAC3D;;0BACC,KAACH;0BACC,cAAA,KAAC8E;oBACCC,MAAK;oBACLvC,WAAU;oBACVC,cAAY,CAAC,iBAAiB,EAAEkB,OAAOxB,GAAG,EAAE;oBAC5CU,SAAS,IAAM,KAAKjC,YAAY+C,OAAOxB,GAAG;8BAE1C,cAAA,KAAC9B;wBAAK2E,IAAG;wBAAOrC,SAAQ;wBAAYH,WAAU;kCAC3CmB,OAAOxB,GAAG;;;;0BAIjB,KAACnC;0BACC,cAAA,KAACyD;oBACCwB,MAAK;oBACLxC,cAAW;oBACXD,WAAU;8BACX;;;0BAIH,KAACxC;gBAAUwC,WAAU;0BACnB,cAAA,KAAC5C;oBAAasF,OAAOvB,OAAOwB,UAAU;;;0BAExC,KAACnF;gBAAUwC,WAAU;0BACnB,cAAA,MAACrD;;sCACC,KAACG;4BAAoB8F,OAAO;sCAC1B,cAAA,KAAClG;gCACC0D,MAAK;gCACLD,SAAQ;gCACRU,MAAK;gCACLZ,cAAY,CAAC,YAAY,EAAEkB,OAAOxB,GAAG,EAAE;;;sCAG3C,MAAC/C;4BAAoBiG,OAAM;;8CACzB,KAAChG;oCAAiBgE,MAAK;oCAAWiC,UAAUzB;8CAAQ;;8CAGpD,KAACxE;oCAAiBgE,MAAK;oCAAgBiC,UAAUxB;8CAAU;;;;;;;;;AAQvE"}
1
+ {"version":3,"sources":["../../src/components/workspace-secrets-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Modal, ModalContent, ModalHeader, ModalTitle} from '@shipfox/react-ui/modal';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useMemo, useState} from 'react';\nimport {type SecretMetadata, workspaceStoreScope} from '#core/store.js';\nimport {useDeleteSecretMutation, useSecretsQuery} from '#hooks/api/secrets.js';\nimport {copyKeyName} from './copy-key.js';\nimport {DeleteEntryDialog} from './delete-entry-dialog.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {SecretForm} from './secret-form.js';\nimport {StoreRowsSkeleton, StoreSurface} from './store-section-shell.js';\n\nconst SECRETS_DESCRIPTION =\n 'Encrypted, write-only values for sensitive data like API keys, tokens, and passwords.';\nconst EMPTY_SECRETS_DESCRIPTION =\n 'Create a secret to store sensitive values like API keys, tokens, and passwords.';\n\ntype FormState = {mode: 'create'} | {mode: 'edit'; key: string} | null;\n\nexport function WorkspaceSecretsSection({workspaceId}: {workspaceId: string}) {\n const secretsQuery = useSecretsQuery(workspaceId);\n const deleteSecret = useDeleteSecretMutation();\n const [formState, setFormState] = useState<FormState>(null);\n const [deleteKey, setDeleteKey] = useState<string | null>(null);\n const [deleteError, setDeleteError] = useState<string | undefined>();\n\n const secrets = useMemo(\n () => [...(secretsQuery.data ?? [])].sort((a, b) => a.key.localeCompare(b.key)),\n [secretsQuery.data],\n );\n\n function closeDelete() {\n setDeleteKey(null);\n setDeleteError(undefined);\n }\n\n return (\n <RelativeTimeProvider>\n <section className=\"flex flex-col gap-16\" aria-label=\"Secrets\">\n <div className=\"flex items-start justify-between gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Secrets</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {SECRETS_DESCRIPTION}\n </Text>\n </div>\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n </div>\n\n {secretsQuery.isPending ? <StoreRowsSkeleton label=\"Loading secrets\" /> : null}\n\n {secretsQuery.isError && secretsQuery.data === undefined ? (\n <StoreSurface className=\"px-16\">\n <QueryLoadError query={secretsQuery} subject=\"secrets\" />\n </StoreSurface>\n ) : null}\n\n {secretsQuery.data !== undefined && secrets.length === 0 ? (\n <StoreSurface className=\"px-16\">\n <EmptyState\n icon=\"keyLine\"\n title=\"No secrets yet\"\n description={EMPTY_SECRETS_DESCRIPTION}\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n }\n />\n </StoreSurface>\n ) : null}\n\n {secrets.length > 0 ? (\n <StoreSurface>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Value</TableHead>\n <TableHead>Last edited</TableHead>\n <TableHead className=\"w-40 text-right\">\n <span className=\"sr-only\">Actions</span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {secrets.map((secret) => (\n <SecretRow\n key={secret.key}\n secret={secret}\n onEdit={() => setFormState({mode: 'edit', key: secret.key})}\n onDelete={() => setDeleteKey(secret.key)}\n />\n ))}\n </TableBody>\n </Table>\n </StoreSurface>\n ) : null}\n </section>\n\n <Modal\n open={formState !== null}\n onOpenChange={(open) => {\n if (!open) setFormState(null);\n }}\n >\n <ModalContent>\n <ModalHeader>\n <ModalTitle>\n {formState?.mode === 'edit' ? 'Update secret' : 'Create secret'}\n </ModalTitle>\n </ModalHeader>\n {formState ? (\n <SecretForm\n workspaceId={workspaceId}\n mode={formState.mode}\n existingKey={formState.mode === 'edit' ? formState.key : undefined}\n reservedKeys={secrets.map((secret) => secret.key)}\n onSaved={() => {\n const wasEdit = formState.mode === 'edit';\n setFormState(null);\n toast.success(wasEdit ? 'Secret updated' : 'Secret created');\n }}\n onCancel={() => setFormState(null)}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <DeleteEntryDialog\n open={deleteKey !== null}\n onOpenChange={(open) => {\n if (!open) closeDelete();\n }}\n entryKey={deleteKey ?? ''}\n isLoading={deleteSecret.isPending}\n errorMessage={deleteError}\n onConfirm={async () => {\n if (deleteKey === null) return;\n setDeleteError(undefined);\n try {\n await deleteSecret.mutateAsync({\n workspaceId,\n key: deleteKey,\n scope: workspaceStoreScope,\n });\n toast.success('Secret deleted');\n closeDelete();\n } catch (error) {\n setDeleteError(secretsErrorToFormError(error).message);\n }\n }}\n />\n </RelativeTimeProvider>\n );\n}\n\nfunction SecretRow({\n secret,\n onEdit,\n onDelete,\n}: {\n secret: SecretMetadata;\n onEdit: () => void;\n onDelete: () => void;\n}) {\n return (\n <TableRow>\n <TableCell>\n <button\n type=\"button\"\n className=\"inline-flex min-w-0 cursor-pointer rounded-4 border-none bg-transparent p-0 text-left text-foreground-neutral-base outline-none transition-colors hover:text-foreground-highlight-interactive focus-visible:shadow-border-interactive-with-active\"\n aria-label={`Copy secret name ${secret.key}`}\n onClick={() => void copyKeyName(secret.key)}\n >\n <Code as=\"span\" variant=\"paragraph\" className=\"truncate\">\n {secret.key}\n </Code>\n </button>\n </TableCell>\n <TableCell>\n <span\n role=\"img\"\n aria-label=\"Value hidden\"\n className=\"font-code text-foreground-neutral-muted\"\n >\n ••••••••\n </span>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={secret.updatedAt} />\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Actions for ${secret.key}`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem icon=\"editLine\" onSelect={onEdit}>\n Edit value\n </DropdownMenuItem>\n <DropdownMenuItem icon=\"deleteBinLine\" onSelect={onDelete}>\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n}\n"],"names":["QueryLoadError","Button","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","EmptyState","Modal","ModalContent","ModalHeader","ModalTitle","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","workspaceStoreScope","useDeleteSecretMutation","useSecretsQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","SecretForm","StoreRowsSkeleton","StoreSurface","SECRETS_DESCRIPTION","EMPTY_SECRETS_DESCRIPTION","WorkspaceSecretsSection","workspaceId","secretsQuery","deleteSecret","formState","setFormState","deleteKey","setDeleteKey","deleteError","setDeleteError","secrets","data","sort","a","b","key","localeCompare","closeDelete","undefined","section","className","aria-label","div","variant","size","onClick","mode","isPending","label","isError","query","subject","length","icon","title","description","action","span","map","secret","SecretRow","onEdit","onDelete","open","onOpenChange","existingKey","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","scope","error","message","button","type","as","role","value","updatedAt","asChild","align","onSelect"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-secrets-section.stories.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.stories.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,IAAI,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAgDhE,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAyBzD,iBAAS,YAAY,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAC,+BAcrD;AAED,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAInC,CAAC;AACF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C,eAAO,MAAM,MAAM,EAAE,KAQpB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAKvB,CAAC"}
1
+ {"version":3,"file":"workspace-secrets-section.stories.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.stories.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,IAAI,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAgDhE,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAyBzD,iBAAS,YAAY,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAC,+BAcrD;AAED,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAInC,CAAC;AACF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C,eAAO,MAAM,MAAM,EAAE,KAQpB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAKvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/workspace-secrets-section.stories.tsx"],"sourcesContent":["import type {SecretDto} from '@shipfox/api-secrets-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Decorator, Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {useEffect, useMemo} from 'react';\nimport {within} from 'storybook/test';\nimport {WorkspaceSecretsSection} from './workspace-secrets-section.js';\n\n// Freeze the clock only while a story is mounted so RelativeTime renders a stable\n// string in argos snapshots, without leaking the override to other stories.\nconst FROZEN_NOW = Date.parse('2026-07-02T00:00:00.000Z');\nconst realDateNow = Date.now;\nconst withFrozenClock: Decorator = (Story) => {\n Date.now = () => FROZEN_NOW;\n useEffect(\n () => () => {\n Date.now = realDateNow;\n },\n [],\n );\n return <Story />;\n};\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst EDITOR_ID = '22222222-2222-4222-8222-222222222222';\n\nconst SECRETS: SecretDto[] = [\n {\n key: 'API_TOKEN',\n project_id: null,\n created_at: '2026-05-01T10:00:00.000Z',\n updated_at: '2026-06-30T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'DATABASE_URL',\n project_id: null,\n created_at: '2026-04-01T10:00:00.000Z',\n updated_at: '2026-06-25T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'STRIPE_SECRET_KEY',\n project_id: null,\n created_at: '2026-03-01T10:00:00.000Z',\n updated_at: '2026-06-20T12:00:00.000Z',\n last_edited_by: null,\n },\n];\n\ntype Scenario = 'loaded' | 'empty' | 'loading' | 'error';\n\nconst RETRY_BUTTON = /retry/i;\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (() => {\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (scenario === 'error') {\n return Promise.resolve(\n new Response(JSON.stringify({code: 'server-error', message: 'Something failed'}), {\n status: 500,\n headers: {'content-type': 'application/json'},\n }),\n );\n }\n const secrets = scenario === 'empty' ? [] : SECRETS;\n return Promise.resolve(\n new Response(JSON.stringify({secrets, next_cursor: null}), {\n status: 200,\n headers: {'content-type': 'application/json'},\n }),\n );\n }) as unknown as typeof fetch;\n}\n\nfunction SectionStory({scenario}: {scenario: Scenario}) {\n configureApiClient({baseUrl: 'https://api.example.test', fetchImpl: fetchForScenario(scenario)});\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n return (\n <QueryClientProvider client={queryClient}>\n <div className=\"w-[720px] p-24\">\n <WorkspaceSecretsSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta: Meta<typeof SectionStory> = {\n title: 'Secrets/WorkspaceSecretsSection',\n component: SectionStory,\n decorators: [withFrozenClock],\n};\nexport default meta;\n\ntype Story = StoryObj<typeof SectionStory>;\n\nexport const Loaded: Story = {\n args: {scenario: 'loaded'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n\n await canvas.findByText('API_TOKEN');\n await canvas.findByRole('button', {name: 'Copy secret name API_TOKEN'});\n },\n};\n\nexport const Empty: Story = {\n args: {scenario: 'empty'},\n play: async ({canvasElement}) => {\n await within(canvasElement).findByText('No secrets yet');\n },\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const LoadError: Story = {\n args: {scenario: 'error'},\n play: async ({canvasElement}) => {\n await within(canvasElement).findByRole('button', {name: RETRY_BUTTON});\n },\n};\n"],"names":["configureApiClient","Toaster","QueryClient","QueryClientProvider","useEffect","useMemo","within","WorkspaceSecretsSection","FROZEN_NOW","Date","parse","realDateNow","now","withFrozenClock","Story","WORKSPACE_ID","EDITOR_ID","SECRETS","key","project_id","created_at","updated_at","last_edited_by","RETRY_BUTTON","fetchForScenario","scenario","Promise","undefined","resolve","Response","JSON","stringify","code","message","status","headers","secrets","next_cursor","SectionStory","baseUrl","fetchImpl","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","decorators","Loaded","args","play","canvasElement","canvas","findByText","findByRole","name","Empty","Loading","LoadError"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/workspace-secrets-section.stories.tsx"],"sourcesContent":["import {configureApiClient} from '@shipfox/client-api';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Decorator, Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {useEffect, useMemo} from 'react';\nimport {within} from 'storybook/test';\nimport {WorkspaceSecretsSection} from './workspace-secrets-section.js';\n\n// Freeze the clock only while a story is mounted so RelativeTime renders a stable\n// string in argos snapshots, without leaking the override to other stories.\nconst FROZEN_NOW = Date.parse('2026-07-02T00:00:00.000Z');\nconst realDateNow = Date.now;\nconst withFrozenClock: Decorator = (Story) => {\n Date.now = () => FROZEN_NOW;\n useEffect(\n () => () => {\n Date.now = realDateNow;\n },\n [],\n );\n return <Story />;\n};\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst EDITOR_ID = '22222222-2222-4222-8222-222222222222';\n\nconst SECRETS = [\n {\n key: 'API_TOKEN',\n project_id: null,\n created_at: '2026-05-01T10:00:00.000Z',\n updated_at: '2026-06-30T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'DATABASE_URL',\n project_id: null,\n created_at: '2026-04-01T10:00:00.000Z',\n updated_at: '2026-06-25T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'STRIPE_SECRET_KEY',\n project_id: null,\n created_at: '2026-03-01T10:00:00.000Z',\n updated_at: '2026-06-20T12:00:00.000Z',\n last_edited_by: null,\n },\n];\n\ntype Scenario = 'loaded' | 'empty' | 'loading' | 'error';\n\nconst RETRY_BUTTON = /retry/i;\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (() => {\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (scenario === 'error') {\n return Promise.resolve(\n new Response(JSON.stringify({code: 'server-error', message: 'Something failed'}), {\n status: 500,\n headers: {'content-type': 'application/json'},\n }),\n );\n }\n const secrets = scenario === 'empty' ? [] : SECRETS;\n return Promise.resolve(\n new Response(JSON.stringify({secrets, next_cursor: null}), {\n status: 200,\n headers: {'content-type': 'application/json'},\n }),\n );\n }) as unknown as typeof fetch;\n}\n\nfunction SectionStory({scenario}: {scenario: Scenario}) {\n configureApiClient({baseUrl: 'https://api.example.test', fetchImpl: fetchForScenario(scenario)});\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n return (\n <QueryClientProvider client={queryClient}>\n <div className=\"w-[720px] p-24\">\n <WorkspaceSecretsSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta: Meta<typeof SectionStory> = {\n title: 'Secrets/WorkspaceSecretsSection',\n component: SectionStory,\n decorators: [withFrozenClock],\n};\nexport default meta;\n\ntype Story = StoryObj<typeof SectionStory>;\n\nexport const Loaded: Story = {\n args: {scenario: 'loaded'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n\n await canvas.findByText('API_TOKEN');\n await canvas.findByRole('button', {name: 'Copy secret name API_TOKEN'});\n },\n};\n\nexport const Empty: Story = {\n args: {scenario: 'empty'},\n play: async ({canvasElement}) => {\n await within(canvasElement).findByText('No secrets yet');\n },\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const LoadError: Story = {\n args: {scenario: 'error'},\n play: async ({canvasElement}) => {\n await within(canvasElement).findByRole('button', {name: RETRY_BUTTON});\n },\n};\n"],"names":["configureApiClient","Toaster","QueryClient","QueryClientProvider","useEffect","useMemo","within","WorkspaceSecretsSection","FROZEN_NOW","Date","parse","realDateNow","now","withFrozenClock","Story","WORKSPACE_ID","EDITOR_ID","SECRETS","key","project_id","created_at","updated_at","last_edited_by","RETRY_BUTTON","fetchForScenario","scenario","Promise","undefined","resolve","Response","JSON","stringify","code","message","status","headers","secrets","next_cursor","SectionStory","baseUrl","fetchImpl","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","decorators","Loaded","args","play","canvasElement","canvas","findByText","findByRole","name","Empty","Loading","LoadError"],"mappings":";AAAA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,OAAO,QAAO,0BAA0B;AAEhD,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SAAQC,SAAS,EAAEC,OAAO,QAAO,QAAQ;AACzC,SAAQC,MAAM,QAAO,iBAAiB;AACtC,SAAQC,uBAAuB,QAAO,iCAAiC;AAEvE,kFAAkF;AAClF,4EAA4E;AAC5E,MAAMC,aAAaC,KAAKC,KAAK,CAAC;AAC9B,MAAMC,cAAcF,KAAKG,GAAG;AAC5B,MAAMC,kBAA6B,CAACC;IAClCL,KAAKG,GAAG,GAAG,IAAMJ;IACjBJ,UACE,IAAM;YACJK,KAAKG,GAAG,GAAGD;QACb,GACA,EAAE;IAEJ,qBAAO,KAACG;AACV;AAEA,MAAMC,eAAe;AACrB,MAAMC,YAAY;AAElB,MAAMC,UAAU;IACd;QACEC,KAAK;QACLC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgBN;IAClB;IACA;QACEE,KAAK;QACLC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgBN;IAClB;IACA;QACEE,KAAK;QACLC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgB;IAClB;CACD;AAID,MAAMC,eAAe;AAErB,SAASC,iBAAiBC,QAAkB;IAC1C,OAAQ;QACN,IAAIA,aAAa,WAAW,OAAO,IAAIC,QAAkB,IAAMC;QAC/D,IAAIF,aAAa,SAAS;YACxB,OAAOC,QAAQE,OAAO,CACpB,IAAIC,SAASC,KAAKC,SAAS,CAAC;gBAACC,MAAM;gBAAgBC,SAAS;YAAkB,IAAI;gBAChFC,QAAQ;gBACRC,SAAS;oBAAC,gBAAgB;gBAAkB;YAC9C;QAEJ;QACA,MAAMC,UAAUX,aAAa,UAAU,EAAE,GAAGR;QAC5C,OAAOS,QAAQE,OAAO,CACpB,IAAIC,SAASC,KAAKC,SAAS,CAAC;YAACK;YAASC,aAAa;QAAI,IAAI;YACzDH,QAAQ;YACRC,SAAS;gBAAC,gBAAgB;YAAkB;QAC9C;IAEJ;AACF;AAEA,SAASG,aAAa,EAACb,QAAQ,EAAuB;IACpDzB,mBAAmB;QAACuC,SAAS;QAA4BC,WAAWhB,iBAAiBC;IAAS;IAC9F,MAAMgB,cAAcpC,QAClB,IAAM,IAAIH,YAAY;YAACwC,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAEJ,qBACE,MAACzC;QAAoB0C,QAAQJ;;0BAC3B,KAACK;gBAAIC,WAAU;0BACb,cAAA,KAACxC;oBAAwByC,aAAajC;;;0BAExC,KAACd;;;AAGP;AAEA,MAAMgD,OAAkC;IACtCC,OAAO;IACPC,WAAWb;IACXc,YAAY;QAACvC;KAAgB;AAC/B;AACA,eAAeoC,KAAK;AAIpB,OAAO,MAAMI,SAAgB;IAC3BC,MAAM;QAAC7B,UAAU;IAAQ;IACzB8B,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMC,SAASnD,OAAOkD;QAEtB,MAAMC,OAAOC,UAAU,CAAC;QACxB,MAAMD,OAAOE,UAAU,CAAC,UAAU;YAACC,MAAM;QAA4B;IACvE;AACF,EAAE;AAEF,OAAO,MAAMC,QAAe;IAC1BP,MAAM;QAAC7B,UAAU;IAAO;IACxB8B,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMlD,OAAOkD,eAAeE,UAAU,CAAC;IACzC;AACF,EAAE;AAEF,OAAO,MAAMI,UAAiB;IAC5BR,MAAM;QAAC7B,UAAU;IAAS;AAC5B,EAAE;AAEF,OAAO,MAAMsC,YAAmB;IAC9BT,MAAM;QAAC7B,UAAU;IAAO;IACxB8B,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMlD,OAAOkD,eAAeG,UAAU,CAAC,UAAU;YAACC,MAAMrC;QAAY;IACtE;AACF,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-variables-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-variables-section.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,yBAAyB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA0I7E"}
1
+ {"version":3,"file":"workspace-variables-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-variables-section.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,yBAAyB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA8I7E"}
@@ -9,6 +9,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
9
9
  import { toast } from '@shipfox/react-ui/toast';
10
10
  import { Code, Header, Text } from '@shipfox/react-ui/typography';
11
11
  import { useMemo, useState } from 'react';
12
+ import { workspaceStoreScope } from '#core/store.js';
12
13
  import { useDeleteVariableMutation, useVariablesQuery } from '#hooks/api/variables.js';
13
14
  import { copyKeyName } from './copy-key.js';
14
15
  import { DeleteEntryDialog } from './delete-entry-dialog.js';
@@ -146,7 +147,7 @@ export function WorkspaceVariablesSection({ workspaceId }) {
146
147
  mode: formState.mode,
147
148
  existingKey: formState.mode === 'edit' ? formState.variable.key : undefined,
148
149
  existingValue: formState.mode === 'edit' ? formState.variable.value : undefined,
149
- existingValueTruncated: formState.mode === 'edit' ? formState.variable.value_truncated : undefined,
150
+ existingValueTruncated: formState.mode === 'edit' ? formState.variable.valueTruncated : undefined,
150
151
  reservedKeys: variables.map((variable)=>variable.key),
151
152
  onSaved: ()=>{
152
153
  const wasEdit = formState.mode === 'edit';
@@ -172,7 +173,8 @@ export function WorkspaceVariablesSection({ workspaceId }) {
172
173
  try {
173
174
  await deleteVariable.mutateAsync({
174
175
  workspaceId,
175
- key: deleteKey
176
+ key: deleteKey,
177
+ scope: workspaceStoreScope
176
178
  });
177
179
  toast.success('Variable deleted');
178
180
  closeDelete();
@@ -214,7 +216,7 @@ function VariableRow({ variable, onEdit, onDelete }) {
214
216
  /*#__PURE__*/ _jsx(TableCell, {
215
217
  className: "text-foreground-neutral-muted",
216
218
  children: /*#__PURE__*/ _jsx(RelativeTime, {
217
- value: variable.updated_at
219
+ value: variable.updatedAt
218
220
  })
219
221
  }),
220
222
  /*#__PURE__*/ _jsx(TableCell, {