@wilshop/dashboard 3.5.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 (697) hide show
  1. package/README.md +116 -0
  2. package/dist/plugin/api/api-extensions.d.ts +1 -0
  3. package/dist/plugin/api/api-extensions.js +35 -0
  4. package/dist/plugin/api/metrics.resolver.d.ts +8 -0
  5. package/dist/plugin/api/metrics.resolver.js +40 -0
  6. package/dist/plugin/config/metrics-strategies.d.ts +39 -0
  7. package/dist/plugin/config/metrics-strategies.js +74 -0
  8. package/dist/plugin/constants.d.ts +8 -0
  9. package/dist/plugin/constants.js +31 -0
  10. package/dist/plugin/dashboard.plugin.d.ts +115 -0
  11. package/dist/plugin/dashboard.plugin.js +339 -0
  12. package/dist/plugin/default-page.html +193 -0
  13. package/dist/plugin/index.d.ts +2 -0
  14. package/dist/plugin/index.js +18 -0
  15. package/dist/plugin/package.json +3 -0
  16. package/dist/plugin/service/metrics.service.d.ts +15 -0
  17. package/dist/plugin/service/metrics.service.js +129 -0
  18. package/dist/plugin/types.d.ts +20 -0
  19. package/dist/plugin/types.js +9 -0
  20. package/dist/vite/constants.d.ts +5 -0
  21. package/dist/vite/constants.js +278 -0
  22. package/dist/vite/index.d.ts +1 -0
  23. package/dist/vite/index.js +1 -0
  24. package/dist/vite/types.d.ts +79 -0
  25. package/dist/vite/types.js +1 -0
  26. package/dist/vite/utils/ast-utils.d.ts +5 -0
  27. package/dist/vite/utils/ast-utils.js +29 -0
  28. package/dist/vite/utils/ast-utils.spec.d.ts +1 -0
  29. package/dist/vite/utils/ast-utils.spec.js +45 -0
  30. package/dist/vite/utils/compiler.d.ts +23 -0
  31. package/dist/vite/utils/compiler.js +165 -0
  32. package/dist/vite/utils/get-dashboard-paths.d.ts +5 -0
  33. package/dist/vite/utils/get-dashboard-paths.js +20 -0
  34. package/dist/vite/utils/logger.d.ts +3 -0
  35. package/dist/vite/utils/logger.js +39 -0
  36. package/dist/vite/utils/plugin-discovery.d.ts +27 -0
  37. package/dist/vite/utils/plugin-discovery.js +398 -0
  38. package/dist/vite/utils/schema-generator.d.ts +10 -0
  39. package/dist/vite/utils/schema-generator.js +35 -0
  40. package/dist/vite/utils/tsconfig-utils.d.ts +9 -0
  41. package/dist/vite/utils/tsconfig-utils.js +51 -0
  42. package/dist/vite/utils/ui-config.d.ts +3 -0
  43. package/dist/vite/utils/ui-config.js +30 -0
  44. package/dist/vite/vite-plugin-admin-api-schema.d.ts +24 -0
  45. package/dist/vite/vite-plugin-admin-api-schema.js +82 -0
  46. package/dist/vite/vite-plugin-config-loader.d.ts +16 -0
  47. package/dist/vite/vite-plugin-config-loader.js +70 -0
  48. package/dist/vite/vite-plugin-config.d.ts +4 -0
  49. package/dist/vite/vite-plugin-config.js +60 -0
  50. package/dist/vite/vite-plugin-dashboard-metadata.d.ts +7 -0
  51. package/dist/vite/vite-plugin-dashboard-metadata.js +61 -0
  52. package/dist/vite/vite-plugin-gql-tada.d.ts +6 -0
  53. package/dist/vite/vite-plugin-gql-tada.js +51 -0
  54. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  55. package/dist/vite/vite-plugin-hmr.js +34 -0
  56. package/dist/vite/vite-plugin-lingui-babel.d.ts +27 -0
  57. package/dist/vite/vite-plugin-lingui-babel.js +86 -0
  58. package/dist/vite/vite-plugin-tailwind-source.d.ts +7 -0
  59. package/dist/vite/vite-plugin-tailwind-source.js +48 -0
  60. package/dist/vite/vite-plugin-theme.d.ts +55 -0
  61. package/dist/vite/vite-plugin-theme.js +130 -0
  62. package/dist/vite/vite-plugin-transform-index.d.ts +11 -0
  63. package/dist/vite/vite-plugin-transform-index.js +40 -0
  64. package/dist/vite/vite-plugin-translations.d.ts +31 -0
  65. package/dist/vite/vite-plugin-translations.js +177 -0
  66. package/dist/vite/vite-plugin-ui-config.d.ts +150 -0
  67. package/dist/vite/vite-plugin-ui-config.js +36 -0
  68. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +142 -0
  69. package/dist/vite/vite-plugin-vendure-dashboard.js +170 -0
  70. package/index.html +14 -0
  71. package/lingui.config.js +37 -0
  72. package/package.json +178 -0
  73. package/src/app/app-providers.tsx +31 -0
  74. package/src/app/common/delete-bulk-action.tsx +149 -0
  75. package/src/app/common/duplicate-bulk-action.tsx +147 -0
  76. package/src/app/common/duplicate-entity-dialog.tsx +117 -0
  77. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  78. package/src/app/common/set-document-direction.ts +7 -0
  79. package/src/app/common/use-page-title.test.ts +263 -0
  80. package/src/app/common/use-page-title.ts +86 -0
  81. package/src/app/main.tsx +134 -0
  82. package/src/app/routeTree.gen.ts +1284 -0
  83. package/src/app/routes/__root.tsx +23 -0
  84. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +88 -0
  85. package/src/app/routes/_authenticated/_administrators/administrators.tsx +104 -0
  86. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +160 -0
  87. package/src/app/routes/_authenticated/_administrators/components/administrator-bulk-actions.tsx +15 -0
  88. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +133 -0
  89. package/src/app/routes/_authenticated/_assets/assets.graphql.ts +76 -0
  90. package/src/app/routes/_authenticated/_assets/assets.tsx +48 -0
  91. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +181 -0
  92. package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +47 -0
  93. package/src/app/routes/_authenticated/_assets/components/asset-tag-filter.tsx +206 -0
  94. package/src/app/routes/_authenticated/_assets/components/asset-tags-editor.tsx +226 -0
  95. package/src/app/routes/_authenticated/_assets/components/manage-tags-dialog.tsx +212 -0
  96. package/src/app/routes/_authenticated/_channels/channels.graphql.ts +102 -0
  97. package/src/app/routes/_authenticated/_channels/channels.tsx +81 -0
  98. package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +251 -0
  99. package/src/app/routes/_authenticated/_channels/components/channel-bulk-actions.tsx +15 -0
  100. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +193 -0
  101. package/src/app/routes/_authenticated/_collections/collections.tsx +337 -0
  102. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +236 -0
  103. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +130 -0
  104. package/src/app/routes/_authenticated/_collections/components/collection-contents-preview-table.tsx +153 -0
  105. package/src/app/routes/_authenticated/_collections/components/collection-contents-sheet.tsx +50 -0
  106. package/src/app/routes/_authenticated/_collections/components/collection-contents-table.tsx +83 -0
  107. package/src/app/routes/_authenticated/_collections/components/collection-filters-selector.tsx +24 -0
  108. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +433 -0
  109. package/src/app/routes/_authenticated/_countries/components/country-bulk-actions.tsx +15 -0
  110. package/src/app/routes/_authenticated/_countries/countries.graphql.ts +82 -0
  111. package/src/app/routes/_authenticated/_countries/countries.tsx +72 -0
  112. package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +123 -0
  113. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-bulk-actions.tsx +15 -0
  114. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-sheet.tsx +51 -0
  115. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-table.tsx +134 -0
  116. package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +80 -0
  117. package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +72 -0
  118. package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +121 -0
  119. package/src/app/routes/_authenticated/_customers/components/customer-address-card.tsx +155 -0
  120. package/src/app/routes/_authenticated/_customers/components/customer-address-form.tsx +347 -0
  121. package/src/app/routes/_authenticated/_customers/components/customer-bulk-actions.tsx +15 -0
  122. package/src/app/routes/_authenticated/_customers/components/customer-group-controls.tsx +4 -0
  123. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +78 -0
  124. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-types.ts +5 -0
  125. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-utils.tsx +124 -0
  126. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history.tsx +113 -0
  127. package/src/app/routes/_authenticated/_customers/components/customer-history/default-customer-history-components.tsx +176 -0
  128. package/src/app/routes/_authenticated/_customers/components/customer-history/index.ts +4 -0
  129. package/src/app/routes/_authenticated/_customers/components/customer-history/use-customer-history.ts +169 -0
  130. package/src/app/routes/_authenticated/_customers/components/customer-order-table.tsx +90 -0
  131. package/src/app/routes/_authenticated/_customers/components/customer-status-badge.tsx +33 -0
  132. package/src/app/routes/_authenticated/_customers/customers.graphql.ts +218 -0
  133. package/src/app/routes/_authenticated/_customers/customers.tsx +104 -0
  134. package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +280 -0
  135. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +99 -0
  136. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  137. package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +49 -0
  138. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +139 -0
  139. package/src/app/routes/_authenticated/_facets/facets.graphql.ts +182 -0
  140. package/src/app/routes/_authenticated/_facets/facets.tsx +121 -0
  141. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +158 -0
  142. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +152 -0
  143. package/src/app/routes/_authenticated/_global-settings/global-settings.graphql.ts +28 -0
  144. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +161 -0
  145. package/src/app/routes/_authenticated/_orders/components/add-manual-payment-dialog.tsx +189 -0
  146. package/src/app/routes/_authenticated/_orders/components/add-surcharge-form.tsx +139 -0
  147. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +100 -0
  148. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +287 -0
  149. package/src/app/routes/_authenticated/_orders/components/fulfill-order-dialog.tsx +320 -0
  150. package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +159 -0
  151. package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +20 -0
  152. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +65 -0
  153. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +307 -0
  154. package/src/app/routes/_authenticated/_orders/components/order-history/default-order-history-components.tsx +98 -0
  155. package/src/app/routes/_authenticated/_orders/components/order-history/index.ts +3 -0
  156. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +80 -0
  157. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-types.ts +5 -0
  158. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-utils.tsx +173 -0
  159. package/src/app/routes/_authenticated/_orders/components/order-history/order-history.tsx +104 -0
  160. package/src/app/routes/_authenticated/_orders/components/order-history/use-order-history.ts +175 -0
  161. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +53 -0
  162. package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +365 -0
  163. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +261 -0
  164. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +100 -0
  165. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +194 -0
  166. package/src/app/routes/_authenticated/_orders/components/order-tax-summary.tsx +40 -0
  167. package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +293 -0
  168. package/src/app/routes/_authenticated/_orders/components/seller-orders-card.tsx +63 -0
  169. package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +80 -0
  170. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +64 -0
  171. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +99 -0
  172. package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +151 -0
  173. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +775 -0
  174. package/src/app/routes/_authenticated/_orders/orders.tsx +120 -0
  175. package/src/app/routes/_authenticated/_orders/orders_.$aggregateOrderId_.seller-orders.$sellerOrderId.tsx +50 -0
  176. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +30 -0
  177. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +298 -0
  178. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +495 -0
  179. package/src/app/routes/_authenticated/_orders/utils/order-detail-loaders.tsx +134 -0
  180. package/src/app/routes/_authenticated/_orders/utils/order-types.ts +10 -0
  181. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +159 -0
  182. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +335 -0
  183. package/src/app/routes/_authenticated/_payment-methods/components/payment-eligibility-checker-selector.tsx +37 -0
  184. package/src/app/routes/_authenticated/_payment-methods/components/payment-handler-selector.tsx +33 -0
  185. package/src/app/routes/_authenticated/_payment-methods/components/payment-method-bulk-actions.tsx +58 -0
  186. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +110 -0
  187. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +88 -0
  188. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +197 -0
  189. package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +49 -0
  190. package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +56 -0
  191. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +110 -0
  192. package/src/app/routes/_authenticated/_product-variants/components/variant-price-detail.tsx +88 -0
  193. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +207 -0
  194. package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +98 -0
  195. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +471 -0
  196. package/src/app/routes/_authenticated/_products/components/add-option-group-dialog.tsx +127 -0
  197. package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +372 -0
  198. package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +282 -0
  199. package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +416 -0
  200. package/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx +190 -0
  201. package/src/app/routes/_authenticated/_products/components/create-product-variants.tsx +370 -0
  202. package/src/app/routes/_authenticated/_products/components/option-groups-editor.tsx +180 -0
  203. package/src/app/routes/_authenticated/_products/components/option-value-input.tsx +72 -0
  204. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +117 -0
  205. package/src/app/routes/_authenticated/_products/components/product-option-group-badge.tsx +19 -0
  206. package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +111 -0
  207. package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +114 -0
  208. package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +120 -0
  209. package/src/app/routes/_authenticated/_products/product-option-groups.graphql.ts +103 -0
  210. package/src/app/routes/_authenticated/_products/products.graphql.ts +352 -0
  211. package/src/app/routes/_authenticated/_products/products.tsx +121 -0
  212. package/src/app/routes/_authenticated/_products/products_.$id.tsx +258 -0
  213. package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +405 -0
  214. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +177 -0
  215. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +204 -0
  216. package/src/app/routes/_authenticated/_profile/profile.graphql.ts +23 -0
  217. package/src/app/routes/_authenticated/_profile/profile.tsx +119 -0
  218. package/src/app/routes/_authenticated/_promotions/components/promotion-actions-selector.tsx +35 -0
  219. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +72 -0
  220. package/src/app/routes/_authenticated/_promotions/components/promotion-conditions-selector.tsx +35 -0
  221. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +121 -0
  222. package/src/app/routes/_authenticated/_promotions/promotions.tsx +94 -0
  223. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +245 -0
  224. package/src/app/routes/_authenticated/_roles/components/expandable-permissions.tsx +54 -0
  225. package/src/app/routes/_authenticated/_roles/components/permissions-table-grid.tsx +251 -0
  226. package/src/app/routes/_authenticated/_roles/components/role-bulk-actions.tsx +15 -0
  227. package/src/app/routes/_authenticated/_roles/roles.graphql.ts +76 -0
  228. package/src/app/routes/_authenticated/_roles/roles.tsx +99 -0
  229. package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +146 -0
  230. package/src/app/routes/_authenticated/_sellers/components/seller-bulk-actions.tsx +15 -0
  231. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +70 -0
  232. package/src/app/routes/_authenticated/_sellers/sellers.tsx +56 -0
  233. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +112 -0
  234. package/src/app/routes/_authenticated/_shipping-methods/components/fulfillment-handler-selector.tsx +56 -0
  235. package/src/app/routes/_authenticated/_shipping-methods/components/metadata-badges.tsx +15 -0
  236. package/src/app/routes/_authenticated/_shipping-methods/components/price-display.tsx +21 -0
  237. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-calculator-selector.tsx +33 -0
  238. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-eligibility-checker-selector.tsx +36 -0
  239. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-bulk-actions.tsx +61 -0
  240. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-test-result-wrapper.tsx +87 -0
  241. package/src/app/routes/_authenticated/_shipping-methods/components/test-address-form.tsx +256 -0
  242. package/src/app/routes/_authenticated/_shipping-methods/components/test-order-builder.tsx +244 -0
  243. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-result.tsx +97 -0
  244. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-sheet.tsx +41 -0
  245. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods.tsx +74 -0
  246. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-method-result.tsx +90 -0
  247. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method-sheet.tsx +56 -0
  248. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method.tsx +82 -0
  249. package/src/app/routes/_authenticated/_shipping-methods/components/use-shipping-method-test-state.ts +67 -0
  250. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +137 -0
  251. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +76 -0
  252. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +197 -0
  253. package/src/app/routes/_authenticated/_stock-locations/components/stock-location-bulk-actions.tsx +58 -0
  254. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +87 -0
  255. package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +65 -0
  256. package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +126 -0
  257. package/src/app/routes/_authenticated/_system/components/payload-dialog.tsx +41 -0
  258. package/src/app/routes/_authenticated/_system/healthchecks.tsx +99 -0
  259. package/src/app/routes/_authenticated/_system/job-queue.graphql.ts +54 -0
  260. package/src/app/routes/_authenticated/_system/job-queue.tsx +265 -0
  261. package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +249 -0
  262. package/src/app/routes/_authenticated/_tax-categories/components/tax-category-bulk-actions.tsx +15 -0
  263. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +72 -0
  264. package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +69 -0
  265. package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +127 -0
  266. package/src/app/routes/_authenticated/_tax-rates/components/tax-rate-bulk-actions.tsx +15 -0
  267. package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +84 -0
  268. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +119 -0
  269. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +149 -0
  270. package/src/app/routes/_authenticated/_zones/components/zone-bulk-actions.tsx +63 -0
  271. package/src/app/routes/_authenticated/_zones/components/zone-countries-add.tsx +0 -0
  272. package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +34 -0
  273. package/src/app/routes/_authenticated/_zones/components/zone-countries-table.tsx +94 -0
  274. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +105 -0
  275. package/src/app/routes/_authenticated/_zones/zones.tsx +64 -0
  276. package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +110 -0
  277. package/src/app/routes/_authenticated/index.tsx +233 -0
  278. package/src/app/routes/_authenticated.tsx +35 -0
  279. package/src/app/routes/login.tsx +49 -0
  280. package/src/app/styles.css +100 -0
  281. package/src/app/tailwindcss-animate.css +275 -0
  282. package/src/i18n/common-strings.ts +111 -0
  283. package/src/i18n/locales/ar.po +4972 -0
  284. package/src/i18n/locales/bg.po +3436 -0
  285. package/src/i18n/locales/cs.po +4972 -0
  286. package/src/i18n/locales/de.po +4972 -0
  287. package/src/i18n/locales/en.po +4972 -0
  288. package/src/i18n/locales/es.po +4972 -0
  289. package/src/i18n/locales/fa.po +4972 -0
  290. package/src/i18n/locales/fr.po +4972 -0
  291. package/src/i18n/locales/he.po +4972 -0
  292. package/src/i18n/locales/hr.po +4972 -0
  293. package/src/i18n/locales/it.po +4972 -0
  294. package/src/i18n/locales/ja.po +4972 -0
  295. package/src/i18n/locales/ko.po +4628 -0
  296. package/src/i18n/locales/nb.po +4972 -0
  297. package/src/i18n/locales/ne.po +4972 -0
  298. package/src/i18n/locales/nl.po +4628 -0
  299. package/src/i18n/locales/pl.po +4972 -0
  300. package/src/i18n/locales/pt_BR.po +4972 -0
  301. package/src/i18n/locales/pt_PT.po +4972 -0
  302. package/src/i18n/locales/ru.po +4972 -0
  303. package/src/i18n/locales/sv.po +4972 -0
  304. package/src/i18n/locales/tr.po +4972 -0
  305. package/src/i18n/locales/uk.po +4972 -0
  306. package/src/i18n/locales/zh_Hans.po +4972 -0
  307. package/src/i18n/locales/zh_Hant.po +4972 -0
  308. package/src/lib/components/data-display/boolean.tsx +23 -0
  309. package/src/lib/components/data-display/date-time.tsx +19 -0
  310. package/src/lib/components/data-display/json.tsx +20 -0
  311. package/src/lib/components/data-display/money.tsx +14 -0
  312. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  313. package/src/lib/components/data-input/affixed-input.tsx +79 -0
  314. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  315. package/src/lib/components/data-input/boolean-input.tsx +16 -0
  316. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  317. package/src/lib/components/data-input/checkbox-input.tsx +15 -0
  318. package/src/lib/components/data-input/combination-mode-input.tsx +61 -0
  319. package/src/lib/components/data-input/configurable-operation-list-input.tsx +58 -0
  320. package/src/lib/components/data-input/custom-field-list-input.tsx +294 -0
  321. package/src/lib/components/data-input/customer-group-input.tsx +61 -0
  322. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  323. package/src/lib/components/data-input/datetime-input.tsx +192 -0
  324. package/src/lib/components/data-input/default-relation-input.tsx +626 -0
  325. package/src/lib/components/data-input/facet-value-input.tsx +78 -0
  326. package/src/lib/components/data-input/index.ts +23 -0
  327. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  328. package/src/lib/components/data-input/money-input.tsx +122 -0
  329. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  330. package/src/lib/components/data-input/number-input.tsx +85 -0
  331. package/src/lib/components/data-input/password-form-input.stories.tsx +65 -0
  332. package/src/lib/components/data-input/password-form-input.tsx +23 -0
  333. package/src/lib/components/data-input/product-multi-selector-input.tsx +416 -0
  334. package/src/lib/components/data-input/relation-input.tsx +165 -0
  335. package/src/lib/components/data-input/relation-selector.tsx +476 -0
  336. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  337. package/src/lib/components/data-input/rich-text-input.tsx +16 -0
  338. package/src/lib/components/data-input/select-with-options.tsx +96 -0
  339. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  340. package/src/lib/components/data-input/slug-input.tsx +289 -0
  341. package/src/lib/components/data-input/string-list-input.tsx +268 -0
  342. package/src/lib/components/data-input/struct-form-input.tsx +320 -0
  343. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  344. package/src/lib/components/data-input/text-input.tsx +16 -0
  345. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  346. package/src/lib/components/data-input/textarea-input.tsx +23 -0
  347. package/src/lib/components/data-table/add-filter-menu.tsx +64 -0
  348. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  349. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +141 -0
  350. package/src/lib/components/data-table/data-table-bulk-actions.tsx +99 -0
  351. package/src/lib/components/data-table/data-table-column-header.tsx +50 -0
  352. package/src/lib/components/data-table/data-table-context.tsx +106 -0
  353. package/src/lib/components/data-table/data-table-faceted-filter.tsx +195 -0
  354. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  355. package/src/lib/components/data-table/data-table-filter-badge.tsx +101 -0
  356. package/src/lib/components/data-table/data-table-filter-dialog.tsx +92 -0
  357. package/src/lib/components/data-table/data-table-pagination.tsx +103 -0
  358. package/src/lib/components/data-table/data-table-utils.ts +291 -0
  359. package/src/lib/components/data-table/data-table-view-options.tsx +130 -0
  360. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  361. package/src/lib/components/data-table/data-table.tsx +508 -0
  362. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +60 -0
  363. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +159 -0
  364. package/src/lib/components/data-table/filters/data-table-id-filter.tsx +63 -0
  365. package/src/lib/components/data-table/filters/data-table-number-filter.tsx +123 -0
  366. package/src/lib/components/data-table/filters/data-table-string-filter.tsx +79 -0
  367. package/src/lib/components/data-table/global-views-bar.tsx +97 -0
  368. package/src/lib/components/data-table/global-views-sheet.tsx +11 -0
  369. package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
  370. package/src/lib/components/data-table/manage-global-views-button.tsx +26 -0
  371. package/src/lib/components/data-table/my-views-button.tsx +47 -0
  372. package/src/lib/components/data-table/refresh-button.tsx +49 -0
  373. package/src/lib/components/data-table/save-view-button.tsx +45 -0
  374. package/src/lib/components/data-table/save-view-dialog.tsx +134 -0
  375. package/src/lib/components/data-table/types.ts +40 -0
  376. package/src/lib/components/data-table/use-all-bulk-actions.ts +19 -0
  377. package/src/lib/components/data-table/use-generated-columns.tsx +411 -0
  378. package/src/lib/components/data-table/user-views-sheet.tsx +11 -0
  379. package/src/lib/components/data-table/views-sheet.tsx +305 -0
  380. package/src/lib/components/date-range-picker.tsx +186 -0
  381. package/src/lib/components/labeled-data.tsx +21 -0
  382. package/src/lib/components/layout/app-layout.tsx +35 -0
  383. package/src/lib/components/layout/app-sidebar.tsx +37 -0
  384. package/src/lib/components/layout/channel-switcher.tsx +229 -0
  385. package/src/lib/components/layout/content-language-selector.tsx +46 -0
  386. package/src/lib/components/layout/dev-mode-indicator.tsx +18 -0
  387. package/src/lib/components/layout/generated-breadcrumbs.tsx +136 -0
  388. package/src/lib/components/layout/language-dialog.tsx +133 -0
  389. package/src/lib/components/layout/manage-languages-dialog.tsx +386 -0
  390. package/src/lib/components/layout/nav-item-wrapper.tsx +107 -0
  391. package/src/lib/components/layout/nav-main.tsx +331 -0
  392. package/src/lib/components/layout/nav-projects.tsx +81 -0
  393. package/src/lib/components/layout/nav-user.tsx +180 -0
  394. package/src/lib/components/login/login-form.tsx +124 -0
  395. package/src/lib/components/shared/alerts.tsx +52 -0
  396. package/src/lib/components/shared/animated-number.tsx +49 -0
  397. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +109 -0
  398. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +103 -0
  399. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  400. package/src/lib/components/shared/asset/asset-gallery.tsx +623 -0
  401. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  402. package/src/lib/components/shared/asset/asset-picker-dialog.tsx +109 -0
  403. package/src/lib/components/shared/asset/asset-preview-dialog.tsx +38 -0
  404. package/src/lib/components/shared/asset/asset-preview-selector.tsx +32 -0
  405. package/src/lib/components/shared/asset/asset-preview.tsx +114 -0
  406. package/src/lib/components/shared/asset/asset-properties.tsx +44 -0
  407. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +71 -0
  408. package/src/lib/components/shared/assign-to-channel-dialog.tsx +156 -0
  409. package/src/lib/components/shared/assigned-channels.tsx +108 -0
  410. package/src/lib/components/shared/assigned-facet-values.tsx +61 -0
  411. package/src/lib/components/shared/channel-chip.tsx +43 -0
  412. package/src/lib/components/shared/channel-code-label.tsx +6 -0
  413. package/src/lib/components/shared/channel-selector.tsx +51 -0
  414. package/src/lib/components/shared/configurable-operation-arg-input.tsx +29 -0
  415. package/src/lib/components/shared/configurable-operation-input.tsx +167 -0
  416. package/src/lib/components/shared/configurable-operation-multi-selector.tsx +263 -0
  417. package/src/lib/components/shared/configurable-operation-selector.tsx +156 -0
  418. package/src/lib/components/shared/confirmation-dialog.tsx +58 -0
  419. package/src/lib/components/shared/copyable-text.tsx +30 -0
  420. package/src/lib/components/shared/country-selector.tsx +108 -0
  421. package/src/lib/components/shared/currency-selector.tsx +33 -0
  422. package/src/lib/components/shared/custom-fields-form.tsx +296 -0
  423. package/src/lib/components/shared/customer-address-form.tsx +338 -0
  424. package/src/lib/components/shared/customer-group-chip.tsx +30 -0
  425. package/src/lib/components/shared/customer-group-selector.tsx +65 -0
  426. package/src/lib/components/shared/customer-selector.tsx +112 -0
  427. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  428. package/src/lib/components/shared/detail-page-button.tsx +59 -0
  429. package/src/lib/components/shared/entity-assets.tsx +411 -0
  430. package/src/lib/components/shared/error-page.tsx +31 -0
  431. package/src/lib/components/shared/facet-value-chip.tsx +59 -0
  432. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  433. package/src/lib/components/shared/facet-value-selector.tsx +458 -0
  434. package/src/lib/components/shared/form-field-wrapper.tsx +111 -0
  435. package/src/lib/components/shared/history-timeline/history-entry-date.tsx +37 -0
  436. package/src/lib/components/shared/history-timeline/history-note-checkbox.tsx +28 -0
  437. package/src/lib/components/shared/history-timeline/history-note-editor.tsx +60 -0
  438. package/src/lib/components/shared/history-timeline/history-note-entry.tsx +65 -0
  439. package/src/lib/components/shared/history-timeline/history-note-input.tsx +39 -0
  440. package/src/lib/components/shared/history-timeline/history-timeline-with-grouping.tsx +141 -0
  441. package/src/lib/components/shared/history-timeline/history-timeline.tsx +16 -0
  442. package/src/lib/components/shared/history-timeline/use-history-note-editor.ts +26 -0
  443. package/src/lib/components/shared/icon-mark.tsx +18 -0
  444. package/src/lib/components/shared/language-selector.tsx +56 -0
  445. package/src/lib/components/shared/logo-mark.tsx +24 -0
  446. package/src/lib/components/shared/multi-select.tsx +159 -0
  447. package/src/lib/components/shared/navigation-confirmation.tsx +64 -0
  448. package/src/lib/components/shared/option-value-input.tsx +97 -0
  449. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  450. package/src/lib/components/shared/paginated-list-data-table.tsx +523 -0
  451. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  452. package/src/lib/components/shared/permission-guard.tsx +51 -0
  453. package/src/lib/components/shared/product-variant-selector.tsx +136 -0
  454. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +93 -0
  455. package/src/lib/components/shared/rich-text-editor/image-dialog.tsx +223 -0
  456. package/src/lib/components/shared/rich-text-editor/link-dialog.tsx +151 -0
  457. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +443 -0
  458. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +339 -0
  459. package/src/lib/components/shared/rich-text-editor/table-delete-menu.tsx +104 -0
  460. package/src/lib/components/shared/rich-text-editor/table-edit-icons.tsx +225 -0
  461. package/src/lib/components/shared/role-code-label.tsx +12 -0
  462. package/src/lib/components/shared/role-selector.tsx +56 -0
  463. package/src/lib/components/shared/seller-selector.tsx +111 -0
  464. package/src/lib/components/shared/stock-level-label.tsx +22 -0
  465. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +80 -0
  466. package/src/lib/components/shared/tax-category-selector.tsx +65 -0
  467. package/src/lib/components/shared/translatable-form-field.tsx +149 -0
  468. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  469. package/src/lib/components/shared/vendure-image.tsx +307 -0
  470. package/src/lib/components/shared/zone-selector.tsx +65 -0
  471. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  472. package/src/lib/components/ui/accordion.tsx +59 -0
  473. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  474. package/src/lib/components/ui/alert-dialog.tsx +128 -0
  475. package/src/lib/components/ui/alert.stories.tsx +35 -0
  476. package/src/lib/components/ui/alert.tsx +60 -0
  477. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  478. package/src/lib/components/ui/aspect-ratio.tsx +9 -0
  479. package/src/lib/components/ui/badge.stories.tsx +28 -0
  480. package/src/lib/components/ui/badge.tsx +38 -0
  481. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  482. package/src/lib/components/ui/breadcrumb.tsx +102 -0
  483. package/src/lib/components/ui/button.stories.tsx +38 -0
  484. package/src/lib/components/ui/button.tsx +58 -0
  485. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  486. package/src/lib/components/ui/calendar.tsx +447 -0
  487. package/src/lib/components/ui/card.stories.tsx +28 -0
  488. package/src/lib/components/ui/card.tsx +47 -0
  489. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  490. package/src/lib/components/ui/carousel.tsx +241 -0
  491. package/src/lib/components/ui/chart.tsx +351 -0
  492. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  493. package/src/lib/components/ui/checkbox.tsx +27 -0
  494. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  495. package/src/lib/components/ui/collapsible.tsx +33 -0
  496. package/src/lib/components/ui/command.stories.tsx +44 -0
  497. package/src/lib/components/ui/command.tsx +139 -0
  498. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  499. package/src/lib/components/ui/context-menu.tsx +252 -0
  500. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  501. package/src/lib/components/ui/dialog.tsx +116 -0
  502. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  503. package/src/lib/components/ui/drawer.tsx +133 -0
  504. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  505. package/src/lib/components/ui/dropdown-menu.tsx +223 -0
  506. package/src/lib/components/ui/form.tsx +141 -0
  507. package/src/lib/components/ui/grid-layout.tsx +376 -0
  508. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  509. package/src/lib/components/ui/hover-card.tsx +36 -0
  510. package/src/lib/components/ui/input-group.tsx +149 -0
  511. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  512. package/src/lib/components/ui/input-otp.tsx +77 -0
  513. package/src/lib/components/ui/input.stories.tsx +38 -0
  514. package/src/lib/components/ui/input.tsx +19 -0
  515. package/src/lib/components/ui/label.stories.tsx +24 -0
  516. package/src/lib/components/ui/label.tsx +21 -0
  517. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  518. package/src/lib/components/ui/menubar.tsx +274 -0
  519. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  520. package/src/lib/components/ui/navigation-menu.tsx +168 -0
  521. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  522. package/src/lib/components/ui/pagination.tsx +106 -0
  523. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  524. package/src/lib/components/ui/password-input.tsx +29 -0
  525. package/src/lib/components/ui/popover.stories.tsx +33 -0
  526. package/src/lib/components/ui/popover.tsx +40 -0
  527. package/src/lib/components/ui/progress.stories.tsx +27 -0
  528. package/src/lib/components/ui/progress.tsx +29 -0
  529. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  530. package/src/lib/components/ui/radio-group.tsx +45 -0
  531. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  532. package/src/lib/components/ui/resizable.tsx +54 -0
  533. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  534. package/src/lib/components/ui/scroll-area.tsx +50 -0
  535. package/src/lib/components/ui/select.stories.tsx +36 -0
  536. package/src/lib/components/ui/select.tsx +183 -0
  537. package/src/lib/components/ui/separator.stories.tsx +35 -0
  538. package/src/lib/components/ui/separator.tsx +26 -0
  539. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  540. package/src/lib/components/ui/sheet.tsx +118 -0
  541. package/src/lib/components/ui/sidebar-context.ts +16 -0
  542. package/src/lib/components/ui/sidebar.tsx +686 -0
  543. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  544. package/src/lib/components/ui/skeleton.tsx +13 -0
  545. package/src/lib/components/ui/slider.stories.tsx +37 -0
  546. package/src/lib/components/ui/slider.tsx +63 -0
  547. package/src/lib/components/ui/sonner.tsx +27 -0
  548. package/src/lib/components/ui/switch.stories.tsx +31 -0
  549. package/src/lib/components/ui/switch.tsx +26 -0
  550. package/src/lib/components/ui/table.stories.tsx +52 -0
  551. package/src/lib/components/ui/table.tsx +82 -0
  552. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  553. package/src/lib/components/ui/tabs.tsx +48 -0
  554. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  555. package/src/lib/components/ui/textarea.tsx +18 -0
  556. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  557. package/src/lib/components/ui/toggle-group.tsx +73 -0
  558. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  559. package/src/lib/components/ui/toggle.tsx +45 -0
  560. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  561. package/src/lib/components/ui/tooltip.tsx +51 -0
  562. package/src/lib/constants.ts +336 -0
  563. package/src/lib/framework/alert/alert-extensions.tsx +19 -0
  564. package/src/lib/framework/alert/alert-item.tsx +44 -0
  565. package/src/lib/framework/alert/alerts-indicator.tsx +22 -0
  566. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  567. package/src/lib/framework/component-registry/component-registry.tsx +18 -0
  568. package/src/lib/framework/component-registry/display-component.tsx +28 -0
  569. package/src/lib/framework/dashboard-widget/base-widget.tsx +101 -0
  570. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +152 -0
  571. package/src/lib/framework/dashboard-widget/latest-orders-widget/latest-orders-widget.graphql.ts +35 -0
  572. package/src/lib/framework/dashboard-widget/metrics-widget/chart.tsx +40 -0
  573. package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +96 -0
  574. package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +20 -0
  575. package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +113 -0
  576. package/src/lib/framework/dashboard-widget/orders-summary/order-summary-widget.graphql.ts +14 -0
  577. package/src/lib/framework/dashboard-widget/widget-extensions.tsx +19 -0
  578. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +36 -0
  579. package/src/lib/framework/data-table/data-table-extensions.ts +35 -0
  580. package/src/lib/framework/defaults.ts +270 -0
  581. package/src/lib/framework/document-extension/extend-detail-form-query.ts +50 -0
  582. package/src/lib/framework/document-extension/extend-document.spec.ts +884 -0
  583. package/src/lib/framework/document-extension/extend-document.ts +159 -0
  584. package/src/lib/framework/document-introspection/add-custom-fields.spec.ts +1458 -0
  585. package/src/lib/framework/document-introspection/add-custom-fields.ts +452 -0
  586. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +581 -0
  587. package/src/lib/framework/document-introspection/get-document-structure.ts +669 -0
  588. package/src/lib/framework/document-introspection/hooks.ts +13 -0
  589. package/src/lib/framework/document-introspection/include-only-selected-list-fields.spec.ts +1840 -0
  590. package/src/lib/framework/document-introspection/include-only-selected-list-fields.ts +940 -0
  591. package/src/lib/framework/document-introspection/testing-utils.ts +161 -0
  592. package/src/lib/framework/extension-api/define-dashboard-extension.ts +99 -0
  593. package/src/lib/framework/extension-api/display-component-extensions.tsx +58 -0
  594. package/src/lib/framework/extension-api/extension-api-types.ts +92 -0
  595. package/src/lib/framework/extension-api/input-component-extensions.tsx +96 -0
  596. package/src/lib/framework/extension-api/logic/alerts.ts +11 -0
  597. package/src/lib/framework/extension-api/logic/data-table.ts +37 -0
  598. package/src/lib/framework/extension-api/logic/detail-forms.ts +35 -0
  599. package/src/lib/framework/extension-api/logic/form-components.ts +14 -0
  600. package/src/lib/framework/extension-api/logic/history-entries.ts +24 -0
  601. package/src/lib/framework/extension-api/logic/index.ts +10 -0
  602. package/src/lib/framework/extension-api/logic/layout.ts +22 -0
  603. package/src/lib/framework/extension-api/logic/login.ts +17 -0
  604. package/src/lib/framework/extension-api/logic/navigation.ts +41 -0
  605. package/src/lib/framework/extension-api/logic/widgets.ts +10 -0
  606. package/src/lib/framework/extension-api/types/alerts.ts +59 -0
  607. package/src/lib/framework/extension-api/types/data-table.ts +159 -0
  608. package/src/lib/framework/extension-api/types/detail-forms.ts +58 -0
  609. package/src/lib/framework/extension-api/types/form-components.ts +41 -0
  610. package/src/lib/framework/extension-api/types/history-entries.ts +120 -0
  611. package/src/lib/framework/extension-api/types/index.ts +10 -0
  612. package/src/lib/framework/extension-api/types/layout.ts +141 -0
  613. package/src/lib/framework/extension-api/types/login.ts +76 -0
  614. package/src/lib/framework/extension-api/types/navigation.ts +105 -0
  615. package/src/lib/framework/extension-api/types/widgets.ts +101 -0
  616. package/src/lib/framework/extension-api/use-dashboard-extensions.ts +27 -0
  617. package/src/lib/framework/extension-api/use-login-extensions.ts +26 -0
  618. package/src/lib/framework/form-engine/custom-form-component-extensions.ts +15 -0
  619. package/src/lib/framework/form-engine/custom-form-component.tsx +16 -0
  620. package/src/lib/framework/form-engine/default-input-for-type.tsx +35 -0
  621. package/src/lib/framework/form-engine/form-control-adapter.tsx +193 -0
  622. package/src/lib/framework/form-engine/form-engine-types.ts +169 -0
  623. package/src/lib/framework/form-engine/form-schema-tools.spec.ts +610 -0
  624. package/src/lib/framework/form-engine/form-schema-tools.ts +417 -0
  625. package/src/lib/framework/form-engine/overridden-form-component.tsx +51 -0
  626. package/src/lib/framework/form-engine/use-generated-form.tsx +226 -0
  627. package/src/lib/framework/form-engine/utils.spec.ts +37 -0
  628. package/src/lib/framework/form-engine/utils.ts +356 -0
  629. package/src/lib/framework/form-engine/value-transformers.ts +150 -0
  630. package/src/lib/framework/history-entry/history-entry-extensions.ts +11 -0
  631. package/src/lib/framework/history-entry/history-entry.tsx +129 -0
  632. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  633. package/src/lib/framework/layout-engine/dev-mode-button.tsx +24 -0
  634. package/src/lib/framework/layout-engine/layout-extensions.ts +27 -0
  635. package/src/lib/framework/layout-engine/location-wrapper.tsx +122 -0
  636. package/src/lib/framework/layout-engine/page-block-provider.tsx +6 -0
  637. package/src/lib/framework/layout-engine/page-layout.spec.tsx +138 -0
  638. package/src/lib/framework/layout-engine/page-layout.tsx +700 -0
  639. package/src/lib/framework/layout-engine/page-provider.tsx +10 -0
  640. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  641. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +112 -0
  642. package/src/lib/framework/page/detail-page-route-loader.tsx +67 -0
  643. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  644. package/src/lib/framework/page/detail-page.tsx +255 -0
  645. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  646. package/src/lib/framework/page/list-page.tsx +613 -0
  647. package/src/lib/framework/page/page-api.ts +9 -0
  648. package/src/lib/framework/page/page-types.ts +51 -0
  649. package/src/lib/framework/page/use-detail-page.ts +341 -0
  650. package/src/lib/framework/page/use-extended-router.tsx +139 -0
  651. package/src/lib/framework/registry/global-registry.ts +50 -0
  652. package/src/lib/framework/registry/registry-types.ts +32 -0
  653. package/src/lib/graphql/api.ts +123 -0
  654. package/src/lib/graphql/common-operations.ts +37 -0
  655. package/src/lib/graphql/fragments.ts +71 -0
  656. package/src/lib/graphql/graphql-env.d.ts +523 -0
  657. package/src/lib/graphql/graphql.ts +15 -0
  658. package/src/lib/graphql/settings-store-operations.ts +17 -0
  659. package/src/lib/hooks/use-alerts.ts +84 -0
  660. package/src/lib/hooks/use-auth.tsx +21 -0
  661. package/src/lib/hooks/use-channel.ts +27 -0
  662. package/src/lib/hooks/use-custom-field-config.ts +27 -0
  663. package/src/lib/hooks/use-display-locale.ts +40 -0
  664. package/src/lib/hooks/use-drag-and-drop.ts +86 -0
  665. package/src/lib/hooks/use-dynamic-translations.ts +46 -0
  666. package/src/lib/hooks/use-extended-detail-query.ts +37 -0
  667. package/src/lib/hooks/use-extended-list-query.ts +80 -0
  668. package/src/lib/hooks/use-floating-bulk-actions.ts +83 -0
  669. package/src/lib/hooks/use-grouped-permissions.ts +55 -0
  670. package/src/lib/hooks/use-local-format.ts +186 -0
  671. package/src/lib/hooks/use-mobile.ts +19 -0
  672. package/src/lib/hooks/use-page-block.tsx +27 -0
  673. package/src/lib/hooks/use-page.tsx +10 -0
  674. package/src/lib/hooks/use-permissions.ts +40 -0
  675. package/src/lib/hooks/use-saved-views.ts +238 -0
  676. package/src/lib/hooks/use-server-config.ts +4 -0
  677. package/src/lib/hooks/use-sorted-languages.ts +41 -0
  678. package/src/lib/hooks/use-theme.ts +10 -0
  679. package/src/lib/hooks/use-ui-language-loader.ts +30 -0
  680. package/src/lib/hooks/use-user-settings.tsx +12 -0
  681. package/src/lib/index.ts +293 -0
  682. package/src/lib/lib/load-i18n-messages.ts +20 -0
  683. package/src/lib/lib/trans.tsx +20 -0
  684. package/src/lib/lib/utils.ts +111 -0
  685. package/src/lib/providers/alerts-provider.tsx +60 -0
  686. package/src/lib/providers/auth.tsx +232 -0
  687. package/src/lib/providers/channel-provider.tsx +245 -0
  688. package/src/lib/providers/i18n-provider.tsx +21 -0
  689. package/src/lib/providers/server-config.tsx +289 -0
  690. package/src/lib/providers/theme-provider.tsx +56 -0
  691. package/src/lib/providers/user-settings.tsx +244 -0
  692. package/src/lib/types/saved-views.ts +42 -0
  693. package/src/lib/utils/config-utils.ts +19 -0
  694. package/src/lib/utils/global-languages.ts +268 -0
  695. package/src/lib/utils/saved-views-utils.ts +40 -0
  696. package/src/lib/virtual.d.ts +39 -0
  697. package/src/vite-env.d.ts +2 -0
@@ -0,0 +1,1458 @@
1
+ import { CustomFieldConfig, CustomFields } from '@vendure/common/lib/generated-types';
2
+ import { graphql } from 'gql.tada';
3
+ import { DocumentNode, FieldNode, FragmentDefinitionNode, Kind, print } from 'graphql';
4
+ import { beforeEach, describe, expect, it } from 'vitest';
5
+
6
+ import { addCustomFields, addCustomFieldsToFragment } from './add-custom-fields.js';
7
+
8
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
9
+ describe('addCustomFields()', () => {
10
+ /**
11
+ * Normalizes the indentation of a string to make it easier to compare with the expected output
12
+ */
13
+ function normalizeIndentation(str: string): string {
14
+ const lines = str.replace(/ /g, ' ').split('\n');
15
+ const indentLength = lines[1].search(/\S|$/); // Find the first non-whitespace character
16
+ return lines
17
+ .map(line => line.slice(indentLength))
18
+ .join('\n')
19
+ .trim()
20
+ .replace(/"/g, '');
21
+ }
22
+
23
+ describe('Query handling', () => {
24
+ it('Adds customFields to entity query', () => {
25
+ const documentNode = graphql(`
26
+ query GetProduct {
27
+ product {
28
+ id
29
+ name
30
+ }
31
+ }
32
+ `);
33
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
34
+ customFieldsConfig.set('Product', [
35
+ { name: 'custom1', type: 'string', list: false },
36
+ { name: 'custom2', type: 'boolean', list: false },
37
+ ]);
38
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
39
+
40
+ expect(print(result)).toBe(
41
+ normalizeIndentation(`
42
+ query GetProduct {
43
+ product {
44
+ id
45
+ name
46
+ customFields {
47
+ custom1
48
+ custom2
49
+ }
50
+ }
51
+ }
52
+ `),
53
+ );
54
+ });
55
+
56
+ it('Adds customFields to paginated list', () => {
57
+ const documentNode = graphql(`
58
+ query GetProducts {
59
+ products {
60
+ items {
61
+ id
62
+ name
63
+ }
64
+ }
65
+ }
66
+ `);
67
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
68
+ customFieldsConfig.set('Product', [
69
+ { name: 'custom1', type: 'string', list: false },
70
+ { name: 'custom2', type: 'boolean', list: false },
71
+ ]);
72
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
73
+ expect(print(result)).toBe(
74
+ normalizeIndentation(`
75
+ query GetProducts {
76
+ products {
77
+ items {
78
+ id
79
+ name
80
+ customFields {
81
+ custom1
82
+ custom2
83
+ }
84
+ }
85
+ }
86
+ }
87
+ `),
88
+ );
89
+ });
90
+ });
91
+
92
+ describe('Fragment handling', () => {
93
+ const productWithVariantsFragment = graphql(`
94
+ fragment ProductWithVariants on Product {
95
+ id
96
+ translations {
97
+ languageCode
98
+ name
99
+ }
100
+ }
101
+ `);
102
+
103
+ const productVariantFragment = graphql(`
104
+ fragment ProductVariant on ProductVariant {
105
+ id
106
+ }
107
+ `);
108
+
109
+ const productOptionGroupFragment = graphql(`
110
+ fragment ProductOptionGroup on ProductOptionGroup {
111
+ id
112
+ }
113
+ `);
114
+
115
+ const productOptionFragment = graphql(`
116
+ fragment ProductOption on ProductOption {
117
+ id
118
+ }
119
+ `);
120
+
121
+ const userFragment = graphql(`
122
+ fragment User on User {
123
+ id
124
+ }
125
+ `);
126
+
127
+ const customerFragment = graphql(`
128
+ fragment Customer on Customer {
129
+ id
130
+ }
131
+ `);
132
+
133
+ const addressFragment = graphql(`
134
+ fragment Address on Address {
135
+ id
136
+ }
137
+ `);
138
+
139
+ let documentNode: DocumentNode;
140
+
141
+ beforeEach(() => {
142
+ documentNode = graphql(
143
+ `
144
+ query GetProductWithVariants {
145
+ product {
146
+ ...ProductWithVariants
147
+ }
148
+ }
149
+ `,
150
+ [productWithVariantsFragment],
151
+ );
152
+ });
153
+
154
+ it('Adds customFields to Product fragment', () => {
155
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
156
+ customFieldsConfig.set('Product', [
157
+ { name: 'custom1', type: 'string', list: false },
158
+ { name: 'custom2', type: 'boolean', list: false },
159
+ ]);
160
+
161
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
162
+ const productFragmentDef = result.definitions[1] as FragmentDefinitionNode;
163
+ const customFieldsDef = productFragmentDef.selectionSet.selections[2] as FieldNode;
164
+ expect(productFragmentDef.selectionSet.selections.length).toBe(3);
165
+ expect(customFieldsDef.selectionSet!.selections.length).toBe(2);
166
+ expect((customFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe('custom1');
167
+ expect((customFieldsDef.selectionSet!.selections[1] as FieldNode).name.value).toBe('custom2');
168
+ });
169
+
170
+ it('Adds customFields to Product translations', () => {
171
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
172
+ customFieldsConfig.set('Product', [
173
+ { name: 'customLocaleString', type: 'localeString', list: false },
174
+ ]);
175
+
176
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
177
+ const productFragmentDef = result.definitions[1] as FragmentDefinitionNode;
178
+ const translationsField = productFragmentDef.selectionSet.selections[1] as FieldNode;
179
+ const customTranslationFieldsDef = translationsField.selectionSet!.selections[2] as FieldNode;
180
+ expect(translationsField.selectionSet!.selections.length).toBe(3);
181
+ expect((customTranslationFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe(
182
+ 'customLocaleString',
183
+ );
184
+ });
185
+
186
+ function addsCustomFieldsToType(type: keyof CustomFields, fragment: DocumentNode) {
187
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
188
+ customFieldsConfig.set(type, [{ name: 'custom', type: 'boolean', list: false }]);
189
+
190
+ const result = addCustomFields(fragment, { customFieldsMap: customFieldsConfig });
191
+ const fragmentDef = result.definitions[0] as FragmentDefinitionNode;
192
+ const customFieldsDef = fragmentDef.selectionSet.selections[1] as FieldNode;
193
+ expect(fragmentDef.selectionSet.selections.length).toBe(2);
194
+ expect(customFieldsDef.selectionSet!.selections.length).toBe(1);
195
+ expect((customFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe('custom');
196
+ }
197
+
198
+ it('Does not duplicate customFields selection set', () => {
199
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
200
+ customFieldsConfig.set('Product', [{ name: 'custom', type: 'boolean', list: false }]);
201
+ const result1 = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
202
+ const result2 = addCustomFields(result1, { customFieldsMap: customFieldsConfig });
203
+
204
+ const fragmentDef = result2.definitions[1] as FragmentDefinitionNode;
205
+ const customFieldSelections = fragmentDef.selectionSet.selections.filter(
206
+ s => s.kind === Kind.FIELD && s.name.value === 'customFields',
207
+ );
208
+ expect(customFieldSelections.length).toBe(1);
209
+ });
210
+
211
+ it('Adds customFields to ProductVariant fragment', () => {
212
+ addsCustomFieldsToType('ProductVariant', productVariantFragment);
213
+ });
214
+
215
+ it('Adds customFields to ProductOptionGroup fragment', () => {
216
+ addsCustomFieldsToType('ProductOptionGroup', productOptionGroupFragment);
217
+ });
218
+
219
+ it('Adds customFields to ProductOption fragment', () => {
220
+ addsCustomFieldsToType('ProductOption', productOptionFragment);
221
+ });
222
+
223
+ it('Adds customFields to User fragment', () => {
224
+ addsCustomFieldsToType('User', userFragment);
225
+ });
226
+
227
+ it('Adds customFields to Customer fragment', () => {
228
+ addsCustomFieldsToType('Customer', customerFragment);
229
+ });
230
+
231
+ it('Adds customFields to Address fragment', () => {
232
+ addsCustomFieldsToType('Address', addressFragment);
233
+ });
234
+ });
235
+
236
+ describe('Nested entity handling', () => {
237
+ it('User example: Should not add custom fields to Asset fragment used in nested featuredAsset', () => {
238
+ const assetFragment = graphql(`
239
+ fragment Asset on Asset {
240
+ id
241
+ createdAt
242
+ updatedAt
243
+ name
244
+ fileSize
245
+ mimeType
246
+ type
247
+ preview
248
+ source
249
+ width
250
+ height
251
+ focalPoint {
252
+ x
253
+ y
254
+ }
255
+ }
256
+ `);
257
+
258
+ const documentNode = graphql(
259
+ `
260
+ query CollectionList($options: CollectionListOptions) {
261
+ collections(options: $options) {
262
+ items {
263
+ id
264
+ createdAt
265
+ updatedAt
266
+ featuredAsset {
267
+ ...Asset
268
+ }
269
+ name
270
+ slug
271
+ breadcrumbs {
272
+ id
273
+ name
274
+ slug
275
+ }
276
+ children {
277
+ id
278
+ name
279
+ }
280
+ position
281
+ isPrivate
282
+ parentId
283
+ productVariants {
284
+ totalItems
285
+ }
286
+ }
287
+ totalItems
288
+ }
289
+ }
290
+ `,
291
+ [assetFragment],
292
+ );
293
+
294
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
295
+ customFieldsConfig.set('Collection', [
296
+ { name: 'featuredProducts', type: 'relation', list: true, scalarFields: ['id', 'name'] },
297
+ { name: 'alternativeAsset', type: 'relation', list: false, scalarFields: ['id', 'name'] },
298
+ ]);
299
+ customFieldsConfig.set('Asset', [
300
+ { name: 'assetCustomField1', type: 'string', list: false },
301
+ { name: 'assetCustomField2', type: 'string', list: false },
302
+ { name: 'assetCustomField3', type: 'string', list: false },
303
+ ]);
304
+
305
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
306
+ const printed = print(result);
307
+
308
+ // Should add customFields ONLY to Collection items (top-level entity)
309
+ expect(printed).toContain('customFields {');
310
+ expect(printed).toContain('featuredProducts {');
311
+ expect(printed).toContain('alternativeAsset {');
312
+
313
+ // Should NOT add customFields to Asset fragment (only used in nested context)
314
+ const fragmentMatch = printed.match(/fragment Asset on Asset\s*\{[^}]*\}/s);
315
+ expect(fragmentMatch).toBeTruthy();
316
+ expect(fragmentMatch![0]).not.toContain('customFields');
317
+ expect(fragmentMatch![0]).not.toContain('assetCustomField1');
318
+
319
+ // Should NOT add customFields to children (nested Collection entities)
320
+ const childrenMatch = printed.match(/children\s*\{[^}]+\}/s);
321
+ expect(childrenMatch).toBeTruthy();
322
+ expect(childrenMatch![0]).not.toContain('customFields');
323
+
324
+ // Should NOT add customFields to breadcrumbs
325
+ const breadcrumbsMatch = printed.match(/breadcrumbs\s*\{[^}]+\}/s);
326
+ expect(breadcrumbsMatch).toBeTruthy();
327
+ expect(breadcrumbsMatch![0]).not.toContain('customFields');
328
+ });
329
+
330
+ it('Should only add custom fields to top-level entity, not nested related entities', () => {
331
+ const documentNode = graphql(`
332
+ query CollectionList($options: CollectionListOptions) {
333
+ collections(options: $options) {
334
+ items {
335
+ id
336
+ name
337
+ slug
338
+ featuredAsset {
339
+ id
340
+ preview
341
+ }
342
+ children {
343
+ id
344
+ name
345
+ slug
346
+ }
347
+ breadcrumbs {
348
+ id
349
+ name
350
+ slug
351
+ }
352
+ }
353
+ totalItems
354
+ }
355
+ }
356
+ `);
357
+
358
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
359
+ customFieldsConfig.set('Collection', [
360
+ { name: 'featuredProducts', type: 'relation', list: true, scalarFields: ['id', 'name'] },
361
+ { name: 'alternativeAsset', type: 'relation', list: false, scalarFields: ['id', 'name'] },
362
+ ]);
363
+ customFieldsConfig.set('Asset', [
364
+ { name: 'assetCustomField1', type: 'string', list: false },
365
+ { name: 'assetCustomField2', type: 'string', list: false },
366
+ ]);
367
+
368
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
369
+ const printed = print(result);
370
+
371
+ // Should add customFields to top-level Collection (items)
372
+ expect(printed).toContain('customFields {');
373
+ expect(printed).toContain('featuredProducts {');
374
+ expect(printed).toContain('alternativeAsset {');
375
+
376
+ // Should NOT add customFields to nested Collection entities (children, breadcrumbs)
377
+ const childrenMatch = printed.match(/children\s*\{[^}]+\}/s);
378
+ const breadcrumbsMatch = printed.match(/breadcrumbs\s*\{[^}]+\}/s);
379
+
380
+ expect(childrenMatch).toBeTruthy();
381
+ expect(breadcrumbsMatch).toBeTruthy();
382
+ expect(childrenMatch![0]).not.toContain('customFields');
383
+ expect(breadcrumbsMatch![0]).not.toContain('customFields');
384
+
385
+ // Should NOT add customFields to nested Asset entity (featuredAsset)
386
+ const featuredAssetMatch = printed.match(/featuredAsset\s*\{[^}]+\}/s);
387
+ expect(featuredAssetMatch).toBeTruthy();
388
+ expect(featuredAssetMatch![0]).not.toContain('customFields');
389
+ expect(featuredAssetMatch![0]).not.toContain('assetCustomField1');
390
+ });
391
+
392
+ it('Should NOT add custom fields to fragments that are only used in nested contexts', () => {
393
+ const assetFragment = graphql(`
394
+ fragment Asset on Asset {
395
+ id
396
+ name
397
+ preview
398
+ }
399
+ `);
400
+
401
+ const documentNode = graphql(
402
+ `
403
+ query ProductList($options: ProductListOptions) {
404
+ products(options: $options) {
405
+ items {
406
+ id
407
+ name
408
+ featuredAsset {
409
+ ...Asset
410
+ }
411
+ variants {
412
+ id
413
+ name
414
+ assets {
415
+ ...Asset
416
+ }
417
+ }
418
+ }
419
+ totalItems
420
+ }
421
+ }
422
+ `,
423
+ [assetFragment],
424
+ );
425
+
426
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
427
+ customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
428
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
429
+ customFieldsConfig.set('ProductVariant', [
430
+ { name: 'variantCustomField', type: 'string', list: false },
431
+ ]);
432
+
433
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
434
+ const printed = print(result);
435
+
436
+ // Should add customFields to Product (top-level query entity)
437
+ expect(printed).toContain('customFields {');
438
+ expect(printed).toContain('productCustomField');
439
+
440
+ // Should NOT add customFields to Asset fragment (only used in nested contexts)
441
+ expect(printed).not.toMatch(/fragment Asset on Asset\s*\{[^}]*customFields/s);
442
+
443
+ // Should NOT add customFields to nested ProductVariant entities
444
+ const variantsMatch = printed.match(/variants\s*\{[^}]+\}/s);
445
+ expect(variantsMatch).toBeTruthy();
446
+ expect(variantsMatch![0]).not.toContain('customFields');
447
+ expect(variantsMatch![0]).not.toContain('variantCustomField');
448
+ });
449
+
450
+ it('Should add custom fields to fragments used at top level', () => {
451
+ const productFragment = graphql(`
452
+ fragment ProductDetails on Product {
453
+ id
454
+ name
455
+ slug
456
+ }
457
+ `);
458
+
459
+ const documentNode = graphql(
460
+ `
461
+ query ProductList($options: ProductListOptions) {
462
+ products(options: $options) {
463
+ items {
464
+ ...ProductDetails
465
+ featuredAsset {
466
+ id
467
+ preview
468
+ }
469
+ }
470
+ totalItems
471
+ }
472
+ }
473
+ `,
474
+ [productFragment],
475
+ );
476
+
477
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
478
+ customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
479
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
480
+
481
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
482
+ const printed = print(result);
483
+
484
+ // Should add customFields to ProductDetails fragment (used at top level in items)
485
+ expect(printed).toContain('fragment ProductDetails on Product');
486
+ expect(printed).toContain('productCustomField');
487
+
488
+ // Should NOT add customFields to featuredAsset (nested entity)
489
+ const featuredAssetMatch = printed.match(/featuredAsset\s*\{[^}]+\}/s);
490
+ expect(featuredAssetMatch).toBeTruthy();
491
+ expect(featuredAssetMatch![0]).not.toContain('customFields');
492
+ });
493
+
494
+ it('Should handle complex nested structure with multiple entity types', () => {
495
+ const documentNode = graphql(`
496
+ query ComplexQuery {
497
+ orders {
498
+ items {
499
+ id
500
+ code
501
+ customer {
502
+ id
503
+ firstName
504
+ addresses {
505
+ id
506
+ streetLine1
507
+ country {
508
+ id
509
+ name
510
+ }
511
+ }
512
+ }
513
+ lines {
514
+ id
515
+ productVariant {
516
+ id
517
+ name
518
+ product {
519
+ id
520
+ name
521
+ }
522
+ }
523
+ }
524
+ }
525
+ }
526
+ }
527
+ `);
528
+
529
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
530
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
531
+ customFieldsConfig.set('Customer', [
532
+ { name: 'customerCustomField', type: 'string', list: false },
533
+ ]);
534
+ customFieldsConfig.set('Address', [{ name: 'addressCustomField', type: 'string', list: false }]);
535
+ customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
536
+
537
+ const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
538
+ const printed = print(result);
539
+
540
+ // Should only add customFields to top-level Order entity
541
+ expect(printed).toContain('customFields {');
542
+ expect(printed).toContain('orderCustomField');
543
+
544
+ // Should NOT add customFields to any nested entities
545
+ expect(printed).not.toMatch(/customer\s*\{[^}]*customFields/s);
546
+ expect(printed).not.toMatch(/addresses\s*\{[^}]*customFields/s);
547
+ expect(printed).not.toMatch(/country\s*\{[^}]*customFields/s);
548
+ expect(printed).not.toMatch(/productVariant\s*\{[^}]*customFields/s);
549
+ expect(printed).not.toMatch(/product\s*\{[^}]*customFields/s);
550
+ });
551
+ });
552
+
553
+ describe('includeNestedFragments option', () => {
554
+ it('Should add custom fields to nested fragments when explicitly included', () => {
555
+ const assetFragment = graphql(`
556
+ fragment Asset on Asset {
557
+ id
558
+ preview
559
+ }
560
+ `);
561
+
562
+ const documentNode = graphql(
563
+ `
564
+ query ProductList($options: ProductListOptions) {
565
+ products(options: $options) {
566
+ items {
567
+ id
568
+ name
569
+ featuredAsset {
570
+ ...Asset
571
+ }
572
+ }
573
+ }
574
+ }
575
+ `,
576
+ [assetFragment],
577
+ );
578
+
579
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
580
+ customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
581
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
582
+
583
+ const result = addCustomFields(documentNode, {
584
+ customFieldsMap: customFieldsConfig,
585
+ includeNestedFragments: ['Asset'], // Explicitly include the nested Asset fragment
586
+ });
587
+ const printed = print(result);
588
+
589
+ // Should add customFields to Product (top-level)
590
+ expect(printed).toContain('productCustomField');
591
+
592
+ // Should ALSO add customFields to Asset (nested, but explicitly included)
593
+ expect(printed).toContain('fragment Asset on Asset');
594
+ expect(printed).toContain('assetCustomField');
595
+ });
596
+
597
+ it('Should handle multiple nested fragments in includeNestedFragments', () => {
598
+ const assetFragment = graphql(`
599
+ fragment Asset on Asset {
600
+ id
601
+ preview
602
+ }
603
+ `);
604
+
605
+ const orderLineFragment = graphql(`
606
+ fragment OrderLine on OrderLine {
607
+ id
608
+ quantity
609
+ }
610
+ `);
611
+
612
+ const documentNode = graphql(
613
+ `
614
+ query GetOrder($id: ID!) {
615
+ order(id: $id) {
616
+ id
617
+ code
618
+ lines {
619
+ ...OrderLine
620
+ }
621
+ featuredAsset {
622
+ ...Asset
623
+ }
624
+ }
625
+ }
626
+ `,
627
+ [orderLineFragment, assetFragment],
628
+ );
629
+
630
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
631
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
632
+ customFieldsConfig.set('OrderLine', [
633
+ { name: 'orderLineCustomField', type: 'string', list: false },
634
+ ]);
635
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
636
+
637
+ const result = addCustomFields(documentNode, {
638
+ customFieldsMap: customFieldsConfig,
639
+ includeNestedFragments: ['OrderLine', 'Asset'], // Include both nested fragments
640
+ });
641
+ const printed = print(result);
642
+
643
+ // Should add customFields to all three
644
+ expect(printed).toContain('orderCustomField');
645
+ expect(printed).toContain('orderLineCustomField');
646
+ expect(printed).toContain('assetCustomField');
647
+ });
648
+
649
+ it('Should only add custom fields to specified nested fragments, not all nested fragments', () => {
650
+ const assetFragment = graphql(`
651
+ fragment Asset on Asset {
652
+ id
653
+ preview
654
+ }
655
+ `);
656
+
657
+ const orderLineFragment = graphql(`
658
+ fragment OrderLine on OrderLine {
659
+ id
660
+ quantity
661
+ }
662
+ `);
663
+
664
+ const documentNode = graphql(
665
+ `
666
+ query GetOrder($id: ID!) {
667
+ order(id: $id) {
668
+ id
669
+ lines {
670
+ ...OrderLine
671
+ }
672
+ featuredAsset {
673
+ ...Asset
674
+ }
675
+ }
676
+ }
677
+ `,
678
+ [orderLineFragment, assetFragment],
679
+ );
680
+
681
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
682
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
683
+ customFieldsConfig.set('OrderLine', [
684
+ { name: 'orderLineCustomField', type: 'string', list: false },
685
+ ]);
686
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
687
+
688
+ const result = addCustomFields(documentNode, {
689
+ customFieldsMap: customFieldsConfig,
690
+ includeNestedFragments: ['OrderLine'], // Only include OrderLine, not Asset
691
+ });
692
+ const printed = print(result);
693
+
694
+ // Should add customFields to Order (top-level) and OrderLine (explicitly included)
695
+ expect(printed).toContain('orderCustomField');
696
+ expect(printed).toContain('orderLineCustomField');
697
+
698
+ // Should NOT add customFields to Asset (nested and not included)
699
+ expect(printed).not.toContain('assetCustomField');
700
+ });
701
+
702
+ it('Works with the timing issue - called later when globalCustomFieldsMap is populated', () => {
703
+ const orderLineFragment = graphql(`
704
+ fragment OrderLine on OrderLine {
705
+ id
706
+ quantity
707
+ }
708
+ `);
709
+
710
+ const orderDetailFragment = graphql(
711
+ `
712
+ fragment OrderDetail on Order {
713
+ id
714
+ code
715
+ lines {
716
+ ...OrderLine
717
+ }
718
+ }
719
+ `,
720
+ [orderLineFragment],
721
+ );
722
+
723
+ const orderDetailDocument = graphql(
724
+ `
725
+ query GetOrder($id: ID!) {
726
+ order(id: $id) {
727
+ ...OrderDetail
728
+ }
729
+ }
730
+ `,
731
+ [orderDetailFragment],
732
+ );
733
+
734
+ // Initially, globalCustomFieldsMap is empty (simulating module load time)
735
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
736
+ // Documents are created...
737
+
738
+ // Later, when server config is loaded and custom fields are available
739
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
740
+ customFieldsConfig.set('OrderLine', [
741
+ { name: 'orderLineCustomField', type: 'string', list: false },
742
+ ]);
743
+
744
+ // Now when addCustomFields is called (e.g., in a component), it has access to custom fields
745
+ const result = addCustomFields(orderDetailDocument, {
746
+ customFieldsMap: customFieldsConfig,
747
+ includeNestedFragments: ['OrderLine'], // Explicitly include nested OrderLine fragment
748
+ });
749
+ const printed = print(result);
750
+
751
+ // Should add customFields to both Order and OrderLine
752
+ expect(printed).toContain('orderCustomField');
753
+ expect(printed).toContain('orderLineCustomField');
754
+ });
755
+ });
756
+ });
757
+
758
+ describe('addCustomFieldsToFragment()', () => {
759
+ /**
760
+ * Normalizes the indentation of a string to make it easier to compare with the expected output
761
+ */
762
+ function normalizeIndentation(str: string): string {
763
+ const lines = str.replace(/ /g, ' ').split('\n');
764
+ const indentLength = lines[1].search(/\S|$/); // Find the first non-whitespace character
765
+ return lines
766
+ .map(line => line.slice(indentLength))
767
+ .join('\n')
768
+ .trim()
769
+ .replace(/"/g, '');
770
+ }
771
+
772
+ describe('Basic functionality', () => {
773
+ it('Adds customFields to a simple fragment', () => {
774
+ const fragmentDocument = graphql(`
775
+ fragment Product on Product {
776
+ id
777
+ name
778
+ }
779
+ `);
780
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
781
+ customFieldsConfig.set('Product', [
782
+ { name: 'custom1', type: 'string', list: false },
783
+ { name: 'custom2', type: 'boolean', list: false },
784
+ ]);
785
+
786
+ const result = addCustomFieldsToFragment(fragmentDocument, {
787
+ customFieldsMap: customFieldsConfig,
788
+ });
789
+
790
+ expect(print(result)).toBe(
791
+ normalizeIndentation(`
792
+ fragment Product on Product {
793
+ id
794
+ name
795
+ customFields {
796
+ custom1
797
+ custom2
798
+ }
799
+ }
800
+ `),
801
+ );
802
+ });
803
+
804
+ it('Adds customFields with includeCustomFields filter', () => {
805
+ const fragmentDocument = graphql(`
806
+ fragment Product on Product {
807
+ id
808
+ name
809
+ }
810
+ `);
811
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
812
+ customFieldsConfig.set('Product', [
813
+ { name: 'custom1', type: 'string', list: false },
814
+ { name: 'custom2', type: 'boolean', list: false },
815
+ { name: 'custom3', type: 'int', list: false },
816
+ ]);
817
+
818
+ const result = addCustomFieldsToFragment(fragmentDocument, {
819
+ customFieldsMap: customFieldsConfig,
820
+ includeCustomFields: ['custom1', 'custom3'],
821
+ });
822
+
823
+ expect(print(result)).toBe(
824
+ normalizeIndentation(`
825
+ fragment Product on Product {
826
+ id
827
+ name
828
+ customFields {
829
+ custom1
830
+ custom3
831
+ }
832
+ }
833
+ `),
834
+ );
835
+ });
836
+
837
+ it('Handles fragment with no custom fields configured', () => {
838
+ const fragmentDocument = graphql(`
839
+ fragment Product on Product {
840
+ id
841
+ name
842
+ }
843
+ `);
844
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
845
+
846
+ const result = addCustomFieldsToFragment(fragmentDocument, {
847
+ customFieldsMap: customFieldsConfig,
848
+ });
849
+
850
+ // Should return the fragment unchanged
851
+ expect(print(result)).toBe(
852
+ normalizeIndentation(`
853
+ fragment Product on Product {
854
+ id
855
+ name
856
+ }
857
+ `),
858
+ );
859
+ });
860
+ });
861
+
862
+ describe('Validation', () => {
863
+ it('Throws error when given a query document', () => {
864
+ const documentNode = graphql(`
865
+ query GetProduct {
866
+ product {
867
+ id
868
+ name
869
+ }
870
+ }
871
+ `);
872
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
873
+ customFieldsConfig.set('Product', [{ name: 'custom', type: 'string', list: false }]);
874
+
875
+ expect(() =>
876
+ addCustomFieldsToFragment(documentNode, { customFieldsMap: customFieldsConfig }),
877
+ ).toThrow('expects a fragment-only document');
878
+ });
879
+
880
+ it('Only modifies the first fragment when multiple fragments are present', () => {
881
+ const productFragment = graphql(`
882
+ fragment Product on Product {
883
+ id
884
+ }
885
+ `);
886
+ const variantFragment = graphql(`
887
+ fragment Variant on ProductVariant {
888
+ id
889
+ }
890
+ `);
891
+
892
+ // Create a document with both fragments (Product first, then Variant)
893
+ const multiFragmentDoc = {
894
+ kind: Kind.DOCUMENT,
895
+ definitions: [...productFragment.definitions, ...variantFragment.definitions],
896
+ } as DocumentNode;
897
+
898
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
899
+ customFieldsConfig.set('Product', [{ name: 'productCustom', type: 'string', list: false }]);
900
+ customFieldsConfig.set('ProductVariant', [
901
+ { name: 'variantCustom', type: 'string', list: false },
902
+ ]);
903
+
904
+ const result = addCustomFieldsToFragment(multiFragmentDoc, {
905
+ customFieldsMap: customFieldsConfig,
906
+ });
907
+ const printed = print(result);
908
+
909
+ // Should add customFields to Product (first fragment)
910
+ expect(printed).toContain('fragment Product on Product');
911
+ expect(printed).toContain('productCustom');
912
+
913
+ // Should NOT add customFields to Variant (dependency fragment)
914
+ expect(printed).toContain('fragment Variant on ProductVariant');
915
+ expect(printed).not.toContain('variantCustom');
916
+ });
917
+
918
+ it('Throws error when given an empty document', () => {
919
+ const emptyDoc = {
920
+ kind: Kind.DOCUMENT,
921
+ definitions: [],
922
+ } as DocumentNode;
923
+
924
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
925
+
926
+ expect(() =>
927
+ addCustomFieldsToFragment(emptyDoc, { customFieldsMap: customFieldsConfig }),
928
+ ).toThrow('expects a document with at least one fragment definition');
929
+ });
930
+ });
931
+
932
+ describe('Advanced field types', () => {
933
+ it('Handles relation custom fields', () => {
934
+ const fragmentDocument = graphql(`
935
+ fragment Product on Product {
936
+ id
937
+ name
938
+ }
939
+ `);
940
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
941
+ customFieldsConfig.set('Product', [
942
+ {
943
+ name: 'relatedProduct',
944
+ type: 'relation',
945
+ list: false,
946
+ scalarFields: ['id', 'name', 'slug'],
947
+ },
948
+ ]);
949
+
950
+ const result = addCustomFieldsToFragment(fragmentDocument, {
951
+ customFieldsMap: customFieldsConfig,
952
+ });
953
+
954
+ expect(print(result)).toBe(
955
+ normalizeIndentation(`
956
+ fragment Product on Product {
957
+ id
958
+ name
959
+ customFields {
960
+ relatedProduct {
961
+ id
962
+ name
963
+ slug
964
+ }
965
+ }
966
+ }
967
+ `),
968
+ );
969
+ });
970
+
971
+ it('Handles struct custom fields', () => {
972
+ const fragmentDocument = graphql(`
973
+ fragment Product on Product {
974
+ id
975
+ name
976
+ }
977
+ `);
978
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
979
+ customFieldsConfig.set('Product', [
980
+ {
981
+ name: 'dimensions',
982
+ type: 'struct',
983
+ list: false,
984
+ fields: [
985
+ { name: 'width', type: 'int' },
986
+ { name: 'height', type: 'int' },
987
+ { name: 'depth', type: 'int' },
988
+ ],
989
+ },
990
+ ]);
991
+
992
+ const result = addCustomFieldsToFragment(fragmentDocument, {
993
+ customFieldsMap: customFieldsConfig,
994
+ });
995
+
996
+ expect(print(result)).toBe(
997
+ normalizeIndentation(`
998
+ fragment Product on Product {
999
+ id
1000
+ name
1001
+ customFields {
1002
+ dimensions {
1003
+ width
1004
+ height
1005
+ depth
1006
+ }
1007
+ }
1008
+ }
1009
+ `),
1010
+ );
1011
+ });
1012
+
1013
+ it('Handles localized custom fields in translations', () => {
1014
+ const fragmentDocument = graphql(`
1015
+ fragment Product on Product {
1016
+ id
1017
+ translations {
1018
+ languageCode
1019
+ name
1020
+ }
1021
+ }
1022
+ `);
1023
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1024
+ customFieldsConfig.set('Product', [
1025
+ { name: 'customDescription', type: 'localeString', list: false },
1026
+ { name: 'customSeoTitle', type: 'localeText', list: false },
1027
+ ]);
1028
+
1029
+ const result = addCustomFieldsToFragment(fragmentDocument, {
1030
+ customFieldsMap: customFieldsConfig,
1031
+ });
1032
+
1033
+ const printed = print(result);
1034
+ // Should add localized fields to translations
1035
+ expect(printed).toContain('translations {');
1036
+ expect(printed).toMatch(/translations\s*\{[^}]*customFields/s);
1037
+
1038
+ const fragmentDef = result.definitions[0] as FragmentDefinitionNode;
1039
+ const translationsField = fragmentDef.selectionSet.selections.find(
1040
+ s => s.kind === Kind.FIELD && s.name.value === 'translations',
1041
+ ) as FieldNode;
1042
+ const customFieldsInTranslations = translationsField.selectionSet!.selections.find(
1043
+ s => s.kind === Kind.FIELD && s.name.value === 'customFields',
1044
+ ) as FieldNode;
1045
+
1046
+ expect(customFieldsInTranslations).toBeTruthy();
1047
+ expect(customFieldsInTranslations.selectionSet!.selections.length).toBe(2);
1048
+ });
1049
+ });
1050
+
1051
+ describe('Special type handling', () => {
1052
+ it('Handles OrderAddress as alias of Address', () => {
1053
+ const fragmentDocument = graphql(`
1054
+ fragment OrderAddress on OrderAddress {
1055
+ id
1056
+ streetLine1
1057
+ }
1058
+ `);
1059
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1060
+ // Custom fields are configured for Address, not OrderAddress
1061
+ customFieldsConfig.set('Address', [{ name: 'buildingNumber', type: 'string', list: false }]);
1062
+
1063
+ const result = addCustomFieldsToFragment(fragmentDocument, {
1064
+ customFieldsMap: customFieldsConfig,
1065
+ });
1066
+
1067
+ // Should still add custom fields because OrderAddress is aliased to Address
1068
+ expect(print(result)).toContain('customFields {');
1069
+ expect(print(result)).toContain('buildingNumber');
1070
+ });
1071
+
1072
+ it('Handles Country as alias of Region', () => {
1073
+ const fragmentDocument = graphql(`
1074
+ fragment Country on Country {
1075
+ id
1076
+ name
1077
+ }
1078
+ `);
1079
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1080
+ // Custom fields are configured for Region, not Country
1081
+ customFieldsConfig.set('Region', [{ name: 'regionCode', type: 'string', list: false }]);
1082
+
1083
+ const result = addCustomFieldsToFragment(fragmentDocument, {
1084
+ customFieldsMap: customFieldsConfig,
1085
+ });
1086
+
1087
+ // Should still add custom fields because Country is aliased to Region
1088
+ expect(print(result)).toContain('customFields {');
1089
+ expect(print(result)).toContain('regionCode');
1090
+ });
1091
+ });
1092
+
1093
+ describe('Memoization', () => {
1094
+ it('Returns the same instance for the same inputs', () => {
1095
+ const fragmentDocument = graphql(`
1096
+ fragment Product on Product {
1097
+ id
1098
+ name
1099
+ }
1100
+ `);
1101
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1102
+ customFieldsConfig.set('Product', [{ name: 'custom', type: 'string', list: false }]);
1103
+
1104
+ const result1 = addCustomFieldsToFragment(fragmentDocument, {
1105
+ customFieldsMap: customFieldsConfig,
1106
+ });
1107
+ const result2 = addCustomFieldsToFragment(fragmentDocument, {
1108
+ customFieldsMap: customFieldsConfig,
1109
+ });
1110
+
1111
+ // Should return the exact same instance (identity equality)
1112
+ expect(result1).toBe(result2);
1113
+ });
1114
+
1115
+ it('Returns different instances for different options', () => {
1116
+ const fragmentDocument = graphql(`
1117
+ fragment Product on Product {
1118
+ id
1119
+ name
1120
+ }
1121
+ `);
1122
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1123
+ customFieldsConfig.set('Product', [
1124
+ { name: 'custom1', type: 'string', list: false },
1125
+ { name: 'custom2', type: 'boolean', list: false },
1126
+ ]);
1127
+
1128
+ const result1 = addCustomFieldsToFragment(fragmentDocument, {
1129
+ customFieldsMap: customFieldsConfig,
1130
+ includeCustomFields: ['custom1'],
1131
+ });
1132
+ const result2 = addCustomFieldsToFragment(fragmentDocument, {
1133
+ customFieldsMap: customFieldsConfig,
1134
+ includeCustomFields: ['custom2'],
1135
+ });
1136
+
1137
+ // Should return different instances for different options
1138
+ expect(result1).not.toBe(result2);
1139
+ expect(print(result1)).toContain('custom1');
1140
+ expect(print(result1)).not.toContain('custom2');
1141
+ expect(print(result2)).toContain('custom2');
1142
+ expect(print(result2)).not.toContain('custom1');
1143
+ });
1144
+ });
1145
+
1146
+ describe('Fragment spreads handling', () => {
1147
+ it('Should only add custom fields to the top-level fragment, not to referenced fragments', () => {
1148
+ const orderLineFragment = graphql(`
1149
+ fragment OrderLine on OrderLine {
1150
+ id
1151
+ quantity
1152
+ }
1153
+ `);
1154
+
1155
+ const orderDetailFragment = graphql(
1156
+ `
1157
+ fragment OrderDetail on Order {
1158
+ id
1159
+ code
1160
+ lines {
1161
+ ...OrderLine
1162
+ }
1163
+ }
1164
+ `,
1165
+ [orderLineFragment],
1166
+ );
1167
+
1168
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1169
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
1170
+ customFieldsConfig.set('OrderLine', [
1171
+ { name: 'orderLineCustomField', type: 'string', list: false },
1172
+ ]);
1173
+
1174
+ // Apply to the OrderDetail fragment only
1175
+ const result = addCustomFieldsToFragment(orderDetailFragment, {
1176
+ customFieldsMap: customFieldsConfig,
1177
+ });
1178
+
1179
+ const printed = print(result);
1180
+
1181
+ // Should add customFields to OrderDetail (top-level fragment)
1182
+ expect(printed).toContain('fragment OrderDetail on Order');
1183
+ expect(printed).toContain('orderCustomField');
1184
+
1185
+ // Should include the OrderLine fragment definition (dependency) but NOT add customFields to it
1186
+ expect(printed).toContain('...OrderLine');
1187
+ expect(printed).toContain('fragment OrderLine on OrderLine');
1188
+ expect(printed).not.toContain('orderLineCustomField');
1189
+ });
1190
+
1191
+ it('Should work with deeply nested fragment spreads', () => {
1192
+ const assetFragment = graphql(`
1193
+ fragment Asset on Asset {
1194
+ id
1195
+ preview
1196
+ }
1197
+ `);
1198
+
1199
+ const orderLineFragment = graphql(
1200
+ `
1201
+ fragment OrderLine on OrderLine {
1202
+ id
1203
+ quantity
1204
+ featuredAsset {
1205
+ ...Asset
1206
+ }
1207
+ }
1208
+ `,
1209
+ [assetFragment],
1210
+ );
1211
+
1212
+ const orderDetailFragment = graphql(
1213
+ `
1214
+ fragment OrderDetail on Order {
1215
+ id
1216
+ code
1217
+ lines {
1218
+ ...OrderLine
1219
+ }
1220
+ }
1221
+ `,
1222
+ [orderLineFragment],
1223
+ );
1224
+
1225
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1226
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
1227
+ customFieldsConfig.set('OrderLine', [
1228
+ { name: 'orderLineCustomField', type: 'string', list: false },
1229
+ ]);
1230
+ customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
1231
+
1232
+ const result = addCustomFieldsToFragment(orderDetailFragment, {
1233
+ customFieldsMap: customFieldsConfig,
1234
+ });
1235
+
1236
+ const printed = print(result);
1237
+
1238
+ // Should ONLY add customFields to OrderDetail
1239
+ expect(printed).toContain('orderCustomField');
1240
+ expect(printed).not.toContain('orderLineCustomField');
1241
+ expect(printed).not.toContain('assetCustomField');
1242
+
1243
+ // Should still contain the fragment definitions (dependencies) but without custom fields
1244
+ expect(printed).toContain('...OrderLine');
1245
+ expect(printed).toContain('fragment OrderLine on OrderLine');
1246
+ expect(printed).toContain('fragment Asset on Asset');
1247
+ });
1248
+ });
1249
+
1250
+ describe('Composability with addCustomFields()', () => {
1251
+ it('Can be used inline in graphql() dependency array (like the original pattern)', () => {
1252
+ const productFragment = graphql(`
1253
+ fragment Product on Product {
1254
+ id
1255
+ name
1256
+ }
1257
+ `);
1258
+
1259
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1260
+ customFieldsConfig.set('Product', [
1261
+ { name: 'custom1', type: 'string', list: false },
1262
+ { name: 'custom2', type: 'boolean', list: false },
1263
+ ]);
1264
+
1265
+ // Use addCustomFieldsToFragment directly in the array - this is the pattern from orders.graphql.ts
1266
+ const queryDocument = graphql(
1267
+ `
1268
+ query GetProduct {
1269
+ product {
1270
+ ...Product
1271
+ }
1272
+ }
1273
+ `,
1274
+ [addCustomFieldsToFragment(productFragment, { customFieldsMap: customFieldsConfig })],
1275
+ );
1276
+
1277
+ // The query should include the modified fragment with custom fields
1278
+ const printed = print(queryDocument);
1279
+ expect(printed).toContain('customFields {');
1280
+ expect(printed).toContain('custom1');
1281
+ expect(printed).toContain('custom2');
1282
+ });
1283
+
1284
+ it('addCustomFieldsToFragment produces same result as addCustomFields for single fragments', () => {
1285
+ const productFragment = graphql(`
1286
+ fragment Product on Product {
1287
+ id
1288
+ name
1289
+ }
1290
+ `);
1291
+
1292
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1293
+ customFieldsConfig.set('Product', [
1294
+ { name: 'custom1', type: 'string', list: false },
1295
+ { name: 'custom2', type: 'boolean', list: false },
1296
+ ]);
1297
+
1298
+ const resultFromFragment = addCustomFieldsToFragment(productFragment, {
1299
+ customFieldsMap: customFieldsConfig,
1300
+ });
1301
+ const resultFromFull = addCustomFields(productFragment, { customFieldsMap: customFieldsConfig });
1302
+
1303
+ // Both should produce the same output
1304
+ expect(print(resultFromFragment)).toBe(print(resultFromFull));
1305
+ });
1306
+
1307
+ it('Works with fragments that have dependencies when used inline', () => {
1308
+ const orderLineFragment = graphql(`
1309
+ fragment OrderLine on OrderLine {
1310
+ id
1311
+ quantity
1312
+ }
1313
+ `);
1314
+
1315
+ const orderDetailFragment = graphql(
1316
+ `
1317
+ fragment OrderDetail on Order {
1318
+ id
1319
+ code
1320
+ lines {
1321
+ ...OrderLine
1322
+ }
1323
+ }
1324
+ `,
1325
+ [orderLineFragment],
1326
+ );
1327
+
1328
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1329
+ customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
1330
+ customFieldsConfig.set('OrderLine', [
1331
+ { name: 'orderLineCustomField', type: 'string', list: false },
1332
+ ]);
1333
+
1334
+ // This is exactly the pattern used in orders.graphql.ts
1335
+ const queryDocument = graphql(
1336
+ `
1337
+ query GetOrder($id: ID!) {
1338
+ order(id: $id) {
1339
+ ...OrderDetail
1340
+ }
1341
+ }
1342
+ `,
1343
+ [addCustomFieldsToFragment(orderDetailFragment, { customFieldsMap: customFieldsConfig })],
1344
+ );
1345
+
1346
+ const printed = print(queryDocument);
1347
+
1348
+ // Should add custom fields to OrderDetail
1349
+ expect(printed).toContain('fragment OrderDetail on Order');
1350
+ expect(printed).toContain('orderCustomField');
1351
+
1352
+ // Should NOT add custom fields to OrderLine (dependency)
1353
+ expect(printed).toContain('fragment OrderLine on OrderLine');
1354
+ expect(printed).not.toContain('orderLineCustomField');
1355
+
1356
+ // Verify the query structure is correct
1357
+ expect(printed).toContain('query GetOrder');
1358
+ expect(printed).toContain('order(id: $id)');
1359
+ expect(printed).toContain('...OrderDetail');
1360
+ });
1361
+
1362
+ it('Can be used to compose fragments in query documents', () => {
1363
+ const productFragment = graphql(`
1364
+ fragment Product on Product {
1365
+ id
1366
+ name
1367
+ }
1368
+ `);
1369
+
1370
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1371
+ customFieldsConfig.set('Product', [
1372
+ { name: 'custom1', type: 'string', list: false },
1373
+ { name: 'custom2', type: 'boolean', list: false },
1374
+ ]);
1375
+
1376
+ // Use addCustomFieldsToFragment to modify the fragment
1377
+ const modifiedFragment = addCustomFieldsToFragment(productFragment, {
1378
+ customFieldsMap: customFieldsConfig,
1379
+ });
1380
+
1381
+ // Then compose it into a query
1382
+ const queryDocument = graphql(
1383
+ `
1384
+ query GetProduct {
1385
+ product {
1386
+ ...Product
1387
+ }
1388
+ }
1389
+ `,
1390
+ [modifiedFragment],
1391
+ );
1392
+
1393
+ // The query should include the modified fragment with custom fields
1394
+ const printed = print(queryDocument);
1395
+ expect(printed).toContain('customFields {');
1396
+ expect(printed).toContain('custom1');
1397
+ expect(printed).toContain('custom2');
1398
+ });
1399
+
1400
+ it('Can selectively modify different fragments with different custom fields', () => {
1401
+ const productFragment = graphql(`
1402
+ fragment Product on Product {
1403
+ id
1404
+ name
1405
+ }
1406
+ `);
1407
+
1408
+ const variantFragment = graphql(`
1409
+ fragment Variant on ProductVariant {
1410
+ id
1411
+ sku
1412
+ }
1413
+ `);
1414
+
1415
+ const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
1416
+ customFieldsConfig.set('Product', [
1417
+ { name: 'productCustom1', type: 'string', list: false },
1418
+ { name: 'productCustom2', type: 'boolean', list: false },
1419
+ ]);
1420
+ customFieldsConfig.set('ProductVariant', [
1421
+ { name: 'variantCustom1', type: 'string', list: false },
1422
+ ]);
1423
+
1424
+ // Selectively modify each fragment with different custom fields
1425
+ const modifiedProductFragment = addCustomFieldsToFragment(productFragment, {
1426
+ customFieldsMap: customFieldsConfig,
1427
+ includeCustomFields: ['productCustom1'], // Only include productCustom1
1428
+ });
1429
+
1430
+ const modifiedVariantFragment = addCustomFieldsToFragment(variantFragment, {
1431
+ customFieldsMap: customFieldsConfig,
1432
+ includeCustomFields: ['variantCustom1'],
1433
+ });
1434
+
1435
+ // Compose into a query
1436
+ const queryDocument = graphql(
1437
+ `
1438
+ query GetProductWithVariants {
1439
+ product {
1440
+ ...Product
1441
+ variants {
1442
+ ...Variant
1443
+ }
1444
+ }
1445
+ }
1446
+ `,
1447
+ [modifiedProductFragment, modifiedVariantFragment],
1448
+ );
1449
+
1450
+ const printed = print(queryDocument);
1451
+ // Product fragment should have only productCustom1
1452
+ expect(printed).toContain('productCustom1');
1453
+ expect(printed).not.toContain('productCustom2');
1454
+ // Variant fragment should have variantCustom1
1455
+ expect(printed).toContain('variantCustom1');
1456
+ });
1457
+ });
1458
+ });