Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
# Enterprise migration — @suaveplan/rules + @suaveplan/rules-hono
**Package**: `@suaveplan/rules`, `@suaveplan/rules-hono`
**Status**: PROPOSED
**Type**: new-package (migration + enterprise harden)
## Why
`@suaveplan/rules` is on the genesis Pending list (workflow plugin). Upstream ships a 63-file, ~17.5k-LOC rule engine (rule engine + builder + context + 30+ operators + 3 validator families + serializer + optimizer + action subsystem + event bus + Hono middleware) at 92.87% coverage and no Zod/OTel/SuaveplanError integration. Genesis ships every package at the bar set by `crypto-rsa` / `webauthn` / `srp`: 100% coverage, sibling `.md` per impl, Zod at every boundary, OTel hot-path instrumentation, `defineCodes` error registry extending `SuaveplanError`, injectable `Clock`/`Rng`, `AbortSignal` threaded through every async surface. This migration brings the package across at that bar in a single change rather than landing a parity port and a follow-up harden.
The Hono middleware is split into a sibling workflow-tier package `@suaveplan/rules-hono`. The core rules engine stays universal-tier with zero Node/browser/framework deps; the Hono integration lives next to its peer so consumers who do not use Hono pay nothing for it. This is the same split pattern used for `crypto-rsa` / `crypto-bin` / `crypto-cli` (core primitive vs. framework consumer).
## What changes
### @suaveplan/rules (universal-tier core)
- ADDED: `RuleEngine` — async evaluator with `evaluate` / `evaluateAll` / `evaluateRuleset` / `check`, depth limit, evaluation cache, AbortSignal, OTel spans + histograms, error results carrying `RulesError` codes
- ADDED: `RuleBuilder` / `FieldBuilder` fluent DSL — `rule()` / `condition()` / `and()` / `or()` / `not()` / `group()` factory functions
- ADDED: `ContextBuilder` / `DefaultContextProvider` / `DefaultFieldResolver` / `createRequestContext` / `createScopedContext` / `getField` / `hasField` / `context` / `contextWith`
- ADDED: `Action` / `ActionRegistry` / `ActionExecutor` with `createAction` / `createActionRegistry` / `createActionExecutor` / `validateActionConfig` / `builtInHandlers` (log/set/delay/noop), timeout + AbortSignal, sequential + parallel execution
- ADDED: `EventBus` / `HookManager` with `createEventBus` / `createHookManager`, bounded history (injectable `Clock`), priority hooks, `waitFor` with timeout + abort
- ADDED: `OperatorRegistry` + 30 built-in operators (`equalsOperator`, `notEqualsOperator`, `greaterThanOperator`, `greaterThanOrEqualsOperator`, `lessThanOperator`, `lessThanOrEqualsOperator`, `betweenOperator`, `inOperator`, `notInOperator`, `containsOperator`, `notContainsOperator`, `containsAllOperator`, `containsAnyOperator`, `overlapsOperator`, `startsWithOperator`, `endsWithOperator`, `matchesOperator`, `existsOperator`, `isNullOperator`, `isNotNullOperator`, `isEmptyOperator`, `isNotEmptyOperator`, `isTypeOperator`, `hasPropertyOperator`, `lengthEqualsOperator`, `lengthGreaterThanOperator`, `lengthLessThanOperator`, `andOperator`, `orOperator`, `notOperator`, `xorOperator`) + `defaultOperatorRegistry`
- ADDED: `SchemaValidator` — JSON-schema-style recursive validator with formats (email/url/uuid/date/datetime/regex/ipv4/ipv6), transforms (trim/lowercase/uppercase/parseNumber/parseDate), nested objects, arrays, custom validator hooks
- ADDED: `FieldValidator` + `field()` + 12 preset chainable validators (`emailFieldValidator`, `passwordFieldValidator`, `usernameFieldValidator`, `phoneFieldValidator`, `urlFieldValidator`, `uuidFieldValidator`, `dateFieldValidator`, `integerFieldValidator`, `ageValidator`, `currencyValidator`, `percentageValidator`, `positiveNumberValidator`) + `createFieldValidator`
- ADDED: `CustomValidator` + `ValidatorRegistry` + 17 built-in validators (`emailValidator`, `urlValidator`, `uuidValidator`, `isoDateValidator`, `creditCardValidator` w/ Luhn, `phoneValidator`, `strongPasswordValidator`, `alphanumericValidator`, `hexColorValidator`, `slugValidator`, `jsonValidator`, `enumValidator`, `lengthValidator`, `patternValidator`, `rangeValidator`, `asyncValidator`, `composeValidators`) + `createValidator` + `defaultValidatorRegistry`
- ADDED: Serializer — `serializeRule` / `serializeRuleset` / `serializeRuleToObject` / `serializeRulesetToObject` / `deserializeRule` / `deserializeRuleset` / `deserializeRuleFromObject` / `deserializeRulesetFromObject` / `cloneRule` / `hashRule` / `areRulesEqual` / `validateSerializedRule` with function-placeholder strategy for `CustomRule.validate` / `ComputedRule.compute`
- ADDED: Optimizer — `optimizeRule` / `analyzeRule` / `estimateCost` / `findFields` / `hasAsyncComponents` / `countRules` (flatten nested AND/OR, dedupe, simplify constant branches, sort for short-circuit)
- ADDED: Type guards — `isConditionRule` / `isLogicalRule` / `isGroupRule` / `isCustomRule` / `isComputedRule`
- ADDED: `RulesError` extending `SuaveplanError` with `defineCodes("RULES", [...])` registry covering: `ENGINE_DEPTH_EXCEEDED`, `ENGINE_EVALUATION_FAILED`, `ENGINE_ABORTED`, `ENGINE_TIMEOUT`, `OPERATOR_NOT_FOUND`, `OPERATOR_INVALID`, `FIELD_RESOLUTION_FAILED`, `CONTEXT_INVALID`, `ACTION_TIMEOUT`, `ACTION_ABORTED`, `ACTION_HANDLER_FAILED`, `ACTION_NOT_FOUND`, `SCHEMA_INVALID`, `VALIDATOR_INVALID`, `VALIDATOR_NOT_FOUND`, `EVENT_TIMEOUT`, `EVENT_ABORTED`, `HOOK_INVALID`, `SERIALIZE_FAILED`, `DESERIALIZE_FAILED`, `RULE_INVALID`, `RULESET_INVALID`, `OPTIMIZER_FAILED`
- ADDED: Subpath exports — `./engine`, `./builder`, `./context`, `./actions`, `./events`, `./operators`, `./validators/schema`, `./validators/field`, `./validators/custom`, `./serializer`, `./optimizer`, `./types`, `./errors`
- ADDED: OTel surface — `rules.engine.evaluate.duration_ms` / `rules.engine.evaluations_total{outcome}` / `rules.engine.depth` / `rules.validator.validate.duration_ms{kind}` / `rules.validator.validations_total{kind,outcome}` / `rules.serializer.duration_ms{op}` / `rules.action.execute.duration_ms` / `rules.action.executions_total{status}` / `rules.event.emitted_total{type}` / `rules.operator.unknown_total{name}`
### @suaveplan/rules-hono (workflow-tier consumer)
- ADDED: `ruleMiddleware` / `validationMiddleware` / `accessControl` / `responseValidation` / `rateLimitRule` / `combineRules` over Hono `MiddlewareHandler`
- ADDED: `RuleMiddlewareOptions` / `ValidationMiddlewareOptions` / `AccessControlOptions` / `HonoValidationError` types
- ADDED: Zod-validated options; default singleton `RuleEngine` opt-in (not module-level); explicit `engine` injection encouraged
- ADDED: Subpath exports — `./middleware`, `./types`
- ADDED: OTel spans `rules_hono.middleware.<name>.duration_ms{outcome}`
## Out of scope
- **Storybook / E2E** — engine + validators are headless universal-tier; Hono middleware tested via in-process Hono `app.request` (no browser/E2E surface).
- **`@suaveplan/schema-registry` integration** — enterprise plan §3 lists this; deferring because the schema-registry use case for `rules` is rule-format evolution across multiple deployed services, not present in genesis-internal consumers yet. Tracked separately when a consumer needs it.
- **Feature flags via `@suaveplan/feature-flags`** — enterprise plan §3 lists this; the engine itself does not have risky-rollout entry points. Consumers compose feature-flags with rules where they need conditional rollouts.
- **Codemod for upstream callers** — genesis is a fresh branch; no upstream consumers depend on `@suaveplan/rules` from genesis yet. CHANGELOG entry covers the API delta for documentation.
- **i18n keying of error messages** — error codes are i18n-ready (typed discriminants per `@suaveplan/error` contract); user-facing message strings remain English-only in this change. Translation catalogues land per-consumer.
## Parity checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| `RuleEngine` evaluate/evaluateAll/evaluateRuleset/check | ✅ | ⬆️ | Enhanced: AbortSignal, OTel, RulesError codes, depth limit enforced via typed code |
| RuleBuilder + FieldBuilder + factories | ✅ | ✅ | |
| ContextBuilder + DefaultContextProvider + DefaultFieldResolver | ✅ | ⬆️ | Enhanced: injectable `Clock`, no `new Date()` at module scope |
| Action / ActionRegistry / ActionExecutor | ✅ | ⬆️ | Enhanced: AbortSignal threaded into `execute`, OTel histogram, typed `ACTION_TIMEOUT` / `ACTION_ABORTED` codes |
| EventBus + HookManager + waitFor | ✅ | ⬆️ | Enhanced: AbortSignal on `waitFor`, bounded history via injectable `Clock`, typed `EVENT_TIMEOUT` / `EVENT_ABORTED` codes |
| OperatorRegistry + 30 built-ins | ✅ | ✅ | |
| SchemaValidator (JSON-schema-like) | ✅ | ⬆️ | Enhanced: Zod-validated `Schema` boundary, OTel histogram |
| FieldValidator + 12 preset chains | ✅ | ⬆️ | Enhanced: AbortSignal on `validate`, OTel histogram |
| CustomValidator + 17 built-in validators + ValidatorRegistry | ✅ | ⬆️ | Enhanced: AbortSignal on async validators, OTel histogram |
| Serializer (serialize/deserialize/clone/hash/equality) | ✅ | ⬆️ | Enhanced: Zod-validated `SerializedRule` boundary, function-placeholder strategy documented per RFC 8259 |
| Optimizer (flatten/dedupe/simplify/sort/analyze/estimateCost/findFields) | ✅ | ✅ | |
| Type guards (isConditionRule etc.) | ✅ | ✅ | |
| Hono middleware (6 functions) | ✅ | 🔄 | Promoted to sibling `@suaveplan/rules-hono` per user direction; surface 1:1 |
| Module-level singleton `defaultEngine` in middleware | ✅ | 🚫 | Scoped out: module-level singleton is a hidden global. Consumers pass `engine` explicitly or call `getDefaultEngine()` lazily (memoised per-process). |
| `console.log` builtin handler | ✅ | ⬆️ | Replaced with `LoggerLike` injection — `console.log` is banned in genesis src/ per AGENTS.md §2 |
| `Math.random` / `Date.now` at module scope | ✅ | ⬆️ | Replaced with injectable `Clock` / `Rng` from `@suaveplan/types` |
## Source mapping
This change derives from `../suaveplan/enterprise/rules.md` and the upstream source at `../suaveplan/packages/universal/rules/`.
| Requirement | Source lines | Notes |
|---|---|---|
| Zero browser/Node/React deps | enterprise/rules.md L37 | verbatim — preserved by keeping rules universal-tier |
| Zod schemas at public boundaries | enterprise/rules.md L38 | verbatim |
| Property-based tests for pure functions | enterprise/rules.md L39 | applied to operators + optimizer + serializer round-trips |
| Deterministic behaviour — no `Math.random`/`Date.now` at module scope | enterprise/rules.md L40 | verbatim — drives `Clock`/`Rng` injection |
| OTel instrumentation via `@suaveplan/opentelemetry` for all public entrypoints | enterprise/rules.md L41 | verbatim |
| Structured errors extending `@suaveplan/error` with stable codes | enterprise/rules.md L42 | drives `RulesError` registry |
| AbortSignal everywhere async | enterprise/rules.md L43 | verbatim |
| i18n-ready, all messages keyed | enterprise/rules.md L46 | drives typed code discriminants (messages translatable per code) |
| Test coverage 100% | enterprise/rules.md L50 | verbatim |
| Docs coverage 100% per `docs:check` | enterprise/rules.md L51 | verbatim — sibling `.md` per impl |
| File-size budget 300 hard / 500 test | enterprise/rules.md L52 | verbatim |
| No mocks — real services | enterprise/rules.md L53 | applied via injectable interfaces (Clock/Rng/Logger), no external IO in rules itself |
| `sideEffects: false` tree-shake | enterprise/rules.md L55 | verbatim |
| Plugin/extension interface | enterprise/rules.md L69 | applied to `OperatorRegistry` + `ValidatorRegistry` + `ActionRegistry` + `RuleEnginePlugin` |
| Observability surface — counters/histograms/spans | enterprise/rules.md L70 | drives OTel instrument list above |
| Benchmark suite in `src/__benchmarks__` | enterprise/rules.md L72 | mitata benchmarks shipped per AGENTS.md §9 |
| Hono middleware | upstream src/middleware/hono/hono.ts L1-L350 | promoted to sibling package per user direction |
| 30 built-in operators | upstream src/operators/operator-registry/operator-registry.ts L1-L682 | full surface preserved |
| 17 custom-validator built-ins | upstream src/validators/custom-validator/custom-validator.ts | full surface preserved |
| 12 field-validator preset chains | upstream src/validators/field-validator/field-validator.ts | full surface preserved |
| Serializer function-placeholder strategy | upstream src/utils/rule-serializer/rule-serializer.ts | preserved; documented as a deliberate limitation |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| `RulesError` code registry of 24 codes | Genesis policy — every error path discriminable. Upstream returns ad-hoc `RuleError` objects without a stable code set; genesis maps every failure mode to a typed code. |
| Subpath exports per feature module | Genesis tree-shaking policy — AGENTS.md §6. |
| Pattern B feature-module layout | Genesis structure policy — CLAUDE.md "Package Structure" section. |
| `@suaveplan/testing/runner` for every test import | Genesis cross-runner policy — AGENTS.md §7, CLAUDE.md rule 11. |
| Explicit named re-exports in feature barrels | Genesis barrel policy — AGENTS.md §6a, CLAUDE.md rule 10. |
| Removal of module-level `defaultEngine` singleton | Hidden globals across module boundaries violate the deterministic / injection-first design. Replaced with explicit `engine` parameter or lazy `getDefaultEngine()`. |
## Verification approach
Every requirement in `specs/rules/spec.md` and `specs/rules-hono/spec.md` carries a `**Verified by:**` line at authoring time per AGENTS.md §13. Test files:
**rules**:
- `src/engine/engine.test.ts` — engine evaluation, depth limit, AbortSignal, OTel emission
- `src/engine/engine.spec-conformance.test.ts` — RulesError code coverage, type-level invariants
- `src/builder/builder.test.ts` — fluent DSL round-trip
- `src/context/context.test.ts` — field resolution (dot path / array index), request-context derivation
- `src/actions/actions.test.ts` — sequential/parallel, timeout, AbortSignal, registry
- `src/events/event-bus.test.ts` — emission, priority hooks, bounded history, waitFor + abort
- `src/operators/operators.test.ts` — all 30 operators, property-based for commutative/associative ones
- `src/validators/schema/schema.test.ts` — full format + transform + nested coverage
- `src/validators/field/field.test.ts` — preset chains + chainable surface
- `src/validators/custom/custom.test.ts` — every built-in including Luhn
- `src/serializer/serializer.test.ts` — round-trip property tests, function-placeholder behaviour
- `src/optimizer/optimizer.test.ts` — flatten/dedupe/simplify property tests
- `src/errors/rules-error.test.ts` — every code thrown by ≥1 production path
**rules-hono**:
- `src/middleware/rule.test.ts`
- `src/middleware/validation.test.ts`
- `src/middleware/access-control.test.ts`
- `src/middleware/response-validation.test.ts`
- `src/middleware/rate-limit-rule.test.ts`
- `src/middleware/combine.test.ts`
## Migration impact
None — additive only. No genesis consumers of `@suaveplan/rules` exist yet (Pending status per STATUS.md). Future consumers (`form-builder`, `workflow`) will import directly; this change is published before they land.
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/rules`.
- No dependents on genesis (Pending → Completed). New package — no behavior change to propagate.
- `@suaveplan/rules-hono` is created in this same change, depends on `@suaveplan/rules` `workspace:^`.