From 8428b4143aeb4b5c935862a309d00b4bc48ff669 Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Sun, 24 Mar 2024 17:12:08 +1000 Subject: [PATCH] fixup! WIP --- ENVIRONMENT.md | 34 ++-- src/declaration/big-integer.ts | 24 +-- src/declaration/binary.ts | 23 ++- src/declaration/boolean.ts | 20 ++- src/declaration/duration.ts | 15 +- src/declaration/enumeration.ts | 22 +-- src/declaration/integer.ts | 27 ++-- src/declaration/kubernetes-address.ts | 20 +-- src/declaration/network-port-number.ts | 13 +- src/declaration/number.ts | 29 ++-- src/declaration/string.ts | 11 +- src/declaration/url.ts | 27 ++-- src/error.ts | 9 ++ src/example.ts | 90 +++++++---- src/specification.ts | 13 +- src/variable.ts | 25 +-- test/fixture/example/env.ts | 29 +++- .../specification/big-integer/default.md | 2 +- .../specification/big-integer/optional.md | 2 +- .../specification/big-integer/required.md | 2 +- test/fixture/specification/constraints.md | 150 ------------------ test/fixture/specification/integer/default.md | 4 +- .../fixture/specification/integer/optional.md | 4 +- .../fixture/specification/integer/required.md | 4 +- test/fixture/specification/number/default.md | 2 +- test/fixture/specification/number/optional.md | 2 +- test/fixture/specification/number/required.md | 2 +- test/fixture/specification/sensitive.md | 8 +- test/fixture/summary/non-canonical.ansi | 2 +- test/suite/constraint/length.spec.ts | 4 +- test/suite/constraint/range.spec.ts | 13 +- test/suite/declaration/big-integer.spec.ts | 2 +- test/suite/declaration/integer.spec.ts | 2 +- test/suite/declaration/number.spec.ts | 2 +- test/suite/examples.spec.ts | 95 +++++++++++ test/suite/specification.spec.ts | 25 +++ test/suite/summary.spec.ts | 9 +- 37 files changed, 403 insertions(+), 364 deletions(-) create mode 100644 test/suite/examples.spec.ts diff --git a/ENVIRONMENT.md b/ENVIRONMENT.md index 2233ad5..ae4d4ee 100644 --- a/ENVIRONMENT.md +++ b/ENVIRONMENT.md @@ -84,7 +84,7 @@ export EARTH_ATOM_COUNT=-123456 # negative ``` ```sh -export EARTH_ATOM_COUNT=0x1E240 # hexadecimal +export EARTH_ATOM_COUNT=0x1e240 # hexadecimal ``` ```sh @@ -104,6 +104,16 @@ that takes **ISO 8601 duration** values with these constraints: - Must be >= PT0.1S and <= PT10S +### Example values + +```sh +export GRPC_TIMEOUT=PT0.3S # 300 milliseconds +``` + +```sh +export GRPC_TIMEOUT=PT5S # 5 seconds +``` + ## `LOG_LEVEL` _The minimum log level to record_ @@ -167,6 +177,12 @@ that takes **string** values with these constraints: - Must have a minimum length of 30 +### Example values + +```sh +export READ_DSN='host=localhost dbname=readmodels user=projector' # local database +``` + ## `REDIS_PRIMARY_SERVICE_HOST` _Kubernetes `redis-primary` service host_ @@ -223,7 +239,7 @@ export SAMPLE_RATIO=1.23456e+2 # exponential ``` ```sh -export SAMPLE_RATIO=0x1E240 # hexadecimal +export SAMPLE_RATIO=0x1e240 # hexadecimal ``` ```sh @@ -259,21 +275,13 @@ that takes **integer** values with these constraints: ### Example values ```sh -export WEIGHT=123456 # positive -``` - -```sh -export WEIGHT=1.23456e+5 # exponential -``` - -```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=1 # lowest ``` ```sh -export WEIGHT=0o361100 # octal +export WEIGHT=100 # high ``` ```sh -export WEIGHT=0b11110001001000000 # binary +export WEIGHT=1000 # very high ``` diff --git a/src/declaration/big-integer.ts b/src/declaration/big-integer.ts index 8c2ca2b..a0f885c 100644 --- a/src/declaration/big-integer.ts +++ b/src/declaration/big-integer.ts @@ -11,15 +11,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & @@ -41,7 +40,7 @@ export function bigInteger( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -73,26 +72,29 @@ function createSchema(name: string, options: Options): ScalarSchema { return createScalar("big integer", toString, unmarshal, constraints); } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { - value: "123456", + value: 123456n, description: "positive", }, { - value: "-123456", + value: -123456n, description: "negative", }, { - value: "0x1E240", + value: 123456n, + as: "0x1e240", description: "hexadecimal", }, { - value: "0o361100", + value: 123456n, + as: "0o361100", description: "octal", }, { - value: "0b11110001001000000", + value: 123456n, + as: "0b11110001001000000", description: "binary", }, ]; diff --git a/src/declaration/binary.ts b/src/declaration/binary.ts index 5e93c44..42d87ee 100644 --- a/src/declaration/binary.ts +++ b/src/declaration/binary.ts @@ -11,15 +11,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar } from "../schema.js"; -import { SpecError } from "../variable.js"; const PATTERNS: Partial> = { base64: /^[A-Za-z0-9+/]*={0,2}$/, @@ -53,9 +52,12 @@ export function binary( default: def, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildExamples(encoding, schema), + examples: resolveExamples( + name, + schema, + () => buildExamples(encoding), + examples, + ), }); return { @@ -109,13 +111,10 @@ function createUnmarshal( }; } -function buildExamples( - encoding: BufferEncoding, - schema: ScalarSchema, -): MarshalledExample[] { +function buildExamples(encoding: BufferEncoding): Example[] { return [ { - value: schema.marshal(Buffer.from("conquistador", "utf-8")), + value: Buffer.from("conquistador", "utf-8"), description: `${encoding} encoded string`, }, ]; diff --git a/src/declaration/boolean.ts b/src/declaration/boolean.ts index ab2801f..38a3711 100644 --- a/src/declaration/boolean.ts +++ b/src/declaration/boolean.ts @@ -6,14 +6,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; +import { SpecError } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { EnumSchema, InvalidEnumError, createEnum } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & { @@ -43,9 +43,12 @@ export function boolean( default: def, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildExamples(literals), + examples: resolveExamples( + name, + schema, + () => buildExamples(literals), + examples, + ), }); return { @@ -90,9 +93,10 @@ function findLiteral( throw new MissingLiteralError(name, native); } -function buildExamples(literals: Literals): MarshalledExample[] { +function buildExamples(literals: Literals): Example[] { return Object.entries(literals).map(([literal, native]) => ({ - value: literal, + value: native, + as: literal, description: String(native), })); } diff --git a/src/declaration/duration.ts b/src/declaration/duration.ts index a110bc0..e28044a 100644 --- a/src/declaration/duration.ts +++ b/src/declaration/duration.ts @@ -12,15 +12,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; -import { SpecError } from "../variable.js"; const { Duration } = Temporal; type Duration = Temporal.Duration; @@ -45,7 +44,7 @@ export function duration( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -77,14 +76,14 @@ function createSchema(name: string, options: Options): ScalarSchema { return createScalar("ISO 8601 duration", toString, unmarshal, constraints); } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { - value: Duration.from({ minutes: 1, seconds: 30 }).toString(), + value: Duration.from({ minutes: 1, seconds: 30 }), description: "ISO 8601 duration", }, { - value: Duration.from({ months: 1, days: 15, hours: 12 }).toString(), + value: Duration.from({ months: 1, days: 15, hours: 12 }), description: "ISO 8601 duration", }, ]; diff --git a/src/declaration/enumeration.ts b/src/declaration/enumeration.ts index a16bbb7..5ad7bb0 100644 --- a/src/declaration/enumeration.ts +++ b/src/declaration/enumeration.ts @@ -6,14 +6,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; +import { SpecError } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { EnumSchema, InvalidEnumError, createEnum } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Members = Record>; @@ -41,9 +41,12 @@ export function enumeration>( default: def, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildExamples(members), + examples: resolveExamples( + name, + schema, + () => buildExamples(members), + examples, + ), }); return { @@ -81,9 +84,10 @@ function createSchema(name: string, members: Members): EnumSchema { return createEnum(schemaMembers, marshal, unmarshal, []); } -function buildExamples(members: Members): MarshalledExample[] { - return Object.entries(members).map(([literal, { description }]) => ({ - value: literal, +function buildExamples(members: Members): Example[] { + return Object.entries(members).map(([literal, { value, description }]) => ({ + value, + as: literal, description, })); } diff --git a/src/declaration/integer.ts b/src/declaration/integer.ts index 0c55c71..7d5e8a1 100644 --- a/src/declaration/integer.ts +++ b/src/declaration/integer.ts @@ -13,15 +13,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & @@ -43,7 +42,7 @@ export function integer( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -72,30 +71,34 @@ function createSchema(name: string, options: Options): ScalarSchema { return createScalar("integer", toString, unmarshal, constraints); } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { - value: "123456", + value: 123456, description: "positive", }, { - value: "-123456", + value: -123456, description: "negative", }, { - value: "1.23456e+5", + value: 1.23456e5, + as: "1.23456e5", description: "exponential", }, { - value: "0x1E240", + value: 0x1e240, + as: "0x1e240", description: "hexadecimal", }, { - value: "0o361100", + value: 0o361100, + as: "0o361100", description: "octal", }, { - value: "0b11110001001000000", + value: 0b11110001001000000, + as: "0b11110001001000000", description: "binary", }, ]; diff --git a/src/declaration/kubernetes-address.ts b/src/declaration/kubernetes-address.ts index 8c6842f..c8656a9 100644 --- a/src/declaration/kubernetes-address.ts +++ b/src/declaration/kubernetes-address.ts @@ -9,11 +9,7 @@ import { } from "../declaration.js"; import { registerVariable } from "../environment.js"; import { normalize } from "../error.js"; -import { - marshalExamples, - type Example, - type MarshalledExample, -} from "../example.js"; +import { resolveExamples, type Example } from "../example.js"; import { Maybe, map, resolve } from "../maybe.js"; import { ScalarSchema, @@ -92,13 +88,11 @@ function registerHost( default: hostDef, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildHostExamples(), + examples: resolveExamples(name, schema, buildHostExamples, examples), }); } -function buildHostExamples(): MarshalledExample[] { +function buildHostExamples(): Example[] { return [ { value: "service.example.org", @@ -146,9 +140,7 @@ function registerPort( default: portDef, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildPortExamples(), + examples: resolveExamples(name, schema, buildPortExamples, examples), }); } @@ -167,10 +159,10 @@ function createPortSchema(): ScalarSchema { ]); } -function buildPortExamples(): MarshalledExample[] { +function buildPortExamples(): Example[] { return [ { - value: "12345", + value: 12345, description: "a port number", }, ]; diff --git a/src/declaration/network-port-number.ts b/src/declaration/network-port-number.ts index 81176b7..5e92fd3 100644 --- a/src/declaration/network-port-number.ts +++ b/src/declaration/network-port-number.ts @@ -13,15 +13,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & @@ -43,7 +42,7 @@ export function networkPortNumber( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -77,10 +76,10 @@ function createSchema(name: string, options: Options): ScalarSchema { return createScalar("port number", toString, unmarshal, constraints); } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { - value: "12345", + value: 12345, description: "a port number", }, ]; diff --git a/src/declaration/number.ts b/src/declaration/number.ts index f2ba064..328b8a0 100644 --- a/src/declaration/number.ts +++ b/src/declaration/number.ts @@ -11,15 +11,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & @@ -41,7 +40,7 @@ export function number( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -73,34 +72,38 @@ function createSchema(name: string, options: Options): ScalarSchema { return createScalar("number", toString, unmarshal, constraints); } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { - value: "123456", + value: 123456, description: "integer", }, { - value: "123.456", + value: 123.456, description: "positive", }, { - value: "-123.456", + value: -123.456, description: "negative", }, { - value: "1.23456e+2", + value: 1.23456e2, + as: "1.23456e+2", description: "exponential", }, { - value: "0x1E240", + value: 0x1e240, + as: "0x1e240", description: "hexadecimal", }, { - value: "0o361100", + value: 0o361100, + as: "0o361100", description: "octal", }, { - value: "0b11110001001000000", + value: 0b11110001001000000, + as: "0b11110001001000000", description: "binary", }, ]; diff --git a/src/declaration/string.ts b/src/declaration/string.ts index acca2be..4d55eba 100644 --- a/src/declaration/string.ts +++ b/src/declaration/string.ts @@ -10,15 +10,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - marshalExamples, + resolveExamples, type DeclarationExampleOptions, - type MarshalledExample, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { createString } from "../schema.js"; -import { SpecError } from "../variable.js"; export type Options = DeclarationOptions & DeclarationExampleOptions & { @@ -51,7 +50,7 @@ export function string( default: def, isSensitive, schema, - examples: examples ? marshalExamples(schema, examples) : buildExamples(), + examples: resolveExamples(name, schema, buildExamples, examples), }); return { @@ -61,7 +60,7 @@ export function string( }; } -function buildExamples(): MarshalledExample[] { +function buildExamples(): Example[] { return [ { value: "conquistador", diff --git a/src/declaration/url.ts b/src/declaration/url.ts index 1143762..5c069be 100644 --- a/src/declaration/url.ts +++ b/src/declaration/url.ts @@ -8,15 +8,14 @@ import { type ExactOptions, } from "../declaration.js"; import { registerVariable } from "../environment.js"; -import { normalize } from "../error.js"; +import { SpecError, normalize } from "../error.js"; import { - MarshalledExample, - marshalExamples, + resolveExamples, type DeclarationExampleOptions, + type Example, } from "../example.js"; import { resolve } from "../maybe.js"; import { createURL, toString, type URLSchema } from "../schema.js"; -import { SpecError } from "../variable.js"; // as per https://www.rfc-editor.org/rfc/rfc3986#section-3.1 const VALID_PROTOCOL_PATTERN = /^[a-zA-Z][a-zA-Z0-9.+-]*:$/; @@ -48,9 +47,12 @@ export function url( default: def, isSensitive, schema, - examples: examples - ? marshalExamples(schema, examples) - : buildExamples(base, protocols), + examples: resolveExamples( + name, + schema, + () => buildExamples(base, protocols), + examples, + ), }); return { @@ -120,23 +122,24 @@ function createSchema( function buildExamples( base: URL | undefined, protocols: string[] | undefined, -): MarshalledExample[] { - const examples: MarshalledExample[] = +): Example[] { + const examples: Example[] = protocols == null ? [ { - value: `https://host.example.org/path/to/resource`, + value: new URL("https://host.example.org/path/to/resource"), description: "URL (absolute)", }, ] : protocols.map((protocol) => ({ - value: `${protocol}//host.example.org/path/to/resource`, + value: new URL(`${protocol}//host.example.org/path/to/resource`), description: `URL (${protocol})`, })); if (base != null) { examples.push({ - value: `path/to/resource`, + value: new URL("path/to/resource", base), + as: "path/to/resource", description: "URL (relative)", }); } diff --git a/src/error.ts b/src/error.ts index a38358a..fde8035 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,3 +1,12 @@ export function normalize(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)); } + +export class SpecError extends Error { + constructor( + public readonly name: string, + public readonly cause: Error, + ) { + super(`specification for ${name} is invalid: ${cause.message}`); + } +} diff --git a/src/example.ts b/src/example.ts index 4f3d452..a746b5c 100644 --- a/src/example.ts +++ b/src/example.ts @@ -1,4 +1,5 @@ import { applyConstraints } from "./constraint.js"; +import { SpecError, normalize } from "./error.js"; import type { Schema } from "./schema.js"; export type DeclarationExampleOptions = { @@ -11,45 +12,70 @@ export type Example = { readonly description: string; }; -export type MarshalledExample = { - readonly value: string; - readonly description: string; -}; - -export function marshalExamples( +export function resolveExamples( + name: string, schema: Schema, - examples: Example[], -): MarshalledExample[] { - return examples.map(({ value, as, description }) => { - if (typeof as === "string") { - return { - value: as, - description, - }; - } + buildExamples: () => Example[], + examples?: Example[], +): Example[] { + const isSpecified = Boolean(examples); + const resolved = examples ?? buildExamples(); - return { - value: schema.marshal(value), - description, - }; - }); -} + for (const { description, value, as } of resolved) { + try { + applyConstraints(schema.constraints, value); + } catch (error) { + if (isSpecified) { + throw new InvalidValueError(name, description, normalize(error)); + } else { + throw new MustProvideExamplesError(name); + } + } -export function removeInvalidExamples( - schema: Schema, - examples: MarshalledExample[], -): MarshalledExample[] { - const filtered: MarshalledExample[] = []; + if (typeof as !== "string") continue; - for (const example of examples) { try { - applyConstraints(schema.constraints, schema.unmarshal(example.value)); - } catch { - continue; + const native = schema.unmarshal(as); + applyConstraints(schema.constraints, native); + + if (schema.marshal(native) !== schema.marshal(value)) { + throw new Error("value mismatch"); + } + } catch (error) { + throw new InvalidAsError(name, description, as, normalize(error)); } + } - filtered.push(example); + return resolved; +} + +class MustProvideExamplesError extends SpecError { + constructor(name: string) { + super(name, new Error("examples must be provided")); } +} - return filtered; +class InvalidValueError extends SpecError { + constructor(name: string, description: string, cause: Error) { + const quotedDescription = JSON.stringify(description); + + super( + name, + new Error(`example ${quotedDescription}: value ${cause.message}`), + ); + } +} + +class InvalidAsError extends SpecError { + constructor(name: string, description: string, as: string, cause: Error) { + const quotedDescription = JSON.stringify(description); + + super( + name, + new Error( + `example ${quotedDescription}: ` + + `value can't be expressed as ${JSON.stringify(as)}: ${cause.message}`, + ), + ); + } } diff --git a/src/specification.ts b/src/specification.ts index 834b934..37a7c17 100644 --- a/src/specification.ts +++ b/src/specification.ts @@ -171,15 +171,18 @@ function defaultExample(variable: Variable): string { ${body}`; } -function examples({ spec: { name, examples } }: Variable): string { +function examples({ + spec: { name, examples, schema }, +}: Variable): string { if (examples.length < 1) return ""; const blocks = []; - for (const { value: canonical, description } of examples) { - blocks.push( - code("sh", `export ${name}=${quote(canonical)} # ${description}`), - ); + for (const example of examples) { + const { as, description } = example; + const value = as ?? schema.marshal(example.value); + + blocks.push(code("sh", `export ${name}=${quote(value)} # ${description}`)); } return ` diff --git a/src/variable.ts b/src/variable.ts index cfbfb1f..ca7c715 100644 --- a/src/variable.ts +++ b/src/variable.ts @@ -1,7 +1,7 @@ import { applyConstraints } from "./constraint.js"; import { readVariable } from "./environment.js"; -import { normalize } from "./error.js"; -import { removeInvalidExamples, type MarshalledExample } from "./example.js"; +import { SpecError, normalize } from "./error.js"; +import { type Example } from "./example.js"; import { Maybe, definedValue, map, undefinedValue } from "./maybe.js"; import { Schema } from "./schema.js"; import { quote } from "./shell.js"; @@ -12,7 +12,7 @@ export type VariableSpec = { readonly default: Maybe; readonly isSensitive: boolean; readonly schema: Schema; - readonly examples: MarshalledExample[]; + readonly examples: Example[]; }; export type Variable = { @@ -31,7 +31,6 @@ export type Value = { }; export function create(spec: VariableSpec): Variable { - spec = normalizeSpec(spec); const { schema } = spec; const def = defaultValue(); let resolution: Resolution; @@ -143,15 +142,6 @@ export function create(spec: VariableSpec): Variable { } } -export class SpecError extends Error { - constructor( - public readonly name: string, - public readonly cause: Error, - ) { - super(`specification for ${name} is invalid: ${cause.message}`); - } -} - export class ValueError extends Error { constructor( public readonly name: string, @@ -170,12 +160,3 @@ export class NotSetError extends Error { super(`${name} is not set and does not have a default value`); } } - -function normalizeSpec(spec: VariableSpec): VariableSpec { - const { examples, schema } = spec; - - return { - ...spec, - examples: removeInvalidExamples(schema, examples), - }; -} diff --git a/test/fixture/example/env.ts b/test/fixture/example/env.ts index 4616063..926a360 100644 --- a/test/fixture/example/env.ts +++ b/test/fixture/example/env.ts @@ -27,6 +27,16 @@ export const grpcTimeout = duration("GRPC_TIMEOUT", "gRPC request timeout", { default: undefined, min: Temporal.Duration.from({ milliseconds: 100 }), max: Temporal.Duration.from({ seconds: 10 }), + examples: [ + { + value: Temporal.Duration.from({ milliseconds: 300 }), + description: "300 milliseconds", + }, + { + value: Temporal.Duration.from({ seconds: 5 }), + description: "5 seconds", + }, + ], }); export const isDebug = boolean( @@ -63,7 +73,15 @@ export const port = networkPortNumber( export const readDsn = string( "READ_DSN", "database connection string for read-models", - { length: { min: 30 } }, + { + length: { min: 30 }, + examples: [ + { + value: "host=localhost dbname=readmodels user=projector", + description: "local database", + }, + ], + }, ); export const redisPrimary = kubernetesAddress("redis-primary"); @@ -78,4 +96,11 @@ export const sessionKey = binary("SESSION_KEY", "session token signing key", { isSensitive: true, }); -export const weight = integer("WEIGHT", "weighting for this node", { min: 1 }); +export const weight = integer("WEIGHT", "weighting for this node", { + min: 1, + examples: [ + { value: 1, description: "lowest" }, + { value: 100, description: "high" }, + { value: 1000, description: "very high" }, + ], +}); diff --git a/test/fixture/specification/big-integer/default.md b/test/fixture/specification/big-integer/default.md index e8dc014..892dd6f 100644 --- a/test/fixture/specification/big-integer/default.md +++ b/test/fixture/specification/big-integer/default.md @@ -37,7 +37,7 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/big-integer/optional.md b/test/fixture/specification/big-integer/optional.md index f3c4189..a9dd3ec 100644 --- a/test/fixture/specification/big-integer/optional.md +++ b/test/fixture/specification/big-integer/optional.md @@ -31,7 +31,7 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/big-integer/required.md b/test/fixture/specification/big-integer/required.md index f562a41..9af0bdc 100644 --- a/test/fixture/specification/big-integer/required.md +++ b/test/fixture/specification/big-integer/required.md @@ -31,7 +31,7 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/constraints.md b/test/fixture/specification/constraints.md index 8fe4846..8a5aa0a 100644 --- a/test/fixture/specification/constraints.md +++ b/test/fixture/specification/constraints.md @@ -74,12 +74,6 @@ that takes **base64** values with these constraints: - Must have a minimum decoded length of 2 -### Example values - -```sh -export AUSTENITE_BINARY_MIN_LENGTH=Y29ucXVpc3RhZG9y # base64 encoded string -``` - ## `AUSTENITE_CUSTOM` _Custom variable_ @@ -108,12 +102,6 @@ that takes **ISO 8601 duration** values with these constraints: - Must be >= PT1S -### Example values - -```sh -export AUSTENITE_DURATION_MIN=PT1M30S # ISO 8601 duration -``` - ## `AUSTENITE_DURATION_RANGE` _Example duration with range constraint_ @@ -132,12 +120,6 @@ that takes **big integer** values with these constraints: - Must be <= 2 -### Example values - -```sh -export AUSTENITE_INTEGER_BIG_MAX=-123456 # negative -``` - ## `AUSTENITE_INTEGER_BIG_MIN` _Example big integer with minimum constraint_ @@ -147,24 +129,6 @@ that takes **big integer** values with these constraints: - Must be >= 1 -### Example values - -```sh -export AUSTENITE_INTEGER_BIG_MIN=123456 # positive -``` - -```sh -export AUSTENITE_INTEGER_BIG_MIN=0x1E240 # hexadecimal -``` - -```sh -export AUSTENITE_INTEGER_BIG_MIN=0o361100 # octal -``` - -```sh -export AUSTENITE_INTEGER_BIG_MIN=0b11110001001000000 # binary -``` - ## `AUSTENITE_INTEGER_BIG_RANGE` _Example big integer with range constraint_ @@ -183,12 +147,6 @@ that takes **integer** values with these constraints: - Must be <= 2 -### Example values - -```sh -export AUSTENITE_INTEGER_MAX=-123456 # negative -``` - ## `AUSTENITE_INTEGER_MIN` _Example integer with minimum constraint_ @@ -198,28 +156,6 @@ that takes **integer** values with these constraints: - Must be >= 1 -### Example values - -```sh -export AUSTENITE_INTEGER_MIN=123456 # positive -``` - -```sh -export AUSTENITE_INTEGER_MIN=1.23456e+5 # exponential -``` - -```sh -export AUSTENITE_INTEGER_MIN=0x1E240 # hexadecimal -``` - -```sh -export AUSTENITE_INTEGER_MIN=0o361100 # octal -``` - -```sh -export AUSTENITE_INTEGER_MIN=0b11110001001000000 # binary -``` - ## `AUSTENITE_INTEGER_RANGE` _Example integer with range constraint_ @@ -238,12 +174,6 @@ that takes **number** values with these constraints: - Must be <= 3 -### Example values - -```sh -export AUSTENITE_NUMBER_MAX=-123.456 # negative -``` - ## `AUSTENITE_NUMBER_MAX_EXCLUSIVE` _Example number with exclusive maximum constraint_ @@ -253,12 +183,6 @@ that takes **number** values with these constraints: - Must be < 4 -### Example values - -```sh -export AUSTENITE_NUMBER_MAX_EXCLUSIVE=-123.456 # negative -``` - ## `AUSTENITE_NUMBER_MIN` _Example number with inclusive minimum constraint_ @@ -268,32 +192,6 @@ that takes **number** values with these constraints: - Must be >= 1 -### Example values - -```sh -export AUSTENITE_NUMBER_MIN=123456 # integer -``` - -```sh -export AUSTENITE_NUMBER_MIN=123.456 # positive -``` - -```sh -export AUSTENITE_NUMBER_MIN=1.23456e+2 # exponential -``` - -```sh -export AUSTENITE_NUMBER_MIN=0x1E240 # hexadecimal -``` - -```sh -export AUSTENITE_NUMBER_MIN=0o361100 # octal -``` - -```sh -export AUSTENITE_NUMBER_MIN=0b11110001001000000 # binary -``` - ## `AUSTENITE_NUMBER_MIN_EXCLUSIVE` _Example number with exclusive minimum constraint_ @@ -303,32 +201,6 @@ that takes **number** values with these constraints: - Must be > 2 -### Example values - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=123456 # integer -``` - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=123.456 # positive -``` - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=1.23456e+2 # exponential -``` - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=0x1E240 # hexadecimal -``` - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=0o361100 # octal -``` - -```sh -export AUSTENITE_NUMBER_MIN_EXCLUSIVE=0b11110001001000000 # binary -``` - ## `AUSTENITE_NUMBER_RANGE` _Example number with range constraint_ @@ -347,12 +219,6 @@ that takes **port number** values with these constraints: - Must be <= 22222 -### Example values - -```sh -export AUSTENITE_PORT_NUMBER_MAX=12345 # a port number -``` - ## `AUSTENITE_PORT_NUMBER_MIN` _Example port number with minimum constraint_ @@ -362,12 +228,6 @@ that takes **port number** values with these constraints: - Must be >= 11111 -### Example values - -```sh -export AUSTENITE_PORT_NUMBER_MIN=12345 # a port number -``` - ## `AUSTENITE_PORT_NUMBER_RANGE` _Example port number with range constraint_ @@ -412,13 +272,3 @@ The `AUSTENITE_STRING_MIN_LENGTH` variable is a **required** variable that takes **string** values with these constraints: - Must have a minimum length of 2 - -### Example values - -```sh -export AUSTENITE_STRING_MIN_LENGTH=conquistador # any value -``` - -```sh -export AUSTENITE_STRING_MIN_LENGTH='alabaster parakeet' # some values may need escaping -``` diff --git a/test/fixture/specification/integer/default.md b/test/fixture/specification/integer/default.md index c780de2..671df9f 100644 --- a/test/fixture/specification/integer/default.md +++ b/test/fixture/specification/integer/default.md @@ -37,11 +37,11 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=1.23456e+5 # exponential +export WEIGHT=1.23456e5 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/integer/optional.md b/test/fixture/specification/integer/optional.md index daee17c..ae854cd 100644 --- a/test/fixture/specification/integer/optional.md +++ b/test/fixture/specification/integer/optional.md @@ -31,11 +31,11 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=1.23456e+5 # exponential +export WEIGHT=1.23456e5 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/integer/required.md b/test/fixture/specification/integer/required.md index c855a3f..190455b 100644 --- a/test/fixture/specification/integer/required.md +++ b/test/fixture/specification/integer/required.md @@ -31,11 +31,11 @@ export WEIGHT=-123456 # negative ``` ```sh -export WEIGHT=1.23456e+5 # exponential +export WEIGHT=1.23456e5 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/number/default.md b/test/fixture/specification/number/default.md index 43663f7..b4fe083 100644 --- a/test/fixture/specification/number/default.md +++ b/test/fixture/specification/number/default.md @@ -45,7 +45,7 @@ export WEIGHT=1.23456e+2 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/number/optional.md b/test/fixture/specification/number/optional.md index 6520642..eebfd6b 100644 --- a/test/fixture/specification/number/optional.md +++ b/test/fixture/specification/number/optional.md @@ -39,7 +39,7 @@ export WEIGHT=1.23456e+2 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/number/required.md b/test/fixture/specification/number/required.md index d23a782..19aacef 100644 --- a/test/fixture/specification/number/required.md +++ b/test/fixture/specification/number/required.md @@ -39,7 +39,7 @@ export WEIGHT=1.23456e+2 # exponential ``` ```sh -export WEIGHT=0x1E240 # hexadecimal +export WEIGHT=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/specification/sensitive.md b/test/fixture/specification/sensitive.md index e5efb19..4930f92 100644 --- a/test/fixture/specification/sensitive.md +++ b/test/fixture/specification/sensitive.md @@ -149,11 +149,11 @@ export AUSTENITE_INTEGER=-123456 # negative ``` ```sh -export AUSTENITE_INTEGER=1.23456e+5 # exponential +export AUSTENITE_INTEGER=1.23456e5 # exponential ``` ```sh -export AUSTENITE_INTEGER=0x1E240 # hexadecimal +export AUSTENITE_INTEGER=0x1e240 # hexadecimal ``` ```sh @@ -188,7 +188,7 @@ export AUSTENITE_INTEGER_BIG=-123456 # negative ``` ```sh -export AUSTENITE_INTEGER_BIG=0x1E240 # hexadecimal +export AUSTENITE_INTEGER_BIG=0x1e240 # hexadecimal ``` ```sh @@ -231,7 +231,7 @@ export AUSTENITE_NUMBER=1.23456e+2 # exponential ``` ```sh -export AUSTENITE_NUMBER=0x1E240 # hexadecimal +export AUSTENITE_NUMBER=0x1e240 # hexadecimal ``` ```sh diff --git a/test/fixture/summary/non-canonical.ansi b/test/fixture/summary/non-canonical.ansi index 8c76b80..6ebc691 100644 --- a/test/fixture/summary/non-canonical.ansi +++ b/test/fixture/summary/non-canonical.ansi @@ -3,7 +3,7 @@ Environment Variables: AUSTENITE_BINARY example binary ✓ set to QmVlcCBib29wIQ== (specified non-canonically as QmVlcCBib29wIQ) AUSTENITE_DURATION example duration ✓ set to PT3H10M (specified non-canonically as PT3H10M0S) AUSTENITE_INTEGER example integer ✓ set to 123456 (specified non-canonically as 1.23456e5) - AUSTENITE_INTEGER_BIG example big integer ✓ set to 123456 (specified non-canonically as 0x1E240) + AUSTENITE_INTEGER_BIG example big integer ✓ set to 123456 (specified non-canonically as 0x1e240) AUSTENITE_NUMBER example number ✓ set to 123.456 (specified non-canonically as 1.23456e2) AUSTENITE_URL example URL ✓ set to https://host.example.org/ (specified non-canonically as https://host.example.org) ❯ AUSTENITE_XTRIGGER trigger failure ✗ not set diff --git a/test/suite/constraint/length.spec.ts b/test/suite/constraint/length.spec.ts index 7233b5b..eb18b04 100644 --- a/test/suite/constraint/length.spec.ts +++ b/test/suite/constraint/length.spec.ts @@ -19,10 +19,10 @@ type LengthOptions = Options & { }; const createString = (options: LengthOptions) => - string("AUSTENITE_VAR", "", options); + string("AUSTENITE_VAR", "", { examples: [], ...options }); const createBinary = (options: LengthOptions) => - binary("AUSTENITE_VAR", "", options); + binary("AUSTENITE_VAR", "", { examples: [], ...options }); const toUTF8 = (buffer: Buffer) => buffer.toString("utf8"); diff --git a/test/suite/constraint/range.spec.ts b/test/suite/constraint/range.spec.ts index 677bb64..caa34a3 100644 --- a/test/suite/constraint/range.spec.ts +++ b/test/suite/constraint/range.spec.ts @@ -31,19 +31,22 @@ type RangeOptions = Options & RangeConstraintSpec; const createNumber = (options: RangeOptions) => - number("AUSTENITE_VAR", "", options); + number("AUSTENITE_VAR", "", { examples: [], ...options }); const createInteger = (options: RangeOptions) => - integer("AUSTENITE_VAR", "", options); + integer("AUSTENITE_VAR", "", { examples: [], ...options }); const createBigInteger = (options: RangeOptions) => - bigInteger("AUSTENITE_VAR", "", options); + bigInteger("AUSTENITE_VAR", "", { examples: [], ...options }); const createNetworkPortNumber = (options: RangeOptions) => - networkPortNumber("AUSTENITE_VAR", "", options); + networkPortNumber("AUSTENITE_VAR", "", { + examples: [], + ...options, + }); const createDuration = (options: RangeOptions) => - duration("AUSTENITE_VAR", "", options); + duration("AUSTENITE_VAR", "", { examples: [], ...options }); describe.each` label | create | min | minCanonical | max | maxCanonical | ok | okNative | low | high diff --git a/test/suite/declaration/big-integer.spec.ts b/test/suite/declaration/big-integer.spec.ts index 91d10d9..de16aee 100644 --- a/test/suite/declaration/big-integer.spec.ts +++ b/test/suite/declaration/big-integer.spec.ts @@ -12,7 +12,7 @@ const validValueTable = [ ["explicit positive", "+123456", 123456n], ["negative", "-123456", -123456n], ["octal", "0o361100", 123456n], - ["hexadecimal", "0x1E240", 123456n], + ["hexadecimal", "0x1e240", 123456n], ["binary", "0b11110001001000000", 123456n], ] as const; diff --git a/test/suite/declaration/integer.spec.ts b/test/suite/declaration/integer.spec.ts index 2ac546d..4a009a1 100644 --- a/test/suite/declaration/integer.spec.ts +++ b/test/suite/declaration/integer.spec.ts @@ -13,7 +13,7 @@ const validValueTable = [ ["negative", "-123456", -123456], ["exponential", "1.23456e+5", 123456], ["octal", "0o361100", 123456], - ["hexadecimal", "0x1E240", 123456], + ["hexadecimal", "0x1e240", 123456], ["binary", "0b11110001001000000", 123456], ] as const; diff --git a/test/suite/declaration/number.spec.ts b/test/suite/declaration/number.spec.ts index c357cd8..c9cc03d 100644 --- a/test/suite/declaration/number.spec.ts +++ b/test/suite/declaration/number.spec.ts @@ -13,7 +13,7 @@ const validValueTable = [ ["negative", "-123.456", -123.456], ["exponential", "1.23456e+2", 123.456], ["octal", "0o361100", 123456], - ["hexadecimal", "0x1E240", 123456], + ["hexadecimal", "0x1e240", 123456], ["binary", "0b11110001001000000", 123456], ] as const; diff --git a/test/suite/examples.spec.ts b/test/suite/examples.spec.ts new file mode 100644 index 0000000..4d57632 --- /dev/null +++ b/test/suite/examples.spec.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from "vitest"; +import { integer } from "../../src/index.js"; + +describe("Examples", () => { + describe("when a declaration example violates an intrinsic constraint", () => { + it("throws", () => { + expect(() => { + integer("AUSTENITE_VAR", " { + it("throws", () => { + expect(() => { + integer("AUSTENITE_VAR", " { + it("throws", () => { + expect(() => { + integer("AUSTENITE_VAR", " { + it("throws", () => { + expect(() => { + integer("AUSTENITE_VAR", " { + it("throws", () => { + expect(() => { + integer("AUSTENITE_VAR", " { "example string with length constraint", { length: 1, + examples: [], }, ); string( @@ -551,6 +552,7 @@ describe("Specification documents", () => { "example string with minimum length constraint", { length: { min: 2 }, + examples: [], }, ); string( @@ -558,6 +560,7 @@ describe("Specification documents", () => { "example string with maximum length constraint", { length: { max: 3 }, + examples: [], }, ); string( @@ -565,6 +568,7 @@ describe("Specification documents", () => { "example string with length range constraint", { length: { min: 4, max: 5 }, + examples: [], }, ); binary( @@ -572,6 +576,7 @@ describe("Specification documents", () => { "example binary with length constraint", { length: 1, + examples: [], }, ); binary( @@ -579,6 +584,7 @@ describe("Specification documents", () => { "example binary with minimum length constraint", { length: { min: 2 }, + examples: [], }, ); binary( @@ -586,6 +592,7 @@ describe("Specification documents", () => { "example binary with maximum length constraint", { length: { max: 3 }, + examples: [], }, ); binary( @@ -593,6 +600,7 @@ describe("Specification documents", () => { "example binary with length range constraint", { length: { min: 4, max: 5 }, + examples: [], }, ); number( @@ -601,6 +609,7 @@ describe("Specification documents", () => { { min: 1, minIsExclusive: false, + examples: [], }, ); number( @@ -609,6 +618,7 @@ describe("Specification documents", () => { { min: 2, minIsExclusive: true, + examples: [], }, ); number( @@ -617,6 +627,7 @@ describe("Specification documents", () => { { max: 3, maxIsExclusive: false, + examples: [], }, ); number( @@ -625,6 +636,7 @@ describe("Specification documents", () => { { max: 4, maxIsExclusive: true, + examples: [], }, ); number("AUSTENITE_NUMBER_RANGE", "example number with range constraint", { @@ -632,12 +644,14 @@ describe("Specification documents", () => { minIsExclusive: false, max: 6, maxIsExclusive: true, + examples: [], }); integer( "AUSTENITE_INTEGER_MIN", "example integer with minimum constraint", { min: 1, + examples: [], }, ); integer( @@ -645,6 +659,7 @@ describe("Specification documents", () => { "example integer with maximum constraint", { max: 2, + examples: [], }, ); integer( @@ -653,6 +668,7 @@ describe("Specification documents", () => { { min: 3, max: 4, + examples: [], }, ); bigInteger( @@ -660,6 +676,7 @@ describe("Specification documents", () => { "example big integer with minimum constraint", { min: 1n, + examples: [], }, ); bigInteger( @@ -667,6 +684,7 @@ describe("Specification documents", () => { "example big integer with maximum constraint", { max: 2n, + examples: [], }, ); bigInteger( @@ -675,6 +693,7 @@ describe("Specification documents", () => { { min: 3n, max: 4n, + examples: [], }, ); networkPortNumber( @@ -682,6 +701,7 @@ describe("Specification documents", () => { "example port number with minimum constraint", { min: 11111, + examples: [], }, ); networkPortNumber( @@ -689,6 +709,7 @@ describe("Specification documents", () => { "example port number with maximum constraint", { max: 22222, + examples: [], }, ); networkPortNumber( @@ -697,6 +718,7 @@ describe("Specification documents", () => { { min: 33333, max: 44444, + examples: [], }, ); duration( @@ -704,6 +726,7 @@ describe("Specification documents", () => { "example duration with minimum constraint", { min: Duration.from("PT1S"), + examples: [], }, ); duration( @@ -711,6 +734,7 @@ describe("Specification documents", () => { "example duration with maximum constraint", { max: Duration.from("PT2S"), + examples: [], }, ); duration( @@ -719,6 +743,7 @@ describe("Specification documents", () => { { min: Duration.from("PT3S"), max: Duration.from("PT4S"), + examples: [], }, ); registerVariable({ diff --git a/test/suite/summary.spec.ts b/test/suite/summary.spec.ts index b5556de..1243837 100644 --- a/test/suite/summary.spec.ts +++ b/test/suite/summary.spec.ts @@ -242,7 +242,7 @@ describe("Validation summary", () => { Object.assign(process.env, { AUSTENITE_BINARY: "QmVlcCBib29wIQ", AUSTENITE_DURATION: "PT3H10M0S", - AUSTENITE_INTEGER_BIG: "0x1E240", + AUSTENITE_INTEGER_BIG: "0x1e240", AUSTENITE_INTEGER: "1.23456e5", AUSTENITE_NUMBER: "1.23456e2", AUSTENITE_URL: "https://host.example.org", @@ -329,24 +329,31 @@ describe("Validation summary", () => { string("AUSTENITE_XTRIGGER", "trigger failure"); string("AUSTENITE_STRING", "example string", { length: 5, + examples: [], }); binary("AUSTENITE_BINARY", "example binary", { length: 5, + examples: [], }); number("AUSTENITE_NUMBER", "example number", { min: 3.3, + examples: [], }); integer("AUSTENITE_INTEGER", "example integer", { min: 3, + examples: [], }); bigInteger("AUSTENITE_INTEGER_BIG", "example big integer", { min: 3n, + examples: [], }); networkPortNumber("AUSTENITE_PORT_NUMBER", "example port number", { min: 33333, + examples: [], }); duration("AUSTENITE_DURATION", "example duration", { min: Duration.from("PT3S"), + examples: [], }); registerVariable({ name: "AUSTENITE_CUSTOM",