@bernierllc/nevar-evaluator 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/README.md CHANGED
@@ -1,45 +1,82 @@
1
1
  # @bernierllc/nevar-evaluator
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ Evaluates condition trees against a flat context using an operator registry for the Nevar rules engine.
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ ## Installation
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
7
+ ```bash
8
+ npm install @bernierllc/nevar-evaluator
9
+ ```
8
10
 
9
- ## Purpose
11
+ ## Usage
10
12
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@bernierllc/nevar-evaluator`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
13
+ ```typescript
14
+ import { evaluate, resolveField } from '@bernierllc/nevar-evaluator';
15
+ import type { OperatorLookup } from '@bernierllc/nevar-evaluator';
16
+ import type { ConditionGroup } from '@bernierllc/nevar-types';
15
17
 
16
- ## What is OIDC Trusted Publishing?
18
+ // Any object implementing OperatorLookup (e.g. OperatorRegistry from nevar-operator-registry)
19
+ const operators: OperatorLookup = {
20
+ has: (name) => name === 'gte',
21
+ evaluate: (name, field, target) => (field as number) >= (target as number),
22
+ };
17
23
 
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.
24
+ const tree: ConditionGroup = {
25
+ operator: 'AND',
26
+ conditions: [
27
+ { field: 'user.age', op: 'gte', value: 18 },
28
+ ],
29
+ };
19
30
 
20
- ## Setup Instructions
31
+ const result = evaluate(tree, { user: { age: 21 } }, operators);
32
+ // result.matched === true
33
+ // result.trace === [{ field: 'user.age', op: 'gte', expected: 18, actual: 21, matched: true }]
21
34
 
22
- To properly configure OIDC trusted publishing for this package:
35
+ // Resolve a dot-path field from a nested object
36
+ const value = resolveField({ user: { name: 'Alice' } }, 'user.name');
37
+ // 'Alice'
38
+ ```
23
39
 
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
40
+ ## API
28
41
 
29
- ## DO NOT USE THIS PACKAGE
42
+ ### `evaluate(tree, context, operators)`
30
43
 
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
44
+ Recursively evaluates a condition tree (AND / OR / NOT groups) against a context object. Returns a boolean match result and a trace of every condition evaluated.
36
45
 
37
- ## More Information
46
+ **Parameters:**
47
+ - `tree: ConditionGroup` - The root condition group to evaluate
48
+ - `context: Record<string, unknown>` - Key-value context to evaluate against
49
+ - `operators: OperatorLookup` - Operator lookup for resolving and evaluating operators
38
50
 
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)
51
+ **Returns:** `EvaluationResult` with `matched: boolean` and `trace: EvaluationTrace[]`
42
52
 
43
- ---
53
+ **Throws:** `NevarEvaluationError` if an unknown operator is encountered.
44
54
 
45
- **Maintained for OIDC setup purposes only**
55
+ ### `resolveField(context, fieldPath)`
56
+
57
+ Resolves a dot-notation field path against a context object. Returns `undefined` if any segment is missing.
58
+
59
+ **Parameters:**
60
+ - `context: Record<string, unknown>` - The context object
61
+ - `fieldPath: string` - Dot-notation path (e.g. `"user.name"`)
62
+
63
+ **Returns:** `unknown` - The resolved value or `undefined`
64
+
65
+ ### `OperatorLookup` (type)
66
+
67
+ Interface for looking up and evaluating operators. Decouples the evaluator from a specific operator registry implementation.
68
+
69
+ - `evaluate(name: string, fieldValue: unknown, targetValue: unknown): boolean`
70
+ - `has(name: string): boolean`
71
+
72
+ ## Integration Documentation
73
+
74
+ ### Logger Integration
75
+ 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.
76
+
77
+ ### NeverHub Integration
78
+ 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.
79
+
80
+ ## License
81
+
82
+ Copyright (c) 2025 Bernier LLC. All rights reserved.
@@ -0,0 +1,41 @@
1
+ import { ConditionGroup, EvaluationResult } from '@bernierllc/nevar-types';
2
+ /**
3
+ * Interface for looking up and evaluating operators.
4
+ * Decouples the evaluator from a specific operator registry implementation.
5
+ */
6
+ export interface OperatorLookup {
7
+ evaluate(name: string, fieldValue: unknown, targetValue: unknown): boolean;
8
+ has(name: string): boolean;
9
+ }
10
+ /**
11
+ * Resolves a dot-path field reference against a flat context object.
12
+ * Returns undefined if any segment is missing.
13
+ *
14
+ * @example
15
+ * resolveField({ user: { name: 'Alice' } }, 'user.name') // 'Alice'
16
+ * resolveField({ a: 1 }, 'b.c') // undefined
17
+ */
18
+ export declare function resolveField(context: Record<string, unknown>, fieldPath: string): unknown;
19
+ /**
20
+ * Evaluates a condition tree against a flat context using an operator registry.
21
+ * Returns a boolean match result and a trace of every condition evaluated.
22
+ *
23
+ * @param tree - The root condition group to evaluate.
24
+ * @param context - A flat (or nested) key-value context to evaluate against.
25
+ * @param operators - An operator lookup for resolving and evaluating operators.
26
+ * @returns An EvaluationResult with matched status and evaluation trace.
27
+ *
28
+ * @throws NevarEvaluationError if an unknown operator is encountered.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const result = evaluate(
33
+ * { operator: 'AND', conditions: [{ field: 'age', op: 'gte', value: 18 }] },
34
+ * { age: 21 },
35
+ * operatorRegistry,
36
+ * );
37
+ * // result.matched === true
38
+ * // result.trace === [{ field: 'age', op: 'gte', expected: 18, actual: 21, matched: true }]
39
+ * ```
40
+ */
41
+ export declare function evaluate(tree: ConditionGroup, context: Record<string, unknown>, operators: OperatorLookup): EvaluationResult;
@@ -0,0 +1,131 @@
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 });
10
+ exports.resolveField = resolveField;
11
+ exports.evaluate = evaluate;
12
+ const nevar_types_1 = require("@bernierllc/nevar-types");
13
+ /**
14
+ * Resolves a dot-path field reference against a flat context object.
15
+ * Returns undefined if any segment is missing.
16
+ *
17
+ * @example
18
+ * resolveField({ user: { name: 'Alice' } }, 'user.name') // 'Alice'
19
+ * resolveField({ a: 1 }, 'b.c') // undefined
20
+ */
21
+ function resolveField(context, fieldPath) {
22
+ const segments = fieldPath.split('.');
23
+ let current = context;
24
+ for (const segment of segments) {
25
+ if (current === null || current === undefined || typeof current !== 'object') {
26
+ return undefined;
27
+ }
28
+ current = current[segment];
29
+ }
30
+ return current;
31
+ }
32
+ /**
33
+ * Evaluates a single leaf condition against the context.
34
+ * Returns the trace entry and whether it matched.
35
+ */
36
+ function evaluateCondition(condition, context, operators) {
37
+ if (!operators.has(condition.op)) {
38
+ throw new nevar_types_1.NevarEvaluationError(`Unknown operator: "${condition.op}"`, {
39
+ code: 'UNKNOWN_OPERATOR',
40
+ context: { operator: condition.op, field: condition.field },
41
+ });
42
+ }
43
+ const actual = resolveField(context, condition.field);
44
+ // Missing field in context evaluates to false
45
+ if (actual === undefined) {
46
+ return {
47
+ matched: false,
48
+ trace: {
49
+ field: condition.field,
50
+ op: condition.op,
51
+ expected: condition.value,
52
+ actual: undefined,
53
+ matched: false,
54
+ },
55
+ };
56
+ }
57
+ const matched = operators.evaluate(condition.op, actual, condition.value);
58
+ return {
59
+ matched,
60
+ trace: {
61
+ field: condition.field,
62
+ op: condition.op,
63
+ expected: condition.value,
64
+ actual,
65
+ matched,
66
+ },
67
+ };
68
+ }
69
+ /**
70
+ * Recursively evaluates a condition group (AND / OR / NOT) against the context.
71
+ * Returns the aggregate match result and all trace entries.
72
+ */
73
+ function evaluateGroup(group, context, operators) {
74
+ const trace = [];
75
+ // Empty conditions → vacuous truth
76
+ if (group.conditions.length === 0) {
77
+ return { matched: true, trace };
78
+ }
79
+ const childResults = [];
80
+ for (const child of group.conditions) {
81
+ if ((0, nevar_types_1.isConditionGroup)(child)) {
82
+ const result = evaluateGroup(child, context, operators);
83
+ trace.push(...result.trace);
84
+ childResults.push(result.matched);
85
+ }
86
+ else if ((0, nevar_types_1.isCondition)(child)) {
87
+ const result = evaluateCondition(child, context, operators);
88
+ trace.push(result.trace);
89
+ childResults.push(result.matched);
90
+ }
91
+ }
92
+ let matched;
93
+ switch (group.operator) {
94
+ case 'AND':
95
+ matched = childResults.every(Boolean);
96
+ break;
97
+ case 'OR':
98
+ matched = childResults.some(Boolean);
99
+ break;
100
+ case 'NOT':
101
+ // NOT evaluates children as AND, then negates
102
+ matched = !childResults.every(Boolean);
103
+ break;
104
+ }
105
+ return { matched, trace };
106
+ }
107
+ /**
108
+ * Evaluates a condition tree against a flat context using an operator registry.
109
+ * Returns a boolean match result and a trace of every condition evaluated.
110
+ *
111
+ * @param tree - The root condition group to evaluate.
112
+ * @param context - A flat (or nested) key-value context to evaluate against.
113
+ * @param operators - An operator lookup for resolving and evaluating operators.
114
+ * @returns An EvaluationResult with matched status and evaluation trace.
115
+ *
116
+ * @throws NevarEvaluationError if an unknown operator is encountered.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const result = evaluate(
121
+ * { operator: 'AND', conditions: [{ field: 'age', op: 'gte', value: 18 }] },
122
+ * { age: 21 },
123
+ * operatorRegistry,
124
+ * );
125
+ * // result.matched === true
126
+ * // result.trace === [{ field: 'age', op: 'gte', expected: 18, actual: 21, matched: true }]
127
+ * ```
128
+ */
129
+ function evaluate(tree, context, operators) {
130
+ return evaluateGroup(tree, context, operators);
131
+ }
@@ -0,0 +1,2 @@
1
+ export { evaluate, resolveField } from './evaluator';
2
+ export type { OperatorLookup } from './evaluator';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
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 });
10
+ exports.resolveField = exports.evaluate = void 0;
11
+ var evaluator_1 = require("./evaluator");
12
+ Object.defineProperty(exports, "evaluate", { enumerable: true, get: function () { return evaluator_1.evaluate; } });
13
+ Object.defineProperty(exports, "resolveField", { enumerable: true, get: function () { return evaluator_1.resolveField; } });
package/package.json CHANGED
@@ -1,10 +1,56 @@
1
1
  {
2
2
  "name": "@bernierllc/nevar-evaluator",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @bernierllc/nevar-evaluator",
3
+ "version": "0.1.0",
4
+ "description": "Evaluates condition trees against a flat context using an operator registry for the Nevar rules engine",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/**/*",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
5
12
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
10
- }
13
+ "nevar",
14
+ "rules-engine",
15
+ "evaluator",
16
+ "condition-tree",
17
+ "evaluation"
18
+ ],
19
+ "author": "Bernier LLC",
20
+ "license": "SEE LICENSE IN LICENSE",
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "registry": "https://registry.npmjs.org/"
24
+ },
25
+ "engines": {
26
+ "node": ">=16.0.0"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/bernierllc/tools.git",
31
+ "directory": "packages/core/nevar-evaluator"
32
+ },
33
+ "dependencies": {
34
+ "@bernierllc/nevar-types": "0.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/jest": "^29.5.0",
38
+ "@types/node": "^20.0.0",
39
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
40
+ "@typescript-eslint/parser": "^6.0.0",
41
+ "eslint": "^8.0.0",
42
+ "jest": "^29.5.0",
43
+ "rimraf": "^5.0.0",
44
+ "ts-jest": "^29.1.0",
45
+ "typescript": "^5.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "prebuild": "npm run clean",
50
+ "clean": "rimraf dist",
51
+ "test": "jest",
52
+ "test:run": "jest",
53
+ "test:coverage": "jest --coverage",
54
+ "lint": "eslint src __tests__ --ext .ts"
55
+ }
56
+ }