@bernierllc/nevar-types 0.0.1 → 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.
package/.eslintrc.js ADDED
@@ -0,0 +1,29 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ module.exports = {
10
+ parser: '@typescript-eslint/parser',
11
+ parserOptions: {
12
+ ecmaVersion: 2022,
13
+ sourceType: 'module'
14
+ },
15
+ extends: [
16
+ 'eslint:recommended',
17
+ 'plugin:@typescript-eslint/recommended'
18
+ ],
19
+ plugins: ['@typescript-eslint'],
20
+ env: {
21
+ node: true,
22
+ jest: true
23
+ },
24
+ rules: {
25
+ '@typescript-eslint/explicit-function-return-type': 'warn',
26
+ '@typescript-eslint/no-explicit-any': 'off',
27
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }]
28
+ }
29
+ };
package/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2025 Bernier LLC
2
+
3
+ This software is licensed to the client under a limited-use license.
4
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
5
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
package/README.md CHANGED
@@ -1,45 +1,130 @@
1
1
  # @bernierllc/nevar-types
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@bernierllc/nevar-types`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**
3
+ Shared type definitions, interfaces, error classes, and type guards for the Nevar rules engine suite.
4
+
5
+ ## Overview
6
+
7
+ This package provides the foundational types that all Nevar packages depend on. It has zero runtime dependencies — it exports only TypeScript types, interfaces, error classes, and type guard functions.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @bernierllc/nevar-types
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import type {
19
+ Rule,
20
+ ConditionGroup,
21
+ ActionDefinition,
22
+ NevarConfig,
23
+ EvaluationResult,
24
+ NevarStorageAdapter,
25
+ } from '@bernierllc/nevar-types';
26
+
27
+ import {
28
+ isConditionGroup,
29
+ isCondition,
30
+ NevarError,
31
+ NevarEvaluationError,
32
+ } from '@bernierllc/nevar-types';
33
+
34
+ // Type guard usage
35
+ const node: ConditionGroup | Condition = getNode();
36
+ if (isConditionGroup(node)) {
37
+ console.log(node.operator, node.conditions);
38
+ } else if (isCondition(node)) {
39
+ console.log(node.field, node.op, node.value);
40
+ }
41
+
42
+ // Error handling with cause chain
43
+ try {
44
+ evaluateRule(rule, context);
45
+ } catch (error) {
46
+ if (error instanceof NevarEvaluationError) {
47
+ console.log(error.code, error.context, error.cause);
48
+ }
49
+ }
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### Types and Interfaces
55
+
56
+ - **`Rule`** - Complete rule definition with trigger type, condition tree, actions, and execution settings
57
+ - **`RuleGroup`** - Named collection of rules
58
+ - **`ConditionGroup`** - Tree node with `operator` (AND/OR/NOT) and nested `conditions`
59
+ - **`Condition`** - Leaf node with `field`, `op`, and `value`
60
+ - **`ActionDefinition`** - Action type, config, retry settings, and execution order
61
+ - **`ActionIntent`** - Resolved action ready for execution, tied to a specific rule
62
+ - **`ActionResult`** - Outcome of executing an action intent
63
+ - **`TriggerDefinition`** - Trigger type definition with payload schema
64
+ - **`OperatorDefinition`** - Operator with evaluate function, label, category, and field types
65
+ - **`NevarStorageAdapter`** - Storage interface for rules, groups, and execution logs
66
+ - **`NevarConfig`** - Full engine configuration including circuit breaker, loop, and audit settings
67
+ - **`EvaluationResult`** - Match result with evaluation trace
68
+ - **`LoopHandle`** - Runtime loop state with heartbeat tracking
69
+
70
+ ### Error Classes
71
+
72
+ All errors extend `NevarError` and support ES2022 `Error.cause` chaining via `NevarErrorOptions`:
73
+
74
+ - **`NevarError`** - Base error with `code: string` and `context?: Record<string, unknown>`
75
+ - **`NevarTriggerError`** - Trigger validation and dispatch failures
76
+ - **`NevarEvaluationError`** - Condition evaluation failures (unknown operators, provider errors)
77
+ - **`NevarActionError`** - Action execution failures
78
+ - **`NevarCircuitBreakerError`** - Circuit breaker limit violations
79
+ - **`NevarLoopError`** - Loop detection and control failures
80
+ - **`NevarStorageError`** - Storage adapter failures
81
+ - **`NevarValidationError`** - Schema and configuration validation failures
82
+
83
+ ### Type Guards
84
+
85
+ - **`isConditionGroup(value)`** - Returns `true` if value has `operator` and `conditions` fields
86
+ - **`isCondition(value)`** - Returns `true` if value has `field`, `op`, and `value` fields
87
+
88
+ ## Exports
89
+
90
+ ### Types & Interfaces
91
+
92
+ - **Models**: `Rule`, `RuleGroup`, `LogLevel`, `EvaluationTrace`
93
+ - **Conditions**: `ConditionGroup`, `Condition`
94
+ - **Actions**: `ActionDefinition`, `ActionRetryConfig`, `ActionIntent`, `ActionResult`, `MatchedRule`, `DeferredTrigger`, `ActionFailureMode`
95
+ - **Triggers**: `TriggerDefinition`
96
+ - **Context**: `ContextProviderDefinition`
97
+ - **Operators**: `OperatorDefinition`, `OperatorInfo`
98
+ - **Storage**: `NevarStorageAdapter`, `GroupFilters`, `RuleFilters`, `LogFilters`, `PaginatedResult`, `ExecutionLogEntry`
99
+ - **Config**: `NevarConfig`, `CircuitBreakerConfig`, `LoopConfig`, `AuditLoggerConfig`, `SeedStrategy`
100
+ - **Results**: `EmitResult`, `PreviewResult`, `EvaluationResult`, `LoopHandle`, `LoopAnalysis`, `DetectedLoop`
101
+
102
+ ### Error Classes
103
+
104
+ All errors extend `NevarError` and follow the ES2022 `Error.cause` pattern:
105
+
106
+ - `NevarError` (base)
107
+ - `NevarTriggerError`
108
+ - `NevarEvaluationError`
109
+ - `NevarActionError`
110
+ - `NevarCircuitBreakerError`
111
+ - `NevarLoopError`
112
+ - `NevarStorageError`
113
+ - `NevarValidationError`
114
+
115
+ ### Type Guards
116
+
117
+ - `isConditionGroup(value)` — checks for `operator` and `conditions` fields
118
+ - `isCondition(value)` — checks for `field`, `op`, `value` fields
119
+
120
+ ## Integration Documentation
121
+
122
+ ### Logger Integration
123
+ This package does not integrate with `@bernierllc/logger`. As a core package, logger integration is optional and not included by default. Consumers should handle logging at the service layer.
124
+
125
+ ### NeverHub Integration
126
+ This package does not integrate with `@bernierllc/neverhub-adapter`. As a core package, NeverHub integration is not applicable. NeverHub registration should be handled by service-layer packages that compose this package.
127
+
128
+ ## License
129
+
130
+ Copyright (c) 2025 Bernier LLC. All rights reserved.
@@ -0,0 +1,189 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import {
10
+ NevarError,
11
+ NevarTriggerError,
12
+ NevarEvaluationError,
13
+ NevarActionError,
14
+ NevarCircuitBreakerError,
15
+ NevarLoopError,
16
+ NevarStorageError,
17
+ NevarValidationError,
18
+ } from '../src/errors';
19
+
20
+ describe('NevarError', () => {
21
+ it('should create with message only', () => {
22
+ const error = new NevarError('something went wrong');
23
+ expect(error.message).toBe('something went wrong');
24
+ expect(error.code).toBe('NEVAR_ERROR');
25
+ expect(error.name).toBe('NevarError');
26
+ expect(error.context).toBeUndefined();
27
+ expect(error.cause).toBeUndefined();
28
+ });
29
+
30
+ it('should create with custom code and context', () => {
31
+ const error = new NevarError('fail', {
32
+ code: 'CUSTOM_CODE',
33
+ context: { ruleId: 'r-1' },
34
+ });
35
+ expect(error.code).toBe('CUSTOM_CODE');
36
+ expect(error.context).toEqual({ ruleId: 'r-1' });
37
+ });
38
+
39
+ it('should chain cause using ES2022 Error.cause', () => {
40
+ const cause = new Error('root cause');
41
+ const error = new NevarError('wrapper', { cause });
42
+ expect(error.cause).toBe(cause);
43
+ });
44
+
45
+ it('should be instanceof Error', () => {
46
+ const error = new NevarError('test');
47
+ expect(error).toBeInstanceOf(Error);
48
+ expect(error).toBeInstanceOf(NevarError);
49
+ });
50
+ });
51
+
52
+ describe('NevarTriggerError', () => {
53
+ it('should have correct defaults', () => {
54
+ const error = new NevarTriggerError('trigger failed');
55
+ expect(error.name).toBe('NevarTriggerError');
56
+ expect(error.code).toBe('NEVAR_TRIGGER_ERROR');
57
+ expect(error.message).toBe('trigger failed');
58
+ });
59
+
60
+ it('should be instanceof NevarError and Error', () => {
61
+ const error = new NevarTriggerError('test');
62
+ expect(error).toBeInstanceOf(NevarError);
63
+ expect(error).toBeInstanceOf(Error);
64
+ });
65
+
66
+ it('should support cause chaining', () => {
67
+ const cause = new Error('underlying');
68
+ const error = new NevarTriggerError('trigger failed', { cause });
69
+ expect(error.cause).toBe(cause);
70
+ });
71
+
72
+ it('should support custom code override', () => {
73
+ const error = new NevarTriggerError('fail', { code: 'CUSTOM' });
74
+ expect(error.code).toBe('CUSTOM');
75
+ });
76
+
77
+ it('should support context', () => {
78
+ const error = new NevarTriggerError('fail', { context: { triggerKey: 'order.created' } });
79
+ expect(error.context).toEqual({ triggerKey: 'order.created' });
80
+ });
81
+ });
82
+
83
+ describe('NevarEvaluationError', () => {
84
+ it('should have correct defaults', () => {
85
+ const error = new NevarEvaluationError('eval failed');
86
+ expect(error.name).toBe('NevarEvaluationError');
87
+ expect(error.code).toBe('NEVAR_EVALUATION_ERROR');
88
+ });
89
+
90
+ it('should be instanceof NevarError', () => {
91
+ expect(new NevarEvaluationError('test')).toBeInstanceOf(NevarError);
92
+ });
93
+
94
+ it('should chain cause', () => {
95
+ const cause = new Error('root');
96
+ const error = new NevarEvaluationError('fail', { cause, context: { ruleId: 'r-1' } });
97
+ expect(error.cause).toBe(cause);
98
+ expect(error.context).toEqual({ ruleId: 'r-1' });
99
+ });
100
+ });
101
+
102
+ describe('NevarActionError', () => {
103
+ it('should have correct defaults', () => {
104
+ const error = new NevarActionError('action failed');
105
+ expect(error.name).toBe('NevarActionError');
106
+ expect(error.code).toBe('NEVAR_ACTION_ERROR');
107
+ });
108
+
109
+ it('should be instanceof NevarError', () => {
110
+ expect(new NevarActionError('test')).toBeInstanceOf(NevarError);
111
+ });
112
+
113
+ it('should support all options', () => {
114
+ const cause = new Error('http timeout');
115
+ const error = new NevarActionError('send email failed', {
116
+ cause,
117
+ code: 'ACTION_TIMEOUT',
118
+ context: { actionType: 'send-email' },
119
+ });
120
+ expect(error.cause).toBe(cause);
121
+ expect(error.code).toBe('ACTION_TIMEOUT');
122
+ expect(error.context).toEqual({ actionType: 'send-email' });
123
+ });
124
+ });
125
+
126
+ describe('NevarCircuitBreakerError', () => {
127
+ it('should have correct defaults', () => {
128
+ const error = new NevarCircuitBreakerError('circuit open');
129
+ expect(error.name).toBe('NevarCircuitBreakerError');
130
+ expect(error.code).toBe('NEVAR_CIRCUIT_BREAKER_ERROR');
131
+ });
132
+
133
+ it('should be instanceof NevarError', () => {
134
+ expect(new NevarCircuitBreakerError('test')).toBeInstanceOf(NevarError);
135
+ });
136
+ });
137
+
138
+ describe('NevarLoopError', () => {
139
+ it('should have correct defaults', () => {
140
+ const error = new NevarLoopError('loop detected');
141
+ expect(error.name).toBe('NevarLoopError');
142
+ expect(error.code).toBe('NEVAR_LOOP_ERROR');
143
+ });
144
+
145
+ it('should be instanceof NevarError', () => {
146
+ expect(new NevarLoopError('test')).toBeInstanceOf(NevarError);
147
+ });
148
+
149
+ it('should carry loop context', () => {
150
+ const error = new NevarLoopError('loop detected at depth 3', {
151
+ context: { depth: 3, triggerKey: 'order.updated', ruleIds: ['r-1', 'r-2'] },
152
+ });
153
+ expect(error.context).toEqual({ depth: 3, triggerKey: 'order.updated', ruleIds: ['r-1', 'r-2'] });
154
+ });
155
+ });
156
+
157
+ describe('NevarStorageError', () => {
158
+ it('should have correct defaults', () => {
159
+ const error = new NevarStorageError('db connection failed');
160
+ expect(error.name).toBe('NevarStorageError');
161
+ expect(error.code).toBe('NEVAR_STORAGE_ERROR');
162
+ });
163
+
164
+ it('should be instanceof NevarError', () => {
165
+ expect(new NevarStorageError('test')).toBeInstanceOf(NevarError);
166
+ });
167
+ });
168
+
169
+ describe('NevarValidationError', () => {
170
+ it('should have correct defaults', () => {
171
+ const error = new NevarValidationError('invalid rule');
172
+ expect(error.name).toBe('NevarValidationError');
173
+ expect(error.code).toBe('NEVAR_VALIDATION_ERROR');
174
+ });
175
+
176
+ it('should be instanceof NevarError', () => {
177
+ expect(new NevarValidationError('test')).toBeInstanceOf(NevarError);
178
+ });
179
+
180
+ it('should support all options', () => {
181
+ const cause = new TypeError('expected string');
182
+ const error = new NevarValidationError('rule name is required', {
183
+ cause,
184
+ context: { field: 'name', received: undefined },
185
+ });
186
+ expect(error.cause).toBe(cause);
187
+ expect(error.context).toEqual({ field: 'name', received: undefined });
188
+ });
189
+ });
@@ -0,0 +1,152 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { isConditionGroup, isCondition } from '../src/conditions';
10
+
11
+ describe('isConditionGroup', () => {
12
+ it('should return true for a valid AND group', () => {
13
+ expect(isConditionGroup({
14
+ operator: 'AND',
15
+ conditions: [{ field: 'age', op: 'gt', value: 18 }],
16
+ })).toBe(true);
17
+ });
18
+
19
+ it('should return true for a valid OR group', () => {
20
+ expect(isConditionGroup({
21
+ operator: 'OR',
22
+ conditions: [],
23
+ })).toBe(true);
24
+ });
25
+
26
+ it('should return true for a valid NOT group', () => {
27
+ expect(isConditionGroup({
28
+ operator: 'NOT',
29
+ conditions: [{ field: 'status', op: 'eq', value: 'banned' }],
30
+ })).toBe(true);
31
+ });
32
+
33
+ it('should return true for nested groups', () => {
34
+ expect(isConditionGroup({
35
+ operator: 'AND',
36
+ conditions: [
37
+ { operator: 'OR', conditions: [] },
38
+ { field: 'x', op: 'eq', value: 1 },
39
+ ],
40
+ })).toBe(true);
41
+ });
42
+
43
+ it('should return false for null', () => {
44
+ expect(isConditionGroup(null)).toBe(false);
45
+ });
46
+
47
+ it('should return false for undefined', () => {
48
+ expect(isConditionGroup(undefined)).toBe(false);
49
+ });
50
+
51
+ it('should return false for a string', () => {
52
+ expect(isConditionGroup('AND')).toBe(false);
53
+ });
54
+
55
+ it('should return false for a number', () => {
56
+ expect(isConditionGroup(42)).toBe(false);
57
+ });
58
+
59
+ it('should return false when operator is missing', () => {
60
+ expect(isConditionGroup({ conditions: [] })).toBe(false);
61
+ });
62
+
63
+ it('should return false when conditions is missing', () => {
64
+ expect(isConditionGroup({ operator: 'AND' })).toBe(false);
65
+ });
66
+
67
+ it('should return false when operator is not AND/OR/NOT', () => {
68
+ expect(isConditionGroup({ operator: 'XOR', conditions: [] })).toBe(false);
69
+ });
70
+
71
+ it('should return false when conditions is not an array', () => {
72
+ expect(isConditionGroup({ operator: 'AND', conditions: 'not-array' })).toBe(false);
73
+ });
74
+
75
+ it('should return false for an empty object', () => {
76
+ expect(isConditionGroup({})).toBe(false);
77
+ });
78
+
79
+ it('should return false for a Condition (not a group)', () => {
80
+ expect(isConditionGroup({ field: 'age', op: 'gt', value: 18 })).toBe(false);
81
+ });
82
+ });
83
+
84
+ describe('isCondition', () => {
85
+ it('should return true for a valid condition', () => {
86
+ expect(isCondition({ field: 'age', op: 'gt', value: 18 })).toBe(true);
87
+ });
88
+
89
+ it('should return true when value is null', () => {
90
+ expect(isCondition({ field: 'name', op: 'is_null', value: null })).toBe(true);
91
+ });
92
+
93
+ it('should return true when value is undefined', () => {
94
+ expect(isCondition({ field: 'name', op: 'is_undefined', value: undefined })).toBe(true);
95
+ });
96
+
97
+ it('should return true when value is 0', () => {
98
+ expect(isCondition({ field: 'count', op: 'eq', value: 0 })).toBe(true);
99
+ });
100
+
101
+ it('should return true when value is false', () => {
102
+ expect(isCondition({ field: 'active', op: 'eq', value: false })).toBe(true);
103
+ });
104
+
105
+ it('should return true when value is empty string', () => {
106
+ expect(isCondition({ field: 'name', op: 'eq', value: '' })).toBe(true);
107
+ });
108
+
109
+ it('should return false for null', () => {
110
+ expect(isCondition(null)).toBe(false);
111
+ });
112
+
113
+ it('should return false for undefined', () => {
114
+ expect(isCondition(undefined)).toBe(false);
115
+ });
116
+
117
+ it('should return false for a string', () => {
118
+ expect(isCondition('field:age')).toBe(false);
119
+ });
120
+
121
+ it('should return false for a number', () => {
122
+ expect(isCondition(42)).toBe(false);
123
+ });
124
+
125
+ it('should return false when field is missing', () => {
126
+ expect(isCondition({ op: 'gt', value: 18 })).toBe(false);
127
+ });
128
+
129
+ it('should return false when op is missing', () => {
130
+ expect(isCondition({ field: 'age', value: 18 })).toBe(false);
131
+ });
132
+
133
+ it('should return false when value key is missing', () => {
134
+ expect(isCondition({ field: 'age', op: 'gt' })).toBe(false);
135
+ });
136
+
137
+ it('should return false when field is not a string', () => {
138
+ expect(isCondition({ field: 123, op: 'gt', value: 18 })).toBe(false);
139
+ });
140
+
141
+ it('should return false when op is not a string', () => {
142
+ expect(isCondition({ field: 'age', op: 123, value: 18 })).toBe(false);
143
+ });
144
+
145
+ it('should return false for an empty object', () => {
146
+ expect(isCondition({})).toBe(false);
147
+ });
148
+
149
+ it('should return false for a ConditionGroup', () => {
150
+ expect(isCondition({ operator: 'AND', conditions: [] })).toBe(false);
151
+ });
152
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Configuration for retrying a failed action with exponential backoff.
3
+ */
4
+ export interface ActionRetryConfig {
5
+ maxAttempts: number;
6
+ backoffMs: number;
7
+ backoffMultiplier: number;
8
+ maxBackoffMs: number;
9
+ retryableErrors?: string[];
10
+ }
11
+ /**
12
+ * Defines an action to execute when a rule matches.
13
+ */
14
+ export interface ActionDefinition {
15
+ actionType: string;
16
+ config: Record<string, unknown>;
17
+ executionOrder: number;
18
+ retry?: ActionRetryConfig;
19
+ }
20
+ /**
21
+ * An intent represents a planned action derived from a matched rule.
22
+ */
23
+ export interface ActionIntent {
24
+ actionType: string;
25
+ config: Record<string, unknown>;
26
+ ruleId: string;
27
+ ruleName: string;
28
+ retry?: ActionRetryConfig;
29
+ }
30
+ /**
31
+ * A trigger that should be fired as a result of an action execution.
32
+ */
33
+ export interface DeferredTrigger {
34
+ triggerKey: string;
35
+ payload: Record<string, unknown>;
36
+ }
37
+ /**
38
+ * The result of executing a single action.
39
+ */
40
+ export interface ActionResult {
41
+ intent: ActionIntent;
42
+ status: 'success' | 'error';
43
+ output: Record<string, unknown>;
44
+ deferredTriggers?: DeferredTrigger[];
45
+ durationMs: number;
46
+ attempts: number;
47
+ lastError?: string;
48
+ }
49
+ /**
50
+ * Controls how action failures are handled within a rule's action list.
51
+ */
52
+ export type ActionFailureMode = 'fail-fast' | 'best-effort';
53
+ /**
54
+ * Defines an action handler that can be registered with the engine.
55
+ * Includes the handler function, metadata for discovery, and a config schema.
56
+ */
57
+ export interface ActionHandlerDefinition {
58
+ label: string;
59
+ category: string;
60
+ canDefer: boolean;
61
+ configSchema: unknown;
62
+ handler: (config: Record<string, unknown>, context: Record<string, unknown>, priorResults: ActionResult[]) => Promise<Record<string, unknown>>;
63
+ }
64
+ /**
65
+ * The aggregate result of executing a set of action intents.
66
+ */
67
+ export interface ExecutionResult {
68
+ results: ActionResult[];
69
+ deferredTriggers: DeferredTrigger[];
70
+ durationMs: number;
71
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A group of conditions combined with a logical operator.
3
+ * NOT groups evaluate children as AND, then negate the result.
4
+ */
5
+ export interface ConditionGroup {
6
+ operator: 'AND' | 'OR' | 'NOT';
7
+ conditions: (ConditionGroup | Condition)[];
8
+ }
9
+ /**
10
+ * A single condition that compares a field value using an operator.
11
+ */
12
+ export interface Condition {
13
+ field: string;
14
+ op: string;
15
+ value: unknown;
16
+ }
17
+ /**
18
+ * Type guard: checks whether the given value is a ConditionGroup.
19
+ */
20
+ export declare function isConditionGroup(value: unknown): value is ConditionGroup;
21
+ /**
22
+ * Type guard: checks whether the given value is a Condition.
23
+ */
24
+ export declare function isCondition(value: unknown): value is Condition;