@cullet/erp-core 1.4.0 → 2.0.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 (306) hide show
  1. package/KIT_CONTEXT.md +7 -1
  2. package/dist/abac/index.d.cts +2 -2
  3. package/dist/abac/index.d.ts +2 -2
  4. package/dist/aggregate-version.cjs +14 -0
  5. package/dist/aggregate-version.cjs.map +1 -0
  6. package/dist/aggregate-version.js +9 -0
  7. package/dist/aggregate-version.js.map +1 -0
  8. package/dist/app-error.cjs +21 -6
  9. package/dist/app-error.cjs.map +1 -1
  10. package/dist/app-error.js +21 -6
  11. package/dist/app-error.js.map +1 -1
  12. package/dist/application/index.cjs +8 -4
  13. package/dist/application/index.d.cts +2 -2
  14. package/dist/application/index.d.ts +2 -2
  15. package/dist/application/index.js +1 -2
  16. package/dist/authorization-error.cjs +67 -17
  17. package/dist/authorization-error.cjs.map +1 -1
  18. package/dist/authorization-error.d.cts +6 -2
  19. package/dist/authorization-error.d.ts +6 -2
  20. package/dist/authorization-error.js +50 -18
  21. package/dist/authorization-error.js.map +1 -1
  22. package/dist/authorizer.port.d.cts +18 -3
  23. package/dist/authorizer.port.d.ts +18 -3
  24. package/dist/composite-authorizer.d.cts +63 -10
  25. package/dist/composite-authorizer.d.ts +63 -10
  26. package/dist/condition-evaluator.cjs +117 -172
  27. package/dist/condition-evaluator.cjs.map +1 -1
  28. package/dist/condition-evaluator.js +118 -167
  29. package/dist/condition-evaluator.js.map +1 -1
  30. package/dist/core-config.d.cts +53 -6
  31. package/dist/core-config.d.ts +53 -6
  32. package/dist/decorate.cjs +14 -0
  33. package/dist/decorate.js +9 -0
  34. package/dist/domain/index.cjs +107 -2
  35. package/dist/domain/index.cjs.map +1 -0
  36. package/dist/domain/index.d.cts +25 -1
  37. package/dist/domain/index.d.ts +25 -1
  38. package/dist/domain/index.js +99 -3
  39. package/dist/domain/index.js.map +1 -0
  40. package/dist/domain-event-contracts.cjs +12 -8
  41. package/dist/domain-event-contracts.cjs.map +1 -1
  42. package/dist/domain-event-contracts.d.cts +29 -4
  43. package/dist/domain-event-contracts.d.ts +29 -4
  44. package/dist/domain-event-contracts.js +11 -7
  45. package/dist/domain-event-contracts.js.map +1 -1
  46. package/dist/domain-exception.cjs +2 -1
  47. package/dist/domain-exception.cjs.map +1 -1
  48. package/dist/domain-exception.js +2 -1
  49. package/dist/domain-exception.js.map +1 -1
  50. package/dist/errors/index.cjs +3 -2
  51. package/dist/errors/index.d.cts +1 -1
  52. package/dist/errors/index.d.ts +1 -1
  53. package/dist/errors/index.js +3 -2
  54. package/dist/exceptions/index.cjs +2 -2
  55. package/dist/exceptions/index.d.cts +1 -2
  56. package/dist/exceptions/index.d.ts +1 -2
  57. package/dist/exceptions/index.js +2 -2
  58. package/dist/gate-engine-registry.cjs.map +1 -1
  59. package/dist/gate-engine-registry.d.cts +1 -1
  60. package/dist/gate-engine-registry.d.ts +1 -1
  61. package/dist/gate-engine-registry.js.map +1 -1
  62. package/dist/gate-v1-payload.schema.cjs +11 -6
  63. package/dist/gate-v1-payload.schema.cjs.map +1 -1
  64. package/dist/gate-v1-payload.schema.js +11 -6
  65. package/dist/gate-v1-payload.schema.js.map +1 -1
  66. package/dist/hashing.cjs +2 -44
  67. package/dist/hashing.cjs.map +1 -1
  68. package/dist/hashing.js +3 -39
  69. package/dist/hashing.js.map +1 -1
  70. package/dist/immutable.cjs +25 -12
  71. package/dist/immutable.cjs.map +1 -1
  72. package/dist/immutable.js +24 -11
  73. package/dist/immutable.js.map +1 -1
  74. package/dist/index.cjs +15 -11
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.d.cts +9 -10
  77. package/dist/index.d.ts +9 -10
  78. package/dist/index.js +8 -9
  79. package/dist/index.js.map +1 -1
  80. package/dist/invalid-state-transition-exception.cjs +6 -6
  81. package/dist/invalid-state-transition-exception.cjs.map +1 -1
  82. package/dist/invalid-state-transition-exception.js +6 -6
  83. package/dist/invalid-state-transition-exception.js.map +1 -1
  84. package/dist/invariant-violation-exception.cjs +2 -2
  85. package/dist/invariant-violation-exception.cjs.map +1 -1
  86. package/dist/invariant-violation-exception.js +2 -2
  87. package/dist/invariant-violation-exception.js.map +1 -1
  88. package/dist/not-found-error.cjs +6 -4
  89. package/dist/not-found-error.cjs.map +1 -1
  90. package/dist/not-found-error.js +6 -4
  91. package/dist/not-found-error.js.map +1 -1
  92. package/dist/outcome.cjs +5 -5
  93. package/dist/outcome.cjs.map +1 -1
  94. package/dist/outcome.js +5 -5
  95. package/dist/outcome.js.map +1 -1
  96. package/dist/parse-gate-payload.d.cts +1 -1
  97. package/dist/parse-gate-payload.d.ts +1 -1
  98. package/dist/path.d.cts +2 -2
  99. package/dist/path.d.ts +2 -2
  100. package/dist/plugin.cjs +23 -13
  101. package/dist/plugin.cjs.map +1 -1
  102. package/dist/plugin.d.cts +22 -8
  103. package/dist/plugin.d.ts +22 -8
  104. package/dist/plugin.js +23 -13
  105. package/dist/plugin.js.map +1 -1
  106. package/dist/policies/engines/index.d.cts +1 -1
  107. package/dist/policies/engines/index.d.ts +1 -1
  108. package/dist/policies/engines/v1/gate/index.d.cts +3 -13
  109. package/dist/policies/engines/v1/gate/index.d.ts +3 -13
  110. package/dist/policies/index.d.cts +1 -1
  111. package/dist/policies/index.d.ts +1 -1
  112. package/dist/policy-bridge.cjs +30 -2
  113. package/dist/policy-bridge.cjs.map +1 -1
  114. package/dist/policy-bridge.d.cts +18 -0
  115. package/dist/policy-bridge.d.ts +18 -0
  116. package/dist/policy-bridge.js +30 -2
  117. package/dist/policy-bridge.js.map +1 -1
  118. package/dist/policy-service.cjs +41 -46
  119. package/dist/policy-service.cjs.map +1 -1
  120. package/dist/policy-service.d.cts +72 -9
  121. package/dist/policy-service.d.ts +72 -9
  122. package/dist/policy-service.js +43 -48
  123. package/dist/policy-service.js.map +1 -1
  124. package/dist/requested-by.cjs +4 -4
  125. package/dist/requested-by.cjs.map +1 -1
  126. package/dist/requested-by.js +2 -2
  127. package/dist/requested-by.js.map +1 -1
  128. package/dist/result.cjs +29 -1
  129. package/dist/result.cjs.map +1 -1
  130. package/dist/result.d.cts +12 -0
  131. package/dist/result.d.ts +12 -0
  132. package/dist/result.js +29 -1
  133. package/dist/result.js.map +1 -1
  134. package/dist/rule.cjs +94 -24
  135. package/dist/rule.cjs.map +1 -1
  136. package/dist/rule.js +94 -24
  137. package/dist/rule.js.map +1 -1
  138. package/dist/ruleset-registry.cjs +19 -0
  139. package/dist/ruleset-registry.cjs.map +1 -1
  140. package/dist/ruleset-registry.js +19 -0
  141. package/dist/ruleset-registry.js.map +1 -1
  142. package/dist/stable-stringify.cjs +87 -0
  143. package/dist/stable-stringify.cjs.map +1 -0
  144. package/dist/stable-stringify.js +76 -0
  145. package/dist/stable-stringify.js.map +1 -0
  146. package/dist/temporal-snapshot.d.cts +87 -0
  147. package/dist/temporal-snapshot.d.ts +87 -0
  148. package/dist/temporal-use-case.cjs +174 -16
  149. package/dist/temporal-use-case.cjs.map +1 -1
  150. package/dist/temporal-use-case.d.cts +82 -44
  151. package/dist/temporal-use-case.d.ts +82 -44
  152. package/dist/temporal-use-case.js +167 -15
  153. package/dist/temporal-use-case.js.map +1 -1
  154. package/dist/unexpected-error.cjs +4 -2
  155. package/dist/unexpected-error.cjs.map +1 -1
  156. package/dist/unexpected-error.js +4 -2
  157. package/dist/unexpected-error.js.map +1 -1
  158. package/dist/uuid-identifier.cjs +141 -8
  159. package/dist/uuid-identifier.cjs.map +1 -1
  160. package/dist/uuid-identifier.d.cts +26 -7
  161. package/dist/uuid-identifier.d.ts +26 -7
  162. package/dist/uuid-identifier.js +135 -8
  163. package/dist/uuid-identifier.js.map +1 -1
  164. package/dist/uuid.cjs +23 -0
  165. package/dist/uuid.cjs.map +1 -0
  166. package/dist/uuid.js +18 -0
  167. package/dist/uuid.js.map +1 -0
  168. package/dist/validation-code.cjs +9 -0
  169. package/dist/validation-code.cjs.map +1 -1
  170. package/dist/validation-code.d.cts +3 -0
  171. package/dist/validation-code.d.ts +3 -0
  172. package/dist/validation-code.js +9 -0
  173. package/dist/validation-code.js.map +1 -1
  174. package/dist/validation-error.cjs +42 -67
  175. package/dist/validation-error.cjs.map +1 -1
  176. package/dist/validation-error.d.cts +29 -8
  177. package/dist/validation-error.d.ts +29 -8
  178. package/dist/validation-error.js +41 -66
  179. package/dist/validation-error.js.map +1 -1
  180. package/dist/validation-exception.cjs +17 -6
  181. package/dist/validation-exception.cjs.map +1 -1
  182. package/dist/validation-exception.d.cts +33 -9
  183. package/dist/validation-exception.d.ts +33 -9
  184. package/dist/validation-exception.js +17 -6
  185. package/dist/validation-exception.js.map +1 -1
  186. package/dist/validation-field.cjs +3 -0
  187. package/dist/validation-field.cjs.map +1 -1
  188. package/dist/validation-field.d.cts +1 -0
  189. package/dist/validation-field.d.ts +1 -0
  190. package/dist/validation-field.js +3 -0
  191. package/dist/validation-field.js.map +1 -1
  192. package/dist/value-object-ruleset.contracts.d.cts +10 -0
  193. package/dist/value-object-ruleset.contracts.d.ts +10 -0
  194. package/dist/value-object.cjs +23 -4
  195. package/dist/value-object.cjs.map +1 -1
  196. package/dist/value-object.d.cts +25 -1
  197. package/dist/value-object.d.ts +25 -1
  198. package/dist/value-object.js +23 -4
  199. package/dist/value-object.js.map +1 -1
  200. package/dist/version.d.cts +27 -0
  201. package/dist/version.d.ts +27 -0
  202. package/dist/versioning/index.d.cts +2 -2
  203. package/dist/versioning/index.d.ts +2 -2
  204. package/meta.json +5 -2
  205. package/package.json +1 -1
  206. package/src/core/abac/authorizer.ts +60 -10
  207. package/src/core/abac/domain/policy-set.ts +52 -5
  208. package/src/core/abac/domain/rule.ts +28 -16
  209. package/src/core/abac/index.ts +7 -1
  210. package/src/core/application/commands/command.ts +14 -1
  211. package/src/core/application/commands/requested-by.ts +13 -5
  212. package/src/core/application/index.ts +2 -1
  213. package/src/core/application/policy-error-mapper.ts +6 -0
  214. package/src/core/application/ports/index.ts +7 -0
  215. package/src/core/application/ports/temporal-repository.port.ts +35 -2
  216. package/src/core/application/queries/index.ts +1 -1
  217. package/src/core/application/queries/query.ts +25 -3
  218. package/src/core/application/temporal/temporal-use-case.ts +31 -4
  219. package/src/core/application/use-case.ts +45 -8
  220. package/src/core/config/core-config.ts +46 -25
  221. package/src/core/config/index.ts +1 -0
  222. package/src/core/config/policy-reporter.ts +32 -1
  223. package/src/core/domain/entity.ts +51 -8
  224. package/src/core/domain/rulesets/entity-ruleset.contracts.ts +0 -2
  225. package/src/core/domain/rulesets/ruleset-registry.ts +24 -0
  226. package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +1 -7
  227. package/src/core/domain/temporal/half-open-interval.ts +34 -0
  228. package/src/core/domain/temporal/temporal-snapshot.ts +13 -2
  229. package/src/core/domain/temporal/transaction-time.ts +19 -15
  230. package/src/core/domain/temporal/valid-time.ts +20 -15
  231. package/src/core/domain/uuid-identifier.ts +6 -11
  232. package/src/core/domain/value-object.ts +29 -3
  233. package/src/core/errors/authentication-error.ts +5 -31
  234. package/src/core/errors/authorization-error.ts +12 -20
  235. package/src/core/errors/business-rule-violation-error.ts +4 -1
  236. package/src/core/errors/idempotency-error.ts +5 -2
  237. package/src/core/errors/index.ts +4 -0
  238. package/src/core/errors/integration-error.ts +4 -24
  239. package/src/core/errors/legacy-incompatible-error.ts +1 -0
  240. package/src/core/errors/not-found-error.ts +4 -1
  241. package/src/core/errors/temporal-error.ts +22 -20
  242. package/src/core/errors/unexpected-error.ts +5 -1
  243. package/src/core/errors/utils/factory-helpers.ts +70 -0
  244. package/src/core/errors/utils/index.ts +5 -0
  245. package/src/core/errors/utils/json-safe.ts +35 -12
  246. package/src/core/errors/validation-error.ts +4 -1
  247. package/src/core/exceptions/business-rule-violation-exception.ts +2 -1
  248. package/src/core/exceptions/domain-exception.ts +9 -1
  249. package/src/core/exceptions/entity-not-found-exception.ts +5 -1
  250. package/src/core/exceptions/invalid-state-transition-exception.ts +5 -2
  251. package/src/core/exceptions/invariant-violation-exception.ts +2 -2
  252. package/src/core/exceptions/validation-code.ts +14 -0
  253. package/src/core/exceptions/validation-exception.ts +44 -5
  254. package/src/core/exceptions/validation-field.ts +11 -1
  255. package/src/core/plugins/plugin.ts +25 -15
  256. package/src/core/plugins/types.ts +4 -2
  257. package/src/core/policies/asof/asof.ts +12 -0
  258. package/src/core/policies/catalog/policy-catalog-entry.ts +3 -1
  259. package/src/core/policies/catalog/policy-catalog.ts +5 -1
  260. package/src/core/policies/context/context-builder.ts +20 -0
  261. package/src/core/policies/context/context-resolver.ts +5 -0
  262. package/src/core/policies/context/context-seed.ts +11 -0
  263. package/src/core/policies/defs/in-memory-policy-definition-repo.ts +0 -2
  264. package/src/core/policies/engines/parse-gate-payload.ts +9 -0
  265. package/src/core/policies/engines/v1/condition-date-operands.ts +112 -0
  266. package/src/core/policies/engines/v1/condition-evaluator.ts +120 -291
  267. package/src/core/policies/engines/v1/condition-schema.ts +23 -19
  268. package/src/core/policies/engines/v1/condition-types.ts +18 -11
  269. package/src/core/policies/index.ts +4 -6
  270. package/src/core/policies/service/policy-service.ts +46 -35
  271. package/src/core/policies/utils/hash.ts +5 -69
  272. package/src/core/policies/utils/result.ts +1 -1
  273. package/src/core/rbac/access-request.ts +12 -2
  274. package/src/core/rbac/authorizer.ts +8 -1
  275. package/src/core/rbac/domain/role.ts +20 -0
  276. package/src/core/rbac/domain/scope.ts +6 -1
  277. package/src/core/result/index.ts +5 -0
  278. package/src/core/result/outcome.ts +5 -7
  279. package/src/core/result/result.ts +34 -1
  280. package/src/core/shared/hashing.ts +6 -57
  281. package/src/core/shared/immutable.ts +22 -4
  282. package/src/core/shared/stable-stringify.ts +144 -0
  283. package/src/core/shared/uuid.ts +16 -0
  284. package/src/core/versioning/domain-event-contracts.ts +43 -15
  285. package/src/core/versioning/index.ts +1 -0
  286. package/src/core/versioning/version.ts +30 -1
  287. package/src/domain/index.ts +5 -0
  288. package/src/examples/rulesets/entity/order-creation-rules-v1.ts +1 -1
  289. package/src/examples/rulesets/entity/order-invariants-v1.ts +2 -4
  290. package/src/examples/rulesets/entity/order-invariants-v2.ts +2 -4
  291. package/src/examples/rulesets/value-object/cpf-rules-v1.ts +2 -4
  292. package/src/examples/rulesets/value-object/cpf-rules-v2.ts +2 -4
  293. package/src/examples/rulesets/value-object/person-name-rules-v1.ts +2 -4
  294. package/src/examples/rulesets/value-object/person-name-rules-v2.ts +2 -4
  295. package/src/result/index.ts +7 -2
  296. package/src/version.ts +1 -1
  297. package/dist/domain-exception.d.cts +0 -7
  298. package/dist/domain-exception.d.ts +0 -7
  299. package/dist/entity.cjs +0 -126
  300. package/dist/entity.cjs.map +0 -1
  301. package/dist/entity.js +0 -115
  302. package/dist/entity.js.map +0 -1
  303. package/dist/use-case.cjs +0 -96
  304. package/dist/use-case.cjs.map +0 -1
  305. package/dist/use-case.js +0 -91
  306. package/dist/use-case.js.map +0 -1
package/dist/plugin.cjs CHANGED
@@ -3,11 +3,23 @@
3
3
  * Registry that holds a set of plugins implementing a shared {@link PluginContract}
4
4
  * and resolves which of them answer a given extension point.
5
5
  *
6
- * Plugins are kept sorted by priority (descending). For a given method,
7
- * {@link invoke} either delegates to the highest-priority enabled plugin
8
- * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).
9
- * When no plugin is enabled, the supplied `fallback` runs instead, so the host
10
- * always has defined behaviour even with an empty registry.
6
+ * Plugins are kept sorted by priority (descending); ties keep registration
7
+ * order (the sort is stable). For a given method, {@link invoke} either
8
+ * delegates to the highest-priority enabled plugin (`'first'`) or folds every
9
+ * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,
10
+ * the supplied `fallback` runs instead, so the host always has defined
11
+ * behaviour even with an empty registry.
12
+ *
13
+ * `name` is a unique key: registering a plugin whose name already exists
14
+ * replaces the previous one, so register/enable/disable/unregister all address
15
+ * the same single plugin.
16
+ *
17
+ * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap
18
+ * them in try/catch. A throwing plugin propagates to the caller (and, in
19
+ * `'pipeline'` mode, discards the accumulated result). Error handling, logging
20
+ * and retries are the host's responsibility by design — the registry's only
21
+ * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin
22
+ * is expected to implement the full contract `P` (the types enforce it).
11
23
  *
12
24
  * @typeParam P - The plugin contract this manager coordinates.
13
25
  */
@@ -15,8 +27,10 @@ var PluginManager = class {
15
27
  constructor() {
16
28
  this.plugins = [];
17
29
  }
18
- /** Registers one or more plugins and re-sorts by priority. */
30
+ /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */
19
31
  register(...plugins) {
32
+ const incoming = new Set(plugins.map((p) => p.name));
33
+ this.plugins = this.plugins.filter((p) => !incoming.has(p.name));
20
34
  this.plugins.push(...plugins);
21
35
  this.reorder();
22
36
  }
@@ -48,18 +62,14 @@ var PluginManager = class {
48
62
  */
49
63
  invoke(method, args, opts) {
50
64
  const { mode = "first", fallback, reducer } = opts;
51
- const enabled = this.list().filter((p) => method in p);
65
+ const enabled = this.list();
52
66
  if (enabled.length === 0) return fallback(...args);
53
- if (mode === "first") {
54
- const fn = enabled[0][method];
55
- return fn(...args);
56
- }
67
+ if (mode === "first") return enabled[0][method](...args);
57
68
  const defaultReducer = (_, curr) => curr;
58
69
  const red = reducer ?? defaultReducer;
59
70
  let acc = enabled[0][method](...args);
60
71
  enabled.slice(1).forEach((plugin, idx) => {
61
- const res = plugin[method](...args);
62
- acc = red(acc, res, plugin, idx + 1);
72
+ acc = red(acc, plugin[method](...args), plugin, idx + 1);
63
73
  });
64
74
  return acc;
65
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs","names":[],"sources":["../src/core/plugins/plugin.ts"],"sourcesContent":["import {\n type BasePlugin,\n type InvokeOptions,\n type PipelineReducer,\n type PluginContract,\n} from \"./types.js\";\n\n/**\n * Registry that holds a set of plugins implementing a shared {@link PluginContract}\n * and resolves which of them answer a given extension point.\n *\n * Plugins are kept sorted by priority (descending). For a given method,\n * {@link invoke} either delegates to the highest-priority enabled plugin\n * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).\n * When no plugin is enabled, the supplied `fallback` runs instead, so the host\n * always has defined behaviour even with an empty registry.\n *\n * @typeParam P - The plugin contract this manager coordinates.\n */\nclass PluginManager<P extends PluginContract> {\n /** Internal list, kept sorted by priority (desc). */\n private plugins: (P & BasePlugin)[] = [];\n\n /** Registers one or more plugins and re-sorts by priority. */\n public register(...plugins: (P & BasePlugin)[]): void {\n this.plugins.push(...plugins);\n this.reorder();\n }\n\n /** Removes a previously registered plugin by name. */\n public unregister(name: string): void {\n this.plugins = this.plugins.filter((p) => p.name !== name);\n }\n\n /** Enables a registered plugin by name. */\n public enable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = true;\n }\n\n /** Disables a registered plugin by name (without removing it). */\n public disable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = false;\n }\n\n /** Lists enabled plugins only, already in priority order. */\n public list(): readonly (P & BasePlugin)[] {\n return this.plugins.filter((p) => p.enabled !== false);\n }\n\n /**\n * Resolves `method` against the registered plugins.\n *\n * In `'first'` mode the highest-priority enabled plugin that implements\n * `method` answers. In `'pipeline'` mode every enabled plugin runs and the\n * results are folded with `reducer` (defaults to \"keep the last result\").\n * When no enabled plugin implements `method`, `fallback` is called.\n */\n public invoke<K extends keyof P>(\n method: K,\n args: Parameters<P[K]>,\n opts: InvokeOptions<P, K>,\n ): ReturnType<P[K]> {\n const { mode = \"first\", fallback, reducer } = opts;\n const enabled = this.list().filter((p) => method in p) as Array<\n P & BasePlugin\n >;\n\n if (enabled.length === 0) {\n return (fallback as P[K])(...args);\n }\n\n if (mode === \"first\") {\n const fn = enabled[0][method] as P[K];\n return fn(...args);\n }\n\n const defaultReducer: PipelineReducer<ReturnType<P[K]>, P> = (\n _,\n curr,\n ) => curr;\n const red = reducer ?? defaultReducer;\n\n let acc: ReturnType<P[K]> = enabled[0][method]!(...args);\n enabled.slice(1).forEach((plugin, idx) => {\n const res = plugin[method]!(...args);\n acc = red(acc, res, plugin, idx + 1);\n });\n return acc;\n }\n\n /** Re-sorts the internal list by priority (desc; default priority = 0). */\n private reorder(): void {\n this.plugins.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n}\n\nexport { PluginManager };\n"],"mappings":";;;;;;;;;;;;;AAmBA,IAAM,gBAAN,MAA8C;;iBAEJ,CAAC;;;CAGvC,SAAgB,GAAG,SAAmC;EAClD,KAAK,QAAQ,KAAK,GAAG,OAAO;EAC5B,KAAK,QAAQ;CACjB;;CAGA,WAAkB,MAAoB;EAClC,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,EAAE,SAAS,IAAI;CAC7D;;CAGA,OAAc,MAAoB;EAC9B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,QAAe,MAAoB;EAC/B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,OAA2C;EACvC,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAE,YAAY,KAAK;CACzD;;;;;;;;;CAUA,OACI,QACA,MACA,MACgB;EAChB,MAAM,EAAE,OAAO,SAAS,UAAU,YAAY;EAC9C,MAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,MAAM,UAAU,CAAC;EAIrD,IAAI,QAAQ,WAAW,GACnB,OAAQ,SAAkB,GAAG,IAAI;EAGrC,IAAI,SAAS,SAAS;GAClB,MAAM,KAAK,QAAQ,GAAG;GACtB,OAAO,GAAG,GAAG,IAAI;EACrB;EAEA,MAAM,kBACF,GACA,SACC;EACL,MAAM,MAAM,WAAW;EAEvB,IAAI,MAAwB,QAAQ,GAAG,QAAS,GAAG,IAAI;EACvD,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ,QAAQ;GACtC,MAAM,MAAM,OAAO,QAAS,GAAG,IAAI;GACnC,MAAM,IAAI,KAAK,KAAK,QAAQ,MAAM,CAAC;EACvC,CAAC;EACD,OAAO;CACX;;CAGA,UAAwB;EACpB,KAAK,QAAQ,MAAM,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;CACrE;AACJ"}
1
+ {"version":3,"file":"plugin.cjs","names":[],"sources":["../src/core/plugins/plugin.ts"],"sourcesContent":["import {\n type BasePlugin,\n type InvokeOptions,\n type PipelineReducer,\n type PluginContract,\n} from \"./types.js\";\n\n/**\n * Registry that holds a set of plugins implementing a shared {@link PluginContract}\n * and resolves which of them answer a given extension point.\n *\n * Plugins are kept sorted by priority (descending); ties keep registration\n * order (the sort is stable). For a given method, {@link invoke} either\n * delegates to the highest-priority enabled plugin (`'first'`) or folds every\n * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,\n * the supplied `fallback` runs instead, so the host always has defined\n * behaviour even with an empty registry.\n *\n * `name` is a unique key: registering a plugin whose name already exists\n * replaces the previous one, so register/enable/disable/unregister all address\n * the same single plugin.\n *\n * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap\n * them in try/catch. A throwing plugin propagates to the caller (and, in\n * `'pipeline'` mode, discards the accumulated result). Error handling, logging\n * and retries are the host's responsibility by design — the registry's only\n * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin\n * is expected to implement the full contract `P` (the types enforce it).\n *\n * @typeParam P - The plugin contract this manager coordinates.\n */\nclass PluginManager<P extends PluginContract> {\n /** Internal list, kept sorted by priority (desc). */\n private plugins: (P & BasePlugin)[] = [];\n\n /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */\n public register(...plugins: (P & BasePlugin)[]): void {\n const incoming = new Set(plugins.map((p) => p.name));\n this.plugins = this.plugins.filter((p) => !incoming.has(p.name));\n this.plugins.push(...plugins);\n this.reorder();\n }\n\n /** Removes a previously registered plugin by name. */\n public unregister(name: string): void {\n this.plugins = this.plugins.filter((p) => p.name !== name);\n }\n\n /** Enables a registered plugin by name. */\n public enable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = true;\n }\n\n /** Disables a registered plugin by name (without removing it). */\n public disable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = false;\n }\n\n /** Lists enabled plugins only, already in priority order. */\n public list(): readonly (P & BasePlugin)[] {\n return this.plugins.filter((p) => p.enabled !== false);\n }\n\n /**\n * Resolves `method` against the registered plugins.\n *\n * In `'first'` mode the highest-priority enabled plugin that implements\n * `method` answers. In `'pipeline'` mode every enabled plugin runs and the\n * results are folded with `reducer` (defaults to \"keep the last result\").\n * When no enabled plugin implements `method`, `fallback` is called.\n */\n public invoke<K extends keyof P>(\n method: K,\n args: Parameters<P[K]>,\n opts: InvokeOptions<P, K>,\n ): ReturnType<P[K]> {\n const { mode = \"first\", fallback, reducer } = opts;\n const enabled = this.list();\n\n if (enabled.length === 0) {\n return fallback(...args);\n }\n\n if (mode === \"first\") {\n return enabled[0][method](...args);\n }\n\n const defaultReducer: PipelineReducer<ReturnType<P[K]>, P> = (\n _,\n curr,\n ) => curr;\n const red = reducer ?? defaultReducer;\n\n let acc: ReturnType<P[K]> = enabled[0][method](...args);\n enabled.slice(1).forEach((plugin, idx) => {\n acc = red(acc, plugin[method](...args), plugin, idx + 1);\n });\n return acc;\n }\n\n /** Re-sorts the internal list by priority (desc; default priority = 0). */\n private reorder(): void {\n this.plugins.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n}\n\nexport { PluginManager };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAM,gBAAN,MAA8C;;iBAEJ,CAAC;;;CAGvC,SAAgB,GAAG,SAAmC;EAClD,MAAM,WAAW,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC;EACnD,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,IAAI,CAAC;EAC/D,KAAK,QAAQ,KAAK,GAAG,OAAO;EAC5B,KAAK,QAAQ;CACjB;;CAGA,WAAkB,MAAoB;EAClC,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,EAAE,SAAS,IAAI;CAC7D;;CAGA,OAAc,MAAoB;EAC9B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,QAAe,MAAoB;EAC/B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,OAA2C;EACvC,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAE,YAAY,KAAK;CACzD;;;;;;;;;CAUA,OACI,QACA,MACA,MACgB;EAChB,MAAM,EAAE,OAAO,SAAS,UAAU,YAAY;EAC9C,MAAM,UAAU,KAAK,KAAK;EAE1B,IAAI,QAAQ,WAAW,GACnB,OAAO,SAAS,GAAG,IAAI;EAG3B,IAAI,SAAS,SACT,OAAO,QAAQ,GAAG,QAAQ,GAAG,IAAI;EAGrC,MAAM,kBACF,GACA,SACC;EACL,MAAM,MAAM,WAAW;EAEvB,IAAI,MAAwB,QAAQ,GAAG,QAAQ,GAAG,IAAI;EACtD,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ,QAAQ;GACtC,MAAM,IAAI,KAAK,OAAO,QAAQ,GAAG,IAAI,GAAG,QAAQ,MAAM,CAAC;EAC3D,CAAC;EACD,OAAO;CACX;;CAGA,UAAwB;EACpB,KAAK,QAAQ,MAAM,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;CACrE;AACJ"}
package/dist/plugin.d.cts CHANGED
@@ -7,7 +7,7 @@
7
7
  interface BasePlugin {
8
8
  /** Unique identifier, used for logs / debugging and for enable/disable. */
9
9
  readonly name: string;
10
- /** Weight; the highest priority wins (default = 0). */
10
+ /** Weight; the highest priority wins (default = 0). Ties keep registration order. */
11
11
  readonly priority?: number;
12
12
  /** Shortcut to disable a plugin without removing it. */
13
13
  enabled?: boolean;
@@ -32,7 +32,9 @@ type PipelineReducer<R, P extends PluginContract> = (accumulated: R, current: R,
32
32
  interface InvokeOptions<P extends PluginContract, K extends keyof P> {
33
33
  /**
34
34
  * `'first'` → returns the first enabled plugin with the highest priority.
35
- * `'pipeline'` → runs every enabled plugin, combining results via `reducer`.
35
+ * `'pipeline'` → fan-out + reduce: every enabled plugin runs against the
36
+ * same original `args` (results are NOT chained into the next plugin's
37
+ * input), and the results are folded together via `reducer`.
36
38
  * default = `'first'`.
37
39
  */
38
40
  mode?: "first" | "pipeline";
@@ -47,18 +49,30 @@ interface InvokeOptions<P extends PluginContract, K extends keyof P> {
47
49
  * Registry that holds a set of plugins implementing a shared {@link PluginContract}
48
50
  * and resolves which of them answer a given extension point.
49
51
  *
50
- * Plugins are kept sorted by priority (descending). For a given method,
51
- * {@link invoke} either delegates to the highest-priority enabled plugin
52
- * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).
53
- * When no plugin is enabled, the supplied `fallback` runs instead, so the host
54
- * always has defined behaviour even with an empty registry.
52
+ * Plugins are kept sorted by priority (descending); ties keep registration
53
+ * order (the sort is stable). For a given method, {@link invoke} either
54
+ * delegates to the highest-priority enabled plugin (`'first'`) or folds every
55
+ * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,
56
+ * the supplied `fallback` runs instead, so the host always has defined
57
+ * behaviour even with an empty registry.
58
+ *
59
+ * `name` is a unique key: registering a plugin whose name already exists
60
+ * replaces the previous one, so register/enable/disable/unregister all address
61
+ * the same single plugin.
62
+ *
63
+ * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap
64
+ * them in try/catch. A throwing plugin propagates to the caller (and, in
65
+ * `'pipeline'` mode, discards the accumulated result). Error handling, logging
66
+ * and retries are the host's responsibility by design — the registry's only
67
+ * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin
68
+ * is expected to implement the full contract `P` (the types enforce it).
55
69
  *
56
70
  * @typeParam P - The plugin contract this manager coordinates.
57
71
  */
58
72
  declare class PluginManager<P extends PluginContract> {
59
73
  /** Internal list, kept sorted by priority (desc). */
60
74
  private plugins;
61
- /** Registers one or more plugins and re-sorts by priority. */
75
+ /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */
62
76
  register(...plugins: (P & BasePlugin)[]): void;
63
77
  /** Removes a previously registered plugin by name. */
64
78
  unregister(name: string): void;
package/dist/plugin.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  interface BasePlugin {
8
8
  /** Unique identifier, used for logs / debugging and for enable/disable. */
9
9
  readonly name: string;
10
- /** Weight; the highest priority wins (default = 0). */
10
+ /** Weight; the highest priority wins (default = 0). Ties keep registration order. */
11
11
  readonly priority?: number;
12
12
  /** Shortcut to disable a plugin without removing it. */
13
13
  enabled?: boolean;
@@ -32,7 +32,9 @@ type PipelineReducer<R, P extends PluginContract> = (accumulated: R, current: R,
32
32
  interface InvokeOptions<P extends PluginContract, K extends keyof P> {
33
33
  /**
34
34
  * `'first'` → returns the first enabled plugin with the highest priority.
35
- * `'pipeline'` → runs every enabled plugin, combining results via `reducer`.
35
+ * `'pipeline'` → fan-out + reduce: every enabled plugin runs against the
36
+ * same original `args` (results are NOT chained into the next plugin's
37
+ * input), and the results are folded together via `reducer`.
36
38
  * default = `'first'`.
37
39
  */
38
40
  mode?: "first" | "pipeline";
@@ -47,18 +49,30 @@ interface InvokeOptions<P extends PluginContract, K extends keyof P> {
47
49
  * Registry that holds a set of plugins implementing a shared {@link PluginContract}
48
50
  * and resolves which of them answer a given extension point.
49
51
  *
50
- * Plugins are kept sorted by priority (descending). For a given method,
51
- * {@link invoke} either delegates to the highest-priority enabled plugin
52
- * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).
53
- * When no plugin is enabled, the supplied `fallback` runs instead, so the host
54
- * always has defined behaviour even with an empty registry.
52
+ * Plugins are kept sorted by priority (descending); ties keep registration
53
+ * order (the sort is stable). For a given method, {@link invoke} either
54
+ * delegates to the highest-priority enabled plugin (`'first'`) or folds every
55
+ * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,
56
+ * the supplied `fallback` runs instead, so the host always has defined
57
+ * behaviour even with an empty registry.
58
+ *
59
+ * `name` is a unique key: registering a plugin whose name already exists
60
+ * replaces the previous one, so register/enable/disable/unregister all address
61
+ * the same single plugin.
62
+ *
63
+ * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap
64
+ * them in try/catch. A throwing plugin propagates to the caller (and, in
65
+ * `'pipeline'` mode, discards the accumulated result). Error handling, logging
66
+ * and retries are the host's responsibility by design — the registry's only
67
+ * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin
68
+ * is expected to implement the full contract `P` (the types enforce it).
55
69
  *
56
70
  * @typeParam P - The plugin contract this manager coordinates.
57
71
  */
58
72
  declare class PluginManager<P extends PluginContract> {
59
73
  /** Internal list, kept sorted by priority (desc). */
60
74
  private plugins;
61
- /** Registers one or more plugins and re-sorts by priority. */
75
+ /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */
62
76
  register(...plugins: (P & BasePlugin)[]): void;
63
77
  /** Removes a previously registered plugin by name. */
64
78
  unregister(name: string): void;
package/dist/plugin.js CHANGED
@@ -3,11 +3,23 @@
3
3
  * Registry that holds a set of plugins implementing a shared {@link PluginContract}
4
4
  * and resolves which of them answer a given extension point.
5
5
  *
6
- * Plugins are kept sorted by priority (descending). For a given method,
7
- * {@link invoke} either delegates to the highest-priority enabled plugin
8
- * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).
9
- * When no plugin is enabled, the supplied `fallback` runs instead, so the host
10
- * always has defined behaviour even with an empty registry.
6
+ * Plugins are kept sorted by priority (descending); ties keep registration
7
+ * order (the sort is stable). For a given method, {@link invoke} either
8
+ * delegates to the highest-priority enabled plugin (`'first'`) or folds every
9
+ * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,
10
+ * the supplied `fallback` runs instead, so the host always has defined
11
+ * behaviour even with an empty registry.
12
+ *
13
+ * `name` is a unique key: registering a plugin whose name already exists
14
+ * replaces the previous one, so register/enable/disable/unregister all address
15
+ * the same single plugin.
16
+ *
17
+ * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap
18
+ * them in try/catch. A throwing plugin propagates to the caller (and, in
19
+ * `'pipeline'` mode, discards the accumulated result). Error handling, logging
20
+ * and retries are the host's responsibility by design — the registry's only
21
+ * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin
22
+ * is expected to implement the full contract `P` (the types enforce it).
11
23
  *
12
24
  * @typeParam P - The plugin contract this manager coordinates.
13
25
  */
@@ -15,8 +27,10 @@ var PluginManager = class {
15
27
  constructor() {
16
28
  this.plugins = [];
17
29
  }
18
- /** Registers one or more plugins and re-sorts by priority. */
30
+ /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */
19
31
  register(...plugins) {
32
+ const incoming = new Set(plugins.map((p) => p.name));
33
+ this.plugins = this.plugins.filter((p) => !incoming.has(p.name));
20
34
  this.plugins.push(...plugins);
21
35
  this.reorder();
22
36
  }
@@ -48,18 +62,14 @@ var PluginManager = class {
48
62
  */
49
63
  invoke(method, args, opts) {
50
64
  const { mode = "first", fallback, reducer } = opts;
51
- const enabled = this.list().filter((p) => method in p);
65
+ const enabled = this.list();
52
66
  if (enabled.length === 0) return fallback(...args);
53
- if (mode === "first") {
54
- const fn = enabled[0][method];
55
- return fn(...args);
56
- }
67
+ if (mode === "first") return enabled[0][method](...args);
57
68
  const defaultReducer = (_, curr) => curr;
58
69
  const red = reducer ?? defaultReducer;
59
70
  let acc = enabled[0][method](...args);
60
71
  enabled.slice(1).forEach((plugin, idx) => {
61
- const res = plugin[method](...args);
62
- acc = red(acc, res, plugin, idx + 1);
72
+ acc = red(acc, plugin[method](...args), plugin, idx + 1);
63
73
  });
64
74
  return acc;
65
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../src/core/plugins/plugin.ts"],"sourcesContent":["import {\n type BasePlugin,\n type InvokeOptions,\n type PipelineReducer,\n type PluginContract,\n} from \"./types.js\";\n\n/**\n * Registry that holds a set of plugins implementing a shared {@link PluginContract}\n * and resolves which of them answer a given extension point.\n *\n * Plugins are kept sorted by priority (descending). For a given method,\n * {@link invoke} either delegates to the highest-priority enabled plugin\n * (`'first'`) or folds every enabled plugin into a single value (`'pipeline'`).\n * When no plugin is enabled, the supplied `fallback` runs instead, so the host\n * always has defined behaviour even with an empty registry.\n *\n * @typeParam P - The plugin contract this manager coordinates.\n */\nclass PluginManager<P extends PluginContract> {\n /** Internal list, kept sorted by priority (desc). */\n private plugins: (P & BasePlugin)[] = [];\n\n /** Registers one or more plugins and re-sorts by priority. */\n public register(...plugins: (P & BasePlugin)[]): void {\n this.plugins.push(...plugins);\n this.reorder();\n }\n\n /** Removes a previously registered plugin by name. */\n public unregister(name: string): void {\n this.plugins = this.plugins.filter((p) => p.name !== name);\n }\n\n /** Enables a registered plugin by name. */\n public enable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = true;\n }\n\n /** Disables a registered plugin by name (without removing it). */\n public disable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = false;\n }\n\n /** Lists enabled plugins only, already in priority order. */\n public list(): readonly (P & BasePlugin)[] {\n return this.plugins.filter((p) => p.enabled !== false);\n }\n\n /**\n * Resolves `method` against the registered plugins.\n *\n * In `'first'` mode the highest-priority enabled plugin that implements\n * `method` answers. In `'pipeline'` mode every enabled plugin runs and the\n * results are folded with `reducer` (defaults to \"keep the last result\").\n * When no enabled plugin implements `method`, `fallback` is called.\n */\n public invoke<K extends keyof P>(\n method: K,\n args: Parameters<P[K]>,\n opts: InvokeOptions<P, K>,\n ): ReturnType<P[K]> {\n const { mode = \"first\", fallback, reducer } = opts;\n const enabled = this.list().filter((p) => method in p) as Array<\n P & BasePlugin\n >;\n\n if (enabled.length === 0) {\n return (fallback as P[K])(...args);\n }\n\n if (mode === \"first\") {\n const fn = enabled[0][method] as P[K];\n return fn(...args);\n }\n\n const defaultReducer: PipelineReducer<ReturnType<P[K]>, P> = (\n _,\n curr,\n ) => curr;\n const red = reducer ?? defaultReducer;\n\n let acc: ReturnType<P[K]> = enabled[0][method]!(...args);\n enabled.slice(1).forEach((plugin, idx) => {\n const res = plugin[method]!(...args);\n acc = red(acc, res, plugin, idx + 1);\n });\n return acc;\n }\n\n /** Re-sorts the internal list by priority (desc; default priority = 0). */\n private reorder(): void {\n this.plugins.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n}\n\nexport { PluginManager };\n"],"mappings":";;;;;;;;;;;;;AAmBA,IAAM,gBAAN,MAA8C;;iBAEJ,CAAC;;;CAGvC,SAAgB,GAAG,SAAmC;EAClD,KAAK,QAAQ,KAAK,GAAG,OAAO;EAC5B,KAAK,QAAQ;CACjB;;CAGA,WAAkB,MAAoB;EAClC,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,EAAE,SAAS,IAAI;CAC7D;;CAGA,OAAc,MAAoB;EAC9B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,QAAe,MAAoB;EAC/B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,OAA2C;EACvC,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAE,YAAY,KAAK;CACzD;;;;;;;;;CAUA,OACI,QACA,MACA,MACgB;EAChB,MAAM,EAAE,OAAO,SAAS,UAAU,YAAY;EAC9C,MAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,MAAM,UAAU,CAAC;EAIrD,IAAI,QAAQ,WAAW,GACnB,OAAQ,SAAkB,GAAG,IAAI;EAGrC,IAAI,SAAS,SAAS;GAClB,MAAM,KAAK,QAAQ,GAAG;GACtB,OAAO,GAAG,GAAG,IAAI;EACrB;EAEA,MAAM,kBACF,GACA,SACC;EACL,MAAM,MAAM,WAAW;EAEvB,IAAI,MAAwB,QAAQ,GAAG,QAAS,GAAG,IAAI;EACvD,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ,QAAQ;GACtC,MAAM,MAAM,OAAO,QAAS,GAAG,IAAI;GACnC,MAAM,IAAI,KAAK,KAAK,QAAQ,MAAM,CAAC;EACvC,CAAC;EACD,OAAO;CACX;;CAGA,UAAwB;EACpB,KAAK,QAAQ,MAAM,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;CACrE;AACJ"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../src/core/plugins/plugin.ts"],"sourcesContent":["import {\n type BasePlugin,\n type InvokeOptions,\n type PipelineReducer,\n type PluginContract,\n} from \"./types.js\";\n\n/**\n * Registry that holds a set of plugins implementing a shared {@link PluginContract}\n * and resolves which of them answer a given extension point.\n *\n * Plugins are kept sorted by priority (descending); ties keep registration\n * order (the sort is stable). For a given method, {@link invoke} either\n * delegates to the highest-priority enabled plugin (`'first'`) or folds every\n * enabled plugin into a single value (`'pipeline'`). When no plugin is enabled,\n * the supplied `fallback` runs instead, so the host always has defined\n * behaviour even with an empty registry.\n *\n * `name` is a unique key: registering a plugin whose name already exists\n * replaces the previous one, so register/enable/disable/unregister all address\n * the same single plugin.\n *\n * Plugins run arbitrary host code and are trusted: {@link invoke} does NOT wrap\n * them in try/catch. A throwing plugin propagates to the caller (and, in\n * `'pipeline'` mode, discards the accumulated result). Error handling, logging\n * and retries are the host's responsibility by design — the registry's only\n * defined-behaviour guarantee is `fallback` for an empty registry. Every plugin\n * is expected to implement the full contract `P` (the types enforce it).\n *\n * @typeParam P - The plugin contract this manager coordinates.\n */\nclass PluginManager<P extends PluginContract> {\n /** Internal list, kept sorted by priority (desc). */\n private plugins: (P & BasePlugin)[] = [];\n\n /** Registers one or more plugins (replacing any with the same name) and re-sorts by priority. */\n public register(...plugins: (P & BasePlugin)[]): void {\n const incoming = new Set(plugins.map((p) => p.name));\n this.plugins = this.plugins.filter((p) => !incoming.has(p.name));\n this.plugins.push(...plugins);\n this.reorder();\n }\n\n /** Removes a previously registered plugin by name. */\n public unregister(name: string): void {\n this.plugins = this.plugins.filter((p) => p.name !== name);\n }\n\n /** Enables a registered plugin by name. */\n public enable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = true;\n }\n\n /** Disables a registered plugin by name (without removing it). */\n public disable(name: string): void {\n const plugin = this.plugins.find((p) => p.name === name);\n if (plugin) plugin.enabled = false;\n }\n\n /** Lists enabled plugins only, already in priority order. */\n public list(): readonly (P & BasePlugin)[] {\n return this.plugins.filter((p) => p.enabled !== false);\n }\n\n /**\n * Resolves `method` against the registered plugins.\n *\n * In `'first'` mode the highest-priority enabled plugin that implements\n * `method` answers. In `'pipeline'` mode every enabled plugin runs and the\n * results are folded with `reducer` (defaults to \"keep the last result\").\n * When no enabled plugin implements `method`, `fallback` is called.\n */\n public invoke<K extends keyof P>(\n method: K,\n args: Parameters<P[K]>,\n opts: InvokeOptions<P, K>,\n ): ReturnType<P[K]> {\n const { mode = \"first\", fallback, reducer } = opts;\n const enabled = this.list();\n\n if (enabled.length === 0) {\n return fallback(...args);\n }\n\n if (mode === \"first\") {\n return enabled[0][method](...args);\n }\n\n const defaultReducer: PipelineReducer<ReturnType<P[K]>, P> = (\n _,\n curr,\n ) => curr;\n const red = reducer ?? defaultReducer;\n\n let acc: ReturnType<P[K]> = enabled[0][method](...args);\n enabled.slice(1).forEach((plugin, idx) => {\n acc = red(acc, plugin[method](...args), plugin, idx + 1);\n });\n return acc;\n }\n\n /** Re-sorts the internal list by priority (desc; default priority = 0). */\n private reorder(): void {\n this.plugins.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n}\n\nexport { PluginManager };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAM,gBAAN,MAA8C;;iBAEJ,CAAC;;;CAGvC,SAAgB,GAAG,SAAmC;EAClD,MAAM,WAAW,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC;EACnD,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,IAAI,CAAC;EAC/D,KAAK,QAAQ,KAAK,GAAG,OAAO;EAC5B,KAAK,QAAQ;CACjB;;CAGA,WAAkB,MAAoB;EAClC,KAAK,UAAU,KAAK,QAAQ,QAAQ,MAAM,EAAE,SAAS,IAAI;CAC7D;;CAGA,OAAc,MAAoB;EAC9B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,QAAe,MAAoB;EAC/B,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;EACvD,IAAI,QAAQ,OAAO,UAAU;CACjC;;CAGA,OAA2C;EACvC,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAE,YAAY,KAAK;CACzD;;;;;;;;;CAUA,OACI,QACA,MACA,MACgB;EAChB,MAAM,EAAE,OAAO,SAAS,UAAU,YAAY;EAC9C,MAAM,UAAU,KAAK,KAAK;EAE1B,IAAI,QAAQ,WAAW,GACnB,OAAO,SAAS,GAAG,IAAI;EAG3B,IAAI,SAAS,SACT,OAAO,QAAQ,GAAG,QAAQ,GAAG,IAAI;EAGrC,MAAM,kBACF,GACA,SACC;EACL,MAAM,MAAM,WAAW;EAEvB,IAAI,MAAwB,QAAQ,GAAG,QAAQ,GAAG,IAAI;EACtD,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ,QAAQ;GACtC,MAAM,IAAI,KAAK,OAAO,QAAQ,GAAG,IAAI,GAAG,QAAQ,MAAM,CAAC;EAC3D,CAAC;EACD,OAAO;CACX;;CAGA,UAAwB;EACpB,KAAK,QAAQ,MAAM,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;CACrE;AACJ"}
@@ -1,4 +1,4 @@
1
- import { C as GateTraceNodeSnapshot, D as VersionedGateEngine, E as PolicyViolation, O as GatePayload, S as GateTraceLeafSnapshot, T as PolicyContext, _ as ConditionEvaluationReportTag, b as GateOutcomeData, g as ConditionEvaluationReportLevel, h as ConditionEvaluationReport, k as GatePayloadV1, m as ConditionEvaluationOptions, p as ConditionEvaluationCause, v as ConditionEvaluatorReporter, w as GateViolationTrace, x as GateStatus, y as GateOutcome } from "../../core-config.cjs";
1
+ import { C as GateViolationTrace, D as GatePayload, E as VersionedGateEngine, O as GatePayloadV1, S as GateTraceNodeSnapshot, T as PolicyViolation, _ as ConditionEvaluatorReporter, b as GateStatus, f as ConditionEvaluationCause, g as ConditionEvaluationReportTag, h as ConditionEvaluationReportLevel, m as ConditionEvaluationReport, p as ConditionEvaluationOptions, v as GateOutcome, w as PolicyContext, x as GateTraceLeafSnapshot, y as GateOutcomeData } from "../../core-config.cjs";
2
2
  import { a as ComputeOutcome, c as VersionedComputeEngine, d as ComputeDecisionTableRule, f as ComputeParamsPayload, i as ComputeEvaluatorRegistration, l as ComputePayload, n as ComputeRegistry, o as ComputeOutcomeData, p as ComputePayloadV1, r as ComputeEvaluator, s as ComputeStatus, t as GateEngineRegistry, u as ComputeDecisionTablePayload } from "../../gate-engine-registry.cjs";
3
3
  import { a as ComputePayloadParserRegistry, c as ComputeEvaluatorRegistry, i as ComputePayloadParser, n as GatePayloadParserRegistry, o as ComputePayloadParsers, r as GatePayloadParsers, s as ComputeEngineRegistry, t as GatePayloadParser } from "../../parse-gate-payload.cjs";
4
4
  export { ComputeDecisionTablePayload, ComputeDecisionTableRule, ComputeEngineRegistry, ComputeEvaluator, ComputeEvaluatorRegistration, ComputeEvaluatorRegistry, ComputeOutcome, ComputeOutcomeData, ComputeParamsPayload, ComputePayload, ComputePayloadParser, ComputePayloadParserRegistry, ComputePayloadParsers, ComputePayloadV1, ComputeRegistry, ComputeStatus, ConditionEvaluationCause, ConditionEvaluationOptions, ConditionEvaluationReport, ConditionEvaluationReportLevel, ConditionEvaluationReportTag, ConditionEvaluatorReporter, GateEngineRegistry, GateOutcome, GateOutcomeData, GatePayload, GatePayloadParser, GatePayloadParserRegistry, GatePayloadParsers, GatePayloadV1, GateStatus, GateTraceLeafSnapshot, GateTraceNodeSnapshot, GateViolationTrace, PolicyContext, PolicyViolation, VersionedComputeEngine, VersionedGateEngine };
@@ -1,4 +1,4 @@
1
- import { C as GateTraceNodeSnapshot, D as VersionedGateEngine, E as PolicyViolation, O as GatePayload, S as GateTraceLeafSnapshot, T as PolicyContext, _ as ConditionEvaluationReportTag, b as GateOutcomeData, g as ConditionEvaluationReportLevel, h as ConditionEvaluationReport, k as GatePayloadV1, m as ConditionEvaluationOptions, p as ConditionEvaluationCause, v as ConditionEvaluatorReporter, w as GateViolationTrace, x as GateStatus, y as GateOutcome } from "../../core-config.js";
1
+ import { C as GateViolationTrace, D as GatePayload, E as VersionedGateEngine, O as GatePayloadV1, S as GateTraceNodeSnapshot, T as PolicyViolation, _ as ConditionEvaluatorReporter, b as GateStatus, f as ConditionEvaluationCause, g as ConditionEvaluationReportTag, h as ConditionEvaluationReportLevel, m as ConditionEvaluationReport, p as ConditionEvaluationOptions, v as GateOutcome, w as PolicyContext, x as GateTraceLeafSnapshot, y as GateOutcomeData } from "../../core-config.js";
2
2
  import { a as ComputeOutcome, c as VersionedComputeEngine, d as ComputeDecisionTableRule, f as ComputeParamsPayload, i as ComputeEvaluatorRegistration, l as ComputePayload, n as ComputeRegistry, o as ComputeOutcomeData, p as ComputePayloadV1, r as ComputeEvaluator, s as ComputeStatus, t as GateEngineRegistry, u as ComputeDecisionTablePayload } from "../../gate-engine-registry.js";
3
3
  import { a as ComputePayloadParserRegistry, c as ComputeEvaluatorRegistry, i as ComputePayloadParser, n as GatePayloadParserRegistry, o as ComputePayloadParsers, r as GatePayloadParsers, s as ComputeEngineRegistry, t as GatePayloadParser } from "../../parse-gate-payload.js";
4
4
  export { ComputeDecisionTablePayload, ComputeDecisionTableRule, ComputeEngineRegistry, ComputeEvaluator, ComputeEvaluatorRegistration, ComputeEvaluatorRegistry, ComputeOutcome, ComputeOutcomeData, ComputeParamsPayload, ComputePayload, ComputePayloadParser, ComputePayloadParserRegistry, ComputePayloadParsers, ComputePayloadV1, ComputeRegistry, ComputeStatus, ConditionEvaluationCause, ConditionEvaluationOptions, ConditionEvaluationReport, ConditionEvaluationReportLevel, ConditionEvaluationReportTag, ConditionEvaluatorReporter, GateEngineRegistry, GateOutcome, GateOutcomeData, GatePayload, GatePayloadParser, GatePayloadParserRegistry, GatePayloadParsers, GatePayloadV1, GateStatus, GateTraceLeafSnapshot, GateTraceNodeSnapshot, GateViolationTrace, PolicyContext, PolicyViolation, VersionedComputeEngine, VersionedGateEngine };
@@ -1,5 +1,5 @@
1
1
  import { r as Result } from "../../../../result.cjs";
2
- import { A as ConditionLeafNode, D as VersionedGateEngine, O as GatePayload, T as PolicyContext, j as ConditionNode, k as GatePayloadV1, m as ConditionEvaluationOptions, t as CoreConfig, y as GateOutcome } from "../../../../core-config.cjs";
2
+ import { A as ConditionNode, D as GatePayload, E as VersionedGateEngine, O as GatePayloadV1, k as ConditionLeafNode, p as ConditionEvaluationOptions, t as CoreConfig, v as GateOutcome, w as PolicyContext } from "../../../../core-config.cjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/core/policies/engines/v1/condition-evaluator.d.ts
@@ -24,20 +24,10 @@ declare class ConditionEvaluatorV1 {
24
24
  private static isNotNode;
25
25
  private static isRelationalOperator;
26
26
  private static describeError;
27
- private static buildNullishNumericOperandMessage;
28
- private static buildNullishDateOperandMessage;
29
- private static buildInvalidDateOperandMessage;
30
- private static buildInvalidNumericOperandMessage;
31
- private static buildInvalidSetOperandMessage;
32
- private static buildEmptyOrConditionMessage;
33
- private static buildEmptyAndConditionMessage;
34
- private static parseComparableDate;
35
- private static evaluateRelationalNumbers;
27
+ private static invalidDateOperandMessage;
36
28
  private buildReport;
29
+ private reportLeafError;
37
30
  private conditionEvalErr;
38
- private reportDateOperandError;
39
- private reportInvalidNumericOperand;
40
- private reportInvalidSetOperand;
41
31
  private evaluateDateRelationalNode;
42
32
  private evaluateOperator;
43
33
  private evaluateNumericRelationalNode;
@@ -1,5 +1,5 @@
1
1
  import { r as Result } from "../../../../result.js";
2
- import { A as ConditionLeafNode, D as VersionedGateEngine, O as GatePayload, T as PolicyContext, j as ConditionNode, k as GatePayloadV1, m as ConditionEvaluationOptions, t as CoreConfig, y as GateOutcome } from "../../../../core-config.js";
2
+ import { A as ConditionNode, D as GatePayload, E as VersionedGateEngine, O as GatePayloadV1, k as ConditionLeafNode, p as ConditionEvaluationOptions, t as CoreConfig, v as GateOutcome, w as PolicyContext } from "../../../../core-config.js";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/core/policies/engines/v1/condition-evaluator.d.ts
@@ -24,20 +24,10 @@ declare class ConditionEvaluatorV1 {
24
24
  private static isNotNode;
25
25
  private static isRelationalOperator;
26
26
  private static describeError;
27
- private static buildNullishNumericOperandMessage;
28
- private static buildNullishDateOperandMessage;
29
- private static buildInvalidDateOperandMessage;
30
- private static buildInvalidNumericOperandMessage;
31
- private static buildInvalidSetOperandMessage;
32
- private static buildEmptyOrConditionMessage;
33
- private static buildEmptyAndConditionMessage;
34
- private static parseComparableDate;
35
- private static evaluateRelationalNumbers;
27
+ private static invalidDateOperandMessage;
36
28
  private buildReport;
29
+ private reportLeafError;
37
30
  private conditionEvalErr;
38
- private reportDateOperandError;
39
- private reportInvalidNumericOperand;
40
- private reportInvalidSetOperand;
41
31
  private evaluateDateRelationalNode;
42
32
  private evaluateOperator;
43
33
  private evaluateNumericRelationalNode;
@@ -1,6 +1,6 @@
1
1
  import { n as Ok, r as Result, t as Err } from "../result.cjs";
2
2
  import { n as Outcome, t as CommonOutcomeStatus } from "../outcome.cjs";
3
- import { C as GateTraceNodeSnapshot, D as VersionedGateEngine, E as PolicyViolation, O as GatePayload, S as GateTraceLeafSnapshot, T as PolicyContext, _ as ConditionEvaluationReportTag, a as PolicyDecisionId, b as GateOutcomeData, c as TenantId, d as asSchoolId, f as asTenantId, g as ConditionEvaluationReportLevel, h as ConditionEvaluationReport, l as asPolicyDecisionId, m as ConditionEvaluationOptions, n as CoreConfigOptions, o as PolicyDefinitionId, p as ConditionEvaluationCause, r as CoreObservabilityConfig, s as SchoolId, t as CoreConfig, u as asPolicyDefinitionId, v as ConditionEvaluatorReporter, w as GateViolationTrace, x as GateStatus, y as GateOutcome } from "../core-config.cjs";
3
+ import { C as GateViolationTrace, D as GatePayload, E as VersionedGateEngine, S as GateTraceNodeSnapshot, T as PolicyViolation, _ as ConditionEvaluatorReporter, a as PolicyDefinitionId, b as GateStatus, c as asPolicyDecisionId, d as asTenantId, f as ConditionEvaluationCause, g as ConditionEvaluationReportTag, h as ConditionEvaluationReportLevel, i as PolicyDecisionId, l as asPolicyDefinitionId, m as ConditionEvaluationReport, n as CoreConfigOptions, o as SchoolId, p as ConditionEvaluationOptions, r as CoreObservabilityConfig, s as TenantId, t as CoreConfig, u as asSchoolId, v as GateOutcome, w as PolicyContext, x as GateTraceLeafSnapshot, y as GateOutcomeData } from "../core-config.cjs";
4
4
  import { a as PolicyPackage, c as coreConfig, i as PolicyCatalogFactory, n as contextResolverRegistry, o as InMemoryPolicyDefinitionRepository, r as registerNamespacedContextResolvers, s as PolicyHashing, t as PolicyContextPath } from "../path.cjs";
5
5
  import { A as PolicyScope, C as BasePolicyDefinitionProps, D as PolicyDefinition, E as GatePolicyDefinitionProps, F as PolicyKey, I as AsOfSource, L as PolicyKind, M as ScopeChain, N as PolicyCatalog, O as PolicyDefinitionProps, P as PolicyCatalogEntryProps, R as PolicyOwner, T as FindCandidatesParams, _ as ContextResolverResilienceOptions, a as PolicyServiceOptions, b as ContextSeed, c as PolicyEvaluationErrors, d as PolicyAsOfResolver, f as PolicyContextBuilder, g as ContextResolverCircuitBreakerOptions, h as registerNamespacedContextResolversIn, i as PolicyService, j as PolicyScopeMatcher, k as PolicyDefinitionStatus, l as PolicyResolver, m as ContextResolverRegistry, n as PolicyDecision, o as PolicyServiceParams, p as PolicyContextBuilderOptions, r as PolicyEvaluationResult, s as PolicyEvaluationError, t as EvaluateInput, u as DeriveAsOfOptions, v as ContextResolverRetryOptions, w as ComputePolicyDefinitionProps, x as ContextSeedValidator, y as ContextValueResolver, z as PolicyScopeLevel } from "../policy-service.cjs";
6
6
  import { a as ComputeOutcome, c as VersionedComputeEngine, i as ComputeEvaluatorRegistration, n as ComputeRegistry, o as ComputeOutcomeData, r as ComputeEvaluator, s as ComputeStatus, t as GateEngineRegistry } from "../gate-engine-registry.cjs";
@@ -1,6 +1,6 @@
1
1
  import { n as Ok, r as Result, t as Err } from "../result.js";
2
2
  import { n as Outcome, t as CommonOutcomeStatus } from "../outcome.js";
3
- import { C as GateTraceNodeSnapshot, D as VersionedGateEngine, E as PolicyViolation, O as GatePayload, S as GateTraceLeafSnapshot, T as PolicyContext, _ as ConditionEvaluationReportTag, a as PolicyDecisionId, b as GateOutcomeData, c as TenantId, d as asSchoolId, f as asTenantId, g as ConditionEvaluationReportLevel, h as ConditionEvaluationReport, l as asPolicyDecisionId, m as ConditionEvaluationOptions, n as CoreConfigOptions, o as PolicyDefinitionId, p as ConditionEvaluationCause, r as CoreObservabilityConfig, s as SchoolId, t as CoreConfig, u as asPolicyDefinitionId, v as ConditionEvaluatorReporter, w as GateViolationTrace, x as GateStatus, y as GateOutcome } from "../core-config.js";
3
+ import { C as GateViolationTrace, D as GatePayload, E as VersionedGateEngine, S as GateTraceNodeSnapshot, T as PolicyViolation, _ as ConditionEvaluatorReporter, a as PolicyDefinitionId, b as GateStatus, c as asPolicyDecisionId, d as asTenantId, f as ConditionEvaluationCause, g as ConditionEvaluationReportTag, h as ConditionEvaluationReportLevel, i as PolicyDecisionId, l as asPolicyDefinitionId, m as ConditionEvaluationReport, n as CoreConfigOptions, o as SchoolId, p as ConditionEvaluationOptions, r as CoreObservabilityConfig, s as TenantId, t as CoreConfig, u as asSchoolId, v as GateOutcome, w as PolicyContext, x as GateTraceLeafSnapshot, y as GateOutcomeData } from "../core-config.js";
4
4
  import { a as PolicyPackage, c as coreConfig, i as PolicyCatalogFactory, n as contextResolverRegistry, o as InMemoryPolicyDefinitionRepository, r as registerNamespacedContextResolvers, s as PolicyHashing, t as PolicyContextPath } from "../path.js";
5
5
  import { A as PolicyScope, C as BasePolicyDefinitionProps, D as PolicyDefinition, E as GatePolicyDefinitionProps, F as PolicyKey, I as AsOfSource, L as PolicyKind, M as ScopeChain, N as PolicyCatalog, O as PolicyDefinitionProps, P as PolicyCatalogEntryProps, R as PolicyOwner, T as FindCandidatesParams, _ as ContextResolverResilienceOptions, a as PolicyServiceOptions, b as ContextSeed, c as PolicyEvaluationErrors, d as PolicyAsOfResolver, f as PolicyContextBuilder, g as ContextResolverCircuitBreakerOptions, h as registerNamespacedContextResolversIn, i as PolicyService, j as PolicyScopeMatcher, k as PolicyDefinitionStatus, l as PolicyResolver, m as ContextResolverRegistry, n as PolicyDecision, o as PolicyServiceParams, p as PolicyContextBuilderOptions, r as PolicyEvaluationResult, s as PolicyEvaluationError, t as EvaluateInput, u as DeriveAsOfOptions, v as ContextResolverRetryOptions, w as ComputePolicyDefinitionProps, x as ContextSeedValidator, y as ContextValueResolver, z as PolicyScopeLevel } from "../policy-service.js";
6
6
  import { a as ComputeOutcome, c as VersionedComputeEngine, i as ComputeEvaluatorRegistration, n as ComputeRegistry, o as ComputeOutcomeData, r as ComputeEvaluator, s as ComputeStatus, t as GateEngineRegistry } from "../gate-engine-registry.js";
@@ -31,6 +31,13 @@ var RbacAuthorizer = class {
31
31
  * 3. grants the permission but not in scope → `out_of_scope`;
32
32
  * 4. otherwise → ALLOW (`Result.ok`).
33
33
  *
34
+ * ALLOW is intentionally silent: `Result.ok(undefined)` carries no trace of
35
+ * *which* grant or role decided it. Denials are rich (see the metadata
36
+ * below) because they need to explain themselves; an allow does not. When an
37
+ * ERP needs a "who allowed this, and why" trail, that is the adapter's job —
38
+ * reconstruct it from the grants it loaded, or gate on `PermissionSet` before
39
+ * calling. Keeping the decisor's allow-path pure is the deliberate trade.
40
+ *
34
41
  * @throws {@link InvalidValueException} when `request.required` is a wildcard
35
42
  * permission (only *granted* permissions may wildcard, never the *required*
36
43
  * one).
@@ -44,7 +51,7 @@ var RbacAuthorizer = class {
44
51
  const metadata = {
45
52
  action: request.action,
46
53
  resource: request.resource,
47
- actor: { userId: request.actor.raw },
54
+ actor: { actorId: request.actor.raw },
48
55
  required
49
56
  };
50
57
  const actorGrants = grants.filter((grant) => grant.appliesTo(request.actor));
@@ -180,6 +187,22 @@ var Role = class Role extends require_value_object.ValueObject {
180
187
  get permissions() {
181
188
  return this._permissions;
182
189
  }
190
+ /**
191
+ * Two roles are equal when they share a name and the same *set* of
192
+ * permissions. Order-insensitive on purpose: a role is a bundle, not a
193
+ * sequence, so `Role.of("cashier", [read, cancel])` equals
194
+ * `Role.of("cashier", [cancel, read])`. Overrides the base
195
+ * {@link ValueObject.equals}, whose stable-stringify keeps array order and
196
+ * would call those two roles different. The stored order (and thus
197
+ * {@link permissions}/{@link toPrimitive}) is still first-seen; only the
198
+ * comparison sorts.
199
+ */
200
+ equals(other) {
201
+ if (this.value.name !== other.value.name) return false;
202
+ const mine = [...this.value.permissions].sort();
203
+ const theirs = [...other.value.permissions].sort();
204
+ return mine.length === theirs.length && mine.every((permission, index) => permission === theirs[index]);
205
+ }
183
206
  /** Whether any permission in this role {@link Permission.implies | implies} `required`. */
184
207
  grants(required) {
185
208
  return this._permissions.some((permission) => permission.implies(required));
@@ -210,7 +233,12 @@ var Scope = class Scope extends require_value_object.ValueObject {
210
233
  super(props);
211
234
  this.finalize();
212
235
  }
213
- /** Validates `"type:id"` or the global `"*"`, throwing on anything else. */
236
+ /**
237
+ * Validates `"type:id"` or the global `"*"`, throwing on anything else.
238
+ * `Scope.of("*")` is accepted and is equivalent to {@link Scope.global} —
239
+ * prefer `global()` at call-sites for intent, `of("*")` when rehydrating a
240
+ * stored string.
241
+ */
214
242
  static of(raw) {
215
243
  if (typeof raw !== "string" || raw.trim().length === 0) throw new require_validation_exception.InvalidValueException(SCOPE_FIELD, require_validation_code.ValidationCode.BLANK, `scope must be a non-empty "type:id" string or "*", got "${raw}"`);
216
244
  if (raw !== "*" && !SCOPE_PATTERN.test(raw)) throw new require_validation_exception.InvalidValueException(SCOPE_FIELD, require_validation_code.ValidationCode.INVALID_FORMAT, `scope must be "type:id" (e.g. "school:42") or the global "*", got "${raw}"`);