@classytic/primitives 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +920 -751
  2. package/README.md +160 -159
  3. package/dist/approval.d.mts +50 -11
  4. package/dist/approval.mjs +44 -10
  5. package/dist/bank-transaction.d.mts +1 -2
  6. package/dist/{brand-LLXqoh5J.d.mts → brand-Lj_75Y_r.d.mts} +6 -6
  7. package/dist/brand.d.mts +1 -1
  8. package/dist/cadence-5X6TiD49.d.mts +107 -0
  9. package/dist/cadence.d.mts +1 -92
  10. package/dist/cadence.mjs +135 -6
  11. package/dist/calendar-DJWSbcgE.d.mts +46 -0
  12. package/dist/calendar.d.mts +1 -45
  13. package/dist/calendar.mjs +2 -2
  14. package/dist/canonical.mjs +17 -0
  15. package/dist/condition.mjs +2 -2
  16. package/dist/context.d.mts +28 -2
  17. package/dist/currency.d.mts +2 -2
  18. package/dist/currency.mjs +134 -19
  19. package/dist/environment.d.mts +85 -0
  20. package/dist/environment.mjs +48 -0
  21. package/dist/event-infra.d.mts +16 -32
  22. package/dist/event-infra.mjs +16 -46
  23. package/dist/idempotency.d.mts +263 -0
  24. package/dist/idempotency.mjs +226 -0
  25. package/dist/identity-verification.d.mts +0 -1
  26. package/dist/memory-outbox.d.mts +63 -0
  27. package/dist/memory-outbox.mjs +172 -0
  28. package/dist/mixin.d.mts +1 -1
  29. package/dist/monetization.d.mts +1 -2
  30. package/dist/money-CfAvfNiM.d.mts +346 -0
  31. package/dist/money.d.mts +1 -140
  32. package/dist/money.mjs +6 -15
  33. package/dist/otp.mjs +1 -1
  34. package/dist/outbox.d.mts +49 -2
  35. package/dist/outbox.mjs +64 -1
  36. package/dist/payment-allocation-status.d.mts +10 -4
  37. package/dist/payment-allocation-status.mjs +14 -0
  38. package/dist/payment-events.d.mts +132 -5
  39. package/dist/payment-events.mjs +34 -2
  40. package/dist/payment-gateway.d.mts +211 -8
  41. package/dist/payment-gateway.mjs +18 -1
  42. package/dist/payment-method-kind.d.mts +23 -11
  43. package/dist/payment-method-kind.mjs +17 -0
  44. package/dist/period.d.mts +149 -7
  45. package/dist/period.mjs +316 -5
  46. package/dist/person-parse.d.mts +81 -0
  47. package/dist/person-parse.mjs +109 -0
  48. package/dist/phone.d.mts +0 -1
  49. package/dist/phone.mjs +3 -3
  50. package/dist/proration.d.mts +2 -3
  51. package/dist/proration.mjs +18 -6
  52. package/dist/retention.mjs +3 -3
  53. package/dist/shelf-life.mjs +1 -1
  54. package/dist/sla-policy.d.mts +1 -2
  55. package/dist/state-diagram.d.mts +0 -1
  56. package/dist/state-diagram.mjs +1 -1
  57. package/dist/state-machine.mjs +13 -0
  58. package/dist/suspension.d.mts +6 -4
  59. package/dist/suspension.mjs +3 -2
  60. package/dist/timezone-CxUjxmRU.d.mts +113 -0
  61. package/dist/timezone.d.mts +2 -100
  62. package/dist/timezone.mjs +38 -3
  63. package/dist/unit-cost-rate.d.mts +1 -3
  64. package/package.json +249 -232
  65. package/dist/currency-BCJnJBlb.d.mts +0 -119
@@ -54,7 +54,7 @@ function describeStateMachine(machine) {
54
54
  * that can never leave the state it is stuck in.
55
55
  */
56
56
  function unreachableStates(machine, initial) {
57
- const seen = new Set([initial]);
57
+ const seen = /* @__PURE__ */ new Set([initial]);
58
58
  const queue = [initial];
59
59
  while (queue.length > 0) for (const next of machine.validTargets(queue.pop())) if (!seen.has(next)) {
60
60
  seen.add(next);
@@ -149,6 +149,19 @@ function defineStateMachine(definition) {
149
149
  */
150
150
  async function assertAndClaim(machine, repo, id, args) {
151
151
  const sources = Array.isArray(args.from) ? args.from : [args.from];
152
+ /**
153
+ * An EMPTY source list must never reach `claim()`.
154
+ *
155
+ * `validSources(to)` returns `[]` for a status nothing transitions into, and
156
+ * the multi-source idiom in the docblock above (`from: machine.validSources(x)`)
157
+ * makes that easy to hit. With `[]` the assert loop below runs ZERO times — the
158
+ * domain check silently does not happen — and the CAS is then handed an empty
159
+ * allow-list. Mongo's `$in: []` matches nothing so today that fails closed and
160
+ * looks exactly like a race-loss (`null`), which is the wrong diagnosis; a kit
161
+ * that read an empty list as "no constraint" would fail OPEN and claim from any
162
+ * state at all. An empty allow-list must never mean "any".
163
+ */
164
+ if (sources.length === 0) throw new IllegalTransitionError(machine.name, id, "(none)", args.to);
152
165
  for (const from of sources) machine.assertTransition(id, from, args.to);
153
166
  return repo.claim(id, {
154
167
  from: Array.isArray(args.from) ? args.from : args.from,
@@ -1,7 +1,6 @@
1
- import { UtcOffsetMinutes } from "./calendar.mjs";
1
+ import { t as UtcOffsetMinutes } from "./calendar-DJWSbcgE.mjs";
2
2
  import { DateRange } from "./period.mjs";
3
3
  import { StatusChangeEntry } from "./status-history.mjs";
4
-
5
4
  //#region src/workflow/suspension.d.ts
6
5
  /**
7
6
  * One suspension, past or current.
@@ -84,7 +83,9 @@ declare function allowancePeriod(at: Date, periodMonths?: number, offsetMinutes?
84
83
  * December. Ignoring that double-counts a straddling pause in both periods and
85
84
  * refuses a member who is within their allowance.
86
85
  */
87
- declare function suspendedDaysInPeriod(history: readonly SuspensionSpan[], period: DateRange, /** Now — bounds still-open spans. Defaults to the period end. */at?: Date): number;
86
+ declare function suspendedDaysInPeriod(history: readonly SuspensionSpan[], period: DateRange,
87
+ /** Now — bounds still-open spans. Defaults to the period end. */
88
+ at?: Date): number;
88
89
  /** Is any span still open at `at`? */
89
90
  declare function openSpans(history: readonly SuspensionSpan[], at: Date): readonly SuspensionSpan[];
90
91
  /**
@@ -144,7 +145,8 @@ declare function spansFromStatusHistory<TStatus extends string>(history: readonl
144
145
  * resume yet (a crash between the write and the append). Leaving it open would
145
146
  * charge the member allowance for time they were not frozen.
146
147
  */
147
- readonly currentStatus?: TStatus | undefined; /** Closes a genuinely-open trailing span for counting. Defaults to open. */
148
+ readonly currentStatus?: TStatus | undefined;
149
+ /** Closes a genuinely-open trailing span for counting. Defaults to open. */
148
150
  readonly openEndedAt?: Date | undefined;
149
151
  }): SuspensionSpan[];
150
152
  //#endregion
@@ -39,11 +39,12 @@ import { addMonths, startOfDay } from "./calendar.mjs";
39
39
  * PURE: no clock reads beyond the `at` you pass, no I/O. The same evaluation runs
40
40
  * on the server, in an admin preview, and in a test.
41
41
  */
42
- const MS_PER_DAY = 1440 * 60 * 1e3;
42
+ const MS_PER_DAY = 864e5;
43
43
  /** The allowance period containing `at`, as a half-open range. */
44
44
  function allowancePeriod(at, periodMonths = 12, offsetMinutes = 0) {
45
+ const anchor = startOfDay(at, offsetMinutes);
45
46
  return {
46
- start: addMonths(startOfDay(at, offsetMinutes), -periodMonths),
47
+ start: addMonths(anchor, -periodMonths),
47
48
  end: at
48
49
  };
49
50
  }
@@ -0,0 +1,113 @@
1
+ import { t as Brand } from "./brand-Lj_75Y_r.mjs";
2
+ import { s as IsoWeekday } from "./cadence-5X6TiD49.mjs";
3
+ import { t as UtcOffsetMinutes } from "./calendar-DJWSbcgE.mjs";
4
+ //#region src/scheduling/timezone.d.ts
5
+ declare class TimeZoneError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /**
9
+ * Is `zone` a valid IANA timezone name on this runtime's ICU data?
10
+ * Use at BOOT to validate host config (an offer's `timezone`, an org
11
+ * setting) — fail loud at wiring time, not on the first localized read.
12
+ */
13
+ declare function isValidTimeZone(zone: string): boolean;
14
+ /**
15
+ * Every IANA timezone name this runtime supports — the data source for a
16
+ * Google-Calendar-style zone picker. Straight from ICU via
17
+ * `Intl.supportedValuesOf` (Node >= 18); no bundled tz list to go stale.
18
+ *
19
+ * UIs typically label each entry with its current offset:
20
+ * `listTimeZones().map((z) => ({ zone: z, label: zoneOffsetLabel(now, z) }))`.
21
+ */
22
+ declare function listTimeZones(): readonly string[];
23
+ /**
24
+ * The zone's UTC offset AT `instant`, in minutes east of UTC — DST-exact.
25
+ * `zoneOffsetMinutes(july, 'Europe/London')` → 60; `(january, …)` → 0;
26
+ * `(any, 'Asia/Dhaka')` → 360.
27
+ *
28
+ * This is the value `/calendar`'s docstring tells hosts to "resolve and pass
29
+ * in": `startOfMonth(instant, zoneOffsetMinutes(instant, zone))`.
30
+ */
31
+ declare function zoneOffsetMinutes(instant: Date, zone: string): UtcOffsetMinutes;
32
+ /**
33
+ * The zone's offset label AT `instant` — `'GMT+06:00'` / `'GMT-05:30'` /
34
+ * `'GMT'`. Ready-made display text for zone pickers.
35
+ */
36
+ declare function zoneOffsetLabel(instant: Date, zone: string): string;
37
+ interface LocalTimeParts {
38
+ /** ISO weekday in the zone — 1=Mon … 7=Sun (matches `/cadence`). */
39
+ isoWeekday: IsoWeekday;
40
+ /** Hour of day in the zone, 0–23. */
41
+ hour: number;
42
+ /** Minute of hour, 0–59. */
43
+ minute: number;
44
+ /** `hour * 60 + minute` — the shape "HH:MM window" checks want. */
45
+ minutesOfDay: number;
46
+ }
47
+ /**
48
+ * The wall-clock weekday + time of `instant` in `zone` — what pricing
49
+ * schedules ("peak Fri 17:00–22:00"), slot grids, and working-hours checks
50
+ * evaluate against. DST-exact per instant.
51
+ */
52
+ declare function localTimeParts(instant: Date, zone: string): LocalTimeParts;
53
+ /**
54
+ * A calendar day — `'YYYY-MM-DD'` — with NO time and NO zone until one is
55
+ * bound. The type for hotel nights, POS business dates, statement periods,
56
+ * VAT filing years. Lexicographic order == chronological order, so it's
57
+ * index- and range-query-friendly as a Mongo string key.
58
+ */
59
+ type CivilDate = Brand<string, 'CivilDate'>;
60
+ /** Runtime guard for external input. */
61
+ declare function isCivilDate(value: unknown): value is CivilDate;
62
+ /** Validate + brand a `'YYYY-MM-DD'` string. Throws on malformed input. */
63
+ declare function civilDate(value: string): CivilDate;
64
+ /**
65
+ * The calendar day `instant` falls on IN `zone` — e.g. the POS business
66
+ * date: `civilDateOf(new Date(), 'Asia/Dhaka')`. Never depends on the
67
+ * server's TZ env.
68
+ */
69
+ declare function civilDateOf(instant: Date, zone: string): CivilDate;
70
+ /**
71
+ * Bind a civil date (+ optional wall time) to a zone → the exact instant.
72
+ * `civilDateToInstant(night, hotelZone, { hour: 14 })` = check-in instant;
73
+ * with no `time`, local midnight — the DST-exact "start of that day in that
74
+ * zone".
75
+ *
76
+ * Two-pass offset resolution: the offset is looked up AT the candidate
77
+ * instant, then re-checked, so days where a DST transition sits between the
78
+ * wall time and the first guess still resolve correctly. Honest limit: a
79
+ * wall time that does not exist (inside a spring-forward gap) resolves to
80
+ * the instant the shifted clock reaches — the same choice Temporal's
81
+ * `disambiguation: 'compatible'` makes.
82
+ */
83
+ declare function civilDateToInstant(cd: CivilDate, zone: string, time?: {
84
+ hour?: number;
85
+ minute?: number;
86
+ second?: number;
87
+ millisecond?: number;
88
+ }): Date;
89
+ /**
90
+ * Pure calendar-day arithmetic — `addCivilDays('2026-02-28', 1)` →
91
+ * `'2026-03-01'`. Zone-free by definition (a civil date has no zone);
92
+ * use it to iterate hotel nights / statement days.
93
+ */
94
+ declare function addCivilDays(cd: CivilDate, days: number): CivilDate;
95
+ /**
96
+ * Whole days from `a` to `b` (positive when `b` is later) — the NIGHTS
97
+ * count for a `[checkIn, checkOut)` stay expressed as civil dates.
98
+ */
99
+ /**
100
+ * The ISO-8601 week a civil date belongs to, as `YYYY-Www`.
101
+ *
102
+ * Zone-free by definition — a civil date has no zone — so callers resolve the
103
+ * date in their business timezone FIRST (`civilDateOf`) and label it here.
104
+ *
105
+ * ISO week 1 is the week containing the first Thursday, which means the week
106
+ * YEAR can differ from the date's own: 2025-12-29 is `2026-W01`. Naive
107
+ * "day-of-year / 7" numbering gets that wrong every few years, silently
108
+ * splitting one business week across two report buckets.
109
+ */
110
+ declare function isoWeekOfCivilDate(cd: CivilDate): string;
111
+ declare function civilDaysBetween(a: CivilDate, b: CivilDate): number;
112
+ //#endregion
113
+ export { civilDate as a, civilDaysBetween as c, isoWeekOfCivilDate as d, listTimeZones as f, zoneOffsetMinutes as h, addCivilDays as i, isCivilDate as l, zoneOffsetLabel as m, LocalTimeParts as n, civilDateOf as o, localTimeParts as p, TimeZoneError as r, civilDateToInstant as s, CivilDate as t, isValidTimeZone as u };
@@ -1,100 +1,2 @@
1
- import { t as Brand } from "./brand-LLXqoh5J.mjs";
2
- import { IsoWeekday } from "./cadence.mjs";
3
- import { UtcOffsetMinutes } from "./calendar.mjs";
4
-
5
- //#region src/scheduling/timezone.d.ts
6
- declare class TimeZoneError extends Error {
7
- constructor(message: string);
8
- }
9
- /**
10
- * Is `zone` a valid IANA timezone name on this runtime's ICU data?
11
- * Use at BOOT to validate host config (an offer's `timezone`, an org
12
- * setting) — fail loud at wiring time, not on the first localized read.
13
- */
14
- declare function isValidTimeZone(zone: string): boolean;
15
- /**
16
- * Every IANA timezone name this runtime supports — the data source for a
17
- * Google-Calendar-style zone picker. Straight from ICU via
18
- * `Intl.supportedValuesOf` (Node >= 18); no bundled tz list to go stale.
19
- *
20
- * UIs typically label each entry with its current offset:
21
- * `listTimeZones().map((z) => ({ zone: z, label: zoneOffsetLabel(now, z) }))`.
22
- */
23
- declare function listTimeZones(): readonly string[];
24
- /**
25
- * The zone's UTC offset AT `instant`, in minutes east of UTC — DST-exact.
26
- * `zoneOffsetMinutes(july, 'Europe/London')` → 60; `(january, …)` → 0;
27
- * `(any, 'Asia/Dhaka')` → 360.
28
- *
29
- * This is the value `/calendar`'s docstring tells hosts to "resolve and pass
30
- * in": `startOfMonth(instant, zoneOffsetMinutes(instant, zone))`.
31
- */
32
- declare function zoneOffsetMinutes(instant: Date, zone: string): UtcOffsetMinutes;
33
- /**
34
- * The zone's offset label AT `instant` — `'GMT+06:00'` / `'GMT-05:30'` /
35
- * `'GMT'`. Ready-made display text for zone pickers.
36
- */
37
- declare function zoneOffsetLabel(instant: Date, zone: string): string;
38
- interface LocalTimeParts {
39
- /** ISO weekday in the zone — 1=Mon … 7=Sun (matches `/cadence`). */
40
- isoWeekday: IsoWeekday;
41
- /** Hour of day in the zone, 0–23. */
42
- hour: number;
43
- /** Minute of hour, 0–59. */
44
- minute: number;
45
- /** `hour * 60 + minute` — the shape "HH:MM window" checks want. */
46
- minutesOfDay: number;
47
- }
48
- /**
49
- * The wall-clock weekday + time of `instant` in `zone` — what pricing
50
- * schedules ("peak Fri 17:00–22:00"), slot grids, and working-hours checks
51
- * evaluate against. DST-exact per instant.
52
- */
53
- declare function localTimeParts(instant: Date, zone: string): LocalTimeParts;
54
- /**
55
- * A calendar day — `'YYYY-MM-DD'` — with NO time and NO zone until one is
56
- * bound. The type for hotel nights, POS business dates, statement periods,
57
- * VAT filing years. Lexicographic order == chronological order, so it's
58
- * index- and range-query-friendly as a Mongo string key.
59
- */
60
- type CivilDate = Brand<string, 'CivilDate'>;
61
- /** Runtime guard for external input. */
62
- declare function isCivilDate(value: unknown): value is CivilDate;
63
- /** Validate + brand a `'YYYY-MM-DD'` string. Throws on malformed input. */
64
- declare function civilDate(value: string): CivilDate;
65
- /**
66
- * The calendar day `instant` falls on IN `zone` — e.g. the POS business
67
- * date: `civilDateOf(new Date(), 'Asia/Dhaka')`. Never depends on the
68
- * server's TZ env.
69
- */
70
- declare function civilDateOf(instant: Date, zone: string): CivilDate;
71
- /**
72
- * Bind a civil date (+ optional wall time) to a zone → the exact instant.
73
- * `civilDateToInstant(night, hotelZone, { hour: 14 })` = check-in instant;
74
- * with no `time`, local midnight — the DST-exact "start of that day in that
75
- * zone".
76
- *
77
- * Two-pass offset resolution: the offset is looked up AT the candidate
78
- * instant, then re-checked, so days where a DST transition sits between the
79
- * wall time and the first guess still resolve correctly. Honest limit: a
80
- * wall time that does not exist (inside a spring-forward gap) resolves to
81
- * the instant the shifted clock reaches — the same choice Temporal's
82
- * `disambiguation: 'compatible'` makes.
83
- */
84
- declare function civilDateToInstant(cd: CivilDate, zone: string, time?: {
85
- hour?: number;
86
- minute?: number;
87
- }): Date;
88
- /**
89
- * Pure calendar-day arithmetic — `addCivilDays('2026-02-28', 1)` →
90
- * `'2026-03-01'`. Zone-free by definition (a civil date has no zone);
91
- * use it to iterate hotel nights / statement days.
92
- */
93
- declare function addCivilDays(cd: CivilDate, days: number): CivilDate;
94
- /**
95
- * Whole days from `a` to `b` (positive when `b` is later) — the NIGHTS
96
- * count for a `[checkIn, checkOut)` stay expressed as civil dates.
97
- */
98
- declare function civilDaysBetween(a: CivilDate, b: CivilDate): number;
99
- //#endregion
100
- export { CivilDate, LocalTimeParts, TimeZoneError, addCivilDays, civilDate, civilDateOf, civilDateToInstant, civilDaysBetween, isCivilDate, isValidTimeZone, listTimeZones, localTimeParts, zoneOffsetLabel, zoneOffsetMinutes };
1
+ import { a as civilDate, c as civilDaysBetween, d as isoWeekOfCivilDate, f as listTimeZones, h as zoneOffsetMinutes, i as addCivilDays, l as isCivilDate, m as zoneOffsetLabel, n as LocalTimeParts, o as civilDateOf, p as localTimeParts, r as TimeZoneError, s as civilDateToInstant, t as CivilDate, u as isValidTimeZone } from "./timezone-CxUjxmRU.mjs";
2
+ export { CivilDate, LocalTimeParts, TimeZoneError, addCivilDays, civilDate, civilDateOf, civilDateToInstant, civilDaysBetween, isCivilDate, isValidTimeZone, isoWeekOfCivilDate, listTimeZones, localTimeParts, zoneOffsetLabel, zoneOffsetMinutes };
package/dist/timezone.mjs CHANGED
@@ -78,7 +78,19 @@ function listTimeZones() {
78
78
  cachedZones = Object.freeze(intl.supportedValuesOf("timeZone"));
79
79
  return cachedZones;
80
80
  }
81
- const OFFSET_RE = /GMT(?:([+-])(\d{2}):(\d{2}))?/;
81
+ /**
82
+ * ANCHORED on purpose.
83
+ *
84
+ * The unanchored form (`/GMT(?:([+-])(\d{2}):(\d{2}))?/`) made the offset group
85
+ * OPTIONAL *and* skippable: any label the runtime emits that this pattern cannot
86
+ * fully parse — `'GMT+5:30'` from an ICU build that does not zero-pad, or a
87
+ * locale that prefixes text — still matched the bare `GMT` alternative and
88
+ * resolved to offset **0**. A half-read offset became "UTC" with no error, which
89
+ * silently shifts every business-day boundary derived from it. Anchoring makes an
90
+ * unrecognised label throw instead; `\d{1,2}` accepts the unpadded variant rather
91
+ * than mis-reading it.
92
+ */
93
+ const OFFSET_RE = /^GMT(?:([+-])(\d{1,2}):(\d{2}))?$/;
82
94
  /**
83
95
  * The zone's UTC offset AT `instant`, in minutes east of UTC — DST-exact.
84
96
  * `zoneOffsetMinutes(july, 'Europe/London')` → 60; `(january, …)` → 0;
@@ -204,7 +216,9 @@ function civilDateToInstant(cd, zone, time = {}) {
204
216
  if (!m) throw new TimeZoneError(`Invalid civil date '${cd}'.`);
205
217
  const hour = time.hour ?? 0;
206
218
  const minute = time.minute ?? 0;
207
- const wallAsUtc = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), hour, minute);
219
+ const second = time.second ?? 0;
220
+ const millisecond = time.millisecond ?? 0;
221
+ const wallAsUtc = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), hour, minute, second, millisecond);
208
222
  const firstOffset = zoneOffsetMinutes(new Date(wallAsUtc), zone);
209
223
  let instantMs = wallAsUtc - firstOffset * MINUTE_MS;
210
224
  const refined = zoneOffsetMinutes(new Date(instantMs), zone);
@@ -226,6 +240,27 @@ function addCivilDays(cd, days) {
226
240
  * Whole days from `a` to `b` (positive when `b` is later) — the NIGHTS
227
241
  * count for a `[checkIn, checkOut)` stay expressed as civil dates.
228
242
  */
243
+ /**
244
+ * The ISO-8601 week a civil date belongs to, as `YYYY-Www`.
245
+ *
246
+ * Zone-free by definition — a civil date has no zone — so callers resolve the
247
+ * date in their business timezone FIRST (`civilDateOf`) and label it here.
248
+ *
249
+ * ISO week 1 is the week containing the first Thursday, which means the week
250
+ * YEAR can differ from the date's own: 2025-12-29 is `2026-W01`. Naive
251
+ * "day-of-year / 7" numbering gets that wrong every few years, silently
252
+ * splitting one business week across two report buckets.
253
+ */
254
+ function isoWeekOfCivilDate(cd) {
255
+ const m = CIVIL_RE.exec(cd);
256
+ if (!m) throw new TimeZoneError(`Invalid civil date '${cd}'.`);
257
+ const utc = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
258
+ const dow = new Date(utc).getUTCDay() || 7;
259
+ const thursday = new Date(utc + (4 - dow) * 864e5);
260
+ const weekYear = thursday.getUTCFullYear();
261
+ const week = Math.floor((thursday.getTime() - Date.UTC(weekYear, 0, 1)) / 864e5 / 7) + 1;
262
+ return `${weekYear}-W${String(week).padStart(2, "0")}`;
263
+ }
229
264
  function civilDaysBetween(a, b) {
230
265
  const pa = CIVIL_RE.exec(a);
231
266
  const pb = CIVIL_RE.exec(b);
@@ -235,4 +270,4 @@ function civilDaysBetween(a, b) {
235
270
  return Math.round((mb - ma) / 864e5);
236
271
  }
237
272
  //#endregion
238
- export { TimeZoneError, addCivilDays, civilDate, civilDateOf, civilDateToInstant, civilDaysBetween, isCivilDate, isValidTimeZone, listTimeZones, localTimeParts, zoneOffsetLabel, zoneOffsetMinutes };
273
+ export { TimeZoneError, addCivilDays, civilDate, civilDateOf, civilDateToInstant, civilDaysBetween, isCivilDate, isValidTimeZone, isoWeekOfCivilDate, listTimeZones, localTimeParts, zoneOffsetLabel, zoneOffsetMinutes };
@@ -1,6 +1,4 @@
1
- import { r as CurrencyCode } from "./currency-BCJnJBlb.mjs";
2
- import { MinorUnits } from "./money.mjs";
3
-
1
+ import { t as MinorUnits, w as CurrencyCode } from "./money-CfAvfNiM.mjs";
4
2
  //#region src/money/unit-cost-rate.d.ts
5
3
  /** Fixed, ecosystem-wide rate scale: 6 decimal places below the minor unit. */
6
4
  declare const RATE_SCALE = 1000000;