@brninpay/core 0.1.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 (98) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/LICENSE +203 -0
  3. package/README.md +7 -0
  4. package/dist/decision-resolver.d.ts +5 -0
  5. package/dist/decision-resolver.d.ts.map +1 -0
  6. package/dist/decision-resolver.js +20 -0
  7. package/dist/decision-resolver.js.map +1 -0
  8. package/dist/errors.d.ts +3 -0
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/errors.js +2 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/index.d.ts +10 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +8 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/policies/allowlist.d.ts +3 -0
  17. package/dist/policies/allowlist.d.ts.map +1 -0
  18. package/dist/policies/allowlist.js +21 -0
  19. package/dist/policies/allowlist.js.map +1 -0
  20. package/dist/policies/budget-check.d.ts +3 -0
  21. package/dist/policies/budget-check.d.ts.map +1 -0
  22. package/dist/policies/budget-check.js +22 -0
  23. package/dist/policies/budget-check.js.map +1 -0
  24. package/dist/policies/index.d.ts +5 -0
  25. package/dist/policies/index.d.ts.map +1 -0
  26. package/dist/policies/index.js +5 -0
  27. package/dist/policies/index.js.map +1 -0
  28. package/dist/policies/max-per-call.d.ts +3 -0
  29. package/dist/policies/max-per-call.d.ts.map +1 -0
  30. package/dist/policies/max-per-call.js +20 -0
  31. package/dist/policies/max-per-call.js.map +1 -0
  32. package/dist/policies/rate-limit.d.ts +3 -0
  33. package/dist/policies/rate-limit.d.ts.map +1 -0
  34. package/dist/policies/rate-limit.js +33 -0
  35. package/dist/policies/rate-limit.js.map +1 -0
  36. package/dist/policy-engine.d.ts +8 -0
  37. package/dist/policy-engine.d.ts.map +1 -0
  38. package/dist/policy-engine.js +39 -0
  39. package/dist/policy-engine.js.map +1 -0
  40. package/dist/types/errors.d.ts +80 -0
  41. package/dist/types/errors.d.ts.map +1 -0
  42. package/dist/types/errors.js +150 -0
  43. package/dist/types/errors.js.map +1 -0
  44. package/dist/types/payment.d.ts +55 -0
  45. package/dist/types/payment.d.ts.map +1 -0
  46. package/dist/types/payment.js +188 -0
  47. package/dist/types/payment.js.map +1 -0
  48. package/dist/types/policy.d.ts +58 -0
  49. package/dist/types/policy.d.ts.map +1 -0
  50. package/dist/types/policy.js +194 -0
  51. package/dist/types/policy.js.map +1 -0
  52. package/dist/types/wallet.d.ts +66 -0
  53. package/dist/types/wallet.d.ts.map +1 -0
  54. package/dist/types/wallet.js +13 -0
  55. package/dist/types/wallet.js.map +1 -0
  56. package/dist/types.d.ts +117 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +4 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/utils/crypto.d.ts +25 -0
  61. package/dist/utils/crypto.d.ts.map +1 -0
  62. package/dist/utils/crypto.js +119 -0
  63. package/dist/utils/crypto.js.map +1 -0
  64. package/dist/validation/schemas.d.ts +598 -0
  65. package/dist/validation/schemas.d.ts.map +1 -0
  66. package/dist/validation/schemas.js +178 -0
  67. package/dist/validation/schemas.js.map +1 -0
  68. package/package.json +30 -0
  69. package/src/decision-resolver.ts +16 -0
  70. package/src/errors.ts +20 -0
  71. package/src/index.ts +139 -0
  72. package/src/policies/allowlist.ts +26 -0
  73. package/src/policies/budget-check.ts +26 -0
  74. package/src/policies/index.ts +4 -0
  75. package/src/policies/max-per-call.ts +25 -0
  76. package/src/policies/rate-limit.ts +40 -0
  77. package/src/policy-engine.ts +65 -0
  78. package/src/types/errors.ts +215 -0
  79. package/src/types/payment.ts +297 -0
  80. package/src/types/policy.ts +289 -0
  81. package/src/types/wallet.ts +84 -0
  82. package/src/types.ts +193 -0
  83. package/src/utils/crypto.ts +182 -0
  84. package/src/validation/schemas.ts +208 -0
  85. package/test/architecture/architecture-invariants.test.ts +95 -0
  86. package/test/authorization-pipeline.test.ts +117 -0
  87. package/test/crypto.test.ts +56 -0
  88. package/test/decision-resolver.test.ts +52 -0
  89. package/test/errors.test.ts +97 -0
  90. package/test/payment-types.test.ts +49 -0
  91. package/test/policies.test.ts +162 -0
  92. package/test/policy-engine.test.ts +96 -0
  93. package/test/policy-types.test.ts +93 -0
  94. package/test/property/auth-invariants.test.ts +110 -0
  95. package/test/property/policy-invariants.test.ts +241 -0
  96. package/test/schemas.test.ts +58 -0
  97. package/tsconfig.json +9 -0
  98. package/vitest.config.ts +8 -0
@@ -0,0 +1,178 @@
1
+ import { z } from 'zod';
2
+ import { PAYMENT_INTENT_STATUSES } from '../types/payment.js';
3
+ export const AddressSchema = z
4
+ .string({ required_error: 'Address is required' })
5
+ .regex(/^0x[a-fA-F0-9]{40}$/, 'Address must be a valid 20-byte hex string');
6
+ export const HexSchema = z
7
+ .string({ required_error: 'Hex value is required' })
8
+ .regex(/^0x(?:[a-fA-F0-9]{2})*$/, 'Value must be a valid even-length hex string');
9
+ export const TimestampSchema = z
10
+ .string({ required_error: 'Timestamp is required' })
11
+ .datetime({ offset: true, message: 'Timestamp must be a valid ISO-8601 string' });
12
+ export const NonNegativeBigIntSchema = z
13
+ .union([
14
+ z.bigint(),
15
+ z.number().int().nonnegative(),
16
+ z
17
+ .string()
18
+ .regex(/^\d+$/, 'Amount must contain only digits'),
19
+ ])
20
+ .transform((value) => BigInt(value));
21
+ export const PositiveBigIntSchema = NonNegativeBigIntSchema.refine((value) => value > 0n, 'Amount must be greater than zero');
22
+ export const PaymentAmountSchema = z.object({
23
+ currency: z.literal('USDC'),
24
+ value: PositiveBigIntSchema,
25
+ decimals: z
26
+ .number({ required_error: 'Amount decimals are required' })
27
+ .int('Amount decimals must be an integer')
28
+ .min(0, 'Amount decimals cannot be negative')
29
+ .max(18, 'Amount decimals cannot exceed 18'),
30
+ });
31
+ export const PaymentRecipientSchema = z.object({
32
+ address: AddressSchema,
33
+ chainId: z
34
+ .number({ required_error: 'Recipient chainId is required' })
35
+ .int('Recipient chainId must be an integer')
36
+ .positive('Recipient chainId must be positive'),
37
+ ensName: z.string().trim().min(1).nullable().optional(),
38
+ });
39
+ export const PaymentIntentFailureSchema = z.object({
40
+ code: z.string().trim().min(1, 'Failure code is required'),
41
+ message: z.string().trim().min(1, 'Failure message is required'),
42
+ retryable: z.boolean(),
43
+ detail: z.record(z.unknown()).optional(),
44
+ });
45
+ export const PaymentIntentSchema = z.object({
46
+ id: z.string().trim().min(1, 'Payment intent id is required'),
47
+ workspaceId: z.string().trim().min(1, 'workspaceId is required'),
48
+ actorId: z.string().trim().min(1, 'actorId is required'),
49
+ status: z.enum(PAYMENT_INTENT_STATUSES),
50
+ amount: PaymentAmountSchema,
51
+ recipient: PaymentRecipientSchema,
52
+ createdAt: TimestampSchema,
53
+ updatedAt: TimestampSchema,
54
+ expiresAt: TimestampSchema.nullable().optional(),
55
+ idempotencyKey: z.string().trim().min(1).nullable().optional(),
56
+ description: z.string().trim().min(1).nullable().optional(),
57
+ reference: z.string().trim().min(1).nullable().optional(),
58
+ transactionHash: HexSchema.nullable().optional(),
59
+ failure: PaymentIntentFailureSchema.nullable().optional(),
60
+ metadata: z.record(z.unknown()).optional(),
61
+ });
62
+ export const BudgetPolicySchema = z.object({
63
+ id: z.string().trim().min(1, 'Policy id is required'),
64
+ name: z.string().trim().min(1, 'Policy name is required'),
65
+ enabled: z.boolean(),
66
+ kind: z.literal('budget'),
67
+ currency: z.literal('USDC'),
68
+ limit: PositiveBigIntSchema,
69
+ softLimit: PositiveBigIntSchema.optional(),
70
+ });
71
+ export const AllowlistPolicySchema = z.object({
72
+ id: z.string().trim().min(1, 'Policy id is required'),
73
+ name: z.string().trim().min(1, 'Policy name is required'),
74
+ enabled: z.boolean(),
75
+ kind: z.literal('allowlist'),
76
+ addresses: z.array(AddressSchema).min(1, 'Allowlist must contain at least one address'),
77
+ chains: z.array(z.number().int().positive()).optional(),
78
+ });
79
+ export const RateLimitPolicySchema = z.object({
80
+ id: z.string().trim().min(1, 'Policy id is required'),
81
+ name: z.string().trim().min(1, 'Policy name is required'),
82
+ enabled: z.boolean(),
83
+ kind: z.literal('rate_limit'),
84
+ maxRequests: z.number().int().positive('maxRequests must be positive'),
85
+ intervalMs: z.number().int().positive('intervalMs must be positive'),
86
+ burst: z.number().int().positive('burst must be positive').optional(),
87
+ scope: z.enum(['actor', 'workspace', 'ip']),
88
+ });
89
+ export const PolicyDefinitionSchema = z.discriminatedUnion('kind', [
90
+ BudgetPolicySchema,
91
+ AllowlistPolicySchema,
92
+ RateLimitPolicySchema,
93
+ ]);
94
+ export const PolicyEvaluationContextSchema = z.object({
95
+ actorId: z.string().trim().min(1, 'actorId is required'),
96
+ workspaceId: z.string().trim().min(1, 'workspaceId is required'),
97
+ recipient: AddressSchema,
98
+ chainId: z.number().int().positive('chainId must be positive'),
99
+ amount: PositiveBigIntSchema,
100
+ currency: z.literal('USDC'),
101
+ timestampMs: z.number().int().nonnegative('timestampMs must be non-negative'),
102
+ spentAmount: NonNegativeBigIntSchema,
103
+ requestCount: z.number().int().nonnegative('requestCount must be non-negative'),
104
+ sourceIp: z.string().trim().min(1).optional(),
105
+ });
106
+ export const SmartWalletSchema = z.object({
107
+ address: AddressSchema,
108
+ ownerAddress: AddressSchema,
109
+ chainId: z.number().int().positive('chainId must be positive'),
110
+ provider: z.enum(['safe', 'kernel', 'circle']),
111
+ entryPointAddress: AddressSchema,
112
+ entryPointVersion: z.enum(['v0.6', 'v0.7']),
113
+ factoryAddress: AddressSchema.optional(),
114
+ deployed: z.boolean(),
115
+ });
116
+ export const UserOperationCallSchema = z.object({
117
+ to: AddressSchema,
118
+ value: NonNegativeBigIntSchema,
119
+ data: HexSchema,
120
+ });
121
+ export const GasEstimateSchema = z.object({
122
+ callGasLimit: NonNegativeBigIntSchema,
123
+ verificationGasLimit: NonNegativeBigIntSchema,
124
+ preVerificationGas: NonNegativeBigIntSchema,
125
+ maxFeePerGas: NonNegativeBigIntSchema,
126
+ maxPriorityFeePerGas: NonNegativeBigIntSchema,
127
+ paymasterVerificationGasLimit: NonNegativeBigIntSchema.optional(),
128
+ paymasterPostOpGasLimit: NonNegativeBigIntSchema.optional(),
129
+ });
130
+ export const UserOperationV06Schema = z.object({
131
+ version: z.literal('v0.6'),
132
+ sender: AddressSchema,
133
+ nonce: NonNegativeBigIntSchema,
134
+ initCode: HexSchema,
135
+ callData: HexSchema,
136
+ callGasLimit: NonNegativeBigIntSchema,
137
+ verificationGasLimit: NonNegativeBigIntSchema,
138
+ preVerificationGas: NonNegativeBigIntSchema,
139
+ maxFeePerGas: NonNegativeBigIntSchema,
140
+ maxPriorityFeePerGas: NonNegativeBigIntSchema,
141
+ paymasterAndData: HexSchema,
142
+ signature: HexSchema,
143
+ });
144
+ export const UserOperationV07Schema = z.object({
145
+ version: z.literal('v0.7'),
146
+ sender: AddressSchema,
147
+ nonce: NonNegativeBigIntSchema,
148
+ factory: z.union([AddressSchema, HexSchema]),
149
+ factoryData: HexSchema,
150
+ callData: HexSchema,
151
+ callGasLimit: NonNegativeBigIntSchema,
152
+ verificationGasLimit: NonNegativeBigIntSchema,
153
+ preVerificationGas: NonNegativeBigIntSchema,
154
+ maxFeePerGas: NonNegativeBigIntSchema,
155
+ maxPriorityFeePerGas: NonNegativeBigIntSchema,
156
+ paymaster: z.union([AddressSchema, HexSchema]),
157
+ paymasterVerificationGasLimit: NonNegativeBigIntSchema,
158
+ paymasterPostOpGasLimit: NonNegativeBigIntSchema,
159
+ paymasterData: HexSchema,
160
+ signature: HexSchema,
161
+ });
162
+ export const UserOperationSchema = z.discriminatedUnion('version', [
163
+ UserOperationV06Schema,
164
+ UserOperationV07Schema,
165
+ ]);
166
+ export function parseAddress(value) {
167
+ return AddressSchema.parse(value);
168
+ }
169
+ export function parsePaymentIntent(value) {
170
+ return PaymentIntentSchema.parse(value);
171
+ }
172
+ export function parsePolicyDefinition(value) {
173
+ return PolicyDefinitionSchema.parse(value);
174
+ }
175
+ export function parseUserOperation(value) {
176
+ return UserOperationSchema.parse(value);
177
+ }
178
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/validation/schemas.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@brninpay/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "dependencies": {
14
+ "zod": "^3.25.76"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^25.9.1",
18
+ "fast-check": "^4.8.0",
19
+ "rimraf": "^6.0.0",
20
+ "typescript": "^5.5.0",
21
+ "vitest": "^2.0.0"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "typecheck": "tsc --noEmit"
29
+ }
30
+ }
@@ -0,0 +1,16 @@
1
+ import type { PolicyResult, AuthorizationDecision } from './types.js'
2
+
3
+ export class DecisionResolver {
4
+ static resolve(results: PolicyResult[]): AuthorizationDecision {
5
+ if (results.length === 0) return 'denied'
6
+ let hasDeny = false
7
+ let hasReview = false
8
+ for (const r of results) {
9
+ if (r.decision === 'deny') hasDeny = true
10
+ else if (r.decision === 'review') hasReview = true
11
+ }
12
+ if (hasDeny) return 'denied'
13
+ if (hasReview) return 'pending-review'
14
+ return 'approved'
15
+ }
16
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,20 @@
1
+ export {
2
+ ErrorCode,
3
+ RuntimeeError,
4
+ ValidationError,
5
+ AuthorizationError,
6
+ BudgetExhaustedError,
7
+ AllowlistDeniedError,
8
+ RateLimitExceededError,
9
+ AuthorizationExpiredError,
10
+ ExecutionError,
11
+ NetworkError,
12
+ GasEstimationError,
13
+ BroadcastError,
14
+ ReorgDetectedError,
15
+ HmacVerificationError,
16
+ isRuntimeeError,
17
+ serializeError,
18
+ } from './types/errors.js'
19
+
20
+ export type { ErrorCodeMessage, SerializedError } from './types/errors.js'
package/src/index.ts ADDED
@@ -0,0 +1,139 @@
1
+ export type {
2
+ Actor,
3
+ Budget,
4
+ Policy,
5
+ SettlementDescriptor,
6
+ Target,
7
+ Purpose,
8
+ Intent,
9
+ BudgetState,
10
+ PolicyResult,
11
+ AuthorizationDecision,
12
+ Authorization,
13
+ SettlementHint,
14
+ ExecutionPlan,
15
+ PolicyContext,
16
+ PolicyMiddleware,
17
+ PolicyEvaluator,
18
+ SimulationResult,
19
+ SignedTransaction,
20
+ Receipt,
21
+ PaymentIntentStatus,
22
+ PaymentAmount,
23
+ PaymentRecipient,
24
+ PaymentIntentFailure,
25
+ PaymentMetadataValue,
26
+ PaymentIntent,
27
+ SerializedBigInt,
28
+ PolicyDecision,
29
+ PolicyReason,
30
+ BasePolicy,
31
+ BudgetPolicy,
32
+ AllowlistPolicy,
33
+ RateLimitPolicy,
34
+ PolicyDefinition,
35
+ PolicyEvaluationContext,
36
+ PolicyEvaluationResult,
37
+ AggregatedPolicyDecision,
38
+ Hex,
39
+ Address,
40
+ SmartWalletProvider,
41
+ EntryPointVersion,
42
+ SmartWallet,
43
+ UserOperationCall,
44
+ GasEstimate,
45
+ UserOperationV06,
46
+ UserOperationV07,
47
+ UserOperation,
48
+ } from './types.js'
49
+
50
+ export {
51
+ AuthorizationError,
52
+ BudgetExhaustedError,
53
+ AllowlistDeniedError,
54
+ RateLimitExceededError,
55
+ AuthorizationExpiredError,
56
+ RuntimeeError,
57
+ ValidationError,
58
+ ExecutionError,
59
+ NetworkError,
60
+ GasEstimationError,
61
+ BroadcastError,
62
+ ReorgDetectedError,
63
+ HmacVerificationError,
64
+ isRuntimeeError,
65
+ serializeError,
66
+ ErrorCode,
67
+ } from './errors.js'
68
+ export type { ErrorCodeMessage, SerializedError } from './errors.js'
69
+
70
+ export {
71
+ PAYMENT_INTENT_STATUSES,
72
+ PAYMENT_INTENT_TERMINAL_STATUSES,
73
+ PAYMENT_INTENT_TRANSITIONS,
74
+ validatePaymentAmount,
75
+ isPaymentIntentTerminalStatus,
76
+ canTransitionPaymentIntentStatus,
77
+ assertValidPaymentIntentTransition,
78
+ assertPaymentIntent,
79
+ serializeBigInts,
80
+ deserializeBigInts,
81
+ serializePaymentIntent,
82
+ deserializePaymentIntent,
83
+ POLICY_DECISIONS,
84
+ evaluatePolicy,
85
+ aggregatePolicyDecisions,
86
+ evaluatePolicies,
87
+ isUserOperationV06,
88
+ isUserOperationV07,
89
+ getUserOperationSender,
90
+ getUserOperationNonce,
91
+ } from './types.js'
92
+
93
+ export { DecisionResolver } from './decision-resolver.js'
94
+
95
+ export { PolicyEngine } from './policy-engine.js'
96
+
97
+ export {
98
+ createBudgetCheckPolicy,
99
+ createAllowlistPolicy,
100
+ createMaxPerCallPolicy,
101
+ createRateLimitPolicy,
102
+ } from './policies/index.js'
103
+
104
+ export {
105
+ AddressSchema,
106
+ HexSchema,
107
+ TimestampSchema,
108
+ NonNegativeBigIntSchema,
109
+ PositiveBigIntSchema,
110
+ PaymentAmountSchema,
111
+ PaymentRecipientSchema,
112
+ PaymentIntentFailureSchema,
113
+ PaymentIntentSchema,
114
+ BudgetPolicySchema,
115
+ AllowlistPolicySchema,
116
+ RateLimitPolicySchema,
117
+ PolicyDefinitionSchema,
118
+ PolicyEvaluationContextSchema,
119
+ SmartWalletSchema,
120
+ UserOperationCallSchema,
121
+ GasEstimateSchema,
122
+ UserOperationV06Schema,
123
+ UserOperationV07Schema,
124
+ UserOperationSchema,
125
+ parseAddress,
126
+ parsePaymentIntent,
127
+ parsePolicyDefinition,
128
+ parseUserOperation,
129
+ } from './validation/schemas.js'
130
+
131
+ export {
132
+ sha256Hex,
133
+ signHmacSha256,
134
+ verifyHmacSha256,
135
+ assertValidHmac,
136
+ generateNonce,
137
+ validateTimestamp,
138
+ assertValidTimestamp,
139
+ } from './utils/crypto.js'
@@ -0,0 +1,26 @@
1
+ import type { PolicyContext, PolicyMiddleware, PolicyEvaluator } from '../types.js'
2
+
3
+ export function createAllowlistPolicy(
4
+ id: string,
5
+ version: string,
6
+ allowedTargets: string[]
7
+ ): PolicyEvaluator {
8
+ const set = new Set(allowedTargets)
9
+ const middleware: PolicyMiddleware = async (ctx, next) => {
10
+ if (!set.has(ctx.intent.target)) {
11
+ return {
12
+ policyId: id,
13
+ policyType: 'allowlist',
14
+ version,
15
+ decision: 'deny',
16
+ reason: {
17
+ code: 'allowlist_denied',
18
+ message: `Target "${ctx.intent.target}" is not in the allowlist`,
19
+ },
20
+ evaluatedAt: new Date().toISOString(),
21
+ }
22
+ }
23
+ return next()
24
+ }
25
+ return { id, type: 'allowlist', version, middleware }
26
+ }
@@ -0,0 +1,26 @@
1
+ import type { PolicyContext, PolicyMiddleware, PolicyEvaluator } from '../types.js'
2
+
3
+ export function createBudgetCheckPolicy(
4
+ id: string,
5
+ version: string
6
+ ): PolicyEvaluator {
7
+ const middleware: PolicyMiddleware = async (ctx, next) => {
8
+ const { intent, budget } = ctx
9
+ const projected = budget.used + intent.amount
10
+ if (projected > budget.total) {
11
+ return {
12
+ policyId: id,
13
+ policyType: 'budget-check',
14
+ version,
15
+ decision: 'deny',
16
+ reason: {
17
+ code: 'budget_exhausted',
18
+ message: `Budget exhausted: ${budget.used + intent.amount} would exceed ${budget.total} (${budget.period})`,
19
+ },
20
+ evaluatedAt: new Date().toISOString(),
21
+ }
22
+ }
23
+ return next()
24
+ }
25
+ return { id, type: 'budget-check', version, middleware }
26
+ }
@@ -0,0 +1,4 @@
1
+ export { createBudgetCheckPolicy } from './budget-check.js'
2
+ export { createAllowlistPolicy } from './allowlist.js'
3
+ export { createMaxPerCallPolicy } from './max-per-call.js'
4
+ export { createRateLimitPolicy } from './rate-limit.js'
@@ -0,0 +1,25 @@
1
+ import type { PolicyContext, PolicyMiddleware, PolicyEvaluator } from '../types.js'
2
+
3
+ export function createMaxPerCallPolicy(
4
+ id: string,
5
+ version: string,
6
+ maxAmount: bigint
7
+ ): PolicyEvaluator {
8
+ const middleware: PolicyMiddleware = async (ctx, next) => {
9
+ if (ctx.intent.amount > maxAmount) {
10
+ return {
11
+ policyId: id,
12
+ policyType: 'max-per-call',
13
+ version,
14
+ decision: 'deny',
15
+ reason: {
16
+ code: 'max_per_call_exceeded',
17
+ message: `Payment amount ${ctx.intent.amount} exceeds max-per-call limit of ${maxAmount}`,
18
+ },
19
+ evaluatedAt: new Date().toISOString(),
20
+ }
21
+ }
22
+ return next()
23
+ }
24
+ return { id, type: 'max-per-call', version, middleware }
25
+ }
@@ -0,0 +1,40 @@
1
+ import type { PolicyMiddleware, PolicyEvaluator } from '../types.js'
2
+
3
+ export function createRateLimitPolicy(
4
+ id: string,
5
+ version: string,
6
+ maxCalls: number,
7
+ windowMs: number,
8
+ store: Map<string, number[]> = new Map()
9
+ ): PolicyEvaluator {
10
+ const middleware: PolicyMiddleware = async (ctx, _next) => {
11
+ const actorKey = ctx.actor.id
12
+ const now = Date.now()
13
+ const timestamps = store.get(actorKey) || []
14
+ const recent = timestamps.filter((t) => now - t < windowMs)
15
+ if (recent.length >= maxCalls) {
16
+ return {
17
+ policyId: id,
18
+ policyType: 'rate-limit',
19
+ version,
20
+ decision: 'deny',
21
+ reason: {
22
+ code: 'rate_limit_exceeded',
23
+ message: `Rate limit exceeded: ${recent.length} calls in window (max ${maxCalls})`,
24
+ },
25
+ evaluatedAt: new Date().toISOString(),
26
+ }
27
+ }
28
+ recent.push(now)
29
+ store.set(actorKey, recent)
30
+ return {
31
+ policyId: id,
32
+ policyType: 'rate-limit',
33
+ version,
34
+ decision: 'pass',
35
+ reason: { code: 'rate_limit_ok', message: 'Rate limit not exceeded' },
36
+ evaluatedAt: new Date().toISOString(),
37
+ }
38
+ }
39
+ return { id, type: 'rate-limit', version, middleware }
40
+ }
@@ -0,0 +1,65 @@
1
+ import type { Actor, Intent, BudgetState, PolicyEvaluator, Authorization } from './types.js'
2
+ import { DecisionResolver } from './decision-resolver.js'
3
+
4
+ export class PolicyEngine {
5
+ private readonly evaluators: PolicyEvaluator[]
6
+
7
+ constructor(evaluators: PolicyEvaluator[]) {
8
+ this.evaluators = evaluators
9
+ }
10
+
11
+ async evaluate(
12
+ actor: Actor,
13
+ intent: Intent,
14
+ budget: BudgetState
15
+ ): Promise<Authorization> {
16
+ const evaluatedAt = new Date().toISOString()
17
+ const policyResults = await Promise.all(
18
+ this.evaluators.map((evaluator) =>
19
+ this.runEvaluator(evaluator, actor, intent, budget)
20
+ )
21
+ )
22
+ const decision = policyResults.length === 0
23
+ ? 'approved' as const
24
+ : DecisionResolver.resolve(policyResults)
25
+ return {
26
+ intent,
27
+ policyResults,
28
+ decision,
29
+ evaluatedAt,
30
+ actorVersion: '1',
31
+ }
32
+ }
33
+
34
+ private async runEvaluator(
35
+ evaluator: PolicyEvaluator,
36
+ actor: Actor,
37
+ intent: Intent,
38
+ budget: BudgetState
39
+ ): Promise<{
40
+ policyId: string
41
+ policyType: string
42
+ version: string
43
+ decision: 'pass' | 'deny' | 'review'
44
+ reason: { code: string; message: string }
45
+ evaluatedAt: string
46
+ }> {
47
+ const ctx = {
48
+ actor,
49
+ intent,
50
+ budget,
51
+ evaluatedPolicies: [],
52
+ }
53
+
54
+ const finalNext = async () => ({
55
+ policyId: evaluator.id,
56
+ policyType: evaluator.type,
57
+ version: evaluator.version,
58
+ decision: 'pass' as const,
59
+ reason: { code: 'noop', message: 'No policy applied' },
60
+ evaluatedAt: new Date().toISOString(),
61
+ })
62
+
63
+ return evaluator.middleware(ctx, finalNext)
64
+ }
65
+ }