@vielzeug/codex 2.1.4 → 2.2.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/dist/errors.js +0 -3
- package/dist/errors.js.map +1 -1
- package/dist/snapshot.js.map +1 -1
- package/dist/tools/packages.js +2 -3
- package/dist/tools/packages.js.map +1 -1
- package/dist/tools/refine.js +2 -2
- package/dist/tools/refine.js.map +1 -1
- package/dist/tools/schema.js +2 -0
- package/dist/tools/schema.js.map +1 -1
- package/package.json +6 -1
- package/data/catalog.json +0 -1689
- package/data/llms-full.txt +0 -25771
- package/data/llms.txt +0 -40
- package/data/manifest.json +0 -8
- package/data/packages/arsenal.json +0 -210
- package/data/packages/assay.json +0 -40
- package/data/packages/clockwork.json +0 -67
- package/data/packages/codex.json +0 -43
- package/data/packages/coins.json +0 -103
- package/data/packages/conduit.json +0 -60
- package/data/packages/courier.json +0 -58
- package/data/packages/dnd.json +0 -77
- package/data/packages/familiar.json +0 -40
- package/data/packages/flux.json +0 -93
- package/data/packages/forge.json +0 -84
- package/data/packages/herald.json +0 -108
- package/data/packages/keymap.json +0 -59
- package/data/packages/ledger.json +0 -57
- package/data/packages/lingua.json +0 -68
- package/data/packages/necromancer.json +0 -50
- package/data/packages/orbit.json +0 -107
- package/data/packages/ore.json +0 -73
- package/data/packages/prism.json +0 -67
- package/data/packages/pulse.json +0 -60
- package/data/packages/refine.json +0 -12
- package/data/packages/ripple.json +0 -83
- package/data/packages/rune.json +0 -80
- package/data/packages/sandbox.json +0 -40
- package/data/packages/scout.json +0 -60
- package/data/packages/scroll.json +0 -114
- package/data/packages/sourcerer.json +0 -74
- package/data/packages/spell.json +0 -134
- package/data/packages/tempo.json +0 -81
- package/data/packages/vault.json +0 -87
- package/data/packages/ward.json +0 -113
- package/data/packages/wayfinder.json +0 -113
- package/data/refine.json +0 -11926
- package/data/search.json +0 -1436
package/data/packages/vault.json
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"apiSource": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';\nexport { scheduleExpiredPrune } from './prune';\nexport type { QueryBuilder } from './query';\nexport { isExpired, ttl } from './ttl';\nexport type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';\nexport { table } from './types';\n",
|
|
3
|
-
"docs": {
|
|
4
|
-
"index": "---\ntitle: Vault — Typed storage\ndescription: Typed browser storage and opt-in driver-neutral SQLite with portable keys, TTL, observation, and transactions.\npackage: vault\ncategory: Storage\nkeywords: [storage, indexeddb, localstorage, sessionstorage, sqlite, ttl, browser, node, deno]\nrelated: [courier, forge, ripple]\nexports: [table, ttl, scheduleExpiredPrune, isExpired, createMemory, createLocalStorage, createSessionStorage, createIndexedDB, createSQLite]\nenvironments: [browser, node, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"vault\" />\n\n## Why Vault?\n\nVault gives browser and SQLite persistence one typed schema while keeping backend guarantees explicit. Use `VaultStore` for portable CRUD and observation; choose IndexedDB or the opt-in SQLite subpath when you need atomic transactions or lazy iteration.\n\n```ts\n// Before\nlocalStorage.setItem('theme', JSON.stringify({ value: 'dark' }));\nconst theme = JSON.parse(localStorage.getItem('theme') ?? '{}').value;\n\n// After\nawait store.put('preferences', { id: 'theme', value: 'dark' });\nconst theme = await store.get('preferences', 'theme');\n```\n\n| Feature | Vault | Raw Web Storage | Dexie |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"vault\" type=\"size\" /> | Browser built-in | Extra dependency |\n| Runtime 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| Typed schema and keys | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Application-defined | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Portable Memory/Web Storage API | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> | IndexedDB only |\n| Explicit atomic transactions | IndexedDB capability | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Driver-neutral SQLite | Opt-in subpath | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Vault when** you need typed browser persistence or application-owned SQLite with one portable CRUD API and explicit storage capabilities.\n\n**Consider raw Web Storage when** you only persist one or two unstructured values. **Consider Dexie when** you need a broader IndexedDB ecosystem.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/vault\n```\n\n```sh [npm]\nnpm install @vielzeug/vault\n```\n\n```sh [yarn]\nyarn add @vielzeug/vault\n```\n\n:::\n\n## Quick Start\n\nDefine a schema, create a portable store, and dispose it with its owner.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createLocalStorage } from '@vielzeug/vault/local-storage';\n\nconst store = createLocalStorage({\n name: 'app-v2',\n schema: { preferences: table<{ id: string; theme: 'dark' | 'light' }>('id') },\n});\n\ntry {\n await store.put('preferences', { id: 'theme', theme: 'dark' });\n console.log(await store.get('preferences', 'theme'));\n} finally {\n await store.dispose();\n}\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `table()` defines typed records with portable string or number keys.\n- `/memory`, `/local-storage`, and `/session-storage` return portable `VaultStore` instances without loading other adapters.\n- `observe()` emits current and changed table snapshots.\n- `ttl` creates validated expiration durations.\n- `/indexeddb` returns `IndexedDbVaultStore` with `batch()` and `iterate()`.\n- `createSQLite()` is an opt-in, driver-neutral subpath for Node, Bun, and Deno SQLite drivers.\n- `/indexeddb` also exports `defineMigration()` for schema upgrades.\n- `scheduleExpiredPrune()` removes stale TTL entries on an owned schedule.\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/index.md) saves and restores form drafts through Vault stores.\n- [Ripple](../ripple/index.md) owns application state that can persist through Vault.\n- [Courier](../courier/index.md) can populate persistent cache data.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
-
"api": "---\ntitle: Vault — API Reference\ndescription: Reference for Vault schemas, adapter entry points, storage capabilities, SQLite drivers, and errors.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createMemory()` | In-memory portable store | Async API | Import from `/memory` |\n| `createLocalStorage()` / `createSessionStorage()` | Web Storage-backed portable stores | Async API | Available only where the corresponding Web API exists |\n| `createIndexedDB()` | Browser transactions and cursor iteration | Async API | Import from `/indexeddb` |\n| `createSQLite()` | Driver-neutral SQLite store | Async API over a synchronous driver | Import from `/sqlite` |\n| `table()` | Typed record schema | Sync | The key field must be a string or finite number |\n| `ttl` | Valid expiration durations | Sync | Durations must be positive |\n| `scheduleExpiredPrune()` | Periodic TTL cleanup | Sync setup, async work | Stop it or give it an abort signal |\n\n## Package Entry Points\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/vault` | Adapter-free schemas, TTL, errors, pruning, queries, and shared types |\n| `@vielzeug/vault/memory` | `createMemory` |\n| `@vielzeug/vault/local-storage` | `createLocalStorage` |\n| `@vielzeug/vault/session-storage` | `createSessionStorage` |\n| `@vielzeug/vault/indexeddb` | `createIndexedDB`, migrations, and IndexedDB-only types |\n| `@vielzeug/vault/sqlite` | `createSQLite` and the SQLite driver protocol types |\n\n## Schemas and TTL\n\n### `table()`\n\n```ts\nfunction table<T extends object, Key extends keyof T & string = keyof T & string>(\n key: Key & (T[Key] extends VaultKey ? unknown : never),\n): TableBuilder<T, Key>;\n```\n\nDefines a typed table and its primary-key field.\n\n| Parameter | Description |\n| --- | --- |\n| `key` | A record field whose values are `string` or finite `number` keys |\n\n**Returns:** A table builder. Call `.ttl()` to set a default expiry and `.index()` to declare an IndexedDB secondary index.\n\n```ts\nimport { table, ttl } from '@vielzeug/vault';\n\nconst users = table<{ id: number; email: string }>('id').index('email').ttl(ttl.days(7));\n```\n\n---\n\n### `ttl`\n\n```ts\nconst ttl: {\n days(n: number): TtlMs;\n hours(n: number): TtlMs;\n minutes(n: number): TtlMs;\n ms(n: number): TtlMs;\n seconds(n: number): TtlMs;\n};\n```\n\nCreates a branded, finite, positive duration for writes and table defaults.\n\n**Returns:** `TtlMs`.\n\n```ts\nimport { ttl } from '@vielzeug/vault';\n\nconst cacheLifetime = ttl.minutes(5);\n```\n\n---\n\n### `isExpired()`\n\n```ts\nfunction isExpired(expiresAt: number | undefined): boolean;\n```\n\nReports whether an expiration timestamp has passed.\n\n**Returns:** `true` when `expiresAt` is defined and no later than the current time.\n\n```ts\nimport { isExpired } from '@vielzeug/vault';\n\nif (isExpired(record.expiresAt)) console.log('expired');\n```\n\n## Factories\n\nAll factory options accept `schema`, plus optional `validators`, `logger`, and `onMetrics`. The root entry does not export any factory.\n\n### `createMemory()`\n\n```ts\nfunction createMemory<S extends AnySchema>(options: {\n name?: string;\n schema: S;\n} & BaseAdapterOptions<S>): VaultStore<S>;\n```\n\nCreates an in-memory portable store. A `name` enables same-origin `BroadcastChannel` observation between memory stores when the platform provides it.\n\n| Parameter | Description |\n| --- | --- |\n| `schema` | Tables created by `table()` |\n| `name` | Optional shared memory-store namespace |\n\n**Returns:** `VaultStore<S>`.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createMemory } from '@vielzeug/vault/memory';\n\nconst store = createMemory({ schema: { users: table<{ id: number; name: string }>('id') } });\n```\n\n---\n\n### `createLocalStorage()`\n\n```ts\nfunction createLocalStorage<S extends AnySchema>(options: {\n name: string;\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n schema: S;\n} & BaseAdapterOptions<S>): VaultStore<S>;\n```\n\nCreates a namespaced `localStorage` store.\n\n| Parameter | Description |\n| --- | --- |\n| `name` | Required storage namespace |\n| `onQuotaExceeded` | Handles a Web Storage quota error; returning `'ignore'` drops that write |\n| `schema` | Tables created by `table()` |\n\n**Returns:** `VaultStore<S>`.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createLocalStorage } from '@vielzeug/vault/local-storage';\n\nconst store = createLocalStorage({ name: 'app', schema: { settings: table<{ id: string }>('id') } });\n```\n\n---\n\n### `createSessionStorage()`\n\n```ts\nfunction createSessionStorage<S extends AnySchema>(options: {\n name: string;\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n schema: S;\n} & BaseAdapterOptions<S>): VaultStore<S>;\n```\n\nCreates a namespaced `sessionStorage` store. Its options and return type match `createLocalStorage()`.\n\n**Returns:** `VaultStore<S>`.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createSessionStorage } from '@vielzeug/vault/session-storage';\n\nconst store = createSessionStorage({ name: 'checkout', schema: { cart: table<{ id: string }>('id') } });\n```\n\n---\n\n### `createIndexedDB()`\n\n```ts\nfunction createIndexedDB<S extends AnySchema>(options: {\n migrate?: MigrationFn;\n name: string;\n schema: S;\n version?: number;\n} & BaseAdapterOptions<S>): IndexedDbVaultStore<S>;\n```\n\nCreates an IndexedDB store with atomic batches, lazy cursor iteration, and optional schema migrations.\n\n| Parameter | Description |\n| --- | --- |\n| `name` | Required database name |\n| `schema` | Tables and IndexedDB secondary indexes |\n| `version` | Positive schema version; defaults to `1` |\n| `migrate` | Synchronous upgrade callback for version changes |\n\n**Returns:** `IndexedDbVaultStore<S>`.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createIndexedDB } from '@vielzeug/vault/indexeddb';\n\nconst store = createIndexedDB({ name: 'app', schema: { users: table<{ id: number }>('id') } });\n```\n\n---\n\n### `createSQLite()`\n\n```ts\nfunction createSQLite<S extends AnySchema>(options: SQLiteVaultOptions<S>): SQLiteVaultStore<S>;\n```\n\nCreates a namespaced SQLite store with atomic batches and keyset-paginated iteration. It accepts an application-provided positional-parameter driver and never opens or imports a runtime driver.\n\n| Parameter | Description |\n| --- | --- |\n| `database` | Caller-provided `SQLiteDatabase` connection |\n| `name` | Namespace within the connection |\n| `schema`, `validators`, `logger`, `onMetrics` | Shared factory options |\n| `closeOnDispose` | Closes the connection during disposal; defaults to `false` |\n\n**Returns:** `SQLiteVaultStore<S>`.\n\n```ts\nimport { DatabaseSync } from 'node:sqlite';\n\nimport { table } from '@vielzeug/vault';\nimport { createSQLite } from '@vielzeug/vault/sqlite';\n\nconst store = createSQLite({\n database: new DatabaseSync(':memory:'),\n name: 'tests',\n schema: { users: table<{ id: number; name: string }>('id') },\n});\n```\n\nNode `DatabaseSync`, Bun `Database`, and Deno `jsr:@db/sqlite` `Database` satisfy the protocol. Values must be JSON-compatible plain objects. During a `batch()` callback, calls on every Vault store sharing that connection reject; use `tx.*` instead.\n\n## Store Capabilities\n\n### `VaultStore`\n\n```ts\ninterface VaultStore<S extends AnySchema> {\n clear<K extends keyof S & string>(table: K): Promise<void>;\n count<K extends keyof S & string>(table: K): Promise<number>;\n delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number>;\n entries<K extends keyof S & string>(table: K): Promise<Array<[KeyOf<S, K>, RecordOf<S, K>]>>;\n get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined>;\n getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]>;\n getMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;\n getOrDefault<K extends keyof S & string>(table: K, key: KeyOf<S, K>, defaultFn: () => RecordOf<S, K>, ttl?: TtlMs): Promise<RecordOf<S, K>>;\n has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean>;\n isEmpty<K extends keyof S & string>(table: K): Promise<boolean>;\n keys<K extends keyof S & string>(table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<S, K>[]>;\n put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: TtlMs): Promise<void>;\n putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: TtlMs): Promise<void>;\n query<K extends keyof S & string>(table: K): QueryBuilder<RecordOf<S, K>>;\n update<K extends keyof S & string>(table: K, key: KeyOf<S, K>, changes: Partial<RecordOf<S, K>>, ttl?: TtlMs): Promise<RecordOf<S, K> | undefined>;\n upsert<K extends keyof S & string>(table: K, key: KeyOf<S, K>, fn: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>, ttl?: TtlMs): Promise<RecordOf<S, K>>;\n pruneExpired(): Promise<Record<keyof S & string, number>>;\n debug(): Promise<DebugInfo<S>>;\n observe<K extends keyof S & string>(table: K, listener: Observer<RecordOf<S, K>>, options?: { immediate?: boolean; signal?: AbortSignal }): Unsubscribe;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n readonly disposalSignal: AbortSignal;\n [Symbol.asyncDispose](): Promise<void>;\n}\n```\n\nThe portable store API is returned by every factory. `observe()` emits the current table snapshot by default and then emits after mutations.\n\n---\n\n### `batch()`\n\n```ts\ninterface TransactionalVaultStore<S extends AnySchema> extends VaultStore<S> {\n batch<K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R>;\n}\n```\n\nRuns a scoped atomic callback. `IndexedDbVaultStore` and `SQLiteVaultStore` provide it.\n\n| Parameter | Description |\n| --- | --- |\n| `tables` | Tables the transaction may access |\n| `fn` | Async callback that uses only the supplied `tx` context |\n\n**Returns:** The callback result after commit.\n\n```ts\nawait store.batch(['users'], async (tx) => {\n await tx.put('users', { id: 1, name: 'Ada' });\n});\n```\n\n---\n\n### `iterate()`\n\n```ts\ninterface IterableVaultStore<S extends AnySchema> extends VaultStore<S> {\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>>;\n}\n```\n\nLazily yields table records. `IndexedDbVaultStore` uses a cursor; `SQLiteVaultStore` uses keyset pagination.\n\n**Returns:** An `AsyncIterable` of records.\n\n```ts\nfor await (const user of store.iterate('users')) console.log(user);\n```\n\n## Queries, Pruning, and Migrations\n\n### `QueryBuilder`\n\n```ts\ninterface QueryBuilder<T extends object, N extends T = T> {\n between(field: string, lower: number | string, upper: number | string): QueryBuilder<T, N>;\n count(): Promise<number>;\n delete(): Promise<number>;\n equals<K extends keyof T & string, V extends T[K]>(field: K, value: V): QueryBuilder<T & Record<K, V>>;\n exists(): Promise<boolean>;\n filter(fn: (value: N, index: number, array: N[]) => boolean): QueryBuilder<T, N>;\n first(): Promise<N | undefined>;\n limit(n: number): QueryBuilder<T, N>;\n offset(n: number): QueryBuilder<T, N>;\n orderBy<K extends keyof T>(field: K, direction?: 'asc' | 'desc'): QueryBuilder<T, N>;\n startsWith(field: keyof T, prefix: string, options?: { ignoreCase?: boolean }): QueryBuilder<T, N>;\n toArray(): Promise<N[]>;\n totalCount(): Promise<number>;\n}\n```\n\nBuilds a lazy table query. `count()` respects `limit()` and `offset()`; `totalCount()` ignores pagination and ordering.\n\n```ts\nconst page = await store.query('users').startsWith('name', 'A').orderBy('name').limit(20).toArray();\n```\n\n---\n\n### `scheduleExpiredPrune()`\n\n```ts\nfunction scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<VaultStore<S>, 'pruneExpired'>,\n options: {\n interval: number;\n onError?: (error: unknown) => void;\n signal?: AbortSignal;\n },\n): () => void;\n```\n\nSchedules `pruneExpired()` at a finite, positive interval.\n\n**Returns:** A stop function.\n\n```ts\nimport { scheduleExpiredPrune, ttl } from '@vielzeug/vault';\n\nconst stop = scheduleExpiredPrune(store, { interval: ttl.hours(1), signal: store.disposalSignal });\nstop();\n```\n\n---\n\n### `defineMigration()`\n\n```ts\nfunction defineMigration(steps: MigrationStep[]): MigrationFn;\n```\n\nBuilds an idempotent IndexedDB migration callback from schema-change steps.\n\n**Returns:** An IndexedDB `MigrationFn`.\n\n```ts\nimport { defineMigration } from '@vielzeug/vault/indexeddb';\n\nconst migrate = defineMigration([{ field: 'email', table: 'users', type: 'addIndex' }]);\n```\n\n## Types\n\n```ts\ntype VaultKey = number | string;\ntype TtlMs = number & { readonly [ttlMsBrand]: never };\ntype Unsubscribe = () => void;\ntype Observer<T> = (records: T[]) => void;\ntype AnySchema = Record<string, {\n defaultTtl?: TtlMs;\n indexes?: readonly string[];\n key: string;\n}>;\ntype SchemaEntry<T extends object, Key extends keyof T & string = keyof T & string> =\n T[Key] extends VaultKey ? {\n defaultTtl?: TtlMs;\n indexes?: readonly (keyof T & string)[];\n key: Key;\n } : never;\ntype TableBuilder<T extends object, Key extends keyof T & string = keyof T & string> =\n SchemaEntry<T, Key> & {\n index: <F extends keyof T & string>(field: F) => TableBuilder<T, Key>;\n ttl: (ms: TtlMs) => TableBuilder<T, Key>;\n };\ntype RecordOf<S extends AnySchema, K extends keyof S> =\n S[K] extends SchemaEntry<infer R, infer _Key> ? R : never;\ntype KeyOf<S extends AnySchema, K extends keyof S> =\n Extract<S[K] extends SchemaEntry<infer R, infer Key> ? R[Key] : never, VaultKey>;\n```\n\n```ts\ntype BaseAdapterOptions<S extends AnySchema> = {\n logger?: VaultLogger;\n onMetrics?: (event: MetricsEvent) => void;\n schema: S;\n validators?: TableValidators<S>;\n};\n\ninterface VaultLogger {\n error(messageOrContext?: Record<string, unknown> | Error | string, message?: string): void;\n}\n\ninterface RecordValidator<T> {\n parse(value: unknown): T;\n}\n\ntype TableValidators<S extends AnySchema> = {\n [K in keyof S]?: RecordValidator<RecordOf<S, K>>;\n};\n\ntype MetricsEvent = {\n duration: number;\n operation: 'batch' | 'clear' | 'count' | 'delete' | 'deleteMany' | 'entries' | 'get' | 'getAll' |\n 'getMany' | 'getOrDefault' | 'has' | 'isEmpty' | 'keys' | 'put' | 'putAll' | 'query' |\n 'queryDelete' | 'update' | 'upsert';\n table: string;\n};\n\ntype DebugStats = { expiredCount: number; recordCount: number };\ntype DebugInfo<S extends AnySchema> = { tables: Array<{ name: keyof S & string } & DebugStats> };\n```\n\n```ts\ntype MigrationContext = {\n db: IDBDatabase;\n newVersion: number | null;\n oldVersion: number;\n tx: IDBTransaction;\n};\n\ntype MigrationFn = (ctx: MigrationContext) => void;\n\ntype MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\ninterface IndexedDbVaultStore<S extends AnySchema>\n extends TransactionalVaultStore<S>, IterableVaultStore<S> {}\n```\n\n```ts\ntype SQLiteParameter = null | number | string;\n\ninterface SQLiteStatement {\n all(...parameters: SQLiteParameter[]): readonly Record<string, unknown>[];\n finalize?(): void;\n get(...parameters: SQLiteParameter[]): Record<string, unknown> | undefined;\n run(...parameters: SQLiteParameter[]): unknown;\n}\n\ninterface SQLiteDatabase {\n close?(): void;\n exec(sql: string): void;\n prepare(sql: string): SQLiteStatement;\n}\n\ntype SQLiteVaultOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n closeOnDispose?: boolean;\n database: SQLiteDatabase;\n name: string;\n};\n\ninterface SQLiteVaultStore<S extends AnySchema>\n extends TransactionalVaultStore<S>, IterableVaultStore<S> {}\n```\n\n`TransactionContext` has the same CRUD, query, and TTL methods as `VaultStore`, narrowed to the tables declared in `batch()`.\n\n## Errors\n\n| Error | Trigger |\n| --- | --- |\n| `VaultError` | Any Vault-originated validation, serialization, storage, or query error |\n| `VaultDisposedError` | An operation after the store or observer hub is disposed |\n| `VaultScopeError` | An IndexedDB transaction accesses a table outside its declared batch scope |\n| `VaultQuotaError` | A LocalStorage or SessionStorage write exceeds the browser quota |\n| `VaultMigrationError` | An IndexedDB migration callback throws |\n\nEvery listed error extends `VaultError`.\n",
|
|
6
|
-
"usage": "---\ntitle: Vault — Usage Guide\ndescription: Persist typed browser or SQLite data, observe table snapshots, and use atomic transactions.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate a portable store with one schema and write a typed row.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createLocalStorage } from '@vielzeug/vault/local-storage';\n\ninterface Preference {\n id: string;\n theme: 'dark' | 'light';\n}\n\nconst store = createLocalStorage({\n name: 'app-v2',\n schema: { preferences: table<Preference>('id') },\n});\n\nawait store.put('preferences', { id: 'theme', theme: 'dark' });\nconsole.log(await store.get('preferences', 'theme'));\n```\n\n## Create a Portable Store\n\nMemory, LocalStorage, and SessionStorage return `VaultStore`. They share portable string/number keys, CRUD methods, queries, TTL, and `observe()`. Vault keeps record values and expiry metadata separate; the physical storage layout is adapter-specific.\n\nThe root entry is adapter-free. Import `createMemory` from `@vielzeug/vault/memory`, `createLocalStorage` from `@vielzeug/vault/local-storage`, or `createSessionStorage` from `@vielzeug/vault/session-storage`. Import each adapter from its focused subpath so unused backends stay out of the bundle.\n\nUse a new storage name when upgrading from Vault 1. Old key and envelope formats are not read by Vault 2.\n\n```ts\nconst store = createLocalStorage({\n name: 'app-v2',\n schema: { preferences: table<Preference>('id') },\n});\n```\n\n## Read and Change Records\n\nUse `update()` for an existing row and `upsert()` when the row may not exist.\n\n```ts\nconst updated = await store.update('preferences', 'theme', { theme: 'light' });\n\nawait store.upsert('preferences', 'locale', (current) => ({\n id: 'locale',\n theme: current?.theme ?? 'dark',\n}));\n\nconsole.log(updated);\n```\n\n`update()` returns `undefined` for a missing key. `upsert()` always writes the record returned by its callback.\n\n## Query Records\n\nBuild a query from a table, then finish it with a terminal method. `totalCount()` ignores pagination, which makes it suitable for page controls.\n\n```ts\nconst query = store.query('preferences').startsWith('id', 'theme');\nconst preferences = await query.orderBy('id').limit(10).toArray();\nconst total = await query.totalCount();\n\nconsole.log({ preferences, total });\n```\n\nMemory and Web Storage queries scan the table. IndexedDB can use declared secondary indexes, while SQLite pushes primary-key equality, range, and case-sensitive prefix filters to the database.\n\n## Use TTL and Pruning\n\nUse `ttl.*` helpers for expiring rows. Schedule pruning when stale rows can accumulate without reads.\n\n```ts\nimport { scheduleExpiredPrune, ttl } from '@vielzeug/vault';\n\nawait store.put('preferences', { id: 'temporary', theme: 'dark' }, ttl.hours(1));\nconst stopPrune = scheduleExpiredPrune(store, { interval: ttl.hours(6), signal: store.disposalSignal });\n\nstopPrune();\n```\n\n## Observe a Table\n\nUse `observe()` for current and future snapshots. Tie subscription lifetime to an `AbortSignal` when a component or request owns it.\n\n```ts\nconst controller = new AbortController();\n\nstore.observe('preferences', (preferences) => {\n console.log(preferences);\n}, { signal: controller.signal });\n\ncontroller.abort();\n```\n\n## Use IndexedDB for Browser Transactions\n\nChoose IndexedDB when browser storage needs multiple writes to commit together or cursor iteration.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createIndexedDB } from '@vielzeug/vault/indexeddb';\n\nconst db = createIndexedDB({\n name: 'app-v2',\n schema: { events: table<{ id: number; type: string }>('id') },\n});\n\nawait db.batch(['events'], async (tx) => {\n await tx.put('events', { id: 1, type: 'opened' });\n await tx.put('events', { id: 2, type: 'saved' });\n});\n```\n\nOnly await `tx.*` operations inside a batch callback. Do not await timers, fetches, or other external asynchronous work; IndexedDB can commit an inactive transaction.\n\n## Use SQLite Outside the Browser\n\nImport SQLite from the opt-in subpath so the browser root stays free of runtime drivers. Vault never opens a connection or configures its SQLite process behavior for you.\n\n```ts\nimport { DatabaseSync } from 'node:sqlite';\n\nimport { table } from '@vielzeug/vault';\nimport { createSQLite } from '@vielzeug/vault/sqlite';\n\nconst database = new DatabaseSync('app.db', { timeout: 5_000 });\nconst store = createSQLite({\n database,\n name: 'app-v2',\n schema: { events: table<{ id: number; type: string }>('id') },\n});\n\nawait store.batch(['events'], async (tx) => {\n await tx.put('events', { id: 1, type: 'opened' });\n await tx.put('events', { id: 2, type: 'saved' });\n});\n```\n\nNode's `node:sqlite` API is experimental. Bun's `bun:sqlite` `Database` satisfies the same positional `exec()` and `prepare()` contract; configure WAL from your application when the deployment needs it. Deno does not include SQLite, but `jsr:@db/sqlite`'s `Database` satisfies the same contract when its FFI, filesystem, and environment permissions are granted.\n\nSQLite stores serialize all access through the injected connection. `batch()` starts `BEGIN IMMEDIATE` and rolls back callback failures. While its callback runs, calls on any store sharing that connection reject rather than waiting behind the transaction; use `tx.*` instead. The underlying drivers are synchronous, so move large scans and writes to a worker or isolate when event-loop latency matters.\n\n## Store SQLite Values and Observe Changes\n\nSQLite accepts JSON-compatible plain-object records only. Circular values, `bigint`, dates, class instances, functions, and non-finite numbers are rejected before writing. Number and string primary keys remain distinct.\n\n`observe()` sees mutations written through Vault stores sharing the same injected connection after a commit. It cannot detect direct SQL changes, writes from another process, or writes through another connection. The connection belongs to the caller by default; use `closeOnDispose: true` only when the store owns it.\n\n## Handle IndexedDB Schema Migrations\n\nDeclare IndexedDB indexes in the schema. Use `migrate` only for IndexedDB version upgrades and mirror Vault’s fixed `value.<field>` index path.\n\n```ts\nimport { table } from '@vielzeug/vault';\nimport { createIndexedDB, type MigrationFn } from '@vielzeug/vault/indexeddb';\n\nconst schema = { users: table<{ id: number; name: string }>('id').index('name') };\nconst migrate: MigrationFn = ({ db, oldVersion, tx }) => {\n if (oldVersion < 2 && db.objectStoreNames.contains('users')) {\n tx.objectStore('users').createIndex('name', 'value.name');\n }\n};\n\ncreateIndexedDB({ name: 'app-v2', migrate, schema, version: 2 });\n```\n\n## Framework Integration\n\n::: code-group\n\n```ts [React]\nimport { useEffect, useState } from 'react';\n\nimport type { AnySchema, RecordOf, VaultStore } from '@vielzeug/vault';\n\nexport function useTable<S extends AnySchema, K extends keyof S & string>(store: VaultStore<S>, table: K) {\n const [rows, setRows] = useState<RecordOf<S, K>[]>([]);\n\n useEffect(() => store.observe(table, setRows), [store, table]);\n return rows;\n}\n```\n\n```ts [Vue 3]\nimport { onUnmounted, shallowRef } from 'vue';\n\nimport type { AnySchema, RecordOf, VaultStore } from '@vielzeug/vault';\n\nexport function useTable<S extends AnySchema, K extends keyof S & string>(store: VaultStore<S>, table: K) {\n const rows = shallowRef<RecordOf<S, K>[]>([]);\n const stop = store.observe(table, (next) => (rows.value = next));\n\n onUnmounted(stop);\n return rows;\n}\n```\n\n```ts [Svelte]\nimport { readable } from 'svelte/store';\n\nimport type { AnySchema, RecordOf, VaultStore } from '@vielzeug/vault';\n\nexport function tableStore<S extends AnySchema, K extends keyof S & string>(store: VaultStore<S>, table: K) {\n return readable<RecordOf<S, K>[]>([], (set) => store.observe(table, set));\n}\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\nUse Forge’s Vault helpers for explicit form-draft persistence. Keep Ripple signals as application state and persist selected changes through Vault writes.\n\n## Best Practices\n\n- Define one schema per storage namespace.\n- Use string or finite-number primary keys only.\n- Choose a new namespace for Vault 1 storage unless you migrate it yourself.\n- Use `observe()` for table snapshots.\n- Use IndexedDB or SQLite for atomic work.\n- Keep external asynchronous work outside `batch()` callbacks.\n- Use `ttl.*` instead of raw durations.\n- Keep SQLite scans and writes off latency-sensitive event loops, and dispose stores with their owner.\n- Dispose stores when their owner ends.\n",
|
|
7
|
-
"examples": "---\ntitle: Vault — Examples\ndescription: Portable storage, observation, transactions, iteration, and SQLite.\n---\n\n- [CRUD](./examples/crud.md)\n- [TTL](./examples/ttl.md)\n- [Querying](./examples/querying.md)\n- [Reactive observation](./examples/reactive.md)\n- [IndexedDB iteration](./examples/iterate.md)\n- [IndexedDB batch transactions](./examples/batch.md)\n- [SQLite transactions and iteration](./examples/sqlite.md)\n- [Plugin validation](./examples/plugins.md)\n"
|
|
8
|
-
},
|
|
9
|
-
"examples": [
|
|
10
|
-
{
|
|
11
|
-
"id": "basic-setup",
|
|
12
|
-
"code": "import { table } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst schema = {\n users: table('id'),\n}\n\nconst db = createLocalStorage({ name: 'demo', schema })\n\nawait db.put('users', { id: 1, name: 'Alice', email: 'alice@example.com' })\nawait db.put('users', { id: 2, name: 'Bob', email: 'bob@example.com' })\n\nconsole.log('Get user 1:', await db.get('users', 1))\nconsole.log('All users:', await db.getAll('users'))\nconsole.log('Count:', await db.query('users').count())",
|
|
13
|
-
"name": "Basic Setup - Initialize Vault"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"id": "bulk-operations",
|
|
17
|
-
"code": "import { table } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst schema = {\n items: table('id'),\n}\n\nconst db = createLocalStorage({ name: 'bulk-demo', schema })\n\nconst items = Array.from({ length: 10 }, (_, index) => ({\n id: index + 1,\n value: +(Math.random() * 1000).toFixed(2),\n}))\n\nawait db.putAll('items', items)\nconsole.log('Inserted', items.length, 'items')\n\n// getMany — fetch multiple by key in one call (missing keys return undefined)\nconst [first, missing, third] = await db.getMany('items', [1, 99, 3])\nconsole.log('getMany [1, 99, 3]:', first?.id, missing, third?.id)\n\n// deleteMany — remove multiple by key, returns count deleted\nconst deleted = await db.deleteMany('items', [1, 2, 3, 99])\nconsole.log('deleteMany [1,2,3,99] deleted:', deleted) // 3 (99 did not exist)\n\n// query-based delete for filter-driven removal\nconst queryDeleted = await db.query('items').filter((item) => item.id <= 6).delete()\nconsole.log('Query-deleted items with id ≤ 6:', queryDeleted)\n\nconsole.log('Remaining count:', await db.query('items').count())\nconsole.log('First remaining item:', await db.query('items').orderBy('id', 'asc').first())",
|
|
18
|
-
"name": "Bulk Operations"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"id": "cache-first",
|
|
22
|
-
"code": "import { table, ttl } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst db = createLocalStorage({ name: 'cache-demo', schema: { cache: table('id') } })\n\nasync function getOrComputeConfig() {\n return db.getOrDefault('cache', 'config', () => ({\n id: 'config',\n data: 'computed value',\n fetchedAt: Date.now(),\n }), ttl.minutes(5))\n}\n\nconst first = await getOrComputeConfig()\nconst second = await getOrComputeConfig()\nconsole.log('Same cached record:', first.fetchedAt === second.fetchedAt)",
|
|
23
|
-
"name": "Cache-First with getOrDefault"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"id": "crud-operations",
|
|
27
|
-
"code": "import { table } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst schema = {\n users: table('id'),\n}\n\nconst db = createLocalStorage({ name: 'demo', schema })\n\nawait db.put('users', { id: 1, name: 'Alice', email: 'alice@example.com', age: 25 })\nawait db.put('users', { id: 2, name: 'Bob', email: 'bob@example.com', age: 30 })\nconsole.log('Created 2 users')\n\nconsole.log('Get user 1:', await db.get('users', 1))\nconsole.log('Count:', await db.count('users'))\nconsole.log('isEmpty before clear:', await db.isEmpty('users')) // false\n\nawait db.update('users', 1, { age: 26, name: 'Alice Smith' })\nconsole.log('Updated user 1:', await db.get('users', 1))\n\nconsole.log('Deleted user 2:', await db.delete('users', 2))\nconsole.log('Remaining users:', await db.getAll('users'))\n\nawait db.clear('users')\nconsole.log('isEmpty after clear:', await db.isEmpty('users')) // true",
|
|
28
|
-
"name": "CRUD Operations"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"id": "indexed-db",
|
|
32
|
-
"code": "import { table, ttl } from '@vielzeug/vault'\nimport { createIndexedDB } from '@vielzeug/vault/indexeddb'\n\nconst schema = {\n logs: table('id'),\n}\n\n// createIndexedDB returns IndexedDbVaultStore with transactions and cursor iteration\nconst db = createIndexedDB({\n name: 'app-logs',\n schema,\n version: 1,\n})\n\nawait db.putAll('logs', [\n { id: 1, level: 'info', message: 'App started', ts: Date.now() - 3000 },\n { id: 2, level: 'warn', message: 'Slow query detected', ts: Date.now() - 2000 },\n { id: 3, level: 'error', message: 'Request failed', ts: Date.now() - 1000 },\n { id: 4, level: 'info', message: 'Request succeeded', ts: Date.now() },\n], ttl.hours(1))\n\n// batch() is atomic on IndexedDB — all writes commit or none do\nawait db.batch(['logs'], async (tx) => {\n await tx.put('logs', { id: 5, level: 'info', message: 'Batch committed', ts: Date.now() })\n await tx.deleteMany('logs', [1, 2]) // remove old entries in the same transaction\n})\n\n// iterate() — cursor-based streaming, only on IndexedDbVaultStore\n// the full table is never loaded into memory at once\nconst messages = []\nfor await (const entry of db.iterate('logs')) {\n messages.push(entry.message)\n}\nconsole.log('Streamed via iterate():', messages)\n\nconst errors = await db.query('logs').equals('level', 'error').toArray()\nconsole.log('Errors:', errors.map((e) => e.message))\nconsole.log('Total logs:', await db.query('logs').count())\n\nconst info = await db.debug()\nfor (const t of info.tables) {\n console.log(t.name + ':', t.recordCount, 'live,', t.expiredCount, 'expired')\n}\n\nawait db.dispose()",
|
|
33
|
-
"name": "IndexedDB — Atomic Batch & iterate()"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"id": "prune-schedule",
|
|
37
|
-
"code": "import { scheduleExpiredPrune, table, ttl } from '@vielzeug/vault'\nimport { createMemory } from '@vielzeug/vault/memory'\n\n// scheduleExpiredPrune runs pruneExpired() on an interval and stops\n// automatically when the adapter is disposed (VaultDisposedError).\n// Use onError to surface unexpected failures instead of silently swallowing them.\n\nconst schema = { sessions: table('token') }\nconst db = createMemory({ schema })\n\nconst stop = scheduleExpiredPrune(db, {\n interval: ttl.minutes(15),\n onError: (err) => console.error('[vault] prune failed:', err),\n})\n\n// Write a session that expires in 1 ms\nawait db.put('sessions', { token: 'abc', user: 1 }, ttl.ms(1))\nawait db.put('sessions', { token: 'def', user: 2 }) // no TTL — permanent\n\nconsole.log('before prune:', await db.count('sessions')) // 2 (lazy eviction: both exist physically)\n\n// Manual prune to demonstrate the API\nawait new Promise((resolve) => setTimeout(resolve, 5))\nconst pruned = await db.pruneExpired()\nconsole.log('pruned:', pruned.sessions) // 1 (the expired session)\nconsole.log('after prune:', await db.count('sessions')) // 1\n\n// When the adapter is disposed, the schedule stops automatically\nawait db.dispose()\nstop() // or call stop() explicitly before dispose",
|
|
38
|
-
"name": "TTL — scheduleExpiredPrune with onError"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"id": "query-builder",
|
|
42
|
-
"code": "import { table } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst schema = {\n products: table('id'),\n}\n\nconst db = createLocalStorage({ name: 'shop', schema })\n\nawait db.putAll('products', [\n { id: 1, name: 'Laptop', price: 999, category: 'electronics', inStock: true },\n { id: 2, name: 'Mouse', price: 29, category: 'electronics', inStock: true },\n { id: 3, name: 'Desk', price: 299, category: 'furniture', inStock: false },\n { id: 4, name: 'Chair', price: 199, category: 'furniture', inStock: true },\n { id: 5, name: 'Monitor', price: 399, category: 'electronics', inStock: true },\n])\n\nconst pageSize = 2\nconst pageIndex = 0\n\n// Build a base query — reuse it for both the page slice and the total count\nconst q = db\n .query('products')\n .equals('category', 'electronics')\n .filter((p) => p.inStock)\n .orderBy('price', 'asc')\n\n// count() respects limit/offset — returns records in the current page\nconst page = await q.limit(pageSize).offset(pageIndex * pageSize).toArray()\nconst pageCount = await q.limit(pageSize).offset(pageIndex * pageSize).count()\n\n// totalCount() ignores limit/offset/orderBy — returns the full filtered set\nconst total = await q.totalCount()\n\nconsole.log('Page:', page.map((p) => p.name))\nconsole.log('Page count:', pageCount, '/ Total matching:', total)\nconsole.log('Page 1 of', Math.ceil(total / pageSize))\n\n// startsWith with case-insensitive flag\nconst mice = await db.query('products').startsWith('name', 'm', { ignoreCase: true }).toArray()\nconsole.log('Starts with m:', mice.map((p) => p.name))\n\n// predicate delete\nconst removed = await db.query('products').filter((p) => !p.inStock).delete()\nconsole.log('Removed out-of-stock:', removed)\n\n// first()\nconst cheapest = await db.query('products').orderBy('price', 'asc').first()\nconsole.log('Cheapest:', cheapest?.name, cheapest?.price)",
|
|
43
|
-
"name": "Query Builder — Filters, Pagination, totalCount"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"id": "reactive-observe",
|
|
47
|
-
"code": "import { table } from '@vielzeug/vault'\nimport { createMemory } from '@vielzeug/vault/memory'\n\nconst db = createMemory({ schema: { users: table('id') } })\nconst snapshots = []\nconst stop = db.observe('users', (users) => snapshots.push(users.map((user) => user.name)))\n\nawait Promise.resolve()\nawait db.put('users', { id: 1, name: 'Ada' })\nawait Promise.resolve()\n\nconsole.log(snapshots) // [[], ['Ada']]\nstop()\nawait db.dispose()",
|
|
48
|
-
"name": "Reactive — observe()"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"id": "ttl-expiration",
|
|
52
|
-
"code": "import { table, ttl } from '@vielzeug/vault'\nimport { createLocalStorage } from '@vielzeug/vault/local-storage'\n\nconst schema = {\n cache: table('id'),\n}\n\nconst db = createLocalStorage({ name: 'cache-demo', schema })\n\n// ttl helpers produce a branded TtlMs value — raw numbers are rejected by the type system\nawait db.put('cache', { id: 'short', data: 'Expires in 1 second' }, ttl.seconds(1))\nawait db.put('cache', { id: 'long', data: 'Expires in 5 minutes' }, ttl.minutes(5))\nconsole.log('Stored records with TTL')\nconsole.log('Immediate read:', await db.get('cache', 'short'))\n\nawait new Promise((resolve) => setTimeout(resolve, 1500))\nconsole.log('After 1.5s:', await db.get('cache', 'short')) // expired — undefined\nconsole.log('Long-lived still here:', await db.get('cache', 'long'))\n\nconsole.log('ttl helpers:', {\n '100ms': ttl.ms(100),\n '5 minutes': ttl.minutes(5),\n '2 hours': ttl.hours(2),\n '7 days': ttl.days(7),\n})",
|
|
53
|
-
"name": "TTL & Expiration"
|
|
54
|
-
}
|
|
55
|
-
],
|
|
56
|
-
"typeSignatures": {
|
|
57
|
-
"VaultDisposedError": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';",
|
|
58
|
-
"VaultError": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';",
|
|
59
|
-
"VaultMigrationError": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';",
|
|
60
|
-
"VaultQuotaError": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';",
|
|
61
|
-
"VaultScopeError": "export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, VaultScopeError } from './errors';",
|
|
62
|
-
"scheduleExpiredPrune": "export { scheduleExpiredPrune } from './prune';",
|
|
63
|
-
"QueryBuilder": "export type { QueryBuilder } from './query';",
|
|
64
|
-
"isExpired": "export { isExpired, ttl } from './ttl';",
|
|
65
|
-
"ttl": "export { isExpired, ttl } from './ttl';",
|
|
66
|
-
"AnySchema": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
67
|
-
"BaseAdapterOptions": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
68
|
-
"DebugInfo": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
69
|
-
"DebugStats": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
70
|
-
"IterableVaultStore": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
71
|
-
"KeyOf": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
72
|
-
"MetricsEvent": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
73
|
-
"Observer": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
74
|
-
"RecordOf": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
75
|
-
"RecordValidator": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
76
|
-
"SchemaEntry": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
77
|
-
"TableBuilder": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
78
|
-
"TableValidators": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
79
|
-
"TransactionalVaultStore": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
80
|
-
"TtlMs": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
81
|
-
"Unsubscribe": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
82
|
-
"VaultKey": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
83
|
-
"VaultLogger": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
84
|
-
"VaultStore": "export type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n DebugStats,\n IterableVaultStore,\n KeyOf,\n MetricsEvent,\n Observer,\n RecordOf,\n RecordValidator,\n SchemaEntry,\n TableBuilder,\n TableValidators,\n TransactionalVaultStore,\n TtlMs,\n Unsubscribe,\n VaultKey,\n VaultLogger,\n VaultStore,\n} from './types';",
|
|
85
|
-
"table": "export { table } from './types';"
|
|
86
|
-
}
|
|
87
|
-
}
|
package/data/packages/ward.json
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"apiSource": "export { allow, deny, owns, predicate, ruleFor } from './builder';\nexport { ANONYMOUS, WILDCARD } from './constants';\nexport { WardConfigError, WardError, WardPredicateError } from './errors';\nexport { createWard } from './factory';\nexport { matchesPattern, patternCovers } from './resource';\nexport type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';\n",
|
|
3
|
-
"docs": {
|
|
4
|
-
"index": "---\ntitle: Ward — Deterministic authorization for TypeScript\ndescription: Typed authorization policies with wildcard matching, deterministic precedence, and decision tracing.\npackage: ward\ncategory: auth\nkeywords: [authorization, rbac, permissions, policy, roles, wildcard, predicates]\nrelated: [wayfinder, conduit, herald]\nexports: [createWard, allow, deny, ruleFor, owns, predicate, ANONYMOUS, WILDCARD, WardError, WardConfigError, WardPredicateError, matchesPattern, patternCovers]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"ward\" />\n\n## Why Ward?\n\nWard keeps authorization policies declarative and decision ordering deterministic. Define rules once, then explain or trace every permission decision without embedding role checks across handlers.\n\n```ts\n// Before\nconst canUpdate = user.roles.includes('editor') && post.authorId === user.id;\n\n// After\nimport { createWard, owns } from '@vielzeug/ward';\n\nconst ward = createWard([\n { role: 'editor', resource: 'posts', action: 'update', effect: 'allow', when: owns('authorId') },\n]);\n\nconst decision = ward.explain({ principal: user, resource: 'posts', action: 'update', data: post });\nconst canUpdate = decision.allowed;\n```\n\n| Feature | Ward | CASL | AccessControl |\n| ------------------------ | -------------------------------------------- | ---------------------------------------- | ---------------------------------------- |\n| Bundle size | <PackageInfo package=\"ward\" type=\"size\" /> | Larger policy engine | Larger policy engine |\n| Zero 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| Deterministic precedence | Priority, specificity, deny, order | Rule-dependent | Role-grant dependent |\n| Decision tracing | `trace()` candidates and winner | Manual inspection | Manual inspection |\n\n<div class=\"decision-callout\">\n\n**Use Ward when** your application needs typed role/resource/action policies with explainable, deterministic outcomes.\n\n**Consider framework-specific authorization when** your application only needs one framework's built-in route or component guard layer.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/ward\n```\n\n```sh [npm]\nnpm install @vielzeug/ward\n```\n\n```sh [yarn]\nyarn add @vielzeug/ward\n```\n\n:::\n\n## Quick Start\n\nCreate a small policy and handle both allowed and denied decisions at the request boundary.\n\n```ts\nimport { createWard } from '@vielzeug/ward';\n\nconst ward = createWard([\n { role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },\n { role: 'editor', resource: 'posts', action: 'update', effect: 'allow' },\n]);\n\nconst decision = ward.explain({\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n action: 'update',\n});\n\nif (decision.allowed) console.log('Update post');\nelse console.log(decision.reason);\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createWard()` creates immutable typed policy instances.\n- `allow()`, `deny()`, and `ruleFor()` build role/resource/action rules.\n- `WILDCARD` and `ANONYMOUS` model broad or unauthenticated access explicitly.\n- `owns()` and `predicate` constrain rules with synchronous request data.\n- `explain()`, `trace()`, and `detectConflicts()` make policy decisions diagnosable.\n- `forUser()` creates a principal-bound view for repeated checks.\n- `checkAll()` evaluates multiple resource/action pairs in one call.\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- [Wayfinder](/wayfinder/) — route middleware can enforce Ward decisions during navigation.\n- [Conduit](/conduit/) — inject a Ward policy into application services.\n- [Herald](/herald/) — publish authorization outcomes as typed application events.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
-
"api": "---\ntitle: Ward — API Reference\ndescription: Complete API reference for @vielzeug/ward.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| ----------------------------------- | -------------------------------------- | -------------- | -------------------------------------- |\n| `createWard` | Creates immutable policy | Sync | Rules cannot be mutated after creation |\n| `allow` / `deny` / `ruleFor` | Builds policy rules | Sync | Priority wins before specificity |\n| `Ward.explain` | Returns one decision | Sync | Pass resource data for predicate rules |\n| `Ward.trace` | Inspects decision candidates | Sync | Does not invoke the logger |\n| `Ward.forUser` | Binds a principal | Sync | Rebind when identity or roles change |\n| `checkAll` | Batch permission checks | Sync | Pass resource data for predicate rules |\n\n## Package Entry Point\n\n| Import | Purpose |\n| ------------------------- | -------------------------------------------------------------------- |\n| `@vielzeug/ward` | Rules, factory, predicates, errors, and public types |\n| `@vielzeug/ward/devtools` | `debugWard()` diagnostic factory |\n\n## Core Factory\n\n### `createWard(rules, options?)`\n\n```ts\ncreateWard<TAction extends string = string, TData = unknown>(\n rules: ReadonlyArray<Readonly<WardRule<TAction, TData>>>,\n options?: WardOptions<TAction, TData>,\n): Ward<TAction, TData>;\n```\n\nCreates an immutable ward instance. Validates `logger`, `onConflict`, and `maxConflicts` options before compiling rules; invalid values throw `WardConfigError`.\n\n## Rule Builders\n\n### `allow(role, resource, actions, options?)`\n\n### `deny(role, resource, actions, options?)`\n\n### `ruleFor(effect, role, resource, actions, options?)`\n\nAll three return `WardRule[]` (one rule per action).\n\n## Ward Methods\n\n### `checkAll(principal, checks)`\n\n```ts\ncheckAll(\n principal: UserPrincipal,\n checks: ReadonlyArray<WardCheck<TAction, TData>>,\n): WardDecisionResult<TAction, TData>[];\n```\n\n### `explain(input)`\n\n```ts\nexplain(input: WardDecisionInput<TAction, TData>): WardDecision<TAction, TData>;\n```\n\n`WardDecisionInput`:\n\n```ts\n{\n principal: Principal;\n resource: string;\n action: TAction;\n data?: TData;\n}\n```\n\n### `trace(input)`\n\n```ts\ntrace(input: WardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;\n```\n\nSame request shape as `explain()`. Returns winner + candidate list. Does not fire logger.\n\n### `allowedActions(input)`\n\n```ts\nallowedActions<TKnown extends TAction>(\n input: WardAllowedActionsInput<TKnown, TData>,\n): TKnown[];\n```\n\nInput shape:\n\n```ts\n{\n principal: Principal;\n resource: string;\n knownActions: readonly TKnown[];\n data?: TData;\n}\n```\n\n### `rulesInScope(input)`\n\n```ts\nrulesInScope(input: WardRulesInScopeInput<TData>): ReadonlyArray<Readonly<WardRule<TAction, TData>>>;\n```\n\nInput shape:\n\n```ts\n{\n principal: Principal;\n resource: string;\n data?: TData;\n}\n```\n\n### `detectConflicts()`\n\n```ts\ndetectConflicts(): readonly WardConflict<TAction, TData>[];\n```\n\n### `forUser(principal)`\n\n```ts\nforUser(principal: Principal): BoundWard<TAction, TData>;\n```\n\nReturns a principal-bound view.\n\n## `BoundWard` Methods\n\n```ts\ninterface BoundWard<TAction extends string = string, TData = unknown> {\n checkAll(checks: ReadonlyArray<WardCheck<TAction, TData>>): WardDecisionResult<TAction, TData>[];\n explain(input: BoundWardDecisionInput<TAction, TData>): WardDecision<TAction, TData>;\n trace(input: BoundWardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;\n allowedActions<TKnown extends TAction>(input: BoundWardAllowedActionsInput<TKnown, TData>): TKnown[];\n rulesInScope(input: BoundWardRulesInScopeInput<TData>): ReadonlyArray<Readonly<WardRule<TAction, TData>>>;\n}\n```\n\nBound input shapes remove `principal`:\n\n```ts\n{ resource: string; action: TAction; data?: TData } // explain/trace\n{ resource: string; knownActions: readonly TKnown[]; data?: TData } // allowedActions\n{ resource: string; data?: TData } // rulesInScope\n```\n\n## Predicate Helpers\n\n### `predicate.owns(attributeKey)`\n\n### `predicate.and(...predicates)`\n\n### `predicate.or(...predicates)`\n\n### `predicate.not(predicate)`\n\n### `owns(attributeKey)` (alias)\n\nPredicates run synchronously. Returning a Promise throws `WardPredicateError`.\n\n## Pattern Helpers\n\n### `matchesPattern(pattern, value): boolean`\n\n### `patternCovers(broad, narrow): boolean`\n\n## Devtools\n\n### `debugWard(rules, options?)`\n\nSub-path import: `@vielzeug/ward/devtools`.\n\n```ts\nimport { debugWard } from '@vielzeug/ward/devtools';\n```\n\n## Types\n\n```ts\nexport type Principal = UserPrincipal | null;\nexport type UserPrincipal = { id: string; roles: readonly string[] };\nexport type WardRule<TAction extends string = string, TData = unknown> = Readonly<{\n action: TAction | typeof WILDCARD;\n effect: 'allow' | 'deny';\n priority?: number;\n resource: string | typeof WILDCARD;\n role: string | typeof ANONYMOUS | readonly (string | typeof ANONYMOUS)[];\n when?: WardPredicate<TData>;\n}>;\nexport type WardDecisionInput<TAction extends string = string, TData = unknown> = {\n action: TAction;\n data?: TData;\n principal: Principal;\n resource: string;\n};\nexport type BoundWardDecisionInput<TAction extends string = string, TData = unknown> = Omit<\n WardDecisionInput<TAction, TData>,\n 'principal'\n>;\n```\n\n`Ward`, `BoundWard`, `WardDecision`, `WardDecisionResult`, `WardTrace`, `WardTraceCandidate`, `WardConflict`,\n`WardOptions`, `WardCheck`, `WardAllowedActionsInput`, `WardRulesInScopeInput`, `RuleContext`,\n`WardLoggerContext`, and `ConflictKind` are exported from the root entry point.\n\n## Errors\n\n- `WardError` is the base error class; use `WardError.is(value)` for narrowing.\n- `WardConfigError` reports malformed rules, invalid `createWard` options (`logger`, `onConflict`, `maxConflicts`), invalid principals, and strict conflict initialization.\n- `WardPredicateError` reports a throwing synchronous predicate and includes its `ruleIndex` and cause.\n",
|
|
6
|
-
"usage": "---\ntitle: Ward — Usage Guide\ndescription: Build deterministic authorization policies with immutable rule sets, wildcard support, and runtime predicates.\n---\n\n[[toc]]\n\n## Basic Usage\n\n```ts\nimport { WILDCARD, createWard } from '@vielzeug/ward';\n\nconst ward = createWard([\n { role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },\n { role: 'editor', resource: 'posts', action: 'update', effect: 'allow' },\n { role: 'blocked', resource: 'posts', action: WILDCARD, effect: 'deny', priority: 100 },\n]);\n```\n\nRules are immutable after creation. Create a new ward to update policy.\n\n## Explain a Decision\n\n```ts\nconst decision = ward.explain({\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n action: 'update',\n data: { authorId: 'u1' },\n});\n\nif (decision.allowed) {\n console.log(decision.rule);\n} else {\n console.log(decision.reason); // 'no-matching-rule' | 'explicit-deny'\n}\n```\n\n## Batch Decisions\n\n```ts\nconst results = ward.checkAll({ id: 'u1', roles: ['editor'] }, [\n { resource: 'posts', action: 'read' },\n { resource: 'posts', action: 'update', data: { authorId: 'u1' } },\n]);\n```\n\n## Bound Ward (`forUser`)\n\n```ts\nconst bound = ward.forUser({ id: 'u1', roles: ['editor'] });\n\nbound.explain({ resource: 'posts', action: 'read' });\nbound.trace({ resource: 'posts', action: 'update', data: { authorId: 'u1' } });\nbound.rulesInScope({ resource: 'posts' });\nbound.allowedActions({ resource: 'posts', knownActions: ['read', 'update', 'delete'] as const });\n```\n\n`forUser()` snapshots the principal. Re-bind when roles/identity change.\n\n## Allowed Actions\n\n`allowedActions()` evaluates a provided action set:\n\n```ts\nconst actions = ward.allowedActions({\n principal: { id: 'u1', roles: ['admin'] },\n resource: 'posts',\n knownActions: ['read', 'update', 'delete'] as const,\n});\n```\n\nIt does not fire the logger.\n\n## Rule Introspection\n\n```ts\nconst scoped = ward.rulesInScope({\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n});\n```\n\nUse optional `data` to filter predicate-gated matches.\n\n## Trace Candidates\n\n```ts\nconst trace = ward.trace({\n principal: { id: 'u1', roles: ['editor', 'blocked'] },\n resource: 'posts',\n action: 'read',\n});\n\ntrace.candidates.forEach((c) => {\n console.log(c.index, c.priority, c.score, c.won);\n});\n```\n\n`trace()` does not fire the logger.\n\n## Predicate Helpers\n\n```ts\nimport { owns, predicate } from '@vielzeug/ward';\n\nconst isOwner = owns('authorId');\nconst canEdit = predicate.and(isOwner, ({ principal }) => principal !== null);\n```\n\nAsync predicates are rejected at runtime with `WardPredicateError`.\n\n## Request Guards\n\nUse `explain()` directly at request boundaries. Extract the principal from your framework's request object and pass it to Ward:\n\n```ts\nconst principal = await extractPrincipal(req);\nconst decision = ward.explain({ principal, resource: 'posts', action: 'read' });\n\nif (!decision.allowed) {\n return res.status(403).json({ error: decision.reason });\n}\n```\n\n## Testing\n\nTest policy outcomes through `explain()` so each test captures an allowed, explicit-deny, or no-match result.\n\n```ts\nimport { expect, it } from 'vitest';\n\nit('denies an action with no matching rule', () => {\n expect(\n ward.explain({ principal: { id: 'u1', roles: ['viewer'] }, resource: 'posts', action: 'delete' }).allowed,\n ).toBe(false);\n});\n```\n\n## Framework Integration\n\nKeep Ward independent from rendering frameworks. Obtain a current principal from framework state, bind it with `forUser()`, and rebind whenever identity or roles change.\n\n::: code-group\n\n```tsx [React]\nconst actions = ward.forUser(user).allowedActions({ resource: 'posts', knownActions: ['read', 'update'] as const });\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nconst actions = ward\n .forUser(user.value)\n .allowedActions({ resource: 'posts', knownActions: ['read', 'update'] as const });\n</script>\n```\n\n```ts [Svelte]\nconst actions = ward.forUser(user).allowedActions({ resource: 'posts', knownActions: ['read', 'update'] as const });\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### With Wayfinder\n\nEnforce Ward decisions in Wayfinder route guards by calling `explain()` inside the guard callback:\n\n```ts\nconst decision = ward.explain({ principal, resource: route.meta.resource, action: 'read' });\n\nif (!decision.allowed) return '/forbidden';\n```\n\n### With Conduit\n\nInject a Ward instance into Conduit-managed services so authorization checks share a single compiled policy:\n\n```ts\nconst ward = createWard(rules);\ncontainer.register('ward', ward);\n```\n\n## Best Practices\n\n- Model default-deny by adding only explicit allow rules.\n- Keep predicates synchronous and provide required resource data.\n- Assign priority deliberately before relying on specificity.\n- Rebind `forUser()` when identity or roles change.\n- Use `trace()` and `detectConflicts()` to diagnose policy behavior.\n- Enforce authorization again at request and mutation boundaries.\n",
|
|
7
|
-
"examples": "---\ntitle: Ward — Examples\ndescription: Practical examples and recipes for ward.\n---\n\n## Examples\n\n- [Blog Roles](./examples/blog-roles.md)\n- [Multi-Role Rules](./examples/multi-role-rules.md)\n- [Wildcard Action](./examples/wildcard-action.md)\n- [Priority and Overrides](./examples/inheritance-and-overrides.md)\n- [Bound Guard in UI Layer](./examples/bound-guard-in-ui-layer.md)\n- [Rule Specificity](./examples/disabling-wildcard-fallback.md)\n- [Logger for Auditing](./examples/logger-for-auditing.md)\n- [Fresh Ward Per Test](./examples/snapshot-restore-for-test-isolation.md)\n- [Conflict Detection](./examples/conflict-detection.md)\n- [Trace a Decision](./examples/trace-decision.md)\n"
|
|
8
|
-
},
|
|
9
|
-
"examples": [
|
|
10
|
-
{
|
|
11
|
-
"id": "basic-rules",
|
|
12
|
-
"code": "import { ANONYMOUS, WILDCARD, allow, createWard, deny } from '@vielzeug/ward'\n\n// Role-based access control with wildcard and anonymous support\nconst ward = createWard([\n ...allow(WILDCARD, 'posts', ['read']),\n ...allow('editor', 'posts', ['update']),\n ...deny('blocked', WILDCARD, [WILDCARD]),\n ...allow(ANONYMOUS, 'posts', ['read']),\n])\n\nconst viewer = { id: 'u1', roles: ['viewer'] }\nconst editor = { id: 'u2', roles: ['editor'] }\nconst blocked = { id: 'u3', roles: ['blocked'] }\n\nconst explain = (p: typeof viewer | null, action: string) =>\n ward.explain({ action, principal: p, resource: 'posts' }).allowed\n\nconsole.log('viewer read: ', explain(viewer, 'read')) // true\nconsole.log('viewer update:', explain(viewer, 'update')) // false\nconsole.log('editor update:', explain(editor, 'update')) // true\nconsole.log('blocked read: ', explain(blocked, 'read')) // false\nconsole.log('anon read: ', explain(null, 'read')) // true",
|
|
13
|
-
"name": "Basic Rules"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"id": "basic-setup",
|
|
17
|
-
"code": "import { ANONYMOUS, allow, createWard } from '@vielzeug/ward'\n\n// role accepts a string or an array of strings (OR semantics)\nconst ward = createWard([\n ...allow(['viewer', 'editor', 'admin'], 'posts', ['read']),\n ...allow(['editor', 'admin'], 'posts', ['update']),\n ...allow('admin', 'posts', ['delete']),\n ...allow(ANONYMOUS, 'posts', ['read']),\n])\n\nconst viewer = { id: '1', roles: ['viewer'] }\nconst editor = { id: '2', roles: ['editor'] }\nconst admin = { id: '3', roles: ['admin'] }\n\nconst can = (p: typeof viewer | null, action: string) =>\n ward.explain({ action, principal: p, resource: 'posts' }).allowed\n\nconsole.log('Viewer can read:', can(viewer, 'read')) // true\nconsole.log('Viewer can update:', can(viewer, 'update')) // false\nconsole.log('Editor can update:', can(editor, 'update')) // true\nconsole.log('Admin can delete:', can(admin, 'delete')) // true\nconsole.log('Anonymous can read:', can(null, 'read')) // true",
|
|
18
|
-
"name": "Basic Setup — Multi-Role Rules"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"id": "batch-decisions",
|
|
22
|
-
"code": "import { allow, createWard, deny } from '@vielzeug/ward'\n\n// checkAll returns WardDecisionResult[] — each entry carries resource + action\nconst ward = createWard([\n ...allow('editor', 'posts', ['read', 'update']),\n ...deny('editor', 'posts', ['delete']),\n])\n\nconst editor = { id: 'u1', roles: ['editor'] }\n\nconst results = ward.checkAll(editor, [\n { resource: 'posts', action: 'read' },\n { resource: 'posts', action: 'update' },\n { resource: 'posts', action: 'delete' },\n { resource: 'comments', action: 'read' },\n])\n\nfor (const r of results) {\n const status = r.allowed ? '✅ allow' : `❌ ${r.reason}`\n console.log(`${r.resource}:${r.action.padEnd(8)} ${status}`)\n}",
|
|
23
|
-
"name": "Batch Decisions"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"id": "bound-view",
|
|
27
|
-
"code": "import { allow, createWard, deny, predicate } from '@vielzeug/ward'\n\n// Principal-bound view: capture user once, check many times\nconst ward = createWard([\n ...allow('editor', 'posts', ['read', 'update']),\n // delete: allow only when user owns the post (higher priority wins)\n ...allow('editor', 'posts', ['delete'], { when: predicate.owns('authorId'), priority: 1 }),\n ...deny('editor', 'posts', ['delete'], { priority: 0 }),\n])\n\nconst user = ward.forUser({ id: 'alice', roles: ['editor'] })\n\nconsole.log('read: ', user.explain({ action: 'read', resource: 'posts' }).allowed)\nconsole.log('update: ', user.explain({ action: 'update', resource: 'posts' }).allowed)\n\n// delete requires ownership\nconst myPost = { authorId: 'alice' }\nconst otherPost = { authorId: 'bob' }\nconsole.log('delete own: ', user.explain({ action: 'delete', data: myPost, resource: 'posts' }).allowed)\nconsole.log('delete other: ', user.explain({ action: 'delete', data: otherPost, resource: 'posts' }).allowed)\n\n// allowedActions — enumerate what alice can do\nconst actions = user.allowedActions({ data: myPost, knownActions: ['read', 'update', 'delete'], resource: 'posts' })\nconsole.log('allowed: ', actions)",
|
|
28
|
-
"name": "Bound View"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"id": "conflict-detection",
|
|
32
|
-
"code": "// Detect rule conflicts — duplicate and shadowed rules — at startup\nimport { createWard } from '@vielzeug/ward'\n\nconst ward = createWard(\n [\n // Rule 0: viewer can read posts\n { role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },\n // Rule 1: duplicate — same (role, resource, action), different effect.\n // One of these can never fire.\n { role: 'viewer', resource: 'posts', action: 'read', effect: 'deny' },\n // Rule 2+3: shadowed — the wildcard-action allow at higher priority\n // will always win over this specific deny.\n { role: 'admin', resource: 'posts', action: '*', effect: 'allow', priority: 10 },\n { role: 'admin', resource: 'posts', action: 'delete', effect: 'deny', priority: 5 },\n ],\n {\n onConflict: (c) => {\n if (c.kind === 'duplicate') {\n console.log(`[conflict] duplicate: Rule[${c.indexA}] always wins over Rule[${c.indexB}]`)\n } else {\n console.log(`[conflict] shadowed: Rule[${c.shadowedIndex}] always overridden by Rule[${c.shadowingIndex}]`)\n }\n },\n },\n)\n\nconst conflicts = ward.detectConflicts()\nconsole.log('Total conflicts detected:', conflicts.length)\n\nconflicts.forEach((c) => {\n if (c.kind === 'duplicate') {\n console.log(` - duplicate: Rule[${c.indexA}] (${c.ruleA.effect}) vs Rule[${c.indexB}] (${c.ruleB.effect})`)\n } else {\n console.log(` - shadowed: Rule[${c.shadowedIndex}] (${c.shadowedRule.effect}) by Rule[${c.shadowingIndex}] (${c.shadowingRule.effect})`)\n }\n})",
|
|
33
|
-
"name": "Conflict Detection"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"id": "dynamic-permissions",
|
|
37
|
-
"code": "import { allow, createWard, predicate } from '@vielzeug/ward'\n\nconst ward = createWard([\n ...allow('user', 'posts', ['update'], { when: predicate.owns('authorId') }),\n])\n\nconst user1 = { id: 'user1', roles: ['user'] }\nconst user2 = { id: 'user2', roles: ['user'] }\nconst post = { id: 'post1', authorId: 'user1', title: 'My Post' }\n\nconsole.log('Author can update: ', ward.explain({ action: 'update', data: post, principal: user1, resource: 'posts' }).allowed)\nconsole.log('Non-author can update: ', ward.explain({ action: 'update', data: post, principal: user2, resource: 'posts' }).allowed)",
|
|
38
|
-
"name": "Dynamic Permissions — Ownership Rules"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"id": "multi-role-rules",
|
|
42
|
-
"code": "import { ANONYMOUS, createWard } from '@vielzeug/ward'\n\n// A single rule can cover multiple roles with array syntax.\n// Semantics are OR: the principal must hold at least one of the listed roles.\nconst ward = createWard([\n // Everyone (including anonymous) can read public content\n { role: [ANONYMOUS, 'user', 'moderator', 'admin'], resource: 'articles', action: 'read', effect: 'allow' },\n // Registered users and above can comment\n { role: ['user', 'moderator', 'admin'], resource: 'articles', action: 'comment', effect: 'allow' },\n // Moderators and admins can remove content\n { role: ['moderator', 'admin'], resource: 'articles', action: 'delete', effect: 'allow' },\n // Only admins can pin articles\n { role: 'admin', resource: 'articles', action: 'pin', effect: 'allow' },\n])\n\nconst guest = null\nconst user = { id: '1', roles: ['user'] }\nconst moderator = { id: '2', roles: ['moderator'] }\nconst admin = { id: '3', roles: ['admin'] }\n\nconst ACTIONS = ['read', 'comment', 'delete', 'pin'] as const\n\nfor (const [label, principal] of [['guest', guest], ['user', user], ['moderator', moderator], ['admin', admin]] as const) {\n const allowed = ward.allowedActions({ knownActions: ACTIONS, principal, resource: 'articles' })\n console.log(`${label} can:`, allowed)\n}",
|
|
43
|
-
"name": "Multi-Role Rules"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"id": "permission-checks",
|
|
47
|
-
"code": "import { allow, createWard, deny } from '@vielzeug/ward'\n\nconst ward = createWard([\n ...allow('editor', 'articles', ['read', 'create', 'update']),\n ...deny('editor', 'articles', ['delete']),\n ...allow('viewer', 'articles', ['read']),\n])\n\nconst editor = { id: '1', roles: ['editor'] }\nconst viewer = { id: '2', roles: ['viewer'] }\n\nconsole.log('Editor can read: ', ward.explain({ action: 'read', principal: editor, resource: 'articles' }).allowed)\nconsole.log('Editor can delete: ', ward.explain({ action: 'delete', principal: editor, resource: 'articles' }).allowed)\nconsole.log('Viewer can create: ', ward.explain({ action: 'create', principal: viewer, resource: 'articles' }).allowed)\n\n// Full decision object with deny reason\nconst decision = ward.explain({ action: 'delete', principal: editor, resource: 'articles' })\nif (!decision.allowed) console.log('Deny reason:', decision.reason)",
|
|
48
|
-
"name": "Permission Checks"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"id": "permission-management",
|
|
52
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n { role: 'user', resource: 'comments', action: 'read', effect: 'allow' },\n { role: 'moderator', resource: 'comments', action: 'delete', effect: 'allow' },\n { role: 'banned', resource: 'comments', action: 'delete', effect: 'deny', priority: 100 },\n])\n\nconst moderator = { id: 'm1', roles: ['moderator'] }\nconst bannedModerator = { id: 'm2', roles: ['moderator', 'banned'] }\n\nconsole.log('Rules in scope for moderator:', ward.rulesInScope({ principal: moderator, resource: 'comments' }))\nconsole.log('Single decision:', ward.explain({ action: 'delete', principal: bannedModerator, resource: 'comments' }))\nconsole.log('Batch decisions:', ward.checkAll(bannedModerator, [\n { resource: 'comments', action: 'read' },\n { resource: 'comments', action: 'delete' },\n]))",
|
|
53
|
-
"name": "Introspection and Batch Decisions"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"id": "role-hierarchy",
|
|
57
|
-
"code": "import { allow, createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n ...allow('editor', 'posts', ['read']),\n ...allow('moderator', 'posts', ['delete']),\n])\n\nconst user = { id: '42', roles: ['editor', 'moderator'] }\nconst bound = ward.forUser(user)\n\nconsole.log('Can read posts: ', bound.explain({ action: 'read', resource: 'posts' }).allowed)\nconsole.log('Can delete posts: ', bound.explain({ action: 'delete', resource: 'posts' }).allowed)\nconsole.log('Allowed actions: ', bound.allowedActions({ knownActions: ['read', 'delete', 'update'], resource: 'posts' }))",
|
|
58
|
-
"name": "Bound Multi-Role Access"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"id": "rule-factories",
|
|
62
|
-
"code": "import { allow, createWard, deny, predicate } from '@vielzeug/ward'\n\n// Rule factories with ownership predicate\nconst ward = createWard([\n ...allow('viewer', 'posts', ['read']),\n ...allow('editor', 'posts', ['read', 'update']),\n // Ownership predicate — update only your own posts\n ...allow('editor', 'posts', ['update'], { when: predicate.owns('authorId') }),\n ...deny('blocked', 'posts', ['read', 'update']),\n])\n\nconst editor = { id: 'u1', roles: ['editor'] }\n\n// read: allowed (no predicate required)\nconsole.log('read: ', ward.explain({ action: 'read', principal: editor, resource: 'posts' }).allowed)\n\n// update with own post\nconst myPost = { authorId: 'u1' }\nconsole.log('update own: ', ward.explain({ action: 'update', data: myPost, principal: editor, resource: 'posts' }).allowed)\n\n// update someone else's post\nconst otherPost = { authorId: 'u2' }\nconsole.log('update other:', ward.explain({ action: 'update', data: otherPost, principal: editor, resource: 'posts' }).allowed)",
|
|
63
|
-
"name": "Rule Factories & Predicates"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"id": "trace-decision",
|
|
67
|
-
"code": "// Inspect all matching rule candidates and why a particular rule won\nimport { WILDCARD, createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n { role: WILDCARD, resource: 'posts', action: 'read', effect: 'allow', priority: 0 },\n { role: 'editor', resource: 'posts', action: 'read', effect: 'allow', priority: 0 },\n { role: 'blocked', resource: 'posts', action: 'read', effect: 'deny', priority: 5 },\n])\n\nconst { decision, candidates } = ward.trace({\n action: 'read',\n principal: { id: 'u1', roles: ['editor', 'blocked'] },\n resource: 'posts',\n})\n\ncandidates.forEach(({ index, rule, priority, score, won }) => {\n console.log(\n won ? '[WINNER]' : '[ ]',\n `Rule[${index}]`,\n `effect=${rule.effect}`,\n `role=${rule.role}`,\n `priority=${priority}`,\n `score=${score}`,\n )\n})\n\nconsole.log('Decision:', decision.allowed ? 'allow' : `deny (${decision.reason})`)",
|
|
68
|
-
"name": "Trace — Inspect Matching Candidates"
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id": "wildcard-permissions",
|
|
72
|
-
"code": "import { WILDCARD, allow, createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n ...allow('admin', WILDCARD, [WILDCARD]),\n ...allow('user', 'posts', ['read']),\n])\n\nconst admin = { id: '1', roles: ['admin'] }\nconst user = { id: '2', roles: ['user'] }\n\nconst can = (p: typeof admin, resource: string, action: string) =>\n ward.explain({ action, principal: p, resource }).allowed\n\nconsole.log('Admin can delete users:', can(admin, 'users', 'delete'))\nconsole.log('User can read posts:', can(user, 'posts', 'read'))\nconsole.log('User can delete posts:', can(user, 'posts', 'delete'))\nconsole.log('Known actions for admin:', ward.allowedActions({ knownActions: ['read', 'delete', 'archive'], principal: admin, resource: 'users' }))",
|
|
73
|
-
"name": "Wildcard Rules"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"typeSignatures": {
|
|
77
|
-
"allow": "export { allow, deny, owns, predicate, ruleFor } from './builder';",
|
|
78
|
-
"deny": "export { allow, deny, owns, predicate, ruleFor } from './builder';",
|
|
79
|
-
"owns": "export { allow, deny, owns, predicate, ruleFor } from './builder';",
|
|
80
|
-
"predicate": "export { allow, deny, owns, predicate, ruleFor } from './builder';",
|
|
81
|
-
"ruleFor": "export { allow, deny, owns, predicate, ruleFor } from './builder';",
|
|
82
|
-
"ANONYMOUS": "export { ANONYMOUS, WILDCARD } from './constants';",
|
|
83
|
-
"WILDCARD": "export { ANONYMOUS, WILDCARD } from './constants';",
|
|
84
|
-
"WardConfigError": "export { WardConfigError, WardError, WardPredicateError } from './errors';",
|
|
85
|
-
"WardError": "export { WardConfigError, WardError, WardPredicateError } from './errors';",
|
|
86
|
-
"WardPredicateError": "export { WardConfigError, WardError, WardPredicateError } from './errors';",
|
|
87
|
-
"createWard": "export { createWard } from './factory';",
|
|
88
|
-
"matchesPattern": "export { matchesPattern, patternCovers } from './resource';",
|
|
89
|
-
"patternCovers": "export { matchesPattern, patternCovers } from './resource';",
|
|
90
|
-
"BoundWard": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
91
|
-
"BoundWardAllowedActionsInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
92
|
-
"BoundWardDecisionInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
93
|
-
"BoundWardRulesInScopeInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
94
|
-
"ConflictKind": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
95
|
-
"Principal": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
96
|
-
"RuleContext": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
97
|
-
"UserPrincipal": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
98
|
-
"Ward": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
99
|
-
"WardAllowedActionsInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
100
|
-
"WardCheck": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
101
|
-
"WardConflict": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
102
|
-
"WardDecision": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
103
|
-
"WardDecisionInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
104
|
-
"WardDecisionResult": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
105
|
-
"WardLoggerContext": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
106
|
-
"WardOptions": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
107
|
-
"WardPredicate": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
108
|
-
"WardRule": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
109
|
-
"WardRulesInScopeInput": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
110
|
-
"WardTrace": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';",
|
|
111
|
-
"WardTraceCandidate": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n ConflictKind,\n Principal,\n RuleContext,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardLoggerContext,\n WardOptions,\n WardPredicate,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';"
|
|
112
|
-
}
|
|
113
|
-
}
|