tsgrid-ui 2.4.1 → 2.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,95 @@
2
2
 
3
3
  All notable changes to **TsGrid UI** will be documented in this file.
4
4
 
5
+ ## v2.6.0 — 2026-05-13
6
+
7
+ ### Refactor
8
+
9
+ - **`date()` extraction — `DateDeps` deps-injection pattern**: Extracted the `TsUtils.date()` method body (22 LOC, the sole `this.lang('Yesterday')` coupling in `tsutils.ts`) into `_date()` in `src/tsutils-datetime.ts` via a new minimal `DateDeps` interface (`{ lang: (phrase: string) => string }`). Class method `date()` becomes a one-line delegator: `return _date(dateStr, this.settings, { lang: this.lang.bind(this) })`. Public signature `TsUtils.date(dateStr: unknown): string` is **byte-identical** to v2.5.0. `vi.spyOn(TsUtils, 'date')` continues to work (prototype delegator). Follows the v2.3 `MessageDeps` / `NotifyDeps` canonical pattern.
10
+ - `DateDeps` is an **internal** interface backing the deps-injection delegation pattern. It is NOT exported through `src/index.ts` barrel (INV-7 byte-identical). Consumers of `TsUtils.date()` are unaffected — the public method signature and runtime behavior are identical to v2.5.0 (same string outputs for all date inputs).
11
+ - **`TsTimeResult` dedup**: Promoted `interface TsTimeResult` in `src/tsutils-datetime.ts` from non-exported to `export interface TsTimeResult`. Deleted the local duplicate declaration in `src/tsutils.ts` (lines 102-107); replaced with `import type { TsTimeResult } from './tsutils-datetime.js'` (for internal use by `isTime()`) and `export type { TsTimeResult } from './tsutils-datetime.js'` (barrel re-export). Single source-of-truth now in `tsutils-datetime.ts`. Mirrors the `TsColorRgb` precedent (`tsutils.ts:104`). `dist/tsgrid-ui.d.ts` continues to declare `TsTimeResult` exactly once (INV-LOC-1 `grep -c` = 1, line 425). Existing `import type { TsTimeResult } from 'tsgrid-ui'` consumers continue to work unchanged — same d.ts reachability, different source file.
12
+
13
+ ### Tests
14
+
15
+ - Added 7-case safety-net suite for `_date()` in `test/unit/tsutils-datetime.test.ts` committed as Phase 4 RED before extraction (INV-TDD PASS):
16
+ - T-DATE-1 — empty string → `''`
17
+ - T-DATE-2 — `null` → `''`
18
+ - T-DATE-3 — invalid date string → `''`
19
+ - T-DATE-4 — today (frozen clock, `vi.useFakeTimers()`) → regex match on `<span title="...">H:MM am|pm</span>`
20
+ - T-DATE-5 — yesterday (frozen clock) → regex match on `<span title="May 12, 2026 ...">Yesterday</span>` (proves `deps.lang` is called)
21
+ - T-DATE-6 — Unix timestamp (older) → `<span title="...">` with month-day-year
22
+ - T-DATE-7 — older date string → `<span title="...">` with month-day-year
23
+ - `vi.useFakeTimers()` / `vi.useRealTimers()` wrap the entire `_date()` describe block (INV-DATE-TIMEFREEZE PASS). Total Vitest tests: **288/288 GREEN** (v2.5.0 baseline: 280/280).
24
+
25
+ ### Bundle
26
+
27
+ Delta vs v2.5.0 baseline:
28
+
29
+ | Artifact | v2.5.0 | v2.6.0 | Δ bytes | Δ % |
30
+ |----------|--------|--------|---------|-----|
31
+ | `dist/tsgrid-ui.js` (CJS) | 946,611 B | 946,686 B | +75 B | +0.0079% |
32
+ | `dist/tsgrid-ui.es6.js` (ESM) | 944,804 B | 944,878 B | +74 B | +0.0078% |
33
+ | `dist/tsgrid-ui.min.js` (CJS min) | 508,902 B | 508,954 B | +52 B | +0.0102% |
34
+ | `dist/tsgrid-ui.es6.min.js` (ESM min) | 507,768 B | 507,819 B | +51 B | +0.0100% |
35
+ | `dist/tsgrid-ui.d.ts` | 93,022 B | 94,446 B | +1,424 B | +1.5308% |
36
+
37
+ All within ±2% gate. PASSED. The JS bundles are essentially byte-stable (≤0.011%) — the mechanical extraction added nothing meaningful at runtime. The `d.ts` grew by +1,424 B because `TsTimeResult` is now emitted as a named export from `tsutils-datetime.ts` (its source-of-truth file) rather than transitively via `isTime()` from `tsutils.ts`; additionally `DateDeps` is declared in `tsutils-datetime.ts` and included in the rolled d.ts.
38
+
39
+ **Note on P6 dist commit**: `pnpm build` was run at P6-DEDUP to enable the INV-LOC-1 audit-trail gate (`grep -c` = 1 in committed dist). P8 re-emits dist with the `v2.6.0` version banner baked into the JS headers. This is intentional and mirrors the v2.5.0 release commit (`51c00836`) pattern.
40
+
41
+ ### BC
42
+
43
+ - `TsUtils.date(dateStr: unknown): string` — signature and runtime behavior **BYTE-IDENTICAL** to v2.5.0. All existing call sites work unchanged.
44
+ - `TsUtils.isTime(val: unknown, retTime?: boolean): boolean | TsTimeResult` — signature **BYTE-IDENTICAL** to v2.5.0.
45
+ - `TsTimeResult` — shape `{ hours: number; minutes: number; seconds: number }` unchanged. Continues to be reachable in `dist/tsgrid-ui.d.ts` at the same surface. Existing `import type { TsTimeResult } from 'tsgrid-ui'` consumers unaffected.
46
+ - `DateDeps` — NEW **internal** interface. Not exported through `src/index.ts` barrel (INV-7 byte-identical). Consumers do not depend on it; `_date()` is not a public export. Spec requirement A-4 (emit `DateDeps` in d.ts as a named export) is NOT honored because honoring it would require modifying `src/index.ts`, violating INV-7. The type IS present in the rolled d.ts (tsutils-datetime.ts is included by tsup), but is not consumer-reachable via a top-level import path. This is a documented spec divergence: INV-7 (backwards compat, blocking) takes precedence over A-4 (additive surface, non-blocking for existing consumers). Future revision: export `DateDeps` from `src/index.ts` if consumers need it.
47
+ - `src/index.ts` barrel — **byte-identical** to v2.5.0 (INV-7 PASS).
48
+ - SEMVER MINOR. BC verdict: NONE.
49
+
50
+ ### Internal
51
+
52
+ - v2.5 SUGG-5 (INV-8 over `src/tsutils-color.ts`) **CLOSED by verification**: ESLint `no-restricted-syntax` glob `src/tsutils-*.ts` already covers `tsutils-color.ts`. The two `arguments` occurrences at lines 110 and 145 of that file are inside explanatory comments (`// ... example: arguments[0]...`), not AST nodes — the rule does not fire on comment text. Confirmed by INV-LINT-INV8 canary: injecting `void arguments.length;` into `src/tsutils-datetime.ts` triggers `pnpm lint` EXIT 1 with the expected diagnostic; reverting restores EXIT 0. No code change required. No carry-forward to v2.7+ SUGG list.
53
+
54
+ ---
55
+
56
+ ## v2.5.0 — 2026-05-13
57
+
58
+ ### Refactor
59
+
60
+ Decomposed the **date-time cluster** (8 methods, 293 LOC) out of `TsUtils` into a new leaf module `src/tsutils-datetime.ts` — **no breaking changes**, public API preserved. Class methods remain; bodies are now one-line delegators routing to pure functions in the sibling module.
61
+
62
+ - `src/tsutils-datetime.ts` — `isDate`, `isTime`, `isDateTime`, `age`, `interval`, `formatDate`, `formatTime`, `formatDateTime` extracted as stateless functions (~427 LOC including header + types + 8 function bodies). Zero `this.X` references in function bodies (INV-9); no runtime import from `tsbase.ts` or `tsutils.ts` (INV-4 leaf rule). Only allowed imports: `_isInt` from `tsutils-type-guards.js`; `TsUISettings` as a type-only import from `tsutils.js` (erased at emit — same precedent as `tsutils-message.ts` and `tsutils-type-guards.ts`).
63
+ - `settings` parameter injected by reference (`this.settings`) in each delegator so TsLocale runtime mutations to `fullmonths`, `shortmonths`, `dateFormat`, `timeFormat`, `datetimeFormat` flow through without restart.
64
+ - Intra-cluster cross-calls become module-level function references: `_isDateTime` calls `_isDate` + `_isTime` directly; `_formatDateTime` calls `_formatDate` + `_formatTime` directly; `_formatTime` calls `_isTime` directly (R-DT-2 / R-DT-8 mitigations — zero `this.X` in extracted bodies).
65
+ - `date()` stays in `tsutils.ts` (sole `this.lang('Yesterday')` coupling; deferred to v2.6 locale cycle). `formatters` initializer stays in `tsutils.ts` (goes through class delegators — one extra hop within bundle budget per R-DT-9).
66
+ - `src/tsutils.ts` shrinks from ~1,470 → ~1,183 LOC (293 cluster LOC removed, 24 LOC delegators added). Mirrors the v2.4 DOM cluster pattern (engram #889).
67
+
68
+ `TsUtils` singleton shape and all ~78 call sites: **UNCHANGED**. SEMVER MINOR. BC verdict: NONE.
69
+
70
+ ### Tests
71
+
72
+ - Added 56 unit tests (224 → 280) across 8 method blocks in `test/unit/tsutils-datetime.test.ts`: `isDate` (12), `isTime` (8), `isDateTime` (7), `age` (8), `interval` (5), `formatDate` (5), `formatTime` (4), `formatDateTime` (3) + delegation spy (2). jsdom feasibility: 100% — no Playwright-only paths in this cluster. Safety-net tests committed as Phase 4 RED before extraction (INV-TDD PASS).
73
+
74
+ ### Bundle
75
+
76
+ Delta vs v2.4.1 baseline:
77
+
78
+ | Artifact | v2.4.1 | v2.5.0 | Delta | % |
79
+ |----------|--------|--------|-------|---|
80
+ | `dist/tsgrid-ui.js` (CJS) | 946,553 B | 946,611 B | +58 B | +0.006% |
81
+ | `dist/tsgrid-ui.es6.js` (ESM) | 944,746 B | 944,804 B | +58 B | +0.006% |
82
+ | `dist/tsgrid-ui.min.js` (CJS min) | 508,818 B | 508,902 B | +84 B | +0.017% |
83
+ | `dist/tsgrid-ui.es6.min.js` (ESM min) | 507,684 B | 507,768 B | +84 B | +0.017% |
84
+ | `dist/tsgrid-ui.d.ts` | 93,022 B | 93,022 B | 0 B | 0% |
85
+
86
+ All within ±2% gate. PASSED. The `+84 B` increase in minified bundles is expected noise from the delegator shim layer (same one-extra-hop pattern as v2.4 DOM cluster). The d.ts is byte-identical — extracted function bodies and the local `TsTimeResult` interface in `tsutils-datetime.ts` are internal, not emitted in the rolled d.ts.
87
+
88
+ ### BC
89
+
90
+ Net-additive (new module + delegators). Public method signatures byte-identical OR narrowed-with-strict-refinement per documentation. `TsTimeResult` continues to be declared in `tsutils.ts` (no-export, emitted transitively in d.ts per v2.4.1 hotfix); the new `tsutils-datetime.ts` module declares a structurally identical local `TsTimeResult` for `_isTime`'s internal use — non-exported, not emitted in rolled d.ts. No consumer-visible type change. `vi.spyOn(TsUtils, 'X')` continues to work for all 8 extracted methods via class-prototype delegators (INV-SPY PASS). SEMVER MINOR. BC verdict: NONE.
91
+
92
+ ---
93
+
5
94
  ## v2.4.1 — 2026-05-13
6
95
 
7
96
  ### Fixed
@@ -1,4 +1,4 @@
1
- /* tsgrid-ui 1.0.x (nightly) (5/13/2026, 8:20:06 PM) (c) 2014 vitmalina@gmail.com, (c) 2026 DaverSoGT — MIT */
1
+ /* tsgrid-ui 1.0.x (nightly) (5/13/2026, 10:54:27 PM) (c) 2014 vitmalina@gmail.com, (c) 2026 DaverSoGT — MIT */
2
2
  /**
3
3
  * TODO:
4
4
  * - remove default styling, only keep tsg-* styles
@@ -9,7 +9,7 @@
9
9
  */
10
10
  @font-face {
11
11
  font-family: "tsgrid-font";
12
- src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff");
12
+ src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff");
13
13
  font-weight: normal;
14
14
  font-style: normal;
15
15
  }
@@ -393,6 +393,41 @@ interface TsLockOptions {
393
393
  onClick?: () => void;
394
394
  }
395
395
 
396
+ /**
397
+ * TsUtils date-time sub-module — Phase 5b of v2.5 SDD.
398
+ * DAG position: leaf module (no tsbase/tsutils imports).
399
+ *
400
+ * Imports:
401
+ * ./tsutils-type-guards.js — isInt as _isInt (needed by isDate, isTime, formatDate, formatTime)
402
+ * ./tsutils.js — type-only import type { TsUISettings } (TS erases at emit)
403
+ * Precedent: tsutils-type-guards.ts:9, tsutils-message.ts:26
404
+ *
405
+ * INV-4: MUST NOT import from tsbase.ts or tsutils.ts at runtime.
406
+ * INV-8: No arguments.length usage.
407
+ * INV-9: No this.X in exported function bodies.
408
+ *
409
+ * 4-space indent convention.
410
+ *
411
+ * OQ-2 (TsTimeResult): local non-exported interface `TsTimeResult` defined inline
412
+ * here (structurally identical to tsutils.ts copy). Avoids back-import of a
413
+ * non-exported type; the class delegator in tsutils.ts casts via `as boolean | TsTimeResult`.
414
+ *
415
+ * R-DT-3 (settings reference): `settings` is passed as a reference to `this.settings`
416
+ * from delegators — never cloned. TsLocale mutations to fullmonths/shortmonths/dateFormat
417
+ * etc. flow through without restart.
418
+ *
419
+ * R-DT-2 / R-DT-8 (intra-cluster calls): _isDateTime calls _isDate + _isTime directly
420
+ * as module-level function refs. _formatDateTime calls _formatDate + _formatTime directly.
421
+ * _formatTime calls _isTime directly. Zero this.X inside any extracted body.
422
+ */
423
+
424
+ /** Return value from _isTime() / TsUtils.isTime() when retTime === true — single canonical declaration (v2.6 dedup) */
425
+ interface TsTimeResult {
426
+ hours: number;
427
+ minutes: number;
428
+ seconds: number;
429
+ }
430
+
396
431
  /**
397
432
  * Part of TsUi 2.0 library
398
433
  * - Dependencies: mQuery, TsUtils, TsBase, TsLocale
@@ -461,12 +496,6 @@ interface TsFormatterExtra {
461
496
  }
462
497
  /** Signature of a grid-cell formatter function */
463
498
  type TsFormatter = (record: TsFormatterExtra, extra?: TsFormatterExtra) => string;
464
- /** Return value from TsUtils.isTime() when retTime === true */
465
- interface TsTimeResult {
466
- hours: number;
467
- minutes: number;
468
- seconds: number;
469
- }
470
499
 
471
500
  /** A normalized menu item */
472
501
  interface TsMenuItem {