gruber 0.2.0 → 0.3.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 (69) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +96 -10
  3. package/{types/core → core}/configuration.d.ts +15 -13
  4. package/core/configuration.js +35 -37
  5. package/core/configuration.test.d.ts +1 -0
  6. package/core/configuration.test.js +32 -42
  7. package/{types/core → core}/fetch-router.d.ts +0 -1
  8. package/core/fetch-router.test.d.ts +1 -0
  9. package/{types/core → core}/http.d.ts +0 -1
  10. package/core/http.test.d.ts +1 -0
  11. package/{types/core → core}/migrator.d.ts +0 -1
  12. package/core/migrator.test.d.ts +1 -0
  13. package/{types/core → core}/mod.d.ts +0 -1
  14. package/{types/core → core}/postgres.d.ts +0 -1
  15. package/core/structures.d.ts +78 -0
  16. package/core/structures.js +202 -0
  17. package/core/structures.test.d.ts +1 -0
  18. package/core/structures.test.js +349 -0
  19. package/core/test-deps.d.ts +1 -0
  20. package/core/test-deps.js +1 -1
  21. package/{types/core → core}/types.d.ts +0 -1
  22. package/{types/core → core}/utilities.d.ts +0 -1
  23. package/core/utilities.test.d.ts +1 -0
  24. package/package.json +4 -5
  25. package/{types/source → source}/configuration.d.ts +4 -9
  26. package/source/configuration.js +0 -2
  27. package/source/core.d.ts +1 -0
  28. package/{types/source → source}/express-router.d.ts +2 -3
  29. package/source/express-router.js +1 -1
  30. package/{types/source → source}/koa-router.d.ts +0 -1
  31. package/{types/source → source}/mod.d.ts +0 -3
  32. package/source/mod.js +2 -2
  33. package/{types/source → source}/node-router.d.ts +8 -8
  34. package/source/node-router.js +1 -1
  35. package/source/package-lock.json +3 -9
  36. package/source/package.json +0 -2
  37. package/source/polyfill.d.ts +1 -0
  38. package/{types/source → source}/postgres.d.ts +0 -1
  39. package/tsconfig.json +0 -2
  40. package/types/core/configuration.d.ts.map +0 -1
  41. package/types/core/configuration.test.d.ts +0 -2
  42. package/types/core/configuration.test.d.ts.map +0 -1
  43. package/types/core/fetch-router.d.ts.map +0 -1
  44. package/types/core/fetch-router.test.d.ts +0 -2
  45. package/types/core/fetch-router.test.d.ts.map +0 -1
  46. package/types/core/http.d.ts.map +0 -1
  47. package/types/core/http.test.d.ts +0 -2
  48. package/types/core/http.test.d.ts.map +0 -1
  49. package/types/core/migrator.d.ts.map +0 -1
  50. package/types/core/migrator.test.d.ts +0 -2
  51. package/types/core/migrator.test.d.ts.map +0 -1
  52. package/types/core/mod.d.ts.map +0 -1
  53. package/types/core/postgres.d.ts.map +0 -1
  54. package/types/core/test-deps.d.ts +0 -2
  55. package/types/core/test-deps.d.ts.map +0 -1
  56. package/types/core/types.d.ts.map +0 -1
  57. package/types/core/utilities.d.ts.map +0 -1
  58. package/types/core/utilities.test.d.ts +0 -2
  59. package/types/core/utilities.test.d.ts.map +0 -1
  60. package/types/source/configuration.d.ts.map +0 -1
  61. package/types/source/core.d.ts +0 -2
  62. package/types/source/core.d.ts.map +0 -1
  63. package/types/source/express-router.d.ts.map +0 -1
  64. package/types/source/koa-router.d.ts.map +0 -1
  65. package/types/source/mod.d.ts.map +0 -1
  66. package/types/source/node-router.d.ts.map +0 -1
  67. package/types/source/polyfill.d.ts +0 -2
  68. package/types/source/polyfill.d.ts.map +0 -1
  69. package/types/source/postgres.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  This file documents notable changes to the project
4
4
 
5
+ ## 0.3.0
6
+
7
+ **new**
8
+
9
+ - Removed use of superstruct in favour of new `structures.js` implementation
10
+ - Added `getJSONSchema` method to `Configuration`
11
+
12
+ **fixed**
13
+
14
+ - Node.js types should work now
15
+ - Node.js types includes a url-pattern polyfil
16
+
5
17
  ## 0.2.0
6
18
 
7
19
  **new**
package/README.md CHANGED
@@ -235,11 +235,11 @@ Building on the [HTTP server](#http-server) above, we'll setup configuration. St
235
235
  **config.js**
236
236
 
237
237
  ```js
238
- import superstruct from "superstruct";
238
+ import fs from "node:fs";
239
239
  import { getNodeConfiguration } from "gruber";
240
240
 
241
241
  const pkg = JSON.parse(fs.readFileSync("./package.json", "utf8"));
242
- const config = getNodeConfiguration({ superstruct });
242
+ const config = getNodeConfiguration();
243
243
 
244
244
  export function getSpecification() {
245
245
  return config.object({
@@ -286,6 +286,11 @@ export const appConfig = await loadConfiguration(
286
286
  export function getConfigurationUsage() {
287
287
  return config.getUsage(getSpecification());
288
288
  }
289
+
290
+ // Export a method to generate a JSON Schema for the configuration
291
+ export function getConfigurationSchema() {
292
+ return config.getJSONSchema(getSpecification());
293
+ }
289
294
  ```
290
295
 
291
296
  ### Usage info
@@ -327,11 +332,11 @@ You can provide a configuration file like **config.json** to load through the co
327
332
  "selfUrl": "http://localhost:3000",
328
333
  "meta": {
329
334
  "name": "gruber-app",
330
- "version": "1.2.3",
335
+ "version": "1.2.3"
331
336
  },
332
337
  "database": {
333
- "url": "postgres://user:secret@localhost:5432/database",
334
- },
338
+ "url": "postgres://user:secret@localhost:5432/database"
339
+ }
335
340
  }
336
341
  ```
337
342
 
@@ -672,7 +677,6 @@ To see how it works, look at the [Node](./node/source/configuration.js) and [Den
672
677
  You can use the static `getOptions` method both subclasses provide and override the parts you want.
673
678
  These are the options:
674
679
 
675
- - `superstruct` — Configuration is based on [superstruct](https://docs.superstructjs.org/), you can pass your own instance if you like.
676
680
  - `readTextFile(url)` — How to load a text file from the file system
677
681
  - `getEnvironmentVariable(key)` — Return a matching environment "variable" for a key
678
682
  - `getCommandArgument(key)` — Get the corresponding "flag" from a CLI argument
@@ -684,10 +688,9 @@ For example, to override in Node:
684
688
  ```js
685
689
  import { Configuration, getNodeConfigOptions } from "gruber";
686
690
  import Yaml from "yaml";
687
- import superstruct from "superstruct";
688
691
 
689
692
  const config = new Configuration({
690
- ...getNodeConfigOptions({ superstruct }),
693
+ ...getNodeConfigOptions(),
691
694
  getEnvionmentVariable: () => undefined,
692
695
  stringify: (v) => Yaml.stringify(v),
693
696
  parse: (v) => Yaml.parse(v),
@@ -870,7 +873,7 @@ You can use it and override parts of it to customise how the postgres migrator w
870
873
 
871
874
  ### Utilities
872
875
 
873
- ### loader
876
+ #### loader
874
877
 
875
878
  `loader` let's you memoize the result of a function to create a singleton from it.
876
879
  It works synchronously or with promises.
@@ -914,6 +917,89 @@ This will generate the table:
914
917
  | Tyler Rockwell | ~ |
915
918
  ```
916
919
 
920
+ #### Structure
921
+
922
+ This is an internal primative for validating objects, strings, numbers and URLs for use in [Configuration](#configuration).
923
+ It is based on a very specific use of [superstruct](https://github.com/ianstormtaylor/superstruct) which it made sense to internalise to make the code base more portable.
924
+ A `Structure` is a type that validates a value is correct by throwing an error if validation fails, i.e. the wrong type is passed.
925
+ Every struct has an intrinsic `fallback` so that if no value (`undefined`) is passed, that is used instead.
926
+
927
+ ```js
928
+ import { Structure } from "gruber/structures.js";
929
+
930
+ // A string primative, or use "Geoff Testington" if no value is passed.
931
+ const name = Structure.string("Geoff Testington");
932
+
933
+ // A URL instance or a string that contains a valid URL, always converting to a URL
934
+ const website = Structure.url("https://example.com");
935
+
936
+ // A number primative, falling back to 42
937
+ const age = Structure.number(42);
938
+
939
+ // An object with all of the fields above and nothing else
940
+ // defaulting to create { name: "Geoff..", age: 42, website: "https..." } with the same fallback values
941
+ const person = Structure.object({ name, age, website });
942
+
943
+ // Process the Structure and get a value out. The returned value is strongly typed!
944
+ // This will throw if the value passed does not match the schema.
945
+ const value = person.process(/* ... */);
946
+ ```
947
+
948
+ Those static Structure methods return a `Structure` instance. You can also create your own types with the constructor. This example shows how to do that, and also starts to unveil how the internals work a bit with [StructError](#structerror).
949
+
950
+ ```js
951
+ import { Structure, StructError } from "gruber/structures.js";
952
+
953
+ // Create a new boolean structure (this should probably be included as Structure.boolean tbh)
954
+ const boolean = new Structure(
955
+ { type: "boolean", default: false },
956
+ (input, context) => {
957
+ if (input === undefined) return false;
958
+ if (typeof input !== "boolean") {
959
+ throw new StructError("Expected a boolean", context?.path);
960
+ }
961
+ return input;
962
+ },
963
+ );
964
+ ```
965
+
966
+ To create a custom Structure, you give it a [JSON schema](https://json-schema.org/) and a "process" function.
967
+ The function is called to validate a value against the structure. It should return the processed value or throw a `StructError`.
968
+
969
+ The `context` object might not be set and this means the struct is at the root level. If it is nested in an `object` then the context contains the path that the struct is located at, all the way from the root object. That path is expressed as an array of strings. That path is used to generate friendlier error messages to explain which nested field failed.
970
+
971
+ With a Structure, you can generate a JSON Schema:
972
+
973
+ ```js
974
+ import { Structure } from "gruber/structures.js";
975
+
976
+ const person = Structure.object({
977
+ name: Structure.string("Geoff Testington"),
978
+ age: Structure.number(42),
979
+ website: Structure.url("https://example.com"),
980
+ });
981
+
982
+ console.log(JSON.stringify(person.getSchema(), null, 2));
983
+ ```
984
+
985
+ This is a bit WIP, but you could use this to generate a JSON schema to lint configurations in your IDE.
986
+
987
+ #### StructError
988
+
989
+ This Error subclass contains extra information about why parsing a `Structure` failed.
990
+
991
+ - The `message` field is a description of what went wrong, in the context of the structure.
992
+ - An extra `path` field exists to describe the path from the root object to get to this failed structure
993
+ - `children` is also available to let a structure have multiple child errors, i.e. for an object to have failures for each of the fields that have failed.
994
+
995
+ On the error, there are also methods to help use it:
996
+
997
+ - `toFriendlyString` goes through all nested failures and outputs a single message to describe everything that went wrong.
998
+ - `getOneLiner` converts the error to a succint one-line error message, concatentating the path and message
999
+ - `[Symbol.iterator]` is also available if you want to loop through all children nodes, only those that do not have children themselves.
1000
+
1001
+ There is also the static method `StructError.chain(error, context)` which is useful for catching errors and applying a context to them (if they are not already a StructError).
1002
+
917
1003
  ## Node.js library
918
1004
 
919
1005
  There are some specific helpers to help use Gruber in Node.js apps.
@@ -969,7 +1055,7 @@ For older version of Node.js that don't support the latest web-standards,
969
1055
  there is a polyfil import you can use to add support for them to your runtime.
970
1056
 
971
1057
  ```js
972
- import "gruber/polyfil";
1058
+ import "gruber/polyfill.js";
973
1059
  ```
974
1060
 
975
1061
  This currently polyfils these APIs:
@@ -1,33 +1,33 @@
1
- /** @typedef {Record<string, import("superstruct").Struct<any, any>>} ObjectSchema */
2
1
  export class Configuration {
3
2
  static spec: symbol;
4
3
  /** @param {ConfigurationOptions} options */
5
4
  constructor(options: ConfigurationOptions);
6
- options: ConfigurationOptions;
5
+ /** @type {ConfigurationOptions} */ options: ConfigurationOptions;
7
6
  /**
8
- * @template {ObjectSchema} T
7
+ * @template {Record<string, Structure<any>>} T
9
8
  * @param {T} spec
9
+ * @returns {Structure<{ [K in keyof T]: import("./structures.js").Infer<T[K]> }>}
10
10
  */
11
- object<T extends ObjectSchema>(spec: T): any;
11
+ object<T extends Record<string, Structure<any>>>(spec: T): Structure<{ [K in keyof T]: import("./structures.js").Infer<T[K]>; }>;
12
12
  /**
13
13
  * @template {SpecOptions} Spec @param {Spec} spec
14
- * @returns {import("superstruct").Struct<string, null>}
14
+ * @returns {Structure<string>}
15
15
  */
16
- string<Spec extends SpecOptions>(spec?: Spec): any;
16
+ string<Spec extends SpecOptions>(spec?: Spec): Structure<string>;
17
17
  /**
18
18
  * @template {SpecOptions} Spec @param {Spec} spec
19
- * @returns {import("superstruct").Struct<URL, null>}
19
+ * @returns {Structure<URL>}
20
20
  */
21
- url<Spec_1 extends SpecOptions>(spec: Spec_1): any;
21
+ url<Spec_1 extends SpecOptions>(spec: Spec_1): Structure<URL>;
22
22
  /** @param {SpecOptions} spec */
23
23
  _getValue(spec: SpecOptions): string;
24
24
  /**
25
25
  * @template T
26
26
  * @param {URL} url
27
- * @param {import("superstruct").Struct<T>} spec
27
+ * @param {Structure<T>} spec
28
28
  * @returns {Promise<T>}
29
29
  */
30
- load<T_1>(url: URL, spec: any): Promise<T_1>;
30
+ load<T_1>(url: URL, spec: Structure<T_1>): Promise<T_1>;
31
31
  /** @template T @param {T} config */
32
32
  getUsage<T_2>(spec: any): string;
33
33
  /**
@@ -39,6 +39,10 @@ export class Configuration {
39
39
  config: any;
40
40
  fields: [string, string];
41
41
  };
42
+ /** @param {Structure<any>} spec */
43
+ getJSONSchema(spec: Structure<any>): {
44
+ $schema: string;
45
+ };
42
46
  }
43
47
  export type SpecOptions = {
44
48
  variable?: string;
@@ -46,12 +50,10 @@ export type SpecOptions = {
46
50
  fallback: string;
47
51
  };
48
52
  export type ConfigurationOptions = {
49
- superstruct: typeof import("superstruct");
50
53
  readTextFile: (url: URL) => Promise<string | null>;
51
54
  getEnvironmentVariable: (key: string) => (string | undefined);
52
55
  getCommandArgument: (key: string) => (string | undefined);
53
56
  stringify: (value: any) => (string | Promise<string>);
54
57
  parse: (value: string) => (any);
55
58
  };
56
- export type ObjectSchema = Record<string, import("superstruct").Struct<any, any>>;
57
- //# sourceMappingURL=configuration.d.ts.map
59
+ import { Structure } from "./structures.js";
@@ -1,4 +1,9 @@
1
1
  import { formatMarkdownTable } from "./utilities.js";
2
+ import { Structure, StructError } from "./structures.js";
3
+
4
+ // NOTE: it would be nice to reverse the object/string/url methods around so they return the "spec" value, then the "struct" is stored under a string. This could mean the underlying architecture could change in the future. I'm not sure if that is possible with the structure nesting in play.
5
+
6
+ // NOTE: the schema generation will include whatever value is passed to the structure, in the context of configuration it will be whatever is configured and may be something secret
2
7
 
3
8
  /**
4
9
  * @typedef {object} SpecOptions
@@ -9,7 +14,6 @@ import { formatMarkdownTable } from "./utilities.js";
9
14
 
10
15
  /**
11
16
  * @typedef {object} ConfigurationOptions
12
- * @property {import("superstruct")} superstruct
13
17
  * @property {(url: URL) => Promise<string | null>} readTextFile
14
18
  * @property {(key: string) => (string | undefined)} getEnvironmentVariable
15
19
  * @property {(key: string) => (string | undefined)} getCommandArgument
@@ -18,7 +22,6 @@ import { formatMarkdownTable } from "./utilities.js";
18
22
  */
19
23
 
20
24
  const _requiredOptions = [
21
- "superstruct",
22
25
  "readTextFile",
23
26
  "getEnvironmentVariable",
24
27
  "getCommandArgument",
@@ -26,12 +29,10 @@ const _requiredOptions = [
26
29
  "parse",
27
30
  ];
28
31
 
29
- /** @typedef {Record<string, import("superstruct").Struct<any, any>>} ObjectSchema */
30
-
31
32
  export class Configuration {
32
33
  static spec = Symbol("Configuration.spec");
33
34
 
34
- options /** @type {ConfigurationOptions} */;
35
+ /** @type {ConfigurationOptions} */ options;
35
36
 
36
37
  /** @param {ConfigurationOptions} options */
37
38
  constructor(options) {
@@ -42,52 +43,40 @@ export class Configuration {
42
43
  }
43
44
 
44
45
  /**
45
- * @template {ObjectSchema} T
46
+ * @template {Record<string, Structure<any>>} T
46
47
  * @param {T} spec
48
+ * @returns {Structure<{ [K in keyof T]: import("./structures.js").Infer<T[K]> }>}
47
49
  */
48
50
  object(spec) {
49
- const struct = this.options.superstruct.defaulted(
50
- this.options.superstruct.object(spec),
51
- {},
52
- )
53
- struct[Configuration.spec]= { type: "object", value: spec }
54
- return struct
51
+ const struct = Structure.object(spec);
52
+ struct[Configuration.spec] = { type: "object", value: spec };
53
+ return struct;
55
54
  }
56
55
 
57
56
  /**
58
57
  * @template {SpecOptions} Spec @param {Spec} spec
59
- * @returns {import("superstruct").Struct<string, null>}
58
+ * @returns {Structure<string>}
60
59
  */
61
60
  string(spec = {}) {
62
61
  if (typeof spec.fallback !== "string") {
63
62
  throw new TypeError("spec.fallback must be a string: " + spec.fallback);
64
63
  }
65
- const struct = this.options.superstruct.defaulted(
66
- this.options.superstruct.string(),
67
- this._getValue(spec),
68
- )
69
- struct[Configuration.spec] = { type: "string", value: spec }
70
- return struct
64
+ const struct = Structure.string(this._getValue(spec));
65
+ struct[Configuration.spec] = { type: "string", value: spec };
66
+ return struct;
71
67
  }
72
68
 
73
69
  /**
74
70
  * @template {SpecOptions} Spec @param {Spec} spec
75
- * @returns {import("superstruct").Struct<URL, null>}
71
+ * @returns {Structure<URL>}
76
72
  */
77
73
  url(spec) {
78
74
  if (typeof spec.fallback !== "string") {
79
75
  throw new TypeError("spec.fallback must be a string");
80
76
  }
81
- const struct = this.options.superstruct.defaulted(
82
- this.options.superstruct.coerce(
83
- this.options.superstruct.instance(URL),
84
- this.options.superstruct.string(),
85
- (value) => new URL(value),
86
- ),
87
- this._getValue(spec),
88
- )
89
- struct[Configuration.spec]= { type: "url", value: spec }
90
- return struct
77
+ const struct = Structure.url(this._getValue(spec));
78
+ struct[Configuration.spec] = { type: "url", value: spec };
79
+ return struct;
91
80
  }
92
81
 
93
82
  /** @param {SpecOptions} spec */
@@ -106,7 +95,7 @@ export class Configuration {
106
95
  /**
107
96
  * @template T
108
97
  * @param {URL} url
109
- * @param {import("superstruct").Struct<T>} spec
98
+ * @param {Structure<T>} spec
110
99
  * @returns {Promise<T>}
111
100
  */
112
101
  async load(url, spec) {
@@ -114,15 +103,19 @@ export class Configuration {
114
103
 
115
104
  // Catch missing files and create a default configuration
116
105
  if (!file) {
117
- return this.options.superstruct.create({}, spec);
106
+ return spec.process({});
118
107
  }
119
108
 
120
109
  // Fail outside the try-catch to surface structure errors
121
- return this.options.superstruct.create(
122
- await this.options.parse(file),
123
- spec,
124
- "Configuration failed to parse",
125
- );
110
+ try {
111
+ return spec.process(await this.options.parse(file));
112
+ } catch (error) {
113
+ console.error("Configuration failed to parse");
114
+ if (error instanceof StructError) {
115
+ error.message = error.toFriendlyString();
116
+ }
117
+ throw error;
118
+ }
126
119
  }
127
120
 
128
121
  /** @template T @param {T} config */
@@ -184,4 +177,9 @@ export class Configuration {
184
177
  }
185
178
  throw new TypeError("Invalid [Configuration.spec].type '" + type + "'");
186
179
  }
180
+
181
+ /** @param {Structure<any>} spec */
182
+ getJSONSchema(spec) {
183
+ return spec.getSchema();
184
+ }
187
185
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,8 @@
1
1
  import { Configuration } from "./configuration.js";
2
- import { assertEquals, assertThrows } from "./test-deps.js";
3
- import { superstruct } from "./test-deps.js";
2
+ import { assertEquals, assertThrows, describe, it } from "./test-deps.js";
4
3
 
5
4
  /** @type {import("./configuration.js").ConfigurationOptions} */
6
5
  const bareOptions = {
7
- superstruct,
8
6
  readTextFile() {},
9
7
  getEnvironmentVariable(_key) {},
10
8
  getCommandArgument(_key) {},
@@ -12,11 +10,10 @@ const bareOptions = {
12
10
  parse(_value) {},
13
11
  };
14
12
 
15
- Deno.test("Configuration", async ({ step }) => {
16
- await step("constructor", async ({ step }) => {
17
- await step("validates options", () => {
13
+ describe("Configuration", () => {
14
+ describe("constructor", () => {
15
+ it("validates options", () => {
18
16
  new Configuration({
19
- superstruct,
20
17
  readTextFile() {},
21
18
  getEnvironmentVariable(_key) {},
22
19
  getCommandArgument(_key) {},
@@ -26,30 +23,28 @@ Deno.test("Configuration", async ({ step }) => {
26
23
  });
27
24
  });
28
25
 
29
- await step("object", async ({ step }) => {
26
+ describe("object", () => {
30
27
  const config = new Configuration(bareOptions);
31
28
 
32
- await step("stores spec", () => {
29
+ it("stores the spec", () => {
33
30
  const result = config.object({});
34
31
  assertEquals(result[Configuration.spec].type, "object");
35
32
  assertEquals(result[Configuration.spec].value, {});
36
33
  });
37
34
  });
38
35
 
39
- await step("string", async ({ step }) => {
36
+ describe("string", () => {
40
37
  const config = new Configuration(bareOptions);
41
38
 
42
- await step("requires a fallback", () => {
39
+ it("requires a fallback", () => {
43
40
  assertThrows(() => config.string({}), TypeError);
44
41
  });
45
-
46
- await step("uses the fallback", () => {
42
+ it("uses the fallback", () => {
47
43
  const struct = config.string({ fallback: "Geoff Testington" });
48
- const result = superstruct.create(undefined, struct);
44
+ const result = struct.process(undefined);
49
45
  assertEquals(result, "Geoff Testington");
50
46
  });
51
-
52
- await step("stores spec", () => {
47
+ it("stores the spec", () => {
53
48
  const result = config.string({
54
49
  variable: "SOME_VAR",
55
50
  fallback: "value",
@@ -62,26 +57,23 @@ Deno.test("Configuration", async ({ step }) => {
62
57
  });
63
58
  });
64
59
 
65
- await step("url", async ({ step }) => {
60
+ describe("url", () => {
66
61
  const config = new Configuration(bareOptions);
67
62
 
68
- await step("requires a fallback", () => {
63
+ it("requires a fallback", () => {
69
64
  assertThrows(() => config.url({}), TypeError);
70
65
  });
71
-
72
- await step("converts to URL", () => {
73
- const struct = config.url({ fallback: "" });
74
- const result = superstruct.create("https://example.com", struct);
66
+ it("converts to URL", () => {
67
+ const struct = config.url({ fallback: "https://fallback.example.com" });
68
+ const result = struct.process("https://example.com");
75
69
  assertEquals(result, new URL("https://example.com"));
76
70
  });
77
-
78
- await step("uses the fallback", () => {
79
- const struct = config.url({ fallback: "https://example.com" });
80
- const result = superstruct.create(undefined, struct);
81
- assertEquals(result, new URL("https://example.com"));
71
+ it("uses the fallback", () => {
72
+ const struct = config.url({ fallback: "https://fallback.example.com" });
73
+ const result = struct.process(undefined);
74
+ assertEquals(result, new URL("https://fallback.example.com"));
82
75
  });
83
-
84
- await step("stores spec", () => {
76
+ it("stores the spec", () => {
85
77
  const result = config.url({
86
78
  variable: "SOME_VAR",
87
79
  fallback: "https://example.com",
@@ -94,8 +86,8 @@ Deno.test("Configuration", async ({ step }) => {
94
86
  });
95
87
  });
96
88
 
97
- await step("_getValue", async ({ step }) => {
98
- await step("uses arguments", () => {
89
+ describe("_getValue", () => {
90
+ it("uses arguments", () => {
99
91
  const args = { "--option": "value-from-arg" };
100
92
  const config = new Configuration({
101
93
  ...bareOptions,
@@ -106,8 +98,7 @@ Deno.test("Configuration", async ({ step }) => {
106
98
  });
107
99
  assertEquals(result, "value-from-arg");
108
100
  });
109
-
110
- await step("uses environment variables", () => {
101
+ it("uses environment variables", () => {
111
102
  const env = { MY_VAR: "value-from-env" };
112
103
  const config = new Configuration({
113
104
  ...bareOptions,
@@ -118,15 +109,14 @@ Deno.test("Configuration", async ({ step }) => {
118
109
  });
119
110
  assertEquals(result, "value-from-env");
120
111
  });
121
-
122
- await step("uses the fallback", () => {
112
+ it("uses the fallback", () => {
123
113
  const config = new Configuration(bareOptions);
124
114
  const result = config._getValue({ fallback: "value-from-fallback" });
125
115
  assertEquals(result, "value-from-fallback");
126
116
  });
127
117
  });
128
118
 
129
- await step("load", async ({ step }) => {
119
+ describe("load", () => {
130
120
  const files = {
131
121
  "config.json":
132
122
  '{"env":"production","meta":{"version":"1.2.3"},"selfUrl":"https://example.com"}',
@@ -146,7 +136,7 @@ Deno.test("Configuration", async ({ step }) => {
146
136
  selfUrl: config.url({ fallback: "http://localhost" }),
147
137
  });
148
138
 
149
- await step("loads config", async () => {
139
+ it("loads config", async () => {
150
140
  const result = await config.load("config.json", spec);
151
141
  assertEquals(result, {
152
142
  env: "production",
@@ -155,7 +145,7 @@ Deno.test("Configuration", async ({ step }) => {
155
145
  });
156
146
  });
157
147
 
158
- await step("uses the fallback", async () => {
148
+ it("uses the fallback", async () => {
159
149
  const result = await config.load("missing-config.json", spec);
160
150
  assertEquals(result, {
161
151
  env: "development",
@@ -165,8 +155,8 @@ Deno.test("Configuration", async ({ step }) => {
165
155
  });
166
156
  });
167
157
 
168
- await step("describeSpecification", async ({ step }) => {
169
- await step("processes strings", () => {
158
+ describe("describeSpecification", () => {
159
+ it("processes strings", () => {
170
160
  const config = new Configuration(bareOptions);
171
161
  const result = config.describeSpecification(
172
162
  config.string({
@@ -188,7 +178,7 @@ Deno.test("Configuration", async ({ step }) => {
188
178
  ]);
189
179
  });
190
180
 
191
- await step("processes urls", () => {
181
+ it("processes urls", () => {
192
182
  const config = new Configuration(bareOptions);
193
183
  const result = config.describeSpecification(
194
184
  config.url({
@@ -210,7 +200,7 @@ Deno.test("Configuration", async ({ step }) => {
210
200
  ]);
211
201
  });
212
202
 
213
- await step("processes objects", () => {
203
+ it("processes objects", () => {
214
204
  const config = new Configuration(bareOptions);
215
205
  const result = config.describeSpecification(
216
206
  config.object({
@@ -49,4 +49,3 @@ export type FetchRouterOptions = {
49
49
  routes?: import("./types.ts").RouteDefinition<any>[];
50
50
  errorHandler?: RouteErrorHandler;
51
51
  };
52
- //# sourceMappingURL=fetch-router.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -52,4 +52,3 @@ export type RouteContext<T> = import("./types.ts").RouteContext<T>;
52
52
  export type RouteHandler<T> = import("./types.ts").RouteHandler<T>;
53
53
  export type RouteOptions<T extends string> = import("./types.ts").RouteOptions<T>;
54
54
  export type RouteDefinition<T = any> = import("./types.ts").RouteDefinition<T>;
55
- //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -53,4 +53,3 @@ export type MigratorOptions<T> = {
53
53
  getRecords: () => Promise<MigrationRecord[]>;
54
54
  execute: (def: MigrationDefinition<T>, direction: "up" | "down") => void | Promise<void>;
55
55
  };
56
- //# sourceMappingURL=migrator.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -4,4 +4,3 @@ export * from "./http.js";
4
4
  export * from "./migrator.js";
5
5
  export * from "./postgres.js";
6
6
  export * from "./utilities.js";
7
- //# sourceMappingURL=mod.d.ts.map
@@ -40,4 +40,3 @@ export type MigrationDefinition = import("./migrator.js").MigrationDefinition;
40
40
  export type PostgresMigratorOptions = {
41
41
  sql: Sql;
42
42
  };
43
- //# sourceMappingURL=postgres.d.ts.map