@vielzeug/codex 3.1.1 → 26.10.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/data/catalog.json +124 -36
- package/data/llms-full.txt +2335 -679
- package/data/llms.txt +5 -3
- package/data/manifest.json +1 -1
- package/data/packages/arsenal.json +30 -6
- package/data/packages/assay.json +11 -19
- package/data/packages/clockwork.json +30 -27
- package/data/packages/codex.json +4 -4
- package/data/packages/conduit.json +33 -34
- package/data/packages/courier.json +15 -14
- package/data/packages/dnd.json +2 -2
- package/data/packages/familiar.json +24 -3
- package/data/packages/flux.json +9 -6
- package/data/packages/focus.json +16 -6
- package/data/packages/gesture.json +10 -4
- package/data/packages/herald.json +7 -8
- package/data/packages/illusionist.json +1 -1
- package/data/packages/keymap.json +11 -7
- package/data/packages/ledger.json +25 -25
- package/data/packages/lingua.json +2 -2
- package/data/packages/mesh.json +29 -31
- package/data/packages/necromancer.json +3 -2
- package/data/packages/orbit.json +4 -3
- package/data/packages/ore.json +4 -3
- package/data/packages/postmaster.json +24 -24
- package/data/packages/pulse.json +28 -28
- package/data/packages/refine.json +6 -4
- package/data/packages/ripple.json +2 -2
- package/data/packages/sandbox.json +2 -2
- package/data/packages/scout.json +13 -7
- package/data/packages/scroll.json +7 -7
- package/data/packages/sentinel.json +26 -15
- package/data/packages/sigil.json +3 -3
- package/data/packages/spell.json +25 -30
- package/data/packages/tandem.json +29 -0
- package/data/packages/tavern.json +23 -0
- package/data/packages/tempo.json +44 -42
- package/data/packages/vault.json +9 -19
- package/data/packages/ward.json +26 -29
- package/data/packages/wayfinder.json +20 -13
- package/data/refine.json +746 -65
- package/data/search.json +198 -163
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/package.json +5 -12
- package/skills/vielzeug/SKILL.md +2 -0
- package/dist/advanced.d.ts +0 -12
- package/dist/advanced.js +0 -13
- package/dist/advanced.js.map +0 -1
package/data/packages/ward.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"apiSource": "export { ANONYMOUS, WILDCARD } from './constants';\nexport { WardConditionError, WardConfigError, WardError } from './errors';\nexport { createWard } from './factory';\nexport { matchesPattern
|
|
2
|
+
"apiSource": "export { ANONYMOUS, WILDCARD } from './constants';\nexport { WardConditionError, WardConfigError, WardError } from './errors';\nexport { createWard } from './factory';\nexport { matchesPattern } from './resource';\nexport { allow, deny, predicate } from './rules';\nexport type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';\n",
|
|
3
3
|
"docs": {
|
|
4
|
-
"index": "---\ntitle: Ward — Ordered authorization rules\ndescription: Zero-dependency authorization decisions with ordered rules, role helpers, ownership predicates, and default deny.\npackage: ward\ncategory: security\nkeywords: [authorization, rbac, permissions, policy, roles, predicates]\nexports: [createWard, allow, deny, predicate, ANONYMOUS, WILDCARD, matchesPattern
|
|
5
|
-
"api": "---\ntitle: Ward — API Reference\ndescription: Ordered authorization rules, typed decisions, immutable policies, and observability.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createWard()` | Compile immutable ordered rules | Sync | First match wins |\n| `allow()` / `deny()` | Create role-conditioned rules | Sync | One rule is produced per action |\n| `predicate` | Compose typed synchronous conditions | Sync | Async results throw |\n| `matchesPattern()` | Match exact and wildcard values | Sync | `posts:*` does not match `posts` |\n
|
|
6
|
-
"usage": "---\ntitle: Ward — Usage Guide\ndescription: Define ordered role, wildcard, attribute, and ownership authorization rules.\n---\n\n[[toc]]\n\n## Basic Usage\n\n```ts\nimport { allow, createWard, deny, WILDCARD } from '@vielzeug/ward';\n\nconst ward = createWard<'read' | 'update', 'posts'>([\n deny('blocked', WILDCARD, [WILDCARD]),\n allow('editor', 'posts', ['read', 'update']),\n allow('viewer', 'posts', ['read']),\n]);\n\nconst decision = ward.decide({\n action: 'update',\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n});\n```\n\nOrder is policy. Put exceptions and explicit denies before broad allows. No match returns a default-deny decision with `matched: false`.\n\n## Check ownership with typed attributes\n\n```ts\nimport { allow, createWard, predicate } from '@vielzeug/ward';\n\ntype Attributes = { authorId: string; status: 'draft' | 'published' };\n\nconst ward = createWard<'update', 'posts', Attributes>([\n allow<'update', 'posts', Attributes>('editor', 'posts', ['update'], {\n when: predicate.owns<Attributes>('authorId'),\n }),\n]);\n\nward.decide({\n action: 'update',\n attributes: { authorId: post.authorId, status: post.status },\n principal,\n resource: 'posts',\n});\n```\n\nAttribute values support finite numbers, strings, booleans, `null`, arrays, and plain objects. Ward rejects class instances, dates, maps, sets, functions, `undefined`, non-finite numbers, and circular values.\n\n## Combine conditions\n\n```ts\nconst canPublishOwnPost = predicate.and<Attributes>(\n predicate.
|
|
4
|
+
"index": "---\ntitle: Ward — Ordered authorization rules\ndescription: Zero-dependency authorization decisions with ordered rules, role helpers, ownership predicates, and default deny.\npackage: ward\ncategory: security\nkeywords: [authorization, rbac, permissions, policy, roles, predicates]\nexports: [createWard, allow, deny, predicate, ANONYMOUS, WILDCARD, matchesPattern]\nrelated: [herald, postmaster, refine]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"ward\" />\n\n## Why Ward?\n\nWard evaluates immutable ordered rules. The first matching rule wins; no match means deny. Typed action, resource, and attribute contracts keep role and ownership policies explicit.\n\n## Quick Start\n\n```ts\nimport { allow, createWard, deny, predicate, WILDCARD } from '@vielzeug/ward';\n\nconst ward = createWard([\n deny('blocked', WILDCARD, [WILDCARD]),\n allow('editor', 'posts', ['read', 'update'], { when: predicate.owns('authorId') }),\n allow('viewer', 'posts', ['read']),\n]);\n\nconst decision = ward.decide({\n action: 'update',\n attributes: { authorId: 'u1' },\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n});\n```\n\n| Feature | Ward | Inline conditionals |\n| --- | --- | --- |\n| Ordered first-match policy | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual |\n| Default deny | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual |\n| Typed actions | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Partial |\n| Ownership predicates | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n## Features\n\n<div class=\"features-grid\">\n\n- Ordered `allow` and `deny` decisions\n- Declarative `roles` data on every rule\n- Concise role/action rule factories\n- `predicate.owns()`, `and()`, `or()`, and `not()`\n- Exact, namespace, and wildcard action/resource patterns\n- Immutable rules, principals, and JSON-compatible attributes\n- Typed batch, bound-principal, and allowed-action checks\n- `tap()` decision observability\n- Default deny when no rule matches\n\n</div>\n\n## Installation\n\n```sh\npnpm add @vielzeug/ward\n```\n\n## Documentation\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n## See Also\n\n- [Postmaster](/postmaster/) — authorize durable job actions at application boundaries.\n- [Refine](/refine/) — consume decisions in accessible UI components.\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Ward — API Reference\ndescription: Ordered authorization rules, typed decisions, immutable policies, and observability.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createWard()` | Compile immutable ordered rules | Sync | First match wins |\n| `allow()` / `deny()` | Create role-conditioned rules | Sync | One rule is produced per action |\n| `predicate` | Compose typed synchronous conditions | Sync | Async results throw |\n| `matchesPattern()` | Match exact and wildcard values | Sync | `posts:*` does not match `posts` |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/ward` | All runtime APIs, errors, constants, and public types |\n\n## Core API\n\n### `createWard()`\n\n```ts\nfunction createWard<Action extends string, Resource extends string, Attributes extends WardAttributes>(\n rules?: readonly (WardRule<Action, Resource, Attributes> | readonly WardRule<Action, Resource, Attributes>[])[],\n): Ward<Action, Resource, Attributes>\n```\n\nReturns an immutable ordered policy. Nested rule arrays are flattened once. Rules, declarative attributes, and bound principals are snapshotted.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `rules` | `readonly (WardRule \\| readonly WardRule[])[]` | Rules in first-match order |\n\n**Returns:** `Ward<Action, Resource, Attributes>`.\n\n```ts\nimport { allow, createWard } from '@vielzeug/ward';\n\nconst ward = createWard<'read', 'posts'>([allow('viewer', 'posts', ['read'])]);\n```\n\n| Method | Returns | Behavior |\n| --- | --- | --- |\n| `decide(input)` | `WardDecision` | Evaluates and emits one decision event |\n| `checkAll(inputs)` | `WardDecision[]` | Evaluates each input in order |\n| `allowedActions(input)` | `Action[]` | Deduplicates and filters the derived known actions without emitting events |\n| `forPrincipal(principal?)` | `BoundWard` | Binds an immutable principal snapshot |\n| `tap(handler, options?)` | `() => void` | Observes decisions; supports `AbortSignal` |\n| `knownActions` | `readonly Action[]` | Exact allow-side actions derived from the compiled rules, in declaration order |\n| `rules` | `readonly WardRule[]` | Immutable compiled rules |\n\n---\n\n### `allow()` and `deny()`\n\n```ts\nallow<Action, Resource, Attributes>(role, resource, actions, options?): WardRule[]\ndeny<Action, Resource, Attributes>(role, resource, actions, options?): WardRule[]\n```\n\nReturn one role-conditioned rule per action. The role list is stored as declarative `roles` data on each rule, not a condition callback.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `role` | `string \\| readonly string[]` | Role, role alternatives, `ANONYMOUS`, or `WILDCARD` |\n| `resource` | `WardPattern<Resource>` | Exact or wildcard resource |\n| `actions` | `readonly WardPattern<Action>[]` | Exact or wildcard actions |\n| `options.when` | `WardCondition<Attributes>` | Additional synchronous condition |\n\n**Returns:** `WardRule<Action, Resource, Attributes>[]`.\n\n```ts\nimport { allow, deny, WILDCARD } from '@vielzeug/ward';\n\nconst rules = [deny('blocked', WILDCARD, [WILDCARD]), allow(['editor', 'admin'], 'posts', ['read'])];\n```\n\n---\n\n### `predicate`\n\n```ts\npredicate.owns<Attributes>(attribute)\npredicate.and<Attributes>(...conditions)\npredicate.or<Attributes>(...conditions)\npredicate.not<Attributes>(condition)\n```\n\nReturns typed `WardCondition` functions. `owns()` accepts only a string key from the selected attribute type. Role checks do not need a predicate — declare `roles` on the rule instead.\n\n---\n\n### Pattern helpers\n\n```ts\nmatchesPattern(pattern: string, value: string): boolean\n```\n\n`matchesPattern()` supports exact values, `*`, and namespace wildcards such as `posts:*`.\n\n## Types\n\n```ts\ntype WardAttributeValue =\n | boolean\n | number\n | string\n | null\n | readonly WardAttributeValue[]\n | { readonly [key: string]: WardAttributeValue };\n\ntype WardAttributes = Readonly<Record<string, WardAttributeValue>>;\ntype WardPattern<Value extends string> = Value | typeof WILDCARD | `${string}:*`;\n```\n\n```ts\ntype UserPrincipal = Readonly<{\n attributes?: WardAttributes;\n id: string;\n roles: readonly string[];\n}>;\n\ntype Principal = UserPrincipal | null;\n```\n\n```ts\ntype WardConditionInput<Attributes extends WardAttributes> = Readonly<{\n attributes?: Attributes;\n principal: Principal;\n}>;\n\ntype WardCondition<Attributes extends WardAttributes> = (\n input: WardConditionInput<Attributes>,\n) => boolean;\n```\n\n```ts\ntype WardRule<Action extends string, Resource extends string, Attributes extends WardAttributes> = Readonly<{\n action: WardPattern<Action>;\n attributes?: Attributes;\n condition?: WardCondition<Attributes>;\n effect: 'allow' | 'deny';\n resource: WardPattern<Resource>;\n roles?: readonly string[];\n}>;\n```\n\n```ts\ntype WardDecisionInput<Action extends string, Resource extends string, Attributes extends WardAttributes> = Readonly<{\n action: Action;\n attributes?: Attributes;\n principal?: Principal;\n resource: Resource;\n}>;\n```\n\n```ts\ntype WardDecision<Action extends string, Resource extends string, Attributes extends WardAttributes> =\n | { effect: 'allow' | 'deny'; matched: true; reason: string; rule: WardRule<Action, Resource, Attributes> }\n | { effect: 'deny'; matched: false; reason: string; rule?: never };\n```\n\n```ts\ntype WardEvent<Action extends string, Resource extends string, Attributes extends WardAttributes> = Readonly<{\n decision: WardDecision<Action, Resource, Attributes>;\n input: WardDecisionInput<Action, Resource, Attributes> & { principal: Principal };\n type: 'decision';\n}>;\n```\n\n`BoundWard` is `Pick<Ward, 'allowedActions' | 'checkAll' | 'decide'>` for the principal-bound form, and `BoundWardDecisionInput` is the principal-free decision input.\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `WardError` | Base for Ward-originated errors | Standard `cause` support |\n| `WardConfigError` | Invalid rules, principals, inputs, or attribute values | Configuration message with field path |\n| `WardConditionError` | Thrown, async, or non-boolean condition result | `ruleIndex`, `cause` |\n",
|
|
6
|
+
"usage": "---\ntitle: Ward — Usage Guide\ndescription: Define ordered role, wildcard, attribute, and ownership authorization rules.\n---\n\n[[toc]]\n\n## Basic Usage\n\n```ts\nimport { allow, createWard, deny, WILDCARD } from '@vielzeug/ward';\n\nconst ward = createWard<'read' | 'update', 'posts'>([\n deny('blocked', WILDCARD, [WILDCARD]),\n allow('editor', 'posts', ['read', 'update']),\n allow('viewer', 'posts', ['read']),\n]);\n\nconst decision = ward.decide({\n action: 'update',\n principal: { id: 'u1', roles: ['editor'] },\n resource: 'posts',\n});\n```\n\nOrder is policy. Put exceptions and explicit denies before broad allows. No match returns a default-deny decision with `matched: false`.\n\n## Check ownership with typed attributes\n\n```ts\nimport { allow, createWard, predicate } from '@vielzeug/ward';\n\ntype Attributes = { authorId: string; status: 'draft' | 'published' };\n\nconst ward = createWard<'update', 'posts', Attributes>([\n allow<'update', 'posts', Attributes>('editor', 'posts', ['update'], {\n when: predicate.owns<Attributes>('authorId'),\n }),\n]);\n\nward.decide({\n action: 'update',\n attributes: { authorId: post.authorId, status: post.status },\n principal,\n resource: 'posts',\n});\n```\n\nAttribute values support finite numbers, strings, booleans, `null`, arrays, and plain objects. Ward rejects class instances, dates, maps, sets, functions, `undefined`, non-finite numbers, and circular values.\n\n## Combine conditions\n\n```ts\nconst canPublishOwnPost = predicate.and<Attributes>(\n predicate.owns<Attributes>('authorId'),\n ({ attributes }) => attributes?.status === 'draft',\n);\n```\n\nConditions are synchronous. A thrown error, Promise, or non-boolean result becomes a `WardConditionError` with the original value in `cause`. Role gating does not need a condition — declare `roles` on the rule.\n\n## Bind a principal\n\n```ts\nconst permissions = ward.forPrincipal(principal);\n\npermissions.decide({ action: 'update', attributes, resource: 'posts' });\npermissions.checkAll([\n { action: 'read', attributes, resource: 'posts' },\n { action: 'update', attributes, resource: 'posts' },\n]);\npermissions.allowedActions({ attributes, resource: 'posts' });\n```\n\n`forPrincipal()` snapshots the principal, roles, and principal attributes. Later caller mutations cannot alter bound decisions.\n\n`allowedActions()` draws from `ward.knownActions` — the exact allow-side actions derived from the compiled rules — and accepts an optional `filter` to narrow the candidates.\n\n## Observe decisions\n\n```ts\nconst controller = new AbortController();\n\nward.tap((event) => audit.write(event), { signal: controller.signal });\ncontroller.abort();\n```\n\n`tap()` observes `decide()` and `checkAll()`. It does not observe the hypothetical checks performed by `allowedActions()`. Handler failures are swallowed.\n\n## Anonymous and wildcard roles\n\n- `ANONYMOUS` matches `null` and an omitted principal.\n- `WILDCARD` as a role matches any authenticated principal.\n- `WILDCARD` as an action or resource matches every value.\n\n## Testing\n\nConstruct a fresh Ward for each policy test. Assert decisions and ordering through the public API; rules are immutable snapshots.\n\n## Framework Integration\n\nKeep one Ward instance near the application boundary. Bind the current principal when rendering several related controls, and repeat authorization at the mutation boundary.\n\n## Working with Other Vielzeug Libraries\n\nSend `WardEvent` values to a Rune logger or Herald bus when an application needs centralized diagnostics. Keep durable audit persistence in the application.\n\n## Best Practices\n\n- Put narrow exceptions before broad rules.\n- Keep default deny.\n- Prefer `allow()` and `deny()` for role policies.\n- Prefer declarative attributes before custom conditions.\n- Type action, resource, and attribute generics at shared policy boundaries.\n- Pass only authorization-relevant attributes.\n- Treat `reason` as diagnostics, not user-facing prose.\n",
|
|
7
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- [Auditing Decisions](./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
8
|
},
|
|
9
9
|
"examples": [
|
|
10
10
|
{
|
|
11
11
|
"id": "basic-rules",
|
|
12
|
-
"code": "import { WILDCARD, createWard } from '@vielzeug/ward'\n\n// Ordered first-match rules — the first matching rule wins; default deny if none match\nconst ward = createWard([\n { action: WILDCARD, resource: WILDCARD, effect: 'deny',
|
|
12
|
+
"code": "import { WILDCARD, createWard } from '@vielzeug/ward'\n\n// Ordered first-match rules — the first matching rule wins; default deny if none match\nconst ward = createWard([\n { action: WILDCARD, resource: WILDCARD, effect: 'deny', roles: ['blocked'] },\n { action: 'read', resource: 'posts', effect: 'allow' },\n { action: 'update', resource: 'posts', effect: 'allow', roles: ['editor'] },\n])\n\nconst viewer = { id: 'u1', roles: ['viewer'] }\nconst editor = { id: 'u2', roles: ['editor'] }\nconst blocked = { id: 'u3', roles: ['blocked'] }\n\nconsole.log('viewer read: ', ward.decide({ action: 'read', principal: viewer, resource: 'posts' }).effect) // allow\nconsole.log('viewer update:', ward.decide({ action: 'update', principal: viewer, resource: 'posts' }).effect) // deny\nconsole.log('editor update:', ward.decide({ action: 'update', principal: editor, resource: 'posts' }).effect) // allow\nconsole.log('blocked read: ', ward.decide({ action: 'read', principal: blocked, resource: 'posts' }).effect) // deny",
|
|
13
13
|
"name": "Basic Rules"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"id": "basic-setup",
|
|
17
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\n// Simple allow/deny rules evaluated in order — first match wins\nconst ward = createWard([\n { action: 'read', resource: 'posts', effect: 'allow' },\n { action: 'write', resource: 'posts', effect: 'allow',
|
|
17
|
+
"code": "import { createWard } from '@vielzeug/ward'\n\n// Simple allow/deny rules evaluated in order — first match wins\nconst ward = createWard([\n { action: 'read', resource: 'posts', effect: 'allow' },\n { action: 'write', resource: 'posts', effect: 'allow', roles: ['writer'] },\n { action: 'delete', resource: 'posts', effect: 'deny' },\n])\n\nconst user = { id: 'u1', roles: ['writer'] }\n\nconsole.log('read: ', ward.decide({ action: 'read', principal: user, resource: 'posts' }).effect) // allow\nconsole.log('write: ', ward.decide({ action: 'write', principal: user, resource: 'posts' }).effect) // allow\nconsole.log('delete:', ward.decide({ action: 'delete', principal: user, resource: 'posts' }).effect) // deny",
|
|
18
18
|
"name": "Basic Setup"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"id": "multi-role-rules",
|
|
27
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\n//
|
|
27
|
+
"code": "import { createWard } from '@vielzeug/ward'\n\n// A rule's roles array matches with OR semantics — any listed role matches\nconst ward = createWard([\n {\n action: 'read',\n resource: 'articles',\n effect: 'allow',\n roles: ['editor', 'reviewer', 'reader'],\n },\n { action: 'read', resource: 'articles', effect: 'deny' },\n])\n\nconst editor = { id: 'u1', roles: ['editor'] }\nconst reviewer = { id: 'u2', roles: ['reviewer'] }\nconst outsider = { id: 'u3', roles: ['guest'] }\n\nconsole.log('editor: ', ward.decide({ action: 'read', principal: editor, resource: 'articles' }).effect) // allow\nconsole.log('reviewer:', ward.decide({ action: 'read', principal: reviewer, resource: 'articles' }).effect) // allow\nconsole.log('outsider:', ward.decide({ action: 'read', principal: outsider, resource: 'articles' }).effect) // deny",
|
|
28
28
|
"name": "Multi-Role Rules"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"id": "permission-checks",
|
|
32
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n { action: 'read', resource: 'articles', effect: 'allow' },\n { action: 'create', resource: 'articles', effect: 'allow',
|
|
32
|
+
"code": "import { createWard } from '@vielzeug/ward'\n\nconst ward = createWard([\n { action: 'read', resource: 'articles', effect: 'allow' },\n { action: 'create', resource: 'articles', effect: 'allow', roles: ['editor'] },\n { action: 'update', resource: 'articles', effect: 'allow', roles: ['editor'] },\n { action: 'delete', resource: 'articles', effect: 'deny' },\n])\n\nconst editor = { id: 'u1', roles: ['editor'] }\nconst guest = { id: 'u2', roles: ['guest'] }\n\nconsole.log('guest read: ', ward.decide({ action: 'read', principal: guest, resource: 'articles' }).effect) // allow\nconsole.log('guest create:', ward.decide({ action: 'create', principal: guest, resource: 'articles' }).effect) // deny\nconsole.log('editor create:', ward.decide({ action: 'create', principal: editor, resource: 'articles' }).effect) // allow\nconsole.log('editor delete:', ward.decide({ action: 'delete', principal: editor, resource: 'articles' }).effect) // deny",
|
|
33
33
|
"name": "Permission Checks"
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
"id": "permission-management",
|
|
37
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\n// Rules are plain
|
|
37
|
+
"code": "import { createWard } from '@vielzeug/ward'\n\n// Rules are plain data — build them dynamically from config or a database\nconst rules = [\n { action: 'read', resource: 'comments', effect: 'allow' },\n { action: 'write', resource: 'comments', effect: 'allow', roles: ['user'] },\n { action: 'delete', resource: 'comments', effect: 'allow', roles: ['moderator'] },\n]\n\nconst ward = createWard(rules)\n\nconst user = { id: 'u1', roles: ['user'] }\nconst mod = { id: 'u2', roles: ['moderator'] }\nconst guest = { id: 'u3', roles: [] }\n\nconsole.log('guest read: ', ward.decide({ action: 'read', principal: guest, resource: 'comments' }).effect) // allow\nconsole.log('user write: ', ward.decide({ action: 'write', principal: user, resource: 'comments' }).effect) // allow\nconsole.log('guest write: ', ward.decide({ action: 'write', principal: guest, resource: 'comments' }).effect) // deny\nconsole.log('mod delete:', ward.decide({ action: 'delete', principal: mod, resource: 'comments' }).effect) // allow",
|
|
38
38
|
"name": "Permission Management"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"id": "role-hierarchy",
|
|
42
|
-
"code": "import { createWard } from '@vielzeug/ward'\n\n//
|
|
42
|
+
"code": "import { createWard } from '@vielzeug/ward'\n\n// Roles are declarative data on each rule — no condition callbacks needed\nconst ward = createWard([\n { action: 'read', resource: 'posts', effect: 'allow' },\n { action: 'update', resource: 'posts', effect: 'allow', roles: ['editor'] },\n { action: 'delete', resource: 'posts', effect: 'allow', roles: ['admin'] },\n])\n\nconst editor = { id: 'u1', roles: ['editor'] }\nconst admin = { id: 'u2', roles: ['admin'] }\nconst viewer = { id: 'u3', roles: ['viewer'] }\n\nconsole.log('viewer read: ', ward.decide({ action: 'read', principal: viewer, resource: 'posts' }).effect) // allow\nconsole.log('editor update:', ward.decide({ action: 'update', principal: editor, resource: 'posts' }).effect) // allow\nconsole.log('admin delete:', ward.decide({ action: 'delete', principal: admin, resource: 'posts' }).effect) // allow\nconsole.log('viewer delete:', ward.decide({ action: 'delete', principal: viewer, resource: 'posts' }).effect) // deny",
|
|
43
43
|
"name": "Role Hierarchy"
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"id": "wildcard-permissions",
|
|
47
|
-
"code": "import { WILDCARD, createWard } from '@vielzeug/ward'\n\n// Wildcards match any action or resource\nconst ward = createWard([\n { action: WILDCARD, resource: WILDCARD, effect: 'allow',
|
|
47
|
+
"code": "import { WILDCARD, createWard } from '@vielzeug/ward'\n\n// Wildcards match any action or resource\nconst ward = createWard([\n { action: WILDCARD, resource: WILDCARD, effect: 'allow', roles: ['admin'] },\n { action: 'read', resource: WILDCARD, effect: 'allow' },\n { action: WILDCARD, resource: WILDCARD, effect: 'deny' },\n])\n\nconst admin = { id: 'u1', roles: ['admin'] }\nconst viewer = { id: 'u2', roles: ['viewer'] }\n\nconsole.log('admin anything:', ward.decide({ action: 'delete', principal: admin, resource: 'users' }).effect) // allow\nconsole.log('viewer read: ', ward.decide({ action: 'read', principal: viewer, resource: 'posts' }).effect) // allow\nconsole.log('viewer delete: ', ward.decide({ action: 'delete', principal: viewer, resource: 'posts' }).effect) // deny",
|
|
48
48
|
"name": "Wildcard Permissions"
|
|
49
49
|
}
|
|
50
50
|
],
|
|
@@ -55,26 +55,23 @@
|
|
|
55
55
|
"WardConfigError": "export { WardConditionError, WardConfigError, WardError } from './errors';",
|
|
56
56
|
"WardError": "export { WardConditionError, WardConfigError, WardError } from './errors';",
|
|
57
57
|
"createWard": "export { createWard } from './factory';",
|
|
58
|
-
"matchesPattern": "export { matchesPattern
|
|
59
|
-
"patternCovers": "export { matchesPattern, patternCovers } from './resource';",
|
|
58
|
+
"matchesPattern": "export { matchesPattern } from './resource';",
|
|
60
59
|
"allow": "export { allow, deny, predicate } from './rules';",
|
|
61
60
|
"deny": "export { allow, deny, predicate } from './rules';",
|
|
62
61
|
"predicate": "export { allow, deny, predicate } from './rules';",
|
|
63
|
-
"BoundWard": "export type {\n BoundWard,\n
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"WardPattern": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
78
|
-
"WardRule": "export type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';"
|
|
62
|
+
"BoundWard": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
63
|
+
"BoundWardDecisionInput": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
64
|
+
"Principal": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
65
|
+
"UserPrincipal": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
66
|
+
"Ward": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
67
|
+
"WardAttributes": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
68
|
+
"WardAttributeValue": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
69
|
+
"WardCondition": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
70
|
+
"WardConditionInput": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
71
|
+
"WardDecision": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
72
|
+
"WardDecisionInput": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
73
|
+
"WardEvent": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
74
|
+
"WardPattern": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';",
|
|
75
|
+
"WardRule": "export type {\n BoundWard,\n BoundWardDecisionInput,\n Principal,\n UserPrincipal,\n Ward,\n WardAttributes,\n WardAttributeValue,\n WardCondition,\n WardConditionInput,\n WardDecision,\n WardDecisionInput,\n WardEvent,\n WardPattern,\n WardRule,\n} from './types';"
|
|
79
76
|
}
|
|
80
77
|
}
|