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.
- package/CHANGELOG.md +13 -1
- package/README.md +76 -37
- package/config/configuration.d.ts +171 -0
- package/config/configuration.d.ts.map +1 -1
- package/config/configuration.js +167 -10
- package/config/configuration.ts +196 -12
- package/config/index.md +175 -0
- package/config/parsers.d.ts +19 -0
- package/config/parsers.d.ts.map +1 -1
- package/config/parsers.js +14 -0
- package/config/parsers.ts +19 -0
- package/config/specifications.d.ts +3 -3
- package/config/specifications.d.ts.map +1 -1
- package/config/specifications.js +3 -3
- package/config/specifications.ts +3 -3
- package/config/standard-schema.d.ts +0 -1
- package/config/standard-schema.d.ts.map +1 -1
- package/config/standard-schema.ts +0 -1
- package/config/struct-context.d.ts +11 -1
- package/config/struct-context.d.ts.map +1 -1
- package/config/struct-context.js +6 -1
- package/config/struct-context.ts +11 -2
- package/config/struct-error.d.ts +66 -1
- package/config/struct-error.d.ts.map +1 -1
- package/config/struct-error.js +54 -1
- package/config/struct-error.test.js +15 -10
- package/config/struct-error.ts +66 -1
- package/config/structure.d.ts +136 -0
- package/config/structure.d.ts.map +1 -1
- package/config/structure.js +138 -3
- package/config/structure.test.js +10 -20
- package/config/structure.ts +138 -7
- package/core/authentication.d.ts +16 -1
- package/core/authentication.d.ts.map +1 -1
- package/core/authentication.js +4 -1
- package/core/authentication.ts +16 -1
- package/core/container.d.ts +83 -2
- package/core/container.d.ts.map +1 -1
- package/core/container.js +71 -2
- package/core/container.ts +83 -2
- package/core/index.md +19 -0
- package/core/migrator.d.ts +145 -0
- package/core/migrator.d.ts.map +1 -1
- package/core/migrator.js +78 -2
- package/core/migrator.ts +149 -2
- package/core/mod.d.ts +0 -2
- package/core/mod.d.ts.map +1 -1
- package/core/mod.js +0 -2
- package/core/mod.ts +0 -2
- package/core/random.d.ts +26 -0
- package/core/random.d.ts.map +1 -1
- package/core/random.js +10 -0
- package/core/random.ts +26 -0
- package/core/store.d.ts +80 -29
- package/core/store.d.ts.map +1 -1
- package/core/store.js +21 -62
- package/core/store.ts +89 -90
- package/core/terminator.d.ts +80 -1
- package/core/terminator.d.ts.map +1 -1
- package/core/terminator.js +63 -1
- package/core/terminator.ts +85 -1
- package/core/test-deps.js +2 -2
- package/core/tokens.d.ts +34 -1
- package/core/tokens.d.ts.map +1 -1
- package/core/tokens.js +6 -0
- package/core/tokens.ts +34 -1
- package/core/utilities.d.ts +180 -2
- package/core/utilities.d.ts.map +1 -1
- package/core/utilities.js +241 -4
- package/core/utilities.test.js +228 -1
- package/core/utilities.ts +251 -4
- package/express-router.d.ts +1 -1
- package/express-router.d.ts.map +1 -1
- package/express-router.js +1 -1
- package/express-router.ts +1 -1
- package/http/authorization.d.ts +88 -2
- package/http/authorization.d.ts.map +1 -1
- package/http/authorization.js +48 -4
- package/http/authorization.ts +99 -6
- package/http/cors.d.ts +50 -4
- package/http/cors.d.ts.map +1 -1
- package/http/cors.js +35 -3
- package/http/cors.ts +52 -5
- package/http/define-route.d.ts +29 -2
- package/http/define-route.d.ts.map +1 -1
- package/http/define-route.js +17 -0
- package/http/define-route.ts +29 -2
- package/http/fetch-router.d.ts +83 -1
- package/http/fetch-router.d.ts.map +1 -1
- package/http/fetch-router.js +121 -10
- package/http/fetch-router.test.js +1 -1
- package/http/fetch-router.ts +135 -12
- package/http/http-error.d.ts +68 -5
- package/http/http-error.d.ts.map +1 -1
- package/http/http-error.js +68 -6
- package/http/http-error.ts +71 -6
- package/http/index.md +57 -0
- package/http/request-body.d.ts +57 -2
- package/http/request-body.d.ts.map +1 -1
- package/http/request-body.js +27 -5
- package/http/request-body.ts +63 -8
- package/http/server-sent-events.d.ts +24 -17
- package/http/server-sent-events.d.ts.map +1 -1
- package/http/server-sent-events.js +9 -11
- package/http/server-sent-events.ts +28 -17
- package/koa-router.d.ts +1 -1
- package/koa-router.d.ts.map +1 -1
- package/koa-router.js +1 -1
- package/koa-router.ts +1 -1
- package/mod.d.ts +6 -1
- package/mod.d.ts.map +1 -1
- package/mod.js +6 -1
- package/mod.ts +6 -1
- package/node/config.d.ts +28 -0
- package/node/config.d.ts.map +1 -0
- package/{source/configuration.js → node/config.js} +18 -2
- package/{source/configuration.ts → node/config.ts} +18 -3
- package/node/index.md +100 -0
- package/node/mod.d.ts +5 -0
- package/node/mod.d.ts.map +1 -0
- package/{source → node}/mod.js +1 -3
- package/{source → node}/mod.ts +1 -4
- package/node/node-router.d.ts +172 -0
- package/node/node-router.d.ts.map +1 -0
- package/{source → node}/node-router.js +132 -13
- package/{source → node}/node-router.ts +132 -13
- package/node/node_modules/.package-lock.json +13 -0
- package/node/node_modules/urlpattern-polyfill/LICENSE +19 -0
- package/node/node_modules/urlpattern-polyfill/README.md +242 -0
- package/node/node_modules/urlpattern-polyfill/dist/index.d.ts +9 -0
- package/node/node_modules/urlpattern-polyfill/dist/types.d.ts +49 -0
- package/node/node_modules/urlpattern-polyfill/dist/urlpattern.cjs +1 -0
- package/node/node_modules/urlpattern-polyfill/dist/urlpattern.js +1 -0
- package/node/node_modules/urlpattern-polyfill/index.cjs +7 -0
- package/node/node_modules/urlpattern-polyfill/index.js +7 -0
- package/node/node_modules/urlpattern-polyfill/package.json +149 -0
- package/{source → node}/postgres.d.ts +14 -1
- package/node/postgres.d.ts.map +1 -0
- package/{source → node}/postgres.js +17 -5
- package/{source → node}/postgres.ts +22 -9
- package/package.json +1 -1
- package/polyfill.d.ts +1 -1
- package/polyfill.d.ts.map +1 -1
- package/polyfill.js +1 -1
- package/polyfill.ts +1 -1
- package/postgres/index.md +18 -0
- package/postgres/mod.d.ts +4 -0
- package/postgres/mod.d.ts.map +1 -0
- package/postgres/mod.js +3 -0
- package/postgres/mod.ts +3 -0
- package/postgres/postgres-migrator.d.ts +75 -0
- package/postgres/postgres-migrator.d.ts.map +1 -0
- package/postgres/postgres-migrator.js +106 -0
- package/postgres/postgres-migrator.ts +141 -0
- package/postgres/postgres-service.d.ts +109 -0
- package/postgres/postgres-service.d.ts.map +1 -0
- package/postgres/postgres-service.js +10 -0
- package/postgres/postgres-service.ts +143 -0
- package/postgres/postgres-store.d.ts +26 -0
- package/postgres/postgres-store.d.ts.map +1 -0
- package/postgres/postgres-store.js +65 -0
- package/postgres/postgres-store.ts +95 -0
- package/testing/{README.md → index.md} +0 -1
- package/config/README.md +0 -11
- package/configuration.d.ts +0 -2
- package/configuration.d.ts.map +0 -1
- package/configuration.js +0 -1
- package/configuration.ts +0 -1
- package/core/README.md +0 -11
- package/core/configuration.d.ts +0 -2
- package/core/configuration.d.ts.map +0 -1
- package/core/configuration.js +0 -1
- package/core/configuration.ts +0 -1
- package/core/postgres.d.ts +0 -13
- package/core/postgres.d.ts.map +0 -1
- package/core/postgres.js +0 -55
- package/core/postgres.ts +0 -84
- package/core.d.ts +0 -2
- package/core.d.ts.map +0 -1
- package/core.js +0 -1
- package/core.ts +0 -1
- package/http/README.md +0 -11
- package/http.d.ts +0 -2
- package/http.d.ts.map +0 -1
- package/http.js +0 -1
- package/http.ts +0 -1
- package/node-router.d.ts +0 -2
- package/node-router.d.ts.map +0 -1
- package/node-router.js +0 -1
- package/node-router.ts +0 -1
- package/postgres.d.ts +0 -2
- package/postgres.d.ts.map +0 -1
- package/postgres.js +0 -1
- package/postgres.ts +0 -1
- package/source/README.md +0 -11
- package/source/configuration.d.ts +0 -12
- package/source/configuration.d.ts.map +0 -1
- package/source/core.d.ts +0 -2
- package/source/core.d.ts.map +0 -1
- package/source/core.js +0 -1
- package/source/core.ts +0 -1
- package/source/http.d.ts +0 -2
- package/source/http.d.ts.map +0 -1
- package/source/http.js +0 -1
- package/source/http.ts +0 -1
- package/source/mod.d.ts +0 -7
- package/source/mod.d.ts.map +0 -1
- package/source/node-router.d.ts +0 -52
- package/source/node-router.d.ts.map +0 -1
- package/source/postgres.d.ts.map +0 -1
- package/source/testing.d.ts +0 -2
- package/source/testing.d.ts.map +0 -1
- package/source/testing.js +0 -1
- package/source/testing.ts +0 -1
- package/terminator.d.ts +0 -2
- package/terminator.d.ts.map +0 -1
- package/terminator.js +0 -1
- package/terminator.ts +0 -1
- package/testing.d.ts +0 -2
- package/testing.d.ts.map +0 -1
- package/testing.js +0 -1
- package/testing.ts +0 -1
- /package/{source → node}/express-router.d.ts +0 -0
- /package/{source → node}/express-router.d.ts.map +0 -0
- /package/{source → node}/express-router.js +0 -0
- /package/{source → node}/express-router.ts +0 -0
- /package/{source → node}/koa-router.d.ts +0 -0
- /package/{source → node}/koa-router.d.ts.map +0 -0
- /package/{source → node}/koa-router.js +0 -0
- /package/{source → node}/koa-router.ts +0 -0
- /package/{source → node}/package-lock.json +0 -0
- /package/{source → node}/package.json +0 -0
- /package/{source → node}/polyfill.d.ts +0 -0
- /package/{source → node}/polyfill.d.ts.map +0 -0
- /package/{source → node}/polyfill.js +0 -0
- /package/{source → node}/polyfill.ts +0 -0
- /package/{source → node}/terminator.d.ts +0 -0
- /package/{source → node}/terminator.d.ts.map +0 -0
- /package/{source → node}/terminator.js +0 -0
- /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
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
##
|
|
40
|
+
## Terms
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
- Minimal
|
|
70
|
-
- Holistic —
|
|
71
|
-
- No magic — it's confusing when you don't know
|
|
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;
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,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;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACpC,+EAA+E;IAC/E,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAExD,yEAAyE;IACzE,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAExD,oFAAoF;IACpF,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAEpD,wEAAwE;IACxE,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhD,8CAA8C;IAC9C,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,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;;;;;;;;;OASG;IACH,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;;;;;;;;;;OAUG;IACH,KAAK,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC;IAW5D;;;;;;;;;;;;;;;;OAgBG;IACH,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;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;IAkB5D;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;IAgB5D;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC;IAgB/D;;;;;;;;;;OAUG;IACH,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC;IAyB5D;;;;;;;;;;;;;;;;OAgBG;IACG,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBlE;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,OAAO;IA6BhD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,wBAAwB;IAM/D;;;;;;;;;;OAUG;IACH,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;CAGpC"}
|
package/config/configuration.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
import { Structure } from "./structure.js";
|
|
2
|
-
import { formatMarkdownTable, PromiseList } from "../core/mod.js";
|
|
3
|
-
import {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
337
|
+
return struct.getFullSchema();
|
|
181
338
|
}
|
|
182
339
|
}
|