llmbic 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.
- package/CHANGELOG.md +23 -0
- package/README.md +53 -6
- package/dist/extractor.js +5 -5
- package/dist/extractor.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/merge.d.ts +21 -2
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +57 -10
- package/dist/merge.js.map +1 -1
- package/dist/types/extractor.types.d.ts +11 -5
- package/dist/types/extractor.types.d.ts.map +1 -1
- package/dist/types/merge.types.d.ts +59 -3
- package/dist/types/merge.types.d.ts.map +1 -1
- package/dist/utils/normalizer-id.d.ts +14 -0
- package/dist/utils/normalizer-id.d.ts.map +1 -0
- package/dist/utils/normalizer-id.js +25 -0
- package/dist/utils/normalizer-id.js.map +1 -0
- package/dist/utils/value-equals.d.ts +21 -0
- package/dist/utils/value-equals.d.ts.map +1 -0
- package/dist/utils/value-equals.js +76 -0
- package/dist/utils/value-equals.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-04-23
|
|
9
|
+
|
|
10
|
+
Non-breaking. Normalizer mutations now show up in `ExtractionResult` as a dedicated, ordered list so callers can audit exactly what each post-fusion transformation did: which field, which normalizer, before-and-after values. Complements `sources[field]` (which still describes the post-fusion origin) - the two are orthogonal signals.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `ExtractionResult.normalizerMutations: NormalizerMutation<T>[]` - one entry per `(normalizer, field)` where the field's value changed during that normalizer's pass, in the order they occurred. Empty array when no normalizers ran or none mutated any field. Enables audit, observability, and regression diagnosis on normalizer changes. Existing consumers keep working unchanged - the field is additive and its neutral value is `[]`.
|
|
15
|
+
- `NormalizerMutation<T>` public type - `{ normalizerId, field, before, after, step }`. `before` / `after` are captured verbatim (no diffing, no deep-cloning); `step` is the zero-based index of the normalizer in the configured pipeline so consumers can order or group mutations produced by the same pass.
|
|
16
|
+
- `defineNormalizer<T, TContext = unknown>(id, apply)` helper - attaches a stable `id` to a normalizer, useful for arrow functions which would otherwise resolve to `'anonymous'` in `NormalizerMutation.normalizerId`. Resolution order for a normalizer's id: `fn.id` (non-empty string) -> `fn.name` -> `'anonymous'`; regular named functions already pick up their name for free.
|
|
17
|
+
|
|
18
|
+
## [1.4.0] - 2026-04-18
|
|
19
|
+
|
|
20
|
+
Non-breaking. Normalizers can now read the same caller-provided `context` the rules see, so post-merge cross-field fix-ups no longer have to be closed over at extractor-declaration time. Typical use: a normalizer that reconciles extracted fields against the `sourceUrl` or per-tenant configuration passed to `extract`.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `Normalizer<T, TContext = unknown>` - second optional generic type parameter describing the shape of the per-call context forwarded to the normalizer's third argument. Defaults to `unknown`, so context-unaware normalizers and legacy code compile unchanged.
|
|
25
|
+
- `Normalizer(data, content, context?)` - third optional argument, left `undefined` when the caller passes no context.
|
|
26
|
+
- `MergeApplyOptions<T, TContext = unknown>` - second optional generic parameter shared with `Normalizer`, surfacing as `normalizers?: Normalizer<T, TContext>[]`.
|
|
27
|
+
- `merge.apply<S, TContext>(schema, rulesResult, llmResult, content, options?, context?)` - sixth optional argument forwarded verbatim to every normalizer.
|
|
28
|
+
- `ExtractorConfig<S, TContext>.normalizers` now types as `Normalizer<z.infer<S>, TContext>[]`, so the context flowing through rules reaches normalizers with the same compile-time shape.
|
|
29
|
+
- `Extractor.merge(partial, llmResult, content, context?)` - fourth optional argument. Rules are still not re-evaluated, but normalizers run here too; accepting `context` keeps them consistent with `Extractor.extract` / `Extractor.extractSync`.
|
|
30
|
+
|
|
8
31
|
## [1.3.0] - 2026-04-18
|
|
9
32
|
|
|
10
33
|
Non-breaking. Rules can now read a caller-provided `context` object alongside `content`, so per-call metadata (locale, tenant configuration, feature flags) no longer has to be captured in rule closures at declaration time.
|
package/README.md
CHANGED
|
@@ -290,7 +290,7 @@ await extractor.extract('Listed at £42', { region: 'uk' });
|
|
|
290
290
|
// -> { price: '42' }
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
-
The type parameter on `rule.create<TContext>` flows to the generic on `createExtractor<Schema, TContext>` and is enforced at every call site. `context` is forwarded verbatim to every rule's `extract` callback;
|
|
293
|
+
The type parameter on `rule.create<TContext>` flows to the generic on `createExtractor<Schema, TContext>` and is enforced at every call site. `context` is forwarded verbatim to every rule's `extract` callback AND to every normalizer as their optional third argument; callbacks that ignore it still compile and work. `extractSync(content, context?)` behaves the same way for batch workflows. `Extractor.merge(partial, llmResult, content, context?)` does not re-evaluate rules (it reuses the partial's values) but normalizers still run, so it accepts the same optional `context`.
|
|
294
294
|
|
|
295
295
|
### Rich schemas
|
|
296
296
|
|
|
@@ -303,22 +303,69 @@ The JSON Schema handed to the LLM supports the Zod constructs that show up in re
|
|
|
303
303
|
|
|
304
304
|
### Normalizers
|
|
305
305
|
|
|
306
|
-
Post-merge transformations. Run in sequence, receive the merged data + original content:
|
|
306
|
+
Post-merge transformations. Run in sequence, receive the merged data + original content, and optionally the same per-call `context` the rules see (see [Per-call context](#per-call-context)):
|
|
307
307
|
|
|
308
308
|
```typescript
|
|
309
|
-
|
|
309
|
+
type Ctx = { sourceUrl: string };
|
|
310
|
+
|
|
311
|
+
const extractor = createExtractor<typeof MySchema, Ctx>({
|
|
310
312
|
schema: MySchema,
|
|
311
313
|
rules: [...],
|
|
312
314
|
normalizers: [
|
|
313
315
|
(data, content) => {
|
|
314
|
-
//
|
|
316
|
+
// Rules/content-only normalizer: no context needed
|
|
315
317
|
if (data.price && data.price < 100) data.price = null;
|
|
316
318
|
return data;
|
|
317
319
|
},
|
|
320
|
+
(data, _content, context) => {
|
|
321
|
+
// Context-aware: cross-field fix-up that depends on where the content came from
|
|
322
|
+
if (context && !data.city && /\/liege\//.test(context.sourceUrl)) {
|
|
323
|
+
data.city = 'Liège';
|
|
324
|
+
}
|
|
325
|
+
return data;
|
|
326
|
+
},
|
|
318
327
|
],
|
|
319
328
|
});
|
|
329
|
+
|
|
330
|
+
await extractor.extract(markdown, { sourceUrl: 'https://example.be/liege/123' });
|
|
320
331
|
```
|
|
321
332
|
|
|
333
|
+
Context-unaware normalizers keep working unchanged - the third argument is optional and left `undefined` when the caller passes no context.
|
|
334
|
+
|
|
335
|
+
#### Normalizer mutation tracking
|
|
336
|
+
|
|
337
|
+
Every run of `merge.apply` (and therefore every `extract` / `extractSync` / `merge` call) returns a `normalizerMutations` array listing every field a normalizer rewrote, in the order it happened. Complements `sources[field]`: `sources` keeps pointing at the post-fusion origin (rule / llm / agreement / flag), `normalizerMutations` tells you what the post-fusion pass then did to the value.
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import { createExtractor, defineNormalizer } from 'llmbic';
|
|
341
|
+
|
|
342
|
+
const clampPrice = defineNormalizer<Listing>('clamp-price', (data) => {
|
|
343
|
+
if (data.price !== null && data.price > 1_000_000) data.price = 1_000_000;
|
|
344
|
+
return data;
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const extractor = createExtractor({
|
|
348
|
+
schema: ListingSchema,
|
|
349
|
+
rules: [...],
|
|
350
|
+
normalizers: [clampPrice],
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const result = await extractor.extract(markdown);
|
|
354
|
+
|
|
355
|
+
result.normalizerMutations;
|
|
356
|
+
// [
|
|
357
|
+
// { normalizerId: 'clamp-price', field: 'price', before: 9_999_999, after: 1_000_000, step: 0 },
|
|
358
|
+
// ]
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The `normalizerId` is resolved in this order:
|
|
362
|
+
|
|
363
|
+
1. An explicit `.id` string property on the normalizer function (set via `defineNormalizer('my-id', fn)` or `Object.assign(fn, { id: 'my-id' })`).
|
|
364
|
+
2. The function's `name` - regular `function myNormalizer() {}` or `const myNormalizer = (data) => ...` pick this up for free.
|
|
365
|
+
3. `'anonymous'` fallback for arrow functions declared inline without a binding.
|
|
366
|
+
|
|
367
|
+
For production code, prefer `defineNormalizer` or named function expressions: ids show up in logs and audit trails, and refactors that inline / extract arrows won't silently rename them. `before` and `after` are recorded verbatim (no diffing, no deep-cloning) - interpretation is the consumer's job.
|
|
368
|
+
|
|
322
369
|
### Validators (invariants)
|
|
323
370
|
|
|
324
371
|
Check the final output for logical consistency:
|
|
@@ -474,7 +521,7 @@ Creates an extractor instance. Signature: `createExtractor<S, TContext = unknown
|
|
|
474
521
|
| `schema` | `ZodObject` | yes | Output schema (drives field enumeration and re-validation). |
|
|
475
522
|
| `rules` | `ExtractionRule<TContext>[]` | yes | Deterministic extraction rules. |
|
|
476
523
|
| `llm` | `ExtractorLlmConfig` | no | LLM fallback. Omit for rules-only mode. See below. |
|
|
477
|
-
| `normalizers` | `Normalizer<T>[]` | no | Post-merge transformations, run in declared order. |
|
|
524
|
+
| `normalizers` | `Normalizer<T, TContext>[]` | no | Post-merge transformations, run in declared order. Each normalizer receives `(data, content, context?)`; `TContext` is shared with the rules array. |
|
|
478
525
|
| `validators` | `Validator<ExtractedData<T>>[]` | no | Invariants populating `result.validation`. |
|
|
479
526
|
| `policy` | `Partial<FieldMergePolicy>` | no | Overrides the per-field merge policy (conflict strategy, confidence defaults, equality) for every field. |
|
|
480
527
|
| `policyByField` | `{ [K in keyof T]?: Partial<FieldMergePolicy> }` | no | Per-field overrides applied on top of `policy`. Precedence: defaults < `policy` < `policyByField[field]`. |
|
|
@@ -517,7 +564,7 @@ Binding `T` once lets TypeScript infer each field's type from the field name, so
|
|
|
517
564
|
| `extractSync(content, context?)` | sync | Rules only. Returns the partial result + `missing` fields. `context` is forwarded verbatim to every rule's `extract` callback. |
|
|
518
565
|
| `prompt(content, partial)` | sync | Builds the LLM request. Covers `partial.missing` in fill-gaps mode, every schema field in cross-check mode. |
|
|
519
566
|
| `parse(raw)` | sync | Parses a raw LLM JSON response, validating each field individually. Never throws. |
|
|
520
|
-
| `merge(partial, llmResult, content)` | sync | Merges rules + LLM, detects conflicts, normalizes, validates. Does not re-evaluate rules,
|
|
567
|
+
| `merge(partial, llmResult, content, context?)` | sync | Merges rules + LLM, detects conflicts, normalizes, validates. Does not re-evaluate rules, but normalizers still run and receive `context` verbatim. |
|
|
521
568
|
|
|
522
569
|
## License
|
|
523
570
|
|
package/dist/extractor.js
CHANGED
|
@@ -84,7 +84,7 @@ export function createExtractor(config) {
|
|
|
84
84
|
async extract(content, context) {
|
|
85
85
|
const startedAt = performance.now();
|
|
86
86
|
const rulesResult = rule.apply(content, config.rules, config.schema, config.logger, context);
|
|
87
|
-
const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions);
|
|
87
|
+
const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions, context);
|
|
88
88
|
const shouldCallLlm = config.llm !== undefined &&
|
|
89
89
|
(buildOptions.mode === 'cross-check' || partial.missing.length > 0);
|
|
90
90
|
if (!shouldCallLlm) {
|
|
@@ -100,13 +100,13 @@ export function createExtractor(config) {
|
|
|
100
100
|
const llmResult = config.llm.transformResponse
|
|
101
101
|
? await config.llm.transformResponse(parsedLlmResult, request)
|
|
102
102
|
: parsedLlmResult;
|
|
103
|
-
const final = merge.apply(config.schema, rulesResult, llmResult, content, mergeOptions);
|
|
103
|
+
const final = merge.apply(config.schema, rulesResult, llmResult, content, mergeOptions, context);
|
|
104
104
|
return stampDuration(final, startedAt);
|
|
105
105
|
},
|
|
106
106
|
extractSync(content, context) {
|
|
107
107
|
const startedAt = performance.now();
|
|
108
108
|
const rulesResult = rule.apply(content, config.rules, config.schema, config.logger, context);
|
|
109
|
-
const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions);
|
|
109
|
+
const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions, context);
|
|
110
110
|
return stampDuration(partial, startedAt);
|
|
111
111
|
},
|
|
112
112
|
prompt(content, partial) {
|
|
@@ -115,10 +115,10 @@ export function createExtractor(config) {
|
|
|
115
115
|
parse(raw) {
|
|
116
116
|
return prompt.parse(config.schema, allFields, raw);
|
|
117
117
|
},
|
|
118
|
-
merge(partial, llmResult, content) {
|
|
118
|
+
merge(partial, llmResult, content, context) {
|
|
119
119
|
const startedAt = performance.now();
|
|
120
120
|
const rulesResult = rulesResultFromPartial(partial, allFields);
|
|
121
|
-
const result = merge.apply(config.schema, rulesResult, llmResult, content, mergeOptions);
|
|
121
|
+
const result = merge.apply(config.schema, rulesResult, llmResult, content, mergeOptions, context);
|
|
122
122
|
return stampDuration(result, startedAt);
|
|
123
123
|
},
|
|
124
124
|
};
|
package/dist/extractor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAKrC;;;;;GAKG;AACH,SAAS,sBAAsB,CAC7B,OAA4B,EAC5B,SAA+B;IAE/B,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAqC,EAAE,CAAC;IACxD,MAAM,SAAS,GAAqC,EAAE,CAAC;IACvD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,SAAS;QACX,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,KAAmB,CAAC;QACpC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,UAAU,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC;QACtC,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;YAC1C,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CACpB,MAA2B,EAC3B,SAAiB;IAEjB,OAAO;QACL,GAAG,MAAM;QACT,IAAI,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE;KACpE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAI7B,MAAoC;IAGpC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAmB,CAAC;IAErE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,YAAY,GAAG;QACnB,YAAY,EAAE,MAAM,CAAC,GAAG,EAAE,YAAY;QACtC,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,IAAI,WAAW;QACrC,eAAe,EAAE,MAAM,CAAC,GAAG,EAAE,eAAe,IAAI,UAAU;KAClD,CAAC;IAEX,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { createExtractor } from './extractor.js';
|
|
13
13
|
export { rule } from './rules.js';
|
|
14
|
-
export { merge } from './merge.js';
|
|
14
|
+
export { merge, defineNormalizer } from './merge.js';
|
|
15
15
|
export { prompt } from './prompt.js';
|
|
16
16
|
export { validator } from './validate.js';
|
|
17
17
|
export type { ExtractionRule, RuleMatch, RulesResult, } from './types/rule.types.js';
|
|
@@ -20,5 +20,5 @@ export type { CrossCheckHints, LlmRequest, PromptBuildMode, PromptBuildOptions,
|
|
|
20
20
|
export type { LlmProvider } from './types/provider.types.js';
|
|
21
21
|
export type { Logger } from './types/logger.types.js';
|
|
22
22
|
export type { Severity, Violation, Validator, } from './types/validate.types.js';
|
|
23
|
-
export type { Conflict, ConflictStrategy, ExtractedData, ExtractionMeta, ExtractionResult, FieldCompare, FieldMergePolicy, FieldMergeResult, FieldSource, LlmResult, MergeApplyOptions, Normalizer, ValidationResult, } from './types/merge.types.js';
|
|
23
|
+
export type { Conflict, ConflictStrategy, ExtractedData, ExtractionMeta, ExtractionResult, FieldCompare, FieldMergePolicy, FieldMergeResult, FieldSource, LlmResult, MergeApplyOptions, Normalizer, NormalizerMutation, ValidationResult, } from './types/merge.types.js';
|
|
24
24
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,YAAY,EACV,cAAc,EACd,SAAS,EACT,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,eAAe,EACf,UAAU,EACV,eAAe,EACf,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,YAAY,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,YAAY,EACV,QAAQ,EACR,SAAS,EACT,SAAS,GACV,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { createExtractor } from './extractor.js';
|
|
13
13
|
export { rule } from './rules.js';
|
|
14
|
-
export { merge } from './merge.js';
|
|
14
|
+
export { merge, defineNormalizer } from './merge.js';
|
|
15
15
|
export { prompt } from './prompt.js';
|
|
16
16
|
export { validator } from './validate.js';
|
|
17
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/merge.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { Logger } from './types/logger.types.js';
|
|
3
3
|
import type { RuleMatch, RulesResult } from './types/rule.types.js';
|
|
4
|
-
import type { ExtractionResult, FieldMergePolicy, FieldMergeResult, LlmResult, MergeApplyOptions } from './types/merge.types.js';
|
|
4
|
+
import type { ExtractionResult, FieldMergePolicy, FieldMergeResult, LlmResult, MergeApplyOptions, Normalizer } from './types/merge.types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Field-level and object-level merge primitives.
|
|
7
7
|
*
|
|
@@ -65,12 +65,31 @@ export declare const merge: {
|
|
|
65
65
|
* later slices; for now `durationMs` is `0`.
|
|
66
66
|
*
|
|
67
67
|
* @typeParam S - A Zod object schema.
|
|
68
|
+
* @typeParam TContext - Shape of the optional context forwarded to every
|
|
69
|
+
* normalizer. Defaults to `unknown`.
|
|
68
70
|
* @param schema - Zod object schema describing the target data shape.
|
|
69
71
|
* @param rulesResult - Output of {@link rule.apply} for the same schema.
|
|
70
72
|
* @param llmResult - Parsed LLM response, or `null` for rules-only mode.
|
|
71
73
|
* @param content - Original text the rules and LLM were derived from; forwarded to normalizers so they can cross-reference the source.
|
|
72
74
|
* @param options - Optional behavior overrides (policy, normalizers, validators, logger).
|
|
75
|
+
* @param context - Optional caller-defined value forwarded to every normalizer's third argument. Left `undefined` when omitted.
|
|
73
76
|
*/
|
|
74
|
-
apply<S extends z.ZodObject<z.ZodRawShape
|
|
77
|
+
apply<S extends z.ZodObject<z.ZodRawShape>, TContext = unknown>(schema: S, rulesResult: RulesResult<z.infer<S>>, llmResult: LlmResult | null, content: string, options?: MergeApplyOptions<z.infer<S>, TContext>, context?: TContext): ExtractionResult<z.infer<S>>;
|
|
75
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* Ergonomic helper to attach a stable `id` to a normalizer. Useful for arrow
|
|
81
|
+
* functions which otherwise resolve to `'anonymous'` in
|
|
82
|
+
* {@link NormalizerMutation.normalizerId}.
|
|
83
|
+
*
|
|
84
|
+
* Equivalent to `Object.assign(fn, { id })` with proper typings. The returned
|
|
85
|
+
* value is a {@link Normalizer} that wraps `apply` verbatim and carries the
|
|
86
|
+
* explicit `id`. `id` takes precedence over `fn.name` per the resolution
|
|
87
|
+
* rules of {@link resolveNormalizerId}.
|
|
88
|
+
*
|
|
89
|
+
* @typeParam T - Non-null target shape of the extraction.
|
|
90
|
+
* @typeParam TContext - Optional per-call context type. Defaults to `unknown`.
|
|
91
|
+
* @param id - Non-empty stable identifier surfaced in mutation records.
|
|
92
|
+
* @param apply - The normalizer body.
|
|
93
|
+
*/
|
|
94
|
+
export declare function defineNormalizer<T, TContext = unknown>(id: string, apply: Normalizer<T, TContext>): Normalizer<T, TContext>;
|
|
76
95
|
//# sourceMappingURL=merge.d.ts.map
|
package/dist/merge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAEhB,SAAS,EACT,iBAAiB,
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAEhB,SAAS,EACT,iBAAiB,EACjB,UAAU,EAEX,MAAM,wBAAwB,CAAC;AAiMhC;;;;;GAKG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;OAMG;;QAED,6CAA6C;;QAE7C,yDAAyD;;QAEzD,sDAAsD;;QAEtD,wDAAwD;;QAExD,qGAAqG;qBACxF,OAAO,KAAK,OAAO,KAAG,OAAO;;IAQ5C;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SACE,MAAM,aACF,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,YACpB,OAAO,WACR,OAAO,CAAC,gBAAgB,CAAC,WACzB,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC;IAgEtB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;UACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,QAAQ,oBAC1C,CAAC,eACI,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aACzB,SAAS,GAAG,IAAI,WAClB,MAAM,YACL,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,YACvC,QAAQ,GACjB,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CA4ChC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,EACpD,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,GAC7B,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAIzB"}
|
package/dist/merge.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { valueEquals } from './utils/value-equals.js';
|
|
2
|
+
import { resolveNormalizerId } from './utils/normalizer-id.js';
|
|
1
3
|
/**
|
|
2
4
|
* Walk every schema field, build the {@link RuleMatch} if rules produced a
|
|
3
5
|
* value, fuse it with the LLM candidate via {@link merge.field}, and collect
|
|
@@ -82,15 +84,37 @@ function deriveSource(fused, ruleMatch, llmValue, policy, ruleId) {
|
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
86
|
* Apply every configured {@link Normalizer} to the merged data in declared
|
|
85
|
-
* order
|
|
86
|
-
* what the rest of the pipeline
|
|
87
|
+
* order and track per-field mutations along the way. Normalizers may mutate
|
|
88
|
+
* their argument; the returned reference is what the rest of the pipeline
|
|
89
|
+
* observes. The caller-provided `context` is forwarded verbatim to every
|
|
90
|
+
* normalizer (left `undefined` when the caller passed none).
|
|
91
|
+
*
|
|
92
|
+
* For each normalizer, every schema field is snapshotted, the normalizer is
|
|
93
|
+
* invoked, and each field whose value changed is recorded as a
|
|
94
|
+
* {@link NormalizerMutation}. Equality is structural (see `valueEquals`) so
|
|
95
|
+
* an arrow that returns `{ ...data, x: x }` without actually changing any
|
|
96
|
+
* value does not generate spurious entries.
|
|
87
97
|
*/
|
|
88
|
-
function runNormalizers(data, normalizers, content) {
|
|
98
|
+
function runNormalizers(data, normalizers, content, context, schemaFields) {
|
|
99
|
+
const mutations = [];
|
|
89
100
|
let current = data;
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
const list = normalizers ?? [];
|
|
102
|
+
for (let step = 0; step < list.length; step++) {
|
|
103
|
+
const normalizer = list[step];
|
|
104
|
+
const before = {};
|
|
105
|
+
for (const field of schemaFields) {
|
|
106
|
+
before[field] = current[field];
|
|
107
|
+
}
|
|
108
|
+
current = normalizer(current, content, context);
|
|
109
|
+
const normalizerId = resolveNormalizerId(normalizer);
|
|
110
|
+
for (const field of schemaFields) {
|
|
111
|
+
const after = current[field];
|
|
112
|
+
if (!valueEquals(before[field], after)) {
|
|
113
|
+
mutations.push({ normalizerId, field, before: before[field], after, step });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
92
116
|
}
|
|
93
|
-
return current;
|
|
117
|
+
return { data: current, mutations };
|
|
94
118
|
}
|
|
95
119
|
/**
|
|
96
120
|
* Produce the violation list for the normalized data: first the Zod schema
|
|
@@ -246,24 +270,28 @@ export const merge = {
|
|
|
246
270
|
* later slices; for now `durationMs` is `0`.
|
|
247
271
|
*
|
|
248
272
|
* @typeParam S - A Zod object schema.
|
|
273
|
+
* @typeParam TContext - Shape of the optional context forwarded to every
|
|
274
|
+
* normalizer. Defaults to `unknown`.
|
|
249
275
|
* @param schema - Zod object schema describing the target data shape.
|
|
250
276
|
* @param rulesResult - Output of {@link rule.apply} for the same schema.
|
|
251
277
|
* @param llmResult - Parsed LLM response, or `null` for rules-only mode.
|
|
252
278
|
* @param content - Original text the rules and LLM were derived from; forwarded to normalizers so they can cross-reference the source.
|
|
253
279
|
* @param options - Optional behavior overrides (policy, normalizers, validators, logger).
|
|
280
|
+
* @param context - Optional caller-defined value forwarded to every normalizer's third argument. Left `undefined` when omitted.
|
|
254
281
|
*/
|
|
255
|
-
apply(schema, rulesResult, llmResult, content, options) {
|
|
282
|
+
apply(schema, rulesResult, llmResult, content, options, context) {
|
|
256
283
|
const schemaKeys = Object.keys(schema.shape);
|
|
257
284
|
const fusion = fuseAllFields(schemaKeys, rulesResult, llmResult, options?.policy, options?.policyByField, options?.logger);
|
|
258
|
-
const normalized = runNormalizers(fusion.data, options?.normalizers, content);
|
|
259
|
-
const violations = collectViolations(schema, normalized, fusion.missing, options?.validators);
|
|
285
|
+
const normalized = runNormalizers(fusion.data, options?.normalizers, content, context, schemaKeys);
|
|
286
|
+
const violations = collectViolations(schema, normalized.data, fusion.missing, options?.validators);
|
|
260
287
|
const valid = !violations.some((v) => v.severity === 'error');
|
|
261
288
|
return {
|
|
262
|
-
data: normalized,
|
|
289
|
+
data: normalized.data,
|
|
263
290
|
confidence: fusion.confidence,
|
|
264
291
|
sources: fusion.sources,
|
|
265
292
|
conflicts: fusion.conflicts,
|
|
266
293
|
missing: fusion.missing,
|
|
294
|
+
normalizerMutations: normalized.mutations,
|
|
267
295
|
validation: { valid, violations },
|
|
268
296
|
meta: {
|
|
269
297
|
rulesMatched: fusion.rulesMatched,
|
|
@@ -273,4 +301,23 @@ export const merge = {
|
|
|
273
301
|
};
|
|
274
302
|
},
|
|
275
303
|
};
|
|
304
|
+
/**
|
|
305
|
+
* Ergonomic helper to attach a stable `id` to a normalizer. Useful for arrow
|
|
306
|
+
* functions which otherwise resolve to `'anonymous'` in
|
|
307
|
+
* {@link NormalizerMutation.normalizerId}.
|
|
308
|
+
*
|
|
309
|
+
* Equivalent to `Object.assign(fn, { id })` with proper typings. The returned
|
|
310
|
+
* value is a {@link Normalizer} that wraps `apply` verbatim and carries the
|
|
311
|
+
* explicit `id`. `id` takes precedence over `fn.name` per the resolution
|
|
312
|
+
* rules of {@link resolveNormalizerId}.
|
|
313
|
+
*
|
|
314
|
+
* @typeParam T - Non-null target shape of the extraction.
|
|
315
|
+
* @typeParam TContext - Optional per-call context type. Defaults to `unknown`.
|
|
316
|
+
* @param id - Non-empty stable identifier surfaced in mutation records.
|
|
317
|
+
* @param apply - The normalizer body.
|
|
318
|
+
*/
|
|
319
|
+
export function defineNormalizer(id, apply) {
|
|
320
|
+
const wrapped = (data, content, context) => apply(data, content, context);
|
|
321
|
+
return Object.assign(wrapped, { id });
|
|
322
|
+
}
|
|
276
323
|
//# sourceMappingURL=merge.js.map
|
package/dist/merge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,8 +62,13 @@ export type ExtractorConfig<S extends z.ZodObject<z.ZodRawShape>, TContext = unk
|
|
|
62
62
|
rules: ExtractionRule<TContext>[];
|
|
63
63
|
/** Optional LLM fallback invoked for fields the rules could not produce. */
|
|
64
64
|
llm?: ExtractorLlmConfig;
|
|
65
|
-
/**
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Post-merge transformations, forwarded to every `merge.apply` call.
|
|
67
|
+
* Normalizers share the extractor's `TContext`, so they can read the same
|
|
68
|
+
* per-call context as the rules (see
|
|
69
|
+
* {@link Extractor.extract} / {@link Extractor.extractSync} / {@link Extractor.merge}).
|
|
70
|
+
*/
|
|
71
|
+
normalizers?: Normalizer<z.infer<S>, TContext>[];
|
|
67
72
|
/** Invariants checked on the normalized data; populate `result.validation`. */
|
|
68
73
|
validators?: Validator<ExtractedData<z.infer<S>>>[];
|
|
69
74
|
/** Overrides for the per-field merge policy (conflict strategy, confidences, compare). */
|
|
@@ -121,9 +126,10 @@ export type Extractor<T, TContext = unknown> = {
|
|
|
121
126
|
/**
|
|
122
127
|
* Merge a previously-obtained `partial` with an LLM result to produce the
|
|
123
128
|
* final {@link ExtractionResult}. Reuses the per-field values, confidence
|
|
124
|
-
* and provenance already carried by `partial
|
|
125
|
-
* not re-evaluated, so
|
|
129
|
+
* and provenance already carried by `partial` - the deterministic rules are
|
|
130
|
+
* not re-evaluated. Normalizers still run here, so an optional `context` is
|
|
131
|
+
* accepted and forwarded verbatim to every normalizer that declares one.
|
|
126
132
|
*/
|
|
127
|
-
merge(partial: ExtractionResult<T>, llmResult: LlmResult, content: string): ExtractionResult<T>;
|
|
133
|
+
merge(partial: ExtractionResult<T>, llmResult: LlmResult, content: string, context?: TContext): ExtractionResult<T>;
|
|
128
134
|
};
|
|
129
135
|
//# sourceMappingURL=extractor.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.types.d.ts","sourceRoot":"","sources":["../../src/types/extractor.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEtF;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+DAA+D;IAC/D,QAAQ,EAAE,WAAW,CAAC;IACtB,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,CACjB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,MAAM,KACZ,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAClB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,UAAU,KAChB,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,CACzB,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EACpC,QAAQ,GAAG,OAAO,IAChB;IACF,2FAA2F;IAC3F,MAAM,EAAE,CAAC,CAAC;IACV,qFAAqF;IACrF,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;IAClC,4EAA4E;IAC5E,GAAG,CAAC,EAAE,kBAAkB,CAAC;IACzB
|
|
1
|
+
{"version":3,"file":"extractor.types.d.ts","sourceRoot":"","sources":["../../src/types/extractor.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,17 +17,29 @@ export type ExtractedData<T> = {
|
|
|
17
17
|
* and return the updated data. They are allowed to mutate their input; the
|
|
18
18
|
* merge pipeline shallow-copies once before invoking them.
|
|
19
19
|
*
|
|
20
|
+
* Normalizers can optionally accept a `context` argument: an opaque,
|
|
21
|
+
* caller-defined value forwarded verbatim by `merge.apply` /
|
|
22
|
+
* `Extractor.extract` - the same per-call context the extractor's rules
|
|
23
|
+
* receive. Typical use is to gate cross-field fix-ups on tenant-specific
|
|
24
|
+
* configuration (e.g. a source URL, a feature flag). `TContext` defaults to
|
|
25
|
+
* `unknown` so context-unaware normalizers stay assignable to arrays typed
|
|
26
|
+
* with any context.
|
|
27
|
+
*
|
|
20
28
|
* @typeParam T - Non-null target shape the extraction is aiming for.
|
|
29
|
+
* @typeParam TContext - Shape of the optional per-call context forwarded to
|
|
30
|
+
* the normalizer. Defaults to `unknown`.
|
|
21
31
|
*/
|
|
22
|
-
export type Normalizer<T> = (data: ExtractedData<T>, content: string) => ExtractedData<T>;
|
|
32
|
+
export type Normalizer<T, TContext = unknown> = (data: ExtractedData<T>, content: string, context?: TContext) => ExtractedData<T>;
|
|
23
33
|
/**
|
|
24
34
|
* Behavior overrides accepted by the top-level merge. Every field is
|
|
25
35
|
* optional; defaults match rules-only mode with no normalization and no
|
|
26
36
|
* validators.
|
|
27
37
|
*
|
|
28
38
|
* @typeParam T - Non-null target shape (`z.infer<Schema>`).
|
|
39
|
+
* @typeParam TContext - Shape of the optional per-call context forwarded to
|
|
40
|
+
* every normalizer. Defaults to `unknown`.
|
|
29
41
|
*/
|
|
30
|
-
export type MergeApplyOptions<T> = {
|
|
42
|
+
export type MergeApplyOptions<T, TContext = unknown> = {
|
|
31
43
|
/** Overrides forwarded to every field-level fusion call. */
|
|
32
44
|
policy?: Partial<FieldMergePolicy>;
|
|
33
45
|
/**
|
|
@@ -39,7 +51,7 @@ export type MergeApplyOptions<T> = {
|
|
|
39
51
|
[K in keyof T]?: Partial<FieldMergePolicy>;
|
|
40
52
|
};
|
|
41
53
|
/** Transformations run in declared order after the per-field fusion. */
|
|
42
|
-
normalizers?: Normalizer<T>[];
|
|
54
|
+
normalizers?: Normalizer<T, TContext>[];
|
|
43
55
|
/** Invariants run on the normalized data; their violations populate `validation`. */
|
|
44
56
|
validators?: Validator<ExtractedData<T>>[];
|
|
45
57
|
/** Logger propagated through the pipeline for warnings and fallbacks. */
|
|
@@ -130,6 +142,40 @@ export type FieldMergePolicy = {
|
|
|
130
142
|
/** Equality check used to detect agreement between the rule and the LLM. */
|
|
131
143
|
compare: FieldCompare;
|
|
132
144
|
};
|
|
145
|
+
/**
|
|
146
|
+
* A single mutation observed while running a {@link Normalizer}. One entry is
|
|
147
|
+
* produced per (normalizer, field) pair whose value changed during that
|
|
148
|
+
* normalizer's pass. Consumers use this signal to audit post-fusion
|
|
149
|
+
* transformations, diagnose regressions after a normalizer change, or surface
|
|
150
|
+
* "what did the pipeline actually do" in observability dashboards.
|
|
151
|
+
*
|
|
152
|
+
* `before` and `after` are recorded verbatim: no diffing, no deep-cloning,
|
|
153
|
+
* no semantic interpretation. The caller interprets them (display, JSON
|
|
154
|
+
* serialization, audit log, ...).
|
|
155
|
+
*
|
|
156
|
+
* @typeParam T - Non-null target shape the extraction is aiming for.
|
|
157
|
+
*/
|
|
158
|
+
export type NormalizerMutation<T> = {
|
|
159
|
+
/**
|
|
160
|
+
* Identifier of the normalizer that produced the mutation. Resolution
|
|
161
|
+
* order: `fn.id` (string property) -> `fn.name` -> `'anonymous'`. Use
|
|
162
|
+
* {@link defineNormalizer} (exported from the package root) to attach a
|
|
163
|
+
* stable id to an arrow function.
|
|
164
|
+
*/
|
|
165
|
+
normalizerId: string;
|
|
166
|
+
/** Schema field whose value changed. */
|
|
167
|
+
field: keyof T;
|
|
168
|
+
/** Value observed by the normalizer for that field. */
|
|
169
|
+
before: unknown;
|
|
170
|
+
/** Value the normalizer wrote for that field. */
|
|
171
|
+
after: unknown;
|
|
172
|
+
/**
|
|
173
|
+
* Zero-based index of the normalizer in the configured pipeline. Useful
|
|
174
|
+
* when a field is mutated by several normalizers in sequence: the entry
|
|
175
|
+
* with the smallest `step` ran first.
|
|
176
|
+
*/
|
|
177
|
+
step: number;
|
|
178
|
+
};
|
|
133
179
|
/**
|
|
134
180
|
* Re-exported from `rule.types` so consumers building {@link FieldMergeResult}
|
|
135
181
|
* or implementing a custom merge on top of {@link FieldMergePolicy} only need
|
|
@@ -193,6 +239,16 @@ export type ExtractionResult<T> = {
|
|
|
193
239
|
conflicts: Conflict[];
|
|
194
240
|
/** Fields for which no value could be produced. */
|
|
195
241
|
missing: (keyof T)[];
|
|
242
|
+
/**
|
|
243
|
+
* Mutations observed while running the configured normalizers, in the order
|
|
244
|
+
* they occurred. One entry per `(normalizer, field)` where the field's
|
|
245
|
+
* value changed. Empty when no normalizers ran or none mutated any field.
|
|
246
|
+
*
|
|
247
|
+
* Orthogonal to {@link ExtractionResult.sources}: `sources[field]` keeps
|
|
248
|
+
* pointing at the post-fusion origin even after a normalizer rewrites
|
|
249
|
+
* the value; this array is where post-fusion rewrites are tracked.
|
|
250
|
+
*/
|
|
251
|
+
normalizerMutations: NormalizerMutation<T>[];
|
|
196
252
|
/** Aggregate output of the validators. */
|
|
197
253
|
validation: ValidationResult;
|
|
198
254
|
/** Runtime metadata about this extraction. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.types.d.ts","sourceRoot":"","sources":["../../src/types/merge.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;CAAE,CAAC;AAE/D
|
|
1
|
+
{"version":3,"file":"merge.types.d.ts","sourceRoot":"","sources":["../../src/types/merge.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a stable identifier for a normalizer function:
|
|
3
|
+
*
|
|
4
|
+
* 1. An explicit `id` property (non-empty string) - set via
|
|
5
|
+
* `defineNormalizer(id, fn)` or any caller-owned convention.
|
|
6
|
+
* 2. The function's `name` (non-empty) - regular named `function` or named
|
|
7
|
+
* arrow assigned to a `const` pick this up for free.
|
|
8
|
+
* 3. `'anonymous'` fallback, used for arrow functions without a `name`.
|
|
9
|
+
*
|
|
10
|
+
* Kept private to the library; consumers read the resolved id off
|
|
11
|
+
* {@link NormalizerMutation.normalizerId} rather than calling this helper.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveNormalizerId(fn: unknown): string;
|
|
14
|
+
//# sourceMappingURL=normalizer-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizer-id.d.ts","sourceRoot":"","sources":["../../src/utils/normalizer-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAWvD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a stable identifier for a normalizer function:
|
|
3
|
+
*
|
|
4
|
+
* 1. An explicit `id` property (non-empty string) - set via
|
|
5
|
+
* `defineNormalizer(id, fn)` or any caller-owned convention.
|
|
6
|
+
* 2. The function's `name` (non-empty) - regular named `function` or named
|
|
7
|
+
* arrow assigned to a `const` pick this up for free.
|
|
8
|
+
* 3. `'anonymous'` fallback, used for arrow functions without a `name`.
|
|
9
|
+
*
|
|
10
|
+
* Kept private to the library; consumers read the resolved id off
|
|
11
|
+
* {@link NormalizerMutation.normalizerId} rather than calling this helper.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveNormalizerId(fn) {
|
|
14
|
+
if (typeof fn === 'function') {
|
|
15
|
+
const explicit = fn.id;
|
|
16
|
+
if (typeof explicit === 'string' && explicit.length > 0) {
|
|
17
|
+
return explicit;
|
|
18
|
+
}
|
|
19
|
+
if (typeof fn.name === 'string' && fn.name.length > 0) {
|
|
20
|
+
return fn.name;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return 'anonymous';
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=normalizer-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizer-id.js","sourceRoot":"","sources":["../../src/utils/normalizer-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAW;IAC7C,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAI,EAAuB,CAAC,EAAE,CAAC;QAC7C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,OAAO,EAAE,CAAC,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep structural equality tuned for the merge pipeline's needs.
|
|
3
|
+
*
|
|
4
|
+
* Called once per (normalizer, field) pair, so the common cases - primitives,
|
|
5
|
+
* `null`/`undefined`, short arrays of primitives, small plain objects - are
|
|
6
|
+
* handled inline without recursion into helper functions. Dates, Maps, Sets
|
|
7
|
+
* and custom classes fall through to a `JSON.stringify` compare. Circular
|
|
8
|
+
* structures downgrade to reference equality: the recursive compare is
|
|
9
|
+
* wrapped in a `try/catch`, so a `RangeError` from unbounded recursion or a
|
|
10
|
+
* `TypeError` from `JSON.stringify` on a cycle falls through to `false`
|
|
11
|
+
* (reference equality was already checked up front).
|
|
12
|
+
*
|
|
13
|
+
* Deliberately *not* exported from the package root: it's an internal helper
|
|
14
|
+
* for {@link runNormalizers} and callers should not rely on its semantics.
|
|
15
|
+
*
|
|
16
|
+
* @param a - First value.
|
|
17
|
+
* @param b - Second value.
|
|
18
|
+
* @returns `true` when `a` and `b` are structurally equal.
|
|
19
|
+
*/
|
|
20
|
+
export declare function valueEquals(a: unknown, b: unknown): boolean;
|
|
21
|
+
//# sourceMappingURL=value-equals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-equals.d.ts","sourceRoot":"","sources":["../../src/utils/value-equals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAe3D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep structural equality tuned for the merge pipeline's needs.
|
|
3
|
+
*
|
|
4
|
+
* Called once per (normalizer, field) pair, so the common cases - primitives,
|
|
5
|
+
* `null`/`undefined`, short arrays of primitives, small plain objects - are
|
|
6
|
+
* handled inline without recursion into helper functions. Dates, Maps, Sets
|
|
7
|
+
* and custom classes fall through to a `JSON.stringify` compare. Circular
|
|
8
|
+
* structures downgrade to reference equality: the recursive compare is
|
|
9
|
+
* wrapped in a `try/catch`, so a `RangeError` from unbounded recursion or a
|
|
10
|
+
* `TypeError` from `JSON.stringify` on a cycle falls through to `false`
|
|
11
|
+
* (reference equality was already checked up front).
|
|
12
|
+
*
|
|
13
|
+
* Deliberately *not* exported from the package root: it's an internal helper
|
|
14
|
+
* for {@link runNormalizers} and callers should not rely on its semantics.
|
|
15
|
+
*
|
|
16
|
+
* @param a - First value.
|
|
17
|
+
* @param b - Second value.
|
|
18
|
+
* @returns `true` when `a` and `b` are structurally equal.
|
|
19
|
+
*/
|
|
20
|
+
export function valueEquals(a, b) {
|
|
21
|
+
if (a === b) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (a === null || b === null || a === undefined || b === undefined) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (typeof a !== 'object' || typeof b !== 'object') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return deepEquals(a, b);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function deepEquals(a, b) {
|
|
38
|
+
const aIsArray = Array.isArray(a);
|
|
39
|
+
const bIsArray = Array.isArray(b);
|
|
40
|
+
if (aIsArray !== bIsArray) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (aIsArray && bIsArray) {
|
|
44
|
+
if (a.length !== b.length) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
for (let i = 0; i < a.length; i++) {
|
|
48
|
+
if (!valueEquals(a[i], b[i])) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
const aProto = Object.getPrototypeOf(a);
|
|
55
|
+
const bProto = Object.getPrototypeOf(b);
|
|
56
|
+
const aPlain = aProto === Object.prototype || aProto === null;
|
|
57
|
+
const bPlain = bProto === Object.prototype || bProto === null;
|
|
58
|
+
if (aPlain && bPlain) {
|
|
59
|
+
const aKeys = Object.keys(a);
|
|
60
|
+
const bKeys = Object.keys(b);
|
|
61
|
+
if (aKeys.length !== bKeys.length) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
for (const key of aKeys) {
|
|
65
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (!valueEquals(a[key], b[key])) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=value-equals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-equals.js","sourceRoot":"","sources":["../../src/utils/value-equals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,CAAU;IAChD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7B,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI,CAAC;IAC9D,MAAM,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI,CAAC;IAC9D,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;gBAClD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,CAAC,WAAW,CAAE,CAA6B,CAAC,GAAG,CAAC,EAAG,CAA6B,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC3F,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
|