@fundamental-engine/dom 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -0
  3. package/dist/apply-recipe.d.ts +103 -0
  4. package/dist/apply-recipe.d.ts.map +1 -0
  5. package/dist/apply-recipe.js +271 -0
  6. package/dist/apply-recipe.js.map +1 -0
  7. package/dist/bind-data.d.ts +72 -0
  8. package/dist/bind-data.d.ts.map +1 -0
  9. package/dist/bind-data.js +164 -0
  10. package/dist/bind-data.js.map +1 -0
  11. package/dist/browser-host.d.ts +11 -0
  12. package/dist/browser-host.d.ts.map +1 -0
  13. package/dist/browser-host.js +41 -0
  14. package/dist/browser-host.js.map +1 -0
  15. package/dist/contours.d.ts +79 -0
  16. package/dist/contours.d.ts.map +1 -0
  17. package/dist/contours.js +88 -0
  18. package/dist/contours.js.map +1 -0
  19. package/dist/env.d.ts +39 -0
  20. package/dist/env.d.ts.map +1 -0
  21. package/dist/env.js +47 -0
  22. package/dist/env.js.map +1 -0
  23. package/dist/export-dom.d.ts +7 -0
  24. package/dist/export-dom.d.ts.map +1 -0
  25. package/dist/export-dom.js +28 -0
  26. package/dist/export-dom.js.map +1 -0
  27. package/dist/feedback.d.ts +57 -0
  28. package/dist/feedback.d.ts.map +1 -0
  29. package/dist/feedback.js +134 -0
  30. package/dist/feedback.js.map +1 -0
  31. package/dist/field-nav.d.ts +35 -0
  32. package/dist/field-nav.d.ts.map +1 -0
  33. package/dist/field-nav.js +82 -0
  34. package/dist/field-nav.js.map +1 -0
  35. package/dist/flip.d.ts +31 -0
  36. package/dist/flip.d.ts.map +1 -0
  37. package/dist/flip.js +65 -0
  38. package/dist/flip.js.map +1 -0
  39. package/dist/governor.d.ts +37 -0
  40. package/dist/governor.d.ts.map +1 -0
  41. package/dist/governor.js +72 -0
  42. package/dist/governor.js.map +1 -0
  43. package/dist/index.d.ts +39 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +42 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/lint.d.ts +78 -0
  48. package/dist/lint.d.ts.map +1 -0
  49. package/dist/lint.js +153 -0
  50. package/dist/lint.js.map +1 -0
  51. package/dist/measurement.d.ts +44 -0
  52. package/dist/measurement.d.ts.map +1 -0
  53. package/dist/measurement.js +95 -0
  54. package/dist/measurement.js.map +1 -0
  55. package/dist/metrics.d.ts +70 -0
  56. package/dist/metrics.d.ts.map +1 -0
  57. package/dist/metrics.js +119 -0
  58. package/dist/metrics.js.map +1 -0
  59. package/dist/overlays.d.ts +48 -0
  60. package/dist/overlays.d.ts.map +1 -0
  61. package/dist/overlays.js +48 -0
  62. package/dist/overlays.js.map +1 -0
  63. package/dist/perf.d.ts +62 -0
  64. package/dist/perf.d.ts.map +1 -0
  65. package/dist/perf.js +94 -0
  66. package/dist/perf.js.map +1 -0
  67. package/dist/platform.d.ts +40 -0
  68. package/dist/platform.d.ts.map +1 -0
  69. package/dist/platform.js +61 -0
  70. package/dist/platform.js.map +1 -0
  71. package/dist/relationships.d.ts +79 -0
  72. package/dist/relationships.d.ts.map +1 -0
  73. package/dist/relationships.js +155 -0
  74. package/dist/relationships.js.map +1 -0
  75. package/dist/schedule.d.ts +84 -0
  76. package/dist/schedule.d.ts.map +1 -0
  77. package/dist/schedule.js +91 -0
  78. package/dist/schedule.js.map +1 -0
  79. package/dist/state.d.ts +36 -0
  80. package/dist/state.d.ts.map +1 -0
  81. package/dist/state.js +113 -0
  82. package/dist/state.js.map +1 -0
  83. package/dist/text-bodies.d.ts +71 -0
  84. package/dist/text-bodies.d.ts.map +1 -0
  85. package/dist/text-bodies.js +159 -0
  86. package/dist/text-bodies.js.map +1 -0
  87. package/dist/thread-overlay.d.ts +63 -0
  88. package/dist/thread-overlay.d.ts.map +1 -0
  89. package/dist/thread-overlay.js +110 -0
  90. package/dist/thread-overlay.js.map +1 -0
  91. package/dist/types.d.ts +51 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +7 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/visual-bindings.d.ts +95 -0
  96. package/dist/visual-bindings.d.ts.map +1 -0
  97. package/dist/visual-bindings.js +211 -0
  98. package/dist/visual-bindings.js.map +1 -0
  99. package/package.json +59 -0
package/dist/lint.d.ts ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Platform lint — the guardrails that keep a field system honest. A field layer fails quietly: a
3
+ * relation points at a missing id, a visual duplicates text screen readers already read, an element
4
+ * is styled from state it was never registered for. These rules surface that drift as warnings.
5
+ *
6
+ * Each rule is a pure function over the registries (or the DOM, given a resolver), so it is testable
7
+ * without a live page; `lintPlatform` aggregates them over a FieldPlatform. Lint reads — it never
8
+ * mutates state, physics, or the DOM.
9
+ */
10
+ import type { MeasurementRegistry } from './measurement.ts';
11
+ import type { StateRegistry } from './state.ts';
12
+ import type { FeedbackRegistry } from './feedback.ts';
13
+ import type { OverlayRegistry } from './overlays.ts';
14
+ import type { RelationshipRegistry } from './relationships.ts';
15
+ import type { VisualBindingRegistry } from './visual-bindings.ts';
16
+ import type { FrameScheduler } from './schedule.ts';
17
+ import type { Resolver } from './relationships.ts';
18
+ export type LintCode = 'relation-target-missing' | 'visual-orphan' | 'visual-not-hidden' | 'state-unregistered' | 'overlay-without-links' | 'feedback-non-css-var' | 'measurement-off-phase' | 'sink-without-feedback' | 'feedback-vars-unwritten' | 'feedback-lane-inert';
19
+ export interface PlatformLintWarning {
20
+ code: LintCode;
21
+ severity: 'warning' | 'error';
22
+ message: string;
23
+ /** the element at fault, when there is one. */
24
+ element?: Element;
25
+ }
26
+ /** A `[data-field-relation]` whose `data-field-target` does not resolve points at nothing. Pure. */
27
+ export declare function lintRelationTargets(root: ParentNode, resolve: Resolver): PlatformLintWarning[];
28
+ /**
29
+ * A sink that captures but never reports: `data-absorb`/`data-max` on a `sink` body with no
30
+ * `data-feedback` — the engine takes matter in but never writes `--load` back, so the body fills
31
+ * invisibly. This exact gap shipped (a vessel that captured for months while its meter sat empty),
32
+ * which is why it lints. Pure.
33
+ */
34
+ export declare function lintSinkFeedback(root: ParentNode): PlatformLintWarning[];
35
+ /**
36
+ * An element whose inline style *reads* a feedback channel (`var(--d…)`, `var(--load…)`,
37
+ * `var(--field-…)`) while its body never opted into writes (`data-body` without `data-feedback`)
38
+ * styles itself from variables that will never be set for it. Pure.
39
+ */
40
+ export declare function lintFeedbackVarReads(root: ParentNode): PlatformLintWarning[];
41
+ /** An element styled from field state it was never registered to measure is feedback with no source. Pure. */
42
+ export declare function lintStateRegistration(state: StateRegistry, measure: MeasurementRegistry): PlatformLintWarning[];
43
+ /** A relationship overlay with no relationships behind it draws a graph that does not exist. Pure. */
44
+ export declare function lintOverlayLinks(overlays: OverlayRegistry, relationships: RelationshipRegistry): PlatformLintWarning[];
45
+ /**
46
+ * A feedback binding to a metric lane the platform can never produce: `--field-<m>` where `<m>` is a
47
+ * DESIGNED metric (not computed generically, not the supplied-only pair) AND the element declares no
48
+ * `data-field-<m>` to ground it. The lane is bound but will never be written — the element styles
49
+ * itself from a channel that stays at rest. This is the gap the navigation sweep hit
50
+ * (`navigation-current` declares `signal`/`route-strength`); same class as a sink that captures but
51
+ * never reports (`lintSinkFeedback`). Pure.
52
+ */
53
+ export declare function lintInertFeedback(feedback: FeedbackRegistry): PlatformLintWarning[];
54
+ /** Feedback must write CSS custom properties, not ARIA/attributes — state is not accessibility state. Pure. */
55
+ export declare function lintFeedbackVars(feedback: FeedbackRegistry): PlatformLintWarning[];
56
+ /** Off-phase reads recorded by the scheduler (e.g. a measurement during the write phase). */
57
+ export declare function lintSchedulerViolations(scheduler: FrameScheduler): PlatformLintWarning[];
58
+ /** Visual-binding warnings (orphan representations, un-hidden decorative visuals), mapped to lint. */
59
+ export declare function lintVisuals(visuals: VisualBindingRegistry): PlatformLintWarning[];
60
+ export interface PlatformLike {
61
+ root: Element;
62
+ measure: MeasurementRegistry;
63
+ state: StateRegistry;
64
+ feedback: FeedbackRegistry;
65
+ relationships: RelationshipRegistry;
66
+ visuals: VisualBindingRegistry;
67
+ overlays: OverlayRegistry;
68
+ scheduler: FrameScheduler;
69
+ }
70
+ export interface LintOptions {
71
+ /** the subtree to scan for relation targets (defaults to the platform root). */
72
+ root?: ParentNode;
73
+ /** id resolver for relation targets (defaults to document.getElementById). */
74
+ resolve?: Resolver;
75
+ }
76
+ /** Run every platform lint rule over a FieldPlatform and return the aggregated warnings. */
77
+ export declare function lintPlatform(platform: PlatformLike, opts?: LintOptions): PlatformLintWarning[];
78
+ //# sourceMappingURL=lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,MAAM,QAAQ,GAChB,yBAAyB,GACzB,eAAe,GACf,mBAAmB,GACnB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,GACvB,yBAAyB,GACzB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,GAAG,mBAAmB,EAAE,CAa9F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,mBAAmB,EAAE,CAcxE;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,mBAAmB,EAAE,CAe5E;AAED,8GAA8G;AAC9G,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAO/G;AAED,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,oBAAoB,GAAG,mBAAmB,EAAE,CAKtH;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAiBnF;AAED,+GAA+G;AAC/G,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CASlF;AAED,6FAA6F;AAC7F,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,cAAc,GAAG,mBAAmB,EAAE,CAMxF;AAED,sGAAsG;AACtG,wBAAgB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,mBAAmB,EAAE,CAOjF;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,EAAE,oBAAoB,CAAC;IACpC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,GAAE,WAAgB,GAAG,mBAAmB,EAAE,CAclG"}
package/dist/lint.js ADDED
@@ -0,0 +1,153 @@
1
+ import { classifyMetric } from "./metrics.js";
2
+ /** A `[data-field-relation]` whose `data-field-target` does not resolve points at nothing. Pure. */
3
+ export function lintRelationTargets(root, resolve) {
4
+ const out = [];
5
+ root.querySelectorAll('[data-field-relation]').forEach((el) => {
6
+ const target = el.getAttribute('data-field-target');
7
+ if (!target) {
8
+ out.push({ code: 'relation-target-missing', severity: 'warning', element: el, message: `[data-field-relation="${el.getAttribute('data-field-relation')}"] has no data-field-target` });
9
+ return;
10
+ }
11
+ const id = target.startsWith('#') ? target.slice(1) : target;
12
+ if (!resolve(id))
13
+ out.push({ code: 'relation-target-missing', severity: 'warning', element: el, message: `data-field-target "${target}" resolves to no element` });
14
+ });
15
+ return out;
16
+ }
17
+ /**
18
+ * A sink that captures but never reports: `data-absorb`/`data-max` on a `sink` body with no
19
+ * `data-feedback` — the engine takes matter in but never writes `--load` back, so the body fills
20
+ * invisibly. This exact gap shipped (a vessel that captured for months while its meter sat empty),
21
+ * which is why it lints. Pure.
22
+ */
23
+ export function lintSinkFeedback(root) {
24
+ const out = [];
25
+ root.querySelectorAll('[data-absorb], [data-max]').forEach((el) => {
26
+ const tokens = (el.getAttribute('data-body') ?? '').split(/\s+/).filter(Boolean);
27
+ if (!tokens.includes('sink'))
28
+ return;
29
+ if (el.hasAttribute('data-feedback'))
30
+ return;
31
+ out.push({
32
+ code: 'sink-without-feedback',
33
+ severity: 'warning',
34
+ element: el,
35
+ message: `a sink body carries ${el.hasAttribute('data-absorb') ? 'data-absorb' : 'data-max'} but no data-feedback — the engine will capture matter but never write --load back`,
36
+ });
37
+ });
38
+ return out;
39
+ }
40
+ /** The inline-style substrings that mean "this element reads an engine-written channel". */
41
+ const FEEDBACK_VAR_READS = ['var(--d', 'var(--load', 'var(--field-'];
42
+ /**
43
+ * An element whose inline style *reads* a feedback channel (`var(--d…)`, `var(--load…)`,
44
+ * `var(--field-…)`) while its body never opted into writes (`data-body` without `data-feedback`)
45
+ * styles itself from variables that will never be set for it. Pure.
46
+ */
47
+ export function lintFeedbackVarReads(root) {
48
+ const out = [];
49
+ root.querySelectorAll('[data-body]').forEach((el) => {
50
+ if (el.hasAttribute('data-feedback'))
51
+ return;
52
+ const style = el.getAttribute('style') ?? '';
53
+ const read = FEEDBACK_VAR_READS.find((v) => style.includes(v));
54
+ if (read)
55
+ out.push({
56
+ code: 'feedback-vars-unwritten',
57
+ severity: 'warning',
58
+ element: el,
59
+ message: `inline style reads ${read}…) but the body carries no data-feedback — that channel is never written for it`,
60
+ });
61
+ });
62
+ return out;
63
+ }
64
+ /** An element styled from field state it was never registered to measure is feedback with no source. Pure. */
65
+ export function lintStateRegistration(state, measure) {
66
+ const out = [];
67
+ for (const el of state.elements()) {
68
+ if (!measure.has(el))
69
+ out.push({ code: 'state-unregistered', severity: 'warning', element: el, message: 'element holds field state but is not registered for measurement' });
70
+ }
71
+ return out;
72
+ }
73
+ /** A relationship overlay with no relationships behind it draws a graph that does not exist. Pure. */
74
+ export function lintOverlayLinks(overlays, relationships) {
75
+ const hasRelOverlay = overlays.all().some((o) => o.type === 'relationship');
76
+ if (hasRelOverlay && relationships.size === 0)
77
+ return [{ code: 'overlay-without-links', severity: 'warning', message: 'a relationship overlay exists but the RelationshipRegistry has no links' }];
78
+ return [];
79
+ }
80
+ /**
81
+ * A feedback binding to a metric lane the platform can never produce: `--field-<m>` where `<m>` is a
82
+ * DESIGNED metric (not computed generically, not the supplied-only pair) AND the element declares no
83
+ * `data-field-<m>` to ground it. The lane is bound but will never be written — the element styles
84
+ * itself from a channel that stays at rest. This is the gap the navigation sweep hit
85
+ * (`navigation-current` declares `signal`/`route-strength`); same class as a sink that captures but
86
+ * never reports (`lintSinkFeedback`). Pure.
87
+ */
88
+ export function lintInertFeedback(feedback) {
89
+ const out = [];
90
+ for (const { element, vars } of feedback.boundVars()) {
91
+ for (const name of vars) {
92
+ if (!name.startsWith('--field-'))
93
+ continue;
94
+ const metric = name.slice('--field-'.length);
95
+ if (!metric || classifyMetric(metric) !== 'designed')
96
+ continue;
97
+ if (element.hasAttribute(`data-field-${metric}`))
98
+ continue; // grounded by the host — fine
99
+ out.push({
100
+ code: 'feedback-lane-inert',
101
+ severity: 'warning',
102
+ element,
103
+ message: `feedback binds ${name} but "${metric}" is neither computed by the platform nor supplied (data-field-${metric}) on this element — the lane is never written`,
104
+ });
105
+ }
106
+ }
107
+ return out;
108
+ }
109
+ /** Feedback must write CSS custom properties, not ARIA/attributes — state is not accessibility state. Pure. */
110
+ export function lintFeedbackVars(feedback) {
111
+ const out = [];
112
+ for (const { element, vars } of feedback.boundVars()) {
113
+ for (const name of vars) {
114
+ if (!name.startsWith('--'))
115
+ out.push({ code: 'feedback-non-css-var', severity: 'error', element, message: `feedback binding writes "${name}", which is not a CSS custom property (state must not write ARIA/attributes)` });
116
+ }
117
+ }
118
+ return out;
119
+ }
120
+ /** Off-phase reads recorded by the scheduler (e.g. a measurement during the write phase). */
121
+ export function lintSchedulerViolations(scheduler) {
122
+ return scheduler.violations().map((v) => ({
123
+ code: 'measurement-off-phase',
124
+ severity: 'warning',
125
+ message: v.message,
126
+ }));
127
+ }
128
+ /** Visual-binding warnings (orphan representations, un-hidden decorative visuals), mapped to lint. */
129
+ export function lintVisuals(visuals) {
130
+ return visuals.lint().map((w) => ({
131
+ code: (w.code === 'orphan-representation' ? 'visual-orphan' : 'visual-not-hidden'),
132
+ severity: w.severity,
133
+ message: w.message,
134
+ element: w.visual,
135
+ }));
136
+ }
137
+ /** Run every platform lint rule over a FieldPlatform and return the aggregated warnings. */
138
+ export function lintPlatform(platform, opts = {}) {
139
+ const root = opts.root ?? platform.root;
140
+ const resolve = opts.resolve ?? ((id) => (typeof document !== 'undefined' ? document.getElementById(id) : null));
141
+ return [
142
+ ...lintRelationTargets(root, resolve),
143
+ ...lintSinkFeedback(root),
144
+ ...lintFeedbackVarReads(root),
145
+ ...lintStateRegistration(platform.state, platform.measure),
146
+ ...lintOverlayLinks(platform.overlays, platform.relationships),
147
+ ...lintFeedbackVars(platform.feedback),
148
+ ...lintInertFeedback(platform.feedback),
149
+ ...lintVisuals(platform.visuals),
150
+ ...lintSchedulerViolations(platform.scheduler),
151
+ ];
152
+ }
153
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * MeasurementRegistry — frame-stable geometry. The native primitive Fundamental wishes existed: read
3
+ * every registered element's box once per frame and hand back an immutable snapshot, so the rest of
4
+ * the system works from one consistent set of rectangles instead of each force calling
5
+ * `getBoundingClientRect()` whenever it likes (which risks layout thrash).
6
+ *
7
+ * Strict read-phase: `measure()` only reads. Feedback (writes) happens in a separate phase. Default
8
+ * measurement is the host box (Shadow-DOM safe); a `getRect` override feeds closed roots / inner cores.
9
+ */
10
+ import type { CoordinateSpace, FieldMeasurement, Viewport } from './types.ts';
11
+ export interface MeasureRegistration {
12
+ /** semantic role (body, relationship endpoint, container, visual…). */
13
+ role?: string;
14
+ coordinateSpace?: CoordinateSpace;
15
+ /** rectangle provider when the body box is not the element box (closed roots, inner cores). */
16
+ getRect?: () => DOMRect;
17
+ }
18
+ export declare class MeasurementRegistry {
19
+ private readonly entries;
20
+ private snapshot;
21
+ private guard;
22
+ /**
23
+ * Install a phase guard (the FrameScheduler supplies one via `readGuard()`). It is consulted
24
+ * before reading layout, so a measurement requested in the write phase is caught. Decoupled by
25
+ * design: measurement never imports the scheduler. Pass `null` to remove the guard.
26
+ */
27
+ setPhaseGuard(guard: ((op: string) => void) | null): void;
28
+ /** Register an element for measurement (idempotent — re-registering refreshes its options). */
29
+ register(element: Element, opts?: MeasureRegistration): void;
30
+ unregister(element: Element): void;
31
+ /** Whether an element is registered for measurement (does not require a measure pass). */
32
+ has(element: Element): boolean;
33
+ get size(): number;
34
+ /**
35
+ * Read every registered element's geometry once and return the immutable snapshot. Disconnected
36
+ * elements are pruned. `now`/`viewport` are injectable so this is testable without a live DOM.
37
+ */
38
+ measure(now?: number, viewport?: Viewport): readonly FieldMeasurement[];
39
+ /** The most recent snapshot (without re-reading layout). */
40
+ last(): readonly FieldMeasurement[];
41
+ /** The latest measurement for one element, if present. */
42
+ for(element: Element): FieldMeasurement | undefined;
43
+ }
44
+ //# sourceMappingURL=measurement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measurement.d.ts","sourceRoot":"","sources":["../src/measurement.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAa,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEzF,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;CACzB;AAkCD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,KAAK,CAAuC;IAEpD;;;;OAIG;IACH,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAIzD,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,mBAAwB,GAAG,IAAI;IAIhE,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC,0FAA0F;IAC1F,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAI9B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;OAGG;IACH,OAAO,CAAC,GAAG,SAAI,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,gBAAgB,EAAE;IAwBlE,4DAA4D;IAC5D,IAAI,IAAI,SAAS,gBAAgB,EAAE;IAInC,0DAA0D;IAC1D,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS;CAGpD"}
@@ -0,0 +1,95 @@
1
+ function toFieldRect(r) {
2
+ const left = r.left;
3
+ const top = r.top;
4
+ const width = r.width;
5
+ const height = r.height;
6
+ return {
7
+ x: left,
8
+ y: top,
9
+ width,
10
+ height,
11
+ cx: left + width / 2,
12
+ cy: top + height / 2,
13
+ top,
14
+ right: left + width,
15
+ bottom: top + height,
16
+ left,
17
+ };
18
+ }
19
+ /** Overlap fraction of a rect within the viewport box ∈ [0,1]. */
20
+ function visibilityRatio(r, vp) {
21
+ const area = r.width * r.height;
22
+ if (area <= 0)
23
+ return 0;
24
+ const ox = Math.max(0, Math.min(r.right, vp.width) - Math.max(r.left, 0));
25
+ const oy = Math.max(0, Math.min(r.bottom, vp.height) - Math.max(r.top, 0));
26
+ return Math.min(1, (ox * oy) / area);
27
+ }
28
+ export class MeasurementRegistry {
29
+ entries = new Map();
30
+ snapshot = [];
31
+ guard = null;
32
+ /**
33
+ * Install a phase guard (the FrameScheduler supplies one via `readGuard()`). It is consulted
34
+ * before reading layout, so a measurement requested in the write phase is caught. Decoupled by
35
+ * design: measurement never imports the scheduler. Pass `null` to remove the guard.
36
+ */
37
+ setPhaseGuard(guard) {
38
+ this.guard = guard;
39
+ }
40
+ /** Register an element for measurement (idempotent — re-registering refreshes its options). */
41
+ register(element, opts = {}) {
42
+ this.entries.set(element, { element, ...opts });
43
+ }
44
+ unregister(element) {
45
+ this.entries.delete(element);
46
+ }
47
+ /** Whether an element is registered for measurement (does not require a measure pass). */
48
+ has(element) {
49
+ return this.entries.has(element);
50
+ }
51
+ get size() {
52
+ return this.entries.size;
53
+ }
54
+ /**
55
+ * Read every registered element's geometry once and return the immutable snapshot. Disconnected
56
+ * elements are pruned. `now`/`viewport` are injectable so this is testable without a live DOM.
57
+ */
58
+ measure(now = 0, viewport) {
59
+ this.guard?.('measure'); // read-phase discipline: reading layout off-phase thrashes
60
+ const vp = viewport ?? defaultViewport();
61
+ const out = [];
62
+ for (const [el, e] of this.entries) {
63
+ if (!el.isConnected) {
64
+ this.entries.delete(el);
65
+ continue;
66
+ }
67
+ const rect = toFieldRect(e.getRect ? e.getRect() : el.getBoundingClientRect());
68
+ const ratio = visibilityRatio(rect, vp);
69
+ out.push({
70
+ element: el,
71
+ rect,
72
+ visible: ratio > 0,
73
+ visibilityRatio: ratio,
74
+ coordinateSpace: e.coordinateSpace ?? 'viewport',
75
+ timestamp: now,
76
+ });
77
+ }
78
+ this.snapshot = out;
79
+ return out;
80
+ }
81
+ /** The most recent snapshot (without re-reading layout). */
82
+ last() {
83
+ return this.snapshot;
84
+ }
85
+ /** The latest measurement for one element, if present. */
86
+ for(element) {
87
+ return this.snapshot.find((m) => m.element === element);
88
+ }
89
+ }
90
+ function defaultViewport() {
91
+ if (typeof window !== 'undefined')
92
+ return { width: window.innerWidth, height: window.innerHeight };
93
+ return { width: 0, height: 0 };
94
+ }
95
+ //# sourceMappingURL=measurement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measurement.js","sourceRoot":"","sources":["../src/measurement.ts"],"names":[],"mappings":"AAuBA,SAAS,WAAW,CAAC,CAAyE;IAC5F,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;IAClB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;IACtB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACxB,OAAO;QACL,CAAC,EAAE,IAAI;QACP,CAAC,EAAE,GAAG;QACN,KAAK;QACL,MAAM;QACN,EAAE,EAAE,IAAI,GAAG,KAAK,GAAG,CAAC;QACpB,EAAE,EAAE,GAAG,GAAG,MAAM,GAAG,CAAC;QACpB,GAAG;QACH,KAAK,EAAE,IAAI,GAAG,KAAK;QACnB,MAAM,EAAE,GAAG,GAAG,MAAM;QACpB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,SAAS,eAAe,CAAC,CAAY,EAAE,EAAY;IACjD,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACxB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,OAAO,mBAAmB;IACb,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,QAAQ,GAAgC,EAAE,CAAC;IAC3C,KAAK,GAAkC,IAAI,CAAC;IAEpD;;;;OAIG;IACH,aAAa,CAAC,KAAoC;QAChD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,+FAA+F;IAC/F,QAAQ,CAAC,OAAgB,EAAE,OAA4B,EAAE;QACvD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,UAAU,CAAC,OAAgB;QACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,0FAA0F;IAC1F,GAAG,CAAC,OAAgB;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,QAAmB;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,2DAA2D;QACpF,MAAM,EAAE,GAAG,QAAQ,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,GAAG,GAAuB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;gBACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,CAAC;YAC/E,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxC,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,EAAE;gBACX,IAAI;gBACJ,OAAO,EAAE,KAAK,GAAG,CAAC;gBAClB,eAAe,EAAE,KAAK;gBACtB,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,UAAU;gBAChD,SAAS,EAAE,GAAG;aACf,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;QACpB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,4DAA4D;IAC5D,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,0DAA0D;IAC1D,GAAG,CAAC,OAAgB;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC1D,CAAC;CACF;AAED,SAAS,eAAe;IACtB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;IACnG,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,70 @@
1
+ export declare const METRIC_KINDS: readonly ["attention", "memory", "coherence", "entropy", "pressure", "confidence", "risk", "recency", "priority"];
2
+ export type MetricKind = (typeof METRIC_KINDS)[number];
3
+ /** The lanes `computeMetrics` produces generically every frame (everything except the supplied-only pair). */
4
+ export declare const COMPUTED_METRICS: readonly ["attention", "memory", "coherence", "entropy", "pressure", "recency", "priority"];
5
+ /** Metrics the engine NEVER invents — present ONLY when the host supplies them (`data-field-<m>`). */
6
+ export declare const SUPPLIED_ONLY_METRICS: readonly ["confidence", "risk"];
7
+ /**
8
+ * How a metric lane is produced:
9
+ * - `computed` — the generic pipeline writes it every frame (proximity/engagement/relations/age).
10
+ * - `supplied-only`— confidence/risk; the engine has no evidence, so it's written ONLY when supplied.
11
+ * - `designed` — a semantic lane (signal, route-strength, sync, …) the HOST must supply via
12
+ * `data-field-<m>` (or a domain model). With neither a supply nor a computed source
13
+ * its `--field-<m>` is **inert** — declared but never written. This is the gap the
14
+ * nav sweep hit (navigation-current's `signal`/`route-strength`); `lintInertFeedback`
15
+ * surfaces it, the same way `lintSinkFeedback` catches a capturing-but-silent sink.
16
+ */
17
+ export type MetricSupport = 'computed' | 'supplied-only' | 'designed';
18
+ /** Classify a metric name by how its `--field-<name>` lane is produced (see {@link MetricSupport}). */
19
+ export declare function classifyMetric(name: string): MetricSupport;
20
+ /**
21
+ * One frame of computed metrics: every lane is a number except `confidence` and `risk`, which are
22
+ * present ONLY when the host supplies them (the engine never invents them — see `computeMetrics`).
23
+ */
24
+ export type ComputedMetrics = Record<Exclude<MetricKind, 'confidence' | 'risk'>, number> & {
25
+ confidence?: number;
26
+ risk?: number;
27
+ };
28
+ export interface MetricInputs {
29
+ /** 0..1 — how centred the element is in the viewport (1 = at centre). */
30
+ proximity: number;
31
+ /** 0..1 — fraction of the element visible. */
32
+ visible: number;
33
+ /** hover / focus / active. */
34
+ engaged: boolean;
35
+ /** frames since the last compute (usually 1). */
36
+ dtFrames: number;
37
+ /** resolved relationships touching this element. */
38
+ relResolved: number;
39
+ /** total relationships touching this element. */
40
+ relTotal: number;
41
+ /** contradicting relationships touching this element. */
42
+ relConflict: number;
43
+ /** metric values supplied by the recipe/data (data-field-<metric>). */
44
+ supplied: Partial<Record<MetricKind, number>>;
45
+ /** prior frame's metric values. */
46
+ prev: Partial<Record<MetricKind, number>>;
47
+ }
48
+ /** The default half-life for a grounded recency lane: 7 days (override per element with `data-field-halflife`, in ms). */
49
+ export declare const DEFAULT_RECENCY_HALF_LIFE_MS: number;
50
+ /** The slice of Element the derivation reads — structural, so this module stays DOM-free and node-testable. */
51
+ interface AttrReader {
52
+ getAttribute(name: string): string | null;
53
+ }
54
+ /**
55
+ * Parse a declared world timestamp (`data-field-at`): epoch milliseconds or an ISO 8601
56
+ * string. Invalid or absent values read as absent (`undefined`) — never NaN.
57
+ */
58
+ export declare function parseFieldAt(value: string | null): number | undefined;
59
+ /**
60
+ * Derive the grounded recency for an element declaring `data-field-at` — `freshness(at, now,
61
+ * halfLife)` with the half-life from `data-field-halflife` (ms, default 7 days). Returns
62
+ * `undefined` when no valid timestamp is declared, so the caller falls through to the
63
+ * interaction-inferred path. `nowMs` is the frame's wall-clock instant, sampled ONCE per
64
+ * frame by the caller (`apply-recipe.ts`) — never per-element `Date.now()`.
65
+ */
66
+ export declare function groundedRecency(el: AttrReader, nowMs: number): number | undefined;
67
+ /** Compute one frame of metrics from inputs (pure). Supplied values override/add to computed ones. */
68
+ export declare function computeMetrics(inp: MetricInputs): ComputedMetrics;
69
+ export {};
70
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,YAAY,mHAUf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6FAQnB,CAAC;AACX,sGAAsG;AACtG,eAAO,MAAM,qBAAqB,iCAAkC,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,eAAe,GAAG,UAAU,CAAC;AAEtE,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAI1D;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,GAAG,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAIF,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,mCAAmC;IACnC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3C;AAWD,0HAA0H;AAC1H,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAE3D,+GAA+G;AAC/G,UAAU,UAAU;IAClB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAMrE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKjF;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,eAAe,CA4BjE"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The platform metric library. Makes the metrics a recipe lists real enough to be written and
3
+ * inspected — typed state values, not prose. The math here is PURE (node-testable); the per-frame
4
+ * wiring onto a `FieldPlatform` lives in `apply-recipe.ts`.
5
+ *
6
+ * Honest about provenance:
7
+ * - COMPUTED generically: attention / memory / recency (proximity + engagement + decay) and
8
+ * coherence / entropy / pressure / priority (from relationship resolution + age). Any of these may
9
+ * be overridden by a supplied value.
10
+ * - SUPPLIED-ONLY: `confidence` and `risk` are external judgments the engine has no evidence for.
11
+ * They are present ONLY when the host supplies them (`data-field-<metric>` / recipe options / a
12
+ * domain model). Confidence is NEVER inferred from relationship presence — a citation is not
13
+ * certainty. Risk is NEVER defaulted to 0 — "no risk" is a claim, not a safe blank. (Relationship
14
+ * resolution is a separate signal, see `apply-recipe.ts`, not confidence.)
15
+ * - GROUNDED by world time: a declared `data-field-at` timestamp GROUNDS the recency lane —
16
+ * recency becomes `freshness(at, now, halfLife)` (the core temporal kernel) instead of the
17
+ * interaction-inferred ease. See `groundedRecency` below; the wiring is `apply-recipe.ts`.
18
+ */
19
+ import { freshness } from '@fundamental-engine/core';
20
+ export const METRIC_KINDS = [
21
+ 'attention',
22
+ 'memory',
23
+ 'coherence',
24
+ 'entropy',
25
+ 'pressure',
26
+ 'confidence',
27
+ 'risk',
28
+ 'recency',
29
+ 'priority',
30
+ ];
31
+ /** The lanes `computeMetrics` produces generically every frame (everything except the supplied-only pair). */
32
+ export const COMPUTED_METRICS = [
33
+ 'attention',
34
+ 'memory',
35
+ 'coherence',
36
+ 'entropy',
37
+ 'pressure',
38
+ 'recency',
39
+ 'priority',
40
+ ];
41
+ /** Metrics the engine NEVER invents — present ONLY when the host supplies them (`data-field-<m>`). */
42
+ export const SUPPLIED_ONLY_METRICS = ['confidence', 'risk'];
43
+ /** Classify a metric name by how its `--field-<name>` lane is produced (see {@link MetricSupport}). */
44
+ export function classifyMetric(name) {
45
+ if (COMPUTED_METRICS.includes(name))
46
+ return 'computed';
47
+ if (SUPPLIED_ONLY_METRICS.includes(name))
48
+ return 'supplied-only';
49
+ return 'designed';
50
+ }
51
+ const clamp01 = (n) => (n < 0 ? 0 : n > 1 ? 1 : n);
52
+ const prevOf = (inp, k) => inp.prev[k] ?? 0;
53
+ // ── world time: the declared-timestamp ground for the recency lane ──────────────────────
54
+ // The metric pipeline runs on two clocks. WITHOUT a declared timestamp, recency is
55
+ // EXPERIENTIAL time — inferred from interaction (the eased computeMetrics behavior below,
56
+ // unchanged). WITH one, the element carries WORLD TIME (`data-field-at` — when the data
57
+ // itself last happened), and that declared timestamp GROUNDS the recency lane:
58
+ // recency = freshness(at, now, halfLife), the core temporal kernel's exponential newness.
59
+ /** The default half-life for a grounded recency lane: 7 days (override per element with `data-field-halflife`, in ms). */
60
+ export const DEFAULT_RECENCY_HALF_LIFE_MS = 7 * 86_400_000;
61
+ /**
62
+ * Parse a declared world timestamp (`data-field-at`): epoch milliseconds or an ISO 8601
63
+ * string. Invalid or absent values read as absent (`undefined`) — never NaN.
64
+ */
65
+ export function parseFieldAt(value) {
66
+ if (value == null || value.trim() === '')
67
+ return undefined;
68
+ const n = Number(value);
69
+ if (Number.isFinite(n))
70
+ return n;
71
+ const t = Date.parse(value);
72
+ return Number.isNaN(t) ? undefined : t;
73
+ }
74
+ /**
75
+ * Derive the grounded recency for an element declaring `data-field-at` — `freshness(at, now,
76
+ * halfLife)` with the half-life from `data-field-halflife` (ms, default 7 days). Returns
77
+ * `undefined` when no valid timestamp is declared, so the caller falls through to the
78
+ * interaction-inferred path. `nowMs` is the frame's wall-clock instant, sampled ONCE per
79
+ * frame by the caller (`apply-recipe.ts`) — never per-element `Date.now()`.
80
+ */
81
+ export function groundedRecency(el, nowMs) {
82
+ const at = parseFieldAt(el.getAttribute('data-field-at'));
83
+ if (at == null)
84
+ return undefined;
85
+ const hl = Number(el.getAttribute('data-field-halflife'));
86
+ return freshness(at, nowMs, Number.isFinite(hl) && hl > 0 ? hl : DEFAULT_RECENCY_HALF_LIFE_MS);
87
+ }
88
+ /** Compute one frame of metrics from inputs (pure). Supplied values override/add to computed ones. */
89
+ export function computeMetrics(inp) {
90
+ const dt = Math.max(1, inp.dtFrames);
91
+ const attention = clamp01(inp.proximity * inp.visible * (inp.engaged ? 1.3 : 1));
92
+ const memory = clamp01(prevOf(inp, 'memory') + (attention > 0.6 ? 0.006 * dt : -0.0008 * dt));
93
+ const recency = inp.engaged || attention > 0.6 ? 1 : clamp01(prevOf(inp, 'recency') - 0.003 * dt);
94
+ const resolvedRatio = inp.relTotal > 0 ? inp.relResolved / inp.relTotal : 0;
95
+ const conflictRatio = inp.relTotal > 0 ? inp.relConflict / inp.relTotal : 0;
96
+ const coherence = inp.relTotal > 0 ? clamp01(resolvedRatio - conflictRatio) : prevOf(inp, 'coherence');
97
+ const entropy = inp.relTotal > 0 ? clamp01(conflictRatio + (1 - resolvedRatio) * 0.5) : prevOf(inp, 'entropy');
98
+ // COMPUTED lanes only. `confidence` and `risk` are intentionally absent: the engine has no evidence
99
+ // for a claim's truth or its danger, so they stay unset unless the host supplies them below. The old
100
+ // `confidence: relTotal > 0 ? resolvedRatio : 0` meant "any citation ⇒ fully confident", and the old
101
+ // `risk: 0` meant "everything is safe by default" — both the wrong default for a trust/ops surface.
102
+ const computed = {
103
+ attention,
104
+ memory,
105
+ recency,
106
+ coherence,
107
+ entropy,
108
+ pressure: clamp01(entropy * 0.6 + (1 - recency) * 0.4),
109
+ priority: attention,
110
+ };
111
+ // supplied values win — these lanes (incl. confidence) are recipe/data-driven, not invented
112
+ for (const k of METRIC_KINDS) {
113
+ const s = inp.supplied[k];
114
+ if (s != null && Number.isFinite(s))
115
+ computed[k] = clamp01(s);
116
+ }
117
+ return computed;
118
+ }
119
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}