@fluid-app/portal-sdk 0.1.128 → 0.1.130

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 (130) hide show
  1. package/dist/{AppDownloadScreen-DTxo4z3_.cjs → AppDownloadScreen-ChKim_6A.cjs} +2 -2
  2. package/dist/{AppDownloadScreen-DTxo4z3_.cjs.map → AppDownloadScreen-ChKim_6A.cjs.map} +1 -1
  3. package/dist/{AppDownloadScreen-BoGSdsJk.mjs → AppDownloadScreen-DGHd6hYM.mjs} +2 -2
  4. package/dist/{AppDownloadScreen-BoGSdsJk.mjs.map → AppDownloadScreen-DGHd6hYM.mjs.map} +1 -1
  5. package/dist/{AppDownloadScreen-Chxavsr_.cjs → AppDownloadScreen-DpyV1tJw.cjs} +2 -2
  6. package/dist/{ContactsScreen-BKOHursc.mjs → ContactsScreen-Bcea6126.mjs} +3 -3
  7. package/dist/{ContactsScreen-BKOHursc.mjs.map → ContactsScreen-Bcea6126.mjs.map} +1 -1
  8. package/dist/{ContactsScreen-DN8Qt2Ih.cjs → ContactsScreen-CsIGZaWy.cjs} +4 -4
  9. package/dist/{ContactsScreen-DN8Qt2Ih.cjs.map → ContactsScreen-CsIGZaWy.cjs.map} +1 -1
  10. package/dist/{ContactsScreen-FrVLbjGO.cjs → ContactsScreen-OBDC1046.cjs} +4 -4
  11. package/dist/{FluidProvider-CgTeGUnF.mjs → FluidProvider-BJQSXofR.mjs} +198 -615
  12. package/dist/FluidProvider-BJQSXofR.mjs.map +1 -0
  13. package/dist/{FluidProvider-BMMu_rp3.cjs → FluidProvider-DvqnkjZI.cjs} +196 -625
  14. package/dist/FluidProvider-DvqnkjZI.cjs.map +1 -0
  15. package/dist/{MessagingScreen-Bvq3Dd5i.mjs → MessagingScreen-B48ksZOJ.mjs} +3 -3
  16. package/dist/{MessagingScreen-Bvq3Dd5i.mjs.map → MessagingScreen-B48ksZOJ.mjs.map} +1 -1
  17. package/dist/{MessagingScreen-DMDXiH97.mjs → MessagingScreen-COGo4S9K.mjs} +2 -2
  18. package/dist/{MessagingScreen-DgbNN4BF.cjs → MessagingScreen-DYgiatey.cjs} +3 -3
  19. package/dist/{MessagingScreen-DgbNN4BF.cjs.map → MessagingScreen-DYgiatey.cjs.map} +1 -1
  20. package/dist/{MessagingScreen-bzzXjQMu.cjs → MessagingScreen-QyUOxYXl.cjs} +2 -2
  21. package/dist/{MySiteScreen-nV8x9xyy.cjs → MySiteScreen-ByIJ6CkU.cjs} +2 -2
  22. package/dist/{MySiteScreen-nV8x9xyy.cjs.map → MySiteScreen-ByIJ6CkU.cjs.map} +1 -1
  23. package/dist/{MySiteScreen-BJH5-RNT.mjs → MySiteScreen-DZ0ru6Bn.mjs} +2 -2
  24. package/dist/{MySiteScreen-BJH5-RNT.mjs.map → MySiteScreen-DZ0ru6Bn.mjs.map} +1 -1
  25. package/dist/{MySiteScreen-CYZpUYTn.cjs → MySiteScreen-DrWUJJiH.cjs} +2 -2
  26. package/dist/{OrdersScreen-BL__flBE.cjs → OrdersScreen-CkvoeTvK.cjs} +3 -3
  27. package/dist/OrdersScreen-D_7TJgZ4.mjs +561 -0
  28. package/dist/OrdersScreen-D_7TJgZ4.mjs.map +1 -0
  29. package/dist/OrdersScreen-DyYYjl9I.cjs +568 -0
  30. package/dist/OrdersScreen-DyYYjl9I.cjs.map +1 -0
  31. package/dist/{ProductsScreen-BIYHPaBZ.cjs → ProductsScreen-B8NmyIJy.cjs} +3 -3
  32. package/dist/{ProductsScreen-COwahI-V.mjs → ProductsScreen-CMnhqsSA.mjs} +5 -5
  33. package/dist/{ProductsScreen-COwahI-V.mjs.map → ProductsScreen-CMnhqsSA.mjs.map} +1 -1
  34. package/dist/{ProductsScreen-D6h-r9ht.mjs → ProductsScreen-DzNmbwVi.mjs} +3 -3
  35. package/dist/{ProductsScreen-C8UfVLRr.cjs → ProductsScreen-Z1hx1YZQ.cjs} +5 -5
  36. package/dist/{ProductsScreen-C8UfVLRr.cjs.map → ProductsScreen-Z1hx1YZQ.cjs.map} +1 -1
  37. package/dist/{ProfileScreen-CZp_NrjO.cjs → ProfileScreen-B81Ovmh_.cjs} +2 -2
  38. package/dist/{ProfileScreen-FYGHStqM.cjs → ProfileScreen-CYTxOGeW.cjs} +526 -137
  39. package/dist/ProfileScreen-CYTxOGeW.cjs.map +1 -0
  40. package/dist/{ProfileScreen-BKRn8AqI.mjs → ProfileScreen-QOXtyrJi.mjs} +522 -133
  41. package/dist/ProfileScreen-QOXtyrJi.mjs.map +1 -0
  42. package/dist/{ShareablesScreen-BEPVTMeI.cjs → ShareablesScreen-1HpfEjyd.cjs} +7 -7
  43. package/dist/{ShareablesScreen-BEPVTMeI.cjs.map → ShareablesScreen-1HpfEjyd.cjs.map} +1 -1
  44. package/dist/{ShareablesScreen-BrC5LGtU.cjs → ShareablesScreen-BXO8MpAy.cjs} +3 -3
  45. package/dist/{ShareablesScreen-BXzxUg0E.mjs → ShareablesScreen-DSsMJJh_.mjs} +7 -7
  46. package/dist/{ShareablesScreen-BXzxUg0E.mjs.map → ShareablesScreen-DSsMJJh_.mjs.map} +1 -1
  47. package/dist/{ShareablesScreen-aMnwEOAH.mjs → ShareablesScreen-Oo3jMHX6.mjs} +3 -3
  48. package/dist/{ShopScreen-B4Tmn4pJ.cjs → ShopScreen-BDcWpmi7.cjs} +5 -5
  49. package/dist/{ShopScreen-B4Tmn4pJ.cjs.map → ShopScreen-BDcWpmi7.cjs.map} +1 -1
  50. package/dist/{ShopScreen-B0VkBuQI.cjs → ShopScreen-CWxOPn7H.cjs} +2 -2
  51. package/dist/{ShopScreen-DJt1rKw3.mjs → ShopScreen-DsReuJ7P.mjs} +5 -5
  52. package/dist/{ShopScreen-DJt1rKw3.mjs.map → ShopScreen-DsReuJ7P.mjs.map} +1 -1
  53. package/dist/{SubscriptionsScreen-oApGaq11.cjs → SubscriptionsScreen-B8mLGt5-.cjs} +27 -10
  54. package/dist/SubscriptionsScreen-B8mLGt5-.cjs.map +1 -0
  55. package/dist/{SubscriptionsScreen-C2F3HNJS.cjs → SubscriptionsScreen-C2zbEjMC.cjs} +4 -4
  56. package/dist/{SubscriptionsScreen-CZ-1jSO2.mjs → SubscriptionsScreen-DZxLo4up.mjs} +21 -4
  57. package/dist/SubscriptionsScreen-DZxLo4up.mjs.map +1 -0
  58. package/dist/index.cjs +37 -37
  59. package/dist/index.d.cts.map +1 -1
  60. package/dist/index.d.mts.map +1 -1
  61. package/dist/index.mjs +37 -37
  62. package/dist/order-status-badge-KooNqnAs.mjs +262 -0
  63. package/dist/order-status-badge-KooNqnAs.mjs.map +1 -0
  64. package/dist/order-status-badge-cwqA8dZ-.cjs +304 -0
  65. package/dist/order-status-badge-cwqA8dZ-.cjs.map +1 -0
  66. package/dist/portal_tenant-CP5Ce8Jn.cjs +261 -0
  67. package/dist/portal_tenant-CP5Ce8Jn.cjs.map +1 -0
  68. package/dist/portal_tenant-CWy4Zg2t.mjs +166 -0
  69. package/dist/portal_tenant-CWy4Zg2t.mjs.map +1 -0
  70. package/dist/src-BOIW-KES.mjs +3 -0
  71. package/dist/src-CzwiFO_J.cjs +3 -0
  72. package/dist/{src-BMUEjfhg.mjs → src-Dgo44BGe.mjs} +1 -1
  73. package/dist/{src-BMUEjfhg.mjs.map → src-Dgo44BGe.mjs.map} +1 -1
  74. package/dist/{src-BJdOxgpp.cjs → src-DkhHoxnS.cjs} +1 -1
  75. package/dist/{src-BJdOxgpp.cjs.map → src-DkhHoxnS.cjs.map} +1 -1
  76. package/dist/use-account-clients-Dim60sir.mjs +451 -0
  77. package/dist/use-account-clients-Dim60sir.mjs.map +1 -0
  78. package/dist/use-account-clients-DoJW3KTx.cjs +481 -0
  79. package/dist/use-account-clients-DoJW3KTx.cjs.map +1 -0
  80. package/dist/{use-current-user-DCk55_Qn.mjs → use-current-user-Baxj7HJt.mjs} +3 -3
  81. package/dist/{use-current-user-DCk55_Qn.mjs.map → use-current-user-Baxj7HJt.mjs.map} +1 -1
  82. package/dist/{use-current-user-BR5_zaoZ.cjs → use-current-user-BcZWV7oU.cjs} +3 -3
  83. package/dist/{use-current-user-BR5_zaoZ.cjs.map → use-current-user-BcZWV7oU.cjs.map} +1 -1
  84. package/dist/{use-fluid-api-CmCAH10d.mjs → use-fluid-api-BP05Cf-f.mjs} +2 -2
  85. package/dist/{use-fluid-api-CmCAH10d.mjs.map → use-fluid-api-BP05Cf-f.mjs.map} +1 -1
  86. package/dist/{use-fluid-api-C1KeHB7q.cjs → use-fluid-api-Ds8BInAZ.cjs} +2 -2
  87. package/dist/{use-fluid-api-C1KeHB7q.cjs.map → use-fluid-api-Ds8BInAZ.cjs.map} +1 -1
  88. package/dist/{use-fluid-auth-BQEV7ylM.mjs → use-fluid-auth-C-Qpl8j4.mjs} +2 -2
  89. package/dist/{use-fluid-auth-BQEV7ylM.mjs.map → use-fluid-auth-C-Qpl8j4.mjs.map} +1 -1
  90. package/dist/{use-fluid-auth-CyKaXLbW.cjs → use-fluid-auth-sGNMgfnt.cjs} +2 -2
  91. package/dist/{use-fluid-auth-CyKaXLbW.cjs.map → use-fluid-auth-sGNMgfnt.cjs.map} +1 -1
  92. package/dist/{use-portal-products-client-pptYMuSw.cjs → use-portal-products-client-DKYkBjm-.cjs} +7 -48
  93. package/dist/use-portal-products-client-DKYkBjm-.cjs.map +1 -0
  94. package/dist/{use-portal-products-client-BL1xVtex.mjs → use-portal-products-client-s2qtZjhU.mjs} +3 -44
  95. package/dist/use-portal-products-client-s2qtZjhU.mjs.map +1 -0
  96. package/dist/vite/index.cjs +44 -2
  97. package/dist/vite/index.cjs.map +1 -1
  98. package/dist/vite/index.d.cts +2 -2
  99. package/dist/vite/index.d.cts.map +1 -1
  100. package/dist/vite/index.d.mts +2 -2
  101. package/dist/vite/index.d.mts.map +1 -1
  102. package/dist/vite/index.mjs +44 -2
  103. package/dist/vite/index.mjs.map +1 -1
  104. package/package.json +15 -14
  105. package/dist/FluidProvider-BMMu_rp3.cjs.map +0 -1
  106. package/dist/FluidProvider-CgTeGUnF.mjs.map +0 -1
  107. package/dist/OrdersScreen-BLb3_KtI.mjs +0 -176
  108. package/dist/OrdersScreen-BLb3_KtI.mjs.map +0 -1
  109. package/dist/OrdersScreen-uL3mRk1h.cjs +0 -183
  110. package/dist/OrdersScreen-uL3mRk1h.cjs.map +0 -1
  111. package/dist/ProfileScreen-BKRn8AqI.mjs.map +0 -1
  112. package/dist/ProfileScreen-FYGHStqM.cjs.map +0 -1
  113. package/dist/SubscriptionsScreen-CZ-1jSO2.mjs.map +0 -1
  114. package/dist/SubscriptionsScreen-oApGaq11.cjs.map +0 -1
  115. package/dist/order-detail-DHXdE4Cl.cjs +0 -961
  116. package/dist/order-detail-DHXdE4Cl.cjs.map +0 -1
  117. package/dist/order-detail-iZm_R0TX.mjs +0 -931
  118. package/dist/order-detail-iZm_R0TX.mjs.map +0 -1
  119. package/dist/src-BJSTFxSO.mjs +0 -1
  120. package/dist/src-DMVR26Fk.cjs +0 -1
  121. package/dist/use-account-clients-CL6rr17o.cjs +0 -214
  122. package/dist/use-account-clients-CL6rr17o.cjs.map +0 -1
  123. package/dist/use-account-clients-CMjRB5On.mjs +0 -190
  124. package/dist/use-account-clients-CMjRB5On.mjs.map +0 -1
  125. package/dist/use-customer-account-BAolVc3q.mjs +0 -22
  126. package/dist/use-customer-account-BAolVc3q.mjs.map +0 -1
  127. package/dist/use-customer-account-DSsXbcme.cjs +0 -28
  128. package/dist/use-customer-account-DSsXbcme.cjs.map +0 -1
  129. package/dist/use-portal-products-client-BL1xVtex.mjs.map +0 -1
  130. package/dist/use-portal-products-client-pptYMuSw.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- const require_FluidProvider = require("./FluidProvider-BMMu_rp3.cjs");
1
+ const require_FluidProvider = require("./FluidProvider-DvqnkjZI.cjs");
2
2
  //#region src/hooks/use-fluid-api.ts
3
3
  /**
4
4
  * Hook to access the Fluid API client
@@ -29,4 +29,4 @@ Object.defineProperty(exports, "useFluidApi", {
29
29
  }
30
30
  });
31
31
 
32
- //# sourceMappingURL=use-fluid-api-C1KeHB7q.cjs.map
32
+ //# sourceMappingURL=use-fluid-api-Ds8BInAZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-fluid-api-C1KeHB7q.cjs","names":["useFluidContext"],"sources":["../src/hooks/use-fluid-api.ts"],"sourcesContent":["import { useFluidContext } from \"../providers/FluidProvider\";\nimport type { FluidClient } from \"../client/fluid-client\";\n\n/**\n * Hook to access the Fluid API client\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const api = useFluidApi();\n *\n * const { data: user } = useQuery({\n * queryKey: [\"me\"],\n * queryFn: () => api.users.me(),\n * });\n *\n * return <p>Hello, {user?.name}</p>;\n * }\n * ```\n */\nexport function useFluidApi(): FluidClient {\n const { client } = useFluidContext();\n return client;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,cAA2B;CACzC,MAAM,EAAE,WAAWA,sBAAAA,iBAAiB;AACpC,QAAO"}
1
+ {"version":3,"file":"use-fluid-api-Ds8BInAZ.cjs","names":["useFluidContext"],"sources":["../src/hooks/use-fluid-api.ts"],"sourcesContent":["import { useFluidContext } from \"../providers/FluidProvider\";\nimport type { FluidClient } from \"../client/fluid-client\";\n\n/**\n * Hook to access the Fluid API client\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const api = useFluidApi();\n *\n * const { data: user } = useQuery({\n * queryKey: [\"me\"],\n * queryFn: () => api.users.me(),\n * });\n *\n * return <p>Hello, {user?.name}</p>;\n * }\n * ```\n */\nexport function useFluidApi(): FluidClient {\n const { client } = useFluidContext();\n return client;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,cAA2B;CACzC,MAAM,EAAE,WAAWA,sBAAAA,iBAAiB;AACpC,QAAO"}
@@ -1,4 +1,4 @@
1
- import { l as useFluidAuthContext } from "./FluidProvider-CgTeGUnF.mjs";
1
+ import { l as useFluidAuthContext } from "./FluidProvider-BJQSXofR.mjs";
2
2
  //#region src/hooks/use-fluid-auth.ts
3
3
  /**
4
4
  * useFluidAuth Hook
@@ -42,4 +42,4 @@ function useFluidAuth() {
42
42
  //#endregion
43
43
  export { useFluidAuth as t };
44
44
 
45
- //# sourceMappingURL=use-fluid-auth-BQEV7ylM.mjs.map
45
+ //# sourceMappingURL=use-fluid-auth-C-Qpl8j4.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-fluid-auth-BQEV7ylM.mjs","names":[],"sources":["../src/hooks/use-fluid-auth.ts"],"sourcesContent":["/**\n * useFluidAuth Hook\n *\n * Provides access to authentication state and utilities.\n * This is the primary hook for interacting with auth in components.\n */\n\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport type { FluidAuthContextValue } from \"../auth/types\";\n\n/**\n * Hook to access authentication state and utilities.\n *\n * Must be used within a `FluidAuthProvider`.\n *\n * @returns Authentication context with user info, loading state, and utilities\n * @throws Error if used outside FluidAuthProvider\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { isAuthenticated, isLoading, user, clearAuth } = useFluidAuth();\n *\n * if (isLoading) {\n * return <Spinner />;\n * }\n *\n * if (!isAuthenticated) {\n * return <p>Please log in</p>;\n * }\n *\n * return (\n * <div>\n * <p>Welcome, {user.full_name}!</p>\n * <button onClick={clearAuth}>Log out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidAuth(): FluidAuthContextValue {\n return useFluidAuthContext();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,eAAsC;AACpD,QAAO,qBAAqB"}
1
+ {"version":3,"file":"use-fluid-auth-C-Qpl8j4.mjs","names":[],"sources":["../src/hooks/use-fluid-auth.ts"],"sourcesContent":["/**\n * useFluidAuth Hook\n *\n * Provides access to authentication state and utilities.\n * This is the primary hook for interacting with auth in components.\n */\n\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport type { FluidAuthContextValue } from \"../auth/types\";\n\n/**\n * Hook to access authentication state and utilities.\n *\n * Must be used within a `FluidAuthProvider`.\n *\n * @returns Authentication context with user info, loading state, and utilities\n * @throws Error if used outside FluidAuthProvider\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { isAuthenticated, isLoading, user, clearAuth } = useFluidAuth();\n *\n * if (isLoading) {\n * return <Spinner />;\n * }\n *\n * if (!isAuthenticated) {\n * return <p>Please log in</p>;\n * }\n *\n * return (\n * <div>\n * <p>Welcome, {user.full_name}!</p>\n * <button onClick={clearAuth}>Log out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidAuth(): FluidAuthContextValue {\n return useFluidAuthContext();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,eAAsC;AACpD,QAAO,qBAAqB"}
@@ -1,4 +1,4 @@
1
- const require_FluidProvider = require("./FluidProvider-BMMu_rp3.cjs");
1
+ const require_FluidProvider = require("./FluidProvider-DvqnkjZI.cjs");
2
2
  //#region src/hooks/use-fluid-auth.ts
3
3
  /**
4
4
  * useFluidAuth Hook
@@ -47,4 +47,4 @@ Object.defineProperty(exports, "useFluidAuth", {
47
47
  }
48
48
  });
49
49
 
50
- //# sourceMappingURL=use-fluid-auth-CyKaXLbW.cjs.map
50
+ //# sourceMappingURL=use-fluid-auth-sGNMgfnt.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-fluid-auth-CyKaXLbW.cjs","names":["useFluidAuthContext"],"sources":["../src/hooks/use-fluid-auth.ts"],"sourcesContent":["/**\n * useFluidAuth Hook\n *\n * Provides access to authentication state and utilities.\n * This is the primary hook for interacting with auth in components.\n */\n\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport type { FluidAuthContextValue } from \"../auth/types\";\n\n/**\n * Hook to access authentication state and utilities.\n *\n * Must be used within a `FluidAuthProvider`.\n *\n * @returns Authentication context with user info, loading state, and utilities\n * @throws Error if used outside FluidAuthProvider\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { isAuthenticated, isLoading, user, clearAuth } = useFluidAuth();\n *\n * if (isLoading) {\n * return <Spinner />;\n * }\n *\n * if (!isAuthenticated) {\n * return <p>Please log in</p>;\n * }\n *\n * return (\n * <div>\n * <p>Welcome, {user.full_name}!</p>\n * <button onClick={clearAuth}>Log out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidAuth(): FluidAuthContextValue {\n return useFluidAuthContext();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,eAAsC;AACpD,QAAOA,sBAAAA,qBAAqB"}
1
+ {"version":3,"file":"use-fluid-auth-sGNMgfnt.cjs","names":["useFluidAuthContext"],"sources":["../src/hooks/use-fluid-auth.ts"],"sourcesContent":["/**\n * useFluidAuth Hook\n *\n * Provides access to authentication state and utilities.\n * This is the primary hook for interacting with auth in components.\n */\n\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport type { FluidAuthContextValue } from \"../auth/types\";\n\n/**\n * Hook to access authentication state and utilities.\n *\n * Must be used within a `FluidAuthProvider`.\n *\n * @returns Authentication context with user info, loading state, and utilities\n * @throws Error if used outside FluidAuthProvider\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { isAuthenticated, isLoading, user, clearAuth } = useFluidAuth();\n *\n * if (isLoading) {\n * return <Spinner />;\n * }\n *\n * if (!isAuthenticated) {\n * return <p>Please log in</p>;\n * }\n *\n * return (\n * <div>\n * <p>Welcome, {user.full_name}!</p>\n * <button onClick={clearAuth}>Log out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidAuth(): FluidAuthContextValue {\n return useFluidAuthContext();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,eAAsC;AACpD,QAAOA,sBAAAA,qBAAqB"}
@@ -1,48 +1,7 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- const require_FluidProvider = require("./FluidProvider-BMMu_rp3.cjs");
2
+ const require_FluidProvider = require("./FluidProvider-DvqnkjZI.cjs");
3
+ const require_portal_tenant = require("./portal_tenant-CP5Ce8Jn.cjs");
3
4
  let react = require("react");
4
- //#region ../../api-clients/portal-tenant/src/namespaces/portal_tenant.ts
5
- /**
6
- * List products
7
- * Returns a paginated list of products available in the tenant company's catalog.
8
- *
9
- * @param client - Fetch client instance
10
- * @param [params] - params
11
- */
12
- async function products_list(client, params) {
13
- return client.get(`/api/products`, params);
14
- }
15
- /**
16
- * Get a product
17
- * Returns a single product by ID with its variants.
18
- *
19
- * @param client - Fetch client instance
20
- * @param id - id
21
- */
22
- async function products_show(client, id) {
23
- return client.get(`/api/products/${id}`);
24
- }
25
- /**
26
- * Search products
27
- * Searches the tenant company's product catalog by keyword, returning paginated results.
28
- *
29
- * @param client - Fetch client instance
30
- * @param [params] - params
31
- */
32
- async function products_search(client, params) {
33
- return client.get(`/api/products/search`, params);
34
- }
35
- /**
36
- * List product media
37
- * Returns all media (images, videos) attached to a product.
38
- *
39
- * @param client - Fetch client instance
40
- * @param id - id
41
- */
42
- async function products_media_list(client, id) {
43
- return client.get(`/api/products/${id}/media`);
44
- }
45
- //#endregion
46
5
  //#region ../../products/api-client/src/portal-tenant-adapter.ts
47
6
  /**
48
7
  * Creates a PortalProductsApi-compatible adapter from a portal-tenant FetchClient.
@@ -58,7 +17,7 @@ async function products_media_list(client, id) {
58
17
  function createPortalProductsApiAdapter(client) {
59
18
  return {
60
19
  listProducts: async (params) => {
61
- const response = await products_list(client, {
20
+ const response = await require_portal_tenant.products_list(client, {
62
21
  "page[cursor]": params?.cursor,
63
22
  "page[limit]": params?.limit
64
23
  });
@@ -68,14 +27,14 @@ function createPortalProductsApiAdapter(client) {
68
27
  };
69
28
  },
70
29
  getProduct: async (id) => {
71
- const response = await products_show(client, id);
30
+ const response = await require_portal_tenant.products_show(client, id);
72
31
  return {
73
32
  product: response.product ?? {},
74
33
  meta: response.meta
75
34
  };
76
35
  },
77
36
  searchProducts: async (query, params) => {
78
- const response = await products_search(client, {
37
+ const response = await require_portal_tenant.products_search(client, {
79
38
  q: query,
80
39
  "page[cursor]": params?.cursor,
81
40
  "page[limit]": params?.limit
@@ -86,7 +45,7 @@ function createPortalProductsApiAdapter(client) {
86
45
  };
87
46
  },
88
47
  getProductMedia: async (productId) => {
89
- const response = await products_media_list(client, productId);
48
+ const response = await require_portal_tenant.products_media_list(client, productId);
90
49
  return {
91
50
  media: response.media ?? [],
92
51
  meta: response.meta
@@ -113,4 +72,4 @@ Object.defineProperty(exports, "usePortalProductsClient", {
113
72
  }
114
73
  });
115
74
 
116
- //# sourceMappingURL=use-portal-products-client-pptYMuSw.cjs.map
75
+ //# sourceMappingURL=use-portal-products-client-DKYkBjm-.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-portal-products-client-DKYkBjm-.cjs","names":["useFluidContext","createFetchClient"],"sources":["../../../products/api-client/src/portal-tenant-adapter.ts","../src/products/use-portal-products-client.ts"],"sourcesContent":["import type {\n PortalProductsApi,\n portalProducts,\n} from \"@fluid-app/products-core\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenant } from \"@fluid-app/portal-tenant-api-client\";\n\n/**\n * Creates a PortalProductsApi-compatible adapter from a portal-tenant FetchClient.\n *\n * This bridges the auto-generated portal-tenant API client to the abstract\n * PortalProductsApi interface defined in @fluid-app/products-core, closing\n * over the FetchClient so consumers don't need to pass it per-call.\n *\n * Each method maps the BFF response to the port type at runtime rather than\n * using type assertions, so TypeScript catches schema drift between the\n * generated client and the hand-authored port types at compile time.\n */\nexport function createPortalProductsApiAdapter(\n client: FetchClient,\n): PortalProductsApi {\n return {\n listProducts: async (params) => {\n const response = await portalTenant.products_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProduct: async (id) => {\n const response = await portalTenant.products_show(client, id);\n return {\n product: response.product ?? {},\n meta: response.meta,\n } satisfies portalProducts.PortalProductResponse;\n },\n\n searchProducts: async (query, params) => {\n const response = await portalTenant.products_search(client, {\n q: query,\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProductMedia: async (productId) => {\n const response = await portalTenant.products_media_list(\n client,\n productId,\n );\n return {\n media: response.media ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductMediaResponse;\n },\n };\n}\n","import { useMemo } from \"react\";\nimport type { PortalProductsApi } from \"@fluid-app/products-core\";\nimport { createPortalProductsApiAdapter } from \"@fluid-app/products-api-client\";\nimport { createFetchClient } from \"@fluid-app/portal-tenant-api-client\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\n\nexport function usePortalProductsClient(): PortalProductsApi {\n const { config } = useFluidContext();\n\n return useMemo(() => {\n // Portal-tenant endpoints already include /api in their paths\n // (e.g. client.get('/api/products')), so the base URL must NOT\n // have /api appended — otherwise we get double /api/api.\n const client = createFetchClient({\n baseUrl: config.baseUrl.replace(/\\/+$/, \"\"),\n onAuthError: config.onAuthError,\n });\n return createPortalProductsApiAdapter(client);\n }, [config.baseUrl, config.onAuthError]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,QACmB;AACnB,QAAO;EACL,cAAc,OAAO,WAAW;GAC9B,MAAM,WAAW,MAAA,sBAAA,cAAiC,QAAQ;IACxD,gBAAgB,QAAQ;IACxB,eAAe,QAAQ;IACxB,CAAC;AACF,UAAO;IACL,UAAU,SAAS,YAAY,EAAE;IACjC,MAAM,SAAS;IAChB;;EAGH,YAAY,OAAO,OAAO;GACxB,MAAM,WAAW,MAAA,sBAAA,cAAiC,QAAQ,GAAG;AAC7D,UAAO;IACL,SAAS,SAAS,WAAW,EAAE;IAC/B,MAAM,SAAS;IAChB;;EAGH,gBAAgB,OAAO,OAAO,WAAW;GACvC,MAAM,WAAW,MAAA,sBAAA,gBAAmC,QAAQ;IAC1D,GAAG;IACH,gBAAgB,QAAQ;IACxB,eAAe,QAAQ;IACxB,CAAC;AACF,UAAO;IACL,UAAU,SAAS,YAAY,EAAE;IACjC,MAAM,SAAS;IAChB;;EAGH,iBAAiB,OAAO,cAAc;GACpC,MAAM,WAAW,MAAA,sBAAA,oBACf,QACA,UACD;AACD,UAAO;IACL,OAAO,SAAS,SAAS,EAAE;IAC3B,MAAM,SAAS;IAChB;;EAEJ;;;;ACzDH,SAAgB,0BAA6C;CAC3D,MAAM,EAAE,WAAWA,sBAAAA,iBAAiB;AAEpC,SAAA,GAAA,MAAA,eAAqB;AAQnB,SAAO,+BAJQC,sBAAAA,kBAAkB;GAC/B,SAAS,OAAO,QAAQ,QAAQ,QAAQ,GAAG;GAC3C,aAAa,OAAO;GACrB,CAAC,CAC2C;IAC5C,CAAC,OAAO,SAAS,OAAO,YAAY,CAAC"}
@@ -1,47 +1,6 @@
1
- import { ct as createFetchClient, n as useFluidContext } from "./FluidProvider-CgTeGUnF.mjs";
1
+ import { n as useFluidContext, rt as createFetchClient } from "./FluidProvider-BJQSXofR.mjs";
2
+ import { a as products_list, c as products_show, o as products_media_list, s as products_search } from "./portal_tenant-CWy4Zg2t.mjs";
2
3
  import { useMemo } from "react";
3
- //#region ../../api-clients/portal-tenant/src/namespaces/portal_tenant.ts
4
- /**
5
- * List products
6
- * Returns a paginated list of products available in the tenant company's catalog.
7
- *
8
- * @param client - Fetch client instance
9
- * @param [params] - params
10
- */
11
- async function products_list(client, params) {
12
- return client.get(`/api/products`, params);
13
- }
14
- /**
15
- * Get a product
16
- * Returns a single product by ID with its variants.
17
- *
18
- * @param client - Fetch client instance
19
- * @param id - id
20
- */
21
- async function products_show(client, id) {
22
- return client.get(`/api/products/${id}`);
23
- }
24
- /**
25
- * Search products
26
- * Searches the tenant company's product catalog by keyword, returning paginated results.
27
- *
28
- * @param client - Fetch client instance
29
- * @param [params] - params
30
- */
31
- async function products_search(client, params) {
32
- return client.get(`/api/products/search`, params);
33
- }
34
- /**
35
- * List product media
36
- * Returns all media (images, videos) attached to a product.
37
- *
38
- * @param client - Fetch client instance
39
- * @param id - id
40
- */
41
- async function products_media_list(client, id) {
42
- return client.get(`/api/products/${id}/media`);
43
- }
44
- //#endregion
45
4
  //#region ../../products/api-client/src/portal-tenant-adapter.ts
46
5
  /**
47
6
  * Creates a PortalProductsApi-compatible adapter from a portal-tenant FetchClient.
@@ -107,4 +66,4 @@ function usePortalProductsClient() {
107
66
  //#endregion
108
67
  export { usePortalProductsClient as t };
109
68
 
110
- //# sourceMappingURL=use-portal-products-client-BL1xVtex.mjs.map
69
+ //# sourceMappingURL=use-portal-products-client-s2qtZjhU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-portal-products-client-s2qtZjhU.mjs","names":["portalTenant.products_list","portalTenant.products_show","portalTenant.products_search","portalTenant.products_media_list"],"sources":["../../../products/api-client/src/portal-tenant-adapter.ts","../src/products/use-portal-products-client.ts"],"sourcesContent":["import type {\n PortalProductsApi,\n portalProducts,\n} from \"@fluid-app/products-core\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenant } from \"@fluid-app/portal-tenant-api-client\";\n\n/**\n * Creates a PortalProductsApi-compatible adapter from a portal-tenant FetchClient.\n *\n * This bridges the auto-generated portal-tenant API client to the abstract\n * PortalProductsApi interface defined in @fluid-app/products-core, closing\n * over the FetchClient so consumers don't need to pass it per-call.\n *\n * Each method maps the BFF response to the port type at runtime rather than\n * using type assertions, so TypeScript catches schema drift between the\n * generated client and the hand-authored port types at compile time.\n */\nexport function createPortalProductsApiAdapter(\n client: FetchClient,\n): PortalProductsApi {\n return {\n listProducts: async (params) => {\n const response = await portalTenant.products_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProduct: async (id) => {\n const response = await portalTenant.products_show(client, id);\n return {\n product: response.product ?? {},\n meta: response.meta,\n } satisfies portalProducts.PortalProductResponse;\n },\n\n searchProducts: async (query, params) => {\n const response = await portalTenant.products_search(client, {\n q: query,\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProductMedia: async (productId) => {\n const response = await portalTenant.products_media_list(\n client,\n productId,\n );\n return {\n media: response.media ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductMediaResponse;\n },\n };\n}\n","import { useMemo } from \"react\";\nimport type { PortalProductsApi } from \"@fluid-app/products-core\";\nimport { createPortalProductsApiAdapter } from \"@fluid-app/products-api-client\";\nimport { createFetchClient } from \"@fluid-app/portal-tenant-api-client\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\n\nexport function usePortalProductsClient(): PortalProductsApi {\n const { config } = useFluidContext();\n\n return useMemo(() => {\n // Portal-tenant endpoints already include /api in their paths\n // (e.g. client.get('/api/products')), so the base URL must NOT\n // have /api appended — otherwise we get double /api/api.\n const client = createFetchClient({\n baseUrl: config.baseUrl.replace(/\\/+$/, \"\"),\n onAuthError: config.onAuthError,\n });\n return createPortalProductsApiAdapter(client);\n }, [config.baseUrl, config.onAuthError]);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,QACmB;AACnB,QAAO;EACL,cAAc,OAAO,WAAW;GAC9B,MAAM,WAAW,MAAMA,cAA2B,QAAQ;IACxD,gBAAgB,QAAQ;IACxB,eAAe,QAAQ;IACxB,CAAC;AACF,UAAO;IACL,UAAU,SAAS,YAAY,EAAE;IACjC,MAAM,SAAS;IAChB;;EAGH,YAAY,OAAO,OAAO;GACxB,MAAM,WAAW,MAAMC,cAA2B,QAAQ,GAAG;AAC7D,UAAO;IACL,SAAS,SAAS,WAAW,EAAE;IAC/B,MAAM,SAAS;IAChB;;EAGH,gBAAgB,OAAO,OAAO,WAAW;GACvC,MAAM,WAAW,MAAMC,gBAA6B,QAAQ;IAC1D,GAAG;IACH,gBAAgB,QAAQ;IACxB,eAAe,QAAQ;IACxB,CAAC;AACF,UAAO;IACL,UAAU,SAAS,YAAY,EAAE;IACjC,MAAM,SAAS;IAChB;;EAGH,iBAAiB,OAAO,cAAc;GACpC,MAAM,WAAW,MAAMC,oBACrB,QACA,UACD;AACD,UAAO;IACL,OAAO,SAAS,SAAS,EAAE;IAC3B,MAAM,SAAS;IAChB;;EAEJ;;;;ACzDH,SAAgB,0BAA6C;CAC3D,MAAM,EAAE,WAAW,iBAAiB;AAEpC,QAAO,cAAc;AAQnB,SAAO,+BAJQ,kBAAkB;GAC/B,SAAS,OAAO,QAAQ,QAAQ,QAAQ,GAAG;GAC3C,aAAa,OAAO;GACrB,CAAC,CAC2C;IAC5C,CAAC,OAAO,SAAS,OAAO,YAAY,CAAC"}
@@ -279,12 +279,54 @@ function isAllowedOrigin(origin) {
279
279
  * Dev mode: serves `/__preview__` by transforming preview.html through
280
280
  * Vite's HTML pipeline (so import resolution and HMR work).
281
281
  *
282
- * Build mode: no-op — the production preview.html is handled by adding
283
- * it as a second Rollup input in vite.config.ts.
282
+ * Build mode: adds preview.html as a second Rollup input so `vite build`
283
+ * produces it alongside index.html for production deployment.
284
284
  */
285
285
  function fluidPreviewPlugin() {
286
286
  return {
287
287
  name: "fluid-preview-plugin",
288
+ config(_config, { command }) {
289
+ if (command !== "build") return;
290
+ const projectRoot = process.cwd();
291
+ const previewHtml = (0, node_path.resolve)(projectRoot, "preview.html");
292
+ if (!(0, node_fs.existsSync)(previewHtml)) return;
293
+ return { build: { rollupOptions: { input: {
294
+ main: (0, node_path.resolve)(projectRoot, "index.html"),
295
+ preview: previewHtml
296
+ } } } };
297
+ },
298
+ configurePreviewServer(server) {
299
+ server.middlewares.use("/__preview__", async (req, res) => {
300
+ try {
301
+ const distDir = server.config.build.outDir;
302
+ const html = await (0, node_fs_promises.readFile)((0, node_path.resolve)(distDir, "preview.html"), "utf-8");
303
+ res.setHeader("Content-Type", "text/html");
304
+ const origin = req.headers.origin;
305
+ if (origin && isAllowedOrigin(origin)) {
306
+ res.setHeader("Access-Control-Allow-Origin", origin);
307
+ res.setHeader("Vary", "Origin");
308
+ }
309
+ res.statusCode = 200;
310
+ res.end(html);
311
+ } catch {
312
+ res.statusCode = 404;
313
+ res.end("preview.html not found in build output");
314
+ }
315
+ });
316
+ server.middlewares.use("/__manifests__", async (_req, res) => {
317
+ try {
318
+ const distDir = server.config.build.outDir;
319
+ const json = await (0, node_fs_promises.readFile)((0, node_path.resolve)(distDir, "__manifests__.json"), "utf-8");
320
+ res.setHeader("Content-Type", "application/json");
321
+ res.setHeader("Access-Control-Allow-Origin", "*");
322
+ res.statusCode = 200;
323
+ res.end(json);
324
+ } catch {
325
+ res.statusCode = 404;
326
+ res.end("__manifests__.json not found in build output");
327
+ }
328
+ });
329
+ },
288
330
  configureServer(server) {
289
331
  server.middlewares.use("/__preview__", async (req, res) => {
290
332
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../../core/src/preview/protocol.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\";\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n ];\n configPath =\n \"/\" +\n (candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\");\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets export.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\")) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","/**\n * PostMessage protocol for builder ↔ iframe widget preview communication.\n *\n * Four-step handshake:\n * 1. Iframe sends `ready` once mounted\n * 2. Builder sends `auth` with token (NEVER via URL)\n * 3. Iframe sends `auth-ready` once FluidProvider settles\n * 4. Builder sends `render-widget` with type + props\n *\n * This prevents the race condition where render-widget arrives\n * before the provider stack has initialized with the auth token.\n */\n\n// ---------------------------------------------------------------------------\n// Builder → Iframe commands\n// ---------------------------------------------------------------------------\n\nexport interface AuthCommand {\n type: \"auth\";\n token: string;\n}\n\nexport interface RenderWidgetCommand {\n type: \"render-widget\";\n widgetType: string;\n props: Record<string, unknown>;\n}\n\nexport interface UnmountWidgetCommand {\n type: \"unmount-widget\";\n}\n\nexport interface SelectWidgetCommand {\n type: \"select-widget\";\n widgetType: string;\n}\n\nexport type PreviewCommand =\n | AuthCommand\n | RenderWidgetCommand\n | UnmountWidgetCommand\n | SelectWidgetCommand;\n\n// ---------------------------------------------------------------------------\n// Iframe → Builder events\n// ---------------------------------------------------------------------------\n\nexport interface ReadyEvent {\n type: \"ready\";\n}\n\nexport interface AuthReadyEvent {\n type: \"auth-ready\";\n}\n\nexport interface WidgetRenderedEvent {\n type: \"widget-rendered\";\n widgetType: string;\n dimensions: { width: number; height: number };\n}\n\nexport interface WidgetErrorEvent {\n type: \"widget-error\";\n widgetType: string;\n error: string;\n}\n\nexport interface WidgetClickedEvent {\n type: \"widget-clicked\";\n widgetType: string;\n}\n\nexport interface WidgetHmrEvent {\n type: \"widget-hmr\";\n}\n\nexport type PreviewEvent =\n | ReadyEvent\n | AuthReadyEvent\n | WidgetRenderedEvent\n | WidgetErrorEvent\n | WidgetClickedEvent\n | WidgetHmrEvent;\n\n// ---------------------------------------------------------------------------\n// Origin validation\n// ---------------------------------------------------------------------------\n\nconst ALLOWED_ORIGIN_PATTERNS = [\n /^https?:\\/\\/localhost(:\\d+)?$/,\n /^https?:\\/\\/127\\.0\\.0\\.1(:\\d+)?$/,\n /^https:\\/\\/[a-z0-9-]+\\.portal\\.fluid\\.app$/,\n /^https:\\/\\/[a-z0-9-]+\\.fluid\\.app$/,\n];\n\n/**\n * Validate that a postMessage origin is trusted.\n * Accepts localhost (any port), *.portal.fluid.app, and *.fluid.app.\n */\nexport function isAllowedOrigin(origin: string): boolean {\n return ALLOWED_ORIGIN_PATTERNS.some((pattern) => pattern.test(origin));\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\n/** Check if a message is a valid PreviewCommand (builder → iframe). */\nexport function isPreviewCommand(data: unknown): data is PreviewCommand {\n if (typeof data !== \"object\" || data === null) return false;\n const msg = data as Record<string, unknown>;\n switch (msg[\"type\"]) {\n case \"auth\":\n return typeof msg[\"token\"] === \"string\";\n case \"render-widget\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"props\"] === \"object\" &&\n msg[\"props\"] !== null &&\n !Array.isArray(msg[\"props\"])\n );\n case \"unmount-widget\":\n return true;\n case \"select-widget\":\n return typeof msg[\"widgetType\"] === \"string\";\n default:\n return false;\n }\n}\n\n/** Check if a message is a valid PreviewEvent (iframe → builder). */\nexport function isPreviewEvent(data: unknown): data is PreviewEvent {\n if (typeof data !== \"object\" || data === null) return false;\n const msg = data as Record<string, unknown>;\n switch (msg[\"type\"]) {\n case \"ready\":\n case \"auth-ready\":\n case \"widget-hmr\":\n return true;\n case \"widget-rendered\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"dimensions\"] === \"object\" &&\n msg[\"dimensions\"] !== null &&\n Number.isFinite(\n (msg[\"dimensions\"] as Record<string, unknown>)[\"width\"],\n ) &&\n Number.isFinite(\n (msg[\"dimensions\"] as Record<string, unknown>)[\"height\"],\n )\n );\n case \"widget-error\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"error\"] === \"string\"\n );\n case \"widget-clicked\":\n return typeof msg[\"widgetType\"] === \"string\";\n default:\n return false;\n }\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport type { Plugin } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: no-op — the production preview.html is handled by adding\n * it as a second Rollup input in vite.config.ts.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts","../../../core/src/preview/protocol.ts","../../src/vite/preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\";\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n ];\n configPath =\n \"/\" +\n (candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\");\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from the resolved widget config\n * (widgets.config.ts or portal.config.ts) via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n configFilePath?: string,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(configFilePath ?? \"/src/portal.config.ts\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const isSSRError =\n /(document|window|navigator|localStorage|sessionStorage|location|history|HTMLElement) is not defined/.test(\n message,\n );\n\n if (isSSRError) {\n const configFile = configFilePath ?? \"/src/portal.config.ts\";\n const isPortalConfig = configFile.includes(\"portal.config\");\n const fixHint = isPortalConfig\n ? ` Fix: Create src/widgets.config.ts with only your customWidgets export.\\n` +\n ` The manifest plugin will load it instead of portal.config.ts.\\n`\n : ` Fix: Ensure ${configFile} does not import browser-only code.\\n`;\n\n logger?.warn(\n `[fluid] Cannot load widget manifests — ${configFile} imports ` +\n `browser-only code that fails during server-side evaluation.\\n` +\n ` Custom widgets will not appear in the builder.\\n` +\n fixHint +\n ` Widget components are fine — the issue is usually screen imports\\n` +\n ` (e.g. DashboardScreen) that pull in the SDK barrel export.`,\n );\n } else {\n logger?.warn(`[fluid] Could not load widget config: ${message}`);\n }\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) {\n if (configFilePath?.includes(\"widgets.config\")) {\n logger?.warn(\n `[fluid] widgets.config.ts was loaded but exports no customWidgets. ` +\n `Custom widgets will not appear in the builder.`,\n );\n }\n return [];\n }\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","/**\n * PostMessage protocol for builder ↔ iframe widget preview communication.\n *\n * Four-step handshake:\n * 1. Iframe sends `ready` once mounted\n * 2. Builder sends `auth` with token (NEVER via URL)\n * 3. Iframe sends `auth-ready` once FluidProvider settles\n * 4. Builder sends `render-widget` with type + props\n *\n * This prevents the race condition where render-widget arrives\n * before the provider stack has initialized with the auth token.\n */\n\n// ---------------------------------------------------------------------------\n// Builder → Iframe commands\n// ---------------------------------------------------------------------------\n\nexport interface AuthCommand {\n type: \"auth\";\n token: string;\n}\n\nexport interface RenderWidgetCommand {\n type: \"render-widget\";\n widgetType: string;\n props: Record<string, unknown>;\n}\n\nexport interface UnmountWidgetCommand {\n type: \"unmount-widget\";\n}\n\nexport interface SelectWidgetCommand {\n type: \"select-widget\";\n widgetType: string;\n}\n\nexport type PreviewCommand =\n | AuthCommand\n | RenderWidgetCommand\n | UnmountWidgetCommand\n | SelectWidgetCommand;\n\n// ---------------------------------------------------------------------------\n// Iframe → Builder events\n// ---------------------------------------------------------------------------\n\nexport interface ReadyEvent {\n type: \"ready\";\n}\n\nexport interface AuthReadyEvent {\n type: \"auth-ready\";\n}\n\nexport interface WidgetRenderedEvent {\n type: \"widget-rendered\";\n widgetType: string;\n dimensions: { width: number; height: number };\n}\n\nexport interface WidgetErrorEvent {\n type: \"widget-error\";\n widgetType: string;\n error: string;\n}\n\nexport interface WidgetClickedEvent {\n type: \"widget-clicked\";\n widgetType: string;\n}\n\nexport interface WidgetHmrEvent {\n type: \"widget-hmr\";\n}\n\nexport type PreviewEvent =\n | ReadyEvent\n | AuthReadyEvent\n | WidgetRenderedEvent\n | WidgetErrorEvent\n | WidgetClickedEvent\n | WidgetHmrEvent;\n\n// ---------------------------------------------------------------------------\n// Origin validation\n// ---------------------------------------------------------------------------\n\nconst ALLOWED_ORIGIN_PATTERNS = [\n /^https?:\\/\\/localhost(:\\d+)?$/,\n /^https?:\\/\\/127\\.0\\.0\\.1(:\\d+)?$/,\n /^https:\\/\\/[a-z0-9-]+\\.portal\\.fluid\\.app$/,\n /^https:\\/\\/[a-z0-9-]+\\.fluid\\.app$/,\n];\n\n/**\n * Validate that a postMessage origin is trusted.\n * Accepts localhost (any port), *.portal.fluid.app, and *.fluid.app.\n */\nexport function isAllowedOrigin(origin: string): boolean {\n return ALLOWED_ORIGIN_PATTERNS.some((pattern) => pattern.test(origin));\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\n/** Check if a message is a valid PreviewCommand (builder → iframe). */\nexport function isPreviewCommand(data: unknown): data is PreviewCommand {\n if (typeof data !== \"object\" || data === null) return false;\n const msg = data as Record<string, unknown>;\n switch (msg[\"type\"]) {\n case \"auth\":\n return typeof msg[\"token\"] === \"string\";\n case \"render-widget\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"props\"] === \"object\" &&\n msg[\"props\"] !== null &&\n !Array.isArray(msg[\"props\"])\n );\n case \"unmount-widget\":\n return true;\n case \"select-widget\":\n return typeof msg[\"widgetType\"] === \"string\";\n default:\n return false;\n }\n}\n\n/** Check if a message is a valid PreviewEvent (iframe → builder). */\nexport function isPreviewEvent(data: unknown): data is PreviewEvent {\n if (typeof data !== \"object\" || data === null) return false;\n const msg = data as Record<string, unknown>;\n switch (msg[\"type\"]) {\n case \"ready\":\n case \"auth-ready\":\n case \"widget-hmr\":\n return true;\n case \"widget-rendered\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"dimensions\"] === \"object\" &&\n msg[\"dimensions\"] !== null &&\n Number.isFinite(\n (msg[\"dimensions\"] as Record<string, unknown>)[\"width\"],\n ) &&\n Number.isFinite(\n (msg[\"dimensions\"] as Record<string, unknown>)[\"height\"],\n )\n );\n case \"widget-error\":\n return (\n typeof msg[\"widgetType\"] === \"string\" &&\n typeof msg[\"error\"] === \"string\"\n );\n case \"widget-clicked\":\n return typeof msg[\"widgetType\"] === \"string\";\n default:\n return false;\n }\n}\n","import { resolve } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\n\n/**\n * Vite plugin that serves the widget preview route.\n *\n * Dev mode: serves `/__preview__` by transforming preview.html through\n * Vite's HTML pipeline (so import resolution and HMR work).\n *\n * Build mode: adds preview.html as a second Rollup input so `vite build`\n * produces it alongside index.html for production deployment.\n */\nexport function fluidPreviewPlugin(): Plugin {\n return {\n name: \"fluid-preview-plugin\",\n\n config(_config: UserConfig, { command }) {\n if (command !== \"build\") return;\n\n // config() runs before configResolved, so use process.cwd()\n // which is the project root when invoked via `vite build`.\n const projectRoot = process.cwd();\n const previewHtml = resolve(projectRoot, \"preview.html\");\n if (!existsSync(previewHtml)) return;\n\n // Add preview.html as a second Rollup entry point\n return {\n build: {\n rollupOptions: {\n input: {\n main: resolve(projectRoot, \"index.html\"),\n preview: previewHtml,\n },\n },\n },\n };\n },\n\n // vite preview: static file server with no rewrite rules.\n // Map /__preview__ → preview.html and /__manifests__ → __manifests__.json\n // so local production testing works the same as nginx in production.\n configurePreviewServer(server) {\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const html = await readFile(\n resolve(distDir, \"preview.html\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch {\n res.statusCode = 404;\n res.end(\"preview.html not found in build output\");\n }\n });\n\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const distDir = server.config.build.outDir;\n const json = await readFile(\n resolve(distDir, \"__manifests__.json\"),\n \"utf-8\",\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.statusCode = 200;\n res.end(json);\n } catch {\n res.statusCode = 404;\n res.end(\"__manifests__.json not found in build output\");\n }\n });\n },\n\n // vite dev: serve /__preview__ with Vite's HTML transform pipeline\n configureServer(server) {\n // Serve the preview HTML shell at /__preview__\n server.middlewares.use(\"/__preview__\", async (req, res) => {\n try {\n // Read the preview.html from the project root (copied from template)\n const htmlPath = resolve(server.config.root, \"preview.html\");\n let html: string;\n try {\n html = await readFile(htmlPath, \"utf-8\");\n } catch {\n // Fallback: serve a minimal preview shell\n html = getDefaultPreviewHtml();\n }\n\n // Transform through Vite's HTML pipeline for HMR + import resolution\n html = await server.transformIndexHtml(\"/__preview__\", html);\n\n res.setHeader(\"Content-Type\", \"text/html\");\n const origin = req.headers.origin;\n if (origin && isAllowedOrigin(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.statusCode = 200;\n res.end(html);\n } catch (err) {\n server.config.logger.error(`[fluid] Failed to serve preview: ${err}`);\n res.statusCode = 500;\n res.end(\"Preview failed to load\");\n }\n });\n },\n };\n}\n\nfunction getDefaultPreviewHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Widget Preview</title>\n <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n <style>body { margin: 0; padding: 0; }</style>\n </head>\n <body>\n <div id=\"preview-root\"></div>\n <script type=\"module\" src=\"/src/preview-entry.tsx\"></script>\n </body>\n</html>`;\n}\n"],"mappings":"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"}
@@ -28,8 +28,8 @@ declare function fluidBuilderPreviewPlugin(): Plugin;
28
28
  * Dev mode: serves `/__preview__` by transforming preview.html through
29
29
  * Vite's HTML pipeline (so import resolution and HMR work).
30
30
  *
31
- * Build mode: no-op — the production preview.html is handled by adding
32
- * it as a second Rollup input in vite.config.ts.
31
+ * Build mode: adds preview.html as a second Rollup input so `vite build`
32
+ * produces it alongside index.html for production deployment.
33
33
  */
34
34
  declare function fluidPreviewPlugin(): Plugin;
35
35
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA;;;;;ADd7C;;;;;;;iBEDgB,kBAAA,CAAA,GAAsB,MAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA;;;;;ADd7C;;;;;;;iBEAgB,kBAAA,CAAA,GAAsB,MAAA"}
@@ -28,8 +28,8 @@ declare function fluidBuilderPreviewPlugin(): Plugin;
28
28
  * Dev mode: serves `/__preview__` by transforming preview.html through
29
29
  * Vite's HTML pipeline (so import resolution and HMR work).
30
30
  *
31
- * Build mode: no-op — the production preview.html is handled by adding
32
- * it as a second Rollup input in vite.config.ts.
31
+ * Build mode: adds preview.html as a second Rollup input so `vite build`
32
+ * produces it alongside index.html for production deployment.
33
33
  */
34
34
  declare function fluidPreviewPlugin(): Plugin;
35
35
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA;;;;;ADd7C;;;;;;;iBEDgB,kBAAA,CAAA,GAAsB,MAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA;;;;;ADd7C;;;;;;;iBEAgB,kBAAA,CAAA,GAAsB,MAAA"}
@@ -277,12 +277,54 @@ function isAllowedOrigin(origin) {
277
277
  * Dev mode: serves `/__preview__` by transforming preview.html through
278
278
  * Vite's HTML pipeline (so import resolution and HMR work).
279
279
  *
280
- * Build mode: no-op — the production preview.html is handled by adding
281
- * it as a second Rollup input in vite.config.ts.
280
+ * Build mode: adds preview.html as a second Rollup input so `vite build`
281
+ * produces it alongside index.html for production deployment.
282
282
  */
283
283
  function fluidPreviewPlugin() {
284
284
  return {
285
285
  name: "fluid-preview-plugin",
286
+ config(_config, { command }) {
287
+ if (command !== "build") return;
288
+ const projectRoot = process.cwd();
289
+ const previewHtml = resolve(projectRoot, "preview.html");
290
+ if (!existsSync(previewHtml)) return;
291
+ return { build: { rollupOptions: { input: {
292
+ main: resolve(projectRoot, "index.html"),
293
+ preview: previewHtml
294
+ } } } };
295
+ },
296
+ configurePreviewServer(server) {
297
+ server.middlewares.use("/__preview__", async (req, res) => {
298
+ try {
299
+ const distDir = server.config.build.outDir;
300
+ const html = await readFile(resolve(distDir, "preview.html"), "utf-8");
301
+ res.setHeader("Content-Type", "text/html");
302
+ const origin = req.headers.origin;
303
+ if (origin && isAllowedOrigin(origin)) {
304
+ res.setHeader("Access-Control-Allow-Origin", origin);
305
+ res.setHeader("Vary", "Origin");
306
+ }
307
+ res.statusCode = 200;
308
+ res.end(html);
309
+ } catch {
310
+ res.statusCode = 404;
311
+ res.end("preview.html not found in build output");
312
+ }
313
+ });
314
+ server.middlewares.use("/__manifests__", async (_req, res) => {
315
+ try {
316
+ const distDir = server.config.build.outDir;
317
+ const json = await readFile(resolve(distDir, "__manifests__.json"), "utf-8");
318
+ res.setHeader("Content-Type", "application/json");
319
+ res.setHeader("Access-Control-Allow-Origin", "*");
320
+ res.statusCode = 200;
321
+ res.end(json);
322
+ } catch {
323
+ res.statusCode = 404;
324
+ res.end("__manifests__.json not found in build output");
325
+ }
326
+ });
327
+ },
286
328
  configureServer(server) {
287
329
  server.middlewares.use("/__preview__", async (req, res) => {
288
330
  try {