@vielzeug/codex 3.0.2 → 3.1.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/README.md +6 -0
- package/data/catalog.json +1984 -0
- package/data/llms-full.txt +31429 -0
- package/data/llms.txt +47 -0
- package/data/manifest.json +8 -0
- package/data/packages/arsenal.json +216 -0
- package/data/packages/assay.json +46 -0
- package/data/packages/clockwork.json +66 -0
- package/data/packages/codex.json +29 -0
- package/data/packages/coins.json +101 -0
- package/data/packages/conduit.json +68 -0
- package/data/packages/courier.json +55 -0
- package/data/packages/dnd.json +60 -0
- package/data/packages/familiar.json +17 -0
- package/data/packages/flux.json +94 -0
- package/data/packages/focus.json +38 -0
- package/data/packages/forge.json +60 -0
- package/data/packages/gesture.json +37 -0
- package/data/packages/herald.json +81 -0
- package/data/packages/illusionist.json +132 -0
- package/data/packages/keymap.json +51 -0
- package/data/packages/ledger.json +60 -0
- package/data/packages/lingua.json +70 -0
- package/data/packages/mesh.json +54 -0
- package/data/packages/necromancer.json +50 -0
- package/data/packages/orbit.json +99 -0
- package/data/packages/ore.json +65 -0
- package/data/packages/postmaster.json +49 -0
- package/data/packages/prism.json +66 -0
- package/data/packages/pulse.json +71 -0
- package/data/packages/refine.json +12 -0
- package/data/packages/ripple.json +88 -0
- package/data/packages/rune.json +78 -0
- package/data/packages/sandbox.json +26 -0
- package/data/packages/scout.json +63 -0
- package/data/packages/scroll.json +110 -0
- package/data/packages/sentinel.json +35 -0
- package/data/packages/sigil.json +49 -0
- package/data/packages/sourcerer.json +70 -0
- package/data/packages/spell.json +132 -0
- package/data/packages/tempo.json +81 -0
- package/data/packages/vault.json +93 -0
- package/data/packages/ward.json +80 -0
- package/data/packages/wayfinder.json +114 -0
- package/data/refine.json +12828 -0
- package/data/search.json +1582 -0
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -1
- package/dist/skills.d.ts +26 -0
- package/dist/skills.js +46 -0
- package/dist/skills.js.map +1 -0
- package/package.json +6 -6
- package/skills/vielzeug/SKILL.md +153 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';\nexport { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';\nexport { createParseContext, type DeepPartial } from './messages';\nexport { s } from './s';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Spell — Schema validation for TypeScript\ndescription: Zero-dependency schema validation with explicit async checks, Standard Schema interoperability, and JSON Schema export.\npackage: spell\ncategory: validation\nkeywords: [schema, validation, parsing, standard-schema, json-schema, locale, typescript, descriptors]\nrelated: [forge, courier, vault]\nexports:\n [s, createParseContext, joinIssuePath, SpellValidationError, SpellDefinitionError, StandardSchemaV1, ErrorCode, './json', './predicates']\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"spell\" />\n\n## Why Spell?\n\nSpell keeps runtime validation, static inference, and portable definitions in one API. Use `s` for schema construction; import JSON conversion and predicates from dedicated subpaths.\n\nThis example shows the difference between manual branching and a single reusable schema.\n\n```ts\n// Before\nfunction parseUserBefore(value: unknown) {\n if (typeof value !== 'object' || value === null) throw new Error('Expected object');\n\n const candidate = value as Record<string, unknown>;\n\n if (typeof candidate.email !== 'string' || !candidate.email.includes('@')) {\n throw new Error('Expected valid email');\n }\n\n if (typeof candidate.role !== 'string' || !['admin', 'editor', 'viewer'].includes(candidate.role)) {\n throw new Error('Expected valid role');\n }\n\n return {\n email: candidate.email,\n role: candidate.role,\n };\n}\n\n// After\nimport { s } from '@vielzeug/spell';\n\nconst User = s.object({\n email: s.string().email(),\n role: s.enum(['admin', 'editor', 'viewer'] as const),\n});\n\nconst user = User.parse({ email: 'ada@example.com', role: 'admin' });\n```\n\n| Feature | Spell | Zod | Yup |\n| ----------------- | --------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------- |\n| Bundle size | <PackageInfo package=\"spell\" type=\"size\" /> | ~62 kB | ~14 kB |\n| Type inference | <ore-icon name=\"check\" size=\"16\"></ore-icon> `Infer<T>` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Partial |\n| Coercion API | <ore-icon name=\"check\" size=\"16\"></ore-icon> `s.coerce.*` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Async validation | <ore-icon name=\"check\" size=\"16\"></ore-icon> `.checkAsync()` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Error flattening | <ore-icon name=\"check\" size=\"16\"></ore-icon> `flatten()` + `flattenFirst()` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Partial |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Spell when** you want a fluent schema API with strong TypeScript inference, structured errors, and no third-party runtime dependencies.\n\n**Consider alternatives when** you are already standardized on another validator ecosystem and migration cost outweighs the API benefits.\n\n</div>\n\n## Installation\n\nUse your workspace package manager to add Spell.\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/spell\n```\n\n```sh [npm]\nnpm install @vielzeug/spell\n```\n\n```sh [yarn]\nyarn add @vielzeug/spell\n```\n\n:::\n\n## Quick Start\n\nStart with a schema, then parse unknown input and use the inferred output type everywhere else.\n\n```ts\nimport { s, type Infer } from '@vielzeug/spell';\n\nconst User = s\n .object({\n email: s.string().email(),\n name: s.string().min(1),\n role: s.enum(['admin', 'editor', 'viewer'] as const),\n })\n .relaxed(); // allow extra keys — omit for strict-mode (default)\n\ntype User = Infer<typeof User>;\n\nconst payload: unknown = {\n email: 'ada@example.com',\n name: 'Ada',\n role: 'admin',\n team: 'platform',\n};\n\nconst result = User.safeParse(payload);\n\nif (!result.success) throw result.error;\nconst user = result.data;\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- Namespace and tree-shakeable schema builders.\n- Sync and async parsing with `parse()`, `safeParse()`, `parseAsync()`, and `safeParseAsync()`.\n- Dependency-free Standard Schema v1 interoperability through `schema['~standard']`.\n- Explicit `check()` and `checkAsync()` rules; sync parsing never skips an async check.\n- Wrapper modes for `optional`, `nullable`, `nullish`, `default`, `catch`, and `required`.\n- Frozen declarative definitions through `definition()` and JSON Schema export via `fromDefinition()` from `@vielzeug/spell/json`.\n- Request-local message customization through `createParseContext()`.\n- Ordered union parsing produces the same selected branch in sync and async modes.\n- Structured errors with direct path lookup and flattened views.\n- Object parsing is hardened against prototype-pollution-style keys.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Forge](/forge/) — typed form state that uses Spell schemas as its validation layer\n- [Courier](/courier/) — HTTP client for validating request and response payloads at service boundaries\n- [Vault](/vault/) — unified storage API that accepts Spell schemas to type-gate persisted data\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Spell — API Reference\ndescription: Reference for Spell schema builders, parsing, errors, context, and tooling exports.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| ------------------------ | ------------------------------- | ---------------------------------- | ------------------------------------------ |\n| `s` | Creates schemas | Sync or async, depending on checks | `checkAsync()` requires async parsing |\n| `createParseContext()` | Creates request-local messages | Sync | Pass the context to each parse call |\n| `joinIssuePath()` | Flattens issue paths to UI keys | Sync | Empty or undefined paths join to `''` |\n| `SpellValidationError` | Provides validation issues | Sync/async parse failures | Use `safeParse()` for expected invalid data |\n\n## Package Entry Point\n\n| Import | Purpose |\n| ---------------------------- | ----------------------------------------------- |\n| `@vielzeug/spell` | Schema builders, errors, types, and parse contexts |\n| `@vielzeug/spell/json` | Convert portable definitions to JSON Schema |\n| `@vielzeug/spell/predicates` | Standalone format and type predicates |\n\n```ts\nimport { createParseContext, s, type Infer } from '@vielzeug/spell';\nimport { fromDefinition } from '@vielzeug/spell/json';\nimport { isEmail } from '@vielzeug/spell/predicates';\n```\n\n## `s`\n\nAll builders live under `s`.\n\n| Builder | Purpose |\n| ----------------------------------------------------------------- | -------------------------- |\n| `string`, `number`, `boolean`, `bigint`, `date` | Primitive values |\n| `literal`, `enum`, `null`, `undefined`, `unknown`, `never` | Exact and universal values |\n| `array`, `tuple`, `set`, `map`, `record`, `object` | Collections |\n| `union`, `intersect`, `discriminatedUnion`, `lazy` | Composition |\n| `coerce.*` | Coercing primitive schemas |\n\n```ts\nconst User = s.object({\n email: s.string().email(),\n id: s.string().uuid(),\n role: s.enum(['admin', 'member'] as const),\n});\n\ntype User = Infer<typeof User>;\n```\n\nObject schemas reject unknown keys. Use `.relaxed()` to retain extras.\n\n## Standard Schema interoperability\n\nEvery schema implements the dependency-free Standard Schema v1 structural protocol through `schema['~standard']`. Consumers that accept Standard Schema validators can use Spell schemas directly without an adapter.\n\n```ts\nconst User = s.object({ name: s.string() });\nconst result = await User['~standard'].validate({ name: 'Ada' });\n```\n\nSuccessful results contain `value`; failures contain portable `{ message, path }` issues. Failed unions remain one stable union issue instead of selecting a branch heuristically.\n\nThe exported `StandardSchemaV1` interface mirrors the full v1 protocol, including `validate(value, options?)` and the `InferInput` and `InferOutput` namespace helpers.\n\n## Parsing\n\nEvery schema provides:\n\n```ts\nschema.parse(value, context?); // Output or SpellValidationError\nschema.safeParse(value, context?); // ParseResult<Output>\nschema.parseAsync(value, context?); // Promise<Output>\nschema.safeParseAsync(value, context?); // Promise<ParseResult<Output>>\nschema.is(value); // value is Output\nschema.assert(value, label?); // assertion\n```\n\n`parse()`, `safeParse()`, `is()`, and `assert()` are available on synchronous schemas. Calling `checkAsync()` returns an async-only schema, where TypeScript exposes only `parseAsync()` and `safeParseAsync()`. That async-only mode propagates through compositional schemas when a child is asynchronous.\n\nUI adapters that accept `AnySchema` cannot call `safeParse()` on it — the structural surface type omits it. Type adapter parameters against `SyncParsable` instead:\n\n```ts\nimport type { SyncParsable } from '@vielzeug/spell';\n\nfunction spellValidator(schema: SyncParsable<unknown>) {\n const result = schema.safeParse(value); // types cleanly, no shim or cast\n}\n```\n\nThe shape is structural, so an async-only schema is assignable to `SyncParsable`; calling `safeParse()` on one returns a failed result (\"parse() cannot evaluate async checks\"). When the mode is not statically known, narrow with `InferSchemaMode` and use `safeParseAsync()` for async members.\n\n## Custom Checks\n\n`check()` is synchronous. `checkAsync()` is asynchronous. Do not return a Promise from `check()`.\n\n```ts\nconst Signup = s.object({ confirm: s.string(), password: s.string() }).check((value, context) => {\n if (value.password !== value.confirm) {\n context.addIssue({ code: 'custom', message: 'Passwords must match', path: ['confirm'] });\n }\n});\n\nconst AvailableEmail = s\n .string()\n .email()\n .checkAsync(async (value) => {\n return (await emailAvailable(value)) || 'Email is already registered';\n });\n```\n\n`CheckContext.addIssue()` takes `{ code, message, params?, path? }`. Paths are relative to current schema.\n\n## Modifiers and Transforms\n\n```ts\ns.string().optional();\ns.string().nullable();\ns.string().nullish();\ns.string().required();\ns.string().default('guest');\ns.string().catch('guest');\ns.string()\n .trim()\n .transform((value) => value.toLowerCase());\ns.string().pipe(s.string().slug());\ns.string().label('User name');\n```\n\n`default()`, `catch()`, preprocessors, transforms, and checks are runtime behavior. They cannot become portable definitions.\n\n## Definitions and JSON Schema\n\n`definition()` is only for schemas containing declarative structure. It returns frozen data and throws `SpellDefinitionError` when runtime behavior is present.\n\n```ts\nimport { s } from '@vielzeug/spell';\nimport { fromDefinition } from '@vielzeug/spell/json';\n\nconst Product = s.object({\n id: s.string().uuid(),\n name: s.string().min(1),\n});\n\nconst definition = Product.definition();\nconst jsonSchema = fromDefinition(definition);\n```\n\nNo implicit schema-to-JSON conversion exists. Make definition boundary explicit.\n\n## Parse Contexts\n\n`createParseContext()` creates immutable request-local validation messages.\n\n```ts\nfunction createParseContext(messages?: DeepPartial<Messages>): ParseContext;\n```\n\n**Returns:** A parse context with built-in messages merged with the supplied overrides.\n\n```ts\nimport { createParseContext, s } from '@vielzeug/spell';\n\nconst context = createParseContext({\n object: { invalidKeys: () => 'Unsupported field' },\n});\n\nconst result = s.object({ email: s.string().email() }).safeParse({ email: 'ada@example.com', extra: true }, context);\n\nif (!result.success) {\n const messages = result.error.messagesAt('email');\n console.log(messages);\n}\n```\n\n## Types\n\n### Inference types\n\n| Type | Resolves to |\n| --- | --- |\n| `Infer<TSchema>` | Parsed output; alias of `InferOutput<TSchema>` |\n| `InferInput<TSchema>` | Input accepted before parsing or transformation |\n| `InferOutput<TSchema>` | Value returned after parsing and transformation |\n\nComposite schemas preserve child input types. Object input types make fields optional when their field schema accepts `undefined`.\n\n### Standard Schema\n\n```ts\ninterface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1.Props<Input, Output>;\n}\n\nnamespace StandardSchemaV1 {\n interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (value: unknown, options?: Options) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output>;\n }\n\n type Result<Output> = { readonly value: Output; readonly issues?: undefined } | { readonly issues: readonly Issue[] };\n interface Options { readonly libraryOptions?: Record<string, unknown> }\n interface Issue { readonly message: string; readonly path?: readonly (PropertyKey | PathSegment)[] }\n interface PathSegment { readonly key: PropertyKey }\n interface Types<Input = unknown, Output = Input> { readonly input: Input; readonly output: Output }\n type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];\n type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];\n}\n```\n\n### Parse result and issues\n\n```ts\ntype ParseResult<T> = { data: T; success: true } | { error: SpellValidationError; success: false };\n\ntype Issue =\n | { code: 'custom'; message: string; params?: Record<string, unknown>; path: (string | number)[] }\n | { code: 'invalid_base64'; message: string; params: { format: string }; path: (string | number)[] }\n | { code: 'invalid_date'; message: string; params?: undefined; path: (string | number)[] }\n | { code: 'invalid_duration'; message: string; params: { format: string }; path: (string | number)[] }\n | { code: 'invalid_enum'; message: string; params: { values: readonly unknown[] }; path: (string | number)[] }\n | { code: 'invalid_finite'; message: string; params?: undefined; path: (string | number)[] }\n | { code: 'invalid_integer'; message: string; params?: undefined; path: (string | number)[] }\n | { code: 'invalid_keys'; message: string; params: { keys: string[] }; path: (string | number)[] }\n | { code: 'invalid_length'; message: string; params: { exact: number }; path: (string | number)[] }\n | { code: 'invalid_literal'; message: string; params: { expected: unknown }; path: (string | number)[] }\n | { code: 'invalid_multiple_of'; message: string; params: { step: number | bigint }; path: (string | number)[] }\n | { code: 'invalid_safe'; message: string; params?: undefined; path: (string | number)[] }\n | {\n code: 'invalid_string';\n message: string;\n params: { format?: string; includes?: string; pattern?: string; prefix?: string; suffix?: string };\n path: (string | number)[];\n }\n | { code: 'invalid_type'; message: string; params?: undefined; path: (string | number)[] }\n | { code: 'invalid_union'; message: string; params: { errors: Issue[][] }; path: (string | number)[] }\n | { code: 'invalid_unique'; message: string; params: { unique: true }; path: (string | number)[] }\n | { code: 'invalid_url'; message: string; params: { format: string }; path: (string | number)[] }\n | {\n code: 'invalid_variant';\n message: string;\n params: { discriminator: string; expected: string[] };\n path: (string | number)[];\n }\n | {\n code: 'too_big';\n message: string;\n params: { exclusive?: boolean; max: number | bigint | Date };\n path: (string | number)[];\n }\n | {\n code: 'too_small';\n message: string;\n params: { exclusive?: boolean; min: number | bigint | Date };\n path: (string | number)[];\n }\n | { code: string & {}; message: string; params?: Record<string, unknown>; path: (string | number)[] };\n```\n\n`ErrorCode` is a const object mapping each issue code to its string literal.\n\n### Validation contracts\n\n```ts\ntype ParseContext = { messages: Messages };\n\ntype CheckContext = {\n addIssue: (issue: {\n code: string;\n message: string;\n params?: Record<string, unknown>;\n path?: (string | number)[];\n }) => void;\n};\n\ntype ValidateResult = boolean | null | undefined | string;\n```\n\n### Messages\n\n```ts\ntype MessageFn<Ctx extends Record<string, unknown> = Record<string, unknown>> = string | ((ctx: Ctx) => string);\n\ntype Messages = {\n array: { length: (ctx: { exact: number; value: unknown[] }) => string; max: (ctx: { max: number; value: unknown[] }) => string; min: (ctx: { min: number; value: unknown[] }) => string; nonEmpty: () => string; type: () => string; unique: () => string };\n bigint: { max: (ctx: { max: bigint; value: bigint }) => string; min: (ctx: { min: bigint; value: bigint }) => string; multipleOf: (ctx: { step: bigint; value: bigint }) => string; negative: () => string; nonNegative: () => string; nonPositive: () => string; positive: () => string; type: () => string };\n boolean: { type: () => string };\n check: { default: () => string };\n date: { max: (ctx: { max: Date; value: Date }) => string; min: (ctx: { min: Date; value: Date }) => string; type: () => string };\n enum: { invalid: (ctx: { values: readonly unknown[] }) => string };\n instanceof: { type: (ctx: { className: string }) => string };\n literal: { expected: (ctx: { expected: unknown }) => string };\n map: { max: (ctx: { max: number; value: Map<unknown, unknown> }) => string; min: (ctx: { min: number; value: Map<unknown, unknown> }) => string; nonEmpty: () => string; size: (ctx: { exact: number; value: Map<unknown, unknown> }) => string; type: () => string };\n never: { invalid: () => string };\n number: { finite: () => string; int: () => string; max: (ctx: { max: number; value: number }) => string; min: (ctx: { min: number; value: number }) => string; multipleOf: (ctx: { step: number; value: number }) => string; negative: () => string; nonNegative: () => string; nonPositive: () => string; positive: () => string; safe: () => string; type: () => string };\n object: { invalidKeys: (ctx: { keys: string[] }) => string; type: () => string };\n set: { max: (ctx: { max: number; value: Set<unknown> }) => string; min: (ctx: { min: number; value: Set<unknown> }) => string; nonEmpty: () => string; size: (ctx: { exact: number; value: Set<unknown> }) => string; type: () => string };\n string: { base64: () => string; base64url: () => string; cuid: () => string; cuid2: () => string; date: () => string; dateTime: () => string; duration: () => string; email: () => string; emoji: () => string; endsWith: (ctx: { suffix: string; value: string }) => string; hex: () => string; hexColor: () => string; includes: (ctx: { substr: string; value: string }) => string; ip: () => string; jwt: () => string; length: (ctx: { exact: number; value: string }) => string; max: (ctx: { max: number; value: string }) => string; min: (ctx: { min: number; value: string }) => string; nanoid: () => string; nonEmpty: () => string; numeric: () => string; regex: (ctx: { value: string }) => string; semver: () => string; slug: () => string; startsWith: (ctx: { prefix: string; value: string }) => string; time: () => string; type: () => string; ulid: () => string; url: () => string; uuid: () => string };\n tuple: { length: (ctx: { exact: number }) => string; min: (ctx: { min: number }) => string; type: () => string };\n union: { invalid: () => string };\n variant: { invalidDiscriminator: (ctx: { discriminator: string; expected: string[] }) => string; type: () => string };\n};\n\ntype DeepPartial<T> = {\n [K in keyof T]?: T[K] extends Record<string, unknown> ? DeepPartial<T[K]> : T[K];\n};\n```\n\n### Descriptor and JSON Schema\n\n```ts\ntype SchemaDescriptor = BaseDescriptor &\n (\n | { kind: 'unknown' | 'never' | 'boolean' | 'bigint' | 'date' | 'lazy' }\n | { className: string; kind: 'instanceof' }\n | { contentEncoding?: string; format?: string; kind: 'string'; maxLength?: number; minLength?: number; pattern?: string | null }\n | { exclusiveMaximum?: number; exclusiveMinimum?: number; kind: 'number'; maximum?: number; minimum?: number; multipleOf?: number; typeHint?: 'integer' }\n | { kind: 'literal'; value: string | number | boolean | null | undefined }\n | { kind: 'enum'; values: readonly (string | number)[] }\n | { items: SchemaDescriptor; kind: 'array'; maxItems?: number; minItems?: number }\n | { items: SchemaDescriptor[]; kind: 'tuple'; rest: SchemaDescriptor | null }\n | { fields: Record<string, SchemaDescriptor>; kind: 'object'; strict: boolean }\n | { key: SchemaDescriptor; kind: 'record'; value: SchemaDescriptor }\n | { items: SchemaDescriptor; kind: 'set' }\n | { key: SchemaDescriptor; kind: 'map'; value: SchemaDescriptor }\n | { branches: SchemaDescriptor[]; kind: 'union' | 'intersect' }\n | { branches: Record<string, SchemaDescriptor>; discriminator: string; kind: 'variant' }\n | { from: SchemaDescriptor; kind: 'pipe'; to: SchemaDescriptor }\n );\n\ntype JsonSchema = Record<string, unknown>;\n```\n\n### Schema traversal\n\n`SchemaWalker<R>` is the visitor type accepted by `schema.walk()`. Handlers are optional and correspond to schema kinds; composite handlers also receive their walked children. Use `unknown` as the fallback handler.\n\n### Error helpers\n\n```ts\ntype FlatError = { messages: string[]; path: (string | number)[] };\ntype FlatErrorFirst = { message: string; path: (string | number)[] };\n\n/** An issue path in either shape: spell-native segments or Standard Schema `{ key }` segments. */\ntype IssuePathSegment = PropertyKey | { readonly key: PropertyKey };\ntype IssuePath = readonly IssuePathSegment[];\n\n/** The synchronous parse surface; `AnySchema` omits `safeParse`, adapter parameters use this. */\ntype SyncParsable<T = unknown> = { safeParse(value: unknown): ParseResult<T> };\n\nfunction joinIssuePath(path: IssuePath | undefined, separator?: string): string;\n```\n\n`joinIssuePath()` flattens an issue path into a UI key such as `'permissions.0.slug'`. It accepts spell-native `(string | number)[]` paths and Standard Schema paths with `{ key }` segments, and returns `''` for empty or undefined paths.\n\n## Errors\n\n- `SpellError` — base class. Use `instanceof SpellError` for cross-boundary narrowing.\n- `SpellValidationError` — validation failure with `issues`, `messagesAt()`, `flatten()`, and `flattenFirst()`.\n- `SpellDefinitionError` — schema cannot create portable definition.\n\n```ts\nconst result = s.object({ email: s.string().email() }).safeParse({ email: 'invalid' });\n\nif (!result.success) {\n const { fieldErrors, formErrors } = result.error.flatten();\n console.log(fieldErrors, formErrors);\n}\n```\n",
|
|
6
|
+
"usage": "---\ntitle: Spell — Usage Guide\ndescription: Learn how to build schemas, compose wrappers, customize locales, and integrate spell with other Vielzeug packages.\n---\n\n[[toc]]\n\n## Basic Usage\n\nStart with `safeParse()` when you want explicit success and failure branches.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Signup = s.object({\n email: s.string().email(),\n password: s.string().min(12),\n referralCode: s.string().optional(),\n});\n\nconst result = Signup.safeParse({\n email: 'ada@example.com',\n password: 'horse-battery-staple',\n});\n\nif (!result.success) {\n console.error(result.error.issues);\n} else {\n console.log(result.data.email);\n}\n```\n\nUse `parse()` when invalid input should throw immediately. Use `safeParse()` when invalid input is part of normal control flow.\n\n## Building Schemas\n\nUse the namespace form when readability matters more than bundle trimming.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Article = s.object({\n id: s.string().uuid(),\n title: s.string().trim().min(1).max(120),\n slug: s.string().slug(),\n tags: s.array(s.string().min(1)).default(() => []),\n meta: s\n .object({\n published: s.boolean(),\n publishedAt: s.date().nullable(),\n })\n .relaxed(),\n});\n```\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Todo = s.object({\n done: s.boolean(),\n tags: s.array(s.string().min(1)).default(() => []),\n title: s.string().min(1),\n});\n```\n\nObject schemas reject unknown keys by default. Call `.relaxed()` when you need to preserve extra properties.\n\nCall `.defaults()` to get a fully default-filled object without providing any input. Every required field must have a `.default()` set, or a `SpellValidationError` is thrown. Call `.partialDefaults()` when only some fields have defaults — fields without a default are silently omitted instead of throwing.\n\n```ts\nconst Config = s.object({\n host: s.string().default('localhost'),\n port: s.number().default(3000),\n});\n\nConfig.defaults(); // { host: 'localhost', port: 3000 }\n\nconst Form = s.object({ name: s.string(), role: s.string().default('viewer') });\nForm.partialDefaults(); // { role: 'viewer' }\n```\n\n## Wrapper Modes, Defaults, and Fallbacks\n\nChain wrappers to describe missing values and recovery rules without losing schema metadata.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst DisplayName = s.string().trim().min(2).label('Display name').optional().default('Guest').nullable();\n\nDisplayName.parse(undefined); // 'Guest'\nDisplayName.parse(null); // null\nDisplayName.description; // 'Display name'\n```\n\nCall `.required()` to remove `undefined` without removing `null`.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst NullableButRequired = s.string().optional().nullable().required();\n\nNullableButRequired.parse('Ada');\nNullableButRequired.parse(null);\n// NullableButRequired.parse(undefined); // throws\n```\n\nUse `.catch()` when you want a fallback output after validation fails.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Port = s.number().int().min(1).max(65535).catch(3000);\n\nPort.parse('not-a-number'); // 3000\n```\n\n## Custom Validation\n\nUse `check()` for synchronous domain rules and `checkAsync()` for asynchronous rules. Sync parsing rejects schemas with asynchronous checks.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\n// Boolean shorthand: return false to fail with default message\nconst EvenNumber = s.number().check((n) => n % 2 === 0);\n\n// String shorthand: return the message as a string\nconst Username = s\n .string()\n .min(3)\n .check((v) => !v.startsWith('_') || 'Cannot start with underscore');\n\n// Multiple issues via ctx.addIssue()\nconst Signup = s.object({ confirm: s.string(), password: s.string() }).check((v, ctx) => {\n if (v.password !== v.confirm) {\n ctx.addIssue({ code: 'custom', message: 'Passwords must match', path: ['confirm'] });\n }\n});\n```\n\n`checkAsync()` returns an async-only schema: TypeScript exposes `parseAsync()` and `safeParseAsync()` but not `parse()` or `safeParse()`. This mode survives fluent modifiers and propagates through nested arrays, objects, unions, intersections, tuples, maps, records, sets, lazy schemas, pipelines, and `s.discriminatedUnion(...)` branches. Sync parsing also fails at runtime instead of accepting an unchecked value.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst takenEmails = new Set(['ada@example.com']);\n\nconst AccountEmail = s\n .string()\n .email()\n .checkAsync(async (value, ctx) => {\n if (takenEmails.has(value)) {\n ctx.addIssue({ code: 'custom', message: 'Email is already taken', path: [] });\n }\n });\n\n// Async checks require parseAsync\nawait AccountEmail.parseAsync('grace@example.com');\n```\n\nUse `check()` for predicate-only rules too. Return `true` on success or message on failure.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst PositivePrice = s.number().check((value) => value > 0 || 'Must be positive');\nPositivePrice.parse(9.99);\n```\n\n## Strings, Numbers, and Safe Regex Usage\n\nUse schema helpers for common string and number constraints instead of hand-written predicates.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Password = s.string().min(12).regex(/[A-Z]/).regex(/[0-9]/);\nconst Price = s.number().nonNegative().multipleOf(0.01);\nconst LaunchWindow = s.date().min(new Date('2025-01-01T00:00:00.000Z'));\n```\n\nSpell strips stateful `/g` and `/y` flags from `regex()` patterns before validation. Repeated parses stay deterministic even when the original regular expression is reused.\n\n## Coercion and Transforms\n\nUse coercion when input arrives as strings, query parameters, or form values.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst Query = s.object({\n draft: s.coerce.boolean().default(false),\n limit: s.coerce.number().int().positive().default(20),\n publishedAt: s.coerce.date().nullable(),\n search: s.coerce.string().trim().min(1).optional(),\n});\n\nconst parsed = Query.parse({\n draft: 'true',\n limit: '50',\n publishedAt: '2025-04-01T12:00:00.000Z',\n search: ' vielzeug ',\n});\n```\n\nUse `transform()` or `pipe()` after validation when downstream code needs a different output shape.\n\n```ts\nimport { s } from '@vielzeug/spell';\n\nconst TrimmedTags = s.array(s.string().trim().min(1)).transform((tags) => tags.map((tag) => tag.toLowerCase()));\nconst Slug = s.string().trim().min(1).pipe(s.string().slug());\n```\n\n## Introspection, Round-Trips, and JSON Schema\n\nUse declarative definitions when schemas need to cross process boundaries or feed tooling.\n\n```ts\nimport { s } from '@vielzeug/spell';\nimport { fromDefinition } from '@vielzeug/spell/json';\n\nconst Product = s\n .object({\n id: s.string().uuid(),\n name: s.string().min(1),\n price: s.number().positive().multipleOf(0.01),\n })\n .label('Product');\n\nconst definition = Product.definition();\nconst jsonSchema = fromDefinition(definition);\n\nProduct.parse({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Keyboard', price: 129.99 });\nconsole.log(jsonSchema.title);\n```\n\nDefinitions are frozen serializable snapshots of declarative schema structure. Use `definition()` and `fromDefinition()` for external tooling. Schemas with runtime checks, transforms, defaults, catches, or preprocessors intentionally have no definition.\n\n## Messages\n\nSpell has no mutable process-wide configuration. Build one parse context per request, locale, or form, then pass it explicitly.\n\n```ts\nimport { createParseContext, s } from '@vielzeug/spell';\n\nconst User = s.object({ email: s.string().email() });\nconst german = createParseContext({\n object: { invalidKeys: () => 'Keine unbekannten Felder erlaubt' },\n});\n\nUser.safeParse({ email: 'ada@example.com', extra: true }, german);\n```\n\nInternal development warnings always use `console.warn` in development builds. Route application diagnostics in application code instead of mutating library-wide logger state.\n\n## Working with Validation Errors\n\nUse `SpellValidationError` helpers when you need UI-ready error structures.\n\n```ts\nimport { s, SpellValidationError } from '@vielzeug/spell';\n\nconst User = s.object({\n email: s.string().email(),\n profile: s.object({\n name: s.string().min(2),\n }),\n});\n\nconst result = User.safeParse({ email: 'nope', profile: { name: '' } });\n\nif (!result.success && result.error instanceof SpellValidationError) {\n const profileErrors = result.error.messagesAt('profile', 'name');\n console.log(profileErrors);\n}\n```\n\nKey inline errors by dot-path with `joinIssuePath()`, which also unwraps Standard Schema `{ key }` segments:\n\n```ts\nimport { joinIssuePath } from '@vielzeug/spell';\n\nfor (const issue of result.error.issues) {\n console.log(joinIssuePath(issue.path), issue.message); // 'profile.name: ...'\n}\n```\n\nUnion failures produce one stable `invalid_union` issue. Use `s.discriminatedUnion()` when a discriminator can identify the intended object branch and provide field-specific errors.\n\n## Validating synchronously in React\n\nAdapters that validate inside a render or `validate` callback need the sync parse surface. `AnySchema` does not expose `safeParse`; type the adapter parameter against `SyncParsable` instead:\n\n```ts\nimport { type SyncParsable } from '@vielzeug/spell';\n\nfunction spellValidator(schema: SyncParsable<unknown>) {\n return (values: unknown) => {\n const result = schema.safeParse(values);\n if (result.success) return undefined;\n\n return result.error.issues.map((issue) => ({\n message: issue.message,\n path: issue.path,\n }));\n };\n}\n\nspellValidator(s.object({ email: s.string() })); // no shim or cast\n```\n\n## Schema Traversal with walk()\n\nUse `walk()` to inspect or transform a schema tree without importing internal implementation classes.\n\n```ts\nimport { s, type SchemaWalker } from '@vielzeug/spell';\n\nconst fields: string[] = [];\n\nconst collectFields: SchemaWalker<void> = {\n object(schema) {\n for (const [key, child] of Object.entries(schema.shape)) {\n fields.push(key);\n child.walk(collectFields);\n }\n },\n unknown() {},\n};\n\nconst User = s.object({\n email: s.string().email(),\n profile: s.object({ name: s.string() }),\n});\n\nUser.walk(collectFields);\nconsole.log(fields); // ['email', 'profile', 'name']\n```\n\n`walk()` dispatches by `schema.kind`. If no handler matches and no `unknown` fallback is provided, `walk()` returns `null`. Add an `unknown` handler to capture any kind not explicitly listed in your visitor.\n\n## Framework Integration\n\nSpell works anywhere you can call a function before state enters your app.\n\n::: code-group\n\n```tsx [React]\nimport { s } from '@vielzeug/spell';\n\nconst SearchParams = s\n .object({\n page: s.coerce.number().int().positive().default(1),\n q: s.string().trim().optional(),\n })\n .relaxed();\n\nexport function SearchPage({ rawParams }: { rawParams: unknown }) {\n const params = SearchParams.parse(rawParams);\n\n return (\n <div>\n {params.q ?? 'All results'} — page {params.page}\n </div>\n );\n}\n```\n\n```ts [Vue]\nimport { computed, ref } from 'vue';\nimport { s } from '@vielzeug/spell';\n\nconst Settings = s.object({\n locale: s.string().min(2),\n compact: s.coerce.boolean().default(false),\n});\n\nconst raw = ref<unknown>({ locale: 'en', compact: 'true' });\nconst settings = computed(() => Settings.parse(raw.value));\n```\n\n:::\n\nUse `safeParse()` at event boundaries and `parse()` inside trusted data flows.\n\n## Working with Other Vielzeug Libraries\n\nUse Spell as the validation layer and let other packages focus on transport, forms, or storage.\n\n```ts\nimport { createForm } from '@vielzeug/forge';\nimport { schemaValidator } from '@vielzeug/forge/schema';\nimport { createCourier } from '@vielzeug/courier';\nimport { s } from '@vielzeug/spell';\n\nconst Profile = s.object({\n displayName: s.string().min(2),\n newsletter: s.boolean(),\n});\n\nconst form = createForm({\n initialValues: {\n displayName: '',\n newsletter: false,\n },\n validate: schemaValidator(Profile),\n});\n\nconst courier = createCourier({ baseUrl: '/api' });\nconst profile = Profile.parse(await courier.get('/profile'));\n```\n\nUse Spell definitions with `@vielzeug/codex` or other tooling when you need generated docs or external schema consumers.\n\n## Best Practices\n\n- Keep schemas close to the boundary where unknown data enters your app.\n- Use `s` consistently for construction; use explicit `/json` and `/predicates` subpaths for tooling.\n- Use `.default(() => value)` for mutable defaults such as arrays, objects, `Map`, and `Set`.\n- Call `.required()` when you want to remove `undefined` but keep `null` semantics intact.\n- Use `check()` with a `ctx` argument when you need `ctx.addIssue()`; return a message for simple predicate failures.\n- Use `checkAsync()` and `parseAsync()` for every asynchronous domain rule.\n- Build a parse context per request or test; never rely on mutable process-wide configuration.\n- Use `definition()` with `fromDefinition()` from `@vielzeug/spell/json` for external tooling.\n",
|
|
7
|
+
"examples": "---\ntitle: Spell — Examples\ndescription: Practical examples and recipes for spell.\n---\n\n## Examples\n\n- [Validating API Payloads](./examples/api.md)\n- [Form-Safe Parsing](./examples/forms.md)\n- [Async Business Rules](./examples/async.md)\n- [Schema Introspection and Round-Trips](./examples/introspection.md)\n- [Unions, Intersections, and Variants](./examples/unions.md)\n- [Schema Traversal with walk()](./examples/walk.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "array-validation",
|
|
12
|
+
"code": "// Validate a product tag list before it hits search filters.\nimport { s } from '@vielzeug/spell'\n\nconst ProductTags = s.array(s.string().trim().min(2)).min(1).max(4).unique()\n\nconsole.log('Valid tags:', ProductTags.safeParse(['ui', 'forms', 'docs']).success)\n\nconst invalid = ProductTags.safeParse(['ui', 'ui', 'x', 'search', 'extra'])\nconsole.log('Invalid tags:', invalid.success)\n\nif (!invalid.success) {\n console.log('Issues:', invalid.error.issues.map((issue) => issue.message))\n}",
|
|
13
|
+
"name": "Array Validation"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "async-validate",
|
|
17
|
+
"code": "// checkAsync() declares asynchronous domain rules.\n// Use safeParseAsync() or parseAsync() for schemas containing async checks.\nimport { s } from '@vielzeug/spell'\n\n// Simulated async check (e.g. database lookup)\nfunction isUsernameAvailable(name) {\n return new Promise(resolve => setTimeout(() => resolve(name !== 'taken'), 50))\n}\n\nconst UsernameSchema = s.string()\n .min(3)\n .checkAsync(async (name) => {\n const available = await isUsernameAvailable(name)\n return available || 'Username is already taken'\n })\n\n// Async checks require safeParseAsync() or parseAsync()\nconst ok = await UsernameSchema.safeParseAsync('alice')\nconsole.log('alice:', ok.success ? 'available' : ok.error.issues[0].message)\n\nconst fail = await UsernameSchema.safeParseAsync('taken')\nconsole.log('taken:', fail.success ? 'available' : fail.error.issues[0].message)\n\nconst tooShort = await UsernameSchema.safeParseAsync('ab')\nconsole.log('ab:', tooShort.success ? 'available' : tooShort.error.issues[0].message)",
|
|
18
|
+
"name": "Async Validation"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "basic-parsing",
|
|
22
|
+
"code": "// Schema definition, type inference, and safe parsing\nimport { s } from '@vielzeug/spell'\n\nconst Product = s.object({\n id: s.string().uuid(),\n name: s.string().min(1).max(120),\n price: s.number().positive().multipleOf(0.01),\n tags: s.array(s.string().min(1)).default(() => []),\n})\n\n// Infer the TypeScript type directly from the schema\n// type Product = { id: string; name: string; price: number; tags: string[] }\n\n// parse() throws on failure — use when invalid input is a programmer error\nconst product = Product.parse({\n id: '550e8400-e29b-41d4-a716-446655440000',\n name: 'Mechanical Keyboard',\n price: 129.99,\n})\nconsole.log('Parsed:', product.name, '— tags:', product.tags)\n\n// safeParse() returns a tagged result union — use at untrusted boundaries\nconst bad = Product.safeParse({ id: 'not-a-uuid', name: '', price: -5 })\nif (!bad.success) {\n const paths = bad.error.issues.map(i => i.path.join('.') || 'root')\n console.log('Validation failed at:', paths.join(', '))\n}",
|
|
23
|
+
"name": "Basic Parsing"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "basic-schema",
|
|
27
|
+
"code": "// Validate a signup payload before it enters application state.\nimport { s } from '@vielzeug/spell'\n\nconst Signup = s.object({\n email: s.string().email(),\n password: s.string().min(12),\n referralCode: s.string().optional(),\n})\n\nconsole.log('Accepted:', Signup.parse({\n email: 'ada@example.com',\n password: 'horse-battery-staple',\n}))\n\nconst invalid = Signup.safeParse({\n email: 'not-an-email',\n password: 'short',\n})\n\nif (!invalid.success) {\n console.log('Email errors:', invalid.error.messagesAt('email'))\n console.log('Password errors:', invalid.error.messagesAt('password'))\n}",
|
|
28
|
+
"name": "Basic Schema Validation"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "coercion",
|
|
32
|
+
"code": "// Coerce query params into typed search options with safe defaults.\nimport { s } from '@vielzeug/spell'\n\nconst SearchQuery = s.object({\n draft: s.coerce.boolean().default(false),\n limit: s.coerce.number().int().positive().default(20),\n page: s.coerce.number().int().positive().default(1),\n q: s.coerce.string().trim().min(1).optional(),\n})\n\nconst parsed = SearchQuery.parse({\n draft: 'true',\n limit: '50',\n page: '2',\n q: ' vielzeug ',\n})\n\nconsole.log(parsed)\nconsole.log('limit type:', typeof parsed.limit)",
|
|
33
|
+
"name": "Type Coercion"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "descriptor-roundtrip",
|
|
37
|
+
"code": "import { s } from '@vielzeug/spell'\nimport { fromDefinition } from '@vielzeug/spell/json'\n\nconst Product = s.object({\n id: s.string().uuid(),\n name: s.string().min(1),\n price: s.number().positive(),\n})\n\nconst definition = Product.definition()\nconst jsonSchema = fromDefinition(definition)\n\nconsole.log(definition.kind)\nconsole.log(jsonSchema)",
|
|
38
|
+
"name": "Declarative Definition Export"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "discriminated-union",
|
|
42
|
+
"code": "// s.discriminatedUnion() validates a discriminated union — objects sharing a common tag field.\n// Spell automatically injects the discriminator literal into each branch.\nimport { s } from '@vielzeug/spell'\n\nconst Event = s.discriminatedUnion('type', {\n click: s.object({ x: s.number(), y: s.number() }),\n keydown: s.object({ key: s.string(), repeat: s.boolean() }),\n resize: s.object({ width: s.number(), height: s.number() }),\n})\n\nconst click = Event.parse({ type: 'click', x: 100, y: 200 })\nconsole.log('click:', click)\n\nconst key = Event.parse({ type: 'keydown', key: 'Enter', repeat: false })\nconsole.log('keydown:', key)\n\n// Wrong discriminator value\nconst bad = Event.safeParse({ type: 'unknown', x: 0 })\nconsole.log('unknown type:', bad.success ? 'ok' : bad.error.issues[0].message)\n\n// Missing required field in matched branch\nconst missingField = Event.safeParse({ type: 'resize', width: 800 })\nconsole.log('missing height:', missingField.success ? 'ok' : missingField.error.issues[0].message)",
|
|
43
|
+
"name": "Discriminated Union"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "format-validators",
|
|
47
|
+
"code": "import { s } from '@vielzeug/spell'\nimport { isEmail, isUuid } from '@vielzeug/spell/predicates'\n\nconsole.log(isEmail('ada@example.com'))\nconsole.log(isEmail('not-an-email'))\nconsole.log(isUuid('550e8400-e29b-41d4-a716-446655440000'))\nconsole.log(isUuid('short'))\n\nconst UserId = s.string().uuid()\nconsole.log(UserId.safeParse('550e8400-e29b-41d4-a716-446655440000').success)",
|
|
48
|
+
"name": "Format Predicates"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "messages-override",
|
|
52
|
+
"code": "import { createParseContext, s } from '@vielzeug/spell'\n\nconst context = createParseContext({\n object: { invalidKeys: () => 'Use only supported fields' },\n})\n\nconsole.log(s.object({ email: s.string().email() }).safeParse({ email: 'ada@example.com', extra: true }, context).success)",
|
|
53
|
+
"name": "Request-local messages"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "nested-objects",
|
|
57
|
+
"code": "// Model an API response with a discriminator instead of a loose union.\nimport { s } from '@vielzeug/spell'\n\nconst SearchResponse = s.discriminatedUnion('status', {\n error: s.object({\n message: s.string().min(1),\n status: s.literal('error'),\n }),\n success: s.object({\n results: s.array(s.object({ id: s.string().uuid(), title: s.string().min(1) })).default(() => []),\n status: s.literal('success'),\n }),\n})\n\nconsole.log('Success branch:', SearchResponse.parse({\n status: 'success',\n results: [{ id: '550e8400-e29b-41d4-a716-446655440000', title: 'Spell docs' }],\n}))\n\nconst invalid = SearchResponse.safeParse({ status: 'success', message: 'no results here' })\nconsole.log('Invalid branch accepted:', invalid.success)",
|
|
58
|
+
"name": "Variant Responses"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "number-validation",
|
|
62
|
+
"code": "// Enforce money-like numeric constraints for a checkout amount.\nimport { s } from '@vielzeug/spell'\n\nconst CheckoutTotal = s.number().nonNegative().multipleOf(0.01).max(9999)\n\nfor (const value of [129.99, -4, 19.999, 15000]) {\n const result = CheckoutTotal.safeParse(value)\n console.log(value, '=>', result.success ? 'accepted' : result.error.issues[0].message)\n}",
|
|
63
|
+
"name": "Number Validation"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "object-defaults",
|
|
67
|
+
"code": "import { s } from '@vielzeug/spell';\n\n// Schema where all fields have defaults\nconst ServerConfig = s.object({\n host: s.string().default('localhost'),\n port: s.number().int().positive().default(3000),\n tls: s.boolean().default(false),\n});\n\n// Get a fully filled config without providing any input\nconst config = ServerConfig.defaults();\nconsole.log(config);\n// { host: 'localhost', port: 3000, tls: false }\n\n// Works with nested schemas too\nconst AppConfig = s.object({\n server: ServerConfig,\n debug: s.boolean().default(false),\n});\n\n// Parse with partial input — missing fields use their defaults\nconst parsed = AppConfig.parse({ server: { host: 'prod.example.com', port: 443, tls: true }, debug: true });\nconsole.log(parsed.server.host); // 'prod.example.com'\n\n// Schema with required field (no default) — throws if .defaults() called\nconst Strict = s.object({ name: s.string() });\nconst result = Strict.safeParse({});\nconsole.log(result.success); // false — name is required\n",
|
|
68
|
+
"name": "Object Defaults"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "object-merge",
|
|
72
|
+
"code": "import { s } from '@vielzeug/spell';\n\n// merge() combines two object schemas (right-hand fields win on conflict)\nconst Base = s.object({\n id: s.string().uuid(),\n createdAt: s.date(),\n});\n\nconst WithMeta = s.object({\n description: s.string().optional(),\n tags: s.array(s.string()).default(() => []),\n});\n\nconst Resource = Base.merge(WithMeta);\n\nconst result = Resource.parse({\n createdAt: new Date('2025-01-01'),\n id: '550e8400-e29b-41d4-a716-446655440000',\n tags: ['api', 'v2'],\n});\nconsole.log(result.tags); // ['api', 'v2']\nconsole.log(result.id); // '550e8400-...'\n\n// merge() inherits the right-hand schema's strict/relaxed mode\nconst Strict = s.object({ a: s.string() });\nconst Relaxed = s.object({ b: s.number() }).relaxed();\n\nconst Merged = Strict.merge(Relaxed);\n// Extra keys are allowed because Relaxed is the right-hand schema\nconsole.log(Merged.safeParse({ a: 'hi', b: 1, extra: true }).success); // true\n\nconst IdOrSlug = s.union(s.string().uuid(), s.string().slug());\nconsole.log(IdOrSlug.safeParse('550e8400-e29b-41d4-a716-446655440000').success); // true\nconsole.log(IdOrSlug.safeParse('my-slug').success); // true\nconsole.log(IdOrSlug.safeParse(42).success); // false\n\nconst NonEmptyString = s.intersect(s.string(), s.string().min(1));\nconsole.log(NonEmptyString.parse('hello')); // 'hello'\n",
|
|
73
|
+
"name": "Object Merge & Aliases"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "optional-nullable",
|
|
77
|
+
"code": "// Preserve defaults and validators while tightening undefined away with required().\nimport { s } from '@vielzeug/spell'\n\nconst DisplayName = s.string().trim().min(2).optional().default('Guest').nullable()\nconst RequiredDisplayName = DisplayName.required()\n\nconsole.log('default for undefined:', DisplayName.parse(undefined))\nconsole.log('null stays null:', DisplayName.parse(null))\n\nconst short = RequiredDisplayName.safeParse('A')\nconsole.log('short name accepted:', short.success)\n\nconst missing = RequiredDisplayName.safeParse(undefined)\nconsole.log('undefined accepted after required():', missing.success)\n\nconsole.log('null accepted after required():', RequiredDisplayName.parse(null))",
|
|
78
|
+
"name": "Optional and Nullable Fields"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "refinements",
|
|
82
|
+
"code": "// check() and checkAsync() — explicit custom domain rules\nimport { s } from '@vielzeug/spell'\n\nconst reserved = new Set(['admin', 'root'])\n\n// check() is synchronous; return a string to fail with that message\nconst Username = s.string().min(3).check((value) =>\n !reserved.has(value) || value + ' is reserved'\n)\n\n// check() receives context for multiple issues or custom error codes\nconst Signup = s.object({ password: s.string().min(8), confirm: s.string() })\n .check((v, ctx) => {\n if (v.password !== v.confirm)\n ctx.addIssue({ code: 'custom', message: 'Passwords must match', path: ['confirm'] })\n })\n\n// check() also covers predicate-only domain rules\nconst EvenPort = s.number().int().min(1).max(65535)\n .check((n) => n % 2 === 0 || 'Port must be even')\n\nfor (const name of ['ad', 'admin', 'grace']) {\n const r = Username.safeParse(name)\n console.log(name, '->', r.success ? 'ok' : r.error.issues[0].message)\n}\n\nconst signupResult = Signup.safeParse({ password: 'secure123', confirm: 'different' })\nconsole.log('signup:', signupResult.success ? 'ok' : signupResult.error.issues[0].message)\n\nfor (const port of [8080, 3001, 443]) {\n const r = EvenPort.safeParse(port)\n console.log('port', port, '->', r.success ? 'ok' : r.error.issues[0].message)\n}",
|
|
83
|
+
"name": "Custom Validation"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "schema-walk",
|
|
87
|
+
"code": "// Traverse a schema tree with walk() to extract field metadata.\nimport { s } from '@vielzeug/spell'\n\nconst Order = s.object({\n id: s.string().uuid(),\n amount: s.number().positive(),\n customer: s.object({\n email: s.string().email(),\n name: s.string().min(1),\n }),\n tags: s.array(s.string()).optional(),\n})\n\n// Collect every field name and whether it is optional.\nconst fields: { name: string; required: boolean }[] = []\n\nOrder.walk({\n object(node) {\n for (const [key, child] of Object.entries(node.shape)) {\n fields.push({ name: key, required: !child.isOptional })\n child.walk(this)\n }\n },\n // unknown() catches any kind without a handler; omitting it returns null instead of throwing\n unknown() {},\n})\n\nconsole.log('Fields:')\nfields.forEach(f => console.log(' ', f.name, f.required ? '(required)' : '(optional)'))\nconsole.log('Total:', fields.length)",
|
|
88
|
+
"name": "Schema Traversal"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "string-validation",
|
|
92
|
+
"code": "// Reuse one stateful regex safely across repeated parses in the browser REPL.\nimport { s } from '@vielzeug/spell'\n\nconst HexColor = s.string().regex(/#[0-9a-f]{6}/gy)\n\nfor (const value of ['#ff8800', '#ff8800', 'oops']) {\n const result = HexColor.safeParse(value)\n console.log(value, '=>', result.success)\n}",
|
|
93
|
+
"name": "String Validation"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "wrappers-and-defaults",
|
|
97
|
+
"code": "// optional(), nullable(), default(), catch() — missing-value semantics\nimport { s } from '@vielzeug/spell'\n\n// optional: accepts undefined, passes through validation otherwise\nconst Nickname = s.string().min(2).optional().default('Guest')\n\nconsole.log(Nickname.parse(undefined)) // 'Guest'\nconsole.log(Nickname.parse('Ada')) // 'Ada'\n\n// nullable: accepts null explicitly\nconst Bio = s.string().max(200).nullable()\n\nconsole.log(Bio.parse(null)) // null\nconsole.log(Bio.parse('Loves types')) // 'Loves types'\n\n// nullish: accepts both null and undefined\nconst Avatar = s.string().url().nullish()\n\nconsole.log(Avatar.parse(null)) // null\nconsole.log(Avatar.parse(undefined)) // undefined\n\n// required(): strips undefined without removing null\nconst NullableButRequired = s.string().optional().nullable().required()\nconsole.log(NullableButRequired.parse(null)) // null\nconsole.log(NullableButRequired.safeParse(undefined).success) // false\n\n// catch(): returns a fallback when validation fails — never throws\nconst Port = s.number().int().min(1).max(65535).catch(3000)\nconsole.log(Port.parse(8080)) // 8080\nconsole.log(Port.parse('not-a-port')) // 3000",
|
|
98
|
+
"name": "Wrappers & Defaults"
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"typeSignatures": {
|
|
102
|
+
"AnySchema": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
103
|
+
"CheckContext": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
104
|
+
"FlatError": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
105
|
+
"FlatErrorFirst": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
106
|
+
"Infer": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
107
|
+
"InferInput": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
108
|
+
"InferOutput": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
109
|
+
"Issue": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
110
|
+
"IssuePath": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
111
|
+
"IssuePathSegment": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
112
|
+
"JsonSchema": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
113
|
+
"MessageFn": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
114
|
+
"Messages": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
115
|
+
"ParseContext": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
116
|
+
"ParseResult": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
117
|
+
"SchemaDescriptor": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
118
|
+
"SchemaWalker": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
119
|
+
"StandardSchemaV1": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
120
|
+
"SyncParsable": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
121
|
+
"ValidateResult": "export type {\n AnySchema,\n CheckContext,\n FlatError,\n FlatErrorFirst,\n Infer,\n InferInput,\n InferOutput,\n Issue,\n IssuePath,\n IssuePathSegment,\n JsonSchema,\n MessageFn,\n Messages,\n ParseContext,\n ParseResult,\n SchemaDescriptor,\n SchemaWalker,\n StandardSchemaV1,\n SyncParsable,\n ValidateResult,\n} from './core';",
|
|
122
|
+
"ErrorCode": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
123
|
+
"joinIssuePath": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
124
|
+
"Schema": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
125
|
+
"SpellDefinitionError": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
126
|
+
"SpellError": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
127
|
+
"SpellValidationError": "export { ErrorCode, joinIssuePath, Schema, SpellDefinitionError, SpellError, SpellValidationError } from './core';",
|
|
128
|
+
"createParseContext": "export { createParseContext, type DeepPartial } from './messages';",
|
|
129
|
+
"DeepPartial": "export { createParseContext, type DeepPartial } from './messages';",
|
|
130
|
+
"s": "export { s } from './s';"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "// Tempo keeps this re-export so all consumers share one Temporal implementation and version.\nexport { Temporal } from '@js-temporal/polyfill';\nexport { inTimeZone, toInstant } from './_convert';\nexport { endOf, startOf } from './boundary';\nexport { classifyExpiry, timeDiff } from './classify';\nexport { clamp, contains, isAfter, isBefore, isSame } from './compare';\nexport { difference, isValid, now, nowInstant, parse, shift } from './core';\nexport {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';\nexport {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';\nexport { dateRange, recurrence } from './range';\nexport type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Tempo — Temporal date and time utilities\ndescription: Explicit Temporal parsing, timezone-safe arithmetic, and localized date/time formatting for TypeScript.\npackage: tempo\ncategory: time\nkeywords: [temporal, date-time, timezone, formatting, arithmetic, dst, intl]\nrelated: [rune, vault]\nexports: [Temporal, parse, now, nowInstant, isValid, toInstant, inTimeZone, shift, difference, isBefore, isAfter, isSame, contains, clamp, startOf, endOf, dateRange, recurrence, format, formatParts, formatRange, formatRangeParts, formatInstant, formatZoned, formatRelative, parseDuration, formatDuration, classifyExpiry, timeDiff, humanize, TempoError, TempoInvalidInputError, TempoInvalidTzError, TempoMissingTzError, TempoUnsupportedInputError]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"tempo\" />\n\n## Why Tempo?\n\nDate/time bugs come from treating an instant and a wall-clock value as interchangeable. Tempo requires an explicit parse target and requires `timeZone` whenever a wall-clock value becomes an instant.\n\n```ts\n// Before\nconst reminder = new Date(meeting.getTime() - 15 * 60_000);\n\n// After\nimport { parse, shift } from '@vielzeug/tempo';\n\nconst localMeeting = parse('2026-03-21T10:30:00', { as: 'plainDateTime' });\nconst reminder = shift(localMeeting, { minutes: -15 }, { timeZone: 'America/New_York' });\n```\n\n| Feature | Tempo | date-fns | Native Date |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"tempo\" type=\"size\" /> | ~10 kB | 0 kB |\n| Zero dependencies | <ore-icon name=\"x\" size=\"16\"></ore-icon> `@js-temporal/polyfill` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Explicit wall-time conversion | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| DST-safe arithmetic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual | Manual |\n| Localized formatting | <ore-icon name=\"check\" size=\"16\"></ore-icon> `Intl` | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Tempo when** you need Temporal values, explicit timezone rules, and DST-safe operations.\n\n**Consider native `Date` when** your data is only elapsed milliseconds and you do not need calendar or timezone behavior.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/tempo\n```\n\n```sh [npm]\nnpm install @vielzeug/tempo\n```\n\n```sh [yarn]\nyarn add @vielzeug/tempo\n```\n\n:::\n\n## Quick Start\n\nParse a wall-clock input explicitly, attach its timezone, then format it for a user.\n\n```ts\nimport { format, parse, shift } from '@vielzeug/tempo';\n\nconst localMeeting = parse('2026-03-21T10:30:00', { as: 'plainDateTime' });\nconst reminder = shift(localMeeting, { minutes: -15 }, { timeZone: 'America/New_York' });\nconst text = format(reminder, {\n locale: 'en-US',\n pattern: 'short',\n});\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `parse()` — Requires an explicit ISO target: instant, zoned date-time, plain date-time, or plain date.\n- `toInstant()` / `inTimeZone()` — Convert wall-clock and absolute values with explicit timezone semantics.\n- `shift()` / `difference()` — Apply DST-safe arithmetic across instant, zoned, and wall-clock inputs.\n- `contains()` / `clamp()` — Compare normalized ranges with optional calendar-unit precision.\n- `startOf()` / `endOf()` — Resolve timezone-aware calendar boundaries, including configurable week starts.\n- `dateRange()` / `recurrence()` — Generate validated, lazy zoned calendar sequences.\n- `format()` / `formatRelative()` / `formatDuration()` — Render localized values through `Intl`, including calendar-aware relative months and years.\n- `classifyExpiry()` — Classify fixed elapsed-time thresholds without month or year approximation.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Rune](/rune/) — format stable Temporal timestamps before writing structured log records.\n- [Vault](/vault/) — derive explicit expiry moments before storing records with TTL policies.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Tempo — API Reference\ndescription: Reference for Tempo Temporal parsing, conversion, arithmetic, comparison, sequence, formatting, and classification APIs.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `parse()` | Parse ISO text to an explicit Temporal kind | Sync | `as` is required |\n| `isValid()` | Narrow an unknown runtime value to `TimeInput` | Sync | Does not parse strings |\n| `toInstant()` | Resolve a value as an absolute instant | Sync | Plain values require `timeZone` |\n| `inTimeZone()` | Project a value to a zone | Sync | Preserves instant, changes wall-clock fields |\n| `shift()` / `difference()` | Timezone-aware arithmetic | Sync | Calendar operations require a timezone |\n| `isBefore()` / `isAfter()` / `isSame()` | Compare absolute values or calendar units | Sync | Plain values require `timeZone` |\n| `contains()` / `clamp()` | Compare normalized ranges | Sync | Bounds normalize automatically |\n| `startOf()` / `endOf()` | Resolve calendar boundaries | Sync | Weeks default to Monday |\n| `dateRange()` / `recurrence()` | Generate lazy zoned sequences | Sync | Steps and intervals must advance |\n| `classifyExpiry()` | Classify fixed elapsed-time thresholds | Sync | Months and years are rejected |\n| `format()` family | Localized and machine formatting | Sync | Use `timeZone`, not `tz` |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/tempo` | Tempo utilities, errors, types, and shared `Temporal` namespace |\n\n## Core Functions\n\n### `parse(input, { as })`\n\n```ts\nparse(input: string, options: { as: 'instant' }): Temporal.Instant;\nparse(input: string, options: { as: 'zonedDateTime' }): Temporal.ZonedDateTime;\nparse(input: string, options: { as: 'plainDateTime' }): Temporal.PlainDateTime;\nparse(input: string, options: { as: 'plainDate' }): Temporal.PlainDate;\n```\n\nParses an ISO 8601 string as the requested temporal kind.\n\n**Parameters**\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `input` | `string` | ISO 8601 input |\n| `options.as` | `ParseAs` | Required result kind |\n\n**Returns:** Requested Temporal value.\n\n**Example:**\n\n```ts\nimport { parse } from '@vielzeug/tempo';\n\nconst instant = parse('2026-03-21T10:15:30Z', { as: 'instant' });\n```\n\n---\n\n### `isValid(value)`\n\n```ts\nisValid(value: unknown): value is TimeInput;\n```\n\nReturns whether `value` is a Tempo-supported Temporal value. It does not parse ISO strings.\n\n**Example:**\n\n```ts\nimport { isValid, parse } from '@vielzeug/tempo';\n\nconst value: unknown = parse('2026-03-21T10:15:30Z', { as: 'instant' });\nconst valid = isValid(value); // true\n```\n\n---\n\n### `now({ timeZone })` / `nowInstant()`\n\n```ts\nnow(options: { timeZone: string }): Temporal.ZonedDateTime;\nnowInstant(): Temporal.Instant;\n```\n\nReturns current zoned or absolute time.\n\n**Example:**\n\n```ts\nimport { now, nowInstant } from '@vielzeug/tempo';\n\nnow({ timeZone: 'Europe/Berlin' });\nnowInstant();\n```\n\n---\n\n### `toInstant(input, options?)` / `inTimeZone(input, timeZone)`\n\n```ts\ntoInstant(input: AbsoluteTime): Temporal.Instant;\ntoInstant(input: WallTime, options: { timeZone: string } & DisambiguationOptions): Temporal.Instant;\ninTimeZone(input: TimeInput, timeZone: string): Temporal.ZonedDateTime;\n```\n\n`toInstant()` resolves wall-clock values. `inTimeZone()` projects a value into a requested timezone.\n\n**Example:**\n\n```ts\nimport { inTimeZone, parse, toInstant } from '@vielzeug/tempo';\n\nconst local = parse('2026-11-01T01:30:00', { as: 'plainDateTime' });\nconst instant = toInstant(local, { disambiguation: 'later', timeZone: 'America/New_York' });\ninTimeZone(instant, 'Europe/Berlin');\n```\n\n---\n\n## Arithmetic and Comparison\n\n### `shift(input, duration, options?)`\n\n```ts\nshift(input: Temporal.ZonedDateTime, duration: Temporal.DurationLike, options?: ShiftOptions): Temporal.ZonedDateTime;\nshift(input: Exclude<TimeInput, Temporal.ZonedDateTime>, duration: Temporal.DurationLike, options: ShiftOptions & { timeZone: string }): Temporal.ZonedDateTime;\n```\n\nAdds a duration through zoned calendar rules.\n\n**Returns:** `Temporal.ZonedDateTime`.\n\n---\n\n### `difference({ start, end, ...options })`\n\n```ts\ndifference(input: DifferenceInput): Temporal.Duration;\n```\n\nReturns the signed duration from `start` to `end` with optional Temporal rounding.\n\n**Returns:** `Temporal.Duration`.\n\n---\n\n### `isBefore()` / `isAfter()` / `isSame()`\n\n```ts\nisBefore(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;\nisAfter(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;\nisSame(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;\n```\n\nCompare absolute instants or their containing calendar units.\n\n**Returns:** A boolean comparison result.\n\n---\n\n### `contains({ value, start, end, ...options })` / `clamp({ value, start, end, ...options })`\n\n```ts\ncontains(input: ContainsInput): boolean;\nclamp(input: ClampInput & { value: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\nclamp(input: ClampInput): Temporal.Instant;\n```\n\nNormalizes reversed bounds, then checks or constrains the value.\n\n**Returns:** `contains()` returns `boolean`; `clamp()` returns the normalized value or nearest bound.\n\n---\n\n### `startOf()` / `endOf()`\n\n```ts\nstartOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;\nendOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;\n```\n\nReturns the first or last nanosecond of a timezone-aware calendar unit.\n\n**Returns:** `Temporal.ZonedDateTime`.\n\n## Calendar Sequences\n\n### `dateRange(start, end, step, options?)`\n\n```ts\ndateRange(start: TimeInput, end: TimeInput, step: Temporal.DurationLike, options?: TimeZoneOptions): Generator<Temporal.ZonedDateTime>;\n```\n\nReturns an inclusive lazy zoned range and rejects steps that do not advance time.\n\n**Returns:** `Generator<Temporal.ZonedDateTime>`.\n\n---\n\n### `recurrence(start, rule, options?)`\n\n```ts\nrecurrence(start: TimeInput, rule: RecurrenceRule, options?: TimeZoneOptions): Generator<Temporal.ZonedDateTime>;\n```\n\nReturns a count- or date-limited lazy recurrence. Intervals must be positive safe integers; counts must be non-negative safe integers.\n\n**Returns:** `Generator<Temporal.ZonedDateTime>`.\n\n## Formatting\n\n### `format(input, options?)`\n\n```ts\nformat(input: TimeInput, options?: FormatOptions): string;\n```\n\nFormats a value through `Intl.DateTimeFormat`.\n\n**Example:**\n\n```ts\nimport { format, parse } from '@vielzeug/tempo';\n\nformat(parse('2026-03-21T10:15:30Z', { as: 'instant' }), {\n locale: 'en-GB',\n pattern: 'short',\n timeZone: 'UTC',\n});\n```\n\n### `formatInstant()` / `formatZoned()` / `formatRelative()` / `formatDuration()`\n\n```ts\nformatInstant(input: TimeInput, options?: TimeZoneOptions): string;\nformatZoned(input: TimeInput, options?: TimeZoneOptions): string;\nformatRelative(input: RelativeTimeInput, options?: RelativeFormatOptions): string;\nformatDuration(input: string | Temporal.DurationLike, options?: DurationFormatOptions): string;\n```\n\n`formatInstant()` produces UTC transport text (`timeZone` needed for wall-time input, ignored for `Instant`). `formatZoned()` produces zoned ISO text (`timeZone` required for non-`ZonedDateTime` input). `formatRelative()` uses fixed units for short spans and complete calendar months or years in the requested or inferred timezone. Zoned inputs with different zones require `options.timeZone`. `formatDuration()` falls back to English when `Intl.DurationFormat` is unavailable.\n\n### `formatParts()` / `formatRange()` / `formatRangeParts()`\n\n```ts\nformatParts(input: TimeInput, options?: FormatOptions): Intl.DateTimeFormatPart[];\nformatRange(start: TimeInput, end: TimeInput, options?: FormatOptions): string;\nformatRangeParts(\n start: TimeInput,\n end: TimeInput,\n options?: FormatOptions,\n): ReturnType<Intl.DateTimeFormat['formatRangeToParts']>;\n```\n\nReturn `Intl` parts or localized range strings using `FormatOptions`.\n\n### `parseDuration()` / `humanize()`\n\n```ts\nparseDuration(input: string | Temporal.DurationLike): Temporal.Duration;\nhumanize(diff: TimeDiffResult, options?: { locale?: Intl.LocalesArgument }): string;\n```\n\n`humanize()` localizes numbers only. Unit names remain English.\n\n## Classification\n\n### `classifyExpiry({ value, thresholds, relativeTo?, timeZone? })`\n\n```ts\nclassifyExpiry<K extends string>(input: ClassifyExpiryInput<K>): K | null;\n```\n\nClassifies an expiry against fixed elapsed-time thresholds in milliseconds or larger units. Months and years throw `TempoInvalidInputError`.\n\n### `timeDiff(a, b?, options?)`\n\n```ts\ntimeDiff(a: TimeInput, b?: TimeInput, options?: TimeZoneOptions): TimeDiffResult;\n```\n\nReturns absolute calendar difference in its largest meaningful unit. Uses Temporal's calendar-aware `.since()` for correct month and year handling.\n\n## Types\n\n```ts\ntype AbsoluteTime = Temporal.Instant | Temporal.ZonedDateTime;\ntype WallTime = Temporal.PlainDate | Temporal.PlainDateTime;\ntype TimeInput = AbsoluteTime | WallTime;\ntype RelativeTimeInput = AbsoluteTime;\ntype ParseAs = 'instant' | 'plainDate' | 'plainDateTime' | 'zonedDateTime';\ntype Disambiguation = 'compatible' | 'earlier' | 'later' | 'reject';\ntype CalendarUnit = 'day' | 'month' | 'week' | 'year';\ntype BoundaryUnit = 'day' | 'hour' | 'minute' | 'month' | 'week' | 'year';\ntype WeekStartDay = 1 | 2 | 3 | 4 | 5 | 6 | 7;\ntype FormatPattern = 'date-only' | 'long' | 'medium' | 'short' | 'time-only';\ntype TempoUnit = 'day' | 'hour' | 'microsecond' | 'millisecond' | 'minute' | 'month' | 'nanosecond' | 'second' | 'week' | 'year';\ntype FixedDuration = Pick<Temporal.DurationLike, 'days' | 'hours' | 'microseconds' | 'milliseconds' | 'minutes' | 'nanoseconds' | 'seconds' | 'weeks'>;\ntype ExpiryThresholds<K extends string> = Record<K, FixedDuration>;\ntype TimeDiffUnit = Exclude<TempoUnit, 'microsecond' | 'nanosecond'>;\ntype TimeDiffResult = { unit: TimeDiffUnit; value: number };\n\ninterface TimeZoneOptions { timeZone?: string }\ninterface DisambiguationOptions { disambiguation?: Disambiguation }\ninterface ShiftOptions extends DisambiguationOptions, TimeZoneOptions {}\ninterface DifferenceInput extends DisambiguationOptions, TimeZoneOptions {\n end: TimeInput;\n largestUnit?: Temporal.DateTimeUnit;\n roundingIncrement?: number;\n roundingMode?: Temporal.RoundingMode;\n smallestUnit?: Temporal.DateTimeUnit;\n start: TimeInput;\n}\ninterface BoundaryOptions extends TimeZoneOptions { weekStartsOn?: WeekStartDay }\ninterface CompareOptions extends TimeZoneOptions { unit?: BoundaryUnit; weekStartsOn?: WeekStartDay }\ninterface ContainsInput extends CompareOptions { end: TimeInput; start: TimeInput; value: TimeInput }\ninterface ClampInput extends CompareOptions { end: TimeInput; start: TimeInput; value: TimeInput }\ntype FormatOptions =\n | { intl: Intl.DateTimeFormatOptions; locale?: Intl.LocalesArgument; pattern?: never; timeZone?: string }\n | { intl?: never; locale?: Intl.LocalesArgument; pattern?: FormatPattern; timeZone?: string };\ninterface RelativeFormatOptions extends TimeZoneOptions {\n base?: RelativeTimeInput;\n locale?: Intl.LocalesArgument;\n numeric?: Intl.RelativeTimeFormatNumeric;\n style?: Intl.RelativeTimeFormatStyle;\n}\ninterface DurationFormatOptions {\n locale?: Intl.LocalesArgument;\n style?: 'digital' | 'long' | 'narrow' | 'short';\n}\ninterface ClassifyExpiryInput<K extends string> extends TimeZoneOptions {\n value: TimeInput;\n thresholds: ExpiryThresholds<K>;\n relativeTo?: Temporal.Instant;\n}\ntype RecurrenceRule = { frequency: 'daily' | 'monthly' | 'weekly' | 'yearly'; interval?: number } &\n ({ count: number; until?: TimeInput } | { count?: number; until: TimeInput });\n```\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `TempoError` | Base Tempo error | `instanceof TempoError` narrows every subtype |\n| `TempoInvalidInputError` | Invalid parse, duration, or fixed-threshold input | Extends `TempoError` |\n| `TempoInvalidTzError` | Invalid IANA zone or offset | Extends `TempoError` |\n| `TempoMissingTzError` | Wall time without required `timeZone` | Extends `TempoError` |\n| `TempoUnsupportedInputError` | Non-Temporal input passed to conversion | Extends `TempoError` |\n\nEvery Tempo error thrown from `parse()`, `parseDuration()`, and timezone validation carries the original Temporal error on its `cause` property (ES2022). Inspect `err.cause` for root-cause details when debugging.\n",
|
|
6
|
+
"usage": "---\ntitle: Tempo — Usage Guide\ndescription: Parse explicit Temporal values, resolve wall-clock time, perform timezone-aware arithmetic, and format dates with Tempo.\n---\n\n[[toc]]\n\n## Basic Usage\n\nParse ISO input with a declared target. Convert plain values with `timeZone` before treating them as an instant.\n\n```ts\nimport { format, parse, shift } from '@vielzeug/tempo';\n\nconst local = parse('2026-03-21T10:15:30', { as: 'plainDateTime' });\nconst reminder = shift(local, { minutes: -15 }, { timeZone: 'America/New_York' });\n\nformat(reminder, { locale: 'en-US', pattern: 'short' });\n```\n\n## Parse ISO Values\n\nChoose the value your boundary actually represents. Tempo does not auto-detect ISO strings.\n\n```ts\nimport { parse } from '@vielzeug/tempo';\n\nconst occurredAt = parse('2026-03-21T10:15:30Z', { as: 'instant' });\nconst meeting = parse('2026-03-21T10:15:30+01:00[Europe/Berlin]', { as: 'zonedDateTime' });\nconst localStart = parse('2026-03-21T10:15:30', { as: 'plainDateTime' });\nconst birthday = parse('2026-03-21', { as: 'plainDate' });\n```\n\n## Convert Timezones\n\nUse `inTimeZone()` to project an absolute value. Use `toInstant()` only when resolving a wall-clock value.\n\n```ts\nimport { inTimeZone, parse, toInstant } from '@vielzeug/tempo';\n\nconst local = parse('2026-11-01T01:30:00', { as: 'plainDateTime' });\nconst firstOccurrence = toInstant(local, {\n disambiguation: 'earlier',\n timeZone: 'America/New_York',\n});\n\nconst berlin = inTimeZone(firstOccurrence, 'Europe/Berlin');\n```\n\n## Calculate and Compare\n\nUse Tempo helpers when an operation must resolve timezones, normalize ranges, or compare calendar units. Use Temporal methods directly when values are already the exact kind and timezone you need.\n\n```ts\nimport { clamp, contains, difference, parse, shift } from '@vielzeug/tempo';\n\nconst start = parse('2026-03-21T10:00:00Z', { as: 'instant' });\nconst end = parse('2026-03-21T12:00:00Z', { as: 'instant' });\nconst value = parse('2026-03-21T13:00:00Z', { as: 'instant' });\n\nconst duration = difference({ end, largestUnit: 'hour', start });\nconst isScheduled = contains({ end, start, value });\nconst bounded = clamp({ end, start, value });\n\nconst local = parse('2026-03-08T01:30:00', { as: 'plainDateTime' });\nconst tomorrow = shift(local, { days: 1 }, { timeZone: 'America/New_York' });\n```\n\n## Classify Expiry\n\nUse fixed elapsed-time thresholds in milliseconds or larger units. Handle `null` as the unclassified state instead of adding a far-future catch-all.\n\n```ts\nimport { classifyExpiry, parse } from '@vielzeug/tempo';\n\nconst status = classifyExpiry({\n relativeTo: parse('2026-06-01T00:00:00Z', { as: 'instant' }),\n thresholds: {\n expired: { days: 0 },\n critical: { days: 3 },\n warning: { days: 14 },\n },\n value: parse('2026-06-04T00:00:00Z', { as: 'instant' }),\n});\n\nconst label = status ?? 'safe';\n```\n\n## Format Values\n\nUse `format()` for UI, `formatInstant()` for transport, and `formatZoned()` for a zoned ISO string.\n\n```ts\nimport { format, formatInstant, formatRelative, formatZoned, parse } from '@vielzeug/tempo';\n\nconst instant = parse('2026-03-21T10:15:30Z', { as: 'instant' });\n\nformat(instant, { locale: 'en-GB', pattern: 'short', timeZone: 'UTC' });\nformatInstant(instant);\nformatZoned(instant, { timeZone: 'Europe/Berlin' });\nformatRelative(instant, { base: parse('2026-03-21T09:15:30Z', { as: 'instant' }) });\n```\n\n`formatRelative()` uses fixed elapsed-time units for short spans and complete calendar months or years for longer spans. Pass `timeZone` when calendar-relative output must use a specific regional calendar boundary.\n\n## Generate Calendar Sequences\n\nUse zoned values when the sequence already has a timezone. Plain and instant starts require `timeZone`.\n\n```ts\nimport { dateRange, parse, recurrence } from '@vielzeug/tempo';\n\nconst start = parse('2026-03-01T09:00:00[America/New_York]', { as: 'zonedDateTime' });\nconst end = parse('2026-03-03T09:00:00[America/New_York]', { as: 'zonedDateTime' });\n\nconst days = [...dateRange(start, end, { days: 1 })];\nconst meetings = [...recurrence(start, { count: 4, frequency: 'weekly', interval: 2 })];\n```\n\nBoth functions advance with zoned calendar arithmetic. Non-advancing range steps, invalid recurrence intervals, and negative or fractional counts throw `TempoInvalidInputError`.\n\n## Testing\n\nPin the reference instant for deterministic expiry tests.\n\n```ts\nimport { classifyExpiry, parse } from '@vielzeug/tempo';\n\nconst relativeTo = parse('2026-06-01T00:00:00Z', { as: 'instant' });\nconst value = parse('2026-05-31T00:00:00Z', { as: 'instant' });\n\nclassifyExpiry({ relativeTo, thresholds: { expired: { days: 0 } }, value });\n```\n\n## Framework Integration\n\nPass ISO strings through component props. Parse and format at the rendering boundary.\n\n::: code-group\n\n```tsx [React]\nimport { format, parse } from '@vielzeug/tempo';\n\nconst label = format(parse(iso, { as: 'instant' }), { locale: 'en-US', pattern: 'medium', timeZone: 'UTC' });\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport { format, parse } from '@vielzeug/tempo';\n\nconst props = defineProps<{ iso: string }>();\nconst label = format(parse(props.iso, { as: 'instant' }), { locale: 'en-US', pattern: 'medium', timeZone: 'UTC' });\n</script>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import { format, parse } from '@vielzeug/tempo';\n export let iso: string;\n $: label = format(parse(iso, { as: 'instant' }), { locale: 'en-US', pattern: 'medium', timeZone: 'UTC' });\n</script>\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### With Rune\n\nWrite stable UTC timestamps to structured logs.\n\n```ts\nimport { formatInstant, nowInstant } from '@vielzeug/tempo';\n\nlogger.info({ timestamp: formatInstant(nowInstant()) }, 'server started');\n```\n\n### With Vault\n\nCalculate an explicit instant before storing an expiring record.\n\n```ts\nimport { now } from '@vielzeug/tempo';\n\nconst expiresAt = now({ timeZone: 'UTC' }).add({ minutes: 30 }).toInstant();\n```\n\n## Best Practices\n\n- Parse strings with their actual temporal meaning and pass `timeZone` when resolving plain values.\n- Use `disambiguation` for DST overlap and gap handling.\n- Use Tempo helpers for timezone resolution, normalized ranges, and calendar units; use Temporal directly for already-normalized values.\n- Use fixed duration units for expiry thresholds.\n- Reject non-advancing range and recurrence configuration.\n- Store instants for transport and database values.\n- Use `formatInstant()` for machine output and `format()` for user-facing text.\n- Pass `timeZone` to `formatRelative()` when month and year boundaries must use a specific region.\n",
|
|
7
|
+
"examples": "---\ntitle: Tempo — Examples\ndescription: Practical examples and recipes for tempo.\n---\n\n## Examples\n\n- [DST-Safe Arithmetic](./examples/dst-safe-arithmetic.md)\n- [Date Ranges and Recurrence](./examples/date-ranges-and-recurrence.md)\n- [Locale Formatting](./examples/locale-formatting.md)\n- [Timezone Conversion](./examples/timezone-conversion.md)\n- [Expiry Classification](./examples/expiry-classification.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "meeting-duration",
|
|
12
|
+
"code": "import { classifyExpiry, contains, difference, format, parse, shift, toInstant } from '@vielzeug/tempo'\n\nconst local = parse('2026-03-21T10:00:00', { as: 'plainDateTime' })\nconst start = toInstant(local, { timeZone: 'America/New_York' })\nconst end = shift(start, { hours: 2 }, { timeZone: 'America/New_York' })\nconst check = parse('2026-03-21T15:00:00Z', { as: 'instant' })\n\nconsole.log('Duration:', difference({ end, largestUnit: 'hour', start }).toString())\nconsole.log('Contains check:', contains({ end, start, value: check }))\nconsole.log('New York:', format(end, { locale: 'en-US', pattern: 'short' }))\nconsole.log('Expiry:', classifyExpiry({ thresholds: { soon: { days: 3 } }, value: end }))",
|
|
13
|
+
"name": "Timezone-Aware Arithmetic and Ranges"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"typeSignatures": {
|
|
17
|
+
"Temporal": "export { Temporal } from '@js-temporal/polyfill';",
|
|
18
|
+
"inTimeZone": "export { inTimeZone, toInstant } from './_convert';",
|
|
19
|
+
"toInstant": "export { inTimeZone, toInstant } from './_convert';",
|
|
20
|
+
"endOf": "export { endOf, startOf } from './boundary';",
|
|
21
|
+
"startOf": "export { endOf, startOf } from './boundary';",
|
|
22
|
+
"classifyExpiry": "export { classifyExpiry, timeDiff } from './classify';",
|
|
23
|
+
"timeDiff": "export { classifyExpiry, timeDiff } from './classify';",
|
|
24
|
+
"clamp": "export { clamp, contains, isAfter, isBefore, isSame } from './compare';",
|
|
25
|
+
"contains": "export { clamp, contains, isAfter, isBefore, isSame } from './compare';",
|
|
26
|
+
"isAfter": "export { clamp, contains, isAfter, isBefore, isSame } from './compare';",
|
|
27
|
+
"isBefore": "export { clamp, contains, isAfter, isBefore, isSame } from './compare';",
|
|
28
|
+
"isSame": "export { clamp, contains, isAfter, isBefore, isSame } from './compare';",
|
|
29
|
+
"difference": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
30
|
+
"isValid": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
31
|
+
"now": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
32
|
+
"nowInstant": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
33
|
+
"parse": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
34
|
+
"shift": "export { difference, isValid, now, nowInstant, parse, shift } from './core';",
|
|
35
|
+
"TempoError": "export {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';",
|
|
36
|
+
"TempoInvalidInputError": "export {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';",
|
|
37
|
+
"TempoInvalidTzError": "export {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';",
|
|
38
|
+
"TempoMissingTzError": "export {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';",
|
|
39
|
+
"TempoUnsupportedInputError": "export {\n TempoError,\n TempoInvalidInputError,\n TempoInvalidTzError,\n TempoMissingTzError,\n TempoUnsupportedInputError,\n} from './errors';",
|
|
40
|
+
"format": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
41
|
+
"formatDuration": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
42
|
+
"formatInstant": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
43
|
+
"formatParts": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
44
|
+
"formatRange": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
45
|
+
"formatRangeParts": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
46
|
+
"formatRelative": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
47
|
+
"formatZoned": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
48
|
+
"humanize": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
49
|
+
"parseDuration": "export {\n format,\n formatDuration,\n formatInstant,\n formatParts,\n formatRange,\n formatRangeParts,\n formatRelative,\n formatZoned,\n humanize,\n parseDuration,\n} from './format';",
|
|
50
|
+
"dateRange": "export { dateRange, recurrence } from './range';",
|
|
51
|
+
"recurrence": "export { dateRange, recurrence } from './range';",
|
|
52
|
+
"AbsoluteTime": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
53
|
+
"BoundaryOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
54
|
+
"BoundaryUnit": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
55
|
+
"CalendarUnit": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
56
|
+
"ClampInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
57
|
+
"ClassifyExpiryInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
58
|
+
"CompareOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
59
|
+
"ContainsInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
60
|
+
"DifferenceInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
61
|
+
"Disambiguation": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
62
|
+
"DisambiguationOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
63
|
+
"DurationFormatOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
64
|
+
"ExpiryThresholds": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
65
|
+
"FixedDuration": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
66
|
+
"FormatOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
67
|
+
"FormatPattern": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
68
|
+
"ParseAs": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
69
|
+
"RecurrenceRule": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
70
|
+
"RelativeFormatOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
71
|
+
"RelativeTimeInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
72
|
+
"ShiftOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
73
|
+
"TempoUnit": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
74
|
+
"TimeDiffResult": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
75
|
+
"TimeDiffUnit": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
76
|
+
"TimeInput": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
77
|
+
"TimeZoneOptions": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
78
|
+
"WallTime": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';",
|
|
79
|
+
"WeekStartDay": "export type {\n AbsoluteTime,\n BoundaryOptions,\n BoundaryUnit,\n CalendarUnit,\n ClampInput,\n ClassifyExpiryInput,\n CompareOptions,\n ContainsInput,\n DifferenceInput,\n Disambiguation,\n DisambiguationOptions,\n DurationFormatOptions,\n ExpiryThresholds,\n FixedDuration,\n FormatOptions,\n FormatPattern,\n ParseAs,\n RecurrenceRule,\n RelativeFormatOptions,\n RelativeTimeInput,\n ShiftOptions,\n TempoUnit,\n TimeDiffResult,\n TimeDiffUnit,\n TimeInput,\n TimeZoneOptions,\n WallTime,\n WeekStartDay,\n} from './types';"
|
|
80
|
+
}
|
|
81
|
+
}
|