@c9up/chronos 0.1.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.
package/src/atlas.ts ADDED
@@ -0,0 +1,160 @@
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
+
29
+ import { DateTime } from "./DateTime.js";
30
+
31
+ /**
32
+ * Cross-realm-safe `DateTime` check. Returns `true` if `value` is either:
33
+ * - a `DateTime` from the current module-realm (`instanceof` matches), OR
34
+ * - a structurally-compatible `DateTime` from another realm — i.e. an
35
+ * object exposing both `toISO(): string` and `equals(other): boolean`,
36
+ * the two methods this adapter relies on.
37
+ *
38
+ * The fallback is needed when pnpm hoisting + transitive duplication produce
39
+ * two distinct `DateTime` constructors at runtime (workspace symlinks +
40
+ * a separately-installed copy under a parent `node_modules`). Without it,
41
+ * `instanceof` returns `false` for instances built from the consumer's
42
+ * own import, and `prepare` would reject perfectly valid `DateTime` values.
43
+ */
44
+ function isDateTimeLike(value: unknown): value is DateTime {
45
+ if (value instanceof DateTime) return true;
46
+ if (value === null || typeof value !== "object") return false;
47
+ const obj = value as { toISO?: unknown; equals?: unknown };
48
+ return typeof obj.toISO === "function" && typeof obj.equals === "function";
49
+ }
50
+
51
+ /**
52
+ * Atlas adapter for `timestamp` / `timestamptz` / `datetime` columns.
53
+ * `consume` lifts string / `Date` / `number` / `bigint` DB values into a
54
+ * {@link DateTime}; `prepare` lowers a `DateTime` back to its ISO 8601 string
55
+ * for the SQL bind parameter.
56
+ *
57
+ * - `consume(null)` / `consume(undefined)` returns `null` so nullable columns
58
+ * keep their semantics through the adapter pipeline.
59
+ * - `consume(existingDateTime)` is idempotent — re-consuming an already-
60
+ * hydrated value returns the same instance untouched.
61
+ * - `consume(number)` and `consume(bigint)` are interpreted as **epoch
62
+ * milliseconds** (matches `Date(ms)` and `DateTime.fromMillis`). If a driver
63
+ * returns timestamps as integer seconds (e.g., a Postgres `int8` column
64
+ * storing unix epochs), wrap them yourself: `new DateTime(seconds * 1000)`.
65
+ * - `prepare(null)` / `prepare(undefined)` returns `null` symmetrically.
66
+ * - `prepare` rejects anything that is not a `DateTime` instance — protects
67
+ * against the common "I forgot to wrap" footgun where a JS `Date` or ISO
68
+ * string would otherwise silently land in the bind parameter and bypass the
69
+ * chronos engine's normalization.
70
+ *
71
+ * **Round-trip canonicalization:** chronos's internal ISO normalization
72
+ * collapses trailing `.000Z` to a bare `Z`. So
73
+ * `prepare(consume('2026-04-30T12:00:00.000Z'))` returns
74
+ * `'2026-04-30T12:00:00Z'` — same instant, compact form. Subsecond precision
75
+ * with non-zero digits (e.g., `.123Z`) is preserved verbatim.
76
+ *
77
+ * **Driver expectations:** `timestamp` columns may come back from the driver
78
+ * as either an ISO 8601 `string`, a JS `Date`, or (rarely) an integer epoch.
79
+ * `node-postgres` returns `Date` for `timestamp` / `timestamptz` by default;
80
+ * SQLite returns whatever the bound type was; configuring drivers to emit
81
+ * ISO strings or `Date` is the supported path. Strings that are NOT parseable
82
+ * ISO 8601 will surface a `RangeError: Invalid time value` from the
83
+ * underlying `Date` constructor — align your DB column / driver settings with
84
+ * ISO 8601 wire format.
85
+ *
86
+ * **Timezone handling — UTC-only:** chronos's internal storage is always UTC.
87
+ * Consequences for this adapter:
88
+ * - Z-suffixed ISO (`'2026-04-30T12:00:00Z'`) → exactly UTC, no
89
+ * transformation.
90
+ * - Offset-bearing ISO (`'2026-04-30T12:00:00+02:00'`) → silently
91
+ * UTC-rebased to `'2026-04-30T10:00:00Z'`. The original offset is NOT
92
+ * preserved on round-trip. Pair with `timestamptz` columns (the only
93
+ * SQL type whose contract is "store UTC instant"). Do **not** use this
94
+ * adapter for `timestamp without time zone` columns where wall-clock
95
+ * fidelity matters — you will lose the offset on read and silently
96
+ * rebase on write.
97
+ * - Naive ISO (`'2026-04-30T12:00:00'` or SQLite-style
98
+ * `'2026-04-30 12:00:00'`, no `Z`, no offset) → parsed by `new Date(...)`
99
+ * in the JS runtime's **local** zone. This means the same DB row hydrates
100
+ * differently across machines (CI host on UTC vs. dev laptop in
101
+ * Europe/Zurich). Do **not** store naive timestamps in columns reaching
102
+ * this adapter; configure your DB / driver to emit Z-suffixed strings or
103
+ * `Date` instances, or pre-process via `DateTime.fromSQL`.
104
+ *
105
+ * **Stacking caveat with `@column.dateTime({ autoCreate, autoUpdate })`:**
106
+ * the auto-timestamp decorator (story 32.8) writes `new Date()` (a JS `Date`)
107
+ * to the entity property when `autoCreate` / `autoUpdate` is set. If you
108
+ * ALSO tag the same property with `@Column(dateTimeAtlasAdapter)`, `prepare`
109
+ * will receive a `Date` and throw the "expected a DateTime instance"
110
+ * `TypeError` at INSERT / UPDATE time. **Only the `autoCreate` / `autoUpdate`
111
+ * flags conflict** — plain `@column.dateTime()` (no flags) does not write
112
+ * anything and is safe to stack. Mitigations when you need auto-timestamps:
113
+ * - **Adapter only**: drop `@column.dateTime({ autoCreate, autoUpdate })`
114
+ * and assign manually (e.g., in a model hook:
115
+ * `entity.createdAt = DateTime.now()`).
116
+ * - **`@column.dateTime({ ... })` only**: drop the adapter and manually
117
+ * wrap reads with `new DateTime(row.createdAt)` at the call site.
118
+ * The adapter does NOT silently coerce `Date` → `DateTime` because that
119
+ * would mask the inconsistency between the two mechanisms.
120
+ *
121
+ * The shape `{ prepare, consume }` is **passable directly** to `@Column(...)`:
122
+ * `@Column(dateTimeAtlasAdapter)` is identical to
123
+ * `@Column({ prepare: dateTimeAtlasAdapter.prepare, consume: dateTimeAtlasAdapter.consume })`.
124
+ *
125
+ * The exported object is `Object.freeze`d, which blocks the most common
126
+ * direct-assignment tampering of the adapter's own slots. `Object.freeze`
127
+ * is shallow: prototype-level mutation of `DateTime` itself, or wholesale
128
+ * replacement via `structuredClone(adapter)` followed by re-binding, are
129
+ * out of scope for the freeze defense.
130
+ *
131
+ * **Cross-realm safety:** `consume` and `prepare` test the input via a
132
+ * structural duck-typed check (`toISO` + `equals` methods present), not a
133
+ * plain `instanceof DateTime`. This protects against pnpm-hoisting quirks
134
+ * where a consumer's `DateTime` import resolves to a duplicate copy of the
135
+ * class — the adapter still recognizes it as a `DateTime` and round-trips
136
+ * cleanly.
137
+ */
138
+ export const dateTimeAtlasAdapter = Object.freeze({
139
+ consume(raw: unknown): DateTime | null {
140
+ if (raw === null || raw === undefined) return null;
141
+ if (isDateTimeLike(raw)) return raw;
142
+ if (raw instanceof Date) return DateTime.fromJSDate(raw);
143
+ if (typeof raw === "string") return new DateTime(raw);
144
+ if (typeof raw === "number") return DateTime.fromMillis(raw);
145
+ if (typeof raw === "bigint") return DateTime.fromMillis(Number(raw));
146
+ throw new TypeError(
147
+ `dateTimeAtlasAdapter.consume: expected string | Date | DateTime | number | bigint | null, got ${typeof raw}`,
148
+ );
149
+ },
150
+ prepare(value: unknown): string | null {
151
+ if (value === null || value === undefined) return null;
152
+ if (!isDateTimeLike(value)) {
153
+ throw new TypeError(
154
+ `dateTimeAtlasAdapter.prepare: expected a DateTime instance, got ${typeof value === "object" ? Object.prototype.toString.call(value) : typeof value}. ` +
155
+ "Wrap the value with `new DateTime(...)` before assigning to a column tagged with this adapter.",
156
+ );
157
+ }
158
+ return value.toISO();
159
+ },
160
+ });
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
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
+
6
+ export type {
7
+ BoundUnit,
8
+ CalendarParts,
9
+ DateInput,
10
+ DateRange,
11
+ DateUnit,
12
+ RangeCompareOptions,
13
+ RangeRelation,
14
+ } from "./DateTime.js";
15
+ export {
16
+ analyzeRange,
17
+ containsRange,
18
+ DateTime,
19
+ inRange,
20
+ overlapsRange,
21
+ } from "./DateTime.js";
22
+ export type { DurationObject, DurationUnit } from "./Duration.js";
23
+ export { Duration } from "./Duration.js";
24
+ export { Interval } from "./Interval.js";
25
+ export type { RRuleBuild } from "./rrule.js";
26
+ export { expandRRule, toRRuleString } from "./rrule.js";
27
+
28
+ // `isNativeAvailable` removed — NAPI is now mandatory (no fallback).
29
+
30
+ import {
31
+ analyzeRange,
32
+ containsRange,
33
+ type DateRange,
34
+ DateTime,
35
+ type DateUnit,
36
+ inRange,
37
+ overlapsRange,
38
+ type RangeCompareOptions,
39
+ } from "./DateTime.js";
40
+ import { expandRRule, type RRuleBuild, toRRuleString } from "./rrule.js";
41
+
42
+ export function at(input?: string | Date): DateTime {
43
+ return new DateTime(input);
44
+ }
45
+
46
+ export const Chronos = {
47
+ at,
48
+ now: (): DateTime => new DateTime(),
49
+ parse: (input: string | Date): DateTime => new DateTime(input),
50
+ add: (input: string | Date, amount: number, unit: DateUnit): DateTime =>
51
+ new DateTime(input).plus(amount, unit),
52
+ subtract: (input: string | Date, amount: number, unit: DateUnit): DateTime =>
53
+ new DateTime(input).minus(amount, unit),
54
+ diff: (a: string | Date, b: string | Date, unit: DateUnit): number =>
55
+ new DateTime(a).diff(b, unit),
56
+ inRange,
57
+ rangeContains: (
58
+ outer: DateRange,
59
+ inner: DateRange,
60
+ options?: RangeCompareOptions,
61
+ ): boolean => containsRange(outer, inner, options),
62
+ rangesOverlap: (
63
+ a: DateRange,
64
+ b: DateRange,
65
+ options?: RangeCompareOptions,
66
+ ): boolean => overlapsRange(a, b, options),
67
+ rangeRelation: (a: DateRange, b: DateRange, options?: RangeCompareOptions) =>
68
+ analyzeRange(a, b, options),
69
+ rrule: (
70
+ startIso: string,
71
+ rrule: string | RRuleBuild,
72
+ limit = 100,
73
+ ): string[] => expandRRule(startIso, rrule, limit),
74
+ buildRRule: (rule: RRuleBuild): string => toRRuleString(rule),
75
+ };
package/src/native.ts ADDED
@@ -0,0 +1,124 @@
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
+
9
+ export interface NativeChronos {
10
+ add(iso: string, amount: number, unit: string): string;
11
+ diff(aIso: string, bIso: string, unit: string): number;
12
+ startOf(iso: string, unit: string): string;
13
+ endOf(iso: string, unit: string): string;
14
+ format(iso: string, pattern: string): string;
15
+ validateTimezone(zone: string): string;
16
+ toZone(
17
+ utcIso: string,
18
+ zone: string,
19
+ ): { iso: string; offsetMinutes: number; zoneName: string };
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
+
47
+ let native: NativeChronos | undefined;
48
+ let loadError: unknown;
49
+
50
+ const isNode =
51
+ typeof globalThis.process !== "undefined" &&
52
+ typeof globalThis.process.versions?.node === "string";
53
+
54
+ if (isNode) {
55
+ try {
56
+ const { createRequire } = await import("node:module");
57
+ const { dirname, join } = await import("node:path");
58
+ const { fileURLToPath } = await import("node:url");
59
+ const { arch, platform } = await import("node:process");
60
+
61
+ const nodeRequire = createRequire(import.meta.url);
62
+ const currentDir = dirname(fileURLToPath(import.meta.url));
63
+
64
+ const platformMap: Record<string, string> = {
65
+ "linux-x64": "linux-x64-gnu",
66
+ "linux-arm64": "linux-arm64-gnu",
67
+ "darwin-x64": "darwin-x64",
68
+ "darwin-arm64": "darwin-arm64",
69
+ "win32-x64": "win32-x64-msvc",
70
+ };
71
+
72
+ const suffix = platformMap[`${platform}-${arch}`];
73
+ if (suffix) {
74
+ native = nodeRequire(join(currentDir, `../index.${suffix}.node`));
75
+ }
76
+ } catch (e) {
77
+ loadError = e;
78
+ }
79
+ } else {
80
+ try {
81
+ // wasm-bindgen preserves Rust snake_case names (start_of, end_of, etc.)
82
+ // while NAPI-RS auto-converts to camelCase. We adapt the WASM module
83
+ // shape (which uses bigint for large integers) to the NativeChronos
84
+ // interface (number-based), so consumers don't deal with the difference.
85
+ const wasm = await import("../wasm/chronos_engine_wasm.js");
86
+ await wasm.default();
87
+ native = {
88
+ add: (iso, amount, unit) => wasm.add(iso, BigInt(amount), unit),
89
+ diff: (a, b, unit) => Number(wasm.diff(a, b, unit)),
90
+ startOf: (iso, unit) => wasm.start_of(iso, unit),
91
+ endOf: (iso, unit) => wasm.end_of(iso, unit),
92
+ format: (iso, pattern) => wasm.format(iso, pattern),
93
+ validateTimezone: (zone) => wasm.validate_timezone(zone),
94
+ toZone: (iso, zone) => wasm.to_zone(iso, zone),
95
+ addInZone: (iso, amount, unit, zone) =>
96
+ wasm.add_in_zone(iso, BigInt(amount), unit, zone),
97
+ diffInZone: (a, b, unit, zone) =>
98
+ Number(wasm.diff_in_zone(a, b, unit, zone)),
99
+ zoneOffset: (iso, zone) => wasm.zone_offset(iso, zone),
100
+ fromLocal: (naive, zone) => wasm.from_local(naive, zone),
101
+ parseRfc2822: (input) => wasm.parse_rfc2822(input),
102
+ parseSql: (input) => wasm.parse_sql(input),
103
+ parseHttp: (input) => wasm.parse_http(input),
104
+ rruleExpand: (start, rrule, limit) =>
105
+ wasm.rrule_expand(start, rrule, limit),
106
+ calendarParts: (iso) => wasm.calendar_parts(iso),
107
+ };
108
+ } catch (e) {
109
+ loadError = e;
110
+ }
111
+ }
112
+
113
+ export function nativeChronos(): NativeChronos {
114
+ if (!native) {
115
+ throw new Error(
116
+ `[CHRONOS_ENGINE_REQUIRED] The Chronos engine is required but not loaded.\n` +
117
+ ` Environment: ${isNode ? "Node" : "Browser"}\n` +
118
+ ` Reason: ${loadError ?? "binary not found"}\n` +
119
+ ` Fix (Node): cd packages/chronos && pnpm build:napi\n` +
120
+ ` Fix (Browser): cd packages/chronos && pnpm build:wasm`,
121
+ );
122
+ }
123
+ return native;
124
+ }
package/src/rrule.ts ADDED
@@ -0,0 +1,102 @@
1
+ import { nativeChronos } from "./native.js";
2
+
3
+ export type RRuleWeekday = "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | "SU";
4
+ export type RRuleByDayToken = RRuleWeekday | `${number}${RRuleWeekday}`;
5
+
6
+ export interface RRuleBuild {
7
+ freq:
8
+ | "SECONDLY"
9
+ | "MINUTELY"
10
+ | "HOURLY"
11
+ | "DAILY"
12
+ | "WEEKLY"
13
+ | "MONTHLY"
14
+ | "YEARLY";
15
+ interval?: number;
16
+ wkst?: RRuleWeekday;
17
+ byDay?: RRuleByDayToken[];
18
+ byMonthDay?: number[];
19
+ byMonth?: number[];
20
+ byWeekNo?: number[];
21
+ byYearDay?: number[];
22
+ bySetPos?: number[];
23
+ byHour?: number[];
24
+ byMinute?: number[];
25
+ bySecond?: number[];
26
+ count?: number;
27
+ until?: string;
28
+ }
29
+
30
+ function pushNumericList(
31
+ parts: string[],
32
+ key: string,
33
+ values: number[] | undefined,
34
+ ): void {
35
+ if (!values || values.length === 0) return;
36
+ parts.push(`${key}=${values.join(",")}`);
37
+ }
38
+
39
+ export function toRRuleString(rule: RRuleBuild): string {
40
+ const parts: string[] = [`FREQ=${rule.freq}`];
41
+
42
+ if (rule.interval && rule.interval !== 1)
43
+ parts.push(`INTERVAL=${rule.interval}`);
44
+ if (rule.wkst) parts.push(`WKST=${rule.wkst}`);
45
+ if (rule.byDay?.length) parts.push(`BYDAY=${rule.byDay.join(",")}`);
46
+
47
+ pushNumericList(parts, "BYMONTHDAY", rule.byMonthDay);
48
+ pushNumericList(parts, "BYMONTH", rule.byMonth);
49
+ pushNumericList(parts, "BYWEEKNO", rule.byWeekNo);
50
+ pushNumericList(parts, "BYYEARDAY", rule.byYearDay);
51
+ pushNumericList(parts, "BYSETPOS", rule.bySetPos);
52
+ pushNumericList(parts, "BYHOUR", rule.byHour);
53
+ pushNumericList(parts, "BYMINUTE", rule.byMinute);
54
+ pushNumericList(parts, "BYSECOND", rule.bySecond);
55
+
56
+ if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);
57
+ if (rule.until) {
58
+ // RFC 5545 §3.3.10 UNTIL format: YYYYMMDDTHHMMSSZ (no hyphens/colons/ms).
59
+ parts.push(
60
+ `UNTIL=${new Date(rule.until)
61
+ .toISOString()
62
+ .replace(/[-:]/g, "")
63
+ .replace(/\.\d+Z$/, "Z")}`,
64
+ );
65
+ }
66
+
67
+ return parts.join(";");
68
+ }
69
+
70
+ /**
71
+ * Expand an RRULE into concrete occurrence dates. **NAPI-only** — the Rust
72
+ * engine handles the full RFC 5545 spec (BYDAY, BYSETPOS, BYMONTHDAY,
73
+ * BYWEEKNO, BYYEARDAY, UNTIL, COUNT, INTERVAL, WKST). No TS fallback.
74
+ */
75
+ /**
76
+ * Hard cap on `expandRRule(... , limit)` so a caller passing an absurd
77
+ * number (or one derived from user input) can't ask the Rust engine to
78
+ * allocate gigabytes / run for seconds. 10_000 covers any realistic
79
+ * pagination + cushion (a year of every-minute occurrences = ~525k, so
80
+ * apps wanting that should iterate or stream, not call once).
81
+ */
82
+ const MAX_RRULE_EXPAND = 10_000;
83
+
84
+ export function expandRRule(
85
+ startIso: string,
86
+ rrule: string | RRuleBuild,
87
+ limit = 100,
88
+ ): string[] {
89
+ if (!Number.isInteger(limit) || limit < 1) {
90
+ throw new RangeError(
91
+ `expandRRule(limit) must be a positive integer (got ${String(limit)})`,
92
+ );
93
+ }
94
+ if (limit > MAX_RRULE_EXPAND) {
95
+ throw new RangeError(
96
+ `expandRRule(limit=${limit}) exceeds the safety cap of ${MAX_RRULE_EXPAND}. ` +
97
+ "Page through the result set instead of asking for everything at once.",
98
+ );
99
+ }
100
+ const built = typeof rrule === "string" ? rrule : toRRuleString(rrule);
101
+ return nativeChronos().rruleExpand(startIso, built, limit);
102
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared helpers for DateTime and RRule modules.
3
+ */
4
+
5
+ /**
6
+ * Normalize an ISO 8601 string: strip the `.000` when subseconds are zero
7
+ * for compact output that matches `new Date(iso).toISOString().replace('.000Z', 'Z')`.
8
+ * When subseconds are non-zero, preserve them so precision isn't silently lost.
9
+ */
10
+ export function normalizeIso(iso: string): string {
11
+ return iso.replace(".000Z", "Z");
12
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Type stub for the WASM module emitted by `wasm-pack build` during the
3
+ * chronos release pipeline. The actual `.js` + `.wasm` artifacts are not
4
+ * checked into the source tree; they live alongside this `.d.ts` at
5
+ * publish time so the browser/edge fallback path in `src/native.ts`
6
+ * resolves at runtime. The stub lets `tsc --noEmit` typecheck during
7
+ * development without forcing every contributor to run `wasm-pack`.
8
+ *
9
+ * Signature mirrors the wasm-bindgen-generated `chronos_engine_wasm.js`,
10
+ * preserving Rust snake_case names that `src/native.ts` adapts to the
11
+ * camelCase `NativeChronos` interface.
12
+ */
13
+ export default function init(): Promise<unknown>;
14
+ export function add(iso: string, amount: bigint, unit: string): string;
15
+ export function diff(a: string, b: string, unit: string): bigint;
16
+ export function start_of(iso: string, unit: string): string;
17
+ export function end_of(iso: string, unit: string): string;
18
+ export function format(iso: string, pattern: string): string;
19
+ export function validate_timezone(zone: string): string;
20
+ export function to_zone(
21
+ iso: string,
22
+ zone: string,
23
+ ): { iso: string; offsetMinutes: number; zoneName: string };
24
+ export function add_in_zone(
25
+ iso: string,
26
+ amount: bigint,
27
+ unit: string,
28
+ zone: string,
29
+ ): string;
30
+ export function diff_in_zone(
31
+ a: string,
32
+ b: string,
33
+ unit: string,
34
+ zone: string,
35
+ ): bigint;
36
+ export function zone_offset(iso: string, zone: string): number;
37
+ export function from_local(naive: string, zone: string): string;
38
+ export function parse_rfc2822(input: string): string;
39
+ export function parse_sql(input: string): string;
40
+ export function parse_http(input: string): string;
41
+ export function rrule_expand(
42
+ start: string,
43
+ rrule: string,
44
+ limit: number,
45
+ ): string[];
46
+ export function calendar_parts(iso: string): {
47
+ year: number;
48
+ month: number;
49
+ day: number;
50
+ hour: number;
51
+ minute: number;
52
+ second: number;
53
+ millisecond: number;
54
+ weekday: number;
55
+ weekNumber: number;
56
+ weekYear: number;
57
+ ordinal: number;
58
+ quarter: number;
59
+ daysInMonth: number;
60
+ daysInYear: number;
61
+ isLeapYear: boolean;
62
+ };