@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.
- package/dist/DateTime.d.ts +108 -0
- package/dist/DateTime.d.ts.map +1 -0
- package/dist/DateTime.js +506 -0
- package/dist/DateTime.js.map +1 -0
- package/dist/Duration.d.ts +94 -0
- package/dist/Duration.d.ts.map +1 -0
- package/dist/Duration.js +330 -0
- package/dist/Duration.js.map +1 -0
- package/dist/Interval.d.ts +61 -0
- package/dist/Interval.d.ts.map +1 -0
- package/dist/Interval.js +168 -0
- package/dist/Interval.js.map +1 -0
- package/dist/atlas.d.ts +120 -0
- package/dist/atlas.d.ts.map +1 -0
- package/dist/atlas.js +163 -0
- package/dist/atlas.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/native.d.ts +47 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +77 -0
- package/dist/native.js.map +1 -0
- package/dist/rrule.d.ts +21 -0
- package/dist/rrule.d.ts.map +1 -0
- package/dist/rrule.js +58 -0
- package/dist/rrule.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +12 -0
- package/dist/utils.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/Interval.js
ADDED
|
@@ -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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"}
|
package/dist/atlas.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
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 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"}
|
package/dist/native.d.ts
ADDED
|
@@ -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"}
|