@cosmicdrift/kumiko-headless 0.156.2 → 0.157.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-headless",
3
- "version": "0.156.2",
3
+ "version": "0.157.0",
4
4
  "description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@cosmicdrift/kumiko-framework": "0.156.2",
39
+ "@cosmicdrift/kumiko-framework": "0.157.0",
40
40
  "temporal-polyfill": "^0.3.2",
41
41
  "zod": "^4.4.3"
42
42
  },
@@ -68,4 +68,25 @@ describe("applyFormatSpec — timestamp/date (formatDateCell-Pfad)", () => {
68
68
  expect(applyFormatSpec({ format: "timestamp" }, "kein-datum")).toBe("kein-datum");
69
69
  expect(applyFormatSpec({ format: "date" }, "kein-datum")).toBe("kein-datum");
70
70
  });
71
+
72
+ test("offset-lose Timestamps (kein Z/Offset) fallen NICHT auf den Rohstring zurück", () => {
73
+ // Temporal.Instant.from is stricter than the old `new Date(raw)` —
74
+ // without a UTC designator/offset it throws. Both forms must still
75
+ // format instead of passing through raw (see toInstant fallback in
76
+ // index.ts).
77
+ const withoutOffset = "2026-01-15T12:00:00";
78
+ const withoutTime = "2026-01-15 12:00:00";
79
+ for (const raw of [withoutOffset, withoutTime]) {
80
+ const out = applyFormatSpec({ format: "timestamp", locale: "en-US" }, raw);
81
+ expect(out).not.toBe(raw);
82
+ expect(out).toContain("2026");
83
+ }
84
+ });
85
+
86
+ test("offset-loser Timestamp im date-Format fällt NICHT auf den Rohstring zurück", () => {
87
+ const withoutOffset = "2026-01-15T12:00:00";
88
+ const out = applyFormatSpec({ format: "date", locale: "en-US" }, withoutOffset);
89
+ expect(out).not.toBe(withoutOffset);
90
+ expect(out).toContain("2026");
91
+ });
71
92
  });
@@ -9,7 +9,20 @@ function toPlainDate(raw: string): Temporal.PlainDate {
9
9
  } catch {
10
10
  // "date"-typed field stored as a full instant (day-boundary timestamp) —
11
11
  // take the calendar date in the local zone rather than fail.
12
- return Temporal.Instant.from(raw).toZonedDateTimeISO(Temporal.Now.timeZoneId()).toPlainDate();
12
+ return toInstant(raw).toZonedDateTimeISO(Temporal.Now.timeZoneId()).toPlainDate();
13
+ }
14
+ }
15
+
16
+ // Temporal.Instant.from is stricter than the `new Date(raw)` this replaced:
17
+ // it requires a UTC designator/offset (Z/+hh:mm). Timestamps without one
18
+ // (e.g. "2026-07-18T12:00:00", still valid input to `new Date`) throw here
19
+ // instead of parsing — fall back to reading them as a local wall-clock time,
20
+ // same posture as toPlainDate's own fallback above.
21
+ function toInstant(raw: string): Temporal.Instant {
22
+ try {
23
+ return Temporal.Instant.from(raw);
24
+ } catch {
25
+ return Temporal.PlainDateTime.from(raw).toZonedDateTime(Temporal.Now.timeZoneId()).toInstant();
13
26
  }
14
27
  }
15
28
 
@@ -34,7 +47,7 @@ function formatDateCell(
34
47
  dateStyle: opts.dateStyle,
35
48
  });
36
49
  }
37
- return Temporal.Instant.from(raw).toLocaleString(locale, {
50
+ return toInstant(raw).toLocaleString(locale, {
38
51
  dateStyle: opts.dateStyle,
39
52
  timeStyle: opts.timeStyle,
40
53
  });
@@ -42,7 +55,7 @@ function formatDateCell(
42
55
  if (type === "date") {
43
56
  return toPlainDate(raw).toLocaleString(locale);
44
57
  }
45
- return Temporal.Instant.from(raw).toLocaleString(locale, {
58
+ return toInstant(raw).toLocaleString(locale, {
46
59
  year: "numeric",
47
60
  month: "short",
48
61
  day: "numeric",