gruber 0.9.0-beta.2 → 0.9.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 (240) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +76 -37
  3. package/config/configuration.d.ts +171 -0
  4. package/config/configuration.d.ts.map +1 -1
  5. package/config/configuration.js +167 -10
  6. package/config/configuration.ts +196 -12
  7. package/config/index.md +175 -0
  8. package/config/parsers.d.ts +19 -0
  9. package/config/parsers.d.ts.map +1 -1
  10. package/config/parsers.js +14 -0
  11. package/config/parsers.ts +19 -0
  12. package/config/specifications.d.ts +3 -3
  13. package/config/specifications.d.ts.map +1 -1
  14. package/config/specifications.js +3 -3
  15. package/config/specifications.ts +3 -3
  16. package/config/standard-schema.d.ts +0 -1
  17. package/config/standard-schema.d.ts.map +1 -1
  18. package/config/standard-schema.ts +0 -1
  19. package/config/struct-context.d.ts +11 -1
  20. package/config/struct-context.d.ts.map +1 -1
  21. package/config/struct-context.js +6 -1
  22. package/config/struct-context.ts +11 -2
  23. package/config/struct-error.d.ts +66 -1
  24. package/config/struct-error.d.ts.map +1 -1
  25. package/config/struct-error.js +54 -1
  26. package/config/struct-error.test.js +15 -10
  27. package/config/struct-error.ts +66 -1
  28. package/config/structure.d.ts +136 -0
  29. package/config/structure.d.ts.map +1 -1
  30. package/config/structure.js +138 -3
  31. package/config/structure.test.js +10 -20
  32. package/config/structure.ts +138 -7
  33. package/core/authentication.d.ts +16 -1
  34. package/core/authentication.d.ts.map +1 -1
  35. package/core/authentication.js +4 -1
  36. package/core/authentication.ts +16 -1
  37. package/core/container.d.ts +83 -2
  38. package/core/container.d.ts.map +1 -1
  39. package/core/container.js +71 -2
  40. package/core/container.ts +83 -2
  41. package/core/index.md +19 -0
  42. package/core/migrator.d.ts +145 -0
  43. package/core/migrator.d.ts.map +1 -1
  44. package/core/migrator.js +78 -2
  45. package/core/migrator.ts +149 -2
  46. package/core/mod.d.ts +0 -2
  47. package/core/mod.d.ts.map +1 -1
  48. package/core/mod.js +0 -2
  49. package/core/mod.ts +0 -2
  50. package/core/random.d.ts +26 -0
  51. package/core/random.d.ts.map +1 -1
  52. package/core/random.js +10 -0
  53. package/core/random.ts +26 -0
  54. package/core/store.d.ts +80 -29
  55. package/core/store.d.ts.map +1 -1
  56. package/core/store.js +21 -62
  57. package/core/store.ts +89 -90
  58. package/core/terminator.d.ts +80 -1
  59. package/core/terminator.d.ts.map +1 -1
  60. package/core/terminator.js +63 -1
  61. package/core/terminator.ts +85 -1
  62. package/core/test-deps.js +2 -2
  63. package/core/tokens.d.ts +34 -1
  64. package/core/tokens.d.ts.map +1 -1
  65. package/core/tokens.js +6 -0
  66. package/core/tokens.ts +34 -1
  67. package/core/utilities.d.ts +180 -2
  68. package/core/utilities.d.ts.map +1 -1
  69. package/core/utilities.js +241 -4
  70. package/core/utilities.test.js +228 -1
  71. package/core/utilities.ts +251 -4
  72. package/express-router.d.ts +1 -1
  73. package/express-router.d.ts.map +1 -1
  74. package/express-router.js +1 -1
  75. package/express-router.ts +1 -1
  76. package/http/authorization.d.ts +88 -2
  77. package/http/authorization.d.ts.map +1 -1
  78. package/http/authorization.js +48 -4
  79. package/http/authorization.ts +99 -6
  80. package/http/cors.d.ts +50 -4
  81. package/http/cors.d.ts.map +1 -1
  82. package/http/cors.js +35 -3
  83. package/http/cors.ts +52 -5
  84. package/http/define-route.d.ts +29 -2
  85. package/http/define-route.d.ts.map +1 -1
  86. package/http/define-route.js +17 -0
  87. package/http/define-route.ts +29 -2
  88. package/http/fetch-router.d.ts +83 -1
  89. package/http/fetch-router.d.ts.map +1 -1
  90. package/http/fetch-router.js +121 -10
  91. package/http/fetch-router.test.js +1 -1
  92. package/http/fetch-router.ts +135 -12
  93. package/http/http-error.d.ts +68 -5
  94. package/http/http-error.d.ts.map +1 -1
  95. package/http/http-error.js +68 -6
  96. package/http/http-error.ts +71 -6
  97. package/http/index.md +57 -0
  98. package/http/request-body.d.ts +57 -2
  99. package/http/request-body.d.ts.map +1 -1
  100. package/http/request-body.js +27 -5
  101. package/http/request-body.ts +63 -8
  102. package/http/server-sent-events.d.ts +24 -17
  103. package/http/server-sent-events.d.ts.map +1 -1
  104. package/http/server-sent-events.js +9 -11
  105. package/http/server-sent-events.ts +28 -17
  106. package/koa-router.d.ts +1 -1
  107. package/koa-router.d.ts.map +1 -1
  108. package/koa-router.js +1 -1
  109. package/koa-router.ts +1 -1
  110. package/mod.d.ts +6 -1
  111. package/mod.d.ts.map +1 -1
  112. package/mod.js +6 -1
  113. package/mod.ts +6 -1
  114. package/node/config.d.ts +28 -0
  115. package/node/config.d.ts.map +1 -0
  116. package/{source/configuration.js → node/config.js} +18 -2
  117. package/{source/configuration.ts → node/config.ts} +18 -3
  118. package/node/index.md +100 -0
  119. package/node/mod.d.ts +5 -0
  120. package/node/mod.d.ts.map +1 -0
  121. package/{source → node}/mod.js +1 -3
  122. package/{source → node}/mod.ts +1 -4
  123. package/node/node-router.d.ts +172 -0
  124. package/node/node-router.d.ts.map +1 -0
  125. package/{source → node}/node-router.js +132 -13
  126. package/{source → node}/node-router.ts +132 -13
  127. package/node/node_modules/.package-lock.json +13 -0
  128. package/node/node_modules/urlpattern-polyfill/LICENSE +19 -0
  129. package/node/node_modules/urlpattern-polyfill/README.md +242 -0
  130. package/node/node_modules/urlpattern-polyfill/dist/index.d.ts +9 -0
  131. package/node/node_modules/urlpattern-polyfill/dist/types.d.ts +49 -0
  132. package/node/node_modules/urlpattern-polyfill/dist/urlpattern.cjs +1 -0
  133. package/node/node_modules/urlpattern-polyfill/dist/urlpattern.js +1 -0
  134. package/node/node_modules/urlpattern-polyfill/index.cjs +7 -0
  135. package/node/node_modules/urlpattern-polyfill/index.js +7 -0
  136. package/node/node_modules/urlpattern-polyfill/package.json +149 -0
  137. package/{source → node}/postgres.d.ts +14 -1
  138. package/node/postgres.d.ts.map +1 -0
  139. package/{source → node}/postgres.js +17 -5
  140. package/{source → node}/postgres.ts +22 -9
  141. package/package.json +1 -1
  142. package/polyfill.d.ts +1 -1
  143. package/polyfill.d.ts.map +1 -1
  144. package/polyfill.js +1 -1
  145. package/polyfill.ts +1 -1
  146. package/postgres/index.md +18 -0
  147. package/postgres/mod.d.ts +4 -0
  148. package/postgres/mod.d.ts.map +1 -0
  149. package/postgres/mod.js +3 -0
  150. package/postgres/mod.ts +3 -0
  151. package/postgres/postgres-migrator.d.ts +75 -0
  152. package/postgres/postgres-migrator.d.ts.map +1 -0
  153. package/postgres/postgres-migrator.js +106 -0
  154. package/postgres/postgres-migrator.ts +141 -0
  155. package/postgres/postgres-service.d.ts +109 -0
  156. package/postgres/postgres-service.d.ts.map +1 -0
  157. package/postgres/postgres-service.js +10 -0
  158. package/postgres/postgres-service.ts +143 -0
  159. package/postgres/postgres-store.d.ts +26 -0
  160. package/postgres/postgres-store.d.ts.map +1 -0
  161. package/postgres/postgres-store.js +65 -0
  162. package/postgres/postgres-store.ts +95 -0
  163. package/testing/{README.md → index.md} +0 -1
  164. package/config/README.md +0 -11
  165. package/configuration.d.ts +0 -2
  166. package/configuration.d.ts.map +0 -1
  167. package/configuration.js +0 -1
  168. package/configuration.ts +0 -1
  169. package/core/README.md +0 -11
  170. package/core/configuration.d.ts +0 -2
  171. package/core/configuration.d.ts.map +0 -1
  172. package/core/configuration.js +0 -1
  173. package/core/configuration.ts +0 -1
  174. package/core/postgres.d.ts +0 -13
  175. package/core/postgres.d.ts.map +0 -1
  176. package/core/postgres.js +0 -55
  177. package/core/postgres.ts +0 -84
  178. package/core.d.ts +0 -2
  179. package/core.d.ts.map +0 -1
  180. package/core.js +0 -1
  181. package/core.ts +0 -1
  182. package/http/README.md +0 -11
  183. package/http.d.ts +0 -2
  184. package/http.d.ts.map +0 -1
  185. package/http.js +0 -1
  186. package/http.ts +0 -1
  187. package/node-router.d.ts +0 -2
  188. package/node-router.d.ts.map +0 -1
  189. package/node-router.js +0 -1
  190. package/node-router.ts +0 -1
  191. package/postgres.d.ts +0 -2
  192. package/postgres.d.ts.map +0 -1
  193. package/postgres.js +0 -1
  194. package/postgres.ts +0 -1
  195. package/source/README.md +0 -11
  196. package/source/configuration.d.ts +0 -12
  197. package/source/configuration.d.ts.map +0 -1
  198. package/source/core.d.ts +0 -2
  199. package/source/core.d.ts.map +0 -1
  200. package/source/core.js +0 -1
  201. package/source/core.ts +0 -1
  202. package/source/http.d.ts +0 -2
  203. package/source/http.d.ts.map +0 -1
  204. package/source/http.js +0 -1
  205. package/source/http.ts +0 -1
  206. package/source/mod.d.ts +0 -7
  207. package/source/mod.d.ts.map +0 -1
  208. package/source/node-router.d.ts +0 -52
  209. package/source/node-router.d.ts.map +0 -1
  210. package/source/postgres.d.ts.map +0 -1
  211. package/source/testing.d.ts +0 -2
  212. package/source/testing.d.ts.map +0 -1
  213. package/source/testing.js +0 -1
  214. package/source/testing.ts +0 -1
  215. package/terminator.d.ts +0 -2
  216. package/terminator.d.ts.map +0 -1
  217. package/terminator.js +0 -1
  218. package/terminator.ts +0 -1
  219. package/testing.d.ts +0 -2
  220. package/testing.d.ts.map +0 -1
  221. package/testing.js +0 -1
  222. package/testing.ts +0 -1
  223. /package/{source → node}/express-router.d.ts +0 -0
  224. /package/{source → node}/express-router.d.ts.map +0 -0
  225. /package/{source → node}/express-router.js +0 -0
  226. /package/{source → node}/express-router.ts +0 -0
  227. /package/{source → node}/koa-router.d.ts +0 -0
  228. /package/{source → node}/koa-router.d.ts.map +0 -0
  229. /package/{source → node}/koa-router.js +0 -0
  230. /package/{source → node}/koa-router.ts +0 -0
  231. /package/{source → node}/package-lock.json +0 -0
  232. /package/{source → node}/package.json +0 -0
  233. /package/{source → node}/polyfill.d.ts +0 -0
  234. /package/{source → node}/polyfill.d.ts.map +0 -0
  235. /package/{source → node}/polyfill.js +0 -0
  236. /package/{source → node}/polyfill.ts +0 -0
  237. /package/{source → node}/terminator.d.ts +0 -0
  238. /package/{source → node}/terminator.d.ts.map +0 -0
  239. /package/{source → node}/terminator.js +0 -0
  240. /package/{source → node}/terminator.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -24,7 +24,12 @@ This file documents notable changes to the project
24
24
  - `assertRequestBody` can return a Promise if you pass a request,
25
25
  where it will use `getRequestBody` to get the body then validate it
26
26
  - Add `createStoppable` to Node.js module and apply it to `serveHTTP`
27
- - Add `testing` module with utilities, stubs, fakes and a testing router
27
+ - Add experimental `testing` module with utilities, stubs, fakes and a testing router
28
+ - Add `preventExtraction` and `dangerouslyExpose` to core module
29
+ - (WIP) Attempting to `JSON.stringify` any values from configuration value now throws a TypeError
30
+ - Add experimental wildcard HTTP methods
31
+ - Add HTTP OPTIONS requests when enabling cors
32
+ - Add experimental `terminator.waitForSignals()` async method
28
33
 
29
34
  **improved**
30
35
 
@@ -35,12 +40,19 @@ This file documents notable changes to the project
35
40
  - Simplified `Structure` and `config` generics with nested structures
36
41
  - Update `urlpattern-polyfill` to 10.1.0
37
42
  - Move to erasable TypeScript code
43
+ - Store conforms to AsyncDisposable
44
+ - FetchRouter — log HTTP errors when `options.log` is set and `errorHandler` is not
45
+ - FetchRouter — Attempt to apply internal middleware during error handling too
38
46
 
39
47
  **fixes**
40
48
 
41
49
  - `AbstractAuthorizationService` includes assert's `options` parameter
42
50
  - Configuration correctly follows `flags > env > json > fallback`
43
51
  - `Structure.object` sets required fields in schema
52
+ - Internaly use `verbatimModuleSyntax` to be more JavaScript agnostic
53
+ - Remove use of conditional imports in the node polyfil
54
+ - `FetchRouter` internally applies middleware during error handling too
55
+ - node - terminate the stream if the ServerResponse is cancelled
44
56
 
45
57
  **deprecations**
46
58
 
package/README.md CHANGED
@@ -11,61 +11,100 @@ An isomorphic JavaScript library for creating web apps.
11
11
 
12
12
  ## Contents
13
13
 
14
- - [Quick tour](/quick-tour/) — a whistle-stop tour of all of Gruber
15
- - [Core](/core/) — essential primitives that use or build on web-standards
16
- - [HTTP](/http/) — quickly define server endpoints
17
- - [Configuration](/config/) — specify exactly how to configure your application
18
- - [Migrations](/core#migrations) — curate how to migrate your state
19
- - [Testing](/testing/) — ensure your code does what you thought it does
20
- - [Node](/node/) — easily use Gruber in Node.js
21
- - [Deno](/deno/) — easily use Gruber in Deno
22
- - [Examples](/examples/) — more in-depth use cases and patterns
14
+ - [Install](#install)
15
+ - [Quick tour](/quick-tour/) — a whistle-stop tour of everything
16
+ - [Core](/core/) — essentials that use or build on web-standards
17
+ - [HTTP](/http/) — quickly create server endpoints
18
+ - [Configuration](/config/) — declaratively configure your application
19
+ - [Migrations](/core#migrations) — specify transitions between application state
20
+ - [Testing](/testing/) — ensure your code does what you expected
23
21
 
24
22
  ## About
25
23
 
26
- Gruber is a collection of modules for creating isomorphic JavaScript applications, that means web-standards JavaScript on the front- and back-end. It's a bet that web-standards aren't going to change, so our apps don't break in the future. There's also a hope that [WinterTG](https://wintertc.org/work) works some stuff out. (Previously WinterCG, which is a good step!)
24
+ Gruber is a collection of modules for creating isomorphic JavaScript applications.
25
+ That means using the same web standards developed for the browser in backend runtimes.
26
+ Web-standards aren't going to change, so apps based on them are less likely break in the future.
27
+ There's also a hope that [WinterTG](https://wintertc.org/work) works some stuff out.
27
28
 
28
29
  Gruber acknowledges that web-standards don't do everything we want (at least yet!) and that they aren't implemented properly everywhere either.
29
- For this reason, the core of Gruber is agnostic and attempts to make sensible building blocks on top of them. Then there are **helpers** for using specific runtimes and **modules** that build around those common primitives.
30
+ For this reason, Gruber tries to be as agnostic as possible and makes building blocks on top of them.
31
+ There are **integrations** with specific runtimes & libraries and **modules** that build around those common primitives.
30
32
 
31
- Gruber itself is a library and can be used however you like. The rest are **patterns** which you can apply if you like.
33
+ Gruber itself is a library and can be used however you like.
34
+ There are also **patterns** which you can apply if you like.
32
35
  Patterns are ways of structuring your code if you don't already have opinions on the matter.
33
36
  They also help to explain why Gruber is made in the way it is.
34
37
 
35
38
  There is a lot not in Gruber too. By design it tries to be as minimal as possible.
36
- For examples, there is a development CORs implementation but a production app should be run behind a reverse proxy and that can do those things for you.
37
39
 
38
- ## Why does this exist
40
+ ## Terms
39
41
 
40
- I've spent the past few years working on JavaScript backends and nothing has really stuck with me.
41
- There have been lots of nice ideas along the way but no one solution ever felt like home.
42
- It always felt like starting from scratch for each project.
42
+ These are a few words that pop-up in the documentation, often in bold,
43
+ here is what they mean in the context of Gruber:
43
44
 
44
- Some of the apps I've made:
45
+ - **standards** well-known, non-proprietary, formal and agreed specifications
46
+ - **modules** — code built around web-standards and the common core
47
+ - **integrations** — a module integrating with a specific JavaScript runtime or library
48
+ - **patterns** — optional but recommended best-practices you can adopt
49
+ - **isomorphic** — running the same JavaScript on the front and backend
45
50
 
46
- - [Open Lab Hub](https://github.com/digitalinteraction/hub.openlab.dev) — Deno + Oak + vue3 + vite
47
- - [BeanCounter](https://github.com/digitalinteraction/beancounter) — Node.js + Koa + vanilla js + parcel
48
- - [MozFest Plaza](https://github.com/digitalinteraction/mozfest) Node.js + Koa + vue2 + webpack
49
- - [Sticker Stories](https://github.com/digitalinteraction/sticker-stories) Node.js + Koa + vue3 + vite
50
- - [Data Diaries](https://github.com/digitalinteraction/data-diaries) Node.js + Koa + vue3 + vite
51
- - [DataOfficer](https://github.com/digitalinteraction/data-officer) — Deno + Acorn
52
- - [IrisMsg](https://github.com/digitalinteraction/iris-msg/tree/master) — Node.js + Express + native app
53
- - [Poster Vote](https://github.com/digitalinteraction/poster-vote) — Node.js + Express + vue + webpack
51
+ ## Background
52
+
53
+ I've spent several years working on JavaScript backends and nothing has really stuck with me.
54
+ There have been lots of nice ideas along the way but no one solution ever felt like home.
55
+ It always felt like starting from scratch for each project.
54
56
 
55
57
  Many frameworks expect you to be making the next big platform with millions of users,
56
58
  I don't expect this to be true and would prefer to keep things as simple as possible for the projects I am working on.
57
59
 
58
- I'm also quite wary of going all-in on a big tech company's library or framework,
59
- having explored lots of them on smaller projects.
60
- The fatigue from breaking changes or deprecations is real,
61
- "move fast and break things" sounds great but it creates a lot of maintenance when you work of lots of small projects in small teams.
60
+ I'm also quite wary of going all-in on a big tech company's library or framework.
61
+ Having explored lots of them, the fatigue from breaking changes or deprecations is real.
62
+ "Move fast and break things" sounds great but it creates a lot of maintenance,
63
+ especially for small projects and teams.
64
+
65
+ I'd prefer a style of **move slow and deliberate**.
66
+ So I use Gruber in a project and develop features within that project.
67
+ Then if I find myself copy-pasting those modules between projects,
68
+ I'll look into ways of contributing them back to the library.
62
69
 
63
70
  ## Principles
64
71
 
65
- - Composabilitylogic should be composed together rather than messily intertwined
66
- - Standards based where available existing standards should be applied or migrated towards
67
- - Agnostica frontend framework or backend runtime shouldn't be forced upon you
68
- - Patternshow you _could_ use modules rather than enforce an implementation
69
- - Minimal start small, carefully add features and consider removing them
70
- - Holistic — by owning the tech-stack it creates unique opportunities for integration
71
- - No magic — it's confusing when you don't know whats going on
72
+ - **Standardised & Compatible** use existing standards, migrate towards them and try not to break things
73
+ - **Agnostic**libraries, frameworks or runtimes shouldn't be forced upon you
74
+ - **Patterns**optional best-practises for how to use modules
75
+ - **Composability**logic should be composed together rather than messily intertwined
76
+ - **Minimal & Deliberate** carefully add only what's is necessary
77
+ - **Holistic**complete ownership and careful abstractions creates unique opportunities for integration
78
+ - **No magic** — it's confusing when you don't know what's going on
79
+
80
+ ## Standards
81
+
82
+ Here is a non-exclusive list of standards that Gruber uses or is interested in.
83
+
84
+ - [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) — used extensively in the HTTP module
85
+ - [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) — is used within `FetchRouter`
86
+ - [StandardSchema V1](https://standardschema.dev/) — is supported by `Structure` and `assertRequestBody`
87
+ - [JSONSchema](https://json-schema.org/) — is used inside `Structure` and can be generated.
88
+
89
+ Maybe
90
+
91
+ - [OpenAPI](https://swagger.io/specification/)
92
+ - Open CLI?
93
+
94
+ ## Direction
95
+
96
+ Here are places I want to explore next.
97
+
98
+ - [CLI](https://github.com/robb-j/gruber/issues/54) — declaratively define command line interfaces
99
+ - Frontend configuration — using the `config` module on the front-end
100
+ - SQL — an abstraction over database communication
101
+ - Open API, HTTP documentation & JS client generation
102
+ - Dependencies — a system for defining interdependent modules and controlling their lifecycle
103
+ - Events — ways of broadcasting and consuming them with different topologies
104
+ - [Even more →](https://github.com/robb-j/gruber/issues)
105
+
106
+ ## Get Started
107
+
108
+ - [Take the tour →](/tour/)
109
+ - [Get started with Node.js →](/node/)
110
+ - [Get started with Deno →](/deno/)
@@ -2,13 +2,50 @@ import { Structure } from "./structure.ts";
2
2
  import { type ConfigurationDescription, type PrimativeOptions } from "./specifications.ts";
3
3
  import { type ConfigurationResult } from "./parsers.ts";
4
4
  import { type StructContext } from "./struct-context.ts";
5
+ /**
6
+ * @group Configuration
7
+ *
8
+ * Options for creating a platform-sepcific {@link Configuration} object,
9
+ * different methods provide abstractions over the filesystem & parsing capabilities of the Configuration.
10
+ *
11
+ * For instance, you could create one that loads remote files over S3 and parses them as YAML,
12
+ * or just a simple one that loads JSON files from the filesystem
13
+ */
5
14
  export interface ConfigurationOptions {
15
+ /** Read in a file and decode it as text, or return null if it doesn't exist */
6
16
  readTextFile(url: URL | string): Promise<string | null>;
17
+ /** Get a specific environment variable, or undefined if it is not set */
7
18
  getEnvironmentVariable(key: string): string | undefined;
19
+ /** Get a specific CLI option, like `--some-thing`, or undefined if it is not set */
8
20
  getCommandArgument(key: string): string | undefined;
21
+ /** Convert an in-memory value to a string for displaying to the user */
9
22
  stringify(value: any): string | Promise<string>;
23
+ /** Parse a text file into in-memory values */
10
24
  parse(value: string): any;
11
25
  }
26
+ /**
27
+ * @group Configuration
28
+ *
29
+ * **Configuration** is both an abstraction around processing config files,
30
+ * environment variables & CLI flags from the platform
31
+ * and also a tool for users to declaratively define how their configuration is.
32
+ *
33
+ * Each platform specifies a default `options` to load JSON files,
34
+ * but you can also construct your own if you want to customise how it works.
35
+ *
36
+ * With an instance, you can then define how an app's config can be specified as either configuration files,
37
+ * CLI flag, environment variables or a combination of any of them.
38
+ *
39
+ * ```js
40
+ * const config = new Configuration({
41
+ * readTextFile(url) {},
42
+ * getEnvironmentVariable(key) {},
43
+ * getCommandArgument() {},
44
+ * stringify(value) {},
45
+ * parse(value) {},
46
+ * })
47
+ * ```
48
+ */
12
49
  export declare class Configuration {
13
50
  static readonly spec: unique symbol;
14
51
  options: ConfigurationOptions;
@@ -16,18 +53,152 @@ export declare class Configuration {
16
53
  _loadValue<T>(path: string | URL, structure: Structure<T>, context: StructContext): Promise<T | null>;
17
54
  /** Wrap a primativ Structure with configuration logic */
18
55
  _primative<T>(struct: Structure<T>, options: PrimativeOptions<T>, deconfigure: (result: ConfigurationResult) => unknown): Structure<T>;
56
+ /**
57
+ * Group or nest configuration in an object.
58
+ *
59
+ * ```js
60
+ * config.object({
61
+ * name: config.string({ fallback: "Geoff Testington" }),
62
+ * age: config.number({ fallback: 42 }),
63
+ * })
64
+ * ```
65
+ */
19
66
  object<T extends Record<string, unknown>>(fields: {
20
67
  [K in keyof T]: Structure<T[K]>;
21
68
  }): Structure<T>;
69
+ /**
70
+ * @unstable
71
+ *
72
+ * Create an ordered list of another type
73
+ *
74
+ * ```js
75
+ * config.array(
76
+ * Structure.string()
77
+ * )
78
+ * ```
79
+ */
22
80
  array<T extends unknown>(item: Structure<T>): Structure<T[]>;
81
+ /**
82
+ * @unstable
83
+ *
84
+ * Load another configuration file or use value in the original configuration
85
+ *
86
+ * ```js
87
+ * config.external(
88
+ * new URL("./api-keys.json", import.meta.url),
89
+ * config.object({
90
+ * keys: Structure.array(Structure.string())
91
+ * })
92
+ * )
93
+ * ```
94
+ *
95
+ * Which will attempt to load "api-keys.json" and parse that,
96
+ * and if that doesn't exist it will also try the value in the original configuration.
97
+ */
23
98
  external<T extends Record<string, unknown> | Array<unknown>>(path: string | URL, struct: Structure<T>): Structure<T>;
99
+ /**
100
+ * Define a string-based value with options to load from the config-file,
101
+ * an environment variable or a CLI flag.
102
+ * The only required field is **fallback**
103
+ *
104
+ * ```js
105
+ * config.string({
106
+ * variable: "HOSTNAME",
107
+ * flag: "--host",
108
+ * fallback: "localhost"
109
+ * })
110
+ * ```
111
+ */
24
112
  string(options: PrimativeOptions<string>): Structure<string>;
113
+ /**
114
+ * Define a numeric value with options to load from the config-file,
115
+ * an environment variable or a CLI flag.
116
+ * The only required field is **fallback**
117
+ *
118
+ * It will also coerce floating point numbers from strings
119
+ *
120
+ * ```js
121
+ * config.number({
122
+ * variable: "PORT",
123
+ * flag: "--port",
124
+ * fallback: "1234"
125
+ * })
126
+ * ```
127
+ */
25
128
  number(options: PrimativeOptions<number>): Structure<number>;
129
+ /**
130
+ * Define a boolean value with options to load from the config-file,
131
+ * an environment variable or a CLI flag.
132
+ * The only required field is **fallback**
133
+ *
134
+ * There are extra coercions for boolean-like strings
135
+ *
136
+ * - `1`, `true` & `yes` coerce to true
137
+ * - `0`, `false` & `no` coerce to false
138
+ *
139
+ * ```js
140
+ * config.boolean({
141
+ * variable: "USE_SSL",
142
+ * flag: "--ssl",
143
+ * fallback: false
144
+ * })
145
+ * ```
146
+ */
26
147
  boolean(options: PrimativeOptions<boolean>): Structure<boolean>;
148
+ /**
149
+ * Define a URL based value, the value is validated and converted into a [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).
150
+ *
151
+ * ```js
152
+ * config.url({
153
+ * variable: "SELF_URL",
154
+ * flag: "--url",
155
+ * fallback: "http://localhost:1234"
156
+ * })
157
+ * ```
158
+ */
27
159
  url(options: PrimativeOptions<string | URL>): Structure<URL>;
160
+ /**
161
+ * Load configuration with a base file, also pulling in environment variables and CLI flags using {@link ConfigurationOptions}
162
+ *
163
+ * ```js
164
+ * const struct = config.object({
165
+ * env: config.string({ variable: "NODE_ENV", fallback: "development" })
166
+ * })
167
+ *
168
+ * config.load(
169
+ * new URL("./app-config.json", import.meta.url),
170
+ * struct
171
+ * )
172
+ * ```
173
+ *
174
+ * It will asynchronously load the configuration, validate it and return the coerced value.
175
+ * If it fails it will output a friendly string listing what is wrong and throw the Structure.Error
176
+ */
28
177
  load<T>(url: URL | string, struct: Structure<T>): Promise<T>;
178
+ /**
179
+ * Given a structure defined using Configuration, generate human-readable usage information.
180
+ * The usage includes a table of all configuration options and what the default value would be if no other soruces are used.
181
+ *
182
+ * Optionally, output the current value of the configuration too.
183
+ */
29
184
  getUsage(struct: unknown, currentValue?: unknown): string;
185
+ /**
186
+ * @ignore
187
+ *
188
+ * Given a structure defined using configuration, get meta-information about it
189
+ */
30
190
  describe(value: unknown, prefix?: string): ConfigurationDescription;
191
+ /**
192
+ * @unstable
193
+ *
194
+ * Given a structure defined using configuration, generate a JSON Schema to validate it. This could be useful to write to a file then use a IDE-based validator using something like
195
+ *
196
+ * ```json
197
+ * {
198
+ * "$schema": "./app-config.schema.json",
199
+ * }
200
+ * ```
201
+ */
31
202
  getJSONSchema(struct: Structure<any>): import("./structure.ts").Schema;
32
203
  }
33
204
  //# sourceMappingURL=configuration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAEN,KAAK,wBAAwB,EAG7B,KAAK,gBAAgB,EAErB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAKN,KAAK,mBAAmB,EACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACpC,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxD,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxD,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC;CAC1B;AAED,qBAAa,aAAa;IACzB,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgC;IAEpD,OAAO,EAAE,oBAAoB,CAAC;gBAElB,OAAO,EAAE,oBAAoB;IAOnC,UAAU,CAAC,CAAC,EACjB,IAAI,EAAE,MAAM,GAAG,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgBpB,yDAAyD;IACzD,UAAU,CAAC,CAAC,EACX,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC5B,WAAW,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO;IActD,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE;SAChD,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC/B,GAAG,SAAS,CAAC,CAAC,CAAC;IAgBhB,KAAK,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC;IAW5D,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,EAC1D,IAAI,EAAE,MAAM,GAAG,GAAG,EAClB,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,SAAS,CAAC,CAAC,CAAC;IAgCf,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;IAkB5D,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;IAgB5D,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC;IAgB/D,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC;IAyBtD,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBlE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,OAAO;IAwBhD,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,wBAAwB;IAM/D,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;CAGpC"}
1
+ {"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["configuration.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,31 @@
1
1
  import { Structure } from "./structure.js";
2
- import { formatMarkdownTable, PromiseList } from "../core/mod.js";
3
- import { arraySpec, getSpecification, objectSpec, primativeSpec, } from "./specifications.js";
2
+ import { dangerouslyExpose, formatMarkdownTable, PromiseList, } from "../core/mod.js";
3
+ import { _arraySpec, getSpecification, _objectSpec, _primativeSpec, } from "./specifications.js";
4
4
  import { _parseBoolean, _parseFloat, _parsePrimative, _parseURL, } from "./parsers.js";
5
5
  import {} from "./struct-context.js";
6
+ /**
7
+ * @group Configuration
8
+ *
9
+ * **Configuration** is both an abstraction around processing config files,
10
+ * environment variables & CLI flags from the platform
11
+ * and also a tool for users to declaratively define how their configuration is.
12
+ *
13
+ * Each platform specifies a default `options` to load JSON files,
14
+ * but you can also construct your own if you want to customise how it works.
15
+ *
16
+ * With an instance, you can then define how an app's config can be specified as either configuration files,
17
+ * CLI flag, environment variables or a combination of any of them.
18
+ *
19
+ * ```js
20
+ * const config = new Configuration({
21
+ * readTextFile(url) {},
22
+ * getEnvironmentVariable(key) {},
23
+ * getCommandArgument() {},
24
+ * stringify(value) {},
25
+ * parse(value) {},
26
+ * })
27
+ * ```
28
+ */
6
29
  export class Configuration {
7
30
  static spec = Symbol("configuration.spec");
8
31
  options;
@@ -33,6 +56,16 @@ export class Configuration {
33
56
  //
34
57
  // Types
35
58
  //
59
+ /**
60
+ * Group or nest configuration in an object.
61
+ *
62
+ * ```js
63
+ * config.object({
64
+ * name: config.string({ fallback: "Geoff Testington" }),
65
+ * age: config.number({ fallback: 42 }),
66
+ * })
67
+ * ```
68
+ */
36
69
  object(fields) {
37
70
  if (typeof fields !== "object" || fields === null) {
38
71
  throw new TypeError("options must be a non-null object");
@@ -44,20 +77,48 @@ export class Configuration {
44
77
  }
45
78
  const struct = Structure.object(fields);
46
79
  Object.defineProperty(struct, Configuration.spec, {
47
- value: objectSpec(fields),
80
+ value: _objectSpec(fields),
48
81
  });
49
82
  return struct;
50
83
  }
84
+ /**
85
+ * @unstable
86
+ *
87
+ * Create an ordered list of another type
88
+ *
89
+ * ```js
90
+ * config.array(
91
+ * Structure.string()
92
+ * )
93
+ * ```
94
+ */
51
95
  array(item) {
52
96
  if (!(item instanceof Structure)) {
53
97
  throw new TypeError("options is not a Structure");
54
98
  }
55
99
  const struct = Structure.array(item);
56
100
  Object.defineProperty(struct, Configuration.spec, {
57
- value: arraySpec(item),
101
+ value: _arraySpec(item),
58
102
  });
59
103
  return struct;
60
104
  }
105
+ /**
106
+ * @unstable
107
+ *
108
+ * Load another configuration file or use value in the original configuration
109
+ *
110
+ * ```js
111
+ * config.external(
112
+ * new URL("./api-keys.json", import.meta.url),
113
+ * config.object({
114
+ * keys: Structure.array(Structure.string())
115
+ * })
116
+ * )
117
+ * ```
118
+ *
119
+ * Which will attempt to load "api-keys.json" and parse that,
120
+ * and if that doesn't exist it will also try the value in the original configuration.
121
+ */
61
122
  external(path, struct) {
62
123
  return new Structure(struct.schema, (value, context) => {
63
124
  if (context.type !== "async") {
@@ -85,36 +146,93 @@ export class Configuration {
85
146
  return internal;
86
147
  });
87
148
  }
149
+ /**
150
+ * Define a string-based value with options to load from the config-file,
151
+ * an environment variable or a CLI flag.
152
+ * The only required field is **fallback**
153
+ *
154
+ * ```js
155
+ * config.string({
156
+ * variable: "HOSTNAME",
157
+ * flag: "--host",
158
+ * fallback: "localhost"
159
+ * })
160
+ * ```
161
+ */
88
162
  string(options) {
89
163
  if (typeof options.fallback !== "string") {
90
164
  throw new TypeError("options.fallback must be a string");
91
165
  }
92
166
  const struct = this._primative(Structure.string(), options, (result) => result.value);
93
167
  Object.defineProperty(struct, Configuration.spec, {
94
- value: primativeSpec("string", options),
168
+ value: _primativeSpec("string", options),
95
169
  });
96
170
  return struct;
97
171
  }
172
+ /**
173
+ * Define a numeric value with options to load from the config-file,
174
+ * an environment variable or a CLI flag.
175
+ * The only required field is **fallback**
176
+ *
177
+ * It will also coerce floating point numbers from strings
178
+ *
179
+ * ```js
180
+ * config.number({
181
+ * variable: "PORT",
182
+ * flag: "--port",
183
+ * fallback: "1234"
184
+ * })
185
+ * ```
186
+ */
98
187
  number(options) {
99
188
  if (typeof options.fallback !== "number") {
100
189
  throw new TypeError("options.fallback must be a number");
101
190
  }
102
191
  const struct = this._primative(Structure.number(), options, (result) => _parseFloat(result));
103
192
  Object.defineProperty(struct, Configuration.spec, {
104
- value: primativeSpec("number", options),
193
+ value: _primativeSpec("number", options),
105
194
  });
106
195
  return struct;
107
196
  }
197
+ /**
198
+ * Define a boolean value with options to load from the config-file,
199
+ * an environment variable or a CLI flag.
200
+ * The only required field is **fallback**
201
+ *
202
+ * There are extra coercions for boolean-like strings
203
+ *
204
+ * - `1`, `true` & `yes` coerce to true
205
+ * - `0`, `false` & `no` coerce to false
206
+ *
207
+ * ```js
208
+ * config.boolean({
209
+ * variable: "USE_SSL",
210
+ * flag: "--ssl",
211
+ * fallback: false
212
+ * })
213
+ * ```
214
+ */
108
215
  boolean(options) {
109
216
  if (typeof options?.fallback !== "boolean") {
110
217
  throw new TypeError("options.fallback must be a boolean");
111
218
  }
112
219
  const struct = this._primative(Structure.boolean(), options, (result) => _parseBoolean(result));
113
220
  Object.defineProperty(struct, Configuration.spec, {
114
- value: primativeSpec("boolean", options),
221
+ value: _primativeSpec("boolean", options),
115
222
  });
116
223
  return struct;
117
224
  }
225
+ /**
226
+ * Define a URL based value, the value is validated and converted into a [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).
227
+ *
228
+ * ```js
229
+ * config.url({
230
+ * variable: "SELF_URL",
231
+ * flag: "--url",
232
+ * fallback: "http://localhost:1234"
233
+ * })
234
+ * ```
235
+ */
118
236
  url(options) {
119
237
  if (typeof options.fallback !== "string" &&
120
238
  !(options.fallback instanceof URL)) {
@@ -123,13 +241,30 @@ export class Configuration {
123
241
  const opts2 = { ...options, fallback: new URL(options.fallback) };
124
242
  const struct = this._primative(Structure.url(), opts2, (result) => _parseURL(result));
125
243
  Object.defineProperty(struct, Configuration.spec, {
126
- value: primativeSpec("url", opts2),
244
+ value: _primativeSpec("url", opts2),
127
245
  });
128
246
  return struct;
129
247
  }
130
248
  //
131
249
  // Methods
132
250
  //
251
+ /**
252
+ * Load configuration with a base file, also pulling in environment variables and CLI flags using {@link ConfigurationOptions}
253
+ *
254
+ * ```js
255
+ * const struct = config.object({
256
+ * env: config.string({ variable: "NODE_ENV", fallback: "development" })
257
+ * })
258
+ *
259
+ * config.load(
260
+ * new URL("./app-config.json", import.meta.url),
261
+ * struct
262
+ * )
263
+ * ```
264
+ *
265
+ * It will asynchronously load the configuration, validate it and return the coerced value.
266
+ * If it fails it will output a friendly string listing what is wrong and throw the Structure.Error
267
+ */
133
268
  async load(url, struct) {
134
269
  const context = {
135
270
  type: "async",
@@ -154,6 +289,12 @@ export class Configuration {
154
289
  throw error;
155
290
  }
156
291
  }
292
+ /**
293
+ * Given a structure defined using Configuration, generate human-readable usage information.
294
+ * The usage includes a table of all configuration options and what the default value would be if no other soruces are used.
295
+ *
296
+ * Optionally, output the current value of the configuration too.
297
+ */
157
298
  getUsage(struct, currentValue) {
158
299
  const { fallback, fields } = this.describe(struct);
159
300
  const lines = [
@@ -166,17 +307,33 @@ export class Configuration {
166
307
  this.options.stringify(fallback),
167
308
  ];
168
309
  if (currentValue) {
169
- lines.push("", "", "Current:", JSON.stringify(currentValue, null, 2));
310
+ lines.push("", "", "Current:", JSON.stringify(dangerouslyExpose(currentValue), null, 2));
170
311
  }
171
312
  return lines.join("\n");
172
313
  }
314
+ /**
315
+ * @ignore
316
+ *
317
+ * Given a structure defined using configuration, get meta-information about it
318
+ */
173
319
  describe(value, prefix = "") {
174
320
  const spec = getSpecification(value);
175
321
  if (!spec)
176
322
  return { fallback: undefined, fields: [] };
177
323
  return spec(prefix);
178
324
  }
325
+ /**
326
+ * @unstable
327
+ *
328
+ * Given a structure defined using configuration, generate a JSON Schema to validate it. This could be useful to write to a file then use a IDE-based validator using something like
329
+ *
330
+ * ```json
331
+ * {
332
+ * "$schema": "./app-config.schema.json",
333
+ * }
334
+ * ```
335
+ */
179
336
  getJSONSchema(struct) {
180
- return struct.getSchema();
337
+ return struct.getFullSchema();
181
338
  }
182
339
  }