@c9up/chronos 0.1.3 → 0.1.4

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.
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Interval — immutable half-open `[start, end)` time range.
3
+ *
4
+ * Provides set-style operations (`contains`, `overlaps`, `union`,
5
+ * `intersection`, `splitBy`, `splitAt`) that the standalone range helpers
6
+ * in `DateTime.ts` could not express cleanly as methods.
7
+ *
8
+ * @implements Story 36.9
9
+ */
10
+ import { DateTime } from "./DateTime.js";
11
+ import { Duration } from "./Duration.js";
12
+ export class Interval {
13
+ #start;
14
+ #end;
15
+ constructor(start, end) {
16
+ if (start.isAfter(end)) {
17
+ throw new Error("Interval start must be <= end");
18
+ }
19
+ this.#start = start;
20
+ this.#end = end;
21
+ }
22
+ // ─── Factories ──────────────────────────────────────────
23
+ /** Build from two `DateInput` values. Half-open: `[start, end)`. */
24
+ static fromDateTimes(start, end) {
25
+ return new Interval(DateTime.from(start), DateTime.from(end));
26
+ }
27
+ /** Build from a start + duration forward. */
28
+ static after(start, duration) {
29
+ const s = DateTime.from(start);
30
+ // For Duration objects, convert to total milliseconds and add as ms —
31
+ // NOT as seconds (which was the original bug: Duration.fromMillis(1500)
32
+ // was being treated as 1500 seconds instead of 1.5 seconds).
33
+ const e = "amount" in duration
34
+ ? s.plus(duration.amount, duration.unit)
35
+ : DateTime.fromMillis(s.toMillis() + Math.round(duration.as("milliseconds")));
36
+ return new Interval(s, e);
37
+ }
38
+ // ─── Accessors ──────────────────────────────────────────
39
+ get start() {
40
+ return this.#start;
41
+ }
42
+ get end() {
43
+ return this.#end;
44
+ }
45
+ /** Length in the given unit (approximate for calendar units). */
46
+ length(unit = "milliseconds") {
47
+ const ms = this.#end.toMillis() - this.#start.toMillis();
48
+ const MS_PER = {
49
+ years: 365.25 * 86400000,
50
+ months: 30 * 86400000,
51
+ weeks: 7 * 86400000,
52
+ days: 86400000,
53
+ hours: 3600000,
54
+ minutes: 60000,
55
+ seconds: 1000,
56
+ milliseconds: 1,
57
+ };
58
+ return ms / (MS_PER[unit] ?? 1);
59
+ }
60
+ /** Duration object between start and end. */
61
+ toDuration() {
62
+ return Duration.fromMillis(this.#end.toMillis() - this.#start.toMillis());
63
+ }
64
+ // ─── Containment ────────────────────────────────────────
65
+ /** Does this interval contain the given instant? Half-open: `[start, end)`. */
66
+ contains(dt) {
67
+ const t = DateTime.from(dt).toMillis();
68
+ return t >= this.#start.toMillis() && t < this.#end.toMillis();
69
+ }
70
+ /** Is the given instant strictly before this interval? */
71
+ isBefore(dt) {
72
+ return DateTime.from(dt).toMillis() >= this.#end.toMillis();
73
+ }
74
+ /** Is the given instant strictly after this interval? */
75
+ isAfter(dt) {
76
+ return DateTime.from(dt).toMillis() < this.#start.toMillis();
77
+ }
78
+ /** Is this interval empty (zero length)? */
79
+ isEmpty() {
80
+ return this.#start.toMillis() === this.#end.toMillis();
81
+ }
82
+ // ─── Set operations ─────────────────────────────────────
83
+ /** Do the two intervals share any time? */
84
+ overlaps(other) {
85
+ return (this.#start.toMillis() < other.#end.toMillis() &&
86
+ other.#start.toMillis() < this.#end.toMillis());
87
+ }
88
+ /** Does this interval fully enclose `other`? */
89
+ engulfs(other) {
90
+ return (this.#start.toMillis() <= other.#start.toMillis() &&
91
+ this.#end.toMillis() >= other.#end.toMillis());
92
+ }
93
+ /** Does `other`'s start touch this interval's end (no overlap, no gap)? */
94
+ abutsStart(other) {
95
+ return this.#end.toMillis() === other.#start.toMillis();
96
+ }
97
+ /** Does `other`'s end touch this interval's start? */
98
+ abutsEnd(other) {
99
+ return other.#end.toMillis() === this.#start.toMillis();
100
+ }
101
+ /** The overlapping sub-interval, or `null` if disjoint. */
102
+ intersection(other) {
103
+ const s = Math.max(this.#start.toMillis(), other.#start.toMillis());
104
+ const e = Math.min(this.#end.toMillis(), other.#end.toMillis());
105
+ if (s >= e)
106
+ return null;
107
+ return Interval.fromDateTimes(DateTime.fromMillis(s), DateTime.fromMillis(e));
108
+ }
109
+ /** The smallest interval that covers both, or `null` if they don't overlap or abut. */
110
+ union(other) {
111
+ if (!this.overlaps(other) &&
112
+ !this.abutsStart(other) &&
113
+ !this.abutsEnd(other)) {
114
+ return null;
115
+ }
116
+ return Interval.fromDateTimes(DateTime.fromMillis(Math.min(this.#start.toMillis(), other.#start.toMillis())), DateTime.fromMillis(Math.max(this.#end.toMillis(), other.#end.toMillis())));
117
+ }
118
+ // ─── Split ──────────────────────────────────────────────
119
+ /** Split this interval into N sub-intervals of roughly equal `duration` length. */
120
+ splitBy(duration) {
121
+ const unitMs = {
122
+ year: 365.25 * 86400000,
123
+ month: 30 * 86400000,
124
+ week: 7 * 86400000,
125
+ day: 86400000,
126
+ hour: 3600000,
127
+ minute: 60000,
128
+ second: 1000,
129
+ };
130
+ const stepMs = "amount" in duration
131
+ ? duration.amount * (unitMs[duration.unit] ?? 1)
132
+ : duration.as("milliseconds");
133
+ if (stepMs <= 0)
134
+ throw new Error("splitBy duration must be positive");
135
+ const result = [];
136
+ let cursor = this.#start.toMillis();
137
+ const endMs = this.#end.toMillis();
138
+ while (cursor < endMs) {
139
+ const next = Math.min(cursor + stepMs, endMs);
140
+ result.push(Interval.fromDateTimes(DateTime.fromMillis(cursor), DateTime.fromMillis(next)));
141
+ cursor = next;
142
+ }
143
+ return result;
144
+ }
145
+ /** Split at specific instants. Instants outside the interval are ignored. */
146
+ splitAt(...dts) {
147
+ const points = dts
148
+ .map((d) => DateTime.from(d).toMillis())
149
+ .filter((ms) => ms > this.#start.toMillis() && ms < this.#end.toMillis())
150
+ .sort((a, b) => a - b);
151
+ const result = [];
152
+ let prev = this.#start.toMillis();
153
+ for (const p of points) {
154
+ result.push(Interval.fromDateTimes(DateTime.fromMillis(prev), DateTime.fromMillis(p)));
155
+ prev = p;
156
+ }
157
+ result.push(Interval.fromDateTimes(DateTime.fromMillis(prev), this.#end));
158
+ return result;
159
+ }
160
+ // ─── Serialization ──────────────────────────────────────
161
+ toString() {
162
+ return `[${this.#start.toISO()}, ${this.#end.toISO()})`;
163
+ }
164
+ toJSON() {
165
+ return { start: this.#start.toISO(), end: this.#end.toISO() };
166
+ }
167
+ }
168
+ //# sourceMappingURL=Interval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Interval.js","sourceRoot":"","sources":["../src/Interval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * `@c9up/chronos/atlas` — Atlas column-type adapter for {@link DateTime}.
3
+ *
4
+ * Wires `Chronos.DateTime` into Atlas's `@Column({ prepare, consume })` opt-in
5
+ * column pipeline. Lives on a sub-export so the default `@c9up/chronos` import
6
+ * surface stays adapter-free.
7
+ *
8
+ * Mirrors Adonis Lucid's `@column.prepare` / `@column.consume` pattern —
9
+ * callbacks are baked into the entity definition; no global registry, no
10
+ * boot-time wiring. Same shape as `@c9up/atom/atlas` (story 35.10).
11
+ *
12
+ * Usage (imports referenced here as prose to keep this JSDoc free of literal
13
+ * `from "@c9up/<sibling>"` strings — that pattern would create false positives
14
+ * for the no-cross-package-import grep gate):
15
+ *
16
+ * // Pull `Column`, `Entity`, `BaseEntity`, `PrimaryKey` from @c9up/atlas
17
+ * // Pull `DateTime` from @c9up/chronos
18
+ * // Pull `dateTimeAtlasAdapter` from @c9up/chronos/atlas
19
+ *
20
+ * @Entity('events')
21
+ * class Event extends BaseEntity {
22
+ * @PrimaryKey() id!: number
23
+ * @Column(dateTimeAtlasAdapter) createdAt!: DateTime | null
24
+ * }
25
+ *
26
+ * @implements Story 36.14
27
+ */
28
+ import { DateTime } from "./DateTime.js";
29
+ /**
30
+ * Atlas adapter for `timestamp` / `timestamptz` / `datetime` columns.
31
+ * `consume` lifts string / `Date` / `number` / `bigint` DB values into a
32
+ * {@link DateTime}; `prepare` lowers a `DateTime` back to its ISO 8601 string
33
+ * for the SQL bind parameter.
34
+ *
35
+ * - `consume(null)` / `consume(undefined)` returns `null` so nullable columns
36
+ * keep their semantics through the adapter pipeline.
37
+ * - `consume(existingDateTime)` is idempotent — re-consuming an already-
38
+ * hydrated value returns the same instance untouched.
39
+ * - `consume(number)` and `consume(bigint)` are interpreted as **epoch
40
+ * milliseconds** (matches `Date(ms)` and `DateTime.fromMillis`). If a driver
41
+ * returns timestamps as integer seconds (e.g., a Postgres `int8` column
42
+ * storing unix epochs), wrap them yourself: `new DateTime(seconds * 1000)`.
43
+ * - `prepare(null)` / `prepare(undefined)` returns `null` symmetrically.
44
+ * - `prepare` rejects anything that is not a `DateTime` instance — protects
45
+ * against the common "I forgot to wrap" footgun where a JS `Date` or ISO
46
+ * string would otherwise silently land in the bind parameter and bypass the
47
+ * chronos engine's normalization.
48
+ *
49
+ * **Round-trip canonicalization:** chronos's internal ISO normalization
50
+ * collapses trailing `.000Z` to a bare `Z`. So
51
+ * `prepare(consume('2026-04-30T12:00:00.000Z'))` returns
52
+ * `'2026-04-30T12:00:00Z'` — same instant, compact form. Subsecond precision
53
+ * with non-zero digits (e.g., `.123Z`) is preserved verbatim.
54
+ *
55
+ * **Driver expectations:** `timestamp` columns may come back from the driver
56
+ * as either an ISO 8601 `string`, a JS `Date`, or (rarely) an integer epoch.
57
+ * `node-postgres` returns `Date` for `timestamp` / `timestamptz` by default;
58
+ * SQLite returns whatever the bound type was; configuring drivers to emit
59
+ * ISO strings or `Date` is the supported path. Strings that are NOT parseable
60
+ * ISO 8601 will surface a `RangeError: Invalid time value` from the
61
+ * underlying `Date` constructor — align your DB column / driver settings with
62
+ * ISO 8601 wire format.
63
+ *
64
+ * **Timezone handling — UTC-only:** chronos's internal storage is always UTC.
65
+ * Consequences for this adapter:
66
+ * - Z-suffixed ISO (`'2026-04-30T12:00:00Z'`) → exactly UTC, no
67
+ * transformation.
68
+ * - Offset-bearing ISO (`'2026-04-30T12:00:00+02:00'`) → silently
69
+ * UTC-rebased to `'2026-04-30T10:00:00Z'`. The original offset is NOT
70
+ * preserved on round-trip. Pair with `timestamptz` columns (the only
71
+ * SQL type whose contract is "store UTC instant"). Do **not** use this
72
+ * adapter for `timestamp without time zone` columns where wall-clock
73
+ * fidelity matters — you will lose the offset on read and silently
74
+ * rebase on write.
75
+ * - Naive ISO (`'2026-04-30T12:00:00'` or SQLite-style
76
+ * `'2026-04-30 12:00:00'`, no `Z`, no offset) → parsed by `new Date(...)`
77
+ * in the JS runtime's **local** zone. This means the same DB row hydrates
78
+ * differently across machines (CI host on UTC vs. dev laptop in
79
+ * Europe/Zurich). Do **not** store naive timestamps in columns reaching
80
+ * this adapter; configure your DB / driver to emit Z-suffixed strings or
81
+ * `Date` instances, or pre-process via `DateTime.fromSQL`.
82
+ *
83
+ * **Stacking caveat with `@column.dateTime({ autoCreate, autoUpdate })`:**
84
+ * the auto-timestamp decorator (story 32.8) writes `new Date()` (a JS `Date`)
85
+ * to the entity property when `autoCreate` / `autoUpdate` is set. If you
86
+ * ALSO tag the same property with `@Column(dateTimeAtlasAdapter)`, `prepare`
87
+ * will receive a `Date` and throw the "expected a DateTime instance"
88
+ * `TypeError` at INSERT / UPDATE time. **Only the `autoCreate` / `autoUpdate`
89
+ * flags conflict** — plain `@column.dateTime()` (no flags) does not write
90
+ * anything and is safe to stack. Mitigations when you need auto-timestamps:
91
+ * - **Adapter only**: drop `@column.dateTime({ autoCreate, autoUpdate })`
92
+ * and assign manually (e.g., in a model hook:
93
+ * `entity.createdAt = DateTime.now()`).
94
+ * - **`@column.dateTime({ ... })` only**: drop the adapter and manually
95
+ * wrap reads with `new DateTime(row.createdAt)` at the call site.
96
+ * The adapter does NOT silently coerce `Date` → `DateTime` because that
97
+ * would mask the inconsistency between the two mechanisms.
98
+ *
99
+ * The shape `{ prepare, consume }` is **passable directly** to `@Column(...)`:
100
+ * `@Column(dateTimeAtlasAdapter)` is identical to
101
+ * `@Column({ prepare: dateTimeAtlasAdapter.prepare, consume: dateTimeAtlasAdapter.consume })`.
102
+ *
103
+ * The exported object is `Object.freeze`d, which blocks the most common
104
+ * direct-assignment tampering of the adapter's own slots. `Object.freeze`
105
+ * is shallow: prototype-level mutation of `DateTime` itself, or wholesale
106
+ * replacement via `structuredClone(adapter)` followed by re-binding, are
107
+ * out of scope for the freeze defense.
108
+ *
109
+ * **Cross-realm safety:** `consume` and `prepare` test the input via a
110
+ * structural duck-typed check (`toISO` + `equals` methods present), not a
111
+ * plain `instanceof DateTime`. This protects against pnpm-hoisting quirks
112
+ * where a consumer's `DateTime` import resolves to a duplicate copy of the
113
+ * class — the adapter still recognizes it as a `DateTime` and round-trips
114
+ * cleanly.
115
+ */
116
+ export declare const dateTimeAtlasAdapter: Readonly<{
117
+ consume(raw: unknown): DateTime | null;
118
+ prepare(value: unknown): string | null;
119
+ }>;
120
+ //# sourceMappingURL=atlas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atlas.d.ts","sourceRoot":"","sources":["../src/atlas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAsBzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,eAAO,MAAM,oBAAoB;iBACnB,OAAO,GAAG,QAAQ,GAAG,IAAI;mBAWvB,OAAO,GAAG,MAAM,GAAG,IAAI;EAUrC,CAAC"}
package/dist/atlas.js ADDED
@@ -0,0 +1,163 @@
1
+ /**
2
+ * `@c9up/chronos/atlas` — Atlas column-type adapter for {@link DateTime}.
3
+ *
4
+ * Wires `Chronos.DateTime` into Atlas's `@Column({ prepare, consume })` opt-in
5
+ * column pipeline. Lives on a sub-export so the default `@c9up/chronos` import
6
+ * surface stays adapter-free.
7
+ *
8
+ * Mirrors Adonis Lucid's `@column.prepare` / `@column.consume` pattern —
9
+ * callbacks are baked into the entity definition; no global registry, no
10
+ * boot-time wiring. Same shape as `@c9up/atom/atlas` (story 35.10).
11
+ *
12
+ * Usage (imports referenced here as prose to keep this JSDoc free of literal
13
+ * `from "@c9up/<sibling>"` strings — that pattern would create false positives
14
+ * for the no-cross-package-import grep gate):
15
+ *
16
+ * // Pull `Column`, `Entity`, `BaseEntity`, `PrimaryKey` from @c9up/atlas
17
+ * // Pull `DateTime` from @c9up/chronos
18
+ * // Pull `dateTimeAtlasAdapter` from @c9up/chronos/atlas
19
+ *
20
+ * @Entity('events')
21
+ * class Event extends BaseEntity {
22
+ * @PrimaryKey() id!: number
23
+ * @Column(dateTimeAtlasAdapter) createdAt!: DateTime | null
24
+ * }
25
+ *
26
+ * @implements Story 36.14
27
+ */
28
+ import { DateTime } from "./DateTime.js";
29
+ /**
30
+ * Cross-realm-safe `DateTime` check. Returns `true` if `value` is either:
31
+ * - a `DateTime` from the current module-realm (`instanceof` matches), OR
32
+ * - a structurally-compatible `DateTime` from another realm — i.e. an
33
+ * object exposing both `toISO(): string` and `equals(other): boolean`,
34
+ * the two methods this adapter relies on.
35
+ *
36
+ * The fallback is needed when pnpm hoisting + transitive duplication produce
37
+ * two distinct `DateTime` constructors at runtime (workspace symlinks +
38
+ * a separately-installed copy under a parent `node_modules`). Without it,
39
+ * `instanceof` returns `false` for instances built from the consumer's
40
+ * own import, and `prepare` would reject perfectly valid `DateTime` values.
41
+ */
42
+ function isDateTimeLike(value) {
43
+ if (value instanceof DateTime)
44
+ return true;
45
+ if (value === null || typeof value !== "object")
46
+ return false;
47
+ const obj = value;
48
+ return typeof obj.toISO === "function" && typeof obj.equals === "function";
49
+ }
50
+ /**
51
+ * Atlas adapter for `timestamp` / `timestamptz` / `datetime` columns.
52
+ * `consume` lifts string / `Date` / `number` / `bigint` DB values into a
53
+ * {@link DateTime}; `prepare` lowers a `DateTime` back to its ISO 8601 string
54
+ * for the SQL bind parameter.
55
+ *
56
+ * - `consume(null)` / `consume(undefined)` returns `null` so nullable columns
57
+ * keep their semantics through the adapter pipeline.
58
+ * - `consume(existingDateTime)` is idempotent — re-consuming an already-
59
+ * hydrated value returns the same instance untouched.
60
+ * - `consume(number)` and `consume(bigint)` are interpreted as **epoch
61
+ * milliseconds** (matches `Date(ms)` and `DateTime.fromMillis`). If a driver
62
+ * returns timestamps as integer seconds (e.g., a Postgres `int8` column
63
+ * storing unix epochs), wrap them yourself: `new DateTime(seconds * 1000)`.
64
+ * - `prepare(null)` / `prepare(undefined)` returns `null` symmetrically.
65
+ * - `prepare` rejects anything that is not a `DateTime` instance — protects
66
+ * against the common "I forgot to wrap" footgun where a JS `Date` or ISO
67
+ * string would otherwise silently land in the bind parameter and bypass the
68
+ * chronos engine's normalization.
69
+ *
70
+ * **Round-trip canonicalization:** chronos's internal ISO normalization
71
+ * collapses trailing `.000Z` to a bare `Z`. So
72
+ * `prepare(consume('2026-04-30T12:00:00.000Z'))` returns
73
+ * `'2026-04-30T12:00:00Z'` — same instant, compact form. Subsecond precision
74
+ * with non-zero digits (e.g., `.123Z`) is preserved verbatim.
75
+ *
76
+ * **Driver expectations:** `timestamp` columns may come back from the driver
77
+ * as either an ISO 8601 `string`, a JS `Date`, or (rarely) an integer epoch.
78
+ * `node-postgres` returns `Date` for `timestamp` / `timestamptz` by default;
79
+ * SQLite returns whatever the bound type was; configuring drivers to emit
80
+ * ISO strings or `Date` is the supported path. Strings that are NOT parseable
81
+ * ISO 8601 will surface a `RangeError: Invalid time value` from the
82
+ * underlying `Date` constructor — align your DB column / driver settings with
83
+ * ISO 8601 wire format.
84
+ *
85
+ * **Timezone handling — UTC-only:** chronos's internal storage is always UTC.
86
+ * Consequences for this adapter:
87
+ * - Z-suffixed ISO (`'2026-04-30T12:00:00Z'`) → exactly UTC, no
88
+ * transformation.
89
+ * - Offset-bearing ISO (`'2026-04-30T12:00:00+02:00'`) → silently
90
+ * UTC-rebased to `'2026-04-30T10:00:00Z'`. The original offset is NOT
91
+ * preserved on round-trip. Pair with `timestamptz` columns (the only
92
+ * SQL type whose contract is "store UTC instant"). Do **not** use this
93
+ * adapter for `timestamp without time zone` columns where wall-clock
94
+ * fidelity matters — you will lose the offset on read and silently
95
+ * rebase on write.
96
+ * - Naive ISO (`'2026-04-30T12:00:00'` or SQLite-style
97
+ * `'2026-04-30 12:00:00'`, no `Z`, no offset) → parsed by `new Date(...)`
98
+ * in the JS runtime's **local** zone. This means the same DB row hydrates
99
+ * differently across machines (CI host on UTC vs. dev laptop in
100
+ * Europe/Zurich). Do **not** store naive timestamps in columns reaching
101
+ * this adapter; configure your DB / driver to emit Z-suffixed strings or
102
+ * `Date` instances, or pre-process via `DateTime.fromSQL`.
103
+ *
104
+ * **Stacking caveat with `@column.dateTime({ autoCreate, autoUpdate })`:**
105
+ * the auto-timestamp decorator (story 32.8) writes `new Date()` (a JS `Date`)
106
+ * to the entity property when `autoCreate` / `autoUpdate` is set. If you
107
+ * ALSO tag the same property with `@Column(dateTimeAtlasAdapter)`, `prepare`
108
+ * will receive a `Date` and throw the "expected a DateTime instance"
109
+ * `TypeError` at INSERT / UPDATE time. **Only the `autoCreate` / `autoUpdate`
110
+ * flags conflict** — plain `@column.dateTime()` (no flags) does not write
111
+ * anything and is safe to stack. Mitigations when you need auto-timestamps:
112
+ * - **Adapter only**: drop `@column.dateTime({ autoCreate, autoUpdate })`
113
+ * and assign manually (e.g., in a model hook:
114
+ * `entity.createdAt = DateTime.now()`).
115
+ * - **`@column.dateTime({ ... })` only**: drop the adapter and manually
116
+ * wrap reads with `new DateTime(row.createdAt)` at the call site.
117
+ * The adapter does NOT silently coerce `Date` → `DateTime` because that
118
+ * would mask the inconsistency between the two mechanisms.
119
+ *
120
+ * The shape `{ prepare, consume }` is **passable directly** to `@Column(...)`:
121
+ * `@Column(dateTimeAtlasAdapter)` is identical to
122
+ * `@Column({ prepare: dateTimeAtlasAdapter.prepare, consume: dateTimeAtlasAdapter.consume })`.
123
+ *
124
+ * The exported object is `Object.freeze`d, which blocks the most common
125
+ * direct-assignment tampering of the adapter's own slots. `Object.freeze`
126
+ * is shallow: prototype-level mutation of `DateTime` itself, or wholesale
127
+ * replacement via `structuredClone(adapter)` followed by re-binding, are
128
+ * out of scope for the freeze defense.
129
+ *
130
+ * **Cross-realm safety:** `consume` and `prepare` test the input via a
131
+ * structural duck-typed check (`toISO` + `equals` methods present), not a
132
+ * plain `instanceof DateTime`. This protects against pnpm-hoisting quirks
133
+ * where a consumer's `DateTime` import resolves to a duplicate copy of the
134
+ * class — the adapter still recognizes it as a `DateTime` and round-trips
135
+ * cleanly.
136
+ */
137
+ export const dateTimeAtlasAdapter = Object.freeze({
138
+ consume(raw) {
139
+ if (raw === null || raw === undefined)
140
+ return null;
141
+ if (isDateTimeLike(raw))
142
+ return raw;
143
+ if (raw instanceof Date)
144
+ return DateTime.fromJSDate(raw);
145
+ if (typeof raw === "string")
146
+ return new DateTime(raw);
147
+ if (typeof raw === "number")
148
+ return DateTime.fromMillis(raw);
149
+ if (typeof raw === "bigint")
150
+ return DateTime.fromMillis(Number(raw));
151
+ throw new TypeError(`dateTimeAtlasAdapter.consume: expected string | Date | DateTime | number | bigint | null, got ${typeof raw}`);
152
+ },
153
+ prepare(value) {
154
+ if (value === null || value === undefined)
155
+ return null;
156
+ if (!isDateTimeLike(value)) {
157
+ throw new TypeError(`dateTimeAtlasAdapter.prepare: expected a DateTime instance, got ${typeof value === "object" ? Object.prototype.toString.call(value) : typeof value}. ` +
158
+ "Wrap the value with `new DateTime(...)` before assigning to a column tagged with this adapter.");
159
+ }
160
+ return value.toISO();
161
+ },
162
+ });
163
+ //# sourceMappingURL=atlas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atlas.js","sourceRoot":"","sources":["../src/atlas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;;;;;;;GAYG;AACH,SAAS,cAAc,CAAC,KAAc;IACrC,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,GAAG,GAAG,KAA8C,CAAC;IAC3D,OAAO,OAAO,GAAG,CAAC,KAAK,KAAK,UAAU,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,CAAC;AAC5E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,OAAO,CAAC,GAAY;QACnB,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACnD,IAAI,cAAc,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QACpC,IAAI,GAAG,YAAY,IAAI;YAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACzD,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtD,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACrE,MAAM,IAAI,SAAS,CAClB,iGAAiG,OAAO,GAAG,EAAE,CAC7G,CAAC;IACH,CAAC;IACD,OAAO,CAAC,KAAc;QACrB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACvD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,SAAS,CAClB,mEAAmE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI;gBACtJ,gGAAgG,CACjG,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;CACD,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @c9up/chronos — advanced date/time and recurrence.
3
+ * The Rust N-API binary is required — there is no JS/TS fallback.
4
+ */
5
+ export type { BoundUnit, CalendarParts, DateInput, DateRange, DateUnit, RangeCompareOptions, RangeRelation, } from "./DateTime.js";
6
+ export { analyzeRange, containsRange, DateTime, inRange, overlapsRange, } from "./DateTime.js";
7
+ export type { DurationObject, DurationUnit } from "./Duration.js";
8
+ export { Duration } from "./Duration.js";
9
+ export { Interval } from "./Interval.js";
10
+ export type { RRuleBuild } from "./rrule.js";
11
+ export { expandRRule, toRRuleString } from "./rrule.js";
12
+ import { type DateRange, DateTime, type DateUnit, inRange, type RangeCompareOptions } from "./DateTime.js";
13
+ import { type RRuleBuild } from "./rrule.js";
14
+ export declare function at(input?: string | Date): DateTime;
15
+ export declare const Chronos: {
16
+ at: typeof at;
17
+ now: () => DateTime;
18
+ parse: (input: string | Date) => DateTime;
19
+ add: (input: string | Date, amount: number, unit: DateUnit) => DateTime;
20
+ subtract: (input: string | Date, amount: number, unit: DateUnit) => DateTime;
21
+ diff: (a: string | Date, b: string | Date, unit: DateUnit) => number;
22
+ inRange: typeof inRange;
23
+ rangeContains: (outer: DateRange, inner: DateRange, options?: RangeCompareOptions) => boolean;
24
+ rangesOverlap: (a: DateRange, b: DateRange, options?: RangeCompareOptions) => boolean;
25
+ rangeRelation: (a: DateRange, b: DateRange, options?: RangeCompareOptions) => import("./DateTime.js").RangeRelation;
26
+ rrule: (startIso: string, rrule: string | RRuleBuild, limit?: number) => string[];
27
+ buildRRule: (rule: RRuleBuild) => string;
28
+ };
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EACX,SAAS,EACT,aAAa,EACb,SAAS,EACT,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,OAAO,EACP,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIxD,OAAO,EAGN,KAAK,SAAS,EACd,QAAQ,EACR,KAAK,QAAQ,EACb,OAAO,EAEP,KAAK,mBAAmB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAe,KAAK,UAAU,EAAiB,MAAM,YAAY,CAAC;AAEzE,wBAAgB,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAElD;AAED,eAAO,MAAM,OAAO;;eAEV,QAAQ;mBACF,MAAM,GAAG,IAAI,KAAG,QAAQ;iBAC1B,MAAM,GAAG,IAAI,UAAU,MAAM,QAAQ,QAAQ,KAAG,QAAQ;sBAEnD,MAAM,GAAG,IAAI,UAAU,MAAM,QAAQ,QAAQ,KAAG,QAAQ;cAEhE,MAAM,GAAG,IAAI,KAAK,MAAM,GAAG,IAAI,QAAQ,QAAQ,KAAG,MAAM;;2BAI1D,SAAS,SACT,SAAS,YACN,mBAAmB,KAC3B,OAAO;uBAEN,SAAS,KACT,SAAS,YACF,mBAAmB,KAC3B,OAAO;uBACS,SAAS,KAAK,SAAS,YAAY,mBAAmB;sBAG9D,MAAM,SACT,MAAM,GAAG,UAAU,qBAExB,MAAM,EAAE;uBACQ,UAAU,KAAG,MAAM;CACtC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @c9up/chronos — advanced date/time and recurrence.
3
+ * The Rust N-API binary is required — there is no JS/TS fallback.
4
+ */
5
+ export { analyzeRange, containsRange, DateTime, inRange, overlapsRange, } from "./DateTime.js";
6
+ export { Duration } from "./Duration.js";
7
+ export { Interval } from "./Interval.js";
8
+ export { expandRRule, toRRuleString } from "./rrule.js";
9
+ // `isNativeAvailable` removed — NAPI is now mandatory (no fallback).
10
+ import { analyzeRange, containsRange, DateTime, inRange, overlapsRange, } from "./DateTime.js";
11
+ import { expandRRule, toRRuleString } from "./rrule.js";
12
+ export function at(input) {
13
+ return new DateTime(input);
14
+ }
15
+ export const Chronos = {
16
+ at,
17
+ now: () => new DateTime(),
18
+ parse: (input) => new DateTime(input),
19
+ add: (input, amount, unit) => new DateTime(input).plus(amount, unit),
20
+ subtract: (input, amount, unit) => new DateTime(input).minus(amount, unit),
21
+ diff: (a, b, unit) => new DateTime(a).diff(b, unit),
22
+ inRange,
23
+ rangeContains: (outer, inner, options) => containsRange(outer, inner, options),
24
+ rangesOverlap: (a, b, options) => overlapsRange(a, b, options),
25
+ rangeRelation: (a, b, options) => analyzeRange(a, b, options),
26
+ rrule: (startIso, rrule, limit = 100) => expandRRule(startIso, rrule, limit),
27
+ buildRRule: (rule) => toRRuleString(rule),
28
+ };
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EACN,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,OAAO,EACP,aAAa,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD,qEAAqE;AAErE,OAAO,EACN,YAAY,EACZ,aAAa,EAEb,QAAQ,EAER,OAAO,EACP,aAAa,GAEb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAmB,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,UAAU,EAAE,CAAC,KAAqB;IACvC,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,EAAE;IACF,GAAG,EAAE,GAAa,EAAE,CAAC,IAAI,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC,KAAoB,EAAY,EAAE,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC;IAC9D,GAAG,EAAE,CAAC,KAAoB,EAAE,MAAc,EAAE,IAAc,EAAY,EAAE,CACvE,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,KAAoB,EAAE,MAAc,EAAE,IAAc,EAAY,EAAE,CAC5E,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;IACxC,IAAI,EAAE,CAAC,CAAgB,EAAE,CAAgB,EAAE,IAAc,EAAU,EAAE,CACpE,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,OAAO;IACP,aAAa,EAAE,CACd,KAAgB,EAChB,KAAgB,EAChB,OAA6B,EACnB,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;IAClD,aAAa,EAAE,CACd,CAAY,EACZ,CAAY,EACZ,OAA6B,EACnB,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC;IAC1C,aAAa,EAAE,CAAC,CAAY,EAAE,CAAY,EAAE,OAA6B,EAAE,EAAE,CAC5E,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,CACN,QAAgB,EAChB,KAA0B,EAC1B,KAAK,GAAG,GAAG,EACA,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC;IAClD,UAAU,EAAE,CAAC,IAAgB,EAAU,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC;CAC7D,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Universal engine loader — auto-detects Node (NAPI) vs Browser (WASM).
3
+ *
4
+ * **No fallback.** If neither NAPI nor WASM loads, `nativeChronos()` throws
5
+ * immediately. Chronos operations (timezone, DST, RFC 5545) are too complex
6
+ * for a pure-TS fallback.
7
+ */
8
+ export interface NativeChronos {
9
+ add(iso: string, amount: number, unit: string): string;
10
+ diff(aIso: string, bIso: string, unit: string): number;
11
+ startOf(iso: string, unit: string): string;
12
+ endOf(iso: string, unit: string): string;
13
+ format(iso: string, pattern: string): string;
14
+ validateTimezone(zone: string): string;
15
+ toZone(utcIso: string, zone: string): {
16
+ iso: string;
17
+ offsetMinutes: number;
18
+ zoneName: string;
19
+ };
20
+ addInZone(utcIso: string, amount: number, unit: string, zone: string): string;
21
+ diffInZone(aUtc: string, bUtc: string, unit: string, zone: string): number;
22
+ zoneOffset(utcIso: string, zone: string): number;
23
+ fromLocal(naiveIso: string, zone: string): string;
24
+ parseRfc2822(input: string): string;
25
+ parseSql(input: string): string;
26
+ parseHttp(input: string): string;
27
+ rruleExpand(startIso: string, rrule: string, limit: number): string[];
28
+ calendarParts(iso: string): {
29
+ year: number;
30
+ month: number;
31
+ day: number;
32
+ hour: number;
33
+ minute: number;
34
+ second: number;
35
+ millisecond: number;
36
+ weekday: number;
37
+ weekNumber: number;
38
+ weekYear: number;
39
+ ordinal: number;
40
+ quarter: number;
41
+ daysInMonth: number;
42
+ daysInYear: number;
43
+ isLeapYear: boolean;
44
+ };
45
+ }
46
+ export declare function nativeChronos(): NativeChronos;
47
+ //# sourceMappingURL=native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACvD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,MAAM,CACL,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACV;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9E,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACjD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACpB,CAAC;CACF;AAoED,wBAAgB,aAAa,IAAI,aAAa,CAW7C"}