@next-box/appsettings 0.0.0-stage → 0.0.4

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 (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -2
  3. package/bin/appsettings.mjs +7 -0
  4. package/dist/cjs/cli/command.cjs +2 -0
  5. package/dist/cjs/cli/command.cjs.map +1 -0
  6. package/dist/cjs/cli/handler.cjs +2 -0
  7. package/dist/cjs/cli/handler.cjs.map +1 -0
  8. package/dist/cjs/cli.cjs +2 -0
  9. package/dist/cjs/cli.cjs.map +1 -0
  10. package/dist/cjs/components/AppSettingsProvider.cjs +3 -0
  11. package/dist/cjs/components/AppSettingsProvider.cjs.map +1 -0
  12. package/dist/cjs/components/Setting.cjs +3 -0
  13. package/dist/cjs/components/Setting.cjs.map +1 -0
  14. package/dist/cjs/config/AppSettingsImplementation.cjs +2 -0
  15. package/dist/cjs/config/AppSettingsImplementation.cjs.map +1 -0
  16. package/dist/cjs/config/ConfigMerger.cjs +2 -0
  17. package/dist/cjs/config/ConfigMerger.cjs.map +1 -0
  18. package/dist/cjs/config/ConfigReader.cjs +2 -0
  19. package/dist/cjs/config/ConfigReader.cjs.map +1 -0
  20. package/dist/cjs/config/ConfigStore.cjs +2 -0
  21. package/dist/cjs/config/ConfigStore.cjs.map +1 -0
  22. package/dist/cjs/config/ConfigValidator.cjs +2 -0
  23. package/dist/cjs/config/ConfigValidator.cjs.map +1 -0
  24. package/dist/cjs/config/ScopedSettingsImplementation.cjs +2 -0
  25. package/dist/cjs/config/ScopedSettingsImplementation.cjs.map +1 -0
  26. package/dist/cjs/config/createAppSettings.cjs +2 -0
  27. package/dist/cjs/config/createAppSettings.cjs.map +1 -0
  28. package/dist/cjs/config/helpers/freezeDeep.cjs +2 -0
  29. package/dist/cjs/config/helpers/freezeDeep.cjs.map +1 -0
  30. package/dist/cjs/hooks/useAppSetting.cjs +2 -0
  31. package/dist/cjs/hooks/useAppSetting.cjs.map +1 -0
  32. package/dist/cjs/hooks/useAppSettings.cjs +2 -0
  33. package/dist/cjs/hooks/useAppSettings.cjs.map +1 -0
  34. package/dist/cjs/hooks/useAppSettingsContext.cjs +2 -0
  35. package/dist/cjs/hooks/useAppSettingsContext.cjs.map +1 -0
  36. package/dist/cjs/index.cjs +2 -0
  37. package/dist/cjs/index.cjs.map +1 -0
  38. package/dist/cjs/server.cjs +2 -0
  39. package/dist/cjs/server.cjs.map +1 -0
  40. package/dist/cjs/sources/AppSettingsSource.cjs +2 -0
  41. package/dist/cjs/sources/AppSettingsSource.cjs.map +1 -0
  42. package/dist/cjs/sources/EnvironmentSource.cjs +2 -0
  43. package/dist/cjs/sources/EnvironmentSource.cjs.map +1 -0
  44. package/dist/cjs/sources/MemorySource.cjs +2 -0
  45. package/dist/cjs/sources/MemorySource.cjs.map +1 -0
  46. package/dist/cjs/sources/RemoteSource.cjs +2 -0
  47. package/dist/cjs/sources/RemoteSource.cjs.map +1 -0
  48. package/dist/cjs/sources/helpers/loadJson.cjs +2 -0
  49. package/dist/cjs/sources/helpers/loadJson.cjs.map +1 -0
  50. package/dist/esm/cli/command.mjs +2 -0
  51. package/dist/esm/cli/command.mjs.map +1 -0
  52. package/dist/esm/cli/handler.mjs +2 -0
  53. package/dist/esm/cli/handler.mjs.map +1 -0
  54. package/dist/esm/cli.mjs +2 -0
  55. package/dist/esm/cli.mjs.map +1 -0
  56. package/dist/esm/components/AppSettingsProvider.mjs +3 -0
  57. package/dist/esm/components/AppSettingsProvider.mjs.map +1 -0
  58. package/dist/esm/components/Setting.mjs +3 -0
  59. package/dist/esm/components/Setting.mjs.map +1 -0
  60. package/dist/esm/config/AppSettingsImplementation.mjs +2 -0
  61. package/dist/esm/config/AppSettingsImplementation.mjs.map +1 -0
  62. package/dist/esm/config/ConfigMerger.mjs +2 -0
  63. package/dist/esm/config/ConfigMerger.mjs.map +1 -0
  64. package/dist/esm/config/ConfigReader.mjs +2 -0
  65. package/dist/esm/config/ConfigReader.mjs.map +1 -0
  66. package/dist/esm/config/ConfigStore.mjs +2 -0
  67. package/dist/esm/config/ConfigStore.mjs.map +1 -0
  68. package/dist/esm/config/ConfigValidator.mjs +2 -0
  69. package/dist/esm/config/ConfigValidator.mjs.map +1 -0
  70. package/dist/esm/config/ScopedSettingsImplementation.mjs +2 -0
  71. package/dist/esm/config/ScopedSettingsImplementation.mjs.map +1 -0
  72. package/dist/esm/config/createAppSettings.mjs +2 -0
  73. package/dist/esm/config/createAppSettings.mjs.map +1 -0
  74. package/dist/esm/config/helpers/freezeDeep.mjs +2 -0
  75. package/dist/esm/config/helpers/freezeDeep.mjs.map +1 -0
  76. package/dist/esm/hooks/useAppSetting.mjs +2 -0
  77. package/dist/esm/hooks/useAppSetting.mjs.map +1 -0
  78. package/dist/esm/hooks/useAppSettings.mjs +2 -0
  79. package/dist/esm/hooks/useAppSettings.mjs.map +1 -0
  80. package/dist/esm/hooks/useAppSettingsContext.mjs +2 -0
  81. package/dist/esm/hooks/useAppSettingsContext.mjs.map +1 -0
  82. package/dist/esm/index.mjs +2 -0
  83. package/dist/esm/index.mjs.map +1 -0
  84. package/dist/esm/server.mjs +2 -0
  85. package/dist/esm/server.mjs.map +1 -0
  86. package/dist/esm/sources/AppSettingsSource.mjs +2 -0
  87. package/dist/esm/sources/AppSettingsSource.mjs.map +1 -0
  88. package/dist/esm/sources/EnvironmentSource.mjs +2 -0
  89. package/dist/esm/sources/EnvironmentSource.mjs.map +1 -0
  90. package/dist/esm/sources/MemorySource.mjs +2 -0
  91. package/dist/esm/sources/MemorySource.mjs.map +1 -0
  92. package/dist/esm/sources/RemoteSource.mjs +2 -0
  93. package/dist/esm/sources/RemoteSource.mjs.map +1 -0
  94. package/dist/esm/sources/helpers/loadJson.mjs +2 -0
  95. package/dist/esm/sources/helpers/loadJson.mjs.map +1 -0
  96. package/dist/production.analysis.txt +89 -0
  97. package/dist/types/cjs/__testUtils__/components/ErrorBoundary.d.cts +14 -0
  98. package/dist/types/cjs/__testUtils__/components/ErrorBoundary.d.cts.map +1 -0
  99. package/dist/types/cjs/cli/command.d.cts +7 -0
  100. package/dist/types/cjs/cli/command.d.cts.map +1 -0
  101. package/dist/types/cjs/cli/handler.d.cts +7 -0
  102. package/dist/types/cjs/cli/handler.d.cts.map +1 -0
  103. package/dist/types/cjs/cli.d.cts +2 -0
  104. package/dist/types/cjs/cli.d.cts.map +1 -0
  105. package/dist/types/cjs/components/AppSettingsProvider.d.cts +7 -0
  106. package/dist/types/cjs/components/AppSettingsProvider.d.cts.map +1 -0
  107. package/dist/types/cjs/components/Setting.d.cts +20 -0
  108. package/dist/types/cjs/components/Setting.d.cts.map +1 -0
  109. package/dist/types/cjs/config/AppSettingsImplementation.d.cts +15 -0
  110. package/dist/types/cjs/config/AppSettingsImplementation.d.cts.map +1 -0
  111. package/dist/types/cjs/config/ConfigMerger.d.cts +4 -0
  112. package/dist/types/cjs/config/ConfigMerger.d.cts.map +1 -0
  113. package/dist/types/cjs/config/ConfigReader.d.cts +14 -0
  114. package/dist/types/cjs/config/ConfigReader.d.cts.map +1 -0
  115. package/dist/types/cjs/config/ConfigStore.d.cts +6 -0
  116. package/dist/types/cjs/config/ConfigStore.d.cts.map +1 -0
  117. package/dist/types/cjs/config/ConfigValidator.d.cts +7 -0
  118. package/dist/types/cjs/config/ConfigValidator.d.cts.map +1 -0
  119. package/dist/types/cjs/config/ScopedSettingsImplementation.d.cts +13 -0
  120. package/dist/types/cjs/config/ScopedSettingsImplementation.d.cts.map +1 -0
  121. package/dist/types/cjs/config/createAppSettings.d.cts +3 -0
  122. package/dist/types/cjs/config/createAppSettings.d.cts.map +1 -0
  123. package/dist/types/cjs/config/helpers/freezeDeep.d.cts +2 -0
  124. package/dist/types/cjs/config/helpers/freezeDeep.d.cts.map +1 -0
  125. package/dist/types/cjs/hooks/useAppSetting.d.cts +6 -0
  126. package/dist/types/cjs/hooks/useAppSetting.d.cts.map +1 -0
  127. package/dist/types/cjs/hooks/useAppSettings.d.cts +6 -0
  128. package/dist/types/cjs/hooks/useAppSettings.d.cts.map +1 -0
  129. package/dist/types/cjs/hooks/useAppSettingsContext.d.cts +5 -0
  130. package/dist/types/cjs/hooks/useAppSettingsContext.d.cts.map +1 -0
  131. package/dist/types/cjs/index.d.cts +10 -0
  132. package/dist/types/cjs/index.d.cts.map +1 -0
  133. package/dist/types/cjs/server.d.cts +8 -0
  134. package/dist/types/cjs/server.d.cts.map +1 -0
  135. package/dist/types/cjs/sources/AppSettingsSource.d.cts +14 -0
  136. package/dist/types/cjs/sources/AppSettingsSource.d.cts.map +1 -0
  137. package/dist/types/cjs/sources/EnvironmentSource.d.cts +16 -0
  138. package/dist/types/cjs/sources/EnvironmentSource.d.cts.map +1 -0
  139. package/dist/types/cjs/sources/MemorySource.d.cts +12 -0
  140. package/dist/types/cjs/sources/MemorySource.d.cts.map +1 -0
  141. package/dist/types/cjs/sources/RemoteSource.d.cts +16 -0
  142. package/dist/types/cjs/sources/RemoteSource.d.cts.map +1 -0
  143. package/dist/types/cjs/sources/helpers/loadJson.d.cts +3 -0
  144. package/dist/types/cjs/sources/helpers/loadJson.d.cts.map +1 -0
  145. package/dist/types/cjs/types/AppSettings.types.d.cts +26 -0
  146. package/dist/types/cjs/types/AppSettings.types.d.cts.map +1 -0
  147. package/dist/types/cjs/types/Config.types.d.cts +12 -0
  148. package/dist/types/cjs/types/Config.types.d.cts.map +1 -0
  149. package/dist/types/cjs/types/Source.types.d.cts +5 -0
  150. package/dist/types/cjs/types/Source.types.d.cts.map +1 -0
  151. package/dist/types/esm/__testUtils__/components/ErrorBoundary.d.ts +14 -0
  152. package/dist/types/esm/__testUtils__/components/ErrorBoundary.d.ts.map +1 -0
  153. package/dist/types/esm/cli/command.d.ts +7 -0
  154. package/dist/types/esm/cli/command.d.ts.map +1 -0
  155. package/dist/types/esm/cli/handler.d.ts +7 -0
  156. package/dist/types/esm/cli/handler.d.ts.map +1 -0
  157. package/dist/types/esm/cli.d.ts +2 -0
  158. package/dist/types/esm/cli.d.ts.map +1 -0
  159. package/dist/types/esm/components/AppSettingsProvider.d.ts +7 -0
  160. package/dist/types/esm/components/AppSettingsProvider.d.ts.map +1 -0
  161. package/dist/types/esm/components/Setting.d.ts +20 -0
  162. package/dist/types/esm/components/Setting.d.ts.map +1 -0
  163. package/dist/types/esm/config/AppSettingsImplementation.d.ts +15 -0
  164. package/dist/types/esm/config/AppSettingsImplementation.d.ts.map +1 -0
  165. package/dist/types/esm/config/ConfigMerger.d.ts +4 -0
  166. package/dist/types/esm/config/ConfigMerger.d.ts.map +1 -0
  167. package/dist/types/esm/config/ConfigReader.d.ts +14 -0
  168. package/dist/types/esm/config/ConfigReader.d.ts.map +1 -0
  169. package/dist/types/esm/config/ConfigStore.d.ts +6 -0
  170. package/dist/types/esm/config/ConfigStore.d.ts.map +1 -0
  171. package/dist/types/esm/config/ConfigValidator.d.ts +7 -0
  172. package/dist/types/esm/config/ConfigValidator.d.ts.map +1 -0
  173. package/dist/types/esm/config/ScopedSettingsImplementation.d.ts +13 -0
  174. package/dist/types/esm/config/ScopedSettingsImplementation.d.ts.map +1 -0
  175. package/dist/types/esm/config/createAppSettings.d.ts +3 -0
  176. package/dist/types/esm/config/createAppSettings.d.ts.map +1 -0
  177. package/dist/types/esm/config/helpers/freezeDeep.d.ts +2 -0
  178. package/dist/types/esm/config/helpers/freezeDeep.d.ts.map +1 -0
  179. package/dist/types/esm/hooks/useAppSetting.d.ts +6 -0
  180. package/dist/types/esm/hooks/useAppSetting.d.ts.map +1 -0
  181. package/dist/types/esm/hooks/useAppSettings.d.ts +6 -0
  182. package/dist/types/esm/hooks/useAppSettings.d.ts.map +1 -0
  183. package/dist/types/esm/hooks/useAppSettingsContext.d.ts +5 -0
  184. package/dist/types/esm/hooks/useAppSettingsContext.d.ts.map +1 -0
  185. package/dist/types/esm/index.d.ts +10 -0
  186. package/dist/types/esm/index.d.ts.map +1 -0
  187. package/dist/types/esm/server.d.ts +8 -0
  188. package/dist/types/esm/server.d.ts.map +1 -0
  189. package/dist/types/esm/sources/AppSettingsSource.d.ts +14 -0
  190. package/dist/types/esm/sources/AppSettingsSource.d.ts.map +1 -0
  191. package/dist/types/esm/sources/EnvironmentSource.d.ts +16 -0
  192. package/dist/types/esm/sources/EnvironmentSource.d.ts.map +1 -0
  193. package/dist/types/esm/sources/MemorySource.d.ts +12 -0
  194. package/dist/types/esm/sources/MemorySource.d.ts.map +1 -0
  195. package/dist/types/esm/sources/RemoteSource.d.ts +16 -0
  196. package/dist/types/esm/sources/RemoteSource.d.ts.map +1 -0
  197. package/dist/types/esm/sources/helpers/loadJson.d.ts +3 -0
  198. package/dist/types/esm/sources/helpers/loadJson.d.ts.map +1 -0
  199. package/dist/types/esm/types/AppSettings.types.d.ts +26 -0
  200. package/dist/types/esm/types/AppSettings.types.d.ts.map +1 -0
  201. package/dist/types/esm/types/Config.types.d.ts +12 -0
  202. package/dist/types/esm/types/Config.types.d.ts.map +1 -0
  203. package/dist/types/esm/types/Source.types.d.ts +5 -0
  204. package/dist/types/esm/types/Source.types.d.ts.map +1 -0
  205. package/dist/types/tsconfig.build.tsbuildinfo +1 -0
  206. package/package.json +85 -3
  207. package/rollup.config.mjs +17 -0
  208. package/src/__testUtils__/AppSettingsSchema.d.ts +9 -0
  209. package/src/__testUtils__/components/ErrorBoundary.tsx +25 -0
  210. package/src/cli/command.test.ts +36 -0
  211. package/src/cli/command.ts +33 -0
  212. package/src/cli/handler.test.ts +61 -0
  213. package/src/cli/handler.ts +58 -0
  214. package/src/cli.test.ts +45 -0
  215. package/src/cli.ts +13 -0
  216. package/src/components/AppSettingsProvider.test.tsx +25 -0
  217. package/src/components/AppSettingsProvider.tsx +18 -0
  218. package/src/components/Setting.test.tsx +59 -0
  219. package/src/components/Setting.tsx +43 -0
  220. package/src/config/AppSettingsImplementation.test.ts +65 -0
  221. package/src/config/AppSettingsImplementation.ts +61 -0
  222. package/src/config/ConfigMerger.test.ts +44 -0
  223. package/src/config/ConfigMerger.ts +9 -0
  224. package/src/config/ConfigReader.test.ts +56 -0
  225. package/src/config/ConfigReader.ts +45 -0
  226. package/src/config/ConfigStore.test.ts +32 -0
  227. package/src/config/ConfigStore.ts +12 -0
  228. package/src/config/ConfigValidator.test.ts +30 -0
  229. package/src/config/ConfigValidator.ts +13 -0
  230. package/src/config/ScopedSettingsImplementation.test.ts +31 -0
  231. package/src/config/ScopedSettingsImplementation.ts +36 -0
  232. package/src/config/createAppSettings.test.ts +84 -0
  233. package/src/config/createAppSettings.ts +18 -0
  234. package/src/config/helpers/freezeDeep.test.ts +41 -0
  235. package/src/config/helpers/freezeDeep.ts +15 -0
  236. package/src/hooks/useAppSetting.test.tsx +37 -0
  237. package/src/hooks/useAppSetting.ts +14 -0
  238. package/src/hooks/useAppSettings.test.tsx +37 -0
  239. package/src/hooks/useAppSettings.ts +17 -0
  240. package/src/hooks/useAppSettingsContext.test.tsx +45 -0
  241. package/src/hooks/useAppSettingsContext.ts +17 -0
  242. package/src/index.ts +11 -0
  243. package/src/server.ts +7 -0
  244. package/src/sources/AppSettingsSource.test.ts +61 -0
  245. package/src/sources/AppSettingsSource.ts +30 -0
  246. package/src/sources/EnvironmentSource.test.ts +45 -0
  247. package/src/sources/EnvironmentSource.ts +87 -0
  248. package/src/sources/MemorySource.test.ts +31 -0
  249. package/src/sources/MemorySource.ts +21 -0
  250. package/src/sources/RemoteSource.test.ts +80 -0
  251. package/src/sources/RemoteSource.ts +50 -0
  252. package/src/sources/helpers/loadJson.test.ts +74 -0
  253. package/src/sources/helpers/loadJson.ts +29 -0
  254. package/src/types/AppSettings.types.test.ts +61 -0
  255. package/src/types/AppSettings.types.ts +30 -0
  256. package/src/types/Config.types.test.ts +36 -0
  257. package/src/types/Config.types.ts +27 -0
  258. package/src/types/Source.types.ts +4 -0
  259. package/tsconfig.build.json +10 -0
  260. package/tsconfig.json +10 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 miami-man
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
1
+ # @next-box/appsettings
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A type-safe configuration library for TypeScript applications, inspired by .NET's `appsettings.json`.
4
+
5
+ [![npm version](https://badge.fury.io/js/%40next-box%2Fappsettings.svg)](https://badge.fury.io/js/%40next-box%2Fappsettings)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ Applications often need to combine configuration from defaults, files, and environment variables. Keeping those values consistent and validated across an application can be difficult.
9
+
10
+ `@next-box/appsettings` loads and validates configuration on the server, then provides a read-only settings context for React client components.
11
+
12
+ ## Installation
13
+
14
+ ```shell
15
+ # terminal
16
+ npm add @next-box/appsettings
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Define a schema
22
+
23
+ ```ts
24
+ // ./src/appSettings.schema.ts
25
+ import { z } from 'zod';
26
+
27
+ export const appSettingsSchema = z.object({
28
+ App: z.object({
29
+ Name: z.string(),
30
+ Environment: z.enum(['development', 'test', 'production']),
31
+ }),
32
+ Api: z.object({
33
+ BaseUrl: z.string().url(),
34
+ Timeout: z.number(),
35
+ }),
36
+ Features: z.object({
37
+ DarkMode: z.boolean(),
38
+ NewDashboard: z.boolean(),
39
+ }),
40
+ });
41
+
42
+ export type AppSettingsConfig = z.infer<typeof appSettingsSchema>;
43
+ ```
44
+
45
+ ### Load configuration on the server
46
+
47
+ `createAppSettings` is exported from `@next-box/appsettings/server`. It merges defaults and sources in order, validates the complete result with the schema, and returns plain configuration data suitable for passing to a Client Component.
48
+
49
+ ```ts
50
+ // ./src/appSettings.ts
51
+ import { AppSettingsSource, createAppSettings, EnvironmentSource } from '@next-box/appsettings/server';
52
+ import { appSettingsSchema } from './appSettings.schema';
53
+
54
+ export const loadAppSettings = () =>
55
+ createAppSettings({
56
+ schema: appSettingsSchema,
57
+ defaults: {
58
+ App: { Name: 'My Application', Environment: 'development' },
59
+ Api: { BaseUrl: 'https://api.example.com', Timeout: 5000 },
60
+ Features: { DarkMode: true, NewDashboard: false },
61
+ },
62
+ sources: [
63
+ new AppSettingsSource({ directory: './config', environment: process.env.NODE_ENV }),
64
+ new EnvironmentSource({ envs: process.env, prefix: 'APP_' }),
65
+ ],
66
+ });
67
+ ```
68
+
69
+ Defaults form the first layer; sources load sequentially in declaration order, with later sources overriding earlier values. Plain objects merge recursively. Arrays and primitive values are replaced. Inputs are not mutated. If loading or validation fails, `createAppSettings` rejects and does not return partial configuration.
70
+
71
+ In Next.js, call the loader from a Server Component and pass the result to the provider:
72
+
73
+ ```tsx
74
+ // ./app/layout.tsx
75
+ import { AppSettingsProvider } from '@next-box/appsettings';
76
+ import { loadAppSettings } from '../src/appSettings';
77
+
78
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
79
+ const settings = await loadAppSettings();
80
+
81
+ return (
82
+ <html lang="en">
83
+ <body>
84
+ <AppSettingsProvider settings={settings}>{children}</AppSettingsProvider>
85
+ </body>
86
+ </html>
87
+ );
88
+ }
89
+ ```
90
+
91
+ The provider creates a read-only in-memory settings instance from the supplied data. Client components do not load files, validate configuration, or reload settings.
92
+
93
+ ### Configure server sources
94
+
95
+ The `@next-box/appsettings/server` entry provides source classes. Pass instances to `createAppSettings` in the order they should be applied:
96
+
97
+ - `new AppSettingsSource({ directory, environment })` reads `appsettings.json` followed by `appsettings.{environment}.json` from the filesystem. The environment file is optional and overrides matching values from the base file. If `environment` is omitted, it uses `NODE_ENV` and defaults to `development`.
98
+ - `new EnvironmentSource({ envs, prefix })` maps the provided environment variable record into nested settings. Pass `process.env` for Node's environment or a separate record for controlled use.
99
+ - `new RemoteSource({ url, fetch, responsePath })` fetches JSON from a URL. `fetch` is required; `responsePath` optionally selects a nested object from the response.
100
+ - `new MemorySource({ values })` supplies in-memory values, useful for additional overrides.
101
+
102
+ Environment keys after the prefix use `__` as a nesting separator. For example, `APP_API__TIMEOUT` maps to `Api.Timeout`. Exact `true`, `false`, and numeric strings are converted to booleans and numbers; other values remain strings.
103
+
104
+ For example, these sources can be combined in one configuration:
105
+
106
+ ```ts
107
+ import {
108
+ AppSettingsSource,
109
+ EnvironmentSource,
110
+ MemorySource,
111
+ RemoteSource,
112
+ createAppSettings,
113
+ } from '@next-box/appsettings/server';
114
+
115
+ const settings = await createAppSettings({
116
+ schema: appSettingsSchema,
117
+ defaults: {
118
+ App: { Name: 'My Application', Environment: 'development' },
119
+ Api: { BaseUrl: 'https://api.example.com', Timeout: 5000 },
120
+ Features: { DarkMode: true, NewDashboard: false },
121
+ },
122
+ sources: [
123
+ new AppSettingsSource({ directory: './config' }),
124
+ new RemoteSource({ url: 'https://config.example.com/appsettings.json', fetch }),
125
+ new EnvironmentSource({ envs: process.env, prefix: 'APP_' }),
126
+ new MemorySource({ values: { Features: { NewDashboard: true } } }),
127
+ ],
128
+ });
129
+ ```
130
+
131
+ Each later source overrides earlier values according to the merge rules described above. The resulting settings object can be passed to `AppSettingsProvider`.
132
+
133
+ ### Read settings
134
+
135
+ Paths use dot notation. `useAppSetting` reads a leaf value and `useAppSettings` reads an object section:
136
+
137
+ ```tsx
138
+ // ./src/ApiTimeout.tsx
139
+ 'use client';
140
+
141
+ import { useAppSetting, useAppSettings } from '@next-box/appsettings';
142
+
143
+ export const ApiTimeout = () => {
144
+ const timeout = useAppSetting('Api.Timeout');
145
+ const features = useAppSettings('Features');
146
+
147
+ return <span>{features?.DarkMode ? timeout : 'Disabled'}</span>;
148
+ };
149
+ ```
150
+
151
+ The settings snapshot is read-only. Configuration is loaded and validated before it reaches the provider; there are no client-side mutation or reload methods.
152
+
153
+ Leaf paths and their values are checked against the inferred schema. The reader uses bounded nested path types and `type-fest`'s `Get<T, P>` for path value types.
154
+
155
+ React context cannot infer the settings type from a provider at a separate hook call site. Generate a module augmentation from the exported schema type:
156
+
157
+ ```shell
158
+ pnpm exec appsettings generate-types ./src/appSettings.schema.ts ./src/appSettings.generated.d.ts --type AppSettingsConfig
159
+ ```
160
+
161
+ The command writes a declaration that imports `AppSettingsConfig` and augments the package's `AppSettingsSchema`. TypeScript then infers each hook result from the value type at the end of its path. Zod enums and literals stay precise, while `z.string()` and `z.number()` remain broad types. The declaration only provides types; configuration still loads and validates on the server.
162
+
163
+ Run the command after changing the schema, or add it to the consuming application's `predev`, `prebuild`, and type-check scripts so the declaration stays current. The generated file must be included by the application's `tsconfig.json`.
164
+
165
+ ### Conditionally render with `Setting`
166
+
167
+ `Setting` renders its children when the current value at `path` strictly equals `value`. Pass an array of expected values to match any one of them. It renders `fallback` when there is no match; `negate` inverts the match.
168
+
169
+ ```tsx
170
+ import { Setting } from '@next-box/appsettings';
171
+
172
+ <Setting path="Features.DarkMode" value={true} fallback={<p>Dark mode is off</p>}>
173
+ <p>Dark mode is on</p>
174
+ </Setting>
175
+ ```
176
+
177
+ ## Changelog
178
+
179
+ Check out the [features, fixes and more](../../CHANGELOG.md) that go into each major, minor and patch version.
180
+
181
+ ## License
182
+
183
+ `@next-box/appsettings` is [MIT Licensed](LICENSE).
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Is unresolved before library has been built.
4
+ // eslint-disable-next-line import-x/no-unresolved
5
+ import { runAppsettingsCli } from '@next-box/appsettings/cli';
6
+
7
+ await runAppsettingsCli();
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./handler.cjs");exports.handler=e.handler,exports.builder=e=>e.positional("schema",{demandOption:!0,describe:"TypeScript module that exports the inferred settings type.",type:"string"}).positional("output",{default:"appsettings.generated.d.ts",describe:"Declaration file to generate.",type:"string"}).option("type",{default:"AppSettingsConfig",describe:"Exported type name.",type:"string"}).check(({type:e})=>{if(!/^[A-Za-z_$][\w$]*$/u.test(e))throw new Error("--type must be a TypeScript identifier.");return!0}),exports.command="generate-types <schema> <output>",exports.describe="Generate a declaration that types appsettings hooks from your schema.";
2
+ //# sourceMappingURL=command.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.cjs","sources":["../../../src/cli/command.ts"],"sourcesContent":["import { type CommandBuilder } from 'yargs';\nimport { type GenerateTypesArguments } from '#cli/handler.ts';\n\nexport const builder: CommandBuilder<object, GenerateTypesArguments> = yargs =>\n yargs\n .positional('schema', {\n demandOption: true,\n describe: 'TypeScript module that exports the inferred settings type.',\n type: 'string',\n })\n .positional('output', {\n default: 'appsettings.generated.d.ts',\n describe: 'Declaration file to generate.',\n type: 'string',\n })\n .option('type', {\n default: 'AppSettingsConfig',\n describe: 'Exported type name.',\n type: 'string',\n })\n .check(({ type }) => {\n if (!/^[A-Za-z_$][\\w$]*$/u.test(type)) {\n throw new Error('--type must be a TypeScript identifier.');\n }\n\n return true;\n });\n\nexport const command = 'generate-types <schema> <output>';\n\nexport const describe = 'Generate a declaration that types appsettings hooks from your schema.';\n\nexport { handler } from './handler.ts';\n"],"names":["yargs","positional","demandOption","describe","type","default","option","check","test","Error"],"mappings":"sFAGuEA,GACrEA,EACGC,WAAW,SAAU,CACpBC,cAAc,EACdC,SAAU,6DACVC,KAAM,WAEPH,WAAW,SAAU,CACpBI,QAAS,6BACTF,SAAU,gCACVC,KAAM,WAEPE,OAAO,OAAQ,CACdD,QAAS,oBACTF,SAAU,sBACVC,KAAM,WAEPG,MAAM,EAAGH,WACR,IAAK,sBAAsBI,KAAKJ,GAC9B,MAAM,IAAIK,MAAM,2CAGlB,OAAO,oBAGU,oDAEC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("node:fs/promises"),t=require("node:path");exports.handler=async({output:r,schema:s,type:i})=>{const o=t.resolve(process.cwd(),s),a=t.resolve(process.cwd(),r),c=[`import type { ${i} } from '${(e=>{let r=e.split(t.sep).join("/").replace(/\.(?:[cm]?tsx?)$/u,"");return r.startsWith(".")||(r=`./${r}`),r})(t.relative(t.dirname(a),o))}';`,"","declare module '@next-box/appsettings' {",` interface AppSettingsSchema extends ${i} {}`,"}","","export {};",""].join("\n");let p;try{p=await e.readFile(a,"utf8")}catch(e){if(!(e=>!!e&&"object"==typeof e&&"code"in e&&"ENOENT"===e.code)(e))throw e}p!==c&&(await e.mkdir(t.dirname(a),{recursive:!0}),await e.writeFile(a,c,"utf8")),process.stdout.write(`Generated ${t.relative(process.cwd(),a)}\n`)};
2
+ //# sourceMappingURL=handler.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.cjs","sources":["../../../src/cli/handler.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, relative, resolve, sep } from 'node:path';\n\nconst createModuleSpecifier = (path: string): string => {\n let specifier = path\n .split(sep)\n .join('/')\n .replace(/\\.(?:[cm]?tsx?)$/u, '');\n\n if (!specifier.startsWith('.')) {\n specifier = `./${specifier}`;\n }\n\n return specifier;\n};\n\nconst isMissingFile = (error: unknown): boolean =>\n !!error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';\n\nexport interface GenerateTypesArguments {\n output: string;\n schema: string;\n type: string;\n}\n\nexport const handler = async ({ output, schema, type }: GenerateTypesArguments): Promise<void> => {\n const schemaPath = resolve(process.cwd(), schema);\n const outputPath = resolve(process.cwd(), output);\n const schemaImportPath = createModuleSpecifier(relative(dirname(outputPath), schemaPath));\n\n const contents = [\n `import type { ${type} } from '${schemaImportPath}';`,\n '',\n \"declare module '@next-box/appsettings' {\",\n ` interface AppSettingsSchema extends ${type} {}`,\n '}',\n '',\n 'export {};',\n '',\n ].join('\\n');\n\n let existingContents: string | undefined;\n\n try {\n existingContents = await readFile(outputPath, 'utf8');\n } catch (error) {\n if (!isMissingFile(error)) {\n throw error;\n }\n }\n\n if (existingContents !== contents) {\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, contents, 'utf8');\n }\n\n process.stdout.write(`Generated ${relative(process.cwd(), outputPath)}\\n`);\n};\n"],"names":["async","output","schema","type","schemaPath","resolve","process","cwd","outputPath","contents","path","specifier","split","sep","join","replace","startsWith","createModuleSpecifier","relative","dirname","existingContents","readFile","error","code","isMissingFile","mkdir","recursive","writeFile","stdout","write"],"mappings":"sFAyBuBA,OAASC,SAAQC,SAAQC,WAC9C,MAAMC,EAAaC,EAAAA,QAAQC,QAAQC,MAAOL,GACpCM,EAAaH,EAAAA,QAAQC,QAAQC,MAAON,GAGpCQ,EAAW,CACf,iBAAiBN,aA5BS,CAACO,IAC7B,IAAIC,EAAYD,EACbE,MAAMC,OACNC,KAAK,KACLC,QAAQ,oBAAqB,IAMhC,OAJKJ,EAAUK,WAAW,OACxBL,EAAY,KAAKA,KAGZA,GAekBM,CAAsBC,EAAAA,SAASC,EAAAA,QAAQX,GAAaJ,QAI3E,GACA,2CACA,yCAAyCD,OACzC,IACA,GACA,aACA,IACAW,KAAK,MAEP,IAAIM,EAEJ,IACEA,QAAyBC,EAAAA,SAASb,EAAY,OAChD,CAAE,MAAOc,GACP,IA9BkB,CAACA,KACnBA,GAA0B,iBAAVA,GAAsB,SAAUA,GAAwB,WAAfA,EAAMC,KA6B1DC,CAAcF,GACjB,MAAMA,CAEV,CAEIF,IAAqBX,UACjBgB,EAAAA,MAAMN,UAAQX,GAAa,CAAEkB,WAAW,UACxCC,YAAUnB,EAAYC,EAAU,SAGxCH,QAAQsB,OAAOC,MAAM,aAAaX,EAAAA,SAASZ,QAAQC,MAAOC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("yargs"),r=require("yargs/helpers"),s=require("./cli/command.cjs");exports.runAppsettingsCli=async()=>{await e(r.hideBin(process.argv)).scriptName("appsettings").command(s).demandCommand(1,"Choose a command to run.").strict().help().parseAsync()};
2
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.cjs","sources":["../../src/cli.ts"],"sourcesContent":["import yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport * as generateTypesCommand from './cli/command.ts';\n\nexport const runAppsettingsCli = async (): Promise<void> => {\n await yargs(hideBin(process.argv))\n .scriptName('appsettings')\n .command(generateTypesCommand)\n .demandCommand(1, 'Choose a command to run.')\n .strict()\n .help()\n .parseAsync();\n};\n"],"names":["async","yargs","hideBin","process","argv","scriptName","command","generateTypesCommand","demandCommand","strict","help","parseAsync"],"mappings":"wHAIiCA,gBACzBC,EAAMC,EAAAA,QAAQC,QAAQC,OACzBC,WAAW,eACXC,QAAQC,GACRC,cAAc,EAAG,4BACjBC,SACAC,OACAC"}
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ "use strict";var e=require("react/jsx-runtime"),t=require("react"),r=require("../config/AppSettingsImplementation.cjs"),n=require("../hooks/useAppSettingsContext.cjs");exports.AppSettingsProvider=({children:i,settings:s})=>{const p=t.useMemo(()=>new r.AppSettingsImplementation(s),[s]);return e.jsx(n.AppSettingsContext,{value:p,children:i})};
3
+ //# sourceMappingURL=AppSettingsProvider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSettingsProvider.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ "use strict";var e=require("../hooks/useAppSettingsContext.cjs");exports.Setting=function({children:t,fallback:r,negate:n=!1,path:s,value:a}){const i=e.useAppSettingsContext().read(s),u=(Array.isArray(a)?a:[a]).includes(i);return(n?!u:u)?t:r};
3
+ //# sourceMappingURL=Setting.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Setting.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda"),t=require("./ScopedSettingsImplementation.cjs"),r=require("./ConfigReader.cjs"),n=require("./ConfigStore.cjs");exports.AppSettingsImplementation=class{constructor(e){this.store=new n.ConfigStore(e)}get(e){return this.read(e)}read(e){return new r.ConfigReader(this.store.getSnapshot()).read(e)}getAll(){return this.store.getSnapshot()}getOrThrow(e){const t=new r.ConfigReader(this.store.getSnapshot()).read(e);if(void 0===t)throw new Error(`Required app setting "${e}" is missing.`);return t}getSection(e){return new r.ConfigReader(this.store.getSnapshot()).read(e)}has(e){return new r.ConfigReader(this.store.getSnapshot()).has(e)}scope(n){const s=new r.ConfigReader(this.store.getSnapshot()).read(n);if(!e.isPlainObject(s))throw new Error(`App setting section "${n}" is missing.`);return new t.ScopedSettingsImplementation(s,n)}};
2
+ //# sourceMappingURL=AppSettingsImplementation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSettingsImplementation.cjs","sources":["../../../src/config/AppSettingsImplementation.ts"],"sourcesContent":["import { isPlainObject } from 'remeda';\nimport { type Get } from 'type-fest';\nimport { ScopedSettingsImplementation } from '#config/ScopedSettingsImplementation.ts';\nimport { type InternalAppSettings, type ScopedSettings } from '#types/AppSettings.types.ts';\nimport { type SettingPath, type SettingSectionPath } from '#types/Config.types.ts';\nimport { ConfigReader } from './ConfigReader.ts';\nimport { ConfigStore } from './ConfigStore.ts';\n\nexport class AppSettingsImplementation<T extends object> implements InternalAppSettings<T> {\n private readonly store: ConfigStore<T>;\n\n constructor(settings: T) {\n this.store = new ConfigStore(settings);\n }\n\n get<P extends SettingPath<T>>(path: P): Get<T, P> | undefined;\n get(path: string): unknown {\n return this.read(path);\n }\n\n read(path: string): unknown {\n return new ConfigReader(this.store.getSnapshot()).read(path);\n }\n\n getAll(): Readonly<T> {\n return this.store.getSnapshot();\n }\n\n getOrThrow<P extends SettingPath<T>>(path: P): Get<T, P>;\n getOrThrow(path: string): unknown {\n const value = new ConfigReader(this.store.getSnapshot()).read(path);\n\n if (value === undefined) {\n throw new Error(`Required app setting \"${path}\" is missing.`);\n }\n\n return value;\n }\n\n getSection<P extends SettingSectionPath<T>>(path: P): Get<T, P> | undefined;\n getSection(path: string): unknown {\n return new ConfigReader(this.store.getSnapshot()).read(path);\n }\n\n has(path: SettingPath<T> | SettingSectionPath<T>): boolean;\n has(path: string): boolean {\n return new ConfigReader(this.store.getSnapshot()).has(path);\n }\n\n scope<P extends SettingSectionPath<T>>(path: P): ScopedSettings<Extract<Get<T, P>, object>>;\n scope(path: string): ScopedSettings<object> {\n const reader = new ConfigReader(this.store.getSnapshot());\n const section = reader.read(path);\n\n if (!isPlainObject(section)) {\n throw new Error(`App setting section \"${path}\" is missing.`);\n }\n\n return new ScopedSettingsImplementation(section, path);\n }\n}\n"],"names":["constructor","settings","this","store","ConfigStore","get","path","read","ConfigReader","getSnapshot","getAll","getOrThrow","value","undefined","Error","getSection","has","scope","section","isPlainObject","ScopedSettingsImplementation"],"mappings":"sLAQO,MAGL,WAAAA,CAAYC,GACVC,KAAKC,MAAQ,IAAIC,EAAAA,YAAYH,EAC/B,CAGAI,GAAAA,CAAIC,GACF,OAAOJ,KAAKK,KAAKD,EACnB,CAEAC,IAAAA,CAAKD,GACH,OAAO,IAAIE,EAAAA,aAAaN,KAAKC,MAAMM,eAAeF,KAAKD,EACzD,CAEAI,MAAAA,GACE,OAAOR,KAAKC,MAAMM,aACpB,CAGAE,UAAAA,CAAWL,GACT,MAAMM,EAAQ,IAAIJ,EAAAA,aAAaN,KAAKC,MAAMM,eAAeF,KAAKD,GAE9D,QAAcO,IAAVD,EACF,MAAM,IAAIE,MAAM,yBAAyBR,kBAG3C,OAAOM,CACT,CAGAG,UAAAA,CAAWT,GACT,OAAO,IAAIE,EAAAA,aAAaN,KAAKC,MAAMM,eAAeF,KAAKD,EACzD,CAGAU,GAAAA,CAAIV,GACF,OAAO,IAAIE,EAAAA,aAAaN,KAAKC,MAAMM,eAAeO,IAAIV,EACxD,CAGAW,KAAAA,CAAMX,GACJ,MACMY,EADS,IAAIV,EAAAA,aAAaN,KAAKC,MAAMM,eACpBF,KAAKD,GAE5B,IAAKa,EAAAA,cAAcD,GACjB,MAAM,IAAIJ,MAAM,wBAAwBR,kBAG1C,OAAO,IAAIc,EAAAA,6BAA6BF,EAASZ,EACnD"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda");exports.ConfigMerger=class{merge(r,...s){return s.reduce((r,s)=>e.mergeDeep(r,s),r)}};
2
+ //# sourceMappingURL=ConfigMerger.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigMerger.cjs","sources":["../../../src/config/ConfigMerger.ts"],"sourcesContent":["import { mergeDeep } from 'remeda';\n\nexport class ConfigMerger {\n merge<T extends object>(first: T, ...configs: readonly T[]): T {\n // TypeScript cannot guarantee that merging arbitrary objects produces T\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return configs.reduce((merged, config) => mergeDeep(merged, config) as T, first);\n }\n}\n"],"names":["merge","first","configs","reduce","merged","config","mergeDeep"],"mappings":"0DAEO,MACLA,KAAAA,CAAwBC,KAAaC,GAGnC,OAAOA,EAAQC,OAAO,CAACC,EAAQC,IAAWC,EAAAA,UAAUF,EAAQC,GAAcJ,EAC5E"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda"),r=require("zcb");exports.ConfigReader=class{constructor(e){this.config=e,this.reader=r.createConfigReader(e)}getAt(r){return r.split(".").reduce((r,t)=>{if(e.isPlainObject(r))return r[t]},this.config)}get(e){return this.reader.read(e)}getSection(e){return this.getAt(e)}has(e){return void 0!==this.getAt(e)}read(e){return this.getAt(e)}scope(e){return this.reader.scope(e)}};
2
+ //# sourceMappingURL=ConfigReader.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigReader.cjs","sources":["../../../src/config/ConfigReader.ts"],"sourcesContent":["import { isPlainObject } from 'remeda';\nimport { type Get } from 'type-fest';\nimport { type Path, type Scope, type ConfigReader as ZcbConfigReader, createConfigReader } from 'zcb';\n\nexport class ConfigReader<T extends object> {\n private reader: ZcbConfigReader<T>;\n private readonly config: T;\n\n constructor(config: T) {\n this.config = config;\n this.reader = createConfigReader(config);\n }\n\n private getAt(path: string): unknown {\n return path.split('.').reduce<unknown>((current, key) => {\n if (!isPlainObject(current)) {\n return;\n }\n\n return current[key];\n }, this.config);\n }\n\n public get<P extends Path<T>>(path: P): Get<T, P> {\n return this.reader.read(path);\n }\n\n public getSection<P extends Scope<T>>(path: P): Get<T, P> {\n // Required as underlying getAt needs to be kept permissive\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return this.getAt(path) as Get<T, P>;\n }\n\n public has(path: string): boolean {\n return this.getAt(path) !== undefined;\n }\n\n public read(path: string): unknown {\n return this.getAt(path);\n }\n\n public scope<P extends Scope<T>>(path: P): ZcbConfigReader<Get<T, P>> {\n return this.reader.scope(path);\n }\n}\n"],"names":["constructor","config","this","reader","createConfigReader","getAt","path","split","reduce","current","key","isPlainObject","get","read","getSection","has","undefined","scope"],"mappings":"2EAIO,MAIL,WAAAA,CAAYC,GACVC,KAAKD,OAASA,EACdC,KAAKC,OAASC,EAAAA,mBAAmBH,EACnC,CAEQI,KAAAA,CAAMC,GACZ,OAAOA,EAAKC,MAAM,KAAKC,OAAgB,CAACC,EAASC,KAC/C,GAAKC,EAAAA,cAAcF,GAInB,OAAOA,EAAQC,IACdR,KAAKD,OACV,CAEOW,GAAAA,CAAuBN,GAC5B,OAAOJ,KAAKC,OAAOU,KAAKP,EAC1B,CAEOQ,UAAAA,CAA+BR,GAGpC,OAAOJ,KAAKG,MAAMC,EACpB,CAEOS,GAAAA,CAAIT,GACT,YAA4BU,IAArBd,KAAKG,MAAMC,EACpB,CAEOO,IAAAA,CAAKP,GACV,OAAOJ,KAAKG,MAAMC,EACpB,CAEOW,KAAAA,CAA0BX,GAC/B,OAAOJ,KAAKC,OAAOc,MAAMX,EAC3B"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda"),s=require("./helpers/freezeDeep.cjs");exports.ConfigStore=class{constructor(r){this.getSnapshot=()=>this.snapshot,this.snapshot=s.freezeDeep(e.clone(r))}};
2
+ //# sourceMappingURL=ConfigStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigStore.cjs","sources":["../../../src/config/ConfigStore.ts"],"sourcesContent":["import { clone } from 'remeda';\nimport { freezeDeep } from '#config/helpers/freezeDeep.ts';\n\nexport class ConfigStore<T extends object> {\n private snapshot: Readonly<T>;\n\n public getSnapshot = (): Readonly<T> => this.snapshot;\n\n constructor(initial: T) {\n this.snapshot = freezeDeep(clone(initial));\n }\n}\n"],"names":["constructor","initial","getSnapshot","this","snapshot","freezeDeep","clone"],"mappings":"+FAGO,MAKL,WAAAA,CAAYC,QAFLC,YAAc,IAAmBC,KAAKC,SAG3CD,KAAKC,SAAWC,EAAAA,WAAWC,EAAAA,MAAML,GACnC"}
@@ -0,0 +1,2 @@
1
+ "use strict";exports.ConfigValidator=class{constructor(s){this.schema=s}validate(s){return this.schema.parse(s)}};
2
+ //# sourceMappingURL=ConfigValidator.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigValidator.cjs","sources":["../../../src/config/ConfigValidator.ts"],"sourcesContent":["import { type ZodType, type output } from 'zod';\n\nexport class ConfigValidator<T extends ZodType> {\n private readonly schema: T;\n\n constructor(schema: T) {\n this.schema = schema;\n }\n\n validate(value: unknown): output<T> {\n return this.schema.parse(value);\n }\n}\n"],"names":["constructor","schema","this","validate","value","parse"],"mappings":"qCAEO,MAGL,WAAAA,CAAYC,GACVC,KAAKD,OAASA,CAChB,CAEAE,QAAAA,CAASC,GACP,OAAOF,KAAKD,OAAOI,MAAMD,EAC3B"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./ConfigReader.cjs");exports.ScopedSettingsImplementation=class{constructor(r,t){this.section=r,this.parentPath=t,this.reader=new e.ConfigReader(r)}get(e){return this.reader.read(e)}getAll(){return this.section}getOrThrow(e){const r=this.reader.read(e);if(void 0===r)throw new Error(`Required app setting "${this.parentPath}.${e}" is missing.`);return r}};
2
+ //# sourceMappingURL=ScopedSettingsImplementation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopedSettingsImplementation.cjs","sources":["../../../src/config/ScopedSettingsImplementation.ts"],"sourcesContent":["import { type Get } from 'type-fest';\nimport { ConfigReader } from '#config/ConfigReader.ts';\nimport { type ScopedSettings } from '#types/AppSettings.types.ts';\nimport { type SettingPath } from '#types/Config.types.ts';\n\nexport class ScopedSettingsImplementation<T extends object> implements ScopedSettings<T> {\n private readonly reader: ConfigReader<T>;\n private readonly section: T;\n private readonly parentPath: string;\n\n constructor(section: T, parentPath: string) {\n this.section = section;\n this.parentPath = parentPath;\n this.reader = new ConfigReader(section);\n }\n\n get<P extends SettingPath<T>>(path: P): Get<T, P> | undefined;\n get(path: string): unknown {\n return this.reader.read(path);\n }\n\n getAll(): Readonly<T> {\n return this.section;\n }\n\n getOrThrow<P extends SettingPath<T>>(path: P): Get<T, P>;\n getOrThrow(path: string): unknown {\n const value = this.reader.read(path);\n\n if (value === undefined) {\n throw new Error(`Required app setting \"${this.parentPath}.${path}\" is missing.`);\n }\n\n return value;\n }\n}\n"],"names":["constructor","section","parentPath","this","reader","ConfigReader","get","path","read","getAll","getOrThrow","value","undefined","Error"],"mappings":"sFAKO,MAKL,WAAAA,CAAYC,EAAYC,GACtBC,KAAKF,QAAUA,EACfE,KAAKD,WAAaA,EAClBC,KAAKC,OAAS,IAAIC,EAAAA,aAAaJ,EACjC,CAGAK,GAAAA,CAAIC,GACF,OAAOJ,KAAKC,OAAOI,KAAKD,EAC1B,CAEAE,MAAAA,GACE,OAAON,KAAKF,OACd,CAGAS,UAAAA,CAAWH,GACT,MAAMI,EAAQR,KAAKC,OAAOI,KAAKD,GAE/B,QAAcK,IAAVD,EACF,MAAM,IAAIE,MAAM,yBAAyBV,KAAKD,cAAcK,kBAG9D,OAAOI,CACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./ConfigMerger.cjs"),r=require("./ConfigValidator.cjs");exports.createAppSettings=async({defaults:s,schema:t,sources:a=[]})=>{const o=[];for(const e of a)o.push(await e.load());const i=(new e.ConfigMerger).merge(s,...o);return new r.ConfigValidator(t).validate(i)};
2
+ //# sourceMappingURL=createAppSettings.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createAppSettings.cjs","sources":["../../../src/config/createAppSettings.ts"],"sourcesContent":["import { type CreateAppSettingsOptions } from '#types/AppSettings.types.ts';\nimport { ConfigMerger } from './ConfigMerger.ts';\nimport { ConfigValidator } from './ConfigValidator.ts';\n\nexport const createAppSettings = async <T extends object>({\n defaults,\n schema,\n sources = [],\n}: CreateAppSettingsOptions<T>): Promise<Readonly<T>> => {\n const loadedSources: Record<string, unknown>[] = [];\n\n for (const source of sources) {\n loadedSources.push(await source.load());\n }\n\n const merged = new ConfigMerger().merge(defaults, ...loadedSources);\n return new ConfigValidator(schema).validate(merged);\n};\n"],"names":["async","defaults","schema","sources","loadedSources","source","push","load","merged","ConfigMerger","merge","ConfigValidator","validate"],"mappings":"8GAIiCA,OAC/BC,WACAC,SACAC,UAAU,OAEV,MAAMC,EAA2C,GAEjD,IAAK,MAAMC,KAAUF,EACnBC,EAAcE,WAAWD,EAAOE,QAGlC,MAAMC,GAAS,IAAIC,EAAAA,cAAeC,MAAMT,KAAaG,GACrD,OAAO,IAAIO,EAAAA,gBAAgBT,GAAQU,SAASJ"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda");const r=t=>{if((e.isPlainObject(t)||Array.isArray(t))&&!Object.isFrozen(t)){const e=Object.values(t);for(const t of e)r(t);Object.freeze(t)}return t};exports.freezeDeep=r;
2
+ //# sourceMappingURL=freezeDeep.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freezeDeep.cjs","sources":["../../../../src/config/helpers/freezeDeep.ts"],"sourcesContent":["import { isPlainObject } from 'remeda';\n\nexport const freezeDeep = <T>(value: T): T => {\n if ((isPlainObject(value) || Array.isArray(value)) && !Object.isFrozen(value)) {\n const values: unknown[] = Object.values(value);\n\n for (const entry of values) {\n freezeDeep(entry);\n }\n\n Object.freeze(value);\n }\n\n return value;\n};\n"],"names":["freezeDeep","value","isPlainObject","Array","isArray","Object","isFrozen","values","entry","freeze"],"mappings":"qCAEO,MAAMA,EAAiBC,IAC5B,IAAKC,EAAAA,cAAcD,IAAUE,MAAMC,QAAQH,MAAYI,OAAOC,SAASL,GAAQ,CAC7E,MAAMM,EAAoBF,OAAOE,OAAON,GAExC,IAAK,MAAMO,KAASD,EAClBP,EAAWQ,GAGbH,OAAOI,OAAOR,EAChB,CAEA,OAAOA"}
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("./useAppSettingsContext.cjs");exports.useAppSetting=function(e){return t.useAppSettingsContext().read(e)};
2
+ //# sourceMappingURL=useAppSetting.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppSetting.cjs","sources":["../../../src/hooks/useAppSetting.ts"],"sourcesContent":["import { type Get } from 'type-fest';\nimport { type AppSettingsSchema } from '../index.ts';\nimport { type SettingPath } from '../types/Config.types.ts';\nimport { useAppSettingsContext } from './useAppSettingsContext.ts';\n\nexport function useAppSetting<P extends SettingPath<AppSettingsSchema>>(path: P): Get<AppSettingsSchema, P> | undefined;\nexport function useAppSetting<T extends object, P extends SettingPath<T>>(path: P): Get<T, P> | undefined;\n\n// Required for overloads\n// eslint-disable-next-line prefer-arrow/prefer-arrow-functions\nexport function useAppSetting(path: string): unknown {\n const settings = useAppSettingsContext();\n return settings.read(path);\n}\n"],"names":["path","useAppSettingsContext","read"],"mappings":"gFAUO,SAAuBA,GAE5B,OADiBC,EAAAA,wBACDC,KAAKF,EACvB"}
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("./useAppSettingsContext.cjs");exports.useAppSettings=function(e){return t.useAppSettingsContext().read(e)};
2
+ //# sourceMappingURL=useAppSettings.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppSettings.cjs","sources":["../../../src/hooks/useAppSettings.ts"],"sourcesContent":["import { type Get } from 'type-fest';\nimport { type SettingSectionPath } from '#types/Config.types.ts';\nimport { type AppSettingsSchema } from '../index.ts';\nimport { useAppSettingsContext } from './useAppSettingsContext.ts';\n\nexport function useAppSettings<P extends SettingSectionPath<AppSettingsSchema>>(\n path: P,\n): Get<AppSettingsSchema, P> | undefined;\n\nexport function useAppSettings<T extends object, P extends SettingSectionPath<T>>(path: P): Get<T, P> | undefined;\n\n// Required for overloads\n// eslint-disable-next-line prefer-arrow/prefer-arrow-functions\nexport function useAppSettings(path: string): unknown {\n const settings = useAppSettingsContext();\n return settings.read(path);\n}\n"],"names":["path","useAppSettingsContext","read"],"mappings":"iFAaO,SAAwBA,GAE7B,OADiBC,EAAAA,wBACDC,KAAKF,EACvB"}
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("react");const e=t.createContext(void 0);exports.AppSettingsContext=e,exports.useAppSettingsContext=()=>{const r=t.use(e);if(!r||"object"!=typeof r)throw new Error("useAppSettingsContext must be used within an <AppSettingsProvider>.");return r};
2
+ //# sourceMappingURL=useAppSettingsContext.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppSettingsContext.cjs","sources":["../../../src/hooks/useAppSettingsContext.ts"],"sourcesContent":["import { createContext, use } from 'react';\nimport { type InternalAppSettings } from '#types/AppSettings.types.ts';\nimport { type AppSettingsSchema } from '../index.ts';\n\nexport const AppSettingsContext = createContext<unknown>(undefined);\n\nexport const useAppSettingsContext = <T extends object = AppSettingsSchema>(): InternalAppSettings<T> => {\n const settings = use(AppSettingsContext);\n\n if (!settings || typeof settings !== 'object') {\n throw new Error('useAppSettingsContext must be used within an <AppSettingsProvider>.');\n }\n\n // Context is untyped because each provider supplies a different schema.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return settings as InternalAppSettings<T>;\n};\n"],"names":["AppSettingsContext","createContext","undefined","settings","use","Error"],"mappings":"oCAIO,MAAMA,EAAqBC,EAAAA,mBAAuBC,8DAEpB,KACnC,MAAMC,EAAWC,EAAAA,IAAIJ,GAErB,IAAKG,GAAgC,iBAAbA,EACtB,MAAM,IAAIE,MAAM,uEAKlB,OAAOF"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./components/AppSettingsProvider.cjs"),t=require("./components/Setting.cjs"),s=require("./hooks/useAppSetting.cjs"),p=require("./hooks/useAppSettings.cjs");exports.AppSettingsProvider=e.AppSettingsProvider,exports.Setting=t.Setting,exports.useAppSetting=s.useAppSetting,exports.useAppSettings=p.useAppSettings;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./config/createAppSettings.cjs"),r=require("./sources/AppSettingsSource.cjs"),o=require("./sources/EnvironmentSource.cjs"),t=require("./sources/RemoteSource.cjs"),c=require("./sources/MemorySource.cjs");exports.createAppSettings=e.createAppSettings,exports.AppSettingsSource=r.AppSettingsSource,exports.EnvironmentSource=o.EnvironmentSource,exports.RemoteSource=t.RemoteSource,exports.MemorySource=c.MemorySource;
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("node:path"),r=require("../config/ConfigMerger.cjs"),s=require("./helpers/loadJson.cjs");exports.AppSettingsSource=class{constructor(e){this.directory=e.directory??process.cwd(),this.environment=e.environment??process.env.NODE_ENV??"development",this.name=e.name??`appsettings:${this.environment}`}async load(){const n=e.resolve(this.directory,"appsettings.json"),t=e.resolve(this.directory,`appsettings.${this.environment}.json`),o=await s.loadJson(n,!1),i=await s.loadJson(t,!0)??{};return(new r.ConfigMerger).merge(o,i)}};
2
+ //# sourceMappingURL=AppSettingsSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSettingsSource.cjs","sources":["../../../src/sources/AppSettingsSource.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { ConfigMerger } from '#config/ConfigMerger.ts';\nimport { loadJson } from '#sources/helpers/loadJson.ts';\nimport { type SettingsSource } from '../types/Source.types.ts';\n\nexport interface AppSettingsSourceOptions {\n directory?: string;\n environment?: string;\n name?: string;\n}\n\nexport class AppSettingsSource implements SettingsSource {\n private directory: string;\n private environment: string;\n public name: string;\n\n public constructor(options: AppSettingsSourceOptions) {\n this.directory = options.directory ?? process.cwd();\n this.environment = options.environment ?? process.env.NODE_ENV ?? 'development';\n this.name = options.name ?? `appsettings:${this.environment}`;\n }\n\n async load(): Promise<Record<string, unknown>> {\n const basePath = resolve(this.directory, 'appsettings.json');\n const environmentPath = resolve(this.directory, `appsettings.${this.environment}.json`);\n const base = await loadJson(basePath, false);\n const override = (await loadJson(environmentPath, true)) ?? {};\n return new ConfigMerger().merge(base, override);\n }\n}\n"],"names":["constructor","options","this","directory","process","cwd","environment","env","NODE_ENV","name","load","basePath","resolve","environmentPath","base","loadJson","override","ConfigMerger","merge"],"mappings":"8IAWO,MAKL,WAAAA,CAAmBC,GACjBC,KAAKC,UAAYF,EAAQE,WAAaC,QAAQC,MAC9CH,KAAKI,YAAcL,EAAQK,aAAeF,QAAQG,IAAIC,UAAY,cAClEN,KAAKO,KAAOR,EAAQQ,MAAQ,eAAeP,KAAKI,aAClD,CAEA,UAAMI,GACJ,MAAMC,EAAWC,EAAAA,QAAQV,KAAKC,UAAW,oBACnCU,EAAkBD,UAAQV,KAAKC,UAAW,eAAeD,KAAKI,oBAC9DQ,QAAaC,WAASJ,GAAU,GAChCK,QAAkBD,EAAAA,SAASF,GAAiB,IAAU,CAAA,EAC5D,OAAO,IAAII,EAAAA,cAAeC,MAAMJ,EAAME,EACxC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda");exports.EnvironmentSource=class{constructor(t){this.normalizeSegment=t=>t.toLowerCase().split("_").filter(Boolean).map(t=>e.capitalize(t)).join(""),this.envs=t.envs,this.prefix=t.prefix??"APP_",this.name=t.name??`environment:${this.prefix}`}parseEnvironmentValue(e){return"true"===e||"false"!==e&&(""!==e.trim()&&Number.isFinite(Number(e))?Number(e):e)}load(){const t={};for(const[i,n]of Object.entries(this.envs)){if(void 0===n||!i.startsWith(this.prefix))continue;const r=i.slice(this.prefix.length).split("__").filter(Boolean).map(e=>this.normalizeSegment(e)),s=r.pop();if(!s)continue;let o=t;for(const t of r){const i=o[t];e.isPlainObject(i)||(o[t]={});const n=o[t];e.isPlainObject(n)&&(o=n)}o[s]=this.parseEnvironmentValue(n)}return Promise.resolve(t)}};
2
+ //# sourceMappingURL=EnvironmentSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvironmentSource.cjs","sources":["../../../src/sources/EnvironmentSource.ts"],"sourcesContent":["import { capitalize, isPlainObject } from 'remeda';\nimport { type SettingsSource } from '../types/Source.types.ts';\n\nexport interface EnvironmentSourceOptions {\n envs: Record<string, string | undefined>;\n name?: string;\n prefix?: string;\n}\n\nexport class EnvironmentSource implements SettingsSource {\n private readonly envs: Record<string, string | undefined>;\n private readonly prefix: string;\n\n private normalizeSegment = (value: string): string =>\n value\n .toLowerCase()\n .split('_')\n .filter(Boolean)\n .map(part => capitalize(part))\n .join('');\n\n public readonly name: string;\n\n public constructor(options: EnvironmentSourceOptions) {\n this.envs = options.envs;\n this.prefix = options.prefix ?? 'APP_';\n this.name = options.name ?? `environment:${this.prefix}`;\n }\n\n private parseEnvironmentValue(value: string): unknown {\n if (value === 'true') {\n return true;\n }\n\n if (value === 'false') {\n return false;\n }\n\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return Number(value);\n }\n\n return value;\n }\n\n load(): Promise<Record<string, unknown>> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(this.envs)) {\n if (value === undefined || !key.startsWith(this.prefix)) {\n continue;\n }\n\n const path = key\n .slice(this.prefix.length)\n .split('__')\n .filter(Boolean)\n .map(part => this.normalizeSegment(part));\n\n const leaf = path.pop();\n\n if (!leaf) {\n continue;\n }\n\n let current = result;\n\n for (const part of path) {\n const next = current[part];\n\n if (!isPlainObject(next)) {\n current[part] = {};\n }\n\n const currentPart = current[part];\n\n if (isPlainObject(currentPart)) {\n current = currentPart;\n }\n }\n\n current[leaf] = this.parseEnvironmentValue(value);\n }\n\n return Promise.resolve(result);\n }\n}\n"],"names":["constructor","options","normalizeSegment","value","toLowerCase","split","filter","Boolean","map","part","capitalize","join","this","envs","prefix","name","parseEnvironmentValue","trim","Number","isFinite","load","result","key","Object","entries","undefined","startsWith","path","slice","length","leaf","pop","current","next","isPlainObject","currentPart","Promise","resolve"],"mappings":"+DASO,MAcL,WAAAA,CAAmBC,GAVXC,KAAAA,iBAAoBC,GAC1BA,EACGC,cACAC,MAAM,KACNC,OAAOC,SACPC,IAAIC,GAAQC,EAAAA,WAAWD,IACvBE,KAAK,IAKRC,KAAKC,KAAOZ,EAAQY,KACpBD,KAAKE,OAASb,EAAQa,QAAU,OAChCF,KAAKG,KAAOd,EAAQc,MAAQ,eAAeH,KAAKE,QAClD,CAEQE,qBAAAA,CAAsBb,GAC5B,MAAc,SAAVA,GAIU,UAAVA,IAIiB,KAAjBA,EAAMc,QAAiBC,OAAOC,SAASD,OAAOf,IACzCe,OAAOf,GAGTA,EACT,CAEAiB,IAAAA,GACE,MAAMC,EAAkC,CAAA,EAExC,IAAK,MAAOC,EAAKnB,KAAUoB,OAAOC,QAAQZ,KAAKC,MAAO,CACpD,QAAcY,IAAVtB,IAAwBmB,EAAII,WAAWd,KAAKE,QAC9C,SAGF,MAAMa,EAAOL,EACVM,MAAMhB,KAAKE,OAAOe,QAClBxB,MAAM,MACNC,OAAOC,SACPC,IAAIC,GAAQG,KAAKV,iBAAiBO,IAE/BqB,EAAOH,EAAKI,MAElB,IAAKD,EACH,SAGF,IAAIE,EAAUX,EAEd,IAAK,MAAMZ,KAAQkB,EAAM,CACvB,MAAMM,EAAOD,EAAQvB,GAEhByB,EAAAA,cAAcD,KACjBD,EAAQvB,GAAQ,CAAA,GAGlB,MAAM0B,EAAcH,EAAQvB,GAExByB,EAAAA,cAAcC,KAChBH,EAAUG,EAEd,CAEAH,EAAQF,GAAQlB,KAAKI,sBAAsBb,EAC7C,CAEA,OAAOiC,QAAQC,QAAQhB,EACzB"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("remeda");exports.MemorySource=class{constructor(e){this.name=e.name??"memory",this.values=e.values}load(){return Promise.resolve(e.clone(this.values))}};
2
+ //# sourceMappingURL=MemorySource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemorySource.cjs","sources":["../../../src/sources/MemorySource.ts"],"sourcesContent":["import { clone } from 'remeda';\nimport { type SettingsSource } from '../types/Source.types.ts';\n\nexport interface MemorySourceOptions {\n name?: string;\n values: Record<string, unknown>;\n}\n\nexport class MemorySource implements SettingsSource {\n private readonly values: Record<string, unknown>;\n public readonly name: string;\n\n public constructor(options: MemorySourceOptions) {\n this.name = options.name ?? 'memory';\n this.values = options.values;\n }\n\n load(): Promise<Record<string, unknown>> {\n return Promise.resolve(clone(this.values));\n }\n}\n"],"names":["constructor","options","this","name","values","load","Promise","resolve","clone"],"mappings":"0DAQO,MAIL,WAAAA,CAAmBC,GACjBC,KAAKC,KAAOF,EAAQE,MAAQ,SAC5BD,KAAKE,OAASH,EAAQG,MACxB,CAEAC,IAAAA,GACE,OAAOC,QAAQC,QAAQC,EAAAA,MAAMN,KAAKE,QACpC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("remeda");exports.RemoteSource=class{constructor(t){this.fetch=t.fetch,this.url=t.url,this.name=t.name??`remote:${this.url}`,this.responsePath=t.responsePath}async load(){if("function"!=typeof this.fetch)throw new TypeError("RemoteSource requires fetch, which is unavailable in this runtime.");const e=await this.fetch(this.url);if(!e.ok)throw new Error(`Failed to load settings from "${this.url}": ${String(e.status)} ${e.statusText}`);let s=await e.json();if(this.responsePath&&!t.isObjectType(s))throw new Error(`Fetched value must be an object type if response path is provided, but was ${typeof s}.`);if(s=this.responsePath?t.prop(s,...t.stringToPath(this.responsePath)):s,!t.isPlainObject(s))throw new Error(`JSON settings from "${this.url}" must contain an object at the root.`);return s}};
2
+ //# sourceMappingURL=RemoteSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteSource.cjs","sources":["../../../src/sources/RemoteSource.ts"],"sourcesContent":["import { isObjectType, isPlainObject, prop, stringToPath } from 'remeda';\nimport { type SettingsSource } from '#types/Source.types.ts';\n\nexport interface RemoteSourceOptions {\n fetch: typeof globalThis.fetch;\n name?: string;\n responsePath?: string;\n url: string;\n}\n\nexport class RemoteSource implements SettingsSource {\n private fetch: typeof globalThis.fetch;\n private responsePath?: string;\n private url: string;\n public name: string;\n\n public constructor(options: RemoteSourceOptions) {\n this.fetch = options.fetch;\n this.url = options.url;\n this.name = options.name ?? `remote:${this.url}`;\n this.responsePath = options.responsePath;\n }\n\n async load(): Promise<Record<string, unknown>> {\n if (typeof this.fetch !== 'function') {\n throw new TypeError('RemoteSource requires fetch, which is unavailable in this runtime.');\n }\n\n const response = await this.fetch(this.url);\n\n if (!response.ok) {\n throw new Error(`Failed to load settings from \"${this.url}\": ${String(response.status)} ${response.statusText}`);\n }\n\n let value: unknown = await response.json();\n\n if (this.responsePath && !isObjectType(value)) {\n throw new Error(`Fetched value must be an object type if response path is provided, but was ${typeof value}.`);\n }\n\n // @ts-expect-error Unable to statically type string literal\n value = this.responsePath ? prop(value, ...stringToPath(this.responsePath)) : value;\n\n if (!isPlainObject(value)) {\n throw new Error(`JSON settings from \"${this.url}\" must contain an object at the root.`);\n }\n\n return value;\n }\n}\n"],"names":["constructor","options","this","fetch","url","name","responsePath","load","TypeError","response","ok","Error","String","status","statusText","value","json","isObjectType","prop","stringToPath","isPlainObject"],"mappings":"0DAUO,MAML,WAAAA,CAAmBC,GACjBC,KAAKC,MAAQF,EAAQE,MACrBD,KAAKE,IAAMH,EAAQG,IACnBF,KAAKG,KAAOJ,EAAQI,MAAQ,UAAUH,KAAKE,MAC3CF,KAAKI,aAAeL,EAAQK,YAC9B,CAEA,UAAMC,GACJ,GAA0B,mBAAfL,KAAKC,MACd,MAAM,IAAIK,UAAU,sEAGtB,MAAMC,QAAiBP,KAAKC,MAAMD,KAAKE,KAEvC,IAAKK,EAASC,GACZ,MAAM,IAAIC,MAAM,iCAAiCT,KAAKE,SAASQ,OAAOH,EAASI,WAAWJ,EAASK,cAGrG,IAAIC,QAAuBN,EAASO,OAEpC,GAAId,KAAKI,eAAiBW,EAAAA,aAAaF,GACrC,MAAM,IAAIJ,MAAM,qFAAqFI,MAMvG,GAFAA,EAAQb,KAAKI,aAAeY,OAAKH,KAAUI,EAAAA,aAAajB,KAAKI,eAAiBS,GAEzEK,EAAAA,cAAcL,GACjB,MAAM,IAAIJ,MAAM,uBAAuBT,KAAKE,4CAG9C,OAAOW,CACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("node:fs/promises"),t=require("remeda");exports.loadJson=async function(r,o){let n;try{n=await e.readFile(r,"utf8")}catch(e){if(o&&"object"==typeof e&&null!==e&&"code"in e&&"ENOENT"===e.code)return;throw e}const i=JSON.parse(n);if(!t.isPlainObject(i))throw new Error(`JSON settings from "${r}" must contain an object at the root.`);return i};
2
+ //# sourceMappingURL=loadJson.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadJson.cjs","sources":["../../../../src/sources/helpers/loadJson.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { isPlainObject } from 'remeda';\n\nexport function loadJson(path: string, isOptional: false): Promise<Record<string, unknown>>;\nexport function loadJson(path: string, isOptional: true): Promise<Record<string, unknown> | undefined>;\n\n// Required for overloads\n// eslint-disable-next-line prefer-arrow/prefer-arrow-functions\nexport async function loadJson(path: string, isOptional: boolean): Promise<Record<string, unknown> | undefined> {\n let contents: string;\n\n try {\n contents = await readFile(path, 'utf8');\n } catch (error) {\n if (isOptional && typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT') {\n return undefined;\n }\n\n throw error;\n }\n\n const value: unknown = JSON.parse(contents);\n\n if (!isPlainObject(value)) {\n throw new Error(`JSON settings from \"${path}\" must contain an object at the root.`);\n }\n\n return value;\n}\n"],"names":["async","path","isOptional","contents","readFile","error","code","value","JSON","parse","isPlainObject","Error"],"mappings":"oFAQOA,eAAwBC,EAAcC,GAC3C,IAAIC,EAEJ,IACEA,QAAiBC,EAAAA,SAASH,EAAM,OAClC,CAAE,MAAOI,GACP,GAAIH,GAA+B,iBAAVG,GAAgC,OAAVA,GAAkB,SAAUA,GAAwB,WAAfA,EAAMC,KACxF,OAGF,MAAMD,CACR,CAEA,MAAME,EAAiBC,KAAKC,MAAMN,GAElC,IAAKO,EAAAA,cAAcH,GACjB,MAAM,IAAII,MAAM,uBAAuBV,0CAGzC,OAAOM,CACT"}
@@ -0,0 +1,2 @@
1
+ export{handler}from"./handler.mjs";const e=e=>e.positional("schema",{demandOption:!0,describe:"TypeScript module that exports the inferred settings type.",type:"string"}).positional("output",{default:"appsettings.generated.d.ts",describe:"Declaration file to generate.",type:"string"}).option("type",{default:"AppSettingsConfig",describe:"Exported type name.",type:"string"}).check(({type:e})=>{if(!/^[A-Za-z_$][\w$]*$/u.test(e))throw new Error("--type must be a TypeScript identifier.");return!0}),t="generate-types <schema> <output>",r="Generate a declaration that types appsettings hooks from your schema.";export{e as builder,t as command,r as describe};
2
+ //# sourceMappingURL=command.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.mjs","sources":["../../../src/cli/command.ts"],"sourcesContent":["import { type CommandBuilder } from 'yargs';\nimport { type GenerateTypesArguments } from '#cli/handler.ts';\n\nexport const builder: CommandBuilder<object, GenerateTypesArguments> = yargs =>\n yargs\n .positional('schema', {\n demandOption: true,\n describe: 'TypeScript module that exports the inferred settings type.',\n type: 'string',\n })\n .positional('output', {\n default: 'appsettings.generated.d.ts',\n describe: 'Declaration file to generate.',\n type: 'string',\n })\n .option('type', {\n default: 'AppSettingsConfig',\n describe: 'Exported type name.',\n type: 'string',\n })\n .check(({ type }) => {\n if (!/^[A-Za-z_$][\\w$]*$/u.test(type)) {\n throw new Error('--type must be a TypeScript identifier.');\n }\n\n return true;\n });\n\nexport const command = 'generate-types <schema> <output>';\n\nexport const describe = 'Generate a declaration that types appsettings hooks from your schema.';\n\nexport { handler } from './handler.ts';\n"],"names":["builder","yargs","positional","demandOption","describe","type","default","option","check","test","Error","command"],"mappings":"yCAGaA,EAA0DC,GACrEA,EACGC,WAAW,SAAU,CACpBC,cAAc,EACdC,SAAU,6DACVC,KAAM,WAEPH,WAAW,SAAU,CACpBI,QAAS,6BACTF,SAAU,gCACVC,KAAM,WAEPE,OAAO,OAAQ,CACdD,QAAS,oBACTF,SAAU,sBACVC,KAAM,WAEPG,MAAM,EAAGH,WACR,IAAK,sBAAsBI,KAAKJ,GAC9B,MAAM,IAAIK,MAAM,2CAGlB,OAAO,IAGAC,EAAU,mCAEVP,EAAW"}
@@ -0,0 +1,2 @@
1
+ import{readFile as t,mkdir as e,writeFile as o}from"node:fs/promises";import{resolve as r,relative as s,dirname as c,sep as p}from"node:path";const i=async({output:i,schema:a,type:n})=>{const d=r(process.cwd(),a),m=r(process.cwd(),i),f=[`import type { ${n} } from '${(t=>{let e=t.split(p).join("/").replace(/\.(?:[cm]?tsx?)$/u,"");return e.startsWith(".")||(e=`./${e}`),e})(s(c(m),d))}';`,"","declare module '@next-box/appsettings' {",` interface AppSettingsSchema extends ${n} {}`,"}","","export {};",""].join("\n");let u;try{u=await t(m,"utf8")}catch(t){if(!(t=>!!t&&"object"==typeof t&&"code"in t&&"ENOENT"===t.code)(t))throw t}u!==f&&(await e(c(m),{recursive:!0}),await o(m,f,"utf8")),process.stdout.write(`Generated ${s(process.cwd(),m)}\n`)};export{i as handler};
2
+ //# sourceMappingURL=handler.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.mjs","sources":["../../../src/cli/handler.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, relative, resolve, sep } from 'node:path';\n\nconst createModuleSpecifier = (path: string): string => {\n let specifier = path\n .split(sep)\n .join('/')\n .replace(/\\.(?:[cm]?tsx?)$/u, '');\n\n if (!specifier.startsWith('.')) {\n specifier = `./${specifier}`;\n }\n\n return specifier;\n};\n\nconst isMissingFile = (error: unknown): boolean =>\n !!error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';\n\nexport interface GenerateTypesArguments {\n output: string;\n schema: string;\n type: string;\n}\n\nexport const handler = async ({ output, schema, type }: GenerateTypesArguments): Promise<void> => {\n const schemaPath = resolve(process.cwd(), schema);\n const outputPath = resolve(process.cwd(), output);\n const schemaImportPath = createModuleSpecifier(relative(dirname(outputPath), schemaPath));\n\n const contents = [\n `import type { ${type} } from '${schemaImportPath}';`,\n '',\n \"declare module '@next-box/appsettings' {\",\n ` interface AppSettingsSchema extends ${type} {}`,\n '}',\n '',\n 'export {};',\n '',\n ].join('\\n');\n\n let existingContents: string | undefined;\n\n try {\n existingContents = await readFile(outputPath, 'utf8');\n } catch (error) {\n if (!isMissingFile(error)) {\n throw error;\n }\n }\n\n if (existingContents !== contents) {\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, contents, 'utf8');\n }\n\n process.stdout.write(`Generated ${relative(process.cwd(), outputPath)}\\n`);\n};\n"],"names":["handler","async","output","schema","type","schemaPath","resolve","process","cwd","outputPath","contents","path","specifier","split","sep","join","replace","startsWith","createModuleSpecifier","relative","dirname","existingContents","readFile","error","code","isMissingFile","mkdir","recursive","writeFile","stdout","write"],"mappings":"8IAGA,MAsBaA,EAAUC,OAASC,SAAQC,SAAQC,WAC9C,MAAMC,EAAaC,EAAQC,QAAQC,MAAOL,GACpCM,EAAaH,EAAQC,QAAQC,MAAON,GAGpCQ,EAAW,CACf,iBAAiBN,aA5BS,CAACO,IAC7B,IAAIC,EAAYD,EACbE,MAAMC,GACNC,KAAK,KACLC,QAAQ,oBAAqB,IAMhC,OAJKJ,EAAUK,WAAW,OACxBL,EAAY,KAAKA,KAGZA,GAekBM,CAAsBC,EAASC,EAAQX,GAAaJ,QAI3E,GACA,2CACA,yCAAyCD,OACzC,IACA,GACA,aACA,IACAW,KAAK,MAEP,IAAIM,EAEJ,IACEA,QAAyBC,EAASb,EAAY,OAChD,CAAE,MAAOc,GACP,IA9BkB,CAACA,KACnBA,GAA0B,iBAAVA,GAAsB,SAAUA,GAAwB,WAAfA,EAAMC,KA6B1DC,CAAcF,GACjB,MAAMA,CAEV,CAEIF,IAAqBX,UACjBgB,EAAMN,EAAQX,GAAa,CAAEkB,WAAW,UACxCC,EAAUnB,EAAYC,EAAU,SAGxCH,QAAQsB,OAAOC,MAAM,aAAaX,EAASZ,QAAQC,MAAOC"}
@@ -0,0 +1,2 @@
1
+ import m from"yargs";import{hideBin as a}from"yargs/helpers";import*as o from"./cli/command.mjs";const r=async()=>{await m(a(process.argv)).scriptName("appsettings").command(o).demandCommand(1,"Choose a command to run.").strict().help().parseAsync()};export{r as runAppsettingsCli};
2
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","sources":["../../src/cli.ts"],"sourcesContent":["import yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport * as generateTypesCommand from './cli/command.ts';\n\nexport const runAppsettingsCli = async (): Promise<void> => {\n await yargs(hideBin(process.argv))\n .scriptName('appsettings')\n .command(generateTypesCommand)\n .demandCommand(1, 'Choose a command to run.')\n .strict()\n .help()\n .parseAsync();\n};\n"],"names":["runAppsettingsCli","async","yargs","hideBin","process","argv","scriptName","command","generateTypesCommand","demandCommand","strict","help","parseAsync"],"mappings":"uGAIaA,EAAoBC,gBACzBC,EAAMC,EAAQC,QAAQC,OACzBC,WAAW,eACXC,QAAQC,GACRC,cAAc,EAAG,4BACjBC,SACAC,OACAC"}
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import{jsx as t}from"react/jsx-runtime";import{useMemo as e}from"react";import{AppSettingsImplementation as o}from"../config/AppSettingsImplementation.mjs";import{AppSettingsContext as r}from"../hooks/useAppSettingsContext.mjs";const n=({children:n,settings:m})=>{const i=e(()=>new o(m),[m]);return t(r,{value:i,children:n})};export{n as AppSettingsProvider};
3
+ //# sourceMappingURL=AppSettingsProvider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSettingsProvider.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import{useAppSettingsContext as e}from"../hooks/useAppSettingsContext.mjs";function r({children:r,fallback:t,negate:n=!1,path:a,value:o}){const s=e().read(a),i=(Array.isArray(o)?o:[o]).includes(s);return(n?!i:i)?r:t}export{r as Setting};
3
+ //# sourceMappingURL=Setting.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Setting.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,2 @@
1
+ import{isPlainObject as t}from"remeda";import{ScopedSettingsImplementation as e}from"./ScopedSettingsImplementation.mjs";import{ConfigReader as r}from"./ConfigReader.mjs";import{ConfigStore as s}from"./ConfigStore.mjs";class o{constructor(t){this.store=new s(t)}get(t){return this.read(t)}read(t){return new r(this.store.getSnapshot()).read(t)}getAll(){return this.store.getSnapshot()}getOrThrow(t){const e=new r(this.store.getSnapshot()).read(t);if(void 0===e)throw new Error(`Required app setting "${t}" is missing.`);return e}getSection(t){return new r(this.store.getSnapshot()).read(t)}has(t){return new r(this.store.getSnapshot()).has(t)}scope(s){const o=new r(this.store.getSnapshot()).read(s);if(!t(o))throw new Error(`App setting section "${s}" is missing.`);return new e(o,s)}}export{o as AppSettingsImplementation};
2
+ //# sourceMappingURL=AppSettingsImplementation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSettingsImplementation.mjs","sources":["../../../src/config/AppSettingsImplementation.ts"],"sourcesContent":["import { isPlainObject } from 'remeda';\nimport { type Get } from 'type-fest';\nimport { ScopedSettingsImplementation } from '#config/ScopedSettingsImplementation.ts';\nimport { type InternalAppSettings, type ScopedSettings } from '#types/AppSettings.types.ts';\nimport { type SettingPath, type SettingSectionPath } from '#types/Config.types.ts';\nimport { ConfigReader } from './ConfigReader.ts';\nimport { ConfigStore } from './ConfigStore.ts';\n\nexport class AppSettingsImplementation<T extends object> implements InternalAppSettings<T> {\n private readonly store: ConfigStore<T>;\n\n constructor(settings: T) {\n this.store = new ConfigStore(settings);\n }\n\n get<P extends SettingPath<T>>(path: P): Get<T, P> | undefined;\n get(path: string): unknown {\n return this.read(path);\n }\n\n read(path: string): unknown {\n return new ConfigReader(this.store.getSnapshot()).read(path);\n }\n\n getAll(): Readonly<T> {\n return this.store.getSnapshot();\n }\n\n getOrThrow<P extends SettingPath<T>>(path: P): Get<T, P>;\n getOrThrow(path: string): unknown {\n const value = new ConfigReader(this.store.getSnapshot()).read(path);\n\n if (value === undefined) {\n throw new Error(`Required app setting \"${path}\" is missing.`);\n }\n\n return value;\n }\n\n getSection<P extends SettingSectionPath<T>>(path: P): Get<T, P> | undefined;\n getSection(path: string): unknown {\n return new ConfigReader(this.store.getSnapshot()).read(path);\n }\n\n has(path: SettingPath<T> | SettingSectionPath<T>): boolean;\n has(path: string): boolean {\n return new ConfigReader(this.store.getSnapshot()).has(path);\n }\n\n scope<P extends SettingSectionPath<T>>(path: P): ScopedSettings<Extract<Get<T, P>, object>>;\n scope(path: string): ScopedSettings<object> {\n const reader = new ConfigReader(this.store.getSnapshot());\n const section = reader.read(path);\n\n if (!isPlainObject(section)) {\n throw new Error(`App setting section \"${path}\" is missing.`);\n }\n\n return new ScopedSettingsImplementation(section, path);\n }\n}\n"],"names":["AppSettingsImplementation","constructor","settings","this","store","ConfigStore","get","path","read","ConfigReader","getSnapshot","getAll","getOrThrow","value","undefined","Error","getSection","has","scope","section","isPlainObject","ScopedSettingsImplementation"],"mappings":"2NAQO,MAAMA,EAGX,WAAAC,CAAYC,GACVC,KAAKC,MAAQ,IAAIC,EAAYH,EAC/B,CAGAI,GAAAA,CAAIC,GACF,OAAOJ,KAAKK,KAAKD,EACnB,CAEAC,IAAAA,CAAKD,GACH,OAAO,IAAIE,EAAaN,KAAKC,MAAMM,eAAeF,KAAKD,EACzD,CAEAI,MAAAA,GACE,OAAOR,KAAKC,MAAMM,aACpB,CAGAE,UAAAA,CAAWL,GACT,MAAMM,EAAQ,IAAIJ,EAAaN,KAAKC,MAAMM,eAAeF,KAAKD,GAE9D,QAAcO,IAAVD,EACF,MAAM,IAAIE,MAAM,yBAAyBR,kBAG3C,OAAOM,CACT,CAGAG,UAAAA,CAAWT,GACT,OAAO,IAAIE,EAAaN,KAAKC,MAAMM,eAAeF,KAAKD,EACzD,CAGAU,GAAAA,CAAIV,GACF,OAAO,IAAIE,EAAaN,KAAKC,MAAMM,eAAeO,IAAIV,EACxD,CAGAW,KAAAA,CAAMX,GACJ,MACMY,EADS,IAAIV,EAAaN,KAAKC,MAAMM,eACpBF,KAAKD,GAE5B,IAAKa,EAAcD,GACjB,MAAM,IAAIJ,MAAM,wBAAwBR,kBAG1C,OAAO,IAAIc,EAA6BF,EAASZ,EACnD"}
@@ -0,0 +1,2 @@
1
+ import{mergeDeep as e}from"remeda";class r{merge(r,...m){return m.reduce((r,m)=>e(r,m),r)}}export{r as ConfigMerger};
2
+ //# sourceMappingURL=ConfigMerger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigMerger.mjs","sources":["../../../src/config/ConfigMerger.ts"],"sourcesContent":["import { mergeDeep } from 'remeda';\n\nexport class ConfigMerger {\n merge<T extends object>(first: T, ...configs: readonly T[]): T {\n // TypeScript cannot guarantee that merging arbitrary objects produces T\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return configs.reduce((merged, config) => mergeDeep(merged, config) as T, first);\n }\n}\n"],"names":["ConfigMerger","merge","first","configs","reduce","merged","config","mergeDeep"],"mappings":"mCAEO,MAAMA,EACXC,KAAAA,CAAwBC,KAAaC,GAGnC,OAAOA,EAAQC,OAAO,CAACC,EAAQC,IAAWC,EAAUF,EAAQC,GAAcJ,EAC5E"}
@@ -0,0 +1,2 @@
1
+ import{isPlainObject as t}from"remeda";import{createConfigReader as r}from"zcb";class e{constructor(t){this.config=t,this.reader=r(t)}getAt(r){return r.split(".").reduce((r,e)=>{if(t(r))return r[e]},this.config)}get(t){return this.reader.read(t)}getSection(t){return this.getAt(t)}has(t){return void 0!==this.getAt(t)}read(t){return this.getAt(t)}scope(t){return this.reader.scope(t)}}export{e as ConfigReader};
2
+ //# sourceMappingURL=ConfigReader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigReader.mjs","sources":["../../../src/config/ConfigReader.ts"],"sourcesContent":["import { isPlainObject } from 'remeda';\nimport { type Get } from 'type-fest';\nimport { type Path, type Scope, type ConfigReader as ZcbConfigReader, createConfigReader } from 'zcb';\n\nexport class ConfigReader<T extends object> {\n private reader: ZcbConfigReader<T>;\n private readonly config: T;\n\n constructor(config: T) {\n this.config = config;\n this.reader = createConfigReader(config);\n }\n\n private getAt(path: string): unknown {\n return path.split('.').reduce<unknown>((current, key) => {\n if (!isPlainObject(current)) {\n return;\n }\n\n return current[key];\n }, this.config);\n }\n\n public get<P extends Path<T>>(path: P): Get<T, P> {\n return this.reader.read(path);\n }\n\n public getSection<P extends Scope<T>>(path: P): Get<T, P> {\n // Required as underlying getAt needs to be kept permissive\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return this.getAt(path) as Get<T, P>;\n }\n\n public has(path: string): boolean {\n return this.getAt(path) !== undefined;\n }\n\n public read(path: string): unknown {\n return this.getAt(path);\n }\n\n public scope<P extends Scope<T>>(path: P): ZcbConfigReader<Get<T, P>> {\n return this.reader.scope(path);\n }\n}\n"],"names":["ConfigReader","constructor","config","this","reader","createConfigReader","getAt","path","split","reduce","current","key","isPlainObject","get","read","getSection","has","undefined","scope"],"mappings":"gFAIO,MAAMA,EAIX,WAAAC,CAAYC,GACVC,KAAKD,OAASA,EACdC,KAAKC,OAASC,EAAmBH,EACnC,CAEQI,KAAAA,CAAMC,GACZ,OAAOA,EAAKC,MAAM,KAAKC,OAAgB,CAACC,EAASC,KAC/C,GAAKC,EAAcF,GAInB,OAAOA,EAAQC,IACdR,KAAKD,OACV,CAEOW,GAAAA,CAAuBN,GAC5B,OAAOJ,KAAKC,OAAOU,KAAKP,EAC1B,CAEOQ,UAAAA,CAA+BR,GAGpC,OAAOJ,KAAKG,MAAMC,EACpB,CAEOS,GAAAA,CAAIT,GACT,YAA4BU,IAArBd,KAAKG,MAAMC,EACpB,CAEOO,IAAAA,CAAKP,GACV,OAAOJ,KAAKG,MAAMC,EACpB,CAEOW,KAAAA,CAA0BX,GAC/B,OAAOJ,KAAKC,OAAOc,MAAMX,EAC3B"}
@@ -0,0 +1,2 @@
1
+ import{clone as s}from"remeda";import{freezeDeep as t}from"./helpers/freezeDeep.mjs";class e{constructor(e){this.getSnapshot=()=>this.snapshot,this.snapshot=t(s(e))}}export{e as ConfigStore};
2
+ //# sourceMappingURL=ConfigStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigStore.mjs","sources":["../../../src/config/ConfigStore.ts"],"sourcesContent":["import { clone } from 'remeda';\nimport { freezeDeep } from '#config/helpers/freezeDeep.ts';\n\nexport class ConfigStore<T extends object> {\n private snapshot: Readonly<T>;\n\n public getSnapshot = (): Readonly<T> => this.snapshot;\n\n constructor(initial: T) {\n this.snapshot = freezeDeep(clone(initial));\n }\n}\n"],"names":["ConfigStore","constructor","initial","getSnapshot","this","snapshot","freezeDeep","clone"],"mappings":"qFAGO,MAAMA,EAKX,WAAAC,CAAYC,QAFLC,YAAc,IAAmBC,KAAKC,SAG3CD,KAAKC,SAAWC,EAAWC,EAAML,GACnC"}
@@ -0,0 +1,2 @@
1
+ class s{constructor(s){this.schema=s}validate(s){return this.schema.parse(s)}}export{s as ConfigValidator};
2
+ //# sourceMappingURL=ConfigValidator.mjs.map