@flamingo-stack/openframe-frontend-core 0.0.442 → 0.0.443

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 (107) hide show
  1. package/dist/{chunk-J5NO7PAG.cjs → chunk-2CJ734N2.cjs} +3 -3
  2. package/dist/{chunk-J5NO7PAG.cjs.map → chunk-2CJ734N2.cjs.map} +1 -1
  3. package/dist/{chunk-UFBUQO7Z.cjs → chunk-323TCNF3.cjs} +18 -18
  4. package/dist/{chunk-UFBUQO7Z.cjs.map → chunk-323TCNF3.cjs.map} +1 -1
  5. package/dist/{chunk-63TSTDME.js → chunk-3ZE55IH6.js} +5 -5
  6. package/dist/{chunk-Y3NFB6HR.js → chunk-5U6OZ5FD.js} +4 -4
  7. package/dist/{chunk-IAZ5AN2E.js → chunk-6IPNXMB7.js} +6 -6
  8. package/dist/{chunk-XMCVUYKC.cjs → chunk-6NO4YYU7.cjs} +12 -12
  9. package/dist/{chunk-XMCVUYKC.cjs.map → chunk-6NO4YYU7.cjs.map} +1 -1
  10. package/dist/{chunk-QO6MCQYE.cjs → chunk-7CZ4MJSI.cjs} +13 -10
  11. package/dist/chunk-7CZ4MJSI.cjs.map +1 -0
  12. package/dist/{chunk-FPE3SQ5D.js → chunk-A7MG4SBL.js} +3 -3
  13. package/dist/{chunk-SM26C2L7.js → chunk-AZQOX2RS.js} +2 -2
  14. package/dist/{chunk-JYI4R25Q.js → chunk-DOVBNECK.js} +3 -3
  15. package/dist/{chunk-7KDR5P23.js → chunk-FQASXKBK.js} +2 -2
  16. package/dist/{chunk-LFWPWIAC.cjs → chunk-H4W3NCR2.cjs} +7 -7
  17. package/dist/{chunk-LFWPWIAC.cjs.map → chunk-H4W3NCR2.cjs.map} +1 -1
  18. package/dist/{chunk-DUQQEQ7G.cjs → chunk-IBIGXER5.cjs} +191 -153
  19. package/dist/chunk-IBIGXER5.cjs.map +1 -0
  20. package/dist/{chunk-IZ6A6K6N.cjs → chunk-KNNSS3CZ.cjs} +15 -15
  21. package/dist/{chunk-IZ6A6K6N.cjs.map → chunk-KNNSS3CZ.cjs.map} +1 -1
  22. package/dist/{chunk-AGV26PUR.js → chunk-LGMRNYTX.js} +3 -3
  23. package/dist/{chunk-YZJUHEPZ.js → chunk-LYYVTI34.js} +5 -5
  24. package/dist/{chunk-TAQMYVO6.cjs → chunk-M6XUXQ5Q.cjs} +40 -40
  25. package/dist/{chunk-TAQMYVO6.cjs.map → chunk-M6XUXQ5Q.cjs.map} +1 -1
  26. package/dist/{chunk-2BTDPDME.js → chunk-MG4AMIFX.js} +4 -1
  27. package/dist/chunk-MG4AMIFX.js.map +1 -0
  28. package/dist/{chunk-ZQYFDYWV.js → chunk-NQODKJ2F.js} +121 -83
  29. package/dist/{chunk-ZQYFDYWV.js.map → chunk-NQODKJ2F.js.map} +1 -1
  30. package/dist/{chunk-KUXGQ2FQ.cjs → chunk-OHKKI6EF.cjs} +32 -32
  31. package/dist/{chunk-KUXGQ2FQ.cjs.map → chunk-OHKKI6EF.cjs.map} +1 -1
  32. package/dist/{chunk-JOH5D76X.cjs → chunk-PRJFSF4W.cjs} +73 -73
  33. package/dist/{chunk-JOH5D76X.cjs.map → chunk-PRJFSF4W.cjs.map} +1 -1
  34. package/dist/{chunk-W3JZXLJ2.js → chunk-PVMPBWKX.js} +6 -6
  35. package/dist/{chunk-7KNOHGMI.js → chunk-RQVR7I73.js} +2 -2
  36. package/dist/{chunk-7QP65IE4.cjs → chunk-RYKANQE6.cjs} +5 -5
  37. package/dist/{chunk-7QP65IE4.cjs.map → chunk-RYKANQE6.cjs.map} +1 -1
  38. package/dist/{chunk-QAG357PE.js → chunk-UXJ47OJF.js} +2 -2
  39. package/dist/{chunk-JNC4LVD6.cjs → chunk-VACXEUGF.cjs} +29 -29
  40. package/dist/{chunk-JNC4LVD6.cjs.map → chunk-VACXEUGF.cjs.map} +1 -1
  41. package/dist/{chunk-LUKRPHEW.cjs → chunk-YPFQ2UIH.cjs} +73 -73
  42. package/dist/{chunk-LUKRPHEW.cjs.map → chunk-YPFQ2UIH.cjs.map} +1 -1
  43. package/dist/{chunk-IPEIOR77.cjs → chunk-ZVAAIP5Y.cjs} +40 -40
  44. package/dist/{chunk-IPEIOR77.cjs.map → chunk-ZVAAIP5Y.cjs.map} +1 -1
  45. package/dist/components/case-studies/index.cjs +9 -9
  46. package/dist/components/case-studies/index.js +3 -3
  47. package/dist/components/chat/index.cjs +3 -3
  48. package/dist/components/chat/index.js +2 -2
  49. package/dist/components/contact/index.cjs +4 -4
  50. package/dist/components/contact/index.js +3 -3
  51. package/dist/components/docs/index.cjs +6 -6
  52. package/dist/components/docs/index.js +5 -5
  53. package/dist/components/embeds/index.cjs +4 -4
  54. package/dist/components/embeds/index.js +3 -3
  55. package/dist/components/faq/index.cjs +5 -5
  56. package/dist/components/faq/index.js +4 -4
  57. package/dist/components/features/index.cjs +3 -3
  58. package/dist/components/features/index.js +2 -2
  59. package/dist/components/features/policy-configuration-panel.d.ts +11 -0
  60. package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
  61. package/dist/components/help-center-pages/index.cjs +22 -22
  62. package/dist/components/help-center-pages/index.js +13 -13
  63. package/dist/components/index.cjs +135 -135
  64. package/dist/components/index.js +11 -11
  65. package/dist/components/navigation/index.cjs +3 -3
  66. package/dist/components/navigation/index.js +2 -2
  67. package/dist/components/onboarding-guides/index.cjs +7 -7
  68. package/dist/components/onboarding-guides/index.js +6 -6
  69. package/dist/components/related-content/index.cjs +5 -5
  70. package/dist/components/related-content/index.js +4 -4
  71. package/dist/components/tickets/index.cjs +6 -6
  72. package/dist/components/tickets/index.js +5 -5
  73. package/dist/components/ui/index.cjs +3 -3
  74. package/dist/components/ui/index.js +2 -2
  75. package/dist/hooks/index.cjs +2 -2
  76. package/dist/hooks/index.js +1 -1
  77. package/dist/index.cjs +3 -3
  78. package/dist/index.js +2 -2
  79. package/dist/types/permissions.d.ts +9 -6
  80. package/dist/types/permissions.d.ts.map +1 -1
  81. package/dist/utils/embed-authed-fetch.d.ts +19 -3
  82. package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
  83. package/dist/utils/index.cjs +3 -0
  84. package/dist/utils/index.cjs.map +1 -1
  85. package/dist/utils/index.js +3 -0
  86. package/dist/utils/index.js.map +1 -1
  87. package/package.json +1 -1
  88. package/src/components/features/policy-configuration-panel.tsx +185 -122
  89. package/src/stories/PolicyConfigurationPanel.stories.tsx +254 -0
  90. package/src/types/permissions.ts +10 -7
  91. package/src/utils/__tests__/embed-authed-fetch.test.ts +39 -0
  92. package/src/utils/embed-authed-fetch.ts +27 -3
  93. package/dist/chunk-2BTDPDME.js.map +0 -1
  94. package/dist/chunk-DUQQEQ7G.cjs.map +0 -1
  95. package/dist/chunk-QO6MCQYE.cjs.map +0 -1
  96. /package/dist/{chunk-63TSTDME.js.map → chunk-3ZE55IH6.js.map} +0 -0
  97. /package/dist/{chunk-Y3NFB6HR.js.map → chunk-5U6OZ5FD.js.map} +0 -0
  98. /package/dist/{chunk-IAZ5AN2E.js.map → chunk-6IPNXMB7.js.map} +0 -0
  99. /package/dist/{chunk-FPE3SQ5D.js.map → chunk-A7MG4SBL.js.map} +0 -0
  100. /package/dist/{chunk-SM26C2L7.js.map → chunk-AZQOX2RS.js.map} +0 -0
  101. /package/dist/{chunk-JYI4R25Q.js.map → chunk-DOVBNECK.js.map} +0 -0
  102. /package/dist/{chunk-7KDR5P23.js.map → chunk-FQASXKBK.js.map} +0 -0
  103. /package/dist/{chunk-AGV26PUR.js.map → chunk-LGMRNYTX.js.map} +0 -0
  104. /package/dist/{chunk-YZJUHEPZ.js.map → chunk-LYYVTI34.js.map} +0 -0
  105. /package/dist/{chunk-W3JZXLJ2.js.map → chunk-PVMPBWKX.js.map} +0 -0
  106. /package/dist/{chunk-7KNOHGMI.js.map → chunk-RQVR7I73.js.map} +0 -0
  107. /package/dist/{chunk-QAG357PE.js.map → chunk-UXJ47OJF.js.map} +0 -0
@@ -11,21 +11,24 @@ export interface PermissionPolicy {
11
11
  approvalLevel: ApprovalLevel;
12
12
  }
13
13
 
14
+ /**
15
+ * Pure data describing one permission category. Presentation state (expansion,
16
+ * bulk-selection display) is owned by PolicyConfigurationPanel itself.
17
+ */
14
18
  export interface PermissionCategory {
15
19
  id: string;
16
20
  name: string;
17
- icon: React.ReactNode;
18
- configurationsCount: number;
19
- globalPermission?: ApprovalLevel;
20
- isExpanded: boolean;
21
+ /** Optional leading icon; the panel falls back to a shield. */
22
+ icon?: React.ReactNode;
21
23
  policies: PermissionPolicy[];
22
24
  }
23
25
 
24
26
  export interface PolicyConfigurationPanelProps {
25
27
  categories: PermissionCategory[];
26
28
  editMode: boolean;
27
- onCategoryToggle: (categoryId: string) => void;
28
- onGlobalPermissionChange: (categoryId: string, level: ApprovalLevel | undefined) => void;
29
+ /** Change a single policy's approval level. `policyId` is the policy's `id`. */
29
30
  onPolicyPermissionChange: (categoryId: string, policyId: string, level: ApprovalLevel) => void;
31
+ /** Bulk-apply an approval level to every policy in a category. */
32
+ onCategoryPermissionChange: (categoryId: string, level: ApprovalLevel) => void;
30
33
  className?: string;
31
- }
34
+ }
@@ -166,3 +166,42 @@ describe('embedAuthedFetch 401 self-heal (adapter.refresh)', () => {
166
166
  expect(fetchSpy).toHaveBeenCalledTimes(1)
167
167
  })
168
168
  })
169
+
170
+ describe('embedAuthedFetch adapter.allowedOrigins (native-shell cross-origin sanction)', () => {
171
+ const originalFetch = globalThis.fetch
172
+
173
+ beforeEach(() => {
174
+ globalThis.fetch = vi.fn(async () => new Response('ok')) as typeof fetch
175
+ // Production mode so these tests exercise the real guard, not the
176
+ // dev-mode warn-and-allow hatch.
177
+ vi.stubEnv('NODE_ENV', 'production')
178
+ })
179
+ afterEach(() => {
180
+ globalThis.fetch = originalFetch
181
+ setEmbedAuthAdapter(null)
182
+ vi.unstubAllEnvs()
183
+ vi.restoreAllMocks()
184
+ })
185
+
186
+ it('allows a cross-origin URL whose exact origin the adapter sanctions', async () => {
187
+ setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
188
+ await expect(embedAuthedFetch('https://gateway.example/content/api/faqs')).resolves.toBeDefined()
189
+ })
190
+
191
+ it('still rejects origins outside the allowlist', () => {
192
+ setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
193
+ expect(() => embedAuthedFetch('https://evil.com/x')).toThrow(/cross-origin/i)
194
+ })
195
+
196
+ it('does not treat an allowlisted origin as a prefix (suffix-domain spoof rejected)', () => {
197
+ setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
198
+ expect(() => embedAuthedFetch('https://gateway.example.evil.com/x')).toThrow(/cross-origin/i)
199
+ })
200
+
201
+ it('cannot allowlist non-http(s) schemes (protocol check runs first)', () => {
202
+ // `javascript:` URLs have origin 'null'; even a (misguided) 'null' entry
203
+ // must not get past the protocol gate.
204
+ setEmbedAuthAdapter({ allowedOrigins: ['null'] })
205
+ expect(() => embedAuthedFetch('javascript:alert(1)')).toThrow(/non-http\(s\)/i)
206
+ })
207
+ })
@@ -86,6 +86,19 @@ export interface EmbedAuthAdapter {
86
86
  * host's own auth layer, not to this fetch wrapper.
87
87
  */
88
88
  refresh?: () => Promise<boolean>
89
+ /**
90
+ * Exact extra origins (`scheme://host[:port]`, as produced by `URL.origin`)
91
+ * the same-origin guard additionally accepts. The guard normally rejects
92
+ * every cross-origin URL in production — but native-shell hosts serve the
93
+ * page from a local pseudo-origin (`capacitor://localhost`,
94
+ * `tauri://localhost`) with NO server behind it, so the gateway their
95
+ * bearer already belongs to is unavoidably cross-origin. Listing it here
96
+ * is the host's explicit sanction to send the adapter's credentials there.
97
+ * Compared AFTER the http(s)-protocol check, so non-http(s) schemes can
98
+ * never be allowlisted. Omit (or leave empty) everywhere else — same-origin
99
+ * remains the rule, this is the narrow exception.
100
+ */
101
+ allowedOrigins?: string[]
89
102
  }
90
103
 
91
104
  /**
@@ -158,9 +171,12 @@ export function hasEmbedAuthAdapter(): boolean {
158
171
  *
159
172
  * **Cross-origin defense:** the wrapper assumes a same-origin `/api/…`
160
173
  * relative URL. Absolute URLs are accepted only when their origin matches
161
- * the current window's origin; cross-origin URLs throw before the bearer
162
- * leaves the page. This is a defense-in-depth guard for future call sites
163
- * there is no legitimate cross-origin use of this fetch wrapper.
174
+ * the current window's origin or appears in the registered adapter's
175
+ * `allowedOrigins` (the native-shell hatch; see that field's doc) and
176
+ * cross-origin URLs otherwise throw before the bearer leaves the page.
177
+ * This is a defense-in-depth guard for future call sites — outside the
178
+ * allowlisted-shell case there is no legitimate cross-origin use of this
179
+ * fetch wrapper.
164
180
  *
165
181
  * **401 self-heal:** when a registered adapter supplies `refresh`, a `401`
166
182
  * response triggers a single token refresh + retry of the same request
@@ -352,6 +368,14 @@ function assertSameOrigin(url: string): void {
352
368
  )
353
369
  }
354
370
  if (target.origin !== pageOrigin) {
371
+ // Host-sanctioned cross-origin target (`EmbedAuthAdapter.allowedOrigins`).
372
+ // Native shells serve the page from a local pseudo-origin with no server
373
+ // behind it, so their gateway — the same backend the host already trusts
374
+ // with this bearer — is unavoidably cross-origin. Exact `URL.origin`
375
+ // match, and only reachable for http(s) targets (checked above).
376
+ if (getRegisteredAuthAdapter()?.allowedOrigins?.includes(target.origin)) {
377
+ return
378
+ }
355
379
  // Dev-mode escape hatch — embedded apps (e.g. openframe-frontend)
356
380
  // run on a different origin from their gateway during local dev,
357
381
  // and forcing a Next.js `rewrites()` workaround is more error-prone