@cavelang/core 0.20.0 → 0.27.3

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.
@@ -16,6 +16,7 @@ export * as Entity from './entity.ts';
16
16
  export * as Key from './key.ts';
17
17
  export * as Multiplier from './multiplier.ts';
18
18
  export * as Tag from './tag.ts';
19
+ export * as Time from './time.ts';
19
20
  export * as Uncertainty from './uncertainty.ts';
20
21
  export * as Uuidv7 from './uuidv7.ts';
21
22
  export * as Value from './value.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
package/dist/src/index.js CHANGED
@@ -16,6 +16,7 @@ export * as Entity from "./entity.js";
16
16
  export * as Key from "./key.js";
17
17
  export * as Multiplier from "./multiplier.js";
18
18
  export * as Tag from "./tag.js";
19
+ export * as Time from "./time.js";
19
20
  export * as Uncertainty from "./uncertainty.js";
20
21
  export * as Uuidv7 from "./uuidv7.js";
21
22
  export * as Value from "./value.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Valid time — periods, ranges, instants (spec §32).
3
+ *
4
+ * A date-like context names a calendar *period*, read as a UTC interval
5
+ * `[start, end)`: `@2025` the year, `@2026-04` the month, `@2026-04-10`
6
+ * the day, `@2026-Q1` / `@2026-H2` the quarter/half, `@2026-W15` the ISO
7
+ * week. A *range* context joins two points with `..` — `@2025..2028`,
8
+ * open-ended `@..2025` / `@2026..` — and covers whole periods at both
9
+ * ends. Ranges are lexically ordinary contexts (spec §6.1 already admits
10
+ * `.` in context atoms); this module is the semantic pass that reads
11
+ * them, exactly like `REVERSE` declarations are ordinary claims read
12
+ * semantically (spec §5.5).
13
+ *
14
+ * These are *valid-time* semantics — when a claim applies in the world —
15
+ * independent of transaction time (when the store learned it, spec §9.1,
16
+ * §12.3). `cave query --at` filters and interpolates on this axis.
17
+ */
18
+ /** Calendar period as a UTC interval `[start, end)`, in ms since epoch. */
19
+ export type Period = {
20
+ readonly start: number;
21
+ readonly end: number;
22
+ };
23
+ export type t = Period;
24
+ /** How a context reads as time: a point period, or a `..` range. */
25
+ export type TimeContext = {
26
+ readonly kind: 'point';
27
+ readonly period: Period;
28
+ } | {
29
+ readonly kind: 'range';
30
+ readonly start?: Period;
31
+ readonly end?: Period;
32
+ };
33
+ /**
34
+ * Parses a date-like time point as the calendar period it names:
35
+ * `2025`, `2026-04`, `2026-04-10`, `2026-Q1`, `2026-H2`, `2026-W15`.
36
+ * @returns `undefined` when `text` is not a time point (including
37
+ * out-of-calendar dates like `2026-02-30`).
38
+ */
39
+ export declare const parsePeriod: (text: string) => undefined | Period;
40
+ /**
41
+ * Parses a `A..B` / `..B` / `A..` time range. The end point of a closed
42
+ * range may abbreviate by dropping *leading* numeric segments, inherited
43
+ * from the start point (spec §32.2): `2026-04-10..04-11` reads as
44
+ * `2026-04-10..2026-04-11`, `2026-04-10..11` likewise. `Q`/`H`/`W`
45
+ * points are always written in full. A closed range must be non-empty
46
+ * (`2028..2025` is not a time range).
47
+ * @returns `undefined` when `text` is not a range.
48
+ */
49
+ export declare const parseRange: (text: string) => undefined | {
50
+ start?: Period;
51
+ end?: Period;
52
+ };
53
+ /**
54
+ * Reads a context as time, when it is one (spec §32.2): a bare or
55
+ * `time:`-prefixed date-like point or `..` range. Every other context —
56
+ * `production`, `src:filing`, `auth.ts:42` — is opaque and returns
57
+ * `undefined`; a context that fails to parse as time is opaque too, the
58
+ * robust-extraction default (spec §1.6).
59
+ */
60
+ export declare const ofContext: (context: string) => undefined | TimeContext;
61
+ /**
62
+ * Parses a query anchor into an instant (ms since epoch): a date-like
63
+ * period reads as its *start* instant (`2026` is 2026-01-01T00:00:00Z —
64
+ * name the finer period to anchor inside one), a `T` timestamp reads
65
+ * exactly. @returns `undefined` when `text` is neither.
66
+ */
67
+ export declare const parseInstant: (text: string) => undefined | number;
68
+ /** @returns whether a time context covers the instant (spec §32.4). */
69
+ export declare const covers: (context: TimeContext, instant: number) => boolean;
70
+ /**
71
+ * Whether a claim with these contexts applies at the instant
72
+ * (spec §32.4): timeless claims — no time context — always apply; a
73
+ * time-anchored claim applies when *any* of its time contexts covers
74
+ * the instant. Opaque contexts never participate.
75
+ */
76
+ export declare const appliesAt: (contexts: readonly string[], instant: number) => boolean;
77
+ /**
78
+ * The single closed range among a claim's contexts — the interval a
79
+ * trajectory value interpolates over (spec §32.3). @returns `undefined`
80
+ * when there is none, or more than one (ambiguous — no interpolation).
81
+ */
82
+ export declare const closedRangeOf: (contexts: readonly string[]) => undefined | {
83
+ start: Period;
84
+ end: Period;
85
+ };
86
+ /**
87
+ * Where an instant sits along a closed range, as the interpolation
88
+ * fraction in [0, 1] (spec §32.3): endpoint values anchor at the *start*
89
+ * instants of the periods that name them — `20B -> 40B @2025..2028` is
90
+ * 20B at 2025-01-01 and 40B at 2028-01-01 — and the fraction clamps to 1
91
+ * through the end period's tail ("40B *in* 2028" holds all of 2028).
92
+ */
93
+ export declare const fractionAt: (range: {
94
+ start: Period;
95
+ end: Period;
96
+ }, instant: number) => number;
97
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,CAAC,GAAG,MAAM,CAAA;AAEtB,oEAAoE;AACpE,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAgB9E;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,SAAS,GAAG,MAwCtD,CAAA;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,SAAS,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAiCnF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,KAAG,SAAS,GAAG,WAQvD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,SAAS,GAAG,MAUvD,CAAA;AAED,uEAAuE;AACvE,eAAO,MAAM,MAAM,GAAI,SAAS,WAAW,EAAE,SAAS,MAAM,KAAG,OAIH,CAAA;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,UAAU,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,KAAG,OAaxE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,SAAS,MAAM,EAAE,KAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAanG,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,SAAS,MAAM,KAAG,MAMnF,CAAA"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Valid time — periods, ranges, instants (spec §32).
3
+ *
4
+ * A date-like context names a calendar *period*, read as a UTC interval
5
+ * `[start, end)`: `@2025` the year, `@2026-04` the month, `@2026-04-10`
6
+ * the day, `@2026-Q1` / `@2026-H2` the quarter/half, `@2026-W15` the ISO
7
+ * week. A *range* context joins two points with `..` — `@2025..2028`,
8
+ * open-ended `@..2025` / `@2026..` — and covers whole periods at both
9
+ * ends. Ranges are lexically ordinary contexts (spec §6.1 already admits
10
+ * `.` in context atoms); this module is the semantic pass that reads
11
+ * them, exactly like `REVERSE` declarations are ordinary claims read
12
+ * semantically (spec §5.5).
13
+ *
14
+ * These are *valid-time* semantics — when a claim applies in the world —
15
+ * independent of transaction time (when the store learned it, spec §9.1,
16
+ * §12.3). `cave query --at` filters and interpolates on this axis.
17
+ */
18
+ const dayMs = 86_400_000;
19
+ const utcDay = (year, month, day) => Date.UTC(year, month - 1, day);
20
+ /** Monday starting ISO week `week` of `year` (ISO 8601: week 1 contains Jan 4). */
21
+ const isoWeekStart = (year, week) => {
22
+ const jan4 = utcDay(year, 1, 4);
23
+ const monday = jan4 - ((new Date(jan4).getUTCDay() + 6) % 7) * dayMs;
24
+ return monday + (week - 1) * 7 * dayMs;
25
+ };
26
+ const periodRe = /^(\d{4})(?:-(?:Q([1-4])|H([1-2])|W(\d{1,2})|(\d{2})(?:-(\d{2}))?))?$/;
27
+ /**
28
+ * Parses a date-like time point as the calendar period it names:
29
+ * `2025`, `2026-04`, `2026-04-10`, `2026-Q1`, `2026-H2`, `2026-W15`.
30
+ * @returns `undefined` when `text` is not a time point (including
31
+ * out-of-calendar dates like `2026-02-30`).
32
+ */
33
+ export const parsePeriod = (text) => {
34
+ const match = periodRe.exec(text);
35
+ if (!match) {
36
+ return undefined;
37
+ }
38
+ const year = Number(match[1]);
39
+ const [, , quarter, half, week, month, day] = match;
40
+ if (quarter !== undefined) {
41
+ const q = Number(quarter);
42
+ return { start: utcDay(year, (q - 1) * 3 + 1, 1), end: utcDay(year, q * 3 + 1, 1) };
43
+ }
44
+ if (half !== undefined) {
45
+ const h = Number(half);
46
+ return { start: utcDay(year, (h - 1) * 6 + 1, 1), end: utcDay(year, h * 6 + 1, 1) };
47
+ }
48
+ if (week !== undefined) {
49
+ const w = Number(week);
50
+ if (w < 1 || w > 53) {
51
+ return undefined;
52
+ }
53
+ const start = isoWeekStart(year, w);
54
+ return { start, end: start + 7 * dayMs };
55
+ }
56
+ if (month !== undefined) {
57
+ const m = Number(month);
58
+ if (m < 1 || m > 12) {
59
+ return undefined;
60
+ }
61
+ if (day !== undefined) {
62
+ const d = Number(day);
63
+ const start = utcDay(year, m, d);
64
+ const date = new Date(start);
65
+ if (date.getUTCMonth() !== m - 1 || date.getUTCDate() !== d) {
66
+ return undefined;
67
+ }
68
+ return { start, end: start + dayMs };
69
+ }
70
+ return { start: utcDay(year, m, 1), end: utcDay(year, m + 1, 1) };
71
+ }
72
+ return { start: utcDay(year, 1, 1), end: utcDay(year + 1, 1, 1) };
73
+ };
74
+ const numericPointRe = /^[\d-]+$/;
75
+ /**
76
+ * Parses a `A..B` / `..B` / `A..` time range. The end point of a closed
77
+ * range may abbreviate by dropping *leading* numeric segments, inherited
78
+ * from the start point (spec §32.2): `2026-04-10..04-11` reads as
79
+ * `2026-04-10..2026-04-11`, `2026-04-10..11` likewise. `Q`/`H`/`W`
80
+ * points are always written in full. A closed range must be non-empty
81
+ * (`2028..2025` is not a time range).
82
+ * @returns `undefined` when `text` is not a range.
83
+ */
84
+ export const parseRange = (text) => {
85
+ const at = text.indexOf('..');
86
+ if (at === -1 || text.indexOf('..', at + 2) !== -1) {
87
+ return undefined;
88
+ }
89
+ const leftText = text.slice(0, at);
90
+ const rightText = text.slice(at + 2);
91
+ if (leftText === '' && rightText === '') {
92
+ return undefined;
93
+ }
94
+ if (leftText === '') {
95
+ const end = parsePeriod(rightText);
96
+ return end === undefined ? undefined : { end };
97
+ }
98
+ const start = parsePeriod(leftText);
99
+ if (start === undefined) {
100
+ return undefined;
101
+ }
102
+ if (rightText === '') {
103
+ return { start };
104
+ }
105
+ let end = parsePeriod(rightText);
106
+ if (end === undefined && numericPointRe.test(leftText) && numericPointRe.test(rightText)) {
107
+ const leftSegments = leftText.split('-');
108
+ const rightSegments = rightText.split('-');
109
+ if (rightSegments.length < leftSegments.length) {
110
+ end = parsePeriod([...leftSegments.slice(0, leftSegments.length - rightSegments.length), ...rightSegments].join('-'));
111
+ }
112
+ }
113
+ if (end === undefined || end.end <= start.start) {
114
+ return undefined;
115
+ }
116
+ return { start, end };
117
+ };
118
+ /**
119
+ * Reads a context as time, when it is one (spec §32.2): a bare or
120
+ * `time:`-prefixed date-like point or `..` range. Every other context —
121
+ * `production`, `src:filing`, `auth.ts:42` — is opaque and returns
122
+ * `undefined`; a context that fails to parse as time is opaque too, the
123
+ * robust-extraction default (spec §1.6).
124
+ */
125
+ export const ofContext = (context) => {
126
+ const body = context.startsWith('time:') ? context.slice('time:'.length) : context;
127
+ if (body.includes('..')) {
128
+ const range = parseRange(body);
129
+ return range === undefined ? undefined : { kind: 'range', ...range };
130
+ }
131
+ const period = parsePeriod(body);
132
+ return period === undefined ? undefined : { kind: 'point', period };
133
+ };
134
+ /**
135
+ * Parses a query anchor into an instant (ms since epoch): a date-like
136
+ * period reads as its *start* instant (`2026` is 2026-01-01T00:00:00Z —
137
+ * name the finer period to anchor inside one), a `T` timestamp reads
138
+ * exactly. @returns `undefined` when `text` is neither.
139
+ */
140
+ export const parseInstant = (text) => {
141
+ const period = parsePeriod(text);
142
+ if (period !== undefined) {
143
+ return period.start;
144
+ }
145
+ if (!text.includes('T')) {
146
+ return undefined;
147
+ }
148
+ const parsed = Date.parse(text);
149
+ return Number.isNaN(parsed) ? undefined : parsed;
150
+ };
151
+ /** @returns whether a time context covers the instant (spec §32.4). */
152
+ export const covers = (context, instant) => context.kind === 'point' ?
153
+ instant >= context.period.start && instant < context.period.end :
154
+ (context.start === undefined || instant >= context.start.start) &&
155
+ (context.end === undefined || instant < context.end.end);
156
+ /**
157
+ * Whether a claim with these contexts applies at the instant
158
+ * (spec §32.4): timeless claims — no time context — always apply; a
159
+ * time-anchored claim applies when *any* of its time contexts covers
160
+ * the instant. Opaque contexts never participate.
161
+ */
162
+ export const appliesAt = (contexts, instant) => {
163
+ let anchored = false;
164
+ for (const context of contexts) {
165
+ const time = ofContext(context);
166
+ if (time === undefined) {
167
+ continue;
168
+ }
169
+ anchored = true;
170
+ if (covers(time, instant)) {
171
+ return true;
172
+ }
173
+ }
174
+ return !anchored;
175
+ };
176
+ /**
177
+ * The single closed range among a claim's contexts — the interval a
178
+ * trajectory value interpolates over (spec §32.3). @returns `undefined`
179
+ * when there is none, or more than one (ambiguous — no interpolation).
180
+ */
181
+ export const closedRangeOf = (contexts) => {
182
+ let found;
183
+ for (const context of contexts) {
184
+ const time = ofContext(context);
185
+ if (time === undefined || time.kind !== 'range' || time.start === undefined || time.end === undefined) {
186
+ continue;
187
+ }
188
+ if (found !== undefined) {
189
+ return undefined;
190
+ }
191
+ found = { start: time.start, end: time.end };
192
+ }
193
+ return found;
194
+ };
195
+ /**
196
+ * Where an instant sits along a closed range, as the interpolation
197
+ * fraction in [0, 1] (spec §32.3): endpoint values anchor at the *start*
198
+ * instants of the periods that name them — `20B -> 40B @2025..2028` is
199
+ * 20B at 2025-01-01 and 40B at 2028-01-01 — and the fraction clamps to 1
200
+ * through the end period's tail ("40B *in* 2028" holds all of 2028).
201
+ */
202
+ export const fractionAt = (range, instant) => {
203
+ const span = range.end.start - range.start.start;
204
+ if (span <= 0) {
205
+ return 1;
206
+ }
207
+ return Math.min(1, Math.max(0, (instant - range.start.start) / span));
208
+ };
209
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/time.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"uuidv7.d.ts","sourceRoot":"","sources":["../../src/uuidv7.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH;;;;;;;GAOG;AACH,eAAO,MAAM,EAAE,GAAI,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,KAAG,MAmB9D,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,MAAK,MAAM,MAAiB,KAAG,MAgBnD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,IAAI,MAAM,KAAG,MACqB,CAAA;AAIvD,6DAA6D;AAC7D,eAAO,MAAM,EAAE,GAAI,GAAG,MAAM,KAAG,OACf,CAAA;AAMhB;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,GAAI,IAAI,MAAM,KAAG,IAUpC,CAAA"}
1
+ {"version":3,"file":"uuidv7.d.ts","sourceRoot":"","sources":["../../src/uuidv7.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;GAOG;AACH,eAAO,MAAM,EAAE,GAAI,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,KAAG,MAmB9D,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,MAAK,MAAM,MAAiB,KAAG,MAkBnD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,IAAI,MAAM,KAAG,MACqB,CAAA;AAIvD,6DAA6D;AAC7D,eAAO,MAAM,EAAE,GAAI,GAAG,MAAM,KAAG,OACf,CAAA;AAMhB;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,GAAI,IAAI,MAAM,KAAG,IAUpC,CAAA"}
@@ -9,7 +9,6 @@
9
9
  * increment a 12-bit sequence in the `rand_a` field, and a backwards system
10
10
  * clock never produces a smaller id.
11
11
  */
12
- import { randomFillSync } from 'node:crypto';
13
12
  const hex = (n, digits) => n.toString(16).padStart(digits, '0');
14
13
  /**
15
14
  * Formats a UUIDv7 from parts — pure, for tests and deterministic imports.
@@ -60,7 +59,9 @@ export const next = (now = Date.now) => {
60
59
  }
61
60
  lastMs = ms;
62
61
  const rand = new Uint8Array(8);
63
- randomFillSync(rand);
62
+ // Web Crypto is available in supported Node.js releases and browsers,
63
+ // keeping UUID generation portable without weakening randomness.
64
+ globalThis.crypto.getRandomValues(rand);
64
65
  return at(ms, lastSeq, rand);
65
66
  };
66
67
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"uuidv7.js","sourceRoot":"","sources":["../../src/uuidv7.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"uuidv7.js","sourceRoot":"","sources":["../../src/uuidv7.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Values, units and multipliers (spec §7.1).
2
+ * Values, units and multipliers (spec §7.1), and trajectories (spec §32.3).
3
3
  *
4
4
  * A value is the payload of an attribute claim (`HAS attr: value`), a metric
5
5
  * claim (`metric IS value`), or a `+/-` uncertainty delta. Parsing keeps the
@@ -11,6 +11,7 @@
11
11
  * - `~20B USD/yr` → the same, `approx` set
12
12
  * - `94.5%` → num 94.5, unit `%`
13
13
  * - `20 conn` → num 20, unit `conn`
14
+ * - `20B -> 40B USD/yr` → trajectory, from 2e10 to 4e10, unit `USD/yr`
14
15
  * - `2026-H2` → date-like, kept textual
15
16
  * - `token-expiry` → atom, kept textual
16
17
  *
@@ -18,17 +19,25 @@
18
19
  * with their kind already known; use {@link ofText} / {@link ofCode}.
19
20
  */
20
21
  /** How the value text was classified. */
21
- export type Kind = 'number' | 'date' | 'atom' | 'text' | 'code';
22
+ export type Kind = 'number' | 'trajectory' | 'date' | 'atom' | 'text' | 'code';
22
23
  export type Value = {
23
24
  /** Exactly as written, including `~` and multiplier letter. */
24
25
  readonly raw: string;
25
26
  readonly kind: Kind;
26
27
  /** `~` prefix (spec §7.1): the value is approximate. */
27
28
  readonly approx: boolean;
28
- /** Normalized numeric value with multiplier expanded, when parseable. */
29
+ /**
30
+ * Normalized numeric value with multiplier expanded, when parseable.
31
+ * Unset for trajectories — a trajectory is not one number, so every
32
+ * scalar consumer (fusion, filters, σ) conservatively skips it.
33
+ */
29
34
  readonly num?: number;
30
35
  /** Normalized unit expression (`USD/yr`, `ms`, `%`), when present. */
31
36
  readonly unit?: string;
37
+ /** Trajectory start value, multiplier expanded (kind `trajectory`). */
38
+ readonly from?: number;
39
+ /** Trajectory end value, multiplier expanded (kind `trajectory`). */
40
+ readonly to?: number;
32
41
  };
33
42
  export type t = Value;
34
43
  /** @returns `true` if `s` is a valid unit expression (`USD/yr`, `ms`, `%`). */
@@ -40,10 +49,34 @@ export declare const isUnit: (s: string) => boolean;
40
49
  export declare const isDateLike: (s: string) => boolean;
41
50
  /**
42
51
  * Parses an unquoted value string (spec §16 `value`), classifying it as
43
- * number, date or atom. The raw text (including `~` and multiplier letters)
44
- * is preserved verbatim.
52
+ * number, trajectory, date or atom. The raw text (including `~` and
53
+ * multiplier letters) is preserved verbatim.
45
54
  */
46
55
  export declare const parse: (raw: string) => Value;
56
+ /**
57
+ * Plain-decimal text of a finite number. The CAVE number grammar
58
+ * (spec §16) has no exponent form, so `String`'s occasional `1e-7` /
59
+ * `1.5e+21` output is expanded to `0.0000001` / `1500000000000000000000`.
60
+ * The digits are JavaScript's shortest round-trip representation, so
61
+ * `parse(formatNumber(n)).num === n`. Every generator writing numbers
62
+ * into CAVE text must go through this (or emit a multiplier form built
63
+ * on it) — `String(n)` alone round-trips tiny and huge magnitudes as
64
+ * atoms. Throws on non-finite input: CAVE has no text for it.
65
+ */
66
+ export declare const formatNumber: (n: number) => string;
67
+ /**
68
+ * Linear interpolation of a trajectory at `fraction` ∈ [0, 1], clamped
69
+ * (spec §32.3). @returns `undefined` for non-trajectory values.
70
+ */
71
+ export declare const interpolate: (value: Value, fraction: number) => undefined | number;
72
+ /**
73
+ * Canonical scalar text of a trajectory at `fraction`, in the
74
+ * trajectory's own style (spec §32.3): multipliers re-compress when the
75
+ * endpoints used them, a glued unit stays glued, 4 significant digits.
76
+ * `20B -> 40B USD/yr` at 0.5 → `30B USD/yr`; `5ms -> 800ms` at 0.5 →
77
+ * `402.5ms`. @returns `undefined` for non-trajectory values.
78
+ */
79
+ export declare const formatAt: (value: Value, fraction: number) => undefined | string;
47
80
  /** @returns value for a double-quoted natural-language literal (spec §4.2). */
48
81
  export declare const ofText: (text: string) => Value;
49
82
  /** @returns value for a backticked exact code literal (spec §4.2). */
@@ -1 +1 @@
1
- {"version":3,"file":"value.d.ts","sourceRoot":"","sources":["../../src/value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,yCAAyC;AACzC,MAAM,MAAM,IAAI,GACZ,QAAQ,GACR,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,CAAA;AAEV,MAAM,MAAM,KAAK,GAAG;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,CAAC,GAAG,KAAK,CAAA;AAMrB,+EAA+E;AAC/E,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,OACnB,CAAA;AAEhB;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,MAAM,KAAG,OACvB,CAAA;AAsChB;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,KAanC,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,KACQ,CAAA;AAE9C,sEAAsE;AACtE,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,KACQ,CAAA;AAE9C;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,MASrC,CAAA"}
1
+ {"version":3,"file":"value.d.ts","sourceRoot":"","sources":["../../src/value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,yCAAyC;AACzC,MAAM,MAAM,IAAI,GACZ,QAAQ,GACR,YAAY,GACZ,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,CAAA;AAEV,MAAM,MAAM,KAAK,GAAG;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,CAAC,GAAG,KAAK,CAAA;AAMrB,+EAA+E;AAC/E,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,OACnB,CAAA;AAEhB;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,MAAM,KAAG,OACvB,CAAA;AA8EhB;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,KAoBnC,CAAA;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MAcxC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,EAAE,UAAU,MAAM,KAAG,SAAS,GAAG,MAGI,CAAA;AAE7E;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,KAAK,EAAE,UAAU,MAAM,KAAG,SAAS,GAAG,MAqBrE,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,KACQ,CAAA;AAE9C,sEAAsE;AACtE,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,KACQ,CAAA;AAE9C;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,MASrC,CAAA"}
package/dist/src/value.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Values, units and multipliers (spec §7.1).
2
+ * Values, units and multipliers (spec §7.1), and trajectories (spec §32.3).
3
3
  *
4
4
  * A value is the payload of an attribute claim (`HAS attr: value`), a metric
5
5
  * claim (`metric IS value`), or a `+/-` uncertainty delta. Parsing keeps the
@@ -11,6 +11,7 @@
11
11
  * - `~20B USD/yr` → the same, `approx` set
12
12
  * - `94.5%` → num 94.5, unit `%`
13
13
  * - `20 conn` → num 20, unit `conn`
14
+ * - `20B -> 40B USD/yr` → trajectory, from 2e10 to 4e10, unit `USD/yr`
14
15
  * - `2026-H2` → date-like, kept textual
15
16
  * - `token-expiry` → atom, kept textual
16
17
  *
@@ -63,14 +64,51 @@ const parseNumeric = (body) => {
63
64
  }
64
65
  return unit === undefined ? { num } : { num, unit };
65
66
  };
67
+ const multiplierHeadRe = /^-?\d+(?:\.\d+)?[TBMK]$/;
68
+ /**
69
+ * Splits `body` as trajectory `from -> to [unit]` (spec §32.3). Each side
70
+ * parses like a scalar; the unit is shared — glued per endpoint or spaced
71
+ * after either — and both sides naming different units is not a
72
+ * trajectory. Style (glue, multipliers) is kept for {@link formatAt}.
73
+ */
74
+ const parseTrajectory = (body) => {
75
+ const parts = body.split(' -> ');
76
+ if (parts.length !== 2 || parts[0] === '' || parts[1] === '') {
77
+ return undefined;
78
+ }
79
+ const [leftText, rightText] = parts;
80
+ const left = parseNumeric(leftText);
81
+ const right = parseNumeric(rightText);
82
+ if (left === undefined || right === undefined) {
83
+ return undefined;
84
+ }
85
+ if (left.unit !== undefined && right.unit !== undefined && left.unit !== right.unit) {
86
+ return undefined;
87
+ }
88
+ const unit = left.unit ?? right.unit;
89
+ return {
90
+ num: left.num,
91
+ to: right.num,
92
+ ...unit === undefined ? {} : { unit },
93
+ glued: unit !== undefined && !leftText.includes(' ') && !rightText.includes(' '),
94
+ multiplier: multiplierHeadRe.test(leftText.split(' ')[0]) || multiplierHeadRe.test(rightText.split(' ')[0])
95
+ };
96
+ };
66
97
  /**
67
98
  * Parses an unquoted value string (spec §16 `value`), classifying it as
68
- * number, date or atom. The raw text (including `~` and multiplier letters)
69
- * is preserved verbatim.
99
+ * number, trajectory, date or atom. The raw text (including `~` and
100
+ * multiplier letters) is preserved verbatim.
70
101
  */
71
102
  export const parse = (raw) => {
72
103
  const approx = raw.startsWith('~');
73
104
  const body = approx ? raw.slice(1) : raw;
105
+ const trajectory = body.includes(' -> ') ? parseTrajectory(body) : undefined;
106
+ if (trajectory !== undefined) {
107
+ return {
108
+ raw, kind: 'trajectory', approx, from: trajectory.num, to: trajectory.to,
109
+ ...trajectory.unit === undefined ? {} : { unit: trajectory.unit }
110
+ };
111
+ }
74
112
  const numeric = parseNumeric(body);
75
113
  if (numeric !== undefined) {
76
114
  return numeric.unit === undefined ?
@@ -82,6 +120,68 @@ export const parse = (raw) => {
82
120
  }
83
121
  return { raw, kind: 'atom', approx };
84
122
  };
123
+ const exponentRe = /^(-?)(\d+)(?:\.(\d+))?e([+-]\d+)$/;
124
+ /**
125
+ * Plain-decimal text of a finite number. The CAVE number grammar
126
+ * (spec §16) has no exponent form, so `String`'s occasional `1e-7` /
127
+ * `1.5e+21` output is expanded to `0.0000001` / `1500000000000000000000`.
128
+ * The digits are JavaScript's shortest round-trip representation, so
129
+ * `parse(formatNumber(n)).num === n`. Every generator writing numbers
130
+ * into CAVE text must go through this (or emit a multiplier form built
131
+ * on it) — `String(n)` alone round-trips tiny and huge magnitudes as
132
+ * atoms. Throws on non-finite input: CAVE has no text for it.
133
+ */
134
+ export const formatNumber = (n) => {
135
+ if (!Number.isFinite(n)) {
136
+ throw new Error(`Expected a finite number, got ${n}.`);
137
+ }
138
+ const match = exponentRe.exec(String(n));
139
+ if (match === null) {
140
+ return String(n);
141
+ }
142
+ const [, sign = '', whole = '', fraction = '', exponent = '0'] = match;
143
+ const digits = `${whole}${fraction}`;
144
+ const point = whole.length + Number(exponent);
145
+ return point <= 0 ? `${sign}0.${'0'.repeat(-point)}${digits}` :
146
+ point >= digits.length ? `${sign}${digits}${'0'.repeat(point - digits.length)}` :
147
+ `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
148
+ };
149
+ /**
150
+ * Linear interpolation of a trajectory at `fraction` ∈ [0, 1], clamped
151
+ * (spec §32.3). @returns `undefined` for non-trajectory values.
152
+ */
153
+ export const interpolate = (value, fraction) => value.from === undefined || value.to === undefined ?
154
+ undefined :
155
+ value.from + (value.to - value.from) * Math.min(1, Math.max(0, fraction));
156
+ /**
157
+ * Canonical scalar text of a trajectory at `fraction`, in the
158
+ * trajectory's own style (spec §32.3): multipliers re-compress when the
159
+ * endpoints used them, a glued unit stays glued, 4 significant digits.
160
+ * `20B -> 40B USD/yr` at 0.5 → `30B USD/yr`; `5ms -> 800ms` at 0.5 →
161
+ * `402.5ms`. @returns `undefined` for non-trajectory values.
162
+ */
163
+ export const formatAt = (value, fraction) => {
164
+ const num = interpolate(value, fraction);
165
+ if (num === undefined) {
166
+ return undefined;
167
+ }
168
+ const style = parseTrajectory(value.approx ? value.raw.slice(1) : value.raw);
169
+ let scaled = num;
170
+ let letter = '';
171
+ if (style?.multiplier === true) {
172
+ for (const m of ['T', 'B', 'M', 'K']) {
173
+ if (Math.abs(num) >= Multiplier.factor(m)) {
174
+ scaled = num / Multiplier.factor(m);
175
+ letter = m;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ const digits = formatNumber(Number(scaled.toPrecision(4)));
181
+ return value.unit === undefined ?
182
+ `${digits}${letter}` :
183
+ `${digits}${letter}${style?.glued === true ? '' : ' '}${value.unit}`;
184
+ };
85
185
  /** @returns value for a double-quoted natural-language literal (spec §4.2). */
86
186
  export const ofText = (text) => ({ raw: text, kind: 'text', approx: false });
87
187
  /** @returns value for a backticked exact code literal (spec §4.2). */
@@ -1 +1 @@
1
- {"version":3,"file":"value.js","sourceRoot":"","sources":["../../src/value.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"value.js","sourceRoot":"","sources":["../../src/value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAiC7C,MAAM,QAAQ,GAAG,iCAAiC,CAAA;AAClD,MAAM,MAAM,GAAG,iDAAiD,CAAA;AAChE,MAAM,MAAM,GAAG,qDAAqD,CAAA;AAEpE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAS,EAAW,EAAE,CAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAEhB;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAS,EAAW,EAAE,CAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAIhB;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,IAAY,EAAuB,EAAE;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;IAC3D,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACxE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,KAAK,CAAA;IAC/B,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IACxB,IAAI,IAAwB,CAAA;IAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,IAAI,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACjC,CAAC;aAAM,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,GAAG,KAAK,CAAA;QACd,CAAC;aAAM,CAAC;YACN,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,IAAI,GAAG,IAAI,CAAA;IACb,CAAC;IACD,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;AACrD,CAAC,CAAA;AAUD,MAAM,gBAAgB,GAAG,yBAAyB,CAAA;AAElD;;;;;GAKG;AACH,MAAM,eAAe,GAAG,CAAC,IAAY,EAA0B,EAAE;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC7D,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAyB,CAAA;IACvD,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;IACnC,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAA;IACrC,IAAI,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;QACpF,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAA;IACpC,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,EAAE,EAAE,KAAK,CAAC,GAAG;QACb,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACrC,KAAK,EAAE,IAAI,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QAChF,UAAU,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;KAC9G,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAS,EAAE;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC5E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE;YACxE,GAAG,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE;SAClE,CAAA;IACH,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAClC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;YACjC,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YACnD,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAA;IACzE,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;IACtC,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AACtC,CAAC,CAAA;AAED,MAAM,UAAU,GAAG,mCAAmC,CAAA;AAEtD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAS,EAAU,EAAE;IAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,GAAG,CAAC,CAAA;IACxD,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAA;IAClB,CAAC;IACD,MAAM,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,KAAK,CAAA;IACtE,MAAM,MAAM,GAAG,GAAG,KAAK,GAAG,QAAQ,EAAE,CAAA;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC7C,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC;QAC7D,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjF,GAAG,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAA;AAC7D,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAY,EAAE,QAAgB,EAAsB,EAAE,CAChF,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;IAClD,SAAS,CAAC,CAAC;IACX,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;AAE7E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAE,QAAgB,EAAsB,EAAE;IAC7E,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IACxC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5E,IAAI,MAAM,GAAG,GAAG,CAAA;IAChB,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,KAAK,EAAE,UAAU,KAAK,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1C,MAAM,GAAG,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACnC,MAAM,GAAG,CAAC,CAAA;gBACV,MAAK;YACP,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1D,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;QAC/B,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QACtB,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;AACxE,CAAC,CAAA;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAS,EAAE,CAC5C,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9C,sEAAsE;AACtE,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAS,EAAE,CAC5C,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAY,EAAU,EAAE;IAC7C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,IAAI,KAAK,CAAC,GAAG,GAAG,CAAA;QACzB,KAAK,MAAM;YACT,OAAO,KAAK,KAAK,CAAC,GAAG,IAAI,CAAA;QAC3B;YACE,OAAO,KAAK,CAAC,GAAG,CAAA;IACpB,CAAC;AACH,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cavelang/core",
3
- "version": "0.20.0",
3
+ "version": "0.27.3",
4
4
  "type": "module",
5
5
  "description": "CAVE domain model — claims, values, units, uncertainty, claim keys, UUIDv7 transactions.",
6
6
  "license": "CC0-1.0",
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * as Entity from './entity.ts'
17
17
  export * as Key from './key.ts'
18
18
  export * as Multiplier from './multiplier.ts'
19
19
  export * as Tag from './tag.ts'
20
+ export * as Time from './time.ts'
20
21
  export * as Uncertainty from './uncertainty.ts'
21
22
  export * as Uuidv7 from './uuidv7.ts'
22
23
  export * as Value from './value.ts'
package/src/time.ts ADDED
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Valid time — periods, ranges, instants (spec §32).
3
+ *
4
+ * A date-like context names a calendar *period*, read as a UTC interval
5
+ * `[start, end)`: `@2025` the year, `@2026-04` the month, `@2026-04-10`
6
+ * the day, `@2026-Q1` / `@2026-H2` the quarter/half, `@2026-W15` the ISO
7
+ * week. A *range* context joins two points with `..` — `@2025..2028`,
8
+ * open-ended `@..2025` / `@2026..` — and covers whole periods at both
9
+ * ends. Ranges are lexically ordinary contexts (spec §6.1 already admits
10
+ * `.` in context atoms); this module is the semantic pass that reads
11
+ * them, exactly like `REVERSE` declarations are ordinary claims read
12
+ * semantically (spec §5.5).
13
+ *
14
+ * These are *valid-time* semantics — when a claim applies in the world —
15
+ * independent of transaction time (when the store learned it, spec §9.1,
16
+ * §12.3). `cave query --at` filters and interpolates on this axis.
17
+ */
18
+
19
+ /** Calendar period as a UTC interval `[start, end)`, in ms since epoch. */
20
+ export type Period = {
21
+ readonly start: number
22
+ readonly end: number
23
+ }
24
+
25
+ export type t = Period
26
+
27
+ /** How a context reads as time: a point period, or a `..` range. */
28
+ export type TimeContext =
29
+ | { readonly kind: 'point', readonly period: Period }
30
+ | { readonly kind: 'range', readonly start?: Period, readonly end?: Period }
31
+
32
+ const dayMs = 86_400_000
33
+
34
+ const utcDay = (year: number, month: number, day: number): number =>
35
+ Date.UTC(year, month - 1, day)
36
+
37
+ /** Monday starting ISO week `week` of `year` (ISO 8601: week 1 contains Jan 4). */
38
+ const isoWeekStart = (year: number, week: number): number => {
39
+ const jan4 = utcDay(year, 1, 4)
40
+ const monday = jan4 - ((new Date(jan4).getUTCDay() + 6) % 7) * dayMs
41
+ return monday + (week - 1) * 7 * dayMs
42
+ }
43
+
44
+ const periodRe = /^(\d{4})(?:-(?:Q([1-4])|H([1-2])|W(\d{1,2})|(\d{2})(?:-(\d{2}))?))?$/
45
+
46
+ /**
47
+ * Parses a date-like time point as the calendar period it names:
48
+ * `2025`, `2026-04`, `2026-04-10`, `2026-Q1`, `2026-H2`, `2026-W15`.
49
+ * @returns `undefined` when `text` is not a time point (including
50
+ * out-of-calendar dates like `2026-02-30`).
51
+ */
52
+ export const parsePeriod = (text: string): undefined | Period => {
53
+ const match = periodRe.exec(text)
54
+ if (!match) {
55
+ return undefined
56
+ }
57
+ const year = Number(match[1])
58
+ const [, , quarter, half, week, month, day] = match
59
+ if (quarter !== undefined) {
60
+ const q = Number(quarter)
61
+ return { start: utcDay(year, (q - 1) * 3 + 1, 1), end: utcDay(year, q * 3 + 1, 1) }
62
+ }
63
+ if (half !== undefined) {
64
+ const h = Number(half)
65
+ return { start: utcDay(year, (h - 1) * 6 + 1, 1), end: utcDay(year, h * 6 + 1, 1) }
66
+ }
67
+ if (week !== undefined) {
68
+ const w = Number(week)
69
+ if (w < 1 || w > 53) {
70
+ return undefined
71
+ }
72
+ const start = isoWeekStart(year, w)
73
+ return { start, end: start + 7 * dayMs }
74
+ }
75
+ if (month !== undefined) {
76
+ const m = Number(month)
77
+ if (m < 1 || m > 12) {
78
+ return undefined
79
+ }
80
+ if (day !== undefined) {
81
+ const d = Number(day)
82
+ const start = utcDay(year, m, d)
83
+ const date = new Date(start)
84
+ if (date.getUTCMonth() !== m - 1 || date.getUTCDate() !== d) {
85
+ return undefined
86
+ }
87
+ return { start, end: start + dayMs }
88
+ }
89
+ return { start: utcDay(year, m, 1), end: utcDay(year, m + 1, 1) }
90
+ }
91
+ return { start: utcDay(year, 1, 1), end: utcDay(year + 1, 1, 1) }
92
+ }
93
+
94
+ const numericPointRe = /^[\d-]+$/
95
+
96
+ /**
97
+ * Parses a `A..B` / `..B` / `A..` time range. The end point of a closed
98
+ * range may abbreviate by dropping *leading* numeric segments, inherited
99
+ * from the start point (spec §32.2): `2026-04-10..04-11` reads as
100
+ * `2026-04-10..2026-04-11`, `2026-04-10..11` likewise. `Q`/`H`/`W`
101
+ * points are always written in full. A closed range must be non-empty
102
+ * (`2028..2025` is not a time range).
103
+ * @returns `undefined` when `text` is not a range.
104
+ */
105
+ export const parseRange = (text: string): undefined | { start?: Period, end?: Period } => {
106
+ const at = text.indexOf('..')
107
+ if (at === -1 || text.indexOf('..', at + 2) !== -1) {
108
+ return undefined
109
+ }
110
+ const leftText = text.slice(0, at)
111
+ const rightText = text.slice(at + 2)
112
+ if (leftText === '' && rightText === '') {
113
+ return undefined
114
+ }
115
+ if (leftText === '') {
116
+ const end = parsePeriod(rightText)
117
+ return end === undefined ? undefined : { end }
118
+ }
119
+ const start = parsePeriod(leftText)
120
+ if (start === undefined) {
121
+ return undefined
122
+ }
123
+ if (rightText === '') {
124
+ return { start }
125
+ }
126
+ let end = parsePeriod(rightText)
127
+ if (end === undefined && numericPointRe.test(leftText) && numericPointRe.test(rightText)) {
128
+ const leftSegments = leftText.split('-')
129
+ const rightSegments = rightText.split('-')
130
+ if (rightSegments.length < leftSegments.length) {
131
+ end = parsePeriod([...leftSegments.slice(0, leftSegments.length - rightSegments.length), ...rightSegments].join('-'))
132
+ }
133
+ }
134
+ if (end === undefined || end.end <= start.start) {
135
+ return undefined
136
+ }
137
+ return { start, end }
138
+ }
139
+
140
+ /**
141
+ * Reads a context as time, when it is one (spec §32.2): a bare or
142
+ * `time:`-prefixed date-like point or `..` range. Every other context —
143
+ * `production`, `src:filing`, `auth.ts:42` — is opaque and returns
144
+ * `undefined`; a context that fails to parse as time is opaque too, the
145
+ * robust-extraction default (spec §1.6).
146
+ */
147
+ export const ofContext = (context: string): undefined | TimeContext => {
148
+ const body = context.startsWith('time:') ? context.slice('time:'.length) : context
149
+ if (body.includes('..')) {
150
+ const range = parseRange(body)
151
+ return range === undefined ? undefined : { kind: 'range', ...range }
152
+ }
153
+ const period = parsePeriod(body)
154
+ return period === undefined ? undefined : { kind: 'point', period }
155
+ }
156
+
157
+ /**
158
+ * Parses a query anchor into an instant (ms since epoch): a date-like
159
+ * period reads as its *start* instant (`2026` is 2026-01-01T00:00:00Z —
160
+ * name the finer period to anchor inside one), a `T` timestamp reads
161
+ * exactly. @returns `undefined` when `text` is neither.
162
+ */
163
+ export const parseInstant = (text: string): undefined | number => {
164
+ const period = parsePeriod(text)
165
+ if (period !== undefined) {
166
+ return period.start
167
+ }
168
+ if (!text.includes('T')) {
169
+ return undefined
170
+ }
171
+ const parsed = Date.parse(text)
172
+ return Number.isNaN(parsed) ? undefined : parsed
173
+ }
174
+
175
+ /** @returns whether a time context covers the instant (spec §32.4). */
176
+ export const covers = (context: TimeContext, instant: number): boolean =>
177
+ context.kind === 'point' ?
178
+ instant >= context.period.start && instant < context.period.end :
179
+ (context.start === undefined || instant >= context.start.start) &&
180
+ (context.end === undefined || instant < context.end.end)
181
+
182
+ /**
183
+ * Whether a claim with these contexts applies at the instant
184
+ * (spec §32.4): timeless claims — no time context — always apply; a
185
+ * time-anchored claim applies when *any* of its time contexts covers
186
+ * the instant. Opaque contexts never participate.
187
+ */
188
+ export const appliesAt = (contexts: readonly string[], instant: number): boolean => {
189
+ let anchored = false
190
+ for (const context of contexts) {
191
+ const time = ofContext(context)
192
+ if (time === undefined) {
193
+ continue
194
+ }
195
+ anchored = true
196
+ if (covers(time, instant)) {
197
+ return true
198
+ }
199
+ }
200
+ return !anchored
201
+ }
202
+
203
+ /**
204
+ * The single closed range among a claim's contexts — the interval a
205
+ * trajectory value interpolates over (spec §32.3). @returns `undefined`
206
+ * when there is none, or more than one (ambiguous — no interpolation).
207
+ */
208
+ export const closedRangeOf = (contexts: readonly string[]): undefined | { start: Period, end: Period } => {
209
+ let found: undefined | { start: Period, end: Period }
210
+ for (const context of contexts) {
211
+ const time = ofContext(context)
212
+ if (time === undefined || time.kind !== 'range' || time.start === undefined || time.end === undefined) {
213
+ continue
214
+ }
215
+ if (found !== undefined) {
216
+ return undefined
217
+ }
218
+ found = { start: time.start, end: time.end }
219
+ }
220
+ return found
221
+ }
222
+
223
+ /**
224
+ * Where an instant sits along a closed range, as the interpolation
225
+ * fraction in [0, 1] (spec §32.3): endpoint values anchor at the *start*
226
+ * instants of the periods that name them — `20B -> 40B @2025..2028` is
227
+ * 20B at 2025-01-01 and 40B at 2028-01-01 — and the fraction clamps to 1
228
+ * through the end period's tail ("40B *in* 2028" holds all of 2028).
229
+ */
230
+ export const fractionAt = (range: { start: Period, end: Period }, instant: number): number => {
231
+ const span = range.end.start - range.start.start
232
+ if (span <= 0) {
233
+ return 1
234
+ }
235
+ return Math.min(1, Math.max(0, (instant - range.start.start) / span))
236
+ }
package/src/uuidv7.ts CHANGED
@@ -10,8 +10,6 @@
10
10
  * clock never produces a smaller id.
11
11
  */
12
12
 
13
- import { randomFillSync } from 'node:crypto'
14
-
15
13
  const hex = (n: number, digits: number): string =>
16
14
  n.toString(16).padStart(digits, '0')
17
15
 
@@ -65,7 +63,9 @@ export const next = (now: () => number = Date.now): string => {
65
63
  }
66
64
  lastMs = ms
67
65
  const rand = new Uint8Array(8)
68
- randomFillSync(rand)
66
+ // Web Crypto is available in supported Node.js releases and browsers,
67
+ // keeping UUID generation portable without weakening randomness.
68
+ globalThis.crypto.getRandomValues(rand)
69
69
  return at(ms, lastSeq, rand)
70
70
  }
71
71
 
package/src/value.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Values, units and multipliers (spec §7.1).
2
+ * Values, units and multipliers (spec §7.1), and trajectories (spec §32.3).
3
3
  *
4
4
  * A value is the payload of an attribute claim (`HAS attr: value`), a metric
5
5
  * claim (`metric IS value`), or a `+/-` uncertainty delta. Parsing keeps the
@@ -11,6 +11,7 @@
11
11
  * - `~20B USD/yr` → the same, `approx` set
12
12
  * - `94.5%` → num 94.5, unit `%`
13
13
  * - `20 conn` → num 20, unit `conn`
14
+ * - `20B -> 40B USD/yr` → trajectory, from 2e10 to 4e10, unit `USD/yr`
14
15
  * - `2026-H2` → date-like, kept textual
15
16
  * - `token-expiry` → atom, kept textual
16
17
  *
@@ -22,11 +23,12 @@ import * as Multiplier from './multiplier.ts'
22
23
 
23
24
  /** How the value text was classified. */
24
25
  export type Kind =
25
- | 'number' // numeric, possibly with multiplier and unit
26
- | 'date' // date-like: 2026-H2, 2026-Q1, 2026-04-10
27
- | 'atom' // bare word(s): token-expiry, critical
28
- | 'text' // double-quoted natural-language literal
29
- | 'code' // backticked exact literal
26
+ | 'number' // numeric, possibly with multiplier and unit
27
+ | 'trajectory' // two numeric endpoints: 20B -> 40B USD/yr (spec §32.3)
28
+ | 'date' // date-like: 2026-H2, 2026-Q1, 2026-04-10
29
+ | 'atom' // bare word(s): token-expiry, critical
30
+ | 'text' // double-quoted natural-language literal
31
+ | 'code' // backticked exact literal
30
32
 
31
33
  export type Value = {
32
34
  /** Exactly as written, including `~` and multiplier letter. */
@@ -34,10 +36,18 @@ export type Value = {
34
36
  readonly kind: Kind
35
37
  /** `~` prefix (spec §7.1): the value is approximate. */
36
38
  readonly approx: boolean
37
- /** Normalized numeric value with multiplier expanded, when parseable. */
39
+ /**
40
+ * Normalized numeric value with multiplier expanded, when parseable.
41
+ * Unset for trajectories — a trajectory is not one number, so every
42
+ * scalar consumer (fusion, filters, σ) conservatively skips it.
43
+ */
38
44
  readonly num?: number
39
45
  /** Normalized unit expression (`USD/yr`, `ms`, `%`), when present. */
40
46
  readonly unit?: string
47
+ /** Trajectory start value, multiplier expanded (kind `trajectory`). */
48
+ readonly from?: number
49
+ /** Trajectory end value, multiplier expanded (kind `trajectory`). */
50
+ readonly to?: number
41
51
  }
42
52
 
43
53
  export type t = Value
@@ -93,14 +103,61 @@ const parseNumeric = (body: string): undefined | Numeric => {
93
103
  return unit === undefined ? { num } : { num, unit }
94
104
  }
95
105
 
106
+ type Trajectory = Numeric & {
107
+ to: number
108
+ /** The unit was glued to the endpoint numbers (`5ms -> 800ms`). */
109
+ glued: boolean
110
+ /** An endpoint used a multiplier letter (`20B -> 40B`). */
111
+ multiplier: boolean
112
+ }
113
+
114
+ const multiplierHeadRe = /^-?\d+(?:\.\d+)?[TBMK]$/
115
+
116
+ /**
117
+ * Splits `body` as trajectory `from -> to [unit]` (spec §32.3). Each side
118
+ * parses like a scalar; the unit is shared — glued per endpoint or spaced
119
+ * after either — and both sides naming different units is not a
120
+ * trajectory. Style (glue, multipliers) is kept for {@link formatAt}.
121
+ */
122
+ const parseTrajectory = (body: string): undefined | Trajectory => {
123
+ const parts = body.split(' -> ')
124
+ if (parts.length !== 2 || parts[0] === '' || parts[1] === '') {
125
+ return undefined
126
+ }
127
+ const [leftText, rightText] = parts as [string, string]
128
+ const left = parseNumeric(leftText)
129
+ const right = parseNumeric(rightText)
130
+ if (left === undefined || right === undefined) {
131
+ return undefined
132
+ }
133
+ if (left.unit !== undefined && right.unit !== undefined && left.unit !== right.unit) {
134
+ return undefined
135
+ }
136
+ const unit = left.unit ?? right.unit
137
+ return {
138
+ num: left.num,
139
+ to: right.num,
140
+ ...unit === undefined ? {} : { unit },
141
+ glued: unit !== undefined && !leftText.includes(' ') && !rightText.includes(' '),
142
+ multiplier: multiplierHeadRe.test(leftText.split(' ')[0]!) || multiplierHeadRe.test(rightText.split(' ')[0]!)
143
+ }
144
+ }
145
+
96
146
  /**
97
147
  * Parses an unquoted value string (spec §16 `value`), classifying it as
98
- * number, date or atom. The raw text (including `~` and multiplier letters)
99
- * is preserved verbatim.
148
+ * number, trajectory, date or atom. The raw text (including `~` and
149
+ * multiplier letters) is preserved verbatim.
100
150
  */
101
151
  export const parse = (raw: string): Value => {
102
152
  const approx = raw.startsWith('~')
103
153
  const body = approx ? raw.slice(1) : raw
154
+ const trajectory = body.includes(' -> ') ? parseTrajectory(body) : undefined
155
+ if (trajectory !== undefined) {
156
+ return {
157
+ raw, kind: 'trajectory', approx, from: trajectory.num, to: trajectory.to,
158
+ ...trajectory.unit === undefined ? {} : { unit: trajectory.unit }
159
+ }
160
+ }
104
161
  const numeric = parseNumeric(body)
105
162
  if (numeric !== undefined) {
106
163
  return numeric.unit === undefined ?
@@ -113,6 +170,73 @@ export const parse = (raw: string): Value => {
113
170
  return { raw, kind: 'atom', approx }
114
171
  }
115
172
 
173
+ const exponentRe = /^(-?)(\d+)(?:\.(\d+))?e([+-]\d+)$/
174
+
175
+ /**
176
+ * Plain-decimal text of a finite number. The CAVE number grammar
177
+ * (spec §16) has no exponent form, so `String`'s occasional `1e-7` /
178
+ * `1.5e+21` output is expanded to `0.0000001` / `1500000000000000000000`.
179
+ * The digits are JavaScript's shortest round-trip representation, so
180
+ * `parse(formatNumber(n)).num === n`. Every generator writing numbers
181
+ * into CAVE text must go through this (or emit a multiplier form built
182
+ * on it) — `String(n)` alone round-trips tiny and huge magnitudes as
183
+ * atoms. Throws on non-finite input: CAVE has no text for it.
184
+ */
185
+ export const formatNumber = (n: number): string => {
186
+ if (!Number.isFinite(n)) {
187
+ throw new Error(`Expected a finite number, got ${n}.`)
188
+ }
189
+ const match = exponentRe.exec(String(n))
190
+ if (match === null) {
191
+ return String(n)
192
+ }
193
+ const [, sign = '', whole = '', fraction = '', exponent = '0'] = match
194
+ const digits = `${whole}${fraction}`
195
+ const point = whole.length + Number(exponent)
196
+ return point <= 0 ? `${sign}0.${'0'.repeat(-point)}${digits}` :
197
+ point >= digits.length ? `${sign}${digits}${'0'.repeat(point - digits.length)}` :
198
+ `${sign}${digits.slice(0, point)}.${digits.slice(point)}`
199
+ }
200
+
201
+ /**
202
+ * Linear interpolation of a trajectory at `fraction` ∈ [0, 1], clamped
203
+ * (spec §32.3). @returns `undefined` for non-trajectory values.
204
+ */
205
+ export const interpolate = (value: Value, fraction: number): undefined | number =>
206
+ value.from === undefined || value.to === undefined ?
207
+ undefined :
208
+ value.from + (value.to - value.from) * Math.min(1, Math.max(0, fraction))
209
+
210
+ /**
211
+ * Canonical scalar text of a trajectory at `fraction`, in the
212
+ * trajectory's own style (spec §32.3): multipliers re-compress when the
213
+ * endpoints used them, a glued unit stays glued, 4 significant digits.
214
+ * `20B -> 40B USD/yr` at 0.5 → `30B USD/yr`; `5ms -> 800ms` at 0.5 →
215
+ * `402.5ms`. @returns `undefined` for non-trajectory values.
216
+ */
217
+ export const formatAt = (value: Value, fraction: number): undefined | string => {
218
+ const num = interpolate(value, fraction)
219
+ if (num === undefined) {
220
+ return undefined
221
+ }
222
+ const style = parseTrajectory(value.approx ? value.raw.slice(1) : value.raw)
223
+ let scaled = num
224
+ let letter = ''
225
+ if (style?.multiplier === true) {
226
+ for (const m of ['T', 'B', 'M', 'K'] as const) {
227
+ if (Math.abs(num) >= Multiplier.factor(m)) {
228
+ scaled = num / Multiplier.factor(m)
229
+ letter = m
230
+ break
231
+ }
232
+ }
233
+ }
234
+ const digits = formatNumber(Number(scaled.toPrecision(4)))
235
+ return value.unit === undefined ?
236
+ `${digits}${letter}` :
237
+ `${digits}${letter}${style?.glued === true ? '' : ' '}${value.unit}`
238
+ }
239
+
116
240
  /** @returns value for a double-quoted natural-language literal (spec §4.2). */
117
241
  export const ofText = (text: string): Value =>
118
242
  ({ raw: text, kind: 'text', approx: false })