@cullet/erp-core 1.3.0 → 1.5.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 (197) hide show
  1. package/KIT_CONTEXT.md +11 -3
  2. package/README.md +160 -0
  3. package/dist/abac/index.cjs +7 -0
  4. package/dist/abac/index.d.cts +2 -0
  5. package/dist/abac/index.d.ts +2 -0
  6. package/dist/abac/index.js +2 -0
  7. package/dist/app-error.cjs +201 -0
  8. package/dist/app-error.cjs.map +1 -0
  9. package/dist/app-error.js +178 -0
  10. package/dist/app-error.js.map +1 -0
  11. package/dist/application/index.cjs +2 -1
  12. package/dist/application/index.d.cts +2 -1
  13. package/dist/application/index.d.ts +2 -1
  14. package/dist/application/index.js +2 -1
  15. package/dist/authorization-error.cjs +157 -0
  16. package/dist/authorization-error.cjs.map +1 -0
  17. package/dist/authorization-error.d.cts +113 -0
  18. package/dist/authorization-error.d.ts +113 -0
  19. package/dist/authorization-error.js +152 -0
  20. package/dist/authorization-error.js.map +1 -0
  21. package/dist/authorizer.port.d.cts +130 -0
  22. package/dist/authorizer.port.d.ts +130 -0
  23. package/dist/composite-authorizer.d.cts +216 -0
  24. package/dist/composite-authorizer.d.ts +216 -0
  25. package/dist/condition-evaluator.cjs +579 -0
  26. package/dist/condition-evaluator.cjs.map +1 -0
  27. package/dist/condition-evaluator.js +550 -0
  28. package/dist/condition-evaluator.js.map +1 -0
  29. package/dist/{gate-types.d.ts → core-config.d.cts} +78 -77
  30. package/dist/{gate-types.d.cts → core-config.d.ts} +78 -77
  31. package/dist/domain/index.cjs +2 -1
  32. package/dist/domain/index.d.cts +2 -1
  33. package/dist/domain/index.d.ts +2 -1
  34. package/dist/domain/index.js +2 -1
  35. package/dist/domain-event-contracts.cjs +3 -2
  36. package/dist/domain-event-contracts.cjs.map +1 -1
  37. package/dist/domain-event-contracts.js +2 -1
  38. package/dist/domain-event-contracts.js.map +1 -1
  39. package/dist/entity.cjs +122 -0
  40. package/dist/entity.cjs.map +1 -0
  41. package/dist/entity.js +111 -0
  42. package/dist/entity.js.map +1 -0
  43. package/dist/errors/index.cjs +10 -7
  44. package/dist/errors/index.d.cts +2 -1
  45. package/dist/errors/index.d.ts +2 -1
  46. package/dist/errors/index.js +6 -3
  47. package/dist/gate-engine-registry.cjs +5 -4
  48. package/dist/gate-engine-registry.cjs.map +1 -1
  49. package/dist/gate-engine-registry.d.cts +3 -2
  50. package/dist/gate-engine-registry.d.ts +3 -2
  51. package/dist/gate-engine-registry.js +2 -1
  52. package/dist/gate-engine-registry.js.map +1 -1
  53. package/dist/gate-v1-payload.schema.cjs +0 -562
  54. package/dist/gate-v1-payload.schema.cjs.map +1 -1
  55. package/dist/gate-v1-payload.schema.js +1 -533
  56. package/dist/gate-v1-payload.schema.js.map +1 -1
  57. package/dist/hashing.cjs +2 -44
  58. package/dist/hashing.cjs.map +1 -1
  59. package/dist/hashing.js +2 -38
  60. package/dist/hashing.js.map +1 -1
  61. package/dist/index.cjs +36 -17
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +19 -12
  64. package/dist/index.d.ts +19 -12
  65. package/dist/index.js +17 -10
  66. package/dist/index.js.map +1 -1
  67. package/dist/not-found-error.cjs +5 -5
  68. package/dist/not-found-error.cjs.map +1 -1
  69. package/dist/not-found-error.js +1 -1
  70. package/dist/outcome.d.cts +1 -83
  71. package/dist/outcome.d.ts +1 -83
  72. package/dist/parse-gate-payload.d.cts +2 -2
  73. package/dist/parse-gate-payload.d.ts +2 -2
  74. package/dist/path.d.cts +2 -2
  75. package/dist/path.d.ts +2 -2
  76. package/dist/policies/engines/index.d.cts +1 -1
  77. package/dist/policies/engines/index.d.ts +1 -1
  78. package/dist/policies/engines/v1/gate/index.cjs +5 -4
  79. package/dist/policies/engines/v1/gate/index.cjs.map +1 -1
  80. package/dist/policies/engines/v1/gate/index.d.cts +4 -2
  81. package/dist/policies/engines/v1/gate/index.d.ts +4 -2
  82. package/dist/policies/engines/v1/gate/index.js +2 -1
  83. package/dist/policies/engines/v1/gate/index.js.map +1 -1
  84. package/dist/policies/index.cjs +5 -5
  85. package/dist/policies/index.d.cts +3 -2
  86. package/dist/policies/index.d.ts +3 -2
  87. package/dist/policies/index.js +2 -2
  88. package/dist/policy-bridge.cjs +437 -0
  89. package/dist/policy-bridge.cjs.map +1 -0
  90. package/dist/policy-bridge.d.cts +185 -0
  91. package/dist/policy-bridge.d.ts +185 -0
  92. package/dist/policy-bridge.js +396 -0
  93. package/dist/policy-bridge.js.map +1 -0
  94. package/dist/policy-service.cjs +241 -240
  95. package/dist/policy-service.cjs.map +1 -1
  96. package/dist/policy-service.d.cts +2 -2
  97. package/dist/policy-service.d.ts +2 -2
  98. package/dist/policy-service.js +240 -239
  99. package/dist/policy-service.js.map +1 -1
  100. package/dist/rbac/index.cjs +9 -0
  101. package/dist/rbac/index.d.cts +3 -0
  102. package/dist/rbac/index.d.ts +3 -0
  103. package/dist/rbac/index.js +2 -0
  104. package/dist/requested-by.cjs +54 -0
  105. package/dist/requested-by.cjs.map +1 -0
  106. package/dist/requested-by.d.cts +25 -0
  107. package/dist/requested-by.d.ts +25 -0
  108. package/dist/requested-by.js +49 -0
  109. package/dist/requested-by.js.map +1 -0
  110. package/dist/result/index.d.cts +2 -1
  111. package/dist/result/index.d.ts +2 -1
  112. package/dist/result.d.cts +84 -0
  113. package/dist/result.d.ts +84 -0
  114. package/dist/rule.cjs +327 -0
  115. package/dist/rule.cjs.map +1 -0
  116. package/dist/rule.js +298 -0
  117. package/dist/rule.js.map +1 -0
  118. package/dist/stable-stringify.cjs +47 -0
  119. package/dist/stable-stringify.cjs.map +1 -0
  120. package/dist/stable-stringify.js +42 -0
  121. package/dist/stable-stringify.js.map +1 -0
  122. package/dist/temporal-use-case.cjs +6 -53
  123. package/dist/temporal-use-case.cjs.map +1 -1
  124. package/dist/temporal-use-case.d.cts +11 -28
  125. package/dist/temporal-use-case.d.ts +11 -28
  126. package/dist/temporal-use-case.js +7 -48
  127. package/dist/temporal-use-case.js.map +1 -1
  128. package/dist/unexpected-error.cjs +3 -192
  129. package/dist/unexpected-error.cjs.map +1 -1
  130. package/dist/unexpected-error.js +2 -167
  131. package/dist/unexpected-error.js.map +1 -1
  132. package/dist/use-case.cjs +2 -6
  133. package/dist/use-case.cjs.map +1 -1
  134. package/dist/use-case.js +2 -6
  135. package/dist/use-case.js.map +1 -1
  136. package/dist/uuid-identifier.cjs +2 -7
  137. package/dist/uuid-identifier.cjs.map +1 -1
  138. package/dist/uuid-identifier.d.cts +2 -85
  139. package/dist/uuid-identifier.d.ts +2 -85
  140. package/dist/uuid-identifier.js +1 -6
  141. package/dist/uuid-identifier.js.map +1 -1
  142. package/dist/uuid.cjs +18 -0
  143. package/dist/uuid.cjs.map +1 -0
  144. package/dist/uuid.js +13 -0
  145. package/dist/uuid.js.map +1 -0
  146. package/dist/validation-error.cjs +33 -166
  147. package/dist/validation-error.cjs.map +1 -1
  148. package/dist/validation-error.d.cts +12 -103
  149. package/dist/validation-error.d.ts +12 -103
  150. package/dist/validation-error.js +7 -134
  151. package/dist/validation-error.js.map +1 -1
  152. package/dist/value-object.cjs +12 -126
  153. package/dist/value-object.cjs.map +1 -1
  154. package/dist/value-object.d.cts +97 -0
  155. package/dist/value-object.d.ts +97 -0
  156. package/dist/value-object.js +13 -115
  157. package/dist/value-object.js.map +1 -1
  158. package/meta.json +20 -4
  159. package/package.json +27 -1
  160. package/src/abac/index.ts +1 -0
  161. package/src/core/abac/abac-request.ts +29 -0
  162. package/src/core/abac/attributes.ts +39 -0
  163. package/src/core/abac/authorizer.ts +108 -0
  164. package/src/core/abac/combining.ts +63 -0
  165. package/src/core/abac/composite-authorizer.ts +45 -0
  166. package/src/core/abac/domain/policy-set.ts +52 -0
  167. package/src/core/abac/domain/rule.ts +197 -0
  168. package/src/core/abac/index.ts +21 -0
  169. package/src/core/application/commands/requested-by.ts +3 -4
  170. package/src/core/application/ports/abac-authorizer.port.ts +20 -0
  171. package/src/core/application/ports/authorizer.port.ts +22 -0
  172. package/src/core/application/queries/query.ts +6 -1
  173. package/src/core/application/use-case.ts +1 -1
  174. package/src/core/domain/entity.ts +1 -1
  175. package/src/core/domain/uuid-identifier.ts +1 -8
  176. package/src/core/domain/value-object.ts +12 -3
  177. package/src/core/errors/authorization-error.ts +26 -0
  178. package/src/core/errors/error-codes.ts +1 -0
  179. package/src/core/errors/utils/json-safe.ts +20 -11
  180. package/src/core/index.ts +7 -0
  181. package/src/core/policies/engines/v1/condition-evaluator.ts +56 -1
  182. package/src/core/rbac/access-request.ts +32 -0
  183. package/src/core/rbac/authorizer.ts +91 -0
  184. package/src/core/rbac/domain/grant.ts +96 -0
  185. package/src/core/rbac/domain/permission-set.ts +67 -0
  186. package/src/core/rbac/domain/permission.ts +119 -0
  187. package/src/core/rbac/domain/role.ts +101 -0
  188. package/src/core/rbac/domain/scope.ts +84 -0
  189. package/src/core/rbac/index.ts +15 -0
  190. package/src/core/rbac/policy-bridge.ts +44 -0
  191. package/src/core/shared/hashing.ts +1 -55
  192. package/src/core/shared/stable-stringify.ts +57 -0
  193. package/src/core/shared/uuid.ts +11 -0
  194. package/src/examples/application/authorize-cancel-order-abac.example.ts +107 -0
  195. package/src/examples/application/authorize-cancel-order.example.ts +101 -0
  196. package/src/rbac/index.ts +1 -0
  197. package/src/version.ts +1 -1
@@ -0,0 +1,216 @@
1
+ import { t as RequestedBy } from "./requested-by.js";
2
+ import { t as AuthorizationError } from "./authorization-error.js";
3
+ import { r as Result } from "./result.js";
4
+ import { T as PolicyContext, j as ConditionNode, t as CoreConfig } from "./core-config.js";
5
+ import { t as ValueObject } from "./value-object.js";
6
+ import { n as AccessRequest, t as AuthorizerPort } from "./authorizer.port.js";
7
+
8
+ //#region src/core/abac/abac-request.d.ts
9
+ /**
10
+ * The attribute bags an ABAC decision reasons over, grouped by the four standard
11
+ * categories. Each is a flat record; {@link abacContext} nests them under
12
+ * `subject` / `resource` / `action` / `env` so a rule's condition can reference
13
+ * fields like `resource.status` or `env.businessHours`.
14
+ */
15
+ type AbacAttributes = {
16
+ readonly subject?: Record<string, unknown>;
17
+ readonly resource?: Record<string, unknown>;
18
+ readonly action?: Record<string, unknown>;
19
+ readonly environment?: Record<string, unknown>;
20
+ };
21
+ /**
22
+ * The question put to the {@link AbacAuthorizer}: *do these attributes authorize
23
+ * this action?* Carries the acting {@link RequestedBy} (reused as the subject),
24
+ * an audit-friendly `action` label, an optional type/id resource reference, and
25
+ * the {@link AbacAttributes} the rules evaluate.
26
+ */
27
+ interface AbacRequest {
28
+ readonly actor: RequestedBy;
29
+ readonly action: string;
30
+ readonly resource?: {
31
+ readonly type: string;
32
+ readonly id?: string;
33
+ };
34
+ readonly attributes: AbacAttributes;
35
+ }
36
+ //#endregion
37
+ //#region src/core/application/ports/abac-authorizer.port.d.ts
38
+ /**
39
+ * The seam an application layer injects to authorize an action by attributes —
40
+ * symmetric to {@link AuthorizerPort} (RBAC). The use case depends only on this
41
+ * contract; an adapter in the consumer's stack resolves the dynamic attributes
42
+ * (resource state, environment, RBAC facts) and delegates the decision to the
43
+ * pure `AbacAuthorizer`.
44
+ *
45
+ * Returns the decision as a {@link Result}: `ok(void)` when permitted, or
46
+ * `err(AuthorizationError)` carrying `reason: "policy_denied"` (or `"forbidden"`
47
+ * on a fail-closed evaluation error) that the HTTP boundary maps to a 403.
48
+ */
49
+ interface AbacAuthorizerPort {
50
+ authorize(request: AbacRequest): Promise<Result<void, AuthorizationError>>;
51
+ }
52
+ //#endregion
53
+ //#region src/core/abac/attributes.d.ts
54
+ /**
55
+ * Flattens an {@link AbacRequest} into the nested {@link PolicyContext} the
56
+ * condition evaluator reads (it resolves dotted fields like `resource.status` by
57
+ * walking nested objects). The actor lands as `subject.id`; the four attribute
58
+ * bags nest under `subject` / `resource` / `action` / `env`; a `resource`
59
+ * reference adds `resource.type` / `resource.id`. The actor id and the resource
60
+ * reference win over any same-named keys in the attribute bags.
61
+ *
62
+ * Consumers fold dynamic facts (e.g. RBAC roles/permissions derived from grants,
63
+ * or a computed `ownerIsActor` flag) into `attributes.subject` / `resource` so
64
+ * rules can reference `subject.roles`, `resource.ownerIsActor`, and the like.
65
+ */
66
+ declare function abacContext(request: AbacRequest): PolicyContext;
67
+ //#endregion
68
+ //#region src/core/abac/domain/rule.d.ts
69
+ /** Whether a matching {@link AbacRule} permits or denies the action. */
70
+ type RuleEffect = "PERMIT" | "DENY";
71
+ /**
72
+ * The serializable shape of a rule: a stable `id`/`version` (surfaced on a
73
+ * denial as `policyId`/`policyVersion`), the {@link RuleEffect}, and the
74
+ * {@link ConditionNode} that decides whether the rule applies to a request's
75
+ * flattened attributes.
76
+ */
77
+ type AbacRuleProps = {
78
+ readonly id: string;
79
+ readonly version: number;
80
+ readonly effect: RuleEffect;
81
+ readonly condition: ConditionNode;
82
+ readonly description?: string;
83
+ };
84
+ /**
85
+ * A single attribute-based rule: an {@link RuleEffect} that takes hold when its
86
+ * {@link ConditionNode} matches the request's flattened attributes. The
87
+ * condition reuses the gate/compute condition DSL (`{ field, op, value }` leaves
88
+ * combined with `and`/`or`/`not`), so a consumer learns one condition language
89
+ * across the whole kit.
90
+ *
91
+ * A zod-free value object: it validates its own shape on construction (throwing
92
+ * {@link InvalidValueException}) and is deep-frozen thereafter. Because rules are
93
+ * authored in TypeScript — trusted data, not untrusted JSON — the structural
94
+ * check is a hand-rolled walk of the condition tree rather than a schema parse.
95
+ * Build one through {@link of}; the constructor is private.
96
+ */
97
+ declare class AbacRule extends ValueObject<AbacRuleProps, AbacRuleProps> {
98
+ private constructor();
99
+ /**
100
+ * Builds a rule, validating that `id` is non-blank, `version` is an integer
101
+ * ≥ 1, `effect` is `"PERMIT"`/`"DENY"`, and `condition` is a well-formed
102
+ * condition node. Throws {@link InvalidValueException} otherwise.
103
+ */
104
+ static of(props: AbacRuleProps): AbacRule;
105
+ private static assertConditionShape;
106
+ private static rejectCondition;
107
+ get id(): string;
108
+ get version(): number;
109
+ get effect(): RuleEffect;
110
+ /** The condition tree evaluated against a request's flattened attributes. */
111
+ get condition(): ConditionNode;
112
+ get description(): string | undefined;
113
+ toPrimitive(): AbacRuleProps;
114
+ }
115
+ //#endregion
116
+ //#region src/core/abac/combining.d.ts
117
+ /**
118
+ * How an {@link AbacPolicySet} resolves several applicable rules into one effect.
119
+ *
120
+ * - `deny-overrides` (default, secure): any applicable DENY wins; otherwise the
121
+ * first PERMIT; otherwise the set's default effect.
122
+ * - `permit-overrides`: any applicable PERMIT wins; otherwise the first DENY;
123
+ * otherwise the default effect.
124
+ * - `first-applicable`: the effect of the first rule that matches; otherwise the
125
+ * default effect.
126
+ */
127
+ type CombiningAlgorithm = "deny-overrides" | "permit-overrides" | "first-applicable";
128
+ /** The resolved decision plus the rule (if any) that produced it. */
129
+ //#endregion
130
+ //#region src/core/abac/domain/policy-set.d.ts
131
+ /**
132
+ * An ordered collection of {@link AbacRule}s plus how to combine them
133
+ * ({@link CombiningAlgorithm}) and what to decide when no rule applies
134
+ * ({@link defaultEffect}). This is where ABAC goes beyond a single gate
135
+ * condition: several PERMIT/DENY rules resolved by an explicit algorithm.
136
+ *
137
+ * Closed by default — the combining algorithm defaults to `"deny-overrides"` and
138
+ * the default effect to `"DENY"`, so a request matching nothing is denied. A
139
+ * plain holder (not a value object); build through {@link of}.
140
+ */
141
+ declare class AbacPolicySet {
142
+ private readonly _rules;
143
+ private readonly _algorithm;
144
+ private readonly _defaultEffect;
145
+ private constructor();
146
+ static of(rules: readonly AbacRule[], options?: {
147
+ readonly algorithm?: CombiningAlgorithm;
148
+ readonly defaultEffect?: RuleEffect;
149
+ }): AbacPolicySet;
150
+ get rules(): readonly AbacRule[];
151
+ get algorithm(): CombiningAlgorithm;
152
+ get defaultEffect(): RuleEffect;
153
+ }
154
+ //#endregion
155
+ //#region src/core/abac/authorizer.d.ts
156
+ /** The successful decision returned by {@link AbacAuthorizer.authorize}. */
157
+ interface AbacDecision {
158
+ /** The audited business action that was allowed. */
159
+ readonly action: string;
160
+ readonly effect: "PERMIT";
161
+ /** The id of the rule that permitted, or `"<default>"` when the set's default did. */
162
+ readonly matchedRule: string;
163
+ readonly algorithm: CombiningAlgorithm;
164
+ /** When the decision was made, ISO-8601. */
165
+ readonly grantedAtIso: string;
166
+ }
167
+ /**
168
+ * The pure ABAC decisor. Given an {@link AbacRequest} and an
169
+ * {@link AbacPolicySet}, it flattens the request's attributes into a context,
170
+ * matches each rule's condition with the gate engine's pure condition evaluator
171
+ * (reused, not reimplemented), resolves the effects through the set's combining
172
+ * algorithm, and returns a {@link Result} — never throwing, mirroring the
173
+ * "errors as values" contract of `RbacAuthorizer`/`GateEngineV1`.
174
+ *
175
+ * A denial maps to {@link AuthorizationError.policyDenied}, attributed to the
176
+ * deciding rule's `id`/`version`; a condition-evaluation failure (a missing
177
+ * attribute, a wrong-typed operand) fails closed as
178
+ * {@link AuthorizationError.forbidden}. Loading the dynamic attributes is the
179
+ * consumer's job (behind an `AbacAuthorizerPort` adapter); this class only decides.
180
+ */
181
+ declare class AbacAuthorizer {
182
+ private readonly coreConfig;
183
+ constructor(params?: {
184
+ readonly coreConfig?: CoreConfig;
185
+ });
186
+ authorize(request: AbacRequest, policies: AbacPolicySet): Result<AbacDecision, AuthorizationError>;
187
+ }
188
+ //#endregion
189
+ //#region src/core/abac/composite-authorizer.d.ts
190
+ /**
191
+ * The request a {@link CompositeAuthorizer} accepts — one object that satisfies
192
+ * both the RBAC {@link AccessRequest} (its `required`/`scope`) and the ABAC
193
+ * request (its `attributes`).
194
+ */
195
+ type CompositeAccessRequest = AccessRequest & {
196
+ readonly attributes: AbacAttributes;
197
+ };
198
+ /**
199
+ * Runs a coarse RBAC check first, then refines with ABAC — the standard hybrid
200
+ * "does the actor hold the capability, *and* do the attributes allow it here?"
201
+ * flow. Short-circuits with the RBAC denial when the actor is not even capable,
202
+ * so the boundary sees the most specific 403.
203
+ *
204
+ * Only the two port interfaces are referenced (both erased at runtime), so this
205
+ * pulls in no RBAC/ABAC engine code of its own — it just sequences the seams the
206
+ * consumer wires up.
207
+ */
208
+ declare class CompositeAuthorizer {
209
+ private readonly rbac;
210
+ private readonly abac;
211
+ constructor(rbac: AuthorizerPort, abac: AbacAuthorizerPort);
212
+ authorize(request: CompositeAccessRequest): Promise<Result<void, AuthorizationError>>;
213
+ }
214
+ //#endregion
215
+ export { AbacPolicySet as a, AbacRuleProps as c, AbacAuthorizerPort as d, AbacAttributes as f, AbacDecision as i, RuleEffect as l, CompositeAuthorizer as n, CombiningAlgorithm as o, AbacRequest as p, AbacAuthorizer as r, AbacRule as s, CompositeAccessRequest as t, abacContext as u };
216
+ //# sourceMappingURL=composite-authorizer.d.ts.map