@qwickapps/react-framework 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +1681 -2
  2. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
  3. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
  4. package/dist/components/ErrorBoundary.d.ts +7 -0
  5. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  6. package/dist/components/Html.d.ts +28 -18
  7. package/dist/components/Html.d.ts.map +1 -1
  8. package/dist/components/Logo.d.ts +12 -35
  9. package/dist/components/Logo.d.ts.map +1 -1
  10. package/dist/components/Markdown.d.ts +18 -13
  11. package/dist/components/Markdown.d.ts.map +1 -1
  12. package/dist/components/QwickApp.d.ts +16 -3
  13. package/dist/components/QwickApp.d.ts.map +1 -1
  14. package/dist/components/QwickIcon.d.ts +23 -0
  15. package/dist/components/QwickIcon.d.ts.map +1 -0
  16. package/dist/components/SafeSpan.d.ts +12 -5
  17. package/dist/components/SafeSpan.d.ts.map +1 -1
  18. package/dist/components/Scaffold.d.ts.map +1 -1
  19. package/dist/components/base/ModelView.d.ts +101 -0
  20. package/dist/components/base/ModelView.d.ts.map +1 -0
  21. package/dist/components/base/index.d.ts +11 -0
  22. package/dist/components/base/index.d.ts.map +1 -0
  23. package/dist/components/blocks/Article.d.ts +12 -2
  24. package/dist/components/blocks/Article.d.ts.map +1 -1
  25. package/dist/components/blocks/Code.d.ts +13 -2
  26. package/dist/components/blocks/Code.d.ts.map +1 -1
  27. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  28. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  30. package/dist/components/blocks/Footer.d.ts.map +1 -1
  31. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  32. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  33. package/dist/components/blocks/Image.d.ts +41 -0
  34. package/dist/components/blocks/Image.d.ts.map +1 -0
  35. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  36. package/dist/components/blocks/Section.d.ts +16 -2
  37. package/dist/components/blocks/Section.d.ts.map +1 -1
  38. package/dist/components/blocks/Text.d.ts +41 -0
  39. package/dist/components/blocks/Text.d.ts.map +1 -0
  40. package/dist/components/blocks/index.d.ts +4 -0
  41. package/dist/components/blocks/index.d.ts.map +1 -1
  42. package/dist/components/buttons/Button.d.ts +23 -7
  43. package/dist/components/buttons/Button.d.ts.map +1 -1
  44. package/dist/components/forms/FormBlock.d.ts +19 -13
  45. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  46. package/dist/components/index.d.ts +4 -0
  47. package/dist/components/index.d.ts.map +1 -1
  48. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  49. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  50. package/dist/components/input/HtmlInputField.d.ts +17 -11
  51. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  52. package/dist/components/input/SelectInputField.d.ts +16 -10
  53. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  54. package/dist/components/input/SwitchInputField.d.ts +16 -10
  55. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  56. package/dist/components/input/TextField.d.ts.map +1 -1
  57. package/dist/components/input/TextInputField.d.ts +16 -11
  58. package/dist/components/input/TextInputField.d.ts.map +1 -1
  59. package/dist/components/layout/GridCell.d.ts +23 -6
  60. package/dist/components/layout/GridCell.d.ts.map +1 -1
  61. package/dist/components/layout/GridLayout.d.ts +24 -23
  62. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  63. package/dist/components/pages/FormPage.d.ts.map +1 -1
  64. package/dist/components/pages/Page.d.ts +49 -87
  65. package/dist/components/pages/Page.d.ts.map +1 -1
  66. package/dist/components/pages/index.d.ts +2 -2
  67. package/dist/components/pages/index.d.ts.map +1 -1
  68. package/dist/config/AppConfig.d.ts +49 -0
  69. package/dist/config/AppConfig.d.ts.map +1 -0
  70. package/dist/config/AppConfigBuilder.d.ts +75 -0
  71. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  72. package/dist/config/index.d.ts +13 -0
  73. package/dist/config/index.d.ts.map +1 -0
  74. package/dist/config/types.d.ts +130 -0
  75. package/dist/config/types.d.ts.map +1 -0
  76. package/dist/config.d.ts +15 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.esm.js +451 -0
  79. package/dist/config.js +455 -0
  80. package/dist/contexts/PrintModeContext.d.ts +27 -0
  81. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  82. package/dist/contexts/QwickAppContext.d.ts +2 -2
  83. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  84. package/dist/contexts/index.d.ts +2 -0
  85. package/dist/contexts/index.d.ts.map +1 -1
  86. package/dist/hooks/index.d.ts +2 -0
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/usePrintMode.d.ts +39 -0
  89. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  90. package/dist/index.css +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +10951 -6238
  95. package/dist/index.js +11014 -6287
  96. package/dist/schemas/CodeSchema.d.ts +2 -1
  97. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  98. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  99. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  100. package/dist/schemas/ContentSchema.d.ts +2 -1
  101. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  102. package/dist/schemas/GridCellSchema.d.ts +25 -0
  103. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  104. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  105. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  106. package/dist/schemas/HtmlSchema.d.ts +14 -0
  107. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  108. package/dist/schemas/ImageSchema.d.ts +32 -0
  109. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  110. package/dist/schemas/LogoSchema.d.ts +35 -0
  111. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  112. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  113. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  114. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  115. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  116. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  117. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  118. package/dist/schemas/SectionSchema.d.ts +2 -1
  119. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  120. package/dist/schemas/TextSchema.d.ts +37 -0
  121. package/dist/schemas/TextSchema.d.ts.map +1 -0
  122. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  123. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  124. package/dist/schemas/index.d.ts +15 -1
  125. package/dist/schemas/index.d.ts.map +1 -1
  126. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  127. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  128. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  129. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  130. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  131. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  132. package/dist/schemas/transformers/registry.d.ts +15 -0
  133. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  134. package/dist/schemas/types/Serializable.d.ts +46 -0
  135. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  136. package/dist/utils/htmlTransform.d.ts.map +1 -1
  137. package/dist/utils/reactUtils.d.ts +12 -3
  138. package/dist/utils/reactUtils.d.ts.map +1 -1
  139. package/package.json +17 -3
  140. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  141. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  142. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  143. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  145. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  148. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  150. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  152. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  154. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  155. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  156. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  158. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  159. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  160. package/src/__tests__/components/Logo.test.js +3 -3
  161. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  162. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  163. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  165. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  166. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  167. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  168. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  169. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  172. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  173. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  174. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  175. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  176. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  177. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  178. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  179. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  180. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  181. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  182. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  183. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  184. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  185. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  186. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  187. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  188. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  189. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  190. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  191. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  192. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  193. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  194. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  195. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  196. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  197. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  198. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  199. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  200. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  201. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  202. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  203. package/src/components/ErrorBoundary.tsx +8 -8
  204. package/src/components/Html.tsx +147 -44
  205. package/src/components/Logo.tsx +198 -100
  206. package/src/components/Markdown.tsx +125 -16
  207. package/src/components/QwickApp.tsx +64 -31
  208. package/src/components/QwickIcon.tsx +59 -0
  209. package/src/components/SafeSpan.tsx +65 -10
  210. package/src/components/Scaffold.tsx +2 -8
  211. package/src/components/base/ModelView.tsx +199 -0
  212. package/src/components/base/index.ts +11 -0
  213. package/src/components/blocks/Article.tsx +57 -18
  214. package/src/components/blocks/Code.md +529 -0
  215. package/src/components/blocks/Code.tsx +102 -15
  216. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  217. package/src/components/blocks/FeatureCard.tsx +1 -2
  218. package/src/components/blocks/FeatureGrid.tsx +19 -1
  219. package/src/components/blocks/Footer.tsx +13 -1
  220. package/src/components/blocks/HeroBlock.tsx +87 -20
  221. package/src/components/blocks/Image.tsx +395 -0
  222. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  223. package/src/components/blocks/ProductCard.tsx +1 -1
  224. package/src/components/blocks/Section.tsx +113 -8
  225. package/src/components/blocks/Text.tsx +285 -0
  226. package/src/components/blocks/index.ts +4 -0
  227. package/src/components/buttons/Button.tsx +184 -15
  228. package/src/components/forms/FormBlock.tsx +70 -17
  229. package/src/components/index.ts +5 -0
  230. package/src/components/input/ChoiceInputField.tsx +48 -18
  231. package/src/components/input/HtmlInputField.tsx +48 -18
  232. package/src/components/input/SelectInputField.tsx +48 -16
  233. package/src/components/input/SwitchInputField.tsx +48 -17
  234. package/src/components/input/TextField.tsx +41 -1
  235. package/src/components/input/TextInputField.tsx +52 -18
  236. package/src/components/layout/GridCell.tsx +118 -9
  237. package/src/components/layout/GridLayout.tsx +125 -24
  238. package/src/components/pages/FormPage.tsx +0 -1
  239. package/src/components/pages/Page.css +304 -332
  240. package/src/components/pages/Page.tsx +307 -255
  241. package/src/components/pages/index.ts +2 -2
  242. package/src/config/AppConfig.ts +133 -0
  243. package/src/config/AppConfigBuilder.ts +421 -0
  244. package/src/config/__tests__/AppConfig.test.ts +385 -0
  245. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  246. package/src/config/index.ts +24 -0
  247. package/src/config/types.ts +170 -0
  248. package/src/config.ts +25 -0
  249. package/src/contexts/PrintModeContext.tsx +332 -0
  250. package/src/contexts/QwickAppContext.tsx +2 -2
  251. package/src/contexts/index.ts +2 -0
  252. package/src/hooks/index.ts +5 -1
  253. package/src/hooks/usePrintMode.ts +73 -0
  254. package/src/index.ts +3 -0
  255. package/src/schemas/CodeSchema.ts +3 -3
  256. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  257. package/src/schemas/ContentSchema.ts +2 -1
  258. package/src/schemas/GridCellSchema.ts +164 -0
  259. package/src/schemas/GridLayoutSchema.ts +133 -0
  260. package/src/schemas/HtmlSchema.ts +47 -0
  261. package/src/schemas/ImageSchema.ts +235 -0
  262. package/src/schemas/LogoSchema.ts +241 -0
  263. package/src/schemas/MarkdownSchema.ts +47 -0
  264. package/src/schemas/PageTemplateSchema.ts +186 -0
  265. package/src/schemas/PrintConfigSchema.ts +207 -0
  266. package/src/schemas/README.md +661 -0
  267. package/src/schemas/SectionSchema.ts +2 -1
  268. package/src/schemas/TextSchema.ts +329 -0
  269. package/src/schemas/ViewModelSchema.ts +115 -0
  270. package/src/schemas/index.ts +21 -2
  271. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  272. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  273. package/src/schemas/transformers/registry.ts +72 -0
  274. package/src/schemas/types/Serializable.ts +51 -0
  275. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  276. package/src/stories/Article.stories.tsx +433 -433
  277. package/src/stories/Button.stories.tsx +1 -1
  278. package/src/stories/CardListGrid.stories.tsx +451 -451
  279. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  280. package/src/stories/Code.stories.tsx +1 -1
  281. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  282. package/src/stories/Content.stories.tsx +393 -393
  283. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  284. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  285. package/src/stories/DataProvider.stories.tsx +1192 -1192
  286. package/src/stories/FeatureCard.stories.tsx +557 -557
  287. package/src/stories/FeatureGrid.stories.tsx +594 -594
  288. package/src/stories/Footer.stories.tsx +640 -640
  289. package/src/stories/FormBlock.stories.tsx +760 -760
  290. package/src/stories/FormComponents.stories.tsx +349 -541
  291. package/src/stories/GridCell.stories.tsx +417 -0
  292. package/src/stories/GridLayout.stories.tsx +353 -0
  293. package/src/stories/HeroBlock.stories.tsx +862 -373
  294. package/src/stories/HtmlInputField.stories.tsx +474 -474
  295. package/src/stories/Image.stories.tsx +819 -0
  296. package/src/stories/Introduction.stories.tsx +667 -667
  297. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  298. package/src/stories/Logo.stories.tsx +165 -6
  299. package/src/stories/Markdown.stories.tsx +137 -137
  300. package/src/stories/ModelView.stories.tsx +477 -0
  301. package/src/stories/Page.stories.tsx +688 -688
  302. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  303. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  304. package/src/stories/ProductCard.stories.tsx +424 -424
  305. package/src/stories/QwickApp.stories.tsx +368 -368
  306. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  307. package/src/stories/SafeSpan.stories.tsx +531 -531
  308. package/src/stories/Section.stories.tsx +90 -2
  309. package/src/stories/SelectInputField.stories.tsx +524 -524
  310. package/src/stories/Text.stories.tsx +560 -0
  311. package/src/stories/TextInputField.stories.tsx +443 -443
  312. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  313. package/src/utils/htmlTransform.tsx +74 -53
  314. package/src/utils/reactUtils.tsx +57 -6
  315. package/dist/index.bundled.css +0 -12
  316. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  317. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  318. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  319. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  320. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * QwickApps AppConfig Implementation
3
+ *
4
+ * Immutable configuration object with computed properties for Vite and PWA.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ import type { UserConfig as ViteUserConfig } from 'vite';
10
+ import type {
11
+ AppConfig as IAppConfig,
12
+ AppIdentity,
13
+ BuildConfig,
14
+ PWAConfig,
15
+ UIConfig,
16
+ CopyrightConfig
17
+ } from './types';
18
+
19
+ /**
20
+ * Immutable application configuration
21
+ */
22
+ export class AppConfig implements IAppConfig {
23
+ public readonly app: AppIdentity;
24
+ public readonly build: BuildConfig;
25
+ public readonly pwa: PWAConfig;
26
+ public readonly ui: UIConfig;
27
+ public readonly copyright: CopyrightConfig;
28
+
29
+ constructor(config: {
30
+ app: AppIdentity;
31
+ build: BuildConfig;
32
+ pwa: PWAConfig;
33
+ ui: UIConfig;
34
+ copyright: CopyrightConfig;
35
+ }) {
36
+ // Create frozen, immutable configuration
37
+ this.app = Object.freeze({ ...config.app });
38
+ this.build = Object.freeze({ ...config.build });
39
+ this.pwa = Object.freeze({ ...config.pwa });
40
+ this.ui = Object.freeze({ ...config.ui });
41
+ this.copyright = Object.freeze({ ...config.copyright });
42
+
43
+ // Freeze the entire object
44
+ Object.freeze(this);
45
+ }
46
+
47
+ /**
48
+ * Generate complete Vite configuration
49
+ */
50
+ get viteConfig(): ViteUserConfig {
51
+ return {
52
+ server: {
53
+ port: this.build.port,
54
+ host: true,
55
+ },
56
+ preview: {
57
+ port: this.build.previewPort,
58
+ host: true,
59
+ },
60
+ build: {
61
+ outDir: this.build.outputDir,
62
+ },
63
+ base: this.build.base,
64
+ publicDir: 'public',
65
+ define: {
66
+ __APP_NAME__: JSON.stringify(this.app.name),
67
+ __APP_ID__: JSON.stringify(this.app.id),
68
+ __APP_VERSION__: JSON.stringify(this.app.version),
69
+ },
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Generate PWA manifest
75
+ */
76
+ get pwaManifest(): Record<string, any> {
77
+ return {
78
+ name: this.app.name,
79
+ short_name: this.app.shortName,
80
+ description: this.app.description,
81
+ start_url: this.pwa.startUrl,
82
+ display: this.pwa.display,
83
+ orientation: this.pwa.orientation,
84
+ theme_color: this.pwa.themeColor,
85
+ background_color: this.pwa.backgroundColor,
86
+ scope: this.pwa.scope,
87
+ icons: [
88
+ {
89
+ src: 'pwa-192x192.png',
90
+ sizes: '192x192',
91
+ type: 'image/png',
92
+ },
93
+ {
94
+ src: 'pwa-512x512.png',
95
+ sizes: '512x512',
96
+ type: 'image/png',
97
+ },
98
+ ],
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Convert to plain object (for serialization)
104
+ */
105
+ toJSON(): Record<string, any> {
106
+ return {
107
+ app: this.app,
108
+ build: this.build,
109
+ pwa: this.pwa,
110
+ ui: this.ui,
111
+ copyright: this.copyright,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Create a copy with overrides (returns new immutable instance)
117
+ */
118
+ with(overrides: Partial<{
119
+ app: Partial<AppIdentity>;
120
+ build: Partial<BuildConfig>;
121
+ pwa: Partial<PWAConfig>;
122
+ ui: Partial<UIConfig>;
123
+ copyright: Partial<CopyrightConfig>;
124
+ }>): AppConfig {
125
+ return new AppConfig({
126
+ app: { ...this.app, ...overrides.app },
127
+ build: { ...this.build, ...overrides.build },
128
+ pwa: { ...this.pwa, ...overrides.pwa },
129
+ ui: { ...this.ui, ...overrides.ui },
130
+ copyright: { ...this.copyright, ...overrides.copyright },
131
+ });
132
+ }
133
+ }
@@ -0,0 +1,421 @@
1
+ /**
2
+ * QwickApps AppConfigBuilder
3
+ *
4
+ * Builder pattern for creating validated, immutable AppConfig instances.
5
+ * Supports fluent API and deferred validation.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ // Node.js imports (only available in build/development environment)
11
+ let fs: typeof import('fs') | undefined;
12
+ let path: typeof import('path') | undefined;
13
+
14
+ try {
15
+ // Only import if we're in Node.js environment
16
+ if (typeof window === 'undefined' && typeof process !== 'undefined') {
17
+ fs = require('fs');
18
+ path = require('path');
19
+ }
20
+ } catch (error) {
21
+ // Browser environment - fs/path not available
22
+ }
23
+ import { AppConfig } from './AppConfig';
24
+ import type {
25
+ AppIdentity,
26
+ BuildConfig,
27
+ PWAConfig,
28
+ UIConfig,
29
+ CopyrightConfig,
30
+ AppConfigOptions,
31
+ ValidationResult
32
+ } from './types';
33
+
34
+ /**
35
+ * Builder for creating AppConfig instances with validation
36
+ */
37
+ export class AppConfigBuilder {
38
+ private config: {
39
+ app: Partial<{
40
+ id: string;
41
+ name: string;
42
+ shortName: string;
43
+ description: string;
44
+ version: string;
45
+ logo: string;
46
+ author?: string;
47
+ homepage?: string;
48
+ }>;
49
+ build: Partial<{
50
+ port: number;
51
+ previewPort: number;
52
+ outputDir: string;
53
+ publicPath: string;
54
+ base: string;
55
+ }>;
56
+ pwa: Partial<{
57
+ themeColor: string;
58
+ backgroundColor: string;
59
+ startUrl: string;
60
+ scope: string;
61
+ display: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';
62
+ orientation?: 'portrait' | 'landscape' | 'any';
63
+ }>;
64
+ ui: Partial<{
65
+ defaultTheme: 'light' | 'dark' | 'system';
66
+ defaultPalette: string;
67
+ enableScaffolding: boolean;
68
+ showThemeSwitcher: boolean;
69
+ showPaletteSwitcher: boolean;
70
+ }>;
71
+ copyright: Partial<{
72
+ year: 'auto' | number;
73
+ author: string;
74
+ text?: string;
75
+ }>;
76
+ } = {
77
+ app: {},
78
+ build: {},
79
+ pwa: {},
80
+ ui: {},
81
+ copyright: {},
82
+ };
83
+
84
+ private options: AppConfigOptions = {};
85
+
86
+ private constructor() {}
87
+
88
+ /**
89
+ * Create a new AppConfigBuilder instance
90
+ */
91
+ static create(): AppConfigBuilder {
92
+ return new AppConfigBuilder();
93
+ }
94
+
95
+ /**
96
+ * Load configuration from JSON file (Node.js only)
97
+ */
98
+ withConfig(configPath: string): AppConfigBuilder {
99
+ if (!fs || !path) {
100
+ // Only warn in development, not in tests or production browsers
101
+ if (process.env.NODE_ENV !== 'test' && typeof window === 'undefined') {
102
+ console.warn('Warning: File system operations not available in browser environment');
103
+ }
104
+ return this;
105
+ }
106
+
107
+ try {
108
+ const fullPath = path.resolve(configPath);
109
+ const configData = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
110
+
111
+ if (configData.app) Object.assign(this.config.app, configData.app);
112
+ if (configData.build) Object.assign(this.config.build, configData.build);
113
+ if (configData.pwa) Object.assign(this.config.pwa, configData.pwa);
114
+ if (configData.ui) Object.assign(this.config.ui, configData.ui);
115
+ if (configData.copyright) Object.assign(this.config.copyright, configData.copyright);
116
+ } catch (error) {
117
+ // Don't throw here - will be handled in build()
118
+ console.warn(`Warning: Could not load config file: ${configPath}`);
119
+ }
120
+
121
+ return this;
122
+ }
123
+
124
+ /**
125
+ * Set builder options
126
+ */
127
+ withOptions(options: AppConfigOptions): AppConfigBuilder {
128
+ this.options = { ...this.options, ...options };
129
+ return this;
130
+ }
131
+
132
+ // App Identity Methods
133
+ withName(name: string): AppConfigBuilder {
134
+ this.config.app.name = name;
135
+ return this;
136
+ }
137
+
138
+ withId(id: string): AppConfigBuilder {
139
+ this.config.app.id = id;
140
+ return this;
141
+ }
142
+
143
+ withShortName(shortName: string): AppConfigBuilder {
144
+ this.config.app.shortName = shortName;
145
+ return this;
146
+ }
147
+
148
+ withDescription(description: string): AppConfigBuilder {
149
+ this.config.app.description = description;
150
+ return this;
151
+ }
152
+
153
+ withVersion(version: string): AppConfigBuilder {
154
+ this.config.app.version = version;
155
+ return this;
156
+ }
157
+
158
+ withLogo(logo: string): AppConfigBuilder {
159
+ this.config.app.logo = logo;
160
+ return this;
161
+ }
162
+
163
+ withAuthor(author: string): AppConfigBuilder {
164
+ this.config.app.author = author;
165
+ return this;
166
+ }
167
+
168
+ withHomepage(homepage: string): AppConfigBuilder {
169
+ this.config.app.homepage = homepage;
170
+ return this;
171
+ }
172
+
173
+ // Build Config Methods
174
+ withPort(port: number): AppConfigBuilder {
175
+ this.config.build.port = port;
176
+ return this;
177
+ }
178
+
179
+ withPreviewPort(previewPort: number): AppConfigBuilder {
180
+ this.config.build.previewPort = previewPort;
181
+ return this;
182
+ }
183
+
184
+ withOutputDir(outputDir: string): AppConfigBuilder {
185
+ this.config.build.outputDir = outputDir;
186
+ return this;
187
+ }
188
+
189
+ withPublicPath(publicPath: string): AppConfigBuilder {
190
+ this.config.build.publicPath = publicPath;
191
+ return this;
192
+ }
193
+
194
+ withBase(base: string): AppConfigBuilder {
195
+ this.config.build.base = base;
196
+ return this;
197
+ }
198
+
199
+ // PWA Config Methods
200
+ withThemeColor(themeColor: string): AppConfigBuilder {
201
+ this.config.pwa.themeColor = themeColor;
202
+ return this;
203
+ }
204
+
205
+ withBackgroundColor(backgroundColor: string): AppConfigBuilder {
206
+ this.config.pwa.backgroundColor = backgroundColor;
207
+ return this;
208
+ }
209
+
210
+ withStartUrl(startUrl: string): AppConfigBuilder {
211
+ this.config.pwa.startUrl = startUrl;
212
+ return this;
213
+ }
214
+
215
+ withScope(scope: string): AppConfigBuilder {
216
+ this.config.pwa.scope = scope;
217
+ return this;
218
+ }
219
+
220
+ withDisplay(display: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser'): AppConfigBuilder {
221
+ this.config.pwa.display = display;
222
+ return this;
223
+ }
224
+
225
+ withOrientation(orientation: 'portrait' | 'landscape' | 'any'): AppConfigBuilder {
226
+ this.config.pwa.orientation = orientation;
227
+ return this;
228
+ }
229
+
230
+ // UI Config Methods
231
+ withDefaultTheme(defaultTheme: 'light' | 'dark' | 'system'): AppConfigBuilder {
232
+ this.config.ui.defaultTheme = defaultTheme;
233
+ return this;
234
+ }
235
+
236
+ withDefaultPalette(defaultPalette: string): AppConfigBuilder {
237
+ this.config.ui.defaultPalette = defaultPalette;
238
+ return this;
239
+ }
240
+
241
+ withScaffolding(enableScaffolding: boolean): AppConfigBuilder {
242
+ this.config.ui.enableScaffolding = enableScaffolding;
243
+ return this;
244
+ }
245
+
246
+ withThemeSwitcher(showThemeSwitcher: boolean): AppConfigBuilder {
247
+ this.config.ui.showThemeSwitcher = showThemeSwitcher;
248
+ return this;
249
+ }
250
+
251
+ withPaletteSwitcher(showPaletteSwitcher: boolean): AppConfigBuilder {
252
+ this.config.ui.showPaletteSwitcher = showPaletteSwitcher;
253
+ return this;
254
+ }
255
+
256
+ // Copyright Methods
257
+ withCopyright(year: 'auto' | number, author: string, text?: string): AppConfigBuilder {
258
+ this.config.copyright.year = year;
259
+ this.config.copyright.author = author;
260
+ if (text !== undefined) this.config.copyright.text = text;
261
+ return this;
262
+ }
263
+
264
+ withCopyrightYear(year: 'auto' | number): AppConfigBuilder {
265
+ this.config.copyright.year = year;
266
+ return this;
267
+ }
268
+
269
+ withCopyrightAuthor(author: string): AppConfigBuilder {
270
+ this.config.copyright.author = author;
271
+ return this;
272
+ }
273
+
274
+ withCopyrightText(text: string): AppConfigBuilder {
275
+ this.config.copyright.text = text;
276
+ return this;
277
+ }
278
+
279
+ /**
280
+ * Build and validate the configuration
281
+ */
282
+ build(): AppConfig {
283
+ const validation = this.validate();
284
+
285
+ // Print warnings
286
+ validation.warnings.forEach(warning => {
287
+ console.warn(`⚠️ AppConfig: ${warning}`);
288
+ });
289
+
290
+ // Throw on errors
291
+ if (!validation.isValid) {
292
+ throw new Error(`AppConfig validation failed:\n${validation.errors.map(e => ` - ${e}`).join('\n')}`);
293
+ }
294
+
295
+ // Apply defaults and build
296
+ const completeConfig = this.applyDefaults();
297
+ return new AppConfig(completeConfig);
298
+ }
299
+
300
+ /**
301
+ * Validate the current configuration
302
+ */
303
+ private validate(): ValidationResult {
304
+ const warnings: string[] = [];
305
+ const errors: string[] = [];
306
+
307
+ // Required fields validation
308
+ if (!this.config.app.name) errors.push('App name is required');
309
+ if (!this.config.app.id) errors.push('App ID is required');
310
+
311
+ // Copyright validation - author will be defaulted if not provided
312
+ if (this.config.copyright.year &&
313
+ this.config.copyright.year !== 'auto' &&
314
+ (!Number.isInteger(this.config.copyright.year) || this.config.copyright.year < 1900)) {
315
+ warnings.push('Copyright year should be "auto" or a valid year >= 1900');
316
+ }
317
+
318
+ // Logo file validation (warning only) - only in Node.js environment
319
+ if (this.config.app.logo && !this.options.skipValidation && fs && path) {
320
+ const logoPath = path.resolve(this.options.baseDir || process.cwd(), this.config.app.logo);
321
+ if (!fs.existsSync(logoPath)) {
322
+ warnings.push(`Logo file not found: ${this.config.app.logo} (will use default)`);
323
+ }
324
+ }
325
+
326
+ // Port validation
327
+ if (this.config.build.port !== undefined) {
328
+ if (!Number.isInteger(this.config.build.port) || this.config.build.port < 1 || this.config.build.port > 65535) {
329
+ errors.push('Port must be a valid integer between 1 and 65535');
330
+ }
331
+ }
332
+
333
+ if (this.config.build.previewPort !== undefined) {
334
+ if (!Number.isInteger(this.config.build.previewPort) || this.config.build.previewPort < 1 || this.config.build.previewPort > 65535) {
335
+ errors.push('Preview port must be a valid integer between 1 and 65535');
336
+ }
337
+ }
338
+
339
+ // Color validation (warnings only)
340
+ if (this.config.pwa.themeColor && !this.isValidColor(this.config.pwa.themeColor)) {
341
+ warnings.push(`Theme color may not be valid: ${this.config.pwa.themeColor}`);
342
+ }
343
+
344
+ if (this.config.pwa.backgroundColor && !this.isValidColor(this.config.pwa.backgroundColor)) {
345
+ warnings.push(`Background color may not be valid: ${this.config.pwa.backgroundColor}`);
346
+ }
347
+
348
+ return {
349
+ warnings,
350
+ errors,
351
+ isValid: errors.length === 0,
352
+ };
353
+ }
354
+
355
+ /**
356
+ * Apply default values to configuration
357
+ */
358
+ private applyDefaults(): {
359
+ app: AppIdentity;
360
+ build: BuildConfig;
361
+ pwa: PWAConfig;
362
+ ui: UIConfig;
363
+ copyright: CopyrightConfig;
364
+ } {
365
+ const app: AppIdentity = {
366
+ id: this.config.app.id!,
367
+ name: this.config.app.name!,
368
+ shortName: this.config.app.shortName || this.config.app.name!,
369
+ description: this.config.app.description || this.config.app.name!,
370
+ version: this.config.app.version || '1.0.0',
371
+ logo: this.config.app.logo || '/favicon.ico',
372
+ author: this.config.app.author,
373
+ homepage: this.config.app.homepage,
374
+ };
375
+
376
+ const build: BuildConfig = {
377
+ port: this.config.build.port || 3000,
378
+ previewPort: this.config.build.previewPort || 4173,
379
+ outputDir: this.config.build.outputDir || 'dist',
380
+ publicPath: this.config.build.publicPath || '/',
381
+ base: this.config.build.base || '/',
382
+ };
383
+
384
+ const pwa: PWAConfig = {
385
+ themeColor: this.config.pwa.themeColor || '#000000',
386
+ backgroundColor: this.config.pwa.backgroundColor || '#ffffff',
387
+ startUrl: this.config.pwa.startUrl || '/',
388
+ scope: this.config.pwa.scope || '/',
389
+ display: this.config.pwa.display || 'standalone',
390
+ orientation: this.config.pwa.orientation,
391
+ };
392
+
393
+ const ui: UIConfig = {
394
+ defaultTheme: this.config.ui.defaultTheme || 'light',
395
+ defaultPalette: this.config.ui.defaultPalette || 'default',
396
+ enableScaffolding: this.config.ui.enableScaffolding ?? true,
397
+ showThemeSwitcher: this.config.ui.showThemeSwitcher ?? false,
398
+ showPaletteSwitcher: this.config.ui.showPaletteSwitcher ?? false,
399
+ };
400
+
401
+ const copyright: CopyrightConfig = {
402
+ year: this.config.copyright.year || 'auto',
403
+ author: this.config.copyright.author || app.author || app.name,
404
+ text: this.config.copyright.text,
405
+ };
406
+
407
+ return { app, build, pwa, ui, copyright };
408
+ }
409
+
410
+ /**
411
+ * Simple color validation
412
+ */
413
+ private isValidColor(color: string): boolean {
414
+ // Basic hex color validation
415
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color) ||
416
+ // Named colors (basic check)
417
+ /^(black|white|red|green|blue|yellow|orange|purple|pink|gray|grey)$/i.test(color) ||
418
+ // RGB/RGBA
419
+ /^rgba?\([^)]+\)$/i.test(color);
420
+ }
421
+ }