@o3co/auth.policy-verifier.builtins 0.12.0 → 0.13.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.
Files changed (54) hide show
  1. package/README.ja.md +24 -3
  2. package/README.md +41 -5
  3. package/dist/collectors/PayloadClaimAttributeCollector.d.mts.map +1 -1
  4. package/dist/collectors/PayloadScopeCollector.d.mts.map +1 -1
  5. package/dist/collectors/PayloadSubjectIdCollector.d.mts.map +1 -1
  6. package/dist/collectors/RequestContextAttributeCollector.d.mts.map +1 -1
  7. package/dist/collectors/StaticPermissionCollector.d.mts +15 -1
  8. package/dist/collectors/StaticPermissionCollector.d.mts.map +1 -1
  9. package/dist/collectors/StaticPermissionCollector.mjs +19 -1
  10. package/dist/collectors/StaticRoleCollector.d.mts +17 -1
  11. package/dist/collectors/StaticRoleCollector.d.mts.map +1 -1
  12. package/dist/collectors/StaticRoleCollector.mjs +40 -1
  13. package/dist/collectors/_attributeMapping.d.mts +10 -2
  14. package/dist/collectors/_attributeMapping.d.mts.map +1 -1
  15. package/dist/collectors/_attributeMapping.mjs +10 -2
  16. package/dist/collectors/_claims.d.mts.map +1 -1
  17. package/dist/collectors/_claims.mjs +7 -3
  18. package/dist/module.d.mts.map +1 -1
  19. package/dist/resource/DotNotationResourceParser.d.mts.map +1 -1
  20. package/dist/rules/AttrLiteralCompare.d.mts +11 -1
  21. package/dist/rules/AttrLiteralCompare.d.mts.map +1 -1
  22. package/dist/rules/AttrLiteralCompare.mjs +21 -9
  23. package/dist/rules/AttrLiteralEqual.d.mts +10 -1
  24. package/dist/rules/AttrLiteralEqual.d.mts.map +1 -1
  25. package/dist/rules/AttrLiteralEqual.mjs +19 -10
  26. package/dist/rules/AttrLiteralIn.d.mts +9 -1
  27. package/dist/rules/AttrLiteralIn.d.mts.map +1 -1
  28. package/dist/rules/AttrLiteralIn.mjs +14 -6
  29. package/dist/rules/AttrLiteralNotEqual.d.mts +12 -2
  30. package/dist/rules/AttrLiteralNotEqual.d.mts.map +1 -1
  31. package/dist/rules/AttrLiteralNotEqual.mjs +21 -11
  32. package/dist/rules/AttrLiteralNotIn.d.mts +11 -2
  33. package/dist/rules/AttrLiteralNotIn.d.mts.map +1 -1
  34. package/dist/rules/AttrLiteralNotIn.mjs +16 -7
  35. package/dist/rules/AttrMatchRule.d.mts +2 -1
  36. package/dist/rules/AttrMatchRule.d.mts.map +1 -1
  37. package/dist/rules/AttrMatchRule.mjs +6 -1
  38. package/dist/rules/AttrPairCompare.d.mts +11 -1
  39. package/dist/rules/AttrPairCompare.d.mts.map +1 -1
  40. package/dist/rules/AttrPairCompare.mjs +22 -10
  41. package/dist/rules/AttrPairEqual.d.mts +10 -1
  42. package/dist/rules/AttrPairEqual.d.mts.map +1 -1
  43. package/dist/rules/AttrPairEqual.mjs +18 -8
  44. package/dist/rules/AttrPairNotEqual.d.mts +10 -1
  45. package/dist/rules/AttrPairNotEqual.d.mts.map +1 -1
  46. package/dist/rules/AttrPairNotEqual.mjs +18 -8
  47. package/dist/rules/HasPermission.d.mts.map +1 -1
  48. package/dist/rules/HasScope.d.mts.map +1 -1
  49. package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts +1 -1
  50. package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts.map +1 -1
  51. package/dist/rules/collectors/ResourceActionPermissionRuleCollector.mjs +1 -1
  52. package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts +5 -1
  53. package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts.map +1 -1
  54. package/package.json +5 -5
package/README.ja.md CHANGED
@@ -1,9 +1,23 @@
1
1
  # @o3co/auth.policy-verifier.builtins
2
2
 
3
+ 最終更新: 2026-09-24
4
+
3
5
  auth.policy-verifier 向けの組み込み attribute collector、rule collector、および resource parser です。
4
6
 
5
7
  **Runtime:** `BigInt` と `Map.groupBy` をサポートするサーバー/エッジ JavaScript ランタイムが対象です — Node.js 22+(`engines.node` で宣言しており、古い Node ではインストール時にブロックされます)、Cloudflare Workers、Vercel Edge、Deno、Bun。ブラウザは設計上対象外です(認可判定はサーバー側で enforcement する必要があるため)。同梱の `server` パッケージは引き続き Node 専用です。
6
8
 
9
+ ## 責務と役割
10
+
11
+ core のインターフェースを実装した、小さな既製のセットです: attribute collector、rule、rule collector、resource parser、そしてそれらを server の `createApp` などのホストに登録する `builtinCollectorsModule`。依存は `@o3co/auth.policy-verifier.core` だけです。`templates/standalone` がこれを組み込み、`server` はテストでのみ使います(`devDependencies`)。
12
+
13
+ - **所有するもの:** これらの実装が何を読み、何を書き、どう照合するか — JWT クレーム(`sub`、`azp`、`scope`、宣言したクレーム)から属性キーへの対応(#170)、`HasScope` / `HasPermission` と比較ルールの照合規則、ドット記法のリソース文法。
14
+ - **所有しないもの:** パイプライン、グルーピング、判定(core)、認証や HTTP(`server`)、ポリシーエンジンや `AsyncRule`(`cedar`)、I/O を行う collector(利用側が書くもの。[docs/extending.ja.md](../../docs/extending.ja.md))。
15
+ - **core と別パッケージである理由:** core はエンジン中立の契約で、subject のフィールドを一切名指ししません(#170)。クレームの語彙と具体的な照合はその線の反対側、つまりここに置きます([AGENTS.md — Core Vocabulary Scope](../../AGENTS.md#core-vocabulary-scope))。また任意導入でもあります: builtins は意図的に基本セットであってカタログではない([docs/extending.ja.md](../../docs/extending.ja.md))ので、collector と rule を自前で書くデプロイメントはインストール不要で、core もこれに合わせて肥大化しません。
16
+
17
+ 各ソースディレクトリの責務・役割・不変条件(英語):
18
+ [`src/collectors/README.md`](src/collectors/README.md)(attribute collector)、
19
+ [`src/rules/README.md`](src/rules/README.md)(rule と rule collector)。
20
+
7
21
  ## インストール
8
22
 
9
23
  ```bash
@@ -23,7 +37,7 @@ npm install @o3co/auth.policy-verifier.builtins
23
37
  | `RequestContextAttributeCollector` | `requestContext` の宣言済みフィールド | 運用者が決めたキー | `{ attributes: Mapping[] }` |
24
38
  | `PayloadClaimAttributeCollector` | 検証済み `subject` の宣言済みクレーム | 運用者が決めたキー、または core の 5 キー | `{ attributes: Mapping[] }` (#219) |
25
39
 
26
- `StaticPermissionCollector` と `StaticRoleCollector` は、リクエストのコンテキストに関わらず、コンストラクタに渡した値を常に出力します。
40
+ `StaticPermissionCollector` と `StaticRoleCollector` は、リクエストのコンテキストに関わらず、コンストラクタに渡した値を常に出力します。渡された値は構築時にコピーされます — 配列、そして `StaticRoleCollector` では各 `Role` とその `permissions` も — ので、構築後に config を変更しても出力は変わりません(#255)。`permissions` と `roles` は配列でなければなりません。それ以外の値は、文字列も含めて(`["posts.*"]` のつもりで `permissions = "posts.*"` と書いた場合など)構築時に `TypeError` で拒否され、デプロイは起動時に失敗します(#264)。
27
41
 
28
42
  ### PayloadClaimAttributeCollector
29
43
 
@@ -40,7 +54,7 @@ npm install @o3co/auth.policy-verifier.builtins
40
54
 
41
55
  マッピングの形は `RequestContextAttributeCollector` と同じです(`{ from, to?, type? }`、完全一致するキーが dot path より優先、own property のみ)。違うのは読み取り元であり、したがって信頼度です: subject バッグは authenticator が検証したものなので、request-context コレクターが拒否する core の 5 キー — `scopes`、`permissions`、`roles`、`userId`、`clientId` — にもマッピングを着地させて**かまいません**。2 つのコレクターが 1 つのリストキーに書けば union されます。それはデプロイが issuer 由来の 2 つのソースを合成しているということであり、config にそう書かれています。一方、2 つのコレクターが異なる値で書いた*スカラー*キーは `AttributeConflictError` を投げ、すべてのリクエストを deny します — `PayloadSubjectIdCollector` も `userId` / `clientId` を書いている間は、そこへマッピングしないでください。別のパッケージが予約したキー(cedar の `request*`)は引き続き拒否されます。それらは subject ではなくリクエストから導出されるものだからです。そして、マッピングするのは IdP が自身の登録データや管理データから埋めるクレームだけにしてください: ユーザーが編集できるメタデータ(Clerk の `unsafe_metadata`、Auth0 の `user_metadata`)から発行されたクレームは、署名されてはいても信頼できるものではありません。
42
56
 
43
- scope クレームについては `PayloadScopeCollector { claim = "scp" }` を優先してください。こちらはスペース区切り文字列の形も読み、`ResourceActionScopeRuleCollector { claim = "scp" }` と組にすることで、どのトークンが scopeless かについて両者の判断が一致します。
57
+ scope クレームについては `PayloadScopeCollector { claim = "scp" }` を優先してください。こちらはスペース区切り文字列の形も読み、`ResourceActionScopeRuleCollector { claim = "scp" }` と組にすることで、両者が同じ claim を見ます(それぞれが自分の `claim` を持ち、一致は検査されないので両方に設定してください)。rule collector は claim の有無だけを見ます — 使える scope リストを持たない claim(`""`、数値)は scope を生まない一方、`scopeless = "skip"` では scope ありとして扱われます。
44
58
 
45
59
  ### RequestContextAttributeCollector
46
60
 
@@ -116,7 +130,7 @@ new HasScope(scope: string, options?: { allowBareScopeRewrite?: boolean })
116
130
 
117
131
  ### AttrMatchRule
118
132
 
119
- **非推奨。** 代わりに [`AttrPairEqual`](#attrpairequal) を使用してください。`AttrMatchRule` は `AttrPairEqual` を継承した薄いラッパークラスとして残されており、後方互換のため旧来の `ruleType`(`attr_match:${a}:${b}`)と旧来の `message` 文言を保持します。型 `AttrMatchRuleConfig` は `AttrPairEqualConfig` の型エイリアスです。将来のメジャーバージョンで削除されます。
133
+ **非推奨。** 代わりに [`AttrPairEqual`](#attrpairequal) を使用してください。`AttrMatchRule` は `AttrPairEqual` を継承した薄いラッパークラスとして残されており、後方互換のため旧来の `ruleType`(`attr_match:${a}:${b}`)と旧来の `message` 文言を保持します。型 `AttrMatchRuleConfig` は `AttrPairEqualConfig` の型エイリアスです。v0.3 から非推奨で、将来のリリースで削除される可能性があります(変更の可能性あり)。
120
134
 
121
135
  ```ts
122
136
  new AttrMatchRule({ a: string, b: string, group?: string })
@@ -224,6 +238,10 @@ new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group
224
238
 
225
239
  属性比較 Rule はすべて、上記 `AttrMatchRule` と同じグルーピング規則に従います。既定では各 Rule の `ruleType` は識別用パラメータから導出されるため、異なる要件は評価器によって AND 結合されます。2 つの Rule に同じ `group` 文字列を渡すと、両者は同じ `ruleType` を持つようになり、評価器は OR 結合します(どちらか一方が成立すれば要件を満たす)。
226
240
 
241
+ ### config は構築時にコピーされる
242
+
243
+ 属性比較 Rule はすべて、config の各フィールドを一度だけ読んで検証し、自身のコピーとして保持します(#255)。構築後に config オブジェクトを変更しても — `a`、`b`、`op`、`v` の置き換えや `values` の編集 — Rule の応答も `ruleType` と `message` も変わらず、コンストラクタが拒否する値(`NaN` リテラルなど)を後から差し込むこともできません。
244
+
227
245
  ## Rule Collectors
228
246
 
229
247
  | 名前 | 導出する permission / scope | 返り値 |
@@ -299,6 +317,8 @@ tchar = %x21 / %x23-2D / %x2F-39 / %x3B-5B / %x5D-7E
299
317
  import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
300
318
  ```
301
319
 
320
+ 以下は執筆時点の登録内容です。正は [`src/module.mts`](src/module.mts) です。
321
+
302
322
  | レジストリ | 名前 | ファクトリ |
303
323
  | --- | --- | --- |
304
324
  | `attributeCollector` | `"PayloadScopeCollector"` | `(config) => new PayloadScopeCollector(config)` |
@@ -313,6 +333,7 @@ import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
313
333
 
314
334
  ## 関連
315
335
 
336
+ - [`src/collectors/README.md`](src/collectors/README.md)、[`src/rules/README.md`](src/rules/README.md) — このパッケージのソースディレクトリの責務・役割・不変条件(英語)
316
337
  - [拡張ガイド (`docs/extending.ja.md`)](../../docs/extending.ja.md) — カスタム `Rule` / `AttributeCollector` の書き方と、`builtins` が基本セットとして位置づけられている理由
317
338
  - [`@o3co/auth.policy-verifier.core`](../core/README.ja.md) — コアインターフェースと attribute 定数
318
339
  - [auth.policy-verifier ルート README](../../README.ja.md) — 完全なセットアップと設定のリファレンス
package/README.md CHANGED
@@ -1,9 +1,38 @@
1
1
  # @o3co/auth.policy-verifier.builtins
2
2
 
3
+ Last updated: 2026-09-24
4
+
3
5
  Built-in attribute collectors, rule collectors, and resource parser for auth.policy-verifier.
4
6
 
5
7
  **Runtime:** Server- and edge-side JavaScript runtimes that support `BigInt` and `Map.groupBy` — Node.js 22+ (declared via `engines.node` so older Node installs are blocked at install time), Cloudflare Workers, Vercel Edge, Deno, Bun. Browsers are out of scope by design: authorization decisions must be enforced server-side. The `server` companion package remains Node-only.
6
8
 
9
+ ## Responsibility
10
+
11
+ A small, ready-made set of implementations of core's interfaces: attribute collectors, rules,
12
+ rule collectors, a resource parser, and `builtinCollectorsModule`, which registers them for
13
+ a host such as the server's `createApp`. It depends on `@o3co/auth.policy-verifier.core` alone;
14
+ `templates/standalone` wires it in, and `server` uses it only in its tests
15
+ (`devDependencies`).
16
+
17
+ - **Owns** what these implementations read, write and match: the mapping from JWT claims
18
+ (`sub`, `azp`, `scope`, declared claims) to attribute keys (#170), the matching rules of
19
+ `HasScope` / `HasPermission` and the comparison rules, and the dot-notation resource
20
+ grammar.
21
+ - **Does not own** the pipelines, the grouping or the decision (core), authentication or HTTP
22
+ (`server`), policy engines or `AsyncRule`s (`cedar`), or collectors that do I/O — those are
23
+ the consumer's to write ([docs/extending.md](../../docs/extending.md)).
24
+ - **Why a separate package from core:** core is the engine-neutral contract and names no
25
+ field of the subject (#170); the claim vocabulary and the concrete matching live here, on
26
+ the other side of that line ([AGENTS.md — Core Vocabulary Scope](../../AGENTS.md#core-vocabulary-scope)).
27
+ It is also optional: builtins is a deliberately basic set, not a catalog
28
+ ([docs/extending.md — Positioning](../../docs/extending.md#positioning-builtins-is-a-basic-set-not-an-exhaustive-catalog)),
29
+ so a deployment that writes its own collectors and rules does not have to install it, and
30
+ core does not grow with it.
31
+
32
+ Responsibility, role and invariants of each source directory:
33
+ [`src/collectors/README.md`](src/collectors/README.md) (attribute collectors) and
34
+ [`src/rules/README.md`](src/rules/README.md) (rules and rule collectors).
35
+
7
36
  ## Install
8
37
 
9
38
  ```bash
@@ -23,7 +52,7 @@ All collectors implement `AttributeCollector`.
23
52
  | `RequestContextAttributeCollector` | declared fields of `requestContext` | the operator's own keys | `{ attributes: Mapping[] }` |
24
53
  | `PayloadClaimAttributeCollector` | declared claims of the verified `subject` | the operator's own keys, or core's five | `{ attributes: Mapping[] }` (#219) |
25
54
 
26
- `StaticPermissionCollector` and `StaticRoleCollector` always emit the values supplied at construction time, regardless of request context.
55
+ `StaticPermissionCollector` and `StaticRoleCollector` always emit the values supplied at construction time, regardless of request context. They copy what they were given at construction — the array, and for `StaticRoleCollector` each `Role` and its `permissions` — so mutating the config afterwards changes nothing they emit (#255). `permissions` and `roles` must be arrays: anything else, a string included (`permissions = "posts.*"` where `["posts.*"]` was meant), is refused with a `TypeError` at construction, so the deployment fails at boot (#264).
27
56
 
28
57
  ### PayloadClaimAttributeCollector
29
58
 
@@ -40,7 +69,7 @@ Promotes declared claims of the **verified subject** into attributes (#219) —
40
69
 
41
70
  Same mapping shape as `RequestContextAttributeCollector` (`{ from, to?, type? }`, an exact key winning over a dot path, own properties only). What differs is the source, and therefore the trust: the subject bag is what the authenticator verified, so a mapping **may** land on core's five keys — `scopes`, `permissions`, `roles`, `userId`, `clientId` — where the request-context collector refuses them. Two collectors writing one list key union it; that is the deployment composing two issuer-derived sources, and it says so in config. A *scalar* key written by two collectors with different values throws `AttributeConflictError` and denies every request — do not map onto `userId` / `clientId` while `PayloadSubjectIdCollector` also writes them. Keys another package reserved (cedar's `request*`) stay refused, because they are derived from the request, not from the subject. And map only claims the IdP populates from its own registration or admin data: a claim minted from user-editable metadata (Clerk's `unsafe_metadata`, Auth0's `user_metadata`) is signed, not trusted.
42
71
 
43
- For the scope claim specifically, prefer `PayloadScopeCollector { claim = "scp" }`, which also reads the space-delimited string form and pairs with `ResourceActionScopeRuleCollector { claim = "scp" }` so both agree about which tokens are scopeless.
72
+ For the scope claim specifically, prefer `PayloadScopeCollector { claim = "scp" }`, which also reads the space-delimited string form and pairs with `ResourceActionScopeRuleCollector { claim = "scp" }` — set on both, since each keeps its own `claim` and nothing checks they match — so both look at the same claim. The rule collector checks only that the claim is present: a claim holding no usable scope list (`""`, a number) yields no scopes but still counts as scoped under `scopeless = "skip"`.
44
73
 
45
74
  ### RequestContextAttributeCollector
46
75
 
@@ -116,7 +145,7 @@ new HasScope(scope: string, options?: { allowBareScopeRewrite?: boolean })
116
145
 
117
146
  ### AttrMatchRule
118
147
 
119
- **Deprecated.** Use [`AttrPairEqual`](#attrpairequal) instead. `AttrMatchRule` is kept as a thin wrapper class that extends `AttrPairEqual` and preserves the legacy `ruleType` (`attr_match:${a}:${b}`) and legacy `message` wording for backward compatibility. The type `AttrMatchRuleConfig` is a type alias of `AttrPairEqualConfig`. It will be removed in a future major version.
148
+ **Deprecated.** Use [`AttrPairEqual`](#attrpairequal) instead. `AttrMatchRule` is kept as a thin wrapper class that extends `AttrPairEqual` and preserves the legacy `ruleType` (`attr_match:${a}:${b}`) and legacy `message` wording for backward compatibility. The type `AttrMatchRuleConfig` is a type alias of `AttrPairEqualConfig`. Deprecated since v0.3; it may be removed in a future release (subject to change).
120
149
 
121
150
  ```ts
122
151
  new AttrMatchRule({ a: string, b: string, group?: string })
@@ -158,7 +187,7 @@ new AttrLiteralNotEqual({ a: string, v: string | number | boolean, group?: strin
158
187
 
159
188
  - `code`: `"attr_equal"`.
160
189
  - Default `ruleType`: `` `attr_literal_not_equal:${a}:${typeof v}:${String(v)}` ``. The `typeof v` segment prevents silent collisions between distinct-type literals (same rationale as `AttrLiteralEqual`).
161
- - Passes when `attrs.get(a)` is the same type as `v` and strictly not equal to it. Missing or wrong-type attributes return `false` (safe-deny).
190
+ - Passes when `attrs.get(a)` is the same type as `v` and strictly not equal to it. Missing or wrong-type attributes return `false` (safe-deny); a `NaN` attribute is a number and passes, as any other unequal number does (#254).
162
191
 
163
192
  ### AttrLiteralIn
164
193
 
@@ -178,7 +207,7 @@ new AttrLiteralNotIn({ a: string, values: (string | number | boolean)[], group?:
178
207
 
179
208
  - `code`: `"attr_in_set"`.
180
209
  - Default `ruleType`: `` `attr_literal_not_in:${a}:${type}:${count}:${hashPrefix}` `` — same stable, deduplication-aware hash scheme as `AttrLiteralIn`.
181
- - `values` must be a non-empty, homogeneous array. Passes when `attrs.get(a)` is NOT in the set. Duplicate elements in `values` are ignored.
210
+ - `values` must be a non-empty, homogeneous array. Passes when `attrs.get(a)` is NOT in the set — a `NaN` attribute of a numeric set included, since it is in no set (#254). Duplicate elements in `values` are ignored.
182
211
 
183
212
  ### AttrLiteralCompare
184
213
 
@@ -224,6 +253,10 @@ new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group
224
253
 
225
254
  All attribute comparison rules follow the same grouping semantics described for `AttrMatchRule` above. By default, each rule's `ruleType` is derived from its distinguishing parameters so that distinct requirements are AND-combined by the evaluator. Pass the same `group` string to two rules to give them the same `ruleType` — the evaluator then OR-combines them (either condition satisfies the requirement).
226
255
 
256
+ ### Configuration is copied at construction
257
+
258
+ Every attribute comparison rule reads each field of its config once, validates it, and keeps its own copy (#255). Mutating the config object afterwards — replacing `a`, `b`, `op` or `v`, or editing `values` — changes neither the rule's answers nor its `ruleType` and `message`, and a value the constructor would refuse (such as a `NaN` literal) cannot be installed after it.
259
+
227
260
  ## Rule Collectors
228
261
 
229
262
  | Name | Derives permission/scope | Returns |
@@ -301,6 +334,8 @@ that syntax.
301
334
  import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
302
335
  ```
303
336
 
337
+ Registrations as of this writing; the source of truth is [`src/module.mts`](src/module.mts).
338
+
304
339
  | Registry | Name | Factory |
305
340
  | --- | --- | --- |
306
341
  | `attributeCollector` | `"PayloadScopeCollector"` | `(config) => new PayloadScopeCollector(config)` |
@@ -315,6 +350,7 @@ import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
315
350
 
316
351
  ## See Also
317
352
 
353
+ - [`src/collectors/README.md`](src/collectors/README.md), [`src/rules/README.md`](src/rules/README.md) — responsibility, role and invariants of this package's source directories
318
354
  - [Extension guide (`docs/extending.md`)](../../docs/extending.md) — how to write custom `Rule` and `AttributeCollector` implementations; positioning of `builtins` as a basic set
319
355
  - [`@o3co/auth.policy-verifier.core`](../core/README.md) — core interfaces and attribute constants
320
356
  - [auth.policy-verifier root README](../../README.md) — full setup and configuration reference
@@ -1 +1 @@
1
- {"version":3,"file":"PayloadClaimAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadClaimAttributeCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yEAAyE;AACzE,MAAM,MAAM,4BAA4B,GAAG,gBAAgB,CAAC;AAC5D,wCAAwC;AACxC,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAC7D,iEAAiE;AACjE,MAAM,MAAM,oCAAoC,GAAG,+BAA+B,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,8BAA+B,YAAW,kBAAkB;IACxE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,oCAAoC;IASlD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAK7D"}
1
+ {"version":3,"file":"PayloadClaimAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadClaimAttributeCollector.mts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yEAAyE;AACzE,MAAM,MAAM,4BAA4B,GAAG,gBAAgB,CAAC;AAC5D,wCAAwC;AACxC,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAC7D,iEAAiE;AACjE,MAAM,MAAM,oCAAoC,GAAG,+BAA+B,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,8BAA+B,YAAW,kBAAkB;IACxE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,oCAAoC;IASlD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAK7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"PayloadScopeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadScopeCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAIzC,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,CAAC,EAAE,2BAA2B;IAI1C,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG7D"}
1
+ {"version":3,"file":"PayloadScopeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadScopeCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAIzC,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,CAAC,EAAE,2BAA2B;IAI1C,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"PayloadSubjectIdCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadSubjectIdCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IAC7D,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAW7D"}
1
+ {"version":3,"file":"PayloadSubjectIdCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadSubjectIdCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IAC7D,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAW7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestContextAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/RequestContextAttributeCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yDAAyD;AACzD,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,CAAC;AAE9D,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAAG,+BAA+B,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,qBAAa,gCAAiC,YAAW,kBAAkB;IAC1E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,sCAAsC;IAOpD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAQ7D"}
1
+ {"version":3,"file":"RequestContextAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/RequestContextAttributeCollector.mts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yDAAyD;AACzD,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,CAAC;AAE9D,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAAG,+BAA+B,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,qBAAa,gCAAiC,YAAW,kBAAkB;IAC1E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,sCAAsC;IAOpD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAQ7D"}
@@ -3,9 +3,23 @@ import type { AttributeCollector, Attributes, CollectorContext } from "@o3co/aut
3
3
  * Attribute collector that returns a configured constant permission list under
4
4
  * `ATTR_PERMISSIONS`, independent of the JWT payload. Useful for
5
5
  * environments where permissions are static per deployment.
6
+ *
7
+ * The list is copied at construction (#255): a caller that mutates the config
8
+ * or its array afterwards changes nothing this collector emits.
9
+ *
10
+ * Only an array is accepted (#264). Anything else — a missing field, `null`, a
11
+ * number, an object, a string — is refused with a `TypeError` naming the
12
+ * collector and the field, so a misconfigured deployment fails at boot. A
13
+ * string is the case that matters: it is iterable, so copying it would split
14
+ * `"posts.*"` into single characters, and `HasPermission` honours a lone `*`
15
+ * as grant-all.
16
+ *
17
+ * The entries are not checked, and a non-string one is copied as it is. It is
18
+ * inert: `HasPermission` skips every permission that is not a string, so such
19
+ * an entry matches no requirement and grants nothing.
6
20
  */
7
21
  export declare class StaticPermissionCollector implements AttributeCollector {
8
- private permissions;
22
+ private readonly permissions;
9
23
  constructor(config: {
10
24
  permissions: string[];
11
25
  });
@@ -1 +1 @@
1
- {"version":3,"file":"StaticPermissionCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticPermissionCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;GAIG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IACnE,OAAO,CAAC,WAAW,CAAW;gBAElB,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE;IAIvC,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
1
+ {"version":3,"file":"StaticPermissionCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticPermissionCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IACnE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;gBAEpC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE;IAUvC,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
@@ -5,11 +5,29 @@ import { ATTR_PERMISSIONS } from "@o3co/auth.policy-verifier.core";
5
5
  * Attribute collector that returns a configured constant permission list under
6
6
  * `ATTR_PERMISSIONS`, independent of the JWT payload. Useful for
7
7
  * environments where permissions are static per deployment.
8
+ *
9
+ * The list is copied at construction (#255): a caller that mutates the config
10
+ * or its array afterwards changes nothing this collector emits.
11
+ *
12
+ * Only an array is accepted (#264). Anything else — a missing field, `null`, a
13
+ * number, an object, a string — is refused with a `TypeError` naming the
14
+ * collector and the field, so a misconfigured deployment fails at boot. A
15
+ * string is the case that matters: it is iterable, so copying it would split
16
+ * `"posts.*"` into single characters, and `HasPermission` honours a lone `*`
17
+ * as grant-all.
18
+ *
19
+ * The entries are not checked, and a non-string one is copied as it is. It is
20
+ * inert: `HasPermission` skips every permission that is not a string, so such
21
+ * an entry matches no requirement and grants nothing.
8
22
  */
9
23
  export class StaticPermissionCollector {
10
24
  permissions;
11
25
  constructor(config) {
12
- this.permissions = config.permissions;
26
+ const { permissions } = config;
27
+ if (!Array.isArray(permissions)) {
28
+ throw new TypeError(`StaticPermissionCollector: permissions must be an array (got ${permissions === null ? "null" : typeof permissions})`);
29
+ }
30
+ this.permissions = [...permissions];
13
31
  }
14
32
  async collect(_context) {
15
33
  return new Map([[ATTR_PERMISSIONS, [...this.permissions]]]);
@@ -3,9 +3,25 @@ import type { AttributeCollector, Attributes, CollectorContext, Role } from "@o3
3
3
  * Attribute collector that returns a configured constant role list under
4
4
  * `ATTR_ROLES`, independent of the JWT payload. Each role bundles a name and
5
5
  * the permissions it implies.
6
+ *
7
+ * The list is copied at construction, and so is each role in it and each
8
+ * role's `permissions` array (#255): a caller that mutates the config, its
9
+ * array or a `Role` in it afterwards changes nothing this collector emits.
10
+ *
11
+ * Only an array is accepted (#264). Anything else — a missing field, `null`, a
12
+ * number, an object, a string — is refused with a `TypeError` naming the
13
+ * collector and the field, so a misconfigured deployment fails at boot. A
14
+ * string is iterable and would otherwise be split into single characters, as
15
+ * it would be in `StaticPermissionCollector`.
16
+ *
17
+ * The entries are not checked: a malformed `Role` entry is copied as it is
18
+ * (see `copyRole`). It is inert: `HasPermission` ignores a role that is not an
19
+ * object or whose `permissions` is not an array, and a non-string permission
20
+ * inside a role. Each collect returns a shallow copy of the list, so its `Role`
21
+ * copies are shared between the outputs of different collects.
6
22
  */
7
23
  export declare class StaticRoleCollector implements AttributeCollector {
8
- private roles;
24
+ private readonly roles;
9
25
  constructor(config: {
10
26
  roles: Role[];
11
27
  });
@@ -1 +1 @@
1
- {"version":3,"file":"StaticRoleCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticRoleCollector.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,MAAM,iCAAiC,CAAC;AAGzC;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE;QAAE,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE;IAI/B,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
1
+ {"version":3,"file":"StaticRoleCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticRoleCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;gBAE5B,MAAM,EAAE;QAAE,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE;IAU/B,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
@@ -5,13 +5,52 @@ import { ATTR_ROLES } from "@o3co/auth.policy-verifier.core";
5
5
  * Attribute collector that returns a configured constant role list under
6
6
  * `ATTR_ROLES`, independent of the JWT payload. Each role bundles a name and
7
7
  * the permissions it implies.
8
+ *
9
+ * The list is copied at construction, and so is each role in it and each
10
+ * role's `permissions` array (#255): a caller that mutates the config, its
11
+ * array or a `Role` in it afterwards changes nothing this collector emits.
12
+ *
13
+ * Only an array is accepted (#264). Anything else — a missing field, `null`, a
14
+ * number, an object, a string — is refused with a `TypeError` naming the
15
+ * collector and the field, so a misconfigured deployment fails at boot. A
16
+ * string is iterable and would otherwise be split into single characters, as
17
+ * it would be in `StaticPermissionCollector`.
18
+ *
19
+ * The entries are not checked: a malformed `Role` entry is copied as it is
20
+ * (see `copyRole`). It is inert: `HasPermission` ignores a role that is not an
21
+ * object or whose `permissions` is not an array, and a non-string permission
22
+ * inside a role. Each collect returns a shallow copy of the list, so its `Role`
23
+ * copies are shared between the outputs of different collects.
8
24
  */
9
25
  export class StaticRoleCollector {
10
26
  roles;
11
27
  constructor(config) {
12
- this.roles = config.roles;
28
+ const { roles } = config;
29
+ if (!Array.isArray(roles)) {
30
+ throw new TypeError(`StaticRoleCollector: roles must be an array (got ${roles === null ? "null" : typeof roles})`);
31
+ }
32
+ this.roles = [...roles].map(copyRole);
13
33
  }
14
34
  async collect(_context) {
15
35
  return new Map([[ATTR_ROLES, [...this.roles]]]);
16
36
  }
17
37
  }
38
+ /**
39
+ * Copies one configured role: the object, and its `permissions` array.
40
+ *
41
+ * Nothing about the entry is validated here, and a malformed one keeps its
42
+ * shape rather than being coerced — `HasPermission` ignores a role that is not
43
+ * an object or whose `permissions` is not an array (#180), and spreading a bare
44
+ * string under `permissions` would splay it into characters it then honours.
45
+ */
46
+ function copyRole(role) {
47
+ if (typeof role !== "object" || role === null)
48
+ return role;
49
+ // An array is an object too: spreading it into `{ ...role }` would turn it
50
+ // into an index-keyed object, so it is copied as an array.
51
+ if (Array.isArray(role))
52
+ return [...role];
53
+ return Array.isArray(role.permissions)
54
+ ? { ...role, permissions: [...role.permissions] }
55
+ : { ...role };
56
+ }
@@ -38,8 +38,16 @@ export declare function parseAttributeMappings(collector: string, config: Attrib
38
38
  * through a configured name.
39
39
  */
40
40
  export declare function readPath(root: Record<string, unknown>, path: string): unknown;
41
- /** Whether `value` is usable as the declared type. Empty strings count as absent. */
41
+ /**
42
+ * Whether `value` is usable as the declared type. Empty strings count as
43
+ * absent, so one empty entry disqualifies a whole `string[]`. A `number` must
44
+ * be finite: `NaN` and `±Infinity` are dropped here, while a rule's comparand
45
+ * (`requireNumber` in `../rules/_sharedValidation.mts`) may be `Infinity`.
46
+ */
42
47
  export declare function matchesType(value: unknown, type: AttributeMappingType): boolean;
43
- /** Promotes every declared field whose value matches its declared type. */
48
+ /**
49
+ * Promotes every declared field whose value matches its declared type, into a
50
+ * fresh map; a promoted list is a copy, not the source's array.
51
+ */
44
52
  export declare function promoteMappings(mappings: readonly ResolvedAttributeMapping[], source: Record<string, unknown>): Attributes;
45
53
  //# sourceMappingURL=_attributeMapping.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_attributeMapping.d.mts","sourceRoot":"","sources":["../../src/collectors/_attributeMapping.mts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG3F,wCAAwC;AACxC,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAKlE,CAAC;AAEF,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,+BAA+B;IAC/C,wDAAwD;IACxD,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,uBAAuB,KAChC,MAAM,GAAG,SAAS,CAAC;AAExB;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,+BAA+B,GAAG,SAAS,EACnD,iBAAiB,EAAE,iBAAiB,GAClC,wBAAwB,EAAE,CA4B5B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7E;AAED,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAW/E;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,wBAAwB,EAAE,EAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,UAAU,CASZ"}
1
+ {"version":3,"file":"_attributeMapping.d.mts","sourceRoot":"","sources":["../../src/collectors/_attributeMapping.mts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG3F,wCAAwC;AACxC,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAKlE,CAAC;AAEF,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,+BAA+B;IAC/C,wDAAwD;IACxD,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,uBAAuB,KAChC,MAAM,GAAG,SAAS,CAAC;AAExB;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,+BAA+B,GAAG,SAAS,EACnD,iBAAiB,EAAE,iBAAiB,GAClC,wBAAwB,EAAE,CA4B5B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAW/E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,wBAAwB,EAAE,EAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,UAAU,CASZ"}
@@ -58,7 +58,12 @@ export function readPath(root, path) {
58
58
  }
59
59
  return current;
60
60
  }
61
- /** Whether `value` is usable as the declared type. Empty strings count as absent. */
61
+ /**
62
+ * Whether `value` is usable as the declared type. Empty strings count as
63
+ * absent, so one empty entry disqualifies a whole `string[]`. A `number` must
64
+ * be finite: `NaN` and `±Infinity` are dropped here, while a rule's comparand
65
+ * (`requireNumber` in `../rules/_sharedValidation.mts`) may be `Infinity`.
66
+ */
62
67
  export function matchesType(value, type) {
63
68
  switch (type) {
64
69
  case "string":
@@ -71,7 +76,10 @@ export function matchesType(value, type) {
71
76
  return Array.isArray(value) && value.every((item) => typeof item === "string" && item !== "");
72
77
  }
73
78
  }
74
- /** Promotes every declared field whose value matches its declared type. */
79
+ /**
80
+ * Promotes every declared field whose value matches its declared type, into a
81
+ * fresh map; a promoted list is a copy, not the source's array.
82
+ */
75
83
  export function promoteMappings(mappings, source) {
76
84
  const attrs = new Map();
77
85
  for (const mapping of mappings) {
@@ -1 +1 @@
1
- {"version":3,"file":"_claims.d.mts","sourceRoot":"","sources":["../../src/collectors/_claims.mts"],"names":[],"mappings":"AASA,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM1F;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAMnD"}
1
+ {"version":3,"file":"_claims.d.mts","sourceRoot":"","sources":["../../src/collectors/_claims.mts"],"names":[],"mappings":"AAaA,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM1F;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAMnD"}
@@ -1,9 +1,13 @@
1
1
  // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  /*
4
- * Which claim carries the scopes, and how its value is read (#219). Shared by
5
- * the collector that promotes scopes and the rule collector that has to know
6
- * whether a token asserted any, so the two cannot disagree about a token.
4
+ * Which claim carries the scopes, and how its value is read (#219).
5
+ * `PayloadScopeCollector` and `ResourceActionScopeRuleCollector` share the
6
+ * default claim name and refuse the same bad `claim` option; only
7
+ * `PayloadScopeCollector` reads the value (`scopesFrom`), while the rule
8
+ * collector checks only whether the claim is present. Each keeps its own
9
+ * `claim` option, and nothing checks that both were given the same name. Not
10
+ * exported from the package.
7
11
  */
8
12
  /** The claim RFC 8693 §4.2 / RFC 9068 §2.2.3 name, and the default. */
9
13
  export const DEFAULT_SCOPE_CLAIM = "scope";
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.mts","sourceRoot":"","sources":["../src/module.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAW9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MA6CrC,CAAC"}
1
+ {"version":3,"file":"module.d.mts","sourceRoot":"","sources":["../src/module.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAW9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MA6CrC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DotNotationResourceParser.d.mts","sourceRoot":"","sources":["../../src/resource/DotNotationResourceParser.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAqBhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAC/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;CAY5B"}
1
+ {"version":3,"file":"DotNotationResourceParser.d.mts","sourceRoot":"","sources":["../../src/resource/DotNotationResourceParser.mts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAqBhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAC/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;CAY5B"}
@@ -19,12 +19,22 @@ export interface AttrLiteralCompareConfig {
19
19
  * `attr_literal_compare:{a}:{op}:{String(v)}`
20
20
  *
21
21
  * Pass an explicit `group` string to override the default ruleType entirely.
22
+ *
23
+ * ## Configuration is copied at construction
24
+ *
25
+ * The constructor reads each field of `config` once, validates it, and keeps
26
+ * the validated value in a field of its own; the object is not retained (#255).
27
+ * A caller that mutates the config afterwards changes nothing: the rule answers
28
+ * from the values it validated, and `ruleType` and `message` keep describing
29
+ * them.
22
30
  */
23
31
  export declare class AttrLiteralCompare implements Rule {
24
- private readonly config;
25
32
  readonly ruleType: string;
26
33
  readonly code = "attr_compare_violated";
27
34
  readonly message: string;
35
+ private readonly a;
36
+ private readonly op;
37
+ private readonly v;
28
38
  constructor(config: AttrLiteralCompareConfig);
29
39
  verify(attrs: ReadonlyAttributes): boolean;
30
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralCompare.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,SAAS,EAKd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,wBAAwB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,kBAAmB,YAAW,IAAI;IAKlC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,wBAAwB;IAU7D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
1
+ {"version":3,"file":"AttrLiteralCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralCompare.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,SAAS,EAKd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,wBAAwB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAmB,YAAW,IAAI;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;gBAEf,MAAM,EAAE,wBAAwB;IAa5C,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -14,25 +14,37 @@ import { applyCompare, requireAttrName, requireCompareOp, requireNumber, require
14
14
  * `attr_literal_compare:{a}:{op}:{String(v)}`
15
15
  *
16
16
  * Pass an explicit `group` string to override the default ruleType entirely.
17
+ *
18
+ * ## Configuration is copied at construction
19
+ *
20
+ * The constructor reads each field of `config` once, validates it, and keeps
21
+ * the validated value in a field of its own; the object is not retained (#255).
22
+ * A caller that mutates the config afterwards changes nothing: the rule answers
23
+ * from the values it validated, and `ruleType` and `message` keep describing
24
+ * them.
17
25
  */
18
26
  export class AttrLiteralCompare {
19
- config;
20
27
  ruleType;
21
28
  code = "attr_compare_violated";
22
29
  message;
30
+ a;
31
+ op;
32
+ v;
23
33
  constructor(config) {
24
- this.config = config;
25
- requireAttrName("AttrLiteralCompare", "a", config.a);
26
- requireNumber("AttrLiteralCompare", "v", config.v);
27
- requireCompareOp("AttrLiteralCompare", config.op);
34
+ const a = requireAttrName("AttrLiteralCompare", "a", config.a);
35
+ const v = requireNumber("AttrLiteralCompare", "v", config.v);
36
+ const op = requireCompareOp("AttrLiteralCompare", config.op);
28
37
  const group = requireOptionalGroup("AttrLiteralCompare", config.group);
29
- this.ruleType = group ?? `attr_literal_compare:${config.a}:${config.op}:${String(config.v)}`;
30
- this.message = `Attribute constraint not satisfied: ${config.a} must be ${config.op} ${String(config.v)}.`;
38
+ this.a = a;
39
+ this.op = op;
40
+ this.v = v;
41
+ this.ruleType = group ?? `attr_literal_compare:${a}:${op}:${String(v)}`;
42
+ this.message = `Attribute constraint not satisfied: ${a} must be ${op} ${String(v)}.`;
31
43
  }
32
44
  verify(attrs) {
33
- const x = attrs.get(this.config.a);
45
+ const x = attrs.get(this.a);
34
46
  if (typeof x !== "number")
35
47
  return false;
36
- return applyCompare(this.config.op, x, this.config.v);
48
+ return applyCompare(this.op, x, this.v);
37
49
  }
38
50
  }
@@ -25,12 +25,21 @@ export interface AttrLiteralEqualConfig {
25
25
  *
26
26
  * Pass a shared `group` string to two instances to opt into OR semantics
27
27
  * (e.g. "role is admin" OR "role is superuser" under one group).
28
+ *
29
+ * ## Configuration is copied at construction
30
+ *
31
+ * The constructor reads each field of `config` once, validates it, and keeps
32
+ * the validated value in a field of its own; the object is not retained (#255).
33
+ * A caller that mutates the config afterwards changes nothing: the rule answers
34
+ * from the values it validated, and `ruleType` and `message` keep describing
35
+ * them.
28
36
  */
29
37
  export declare class AttrLiteralEqual implements Rule {
30
- private readonly config;
31
38
  readonly ruleType: string;
32
39
  readonly code = "attr_not_equal";
33
40
  readonly message: string;
41
+ private readonly a;
42
+ private readonly v;
34
43
  constructor(config: AttrLiteralEqualConfig);
35
44
  verify(attrs: ReadonlyAttributes): boolean;
36
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralEqual.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAKhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,sBAAsB;IAU3D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
1
+ {"version":3,"file":"AttrLiteralEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralEqual.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAe;gBAErB,MAAM,EAAE,sBAAsB;IAW1C,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}