@vielzeug/codex 26.10.0 → 26.10.2
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 +1 -1
- package/data/catalog.json +56 -60
- package/data/llms-full.txt +1473 -1069
- package/data/llms.txt +6 -6
- package/data/manifest.json +1 -1
- package/data/packages/arsenal.json +13 -13
- package/data/packages/assay.json +4 -4
- package/data/packages/clockwork.json +4 -4
- package/data/packages/codex.json +3 -3
- package/data/packages/coins.json +4 -4
- package/data/packages/conduit.json +4 -4
- package/data/packages/courier.json +4 -4
- package/data/packages/dnd.json +10 -10
- package/data/packages/familiar.json +4 -4
- package/data/packages/flux.json +4 -4
- package/data/packages/focus.json +8 -5
- package/data/packages/forge.json +4 -4
- package/data/packages/gesture.json +4 -4
- package/data/packages/herald.json +12 -12
- package/data/packages/illusionist.json +5 -5
- package/data/packages/keymap.json +9 -9
- package/data/packages/ledger.json +4 -4
- package/data/packages/lingua.json +4 -4
- package/data/packages/mesh.json +5 -5
- package/data/packages/necromancer.json +5 -5
- package/data/packages/orbit.json +7 -7
- package/data/packages/ore.json +5 -5
- package/data/packages/postmaster.json +4 -4
- package/data/packages/prism.json +50 -42
- package/data/packages/pulse.json +8 -8
- package/data/packages/refine.json +3 -3
- package/data/packages/ripple.json +4 -4
- package/data/packages/rune.json +9 -9
- package/data/packages/sandbox.json +4 -4
- package/data/packages/scout.json +7 -7
- package/data/packages/scroll.json +10 -10
- package/data/packages/sentinel.json +4 -4
- package/data/packages/sigil.json +6 -6
- package/data/packages/sourcerer.json +4 -4
- package/data/packages/spell.json +9 -9
- package/data/packages/tandem.json +5 -5
- package/data/packages/tavern.json +12 -12
- package/data/packages/tempo.json +4 -4
- package/data/packages/vault.json +13 -13
- package/data/packages/ward.json +9 -9
- package/data/packages/wayfinder.json +17 -17
- package/data/refine.json +259 -87
- package/data/search.json +249 -249
- package/dist/http.js +2 -2
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/refine.d.ts +1 -1
- package/dist/refine.js +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/refine.js +1 -1
- package/package.json +2 -2
- package/skills/vielzeug/SKILL.md +57 -57
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"apiSource": "export { createBus } from './bus';\nexport { BusDisposedError, HeraldError } from './errors';\nexport type {\n Bus,\n BusOptions,\n EventKey,\n EventMap,\n HeraldEvent,\n Listener,\n SubscribeOptions,\n Unsubscribe,\n WaitAnyResult,\n} from './types';\n",
|
|
3
3
|
"docs": {
|
|
4
|
-
"index": "---\ntitle: Herald
|
|
5
|
-
"api": "---\ntitle: Herald
|
|
6
|
-
"usage": "---\ntitle: Herald
|
|
7
|
-
"examples": "---\ntitle: Herald
|
|
4
|
+
"index": "---\ntitle: 'Herald: Typed event bus for TypeScript'\ndescription: Typed synchronous event delivery with wildcard subscriptions, one-shot waits, tracing, and AbortSignal lifecycle.\npackage: herald\ncategory: events\nkeywords: [event-bus, typed-events, pub-sub, async-wait, abort-signal]\nrelated: [ripple, wayfinder, familiar]\nexports: [createBus, HeraldError, BusDisposedError]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"herald\" />\n\n## Why Herald?\n\nRaw event emitters lose payload inference and leave one-shot waiting, cancellation, diagnostics, and teardown to every caller. Herald keeps events temporal: use [Ripple](/ripple/) when you need retained state.\n\n```ts\n// Before\nconst listeners = new Set<(payload: unknown) => void>();\nlisteners.add((payload) => loadProfile((payload as { id: string }).id));\n\n// After\nimport { createBus } from '@vielzeug/herald';\n\ninterface AppEvents {\n 'user:login': { id: string };\n}\n\nfunction loadProfile(id: string): void {\n console.log(id);\n}\n\nconst bus = createBus<AppEvents>();\nbus.on('user:login', ({ id }) => loadProfile(id));\n```\n\nDelivery is synchronous and temporal: events are not retained or replayed. Listener failures do not stop later listeners; `emit()` rethrows the first failure after dispatch, while `tap()` exposes every failure for diagnostics.\n\n| Feature | Herald | mitt | EventEmitter3 |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"herald\" type=\"size\" /> | ~200 B | ~1.5 kB |\n| Typed payloads | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> |\n| One-shot async wait | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| AbortSignal lifecycle | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Runtime activity tracing | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <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 Herald when** modules need typed temporal event delivery with owned lifecycle.\n\n**Consider Ripple when** consumers need current state and replayed values.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/herald\n```\n\n```sh [npm]\nnpm install @vielzeug/herald\n```\n\n```sh [yarn]\nyarn add @vielzeug/herald\n```\n\n:::\n\n## Quick Start\n\n```ts\nimport { createBus } from '@vielzeug/herald';\n\ninterface AppEvents {\n 'user:login': { id: string };\n 'user:logout': void;\n}\n\nconst bus = createBus<AppEvents>();\nconst stop = bus.on('user:login', ({ id }) => console.log(id));\n\nbus.emit('user:login', { id: '42' });\nstop();\nbus.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `on()` / `once()`: typed subscriptions with explicit teardown\n- `onAny()`: cross-cutting event observation\n- `tap()`: observe bus activity for logging and diagnostics\n- `wait()` / `waitAny()`: one-shot async coordination\n- `listenerCount()` / `wildcardCount()` / `eventNames()`: listener inspection\n- `error` tap events: isolate listener failures without interrupting delivery\n- `AbortSignal`: subscription, wait, tap, and disposal ownership\n- `createTestBus()`: emitted-payload recording for tests\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- [Ripple](/ripple/): retained reactive state.\n- [Wayfinder](/wayfinder/): route lifecycle events.\n- [Familiar](/familiar/): worker completion events.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: 'Herald: API Reference'\ndescription: Reference for typed synchronous events, subscriptions, waits, tracing, lifecycle, and test helpers.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution | Common gotcha |\n| --- | --- | --- | --- |\n| `createBus()` | Create a typed temporal event bus | Sync | Events are not retained or replayed |\n| `emit()` | Deliver one event | Sync | Returns `void`; rethrows the first listener failure after dispatch |\n| `on()` / `once()` | Subscribe to one event | Sync | Keep the unsubscribe function or pass a signal |\n| `onAny()` | Subscribe to every event | Sync | Payload is `unknown` |\n| `wait()` / `waitAny()` | Await future one-shot events | Async | Disposal and external abort reject the promise |\n| `tap()` | Observe bus runtime activity | Sync | Tap-handler failures are swallowed |\n| `createTestBus()` | Record emitted payloads | Sync | Import from `/testing` |\n\n## Package Entry Points\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/herald` | Bus factory, errors, and public bus types |\n| `@vielzeug/herald/testing` | `createTestBus()` and `TestBus` |\n\n## `createBus()`\n\n```ts\nfunction createBus<T extends EventMap = Record<string, unknown>>(\n options?: BusOptions,\n): Bus<T>\n```\n\nCreates a synchronous bus for future event delivery. Construction has no external side effects.\n\n```ts\nimport { createBus } from '@vielzeug/herald';\n\ninterface Events {\n count: number;\n ready: void;\n}\n\nconst bus = createBus<Events>({ maxListeners: 20, name: 'worker' });\nbus.emit('count', 1);\nbus.emit('ready');\nbus.dispose();\n```\n\n### `BusOptions`\n\n```ts\ntype BusOptions = {\n maxListeners?: number;\n name?: string;\n};\n```\n\n| Field | Description |\n| --- | --- |\n| `maxListeners` | Development warning threshold applied separately to one event's listeners and wildcard listeners. Omit to disable the check. |\n| `name` | Display name included in disposal errors and listener-threshold warnings. |\n\n## `Bus`\n\n```ts\ntype Bus<T extends EventMap> = {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n emit<K extends EventKey<T>>(event: K, ...args: T[K] extends void ? [] : [payload: T[K]]): void;\n eventNames(): EventKey<T>[];\n listenerCount(event?: EventKey<T>): number;\n on<K extends EventKey<T>>(event: K, listener: Listener<T[K]>, options?: SubscribeOptions): Unsubscribe;\n onAny(listener: (event: EventKey<T>, payload: unknown) => void, options?: SubscribeOptions): Unsubscribe;\n once<K extends EventKey<T>>(event: K, listener: Listener<T[K]>, options?: { signal?: AbortSignal }): Unsubscribe;\n tap(handler: (event: HeraldEvent<T>) => void, options?: { signal?: AbortSignal }): Unsubscribe;\n wait<K extends EventKey<T>>(event: K, options?: { signal?: AbortSignal }): Promise<T[K]>;\n waitAny<const K extends readonly EventKey<T>[]>(\n events: K,\n options?: { signal?: AbortSignal },\n ): Promise<WaitAnyResult<T, K>>;\n wildcardCount(): number;\n};\n```\n\n### `emit()`\n\nCalls a snapshot of event-specific listeners synchronously, then wildcard listeners, then emits an `emit` trace event. It always returns `void`. Synchronous listener failures produce `error` trace events without stopping delivery; after dispatch, the first failure is rethrown.\n\nAfter disposal, emission is a no-op.\n\n### `on()` and `once()`\n\n```ts\ntype Listener<T> = (payload: T) => void;\ntype SubscribeOptions = { signal?: AbortSignal };\ntype Unsubscribe = () => void;\n```\n\n`on()` returns an idempotent unsubscribe function. `once()` is the one-shot variant: it unsubscribes before invoking the listener. An already-aborted signal creates no subscription.\n\n### `onAny()`\n\nReceives `(event, payload)` after event-specific listeners and supports `signal`. Wildcard listeners are excluded from `listenerCount()` and reported by `wildcardCount()`.\n\n### `wait()`\n\nResolves with the next payload for one event. It rejects with the external signal's reason when aborted, or `BusDisposedError` when bus disposal wins. Earlier emissions are not replayed.\n\n### `waitAny()`\n\nAccepts one or more event keys. It resolves once with a typed `{ event, payload }` discriminated union and removes every losing subscription.\n\n```ts\nconst result = await bus.waitAny(['count', 'ready']);\nif (result.event === 'count') console.log(result.payload);\n```\n\n### Listener inspection\n\n- `listenerCount(event)` returns specific listeners for one key.\n- `listenerCount()` returns all specific listeners.\n- `wildcardCount()` returns `onAny()` listeners.\n- `eventNames()` returns keys with active specific listeners.\n\n### `tap()`\n\nObserves runtime activity without affecting delivery. It returns an idempotent unsubscribe function and accepts an ownership signal. Tap-handler errors are swallowed.\n\n```ts\nconst stop = bus.tap((event) => {\n if (event.type === 'error') recordListenerFailure(event.error);\n});\n```\n\n### Disposal\n\n`dispose()` is permanent and idempotent. It emits a final `dispose` trace event, aborts `disposalSignal` with `BusDisposedError`, rejects pending waits, removes subscriptions, and clears taps. `[Symbol.dispose]()` delegates to it.\n\n## Runtime Events\n\n```ts\ntype HeraldEvent<T extends EventMap = EventMap> =\n | { readonly event: EventKey<T>; readonly listeners: number; readonly payload: unknown; readonly type: 'emit' }\n | { readonly event: EventKey<T>; readonly type: 'subscribe' }\n | { readonly event: EventKey<T>; readonly type: 'unsubscribe' }\n | { readonly type: 'subscribe-any' }\n | { readonly type: 'unsubscribe-any' }\n | { readonly error: unknown; readonly event: EventKey<T>; readonly type: 'error' }\n | { readonly type: 'dispose' };\n```\n\n`listeners` on an `emit` event counts every specific and wildcard listener invoked, including listeners that threw. Runtime events intentionally contain no timestamp; observers can add one using their own clock.\n\n## Shared Types\n\n```ts\ntype EventMap = object;\ntype EventKey<T extends EventMap> = Extract<keyof T, string>;\n\ntype WaitAnyResult<T extends EventMap, K extends readonly EventKey<T>[]> = {\n [I in keyof K]: K[I] extends EventKey<T>\n ? { event: K[I]; payload: T[K[I]] }\n : never;\n}[number];\n```\n\nInterfaces and object type aliases are valid event maps. Only string keys participate in the bus API.\n\n## Testing\n\n### `createTestBus()`\n\n```ts\nimport { createTestBus } from '@vielzeug/herald/testing';\n\nfunction createTestBus<T extends EventMap = Record<string, unknown>>(\n options?: BusOptions,\n): TestBus<T>\n```\n\nCreates a regular bus that records every dispatched payload through a `tap()` observer, including dispatches whose listeners throw.\n\n### `TestBus`\n\n```ts\ntype TestBus<T extends EventMap> = Bus<T> & {\n allEmitted(): { [K in EventKey<T>]?: T[K][] };\n emitted<K extends EventKey<T>>(event: K): T[K][];\n emittedCount<K extends EventKey<T>>(event: K): number;\n reset(): void;\n};\n```\n\n`emitted()` and `allEmitted()` return array snapshots. `reset()` clears records without affecting subscriptions. Disposal clears records and the underlying bus. To prevent prototype mutation in the returned plain object, `allEmitted()` omits records named `__proto__`, `constructor`, or `prototype`; `emitted(event)` remains available for those keys.\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `BusDisposedError` | Bus disposal aborts `disposalSignal` or interrupts `wait()` / `waitAny()` | Configured bus name appears in the message |\n| `HeraldError` | Base class for Herald-originated errors | Use `instanceof HeraldError` |\n",
|
|
6
|
+
"usage": "---\ntitle: 'Herald: Usage Guide'\ndescription: Define typed events, own subscriptions, await one-shot events, trace failures, and test emissions.\n---\n\n[[toc]]\n\n## Basic Usage\n\nUse an interface or object type whose string keys are event names and whose values are payloads. Use `void` or `undefined` for payloadless events.\n\n```ts\nimport { createBus } from '@vielzeug/herald';\n\ninterface AppEvents {\n 'cart:updated': { count: number };\n 'user:logout': void;\n}\n\nusing bus = createBus<AppEvents>({ name: 'app' });\nconst stop = bus.on('cart:updated', ({ count }) => console.log(count));\n\nbus.emit('cart:updated', { count: 1 });\nbus.emit('user:logout');\nstop();\n```\n\n`emit()` calls listeners synchronously and returns `void`. Event-specific listeners run before wildcard listeners. Events emitted without listeners are discarded.\n\n## Own Subscription Lifetimes\n\nKeep the returned unsubscribe function or bind a subscription to an `AbortSignal`. `once()` unsubscribes before invoking the listener.\n\n```ts\nconst controller = new AbortController();\n\nbus.on('cart:updated', renderCart, { signal: controller.signal });\nbus.once('user:logout', clearSession);\n\ncontroller.abort();\n```\n\nAn already-aborted signal creates no subscription and returns an idempotent no-op unsubscribe function.\n\n## Observe Every Event\n\nUse `onAny()` for cross-cutting application behavior. Wildcard listeners receive the event key and untyped payload after event-specific listeners.\n\n```ts\nconst stop = bus.onAny((event, payload) => {\n audit(event, payload);\n});\n\nstop();\n```\n\n`wildcardCount()` reports wildcard listeners separately from `listenerCount()`.\n\n## Await One Event\n\n`wait()` resolves on the next matching event. `waitAny()` accepts one or more keys and returns a discriminated union.\n\n```ts\nconst cart = await bus.wait('cart:updated', { signal: AbortSignal.timeout(5_000) });\nconst result = await bus.waitAny(['cart:updated', 'user:logout'], {\n signal: AbortSignal.timeout(5_000),\n});\n\nif (result.event === 'cart:updated') console.log(result.payload.count);\n```\n\nThese methods do not replay earlier events. External cancellation rejects with the signal reason. Bus disposal rejects pending waits with `BusDisposedError`.\n\n## Handle Listener Failures\n\nListener exceptions do not prevent remaining specific or wildcard listeners from running. Each failure produces a `error` tap event, then `emit()` rethrows the first error after dispatch.\n\n```ts\nconst stopTrace = bus.tap((event) => {\n if (event.type === 'error') {\n recordListenerFailure(event.error);\n }\n});\n\nstopTrace();\n```\n\nHerald captures synchronous listener throws only, and `tap()` does not suppress the error rethrown after dispatch. A listener that starts asynchronous work must handle its own rejected promises.\n\n## Trace Bus Activity\n\n`tap()` observes subscriptions, unsubscriptions, wildcard subscriptions, emissions, listener failures, and disposal without changing bus behavior. Tap-handler failures are swallowed.\n\n```ts\nconst stopTrace = bus.tap((event) => {\n recordHeraldEvent(event);\n}, { signal: owner.signal });\n```\n\nAn `emit` trace includes the event key, payload, and number of specific plus wildcard listeners invoked. Trace events do not include timestamps; add them in the observer if needed.\n\n## Inspect Listeners\n\n```ts\nbus.listenerCount('cart:updated'); // specific listeners for this key\nbus.listenerCount(); // all specific listeners\nbus.wildcardCount(); // onAny listeners\nbus.eventNames(); // keys with active specific listeners\n```\n\n`eventNames()` does not include events that only have wildcard listeners.\n\n## Dispose the Bus\n\n`dispose()` is permanent and idempotent. It emits a final tap event, aborts `disposalSignal` with `BusDisposedError`, clears all listeners and taps, and rejects pending waits. Later subscriptions and emissions are no-ops.\n\n```ts\nusing scopedBus = createBus<AppEvents>({ name: 'checkout' });\nscopedBus.disposalSignal.addEventListener('abort', () => releaseOwnedWork());\n```\n\nThe configured name appears in disposal errors and listener-threshold warnings.\n\n## Test Emissions\n\nImport test helpers from the focused testing entry point.\n\n```ts\nimport { createTestBus } from '@vielzeug/herald/testing';\n\nusing testBus = createTestBus<AppEvents>();\ntestBus.emit('cart:updated', { count: 2 });\n\nexpect(testBus.emitted('cart:updated')).toEqual([{ count: 2 }]);\nexpect(testBus.emittedCount('cart:updated')).toBe(1);\nexpect(testBus.allEmitted()).toEqual({ 'cart:updated': [{ count: 2 }] });\n\ntestBus.reset();\n```\n\nReturned recordings are snapshots. `reset()` clears recording history without removing listeners; disposal clears both.\n\n## Working with Other Vielzeug Libraries\n\nUse Herald for temporal facts, Ripple for retained state, Rune for structured logging inside `tap()`, and Postmaster when work must survive reloads or retry durably.\n\n## Best Practices\n\n- Define one explicit event map per module or application boundary.\n- Emit facts, not mutable retained state.\n- Keep synchronous listeners short.\n- Handle rejected promises inside asynchronous listener work.\n- Keep unsubscribe functions or pass ownership signals.\n- Use `wait()` and `waitAny()` only for future one-shot events.\n- Install a `tap()` observer when listener failures must be visible.\n- Dispose owner-scoped buses.\n",
|
|
7
|
+
"examples": "---\ntitle: 'Herald: Examples'\ndescription: Recipes for typed delivery, waits, lifecycle ownership, diagnostics, bridging, and testing.\n---\n\n## Examples\n\n- [Standalone Bus](./examples/standalone-entry.md)\n- [Module-Level Bus](./examples/module-level-bus.md)\n- [Await a One-Time Event](./examples/awaiting-a-one-time-event.md)\n- [Inspect Listener Counts](./examples/inspecting-listener-counts.md)\n- [Observe Listener Failures](./examples/custom-error-boundary.md)\n- [Handle Disposal in Async Code](./examples/handling-disposal-in-async-code.md)\n- [Scope a Bus to a Request](./examples/request-scoping.md)\n- [Consume Ongoing Events](./examples/streaming-with-events.md)\n- [Bridge Buses Explicitly](./examples/bus-bridging-with-pipeevents.md)\n- [Test with `createTestBus()`](./examples/testing-with-createtestbus.md)\n"
|
|
8
8
|
},
|
|
9
9
|
"examples": [
|
|
10
10
|
{
|
|
11
11
|
"id": "abort-signal",
|
|
12
|
-
"code": "import { createBus, BusDisposedError } from '@vielzeug/herald'\n\n// Demonstrate AbortSignal auto-unsubscribe and BusDisposedError\nconst bus = createBus()\n\nconst controller = new AbortController()\nconst { signal } = controller\n\nbus.on('message', (msg) => {\n console.log('listener received:', msg)\n}, { signal })\n\nbus.emit('message', 'first') // fires\nbus.emit('message', 'second') // fires\n\ncontroller.abort() // removes the listener\n\nbus.emit('message', 'third') // ignored
|
|
12
|
+
"code": "import { createBus, BusDisposedError } from '@vielzeug/herald'\n\n// Demonstrate AbortSignal auto-unsubscribe and BusDisposedError\nconst bus = createBus()\n\nconst controller = new AbortController()\nconst { signal } = controller\n\nbus.on('message', (msg) => {\n console.log('listener received:', msg)\n}, { signal })\n\nbus.emit('message', 'first') // fires\nbus.emit('message', 'second') // fires\n\ncontroller.abort() // removes the listener\n\nbus.emit('message', 'third') // ignored: no listeners\nconsole.log('listeners after abort:', bus.listenerCount())\n\n// BusDisposedError: pending wait() rejects when bus is disposed\nconst bus2 = createBus()\n\nvoid bus2.wait('done').catch((err) => {\n if (err instanceof BusDisposedError) {\n console.log('BusDisposedError caught:', err.message)\n }\n})\n\nbus2.dispose()",
|
|
13
13
|
"name": "AbortSignal & BusDisposedError"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"id": "async-wait",
|
|
17
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\n// Await a single event with wait(), race multiple with waitAny()\nconst bus = createBus()\n\n// Emit after a short delay\nsetTimeout(() => bus.emit('user:login', { userId: '42', email: 'alice@example.com' }), 50)\nsetTimeout(() => bus.emit('theme:change', 'dark'), 80)\n\n// wait() resolves on the next emit of that event\nconst loginPayload = await bus.wait('user:login')\nconsole.log('got login:', loginPayload.userId)\n\n// Reset and race two events
|
|
17
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\n// Await a single event with wait(), race multiple with waitAny()\nconst bus = createBus()\n\n// Emit after a short delay\nsetTimeout(() => bus.emit('user:login', { userId: '42', email: 'alice@example.com' }), 50)\nsetTimeout(() => bus.emit('theme:change', 'dark'), 80)\n\n// wait() resolves on the next emit of that event\nconst loginPayload = await bus.wait('user:login')\nconsole.log('got login:', loginPayload.userId)\n\n// Reset and race two events: whichever fires first wins\nsetTimeout(() => bus.emit('user:login', { userId: '1', email: 'a@b.com' }), 20)\nsetTimeout(() => bus.emit('theme:change', 'light'), 60)\n\nconst result = await bus.waitAny(['user:login', 'theme:change'])\n\nif (result.event === 'user:login') {\n console.log('login won the race:', result.payload.userId)\n} else {\n console.log('theme won the race:', result.payload)\n}\n\nbus.dispose()",
|
|
18
18
|
"name": "Async wait()"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"id": "basic-bus",
|
|
22
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\n// Typed pub/sub with on(), emit(), and once()\nconst bus = createBus()\n\nconst unsub = bus.on('user:login', ({ userId, email }) => {\n console.log('login:', userId, email)\n})\n\nbus.once('user:logout', () => {\n console.log('logged out (fires once)')\n})\n\nbus.emit('user:login', { userId: '1', email: 'alice@example.com' })\nbus.emit('user:logout')\nbus.emit('user:logout') // once() already removed; no output\n\nunsub()\nbus.emit('user:login', { userId: '2', email: 'bob@example.com' }) // no output
|
|
22
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\n// Typed pub/sub with on(), emit(), and once()\nconst bus = createBus()\n\nconst unsub = bus.on('user:login', ({ userId, email }) => {\n console.log('login:', userId, email)\n})\n\nbus.once('user:logout', () => {\n console.log('logged out (fires once)')\n})\n\nbus.emit('user:login', { userId: '1', email: 'alice@example.com' })\nbus.emit('user:logout')\nbus.emit('user:logout') // once() already removed; no output\n\nunsub()\nbus.emit('user:login', { userId: '2', email: 'bob@example.com' }) // no output: unsubscribed\n\nbus.dispose()",
|
|
23
23
|
"name": "Basic Bus"
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"id": "bus-basics",
|
|
27
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\n// on() returns an unsubscribe handle; emit() delivers to all active listeners\nconst bus = createBus()\n\nconst unsubLogin = bus.on('user:login', (payload) => {\n console.log('login:', payload.name, '(' + payload.userId + ')')\n})\n\nbus.on('user:logout', () => console.log('logged out'))\nbus.on('notification', (msg) => console.log('notification:', msg))\n\nbus.emit('user:login', { userId: '123', name: 'Alice' })\nbus.emit('notification', 'welcome back!')\n\nunsubLogin()\n\nbus.emit('user:login', { userId: '456', name: 'Bob' }) // no output
|
|
27
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\n// on() returns an unsubscribe handle; emit() delivers to all active listeners\nconst bus = createBus()\n\nconst unsubLogin = bus.on('user:login', (payload) => {\n console.log('login:', payload.name, '(' + payload.userId + ')')\n})\n\nbus.on('user:logout', () => console.log('logged out'))\nbus.on('notification', (msg) => console.log('notification:', msg))\n\nbus.emit('user:login', { userId: '123', name: 'Alice' })\nbus.emit('notification', 'welcome back!')\n\nunsubLogin()\n\nbus.emit('user:login', { userId: '456', name: 'Bob' }) // no output: unsubscribed\nconsole.log('active listeners:', bus.listenerCount())\n\nbus.dispose()",
|
|
28
28
|
"name": "createBus - Basics"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"id": "disposal-signal",
|
|
32
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\n// disposalSignal ties an external subscription's lifetime to this bus\nconst mainBus = createBus()\nconst childBus = createBus()\n\n// Pass disposalSignal so the child listener is removed when mainBus disposes\nchildBus.on('data:update', ({ value }) => {\n console.log('child received:', value)\n}, { signal: mainBus.disposalSignal })\n\nconsole.log('child listeners before dispose:', childBus.listenerCount())\n\nchildBus.emit('data:update', { value: 10 }) // fires
|
|
32
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\n// disposalSignal ties an external subscription's lifetime to this bus\nconst mainBus = createBus()\nconst childBus = createBus()\n\n// Pass disposalSignal so the child listener is removed when mainBus disposes\nchildBus.on('data:update', ({ value }) => {\n console.log('child received:', value)\n}, { signal: mainBus.disposalSignal })\n\nconsole.log('child listeners before dispose:', childBus.listenerCount())\n\nchildBus.emit('data:update', { value: 10 }) // fires: listener is active\n\nmainBus.dispose() // disposalSignal fires: child listener auto-removed\n\nconsole.log('child listeners after mainBus.dispose():', childBus.listenerCount())\n\nchildBus.emit('data:update', { value: 20 }) // no output: listener is gone\nconsole.log('mainBus.disposed:', mainBus.disposed)\nconsole.log('disposalSignal aborted:', mainBus.disposalSignal.aborted)",
|
|
33
33
|
"name": "disposalSignal"
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -49,17 +49,17 @@
|
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"id": "test-bus",
|
|
52
|
-
"code": "import { createTestBus } from '@vielzeug/herald/testing'\n\n// TestBus wraps a normal bus with emission recording
|
|
52
|
+
"code": "import { createTestBus } from '@vielzeug/herald/testing'\n\n// TestBus wraps a normal bus with emission recording: no mocking required\nconst bus = createTestBus()\n\nconst stop = bus.on('cart:updated', (cart) => console.log('handler saw total:', cart.total))\n\nbus.emit('cart:updated', { items: 1, total: 19.99 })\nbus.emit('cart:updated', { items: 2, total: 39.98 })\nbus.emit('user:logout')\n\nconsole.log('emitted count:', bus.emittedCount('cart:updated')) // 2\nconsole.log('all payloads:', bus.emitted('cart:updated'))\nconsole.log('every recorded event:', bus.allEmitted())\n\nstop()\nbus.emit('cart:updated', { items: 3, total: 59.97 }) // still recorded\n\nconsole.log('after unsubscribe:', bus.emitted('cart:updated'))\n\nbus.dispose()",
|
|
53
53
|
"name": "createTestBus()"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"id": "wait-any",
|
|
57
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\n// waitAny resolves with { event, payload } for whichever event fires first\nconst bus = createBus()\n\nasync function watchNextSessionEvent() {\n console.log('waiting for first session event...')\n\n const result = await bus.waitAny(['user:login', 'user:logout', 'session:expired'])\n\n if (result.event === 'user:login') {\n console.log('login:', result.payload.email, '(id:', result.payload.userId + ')')\n } else if (result.event === 'session:expired') {\n console.log('session expired:', result.payload.reason)\n } else {\n console.log('user logged out')\n }\n}\n\nvoid watchNextSessionEvent()\n\nsetTimeout(() => {\n bus.emit('user:login', { email: 'alice@example.com', userId: '42' })\n bus.emit('user:logout') // ignored
|
|
57
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\n// waitAny resolves with { event, payload } for whichever event fires first\nconst bus = createBus()\n\nasync function watchNextSessionEvent() {\n console.log('waiting for first session event...')\n\n const result = await bus.waitAny(['user:login', 'user:logout', 'session:expired'])\n\n if (result.event === 'user:login') {\n console.log('login:', result.payload.email, '(id:', result.payload.userId + ')')\n } else if (result.event === 'session:expired') {\n console.log('session expired:', result.payload.reason)\n } else {\n console.log('user logged out')\n }\n}\n\nvoid watchNextSessionEvent()\n\nsetTimeout(() => {\n bus.emit('user:login', { email: 'alice@example.com', userId: '42' })\n bus.emit('user:logout') // ignored: waitAny already resolved\n}, 30)",
|
|
58
58
|
"name": "waitAny()"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
"id": "wildcard-listeners",
|
|
62
|
-
"code": "import { createBus } from '@vielzeug/herald'\n\ntype AppEvents = {\n 'user:login': { userId: string }\n 'user:logout': void\n 'cart:updated': { items: number }\n}\n\nconst bus = createBus<AppEvents>({ name: 'app' })\n\n// onAny receives every event
|
|
62
|
+
"code": "import { createBus } from '@vielzeug/herald'\n\ntype AppEvents = {\n 'user:login': { userId: string }\n 'user:logout': void\n 'cart:updated': { items: number }\n}\n\nconst bus = createBus<AppEvents>({ name: 'app' })\n\n// onAny receives every event: useful for logging, analytics, tracing\nconst unsub = bus.onAny((event, payload) => {\n console.log('[audit]', event, payload)\n})\n\nconsole.log('wildcard listeners:', bus.wildcardCount()) // 1\n\nbus.emit('user:login', { userId: 'alice' })\nbus.emit('cart:updated', { items: 3 })\nbus.emit('user:logout')\n\n// Remove the wildcard listener\nunsub()\nconsole.log('after unsub:', bus.wildcardCount()) // 0\n\nbus.dispose()",
|
|
63
63
|
"name": "onAny + wildcardCount()"
|
|
64
64
|
}
|
|
65
65
|
],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"apiSource": "export * from './commerce/commerce';\nexport * from './date/date';\nexport * from './errors';\nexport * from './factory';\nexport * from './finance/finance';\nexport * from './internet/internet';\nexport * from './location/location';\nexport * from './lorem/lorem';\nexport * from './person/person';\nexport * from './seed/create-seed';\nexport * from './seed/mulberry32';\nexport * from './types';\n",
|
|
3
3
|
"docs": {
|
|
4
|
-
"index": "---\ntitle: Illusionist
|
|
5
|
-
"api": "---\ntitle: Illusionist — API Reference\ndescription: createIllusion, all category functions, seed utilities, types, and errors.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution | Common gotcha |\n| --- | --- | --- | --- |\n| `createIllusion` | Create a bound, seeded instance | Sync | Locale is fixed for the instance lifetime |\n| `person.*` | Names, gender, job titles | Sync | Locale-specific datasets (`en`, `de`) |\n| `internet.*` | Emails, URLs, IPs, HTTP metadata | Sync | `ip()` defaults to IPv4 |\n| `commerce.*` | Product names, prices | Sync | `price()` returns coins `Money` |\n| `date.*` | Past, future, recent, birthday | Sync | Returns tempo `Temporal` objects |\n| `finance.*` | IBANs, cards, crypto addresses | Sync | IBANs pass mod-97; cards pass Luhn |\n| `location.*` | Cities, streets, GPS | Sync | Locale-specific datasets |\n| `lorem.*` | Words, sentences, paragraphs | Sync | Word pool is fixed |\n| `system.*` | Files, semver, UUIDs, ports | Sync | `port()` avoids well-known ports by default |\n| `createSeed` | Build a `RandomSource` from a seed | Sync | Non-finite numeric seeds throw |\n| `mulberry32` | Low-level 32-bit PRNG | Sync | Not cryptographically secure |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/illusionist` | `createIllusion`, `createSeed`, `mulberry32`, `Illusionist`, `IllusionistOptions`, `IllusionistLocale`, error classes |\n| `@vielzeug/illusionist/locales` | Tree-shakeable barrel — `en`, `de` locale objects |\n| `@vielzeug/illusionist/locales/en` | English locale object only |\n| `@vielzeug/illusionist/locales/de` | German locale object only |\n\n## createIllusion\n\n```ts\nfunction createIllusion(options: IllusionistOptions): Illusionist;\n```\n\nCreates a bound instance. All categories share one seeded random source and one locale. Locale data is included only when its dedicated subpath is imported; the root entry does not statically import a default locale. For dynamic switching, use `await import('@vielzeug/illusionist/locales')` before calling this synchronous factory.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `seed` | `number \\| string` | `undefined` | Seed for deterministic output. Omit for cryptographic randomness. |\n| `locale` | `IllusionistLocale` | Required | Explicit locale object for locale-aware categories. |\n\n**Returns:** `Illusionist` — an object with `person`, `internet`, `commerce`, `date`, `finance`, `location`, `lorem`, and `system` categories, plus readonly `seed` and `locale` values. It owns no external resources and requires no cleanup.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 12345, locale: en });\nillusion.person.fullName();\n```\n\n---\n\n## Person\n\n### `person.firstName()`\n\n```ts\nfunction firstName(): string;\n```\n\nReturns a random first name from the locale dataset.\n\n### `person.lastName()`\n\n```ts\nfunction lastName(): string;\n```\n\nReturns a random last name from the locale dataset.\n\n### `person.fullName()`\n\n```ts\nfunction fullName(): string;\n```\n\nReturns a first and last name separated by a space.\n\n### `person.gender()`\n\n```ts\nfunction gender(): string;\n```\n\nReturns a random gender label from the locale dataset.\n\n### `person.prefix()`\n\n```ts\nfunction prefix(): string;\n```\n\nReturns a random name prefix (e.g. `Mr.`, `Dr.`).\n\n### `person.suffix()`\n\n```ts\nfunction suffix(): string;\n```\n\nReturns a random name suffix. Returns an empty string when the locale dataset has no suffixes.\n\n### `person.jobTitle()`\n\n```ts\nfunction jobTitle(): string;\n```\n\nReturns a job area and job type joined by a space.\n\n---\n\n## Internet\n\n### `internet.email()`\n\n```ts\nfunction email(): string;\n```\n\nReturns an email of the form `firstname.lastname@domain.tld`.\n\n### `internet.username()`\n\n```ts\nfunction username(): string;\n```\n\nReturns either a random alphanumeric string or a `firstname.lastname` pattern.\n\n### `internet.password(options?)`\n\n```ts\nfunction password(options?: PasswordOptions): string;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `length` | `number` | `12` | Password length. |\n| `memorable` | `boolean` | `false` | Build from name fragments and digits. |\n\nReturns a password mixing upper/lowercase letters, digits, and special characters.\n\n### `internet.url()`\n\n```ts\nfunction url(): string;\n```\n\nReturns a URL of the form `protocol://sub.domain.tld/path/...`.\n\n### `internet.domainName()`\n\n```ts\nfunction domainName(): string;\n```\n\nReturns a domain of the form `domain.tld`.\n\n### `internet.ip(version?)`\n\n```ts\nfunction ip(version?: 4 | 6): string;\n```\n\nReturns an IPv4 or IPv6 address. Defaults to IPv4.\n\n### `internet.mac()`\n\n```ts\nfunction mac(): string;\n```\n\nReturns a MAC address of the form `XX:XX:XX:XX:XX:XX`.\n\n### `internet.userAgent()`\n\n```ts\nfunction userAgent(): string;\n```\n\nReturns a random user agent string.\n\n### `internet.httpMethod()`\n\n```ts\nfunction httpMethod(): string;\n```\n\nReturns a random HTTP method.\n\n### `internet.statusCode()`\n\n```ts\nfunction statusCode(): number;\n```\n\nReturns a random HTTP status code.\n\n### `internet.mimeType()`\n\n```ts\nfunction mimeType(): string;\n```\n\nReturns a random MIME type.\n\n---\n\n## Commerce\n\n### `commerce.productAdjective()`\n\n```ts\nfunction productAdjective(): string;\n```\n\nReturns a random product adjective.\n\n### `commerce.productMaterial()`\n\n```ts\nfunction productMaterial(): string;\n```\n\nReturns a random product material.\n\n### `commerce.productNoun()`\n\n```ts\nfunction productNoun(): string;\n```\n\nReturns a random product noun.\n\n### `commerce.productName()`\n\n```ts\nfunction productName(): string;\n```\n\nReturns an adjective, material, and noun joined by spaces.\n\n### `commerce.department()`\n\n```ts\nfunction department(): string;\n```\n\nReturns a random department name.\n\n### `commerce.price(options?)`\n\n```ts\nfunction price(options?: PriceOptions): Money;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `0.01` | Minimum price. |\n| `max` | `number` | `1000` | Maximum price. |\n| `currency` | `'USD' \\| 'EUR' \\| 'GBP'` | `'USD'` | Currency code. |\n\nReturns a coins `Money` value with two decimal places.\n\n### `commerce.productDescription()`\n\n```ts\nfunction productDescription(): string;\n```\n\nReturns one or two sentences describing a product.\n\n---\n\n## Date\n\nAll date functions return tempo `Temporal` objects.\n\n### `date.past(options?)`\n\n```ts\nfunction past(options?: { years?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date in the past within `years` (default `1`) from `ref` (default now).\n\n### `date.future(options?)`\n\n```ts\nfunction future(options?: { years?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date in the future within `years` (default `1`) from `ref` (default now).\n\n### `date.recent(options?)`\n\n```ts\nfunction recent(options?: { days?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date within `days` (default `1`) in the past from `ref` (default now).\n\n### `date.between(from, to)`\n\n```ts\nfunction between(from: Temporal.ZonedDateTime, to: Temporal.ZonedDateTime): Temporal.ZonedDateTime;\n```\n\nReturns a date between `from` and `to`. Returns `from` if `from` is after `to`.\n\n### `date.birthday(options?)`\n\n```ts\nfunction birthday(options?: { minAge?: number; maxAge?: number; ref?: Temporal.ZonedDateTime }): Temporal.PlainDate;\n```\n\nReturns a `PlainDate` with a random age between `minAge` (default `18`) and `maxAge` (default `80`).\n\n### `date.weekday(locale?)`\n\n```ts\nfunction weekday(locale?: string): string;\n```\n\nReturns a random weekday name. Uses the instance locale unless overridden.\n\n### `date.month(locale?)`\n\n```ts\nfunction month(locale?: string): string;\n```\n\nReturns a random month name. Uses the instance locale unless overridden.\n\n---\n\n## Finance\n\n### `finance.amount(options?)`\n\n```ts\nfunction amount(options?: AmountOptions): Money;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `100` | Minimum amount. |\n| `max` | `number` | `10000` | Maximum amount. |\n| `currency` | `'USD' \\| 'EUR' \\| 'GBP'` | `'USD'` | Currency code. |\n\nReturns a coins `Money` value with two decimal places.\n\n### `finance.iban(countryCode?)`\n\n```ts\nfunction iban(countryCode?: string): string;\n```\n\nReturns an IBAN. Pass a country code to fix the country; otherwise a random supported country is chosen. The check digits are computed so the IBAN passes mod-97 validation.\n\n### `finance.bic()`\n\n```ts\nfunction bic(): string;\n```\n\nReturns a BIC/SWIFT code of 8 or 11 characters.\n\n### `finance.creditCardNumber(type?)`\n\n```ts\nfunction creditCardNumber(type?: 'visa' | 'mastercard' | 'amex'): string;\n```\n\nReturns a card number with a valid Luhn check digit. Amex returns 15 digits; others return 16.\n\n### `finance.creditCardCVV(type?)`\n\n```ts\nfunction creditCardCVV(type?: 'visa' | 'mastercard' | 'amex'): string;\n```\n\nReturns a CVV. Amex returns 4 digits; others return 3.\n\n### `finance.bitcoinAddress()`\n\n```ts\nfunction bitcoinAddress(): string;\n```\n\nReturns a Bitcoin address with a `1`, `3`, or `bc1` prefix.\n\n### `finance.ethereumAddress()`\n\n```ts\nfunction ethereumAddress(): string;\n```\n\nReturns a 42-character Ethereum address prefixed with `0x`.\n\n### `finance.transactionType()`\n\n```ts\nfunction transactionType(): string;\n```\n\nReturns a random transaction type label.\n\n### `finance.bank()`\n\n```ts\nfunction bank(): string;\n```\n\nReturns a random bank name.\n\n---\n\n## Location\n\n### `location.city()`\n\n```ts\nfunction city(): string;\n```\n\nReturns a random city from the locale dataset.\n\n### `location.street()`\n\n```ts\nfunction street(): string;\n```\n\nReturns a random street from the locale dataset.\n\n### `location.streetAddress()`\n\n```ts\nfunction streetAddress(): string;\n```\n\nReturns a house number (1–999) followed by a street name.\n\n### `location.zipCode()`\n\n```ts\nfunction zipCode(): string;\n```\n\nReturns a ZIP code matching the locale's pattern.\n\n### `location.state()`\n\n```ts\nfunction state(): string;\n```\n\nReturns a random state or region from the locale dataset.\n\n### `location.country()`\n\n```ts\nfunction country(): string;\n```\n\nReturns a random country from the locale dataset.\n\n### `location.latitude()`\n\n```ts\nfunction latitude(): number;\n```\n\nReturns a latitude in the range `[-90, 90]`.\n\n### `location.longitude()`\n\n```ts\nfunction longitude(): number;\n```\n\nReturns a longitude in the range `[-180, 180]`.\n\n### `location.nearbyGPSCoordinate(ref?)`\n\n```ts\nfunction nearbyGPSCoordinate(ref?: Coordinate): Coordinate;\n```\n\nReturns a coordinate within ~1 degree of `ref`. When `ref` is omitted, a random coordinate is used as the base.\n\n---\n\n## Lorem\n\n### `lorem.word()`\n\n```ts\nfunction word(): string;\n```\n\nReturns a single random word.\n\n### `lorem.words(count?)`\n\n```ts\nfunction words(count?: number): string;\n```\n\nReturns `count` (default `3`) space-joined words.\n\n### `lorem.sentence(wordCount?)`\n\n```ts\nfunction sentence(wordCount?: number): string;\n```\n\nReturns a sentence of `wordCount` words (default 6–12) with a capital first letter and trailing period.\n\n### `lorem.sentences(count?)`\n\n```ts\nfunction sentences(count?: number): string;\n```\n\nReturns `count` (default `3`) space-joined sentences.\n\n### `lorem.paragraph(sentenceCount?)`\n\n```ts\nfunction paragraph(sentenceCount?: number): string;\n```\n\nReturns a paragraph of `sentenceCount` sentences (default 3–7).\n\n### `lorem.paragraphs(count?)`\n\n```ts\nfunction paragraphs(count?: number): string;\n```\n\nReturns `count` (default `3`) newline-joined paragraphs.\n\n### `lorem.slug(wordCount?)`\n\n```ts\nfunction slug(wordCount?: number): string;\n```\n\nReturns a hyphen-joined slug of `wordCount` (default `3`) words.\n\n### `lorem.lines(count?)`\n\n```ts\nfunction lines(count?: number): string;\n```\n\nReturns `count` (default `5`) newline-joined lines, each a sentence.\n\n---\n\n## System\n\n### `system.fileExtension()`\n\n```ts\nfunction fileExtension(): string;\n```\n\nReturns a random file extension.\n\n### `system.fileName()`\n\n```ts\nfunction fileName(): string;\n```\n\nReturns a random file name with extension.\n\n### `system.filePath()`\n\n```ts\nfunction filePath(): string;\n```\n\nReturns a path with 1–4 directory segments and a file name.\n\n### `system.mimeType()`\n\n```ts\nfunction mimeType(): string;\n```\n\nReturns a random MIME type.\n\n### `system.semver(options?)`\n\n```ts\nfunction semver(options?: { maxMajor?: number; includePrerelease?: boolean }): string;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `maxMajor` | `number` | `20` | Maximum major version. |\n| `includePrerelease` | `boolean` | `false` | Occasionally append a prerelease label. |\n\nReturns a semver string.\n\n### `system.uuid()`\n\n```ts\nfunction uuid(): string;\n```\n\nReturns a random UUID via `crypto.randomUUID()`. **Not deterministic** — ignores the seeded `RandomSource`. Use only when uniqueness matters more than reproducibility.\n\n### `system.port(options?)`\n\n```ts\nfunction port(options?: { min?: number; max?: number }): number;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `1024` | Minimum port. |\n| `max` | `number` | `65535` | Maximum port. |\n\nReturns a random port number.\n\n### `system.cron()`\n\n```ts\nfunction cron(): string;\n```\n\nReturns a random cron expression from common patterns.\n\n### `system.process()`\n\n```ts\nfunction process(): string;\n```\n\nReturns a random process name of the form `prefix_suffix`.\n\n---\n\n## Seed\n\n```ts\nimport { createSeed, mulberry32 } from '@vielzeug/illusionist';\n```\n\n### `createSeed(seed?)`\n\n```ts\nfunction createSeed(seed?: number | string): RandomSource;\n```\n\nCreates a `RandomSource` from a seed. Number seeds are used directly as mulberry32 state. String seeds are hashed to a 32-bit integer. Omit the seed for cryptographic randomness via `crypto.getRandomValues`. Throws `IllusionistSeedError` for non-finite numeric seeds.\n\n```ts\nconst a = createSeed(12345); // deterministic\nconst b = createSeed('hello'); // deterministic (hashed)\nconst c = createSeed(); // cryptographic\n```\n\n### `mulberry32(seed)`\n\n```ts\nfunction mulberry32(seed: number): RandomSource;\n```\n\nLow-level 32-bit PRNG. Not cryptographically secure. Returns a `RandomSource` producing floats in `[0, 1)`.\n\n---\n\n## Types\n\n```ts\ntype PersonLocaleData = {\n readonly firstNameFemale: readonly string[];\n readonly firstNameMale: readonly string[];\n readonly gender: readonly string[];\n readonly jobAreas: readonly string[];\n readonly jobTypes: readonly string[];\n readonly lastName: readonly string[];\n readonly prefix: readonly string[];\n readonly suffix: readonly string[];\n};\n\ntype LocationLocaleData = {\n readonly cities: readonly string[];\n readonly countries: readonly string[];\n readonly states: readonly string[];\n readonly streets: readonly string[];\n readonly zipPattern: string;\n};\n\ntype IllusionistLocale = {\n readonly code: string;\n readonly person: PersonLocaleData;\n readonly location: LocationLocaleData;\n};\n\ntype IllusionistOptions = {\n seed?: number | string;\n locale: IllusionistLocale;\n};\n\ntype Illusionist = {\n readonly person: typeof person;\n readonly internet: typeof internet;\n readonly commerce: typeof commerce;\n readonly date: typeof date;\n readonly finance: typeof finance;\n readonly location: typeof location;\n readonly lorem: typeof lorem;\n readonly system: typeof system;\n readonly seed: number | string | undefined;\n readonly locale: IllusionistLocale;\n};\n\ntype Coordinate = {\n lat: number;\n lng: number;\n};\n\ntype PasswordOptions = {\n length?: number;\n memorable?: boolean;\n};\n\ntype PriceOptions = {\n readonly min?: number;\n readonly max?: number;\n readonly currency?: 'USD' | 'EUR' | 'GBP';\n};\n\ntype AmountOptions = {\n readonly min?: number;\n readonly max?: number;\n readonly currency?: 'USD' | 'EUR' | 'GBP';\n};\n\n// Re-exported from @vielzeug/arsenal\ntype RandomSource = {\n next(): number; // finite float in [0, 1)\n};\n```\n\nCategory helpers throw `RangeError` when a custom source returns a non-finite value or a value outside `[0, 1)`.\n\n## Errors\n\nAll errors extend `IllusionistError`, which extends `Error`. Use `instanceof IllusionistError` to catch any illusionist-originated error.\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `IllusionistError` | Base class for all illusionist errors | `name`, `message` |\n| `IllusionistSeedError` | Non-finite numeric seed passed to `createSeed` (`NaN`, `Infinity`, `-Infinity`) | `name`, `message` |\n\n```ts\nimport { IllusionistError, IllusionistSeedError, createSeed } from '@vielzeug/illusionist';\n\ntry {\n createSeed(Number.NaN);\n} catch (error) {\n if (error instanceof IllusionistSeedError) {\n console.log(error.message);\n }\n}\n```\n",
|
|
6
|
-
"usage": "---\ntitle: Illusionist
|
|
7
|
-
"examples": "---\ntitle: Illusionist
|
|
4
|
+
"index": "---\ntitle: 'Illusionist: Fake Data Generator for TypeScript'\ndescription: Typed, deterministic, locale-aware fake data generator with a seeded PRNG, eight data categories, and zero external runtime dependencies.\npackage: illusionist\ncategory: data\nkeywords: [fake-data, mock, seed, faker, test-fixtures, deterministic]\nexports: [createIllusion, createSeed, mulberry32]\nrelated: [arsenal, coins, tempo]\nenvironments: [browser, node, ssr]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"illusionist\" />\n\n## Why Illusionist?\n\nIllusionist generates realistic fake data from a single seeded random source. The same seed always produces the same output, so test fixtures and snapshots stay reproducible across runs, machines, and CI. Every category shares one bound instance with one locale, so a person, their email, and their address stay internally consistent.\n\n```ts\n// Before\nconst user = {\n name: 'Test User',\n email: 'test@example.com',\n address: '123 Main St',\n};\n\n// After\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 12345, locale: en });\n\nconst user = {\n name: illusion.person.fullName(),\n email: illusion.internet.email(),\n address: illusion.location.streetAddress(),\n};\n\n```\n\n| Feature | Illusionist | Faker.js | @faker-js/faker |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"illusionist\" type=\"size\" /> | External dependency | External dependency |\n| Zero external dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Seeded determinism | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Partial | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Locale-aware datasets | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| TypeScript-native types | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Illusionist when** test fixtures, mock APIs, or database seeds must be realistic and reproducible from a single seed value.\n\n**Consider @faker-js/faker when** you need a large catalog of locale datasets beyond `en` and `de` or a community plugin ecosystem.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/illusionist\n```\n\n```sh [npm]\nnpm install @vielzeug/illusionist\n```\n\n```sh [yarn]\nyarn add @vielzeug/illusionist\n```\n\n:::\n\n## Quick Start\n\nCreate a bound instance with a seed and locale. All categories share that seed, so output is deterministic.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 12345, locale: en });\n\nillusion.person.fullName(); // 'Ashley Harris'\nillusion.internet.email(); // 'samantha.sanchez@mail.com'\nillusion.commerce.price(); // Money { amount: 76640n, currency: USD }\nillusion.date.past({ years: 2 }); // Temporal.ZonedDateTime\n\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- **`person`**: names, gender, prefixes, suffixes, job titles\n- **`internet`**: emails, usernames, passwords, URLs, IPs, MACs, HTTP metadata\n- **`commerce`**: product names, departments, prices as coins `Money`\n- **`date`**: past, future, recent, between, birthday as tempo `Temporal` objects\n- **`finance`**: amounts, IBANs, BICs, credit cards, crypto addresses\n- **`location`**: cities, streets, states, countries, GPS coordinates\n- **`lorem`**: words, sentences, paragraphs, slugs\n- **`system`**: file paths, semver, UUIDs, ports, cron expressions\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- [Arsenal](/arsenal/): random primitives (`RandomSource`, `uuid`) that Illusionist builds on.\n- [Coins](/coins/): exact money type returned by `commerce.price()` and `finance.amount()`.\n- [Tempo](/tempo/): `Temporal` date utilities returned by every `date` function.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: 'Illusionist: API Reference'\ndescription: createIllusion, all category functions, seed utilities, types, and errors.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution | Common gotcha |\n| --- | --- | --- | --- |\n| `createIllusion` | Create a bound, seeded instance | Sync | Locale is fixed for the instance lifetime |\n| `person.*` | Names, gender, job titles | Sync | Locale-specific datasets (`en`, `de`) |\n| `internet.*` | Emails, URLs, IPs, HTTP metadata | Sync | `ip()` defaults to IPv4 |\n| `commerce.*` | Product names, prices | Sync | `price()` returns coins `Money` |\n| `date.*` | Past, future, recent, birthday | Sync | Returns tempo `Temporal` objects |\n| `finance.*` | IBANs, cards, crypto addresses | Sync | IBANs pass mod-97; cards pass Luhn |\n| `location.*` | Cities, streets, GPS | Sync | Locale-specific datasets |\n| `lorem.*` | Words, sentences, paragraphs | Sync | Word pool is fixed |\n| `system.*` | Files, semver, UUIDs, ports | Sync | `port()` avoids well-known ports by default |\n| `createSeed` | Build a `RandomSource` from a seed | Sync | Non-finite numeric seeds throw |\n| `mulberry32` | Low-level 32-bit PRNG | Sync | Not cryptographically secure |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/illusionist` | `createIllusion`, `createSeed`, `mulberry32`, `Illusionist`, `IllusionistOptions`, `IllusionistLocale`, error classes |\n| `@vielzeug/illusionist/locales` | Tree-shakeable barrel: `en`, `de` locale objects |\n| `@vielzeug/illusionist/locales/en` | English locale object only |\n| `@vielzeug/illusionist/locales/de` | German locale object only |\n\n## createIllusion\n\n```ts\nfunction createIllusion(options: IllusionistOptions): Illusionist;\n```\n\nCreates a bound instance. All categories share one seeded random source and one locale. Locale data is included only when its dedicated subpath is imported; the root entry does not statically import a default locale. For dynamic switching, use `await import('@vielzeug/illusionist/locales')` before calling this synchronous factory.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `seed` | `number \\| string` | `undefined` | Seed for deterministic output. Omit for cryptographic randomness. |\n| `locale` | `IllusionistLocale` | Required | Explicit locale object for locale-aware categories. |\n\n**Returns:** `Illusionist`: an object with `person`, `internet`, `commerce`, `date`, `finance`, `location`, `lorem`, and `system` categories, plus readonly `seed` and `locale` values. It owns no external resources and requires no cleanup.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 12345, locale: en });\nillusion.person.fullName();\n```\n\n---\n\n## Person\n\n### `person.firstName()`\n\n```ts\nfunction firstName(): string;\n```\n\nReturns a random first name from the locale dataset.\n\n### `person.lastName()`\n\n```ts\nfunction lastName(): string;\n```\n\nReturns a random last name from the locale dataset.\n\n### `person.fullName()`\n\n```ts\nfunction fullName(): string;\n```\n\nReturns a first and last name separated by a space.\n\n### `person.gender()`\n\n```ts\nfunction gender(): string;\n```\n\nReturns a random gender label from the locale dataset.\n\n### `person.prefix()`\n\n```ts\nfunction prefix(): string;\n```\n\nReturns a random name prefix (e.g. `Mr.`, `Dr.`).\n\n### `person.suffix()`\n\n```ts\nfunction suffix(): string;\n```\n\nReturns a random name suffix. Returns an empty string when the locale dataset has no suffixes.\n\n### `person.jobTitle()`\n\n```ts\nfunction jobTitle(): string;\n```\n\nReturns a job area and job type joined by a space.\n\n---\n\n## Internet\n\n### `internet.email()`\n\n```ts\nfunction email(): string;\n```\n\nReturns an email of the form `firstname.lastname@domain.tld`.\n\n### `internet.username()`\n\n```ts\nfunction username(): string;\n```\n\nReturns either a random alphanumeric string or a `firstname.lastname` pattern.\n\n### `internet.password(options?)`\n\n```ts\nfunction password(options?: PasswordOptions): string;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `length` | `number` | `12` | Password length. |\n| `memorable` | `boolean` | `false` | Build from name fragments and digits. |\n\nReturns a password mixing upper/lowercase letters, digits, and special characters.\n\n### `internet.url()`\n\n```ts\nfunction url(): string;\n```\n\nReturns a URL of the form `protocol://sub.domain.tld/path/...`.\n\n### `internet.domainName()`\n\n```ts\nfunction domainName(): string;\n```\n\nReturns a domain of the form `domain.tld`.\n\n### `internet.ip(version?)`\n\n```ts\nfunction ip(version?: 4 | 6): string;\n```\n\nReturns an IPv4 or IPv6 address. Defaults to IPv4.\n\n### `internet.mac()`\n\n```ts\nfunction mac(): string;\n```\n\nReturns a MAC address of the form `XX:XX:XX:XX:XX:XX`.\n\n### `internet.userAgent()`\n\n```ts\nfunction userAgent(): string;\n```\n\nReturns a random user agent string.\n\n### `internet.httpMethod()`\n\n```ts\nfunction httpMethod(): string;\n```\n\nReturns a random HTTP method.\n\n### `internet.statusCode()`\n\n```ts\nfunction statusCode(): number;\n```\n\nReturns a random HTTP status code.\n\n### `internet.mimeType()`\n\n```ts\nfunction mimeType(): string;\n```\n\nReturns a random MIME type.\n\n---\n\n## Commerce\n\n### `commerce.productAdjective()`\n\n```ts\nfunction productAdjective(): string;\n```\n\nReturns a random product adjective.\n\n### `commerce.productMaterial()`\n\n```ts\nfunction productMaterial(): string;\n```\n\nReturns a random product material.\n\n### `commerce.productNoun()`\n\n```ts\nfunction productNoun(): string;\n```\n\nReturns a random product noun.\n\n### `commerce.productName()`\n\n```ts\nfunction productName(): string;\n```\n\nReturns an adjective, material, and noun joined by spaces.\n\n### `commerce.department()`\n\n```ts\nfunction department(): string;\n```\n\nReturns a random department name.\n\n### `commerce.price(options?)`\n\n```ts\nfunction price(options?: PriceOptions): Money;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `0.01` | Minimum price. |\n| `max` | `number` | `1000` | Maximum price. |\n| `currency` | `'USD' \\| 'EUR' \\| 'GBP'` | `'USD'` | Currency code. |\n\nReturns a coins `Money` value with two decimal places.\n\n### `commerce.productDescription()`\n\n```ts\nfunction productDescription(): string;\n```\n\nReturns one or two sentences describing a product.\n\n---\n\n## Date\n\nAll date functions return tempo `Temporal` objects.\n\n### `date.past(options?)`\n\n```ts\nfunction past(options?: { years?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date in the past within `years` (default `1`) from `ref` (default now).\n\n### `date.future(options?)`\n\n```ts\nfunction future(options?: { years?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date in the future within `years` (default `1`) from `ref` (default now).\n\n### `date.recent(options?)`\n\n```ts\nfunction recent(options?: { days?: number; ref?: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;\n```\n\nReturns a date within `days` (default `1`) in the past from `ref` (default now).\n\n### `date.between(from, to)`\n\n```ts\nfunction between(from: Temporal.ZonedDateTime, to: Temporal.ZonedDateTime): Temporal.ZonedDateTime;\n```\n\nReturns a date between `from` and `to`. Returns `from` if `from` is after `to`.\n\n### `date.birthday(options?)`\n\n```ts\nfunction birthday(options?: { minAge?: number; maxAge?: number; ref?: Temporal.ZonedDateTime }): Temporal.PlainDate;\n```\n\nReturns a `PlainDate` with a random age between `minAge` (default `18`) and `maxAge` (default `80`).\n\n### `date.weekday(locale?)`\n\n```ts\nfunction weekday(locale?: string): string;\n```\n\nReturns a random weekday name. Uses the instance locale unless overridden.\n\n### `date.month(locale?)`\n\n```ts\nfunction month(locale?: string): string;\n```\n\nReturns a random month name. Uses the instance locale unless overridden.\n\n---\n\n## Finance\n\n### `finance.amount(options?)`\n\n```ts\nfunction amount(options?: AmountOptions): Money;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `100` | Minimum amount. |\n| `max` | `number` | `10000` | Maximum amount. |\n| `currency` | `'USD' \\| 'EUR' \\| 'GBP'` | `'USD'` | Currency code. |\n\nReturns a coins `Money` value with two decimal places.\n\n### `finance.iban(countryCode?)`\n\n```ts\nfunction iban(countryCode?: string): string;\n```\n\nReturns an IBAN. Pass a country code to fix the country; otherwise a random supported country is chosen. The check digits are computed so the IBAN passes mod-97 validation.\n\n### `finance.bic()`\n\n```ts\nfunction bic(): string;\n```\n\nReturns a BIC/SWIFT code of 8 or 11 characters.\n\n### `finance.creditCardNumber(type?)`\n\n```ts\nfunction creditCardNumber(type?: 'visa' | 'mastercard' | 'amex'): string;\n```\n\nReturns a card number with a valid Luhn check digit. Amex returns 15 digits; others return 16.\n\n### `finance.creditCardCVV(type?)`\n\n```ts\nfunction creditCardCVV(type?: 'visa' | 'mastercard' | 'amex'): string;\n```\n\nReturns a CVV. Amex returns 4 digits; others return 3.\n\n### `finance.bitcoinAddress()`\n\n```ts\nfunction bitcoinAddress(): string;\n```\n\nReturns a Bitcoin address with a `1`, `3`, or `bc1` prefix.\n\n### `finance.ethereumAddress()`\n\n```ts\nfunction ethereumAddress(): string;\n```\n\nReturns a 42-character Ethereum address prefixed with `0x`.\n\n### `finance.transactionType()`\n\n```ts\nfunction transactionType(): string;\n```\n\nReturns a random transaction type label.\n\n### `finance.bank()`\n\n```ts\nfunction bank(): string;\n```\n\nReturns a random bank name.\n\n---\n\n## Location\n\n### `location.city()`\n\n```ts\nfunction city(): string;\n```\n\nReturns a random city from the locale dataset.\n\n### `location.street()`\n\n```ts\nfunction street(): string;\n```\n\nReturns a random street from the locale dataset.\n\n### `location.streetAddress()`\n\n```ts\nfunction streetAddress(): string;\n```\n\nReturns a house number (1–999) followed by a street name.\n\n### `location.zipCode()`\n\n```ts\nfunction zipCode(): string;\n```\n\nReturns a ZIP code matching the locale's pattern.\n\n### `location.state()`\n\n```ts\nfunction state(): string;\n```\n\nReturns a random state or region from the locale dataset.\n\n### `location.country()`\n\n```ts\nfunction country(): string;\n```\n\nReturns a random country from the locale dataset.\n\n### `location.latitude()`\n\n```ts\nfunction latitude(): number;\n```\n\nReturns a latitude in the range `[-90, 90]`.\n\n### `location.longitude()`\n\n```ts\nfunction longitude(): number;\n```\n\nReturns a longitude in the range `[-180, 180]`.\n\n### `location.nearbyGPSCoordinate(ref?)`\n\n```ts\nfunction nearbyGPSCoordinate(ref?: Coordinate): Coordinate;\n```\n\nReturns a coordinate within ~1 degree of `ref`. When `ref` is omitted, a random coordinate is used as the base.\n\n---\n\n## Lorem\n\n### `lorem.word()`\n\n```ts\nfunction word(): string;\n```\n\nReturns a single random word.\n\n### `lorem.words(count?)`\n\n```ts\nfunction words(count?: number): string;\n```\n\nReturns `count` (default `3`) space-joined words.\n\n### `lorem.sentence(wordCount?)`\n\n```ts\nfunction sentence(wordCount?: number): string;\n```\n\nReturns a sentence of `wordCount` words (default 6–12) with a capital first letter and trailing period.\n\n### `lorem.sentences(count?)`\n\n```ts\nfunction sentences(count?: number): string;\n```\n\nReturns `count` (default `3`) space-joined sentences.\n\n### `lorem.paragraph(sentenceCount?)`\n\n```ts\nfunction paragraph(sentenceCount?: number): string;\n```\n\nReturns a paragraph of `sentenceCount` sentences (default 3–7).\n\n### `lorem.paragraphs(count?)`\n\n```ts\nfunction paragraphs(count?: number): string;\n```\n\nReturns `count` (default `3`) newline-joined paragraphs.\n\n### `lorem.slug(wordCount?)`\n\n```ts\nfunction slug(wordCount?: number): string;\n```\n\nReturns a hyphen-joined slug of `wordCount` (default `3`) words.\n\n### `lorem.lines(count?)`\n\n```ts\nfunction lines(count?: number): string;\n```\n\nReturns `count` (default `5`) newline-joined lines, each a sentence.\n\n---\n\n## System\n\n### `system.fileExtension()`\n\n```ts\nfunction fileExtension(): string;\n```\n\nReturns a random file extension.\n\n### `system.fileName()`\n\n```ts\nfunction fileName(): string;\n```\n\nReturns a random file name with extension.\n\n### `system.filePath()`\n\n```ts\nfunction filePath(): string;\n```\n\nReturns a path with 1–4 directory segments and a file name.\n\n### `system.mimeType()`\n\n```ts\nfunction mimeType(): string;\n```\n\nReturns a random MIME type.\n\n### `system.semver(options?)`\n\n```ts\nfunction semver(options?: { maxMajor?: number; includePrerelease?: boolean }): string;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `maxMajor` | `number` | `20` | Maximum major version. |\n| `includePrerelease` | `boolean` | `false` | Occasionally append a prerelease label. |\n\nReturns a semver string.\n\n### `system.uuid()`\n\n```ts\nfunction uuid(): string;\n```\n\nReturns a random UUID via `crypto.randomUUID()`. **Not deterministic**: ignores the seeded `RandomSource`. Use only when uniqueness matters more than reproducibility.\n\n### `system.port(options?)`\n\n```ts\nfunction port(options?: { min?: number; max?: number }): number;\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `min` | `number` | `1024` | Minimum port. |\n| `max` | `number` | `65535` | Maximum port. |\n\nReturns a random port number.\n\n### `system.cron()`\n\n```ts\nfunction cron(): string;\n```\n\nReturns a random cron expression from common patterns.\n\n### `system.process()`\n\n```ts\nfunction process(): string;\n```\n\nReturns a random process name of the form `prefix_suffix`.\n\n---\n\n## Seed\n\n```ts\nimport { createSeed, mulberry32 } from '@vielzeug/illusionist';\n```\n\n### `createSeed(seed?)`\n\n```ts\nfunction createSeed(seed?: number | string): RandomSource;\n```\n\nCreates a `RandomSource` from a seed. Number seeds are used directly as mulberry32 state. String seeds are hashed to a 32-bit integer. Omit the seed for cryptographic randomness via `crypto.getRandomValues`. Throws `IllusionistSeedError` for non-finite numeric seeds.\n\n```ts\nconst a = createSeed(12345); // deterministic\nconst b = createSeed('hello'); // deterministic (hashed)\nconst c = createSeed(); // cryptographic\n```\n\n### `mulberry32(seed)`\n\n```ts\nfunction mulberry32(seed: number): RandomSource;\n```\n\nLow-level 32-bit PRNG. Not cryptographically secure. Returns a `RandomSource` producing floats in `[0, 1)`.\n\n---\n\n## Types\n\n```ts\ntype PersonLocaleData = {\n readonly firstNameFemale: readonly string[];\n readonly firstNameMale: readonly string[];\n readonly gender: readonly string[];\n readonly jobAreas: readonly string[];\n readonly jobTypes: readonly string[];\n readonly lastName: readonly string[];\n readonly prefix: readonly string[];\n readonly suffix: readonly string[];\n};\n\ntype LocationLocaleData = {\n readonly cities: readonly string[];\n readonly countries: readonly string[];\n readonly states: readonly string[];\n readonly streets: readonly string[];\n readonly zipPattern: string;\n};\n\ntype IllusionistLocale = {\n readonly code: string;\n readonly person: PersonLocaleData;\n readonly location: LocationLocaleData;\n};\n\ntype IllusionistOptions = {\n seed?: number | string;\n locale: IllusionistLocale;\n};\n\ntype Illusionist = {\n readonly person: typeof person;\n readonly internet: typeof internet;\n readonly commerce: typeof commerce;\n readonly date: typeof date;\n readonly finance: typeof finance;\n readonly location: typeof location;\n readonly lorem: typeof lorem;\n readonly system: typeof system;\n readonly seed: number | string | undefined;\n readonly locale: IllusionistLocale;\n};\n\ntype Coordinate = {\n lat: number;\n lng: number;\n};\n\ntype PasswordOptions = {\n length?: number;\n memorable?: boolean;\n};\n\ntype PriceOptions = {\n readonly min?: number;\n readonly max?: number;\n readonly currency?: 'USD' | 'EUR' | 'GBP';\n};\n\ntype AmountOptions = {\n readonly min?: number;\n readonly max?: number;\n readonly currency?: 'USD' | 'EUR' | 'GBP';\n};\n\n// Re-exported from @vielzeug/arsenal\ntype RandomSource = {\n next(): number; // finite float in [0, 1)\n};\n```\n\nCategory helpers throw `RangeError` when a custom source returns a non-finite value or a value outside `[0, 1)`.\n\n## Errors\n\nAll errors extend `IllusionistError`, which extends `Error`. Use `instanceof IllusionistError` to catch any illusionist-originated error.\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `IllusionistError` | Base class for all illusionist errors | `name`, `message` |\n| `IllusionistSeedError` | Non-finite numeric seed passed to `createSeed` (`NaN`, `Infinity`, `-Infinity`) | `name`, `message` |\n\n```ts\nimport { IllusionistError, IllusionistSeedError, createSeed } from '@vielzeug/illusionist';\n\ntry {\n createSeed(Number.NaN);\n} catch (error) {\n if (error instanceof IllusionistSeedError) {\n console.log(error.message);\n }\n}\n```\n",
|
|
6
|
+
"usage": "---\ntitle: 'Illusionist: Usage Guide'\ndescription: Generate deterministic, locale-aware fake data with Illusionist.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate an illusionist instance with `createIllusion`. Access data through the eight bound categories. Each call consumes from the shared random source, so output is deterministic for a given seed.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 12345, locale: en });\n\nillusion.person.firstName(); // 'Ashley'\nillusion.internet.username(); // 'fVEv9zc638m'\nillusion.commerce.productName(); // 'Intelligent Granite Table'\nillusion.date.recent({ days: 7 }); // Temporal.ZonedDateTime within the last week\nillusion.lorem.sentence(); // 'Enim ex non ea minim amet sint laborum proident nisi anim officia.'\n\n```\n\n## Seeded Determinism\n\nPass a number or string seed to make output reproducible. The same seed always produces the same sequence across runs, machines, and Node versions.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst a = createIllusion({ seed: 12345, locale: en });\nconst b = createIllusion({ seed: 12345, locale: en });\n\na.person.fullName() === b.person.fullName(); // true\n\nconst c = createIllusion({ seed: 'my-test-suite', locale: en });\nconst d = createIllusion({ seed: 'my-test-suite', locale: en });\n\nc.internet.email() === d.internet.email(); // true: string seeds are hashed\n```\n\nOmit the seed for cryptographic randomness backed by `crypto.getRandomValues`. Output is then non-deterministic and unsuitable for snapshots.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst random = createIllusion({ locale: en });\nrandom.person.fullName(); // different every run\n```\n\n## Locale Support\n\nImport a locale object and pass it at creation time. The `person` and `location` categories draw from that object's datasets. The `date.weekday` and `date.month` functions use its locale code.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { de, en } from '@vielzeug/illusionist/locales';\n\nconst english = createIllusion({ seed: 1, locale: en });\nconst german = createIllusion({ seed: 1, locale: de });\n\nenglish.person.firstName(); // 'Mary'\ngerman.person.firstName(); // 'Mia'\n\nenglish.location.city(); // 'Austin'\ngerman.location.city(); // 'Bremen'\n\nenglish.date.month(); // 'December'\ngerman.date.month(); // 'Dezember'\n```\n\nThe locale is fixed for the lifetime of an instance. Each locale is a separate subpath, so locale data ships only when that subpath is imported; the root package does not statically include English or German data. Create a new instance to switch locales.\n\nFor dynamic app switching, load the desired locale before calling the synchronous factory:\n\n```ts\nconst { de } = await import('@vielzeug/illusionist/locales/de');\nconst german = createIllusion({ locale: de });\n```\n\n### Custom Locales\n\nThe shipped `en` and `de` objects are just plain data that `satisfies IllusionistLocale`. Build your own the same way: import the type, assemble the `person` and `location` datasets, and pass the result to `createIllusion`. No registration step; the factory accepts any object that matches the shape.\n\n```ts\nimport { createIllusion, type IllusionistLocale } from '@vielzeug/illusionist';\n\nconst fr: IllusionistLocale = {\n code: 'fr',\n person: {\n firstNameFemale: ['Marie', 'Camille', 'Sophie'],\n firstNameMale: ['Louis', 'Hugo', 'Léo'],\n lastName: ['Martin', 'Bernard', 'Dubois'],\n gender: ['féminin', 'masculin', 'non-binaire'],\n jobAreas: ['marketing', 'ingénierie', 'ventes'],\n jobTypes: ['directeur', 'ingénieur', 'analyste'],\n prefix: ['M.', 'Mme', 'Dr.'],\n suffix: ['PhD', 'Jr.'],\n },\n location: {\n cities: ['Paris', 'Lyon', 'Marseille'],\n countries: ['France', 'Belgique', 'Suisse'],\n states: ['Île-de-France', 'Auvergne-Rhône-Alpes', 'Provence-Alpes-Côte d\\'Azur'],\n streets: ['rue de la Paix', 'avenue des Champs-Élysées', 'boulevard Saint-Germain'],\n zipPattern: '#####',\n },\n};\n\nconst illusion = createIllusion({ seed: 42, locale: fr });\n\nillusion.person.fullName(); // 'Camille Dubois'\nillusion.location.city(); // 'Marseille'\nillusion.person.jobTitle(); // 'marketing ingénieur'\n```\n\n`date.weekday()` and `date.month()` currently ship English and German name arrays only; a custom locale code falls through to the English set. For other languages, format a generated `Temporal` date with `@vielzeug/tempo`'s `format()` and your own `Intl.DateTimeFormat` options.\n\nUse `satisfies IllusionistLocale` instead of a bare type annotation to get error locality: TypeScript points at the offending field rather than the whole object.\n\n## Category Overview\n\n| Category | Example call | Returns |\n| --- |-------------------------------------| --- |\n| `person` | `illusion.person.fullName()` | `string` |\n| `internet` | `illusion.internet.email()` | `string` |\n| `commerce` | `illusion.commerce.price()` | `Money` (coins) |\n| `date` | `illusion.date.past({ years: 1 })` | `Temporal.ZonedDateTime` (tempo) |\n| `finance` | `illusion.finance.iban()` | `string` |\n| `location` | `illusion.location.streetAddress()` | `string` |\n| `lorem` | `illusion.lorem.paragraph()` | `string` |\n| `system` | `illusion.system.uuid()` | `string` |\n\n## Working with Other Vielzeug Libraries\n\nIllusionist integrates with other Vielzeug packages at the return-type level. `commerce.price()` and `finance.amount()` return coins `Money`, so you can format, add, or allocate them directly. `date` functions return tempo `Temporal` objects, so you can shift, compare, or format them.\n\n```ts\nimport { format, add, money } from '@vielzeug/coins';\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\nimport { formatZonedDateTimeISO } from '@vielzeug/tempo';\n\nconst illusion = createIllusion({ seed: 42, locale: en });\n\nconst price = illusion.commerce.price({ min: 10, max: 50, currency: 'EUR' });\nconst tax = money('5.00', price.currency);\nconst total = add(price, tax);\n\nconsole.log(format(total, { locale: 'de-DE' }));\n\nconst orderDate = illusion.date.recent({ days: 30 });\nconsole.log(formatZonedDateTimeISO(orderDate));\n```\n\n## Best Practices\n\n- Pass a seed in tests and CI; omit it only for one-off non-reproducible mocks.\n- Create one instance per test case so each test starts from a known random state.\n- Treat instances as ordinary stateful values; they own no resources and require no cleanup.\n- Fix the locale at creation time; create a new instance to switch locales rather than mixing.\n- Use string seeds for named test suites: they are self-documenting and hash to a stable number.\n- Combine `person`, `internet`, and `location` to build internally consistent mock entities.\n- Treat `Money` and `Temporal` return values as first-class: pass them to coins and tempo functions directly.\n- Avoid sharing a single instance across concurrent async tasks; each call advances the shared random source.\n- `system.uuid()` uses `crypto.randomUUID()`, not the seeded source. Do not use it in deterministic fixtures or snapshot tests.\n",
|
|
7
|
+
"examples": "---\ntitle: 'Illusionist: Examples'\ndescription: Practical examples and recipes for @vielzeug/illusionist.\n---\n\n[[toc]]\n\n## Generating Test Fixtures\n\nBuild a batch of realistic records from a fixed seed. The same seed reproduces the same fixtures in every run. For a full Vitest setup, see the [Test Fixtures recipe](./examples/test-fixtures.md).\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 'fixtures-v1', locale: en });\n\nconst users = Array.from({ length: 10 }, () => ({\n name: illusion.person.fullName(),\n email: illusion.internet.email(),\n address: illusion.location.streetAddress(),\n city: illusion.location.city(),\n zip: illusion.location.zipCode(),\n}));\n\n```\n\n## Seeded Test Data for Snapshot Testing\n\nUse a named string seed so snapshot output is stable across CI runs. Each test creates its own instance to avoid cross-test random-state drift.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\ntest('order receipt snapshot', () => {\n const illusion = createIllusion({ seed: 'order-receipt', locale: en });\n\n const order = {\n customer: illusion.person.fullName(),\n product: illusion.commerce.productName(),\n price: illusion.commerce.price({ min: 5, max: 50 }),\n date: illusion.date.recent({ days: 7 }),\n };\n\n expect(order).toMatchInlineSnapshot();\n });\n```\n\n## Locale-Specific Data (German)\n\nImport the German locale object to draw names, cities, and weekday labels from its dataset.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { de } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 42, locale: de });\n\nillusion.person.fullName(); // 'Mathilda Scholz'\nillusion.location.city(); // 'Nürnberg'\nillusion.location.zipCode(); // '15268'\nillusion.date.weekday(); // 'Donnerstag'\nillusion.date.month(); // 'März'\n\n```\n\n## E-commerce Mock Data\n\nCombine `person`, `commerce`, and `location` to build a consistent customer-order-shipping record. `commerce.price()` returns coins `Money`, so you can format it directly.\n\n```ts\nimport { format } from '@vielzeug/coins';\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 'ecommerce-mock', locale: en });\n\nconst order = {\n customer: {\n name: illusion.person.fullName(),\n email: illusion.internet.email(),\n },\n item: illusion.commerce.productName(),\n price: illusion.commerce.price({ min: 20, max: 200, currency: 'EUR' }),\n shipping: {\n address: illusion.location.streetAddress(),\n city: illusion.location.city(),\n zip: illusion.location.zipCode(),\n country: illusion.location.country(),\n },\n};\n\nconsole.log(format(order.price, { locale: 'de-DE' }));\n```\n\n## Database Seeding Pattern\n\nGenerate rows for a database seed script. Use a stable seed so the seed file is reproducible and reviewable.\n\n```ts\nimport { createIllusion } from '@vielzeug/illusionist';\nimport { en } from '@vielzeug/illusionist/locales';\n\nconst illusion = createIllusion({ seed: 'db-seed-2024', locale: en });\n\nconst products = Array.from({ length: 50 }, () => ({\n name: illusion.commerce.productName(),\n department: illusion.commerce.department(),\n price: illusion.commerce.price({ min: 1, max: 500 }),\n description: illusion.commerce.productDescription(),\n}));\n\nconst customers = Array.from({ length: 100 }, () => ({\n firstName: illusion.person.firstName(),\n lastName: illusion.person.lastName(),\n email: illusion.internet.email(),\n createdAt: illusion.date.past({ years: 2 }),\n}));\n\n```\n"
|
|
8
8
|
},
|
|
9
9
|
"examples": [
|
|
10
10
|
{
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"email": "export function email(ctx: IllusionistContext): string {\n const first = (\n pick(ctx.locale.person.firstNameFemale, ctx.source) ??\n pick(ctx.locale.person.firstNameMale, ctx.source) ??\n 'user'\n ).toLowerCase();\n const last = (pick(ctx.locale.person.lastName, ctx.source) ?? 'name').toLowerCase();\n const domain = pick(INTERNET_DATA.domains, ctx.source) ?? 'example';\n const tld = pick(INTERNET_DATA.tlds, ctx.source) ?? 'com';\n return `${first}.${last}@${domain}.${tld}`;\n}",
|
|
88
88
|
"username": "export function username(ctx: IllusionistContext): string {\n if (int(0, 1, ctx.source) === 0) {\n return alphanumeric(int(6, 12, ctx.source), ctx.source);\n }\n const first = (\n pick(ctx.locale.person.firstNameFemale, ctx.source) ??\n pick(ctx.locale.person.firstNameMale, ctx.source) ??\n 'user'\n ).toLowerCase();\n const last = (pick(ctx.locale.person.lastName, ctx.source) ?? 'name').toLowerCase();\n return `${first}.${last}`;\n}",
|
|
89
89
|
"PasswordOptions": "export type PasswordOptions = {\n /** Password length. Defaults to `12`. */\n length?: number;\n /** When `true`, builds a memorable password from name fragments and digits. */\n memorable?: boolean;\n};",
|
|
90
|
-
"password": "export function password(ctx: IllusionistContext, opts: PasswordOptions = {}): string {\n const length = opts.length ?? 12;\n\n if (opts.memorable) {\n const first = (\n pick(ctx.locale.person.firstNameFemale, ctx.source) ??\n pick(ctx.locale.person.firstNameMale, ctx.source) ??\n 'user'\n ).toLowerCase();\n const last = (pick(ctx.locale.person.lastName, ctx.source) ?? 'name').toLowerCase();\n const num = String(int(10, 99, ctx.source));\n const special = pick(SPECIAL_CHARS.split(''), ctx.source) ?? '!';\n const base = `${first}${last}${num}${special}`;\n if (base.length <= length) return base + alphanumeric(length - base.length, ctx.source);\n // Truncation would cut the special char/number
|
|
90
|
+
"password": "export function password(ctx: IllusionistContext, opts: PasswordOptions = {}): string {\n const length = opts.length ?? 12;\n\n if (opts.memorable) {\n const first = (\n pick(ctx.locale.person.firstNameFemale, ctx.source) ??\n pick(ctx.locale.person.firstNameMale, ctx.source) ??\n 'user'\n ).toLowerCase();\n const last = (pick(ctx.locale.person.lastName, ctx.source) ?? 'name').toLowerCase();\n const num = String(int(10, 99, ctx.source));\n const special = pick(SPECIAL_CHARS.split(''), ctx.source) ?? '!';\n const base = `${first}${last}${num}${special}`;\n if (base.length <= length) return base + alphanumeric(length - base.length, ctx.source);\n // Truncation would cut the special char/number: put them first, then fill from the name.\n const essential = `${num}${special}`;\n const remaining = length - essential.length;\n const namePart = remaining > 0 ? (first + last).slice(0, remaining) : '';\n const result = essential + namePart;\n\n return result.length < length ? result + alphanumeric(length - result.length, ctx.source) : result.slice(0, length);\n }\n\n const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n const lower = 'abcdefghijklmnopqrstuvwxyz';\n const digits = '0123456789';\n const pools = [upper, lower, digits, SPECIAL_CHARS];\n\n // Guarantee at least one char from each pool, then fill the rest randomly.\n const chars: string[] = [];\n for (const pool of pools) {\n chars.push(pick(pool.split(''), ctx.source)!);\n }\n const all = upper + lower + digits + SPECIAL_CHARS;\n while (chars.length < length) {\n chars.push(pick(all.split(''), ctx.source)!);\n }\n\n // Shuffle via Fisher-Yates using ctx.source.\n for (let i = chars.length - 1; i > 0; i--) {\n const j = int(0, i, ctx.source);\n [chars[i], chars[j]] = [chars[j]!, chars[i]!];\n }\n\n return chars.slice(0, length).join('');\n}",
|
|
91
91
|
"url": "export function url(ctx: IllusionistContext): string {\n const protocol = pick(INTERNET_DATA.protocols, ctx.source) ?? 'https';\n const sub = pick(['www', 'api', 'app', 'mail', 'static', 'cdn'], ctx.source) ?? 'www';\n const domain = pick(INTERNET_DATA.domains, ctx.source) ?? 'example';\n const tld = pick(INTERNET_DATA.tlds, ctx.source) ?? 'com';\n const host = `${sub}.${domain}.${tld}`;\n\n const segmentCount = int(1, 4, ctx.source);\n const segments: string[] = [];\n for (let i = 0; i < segmentCount; i++) {\n const word = pick(URL_PATH_WORDS, ctx.source) ?? 'api';\n // Occasionally append a numeric or short alphanumeric suffix to a segment.\n if (int(0, 2, ctx.source) === 0) {\n segments.push(`${word}-${alphanumeric(int(2, 5), ctx.source).toLowerCase()}`);\n } else {\n segments.push(word);\n }\n }\n\n return `${protocol}://${host}/${segments.join('/')}`;\n}",
|
|
92
92
|
"domainName": "export function domainName(ctx: IllusionistContext): string {\n const domain = pick(INTERNET_DATA.domains, ctx.source) ?? 'example';\n const tld = pick(INTERNET_DATA.tlds, ctx.source) ?? 'com';\n return `${domain}.${tld}`;\n}",
|
|
93
93
|
"ip": "export function ip(ctx: IllusionistContext, version: 4 | 6 = 4): string {\n if (version === 6) {\n const groups: string[] = [];\n for (let i = 0; i < 8; i++) {\n groups.push(hexString(4, ctx.source));\n }\n return groups.join(':');\n }\n const octets: number[] = [];\n for (let i = 0; i < 4; i++) {\n octets.push(int(0, 255, ctx.source));\n }\n return octets.join('.');\n}",
|