@dxtmisha/functional-basic 1.7.1 → 1.8.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 +10 -0
- package/README.md +2 -0
- package/ai-description.md +4 -16
- package/ai-doc.md +33 -112
- package/ai-types.md +660 -767
- package/dist/functions/domContentLoaded.d.ts +13 -0
- package/dist/functions/getRandomItem.d.ts +10 -0
- package/dist/functions/sortList.d.ts +11 -0
- package/dist/functions/toNumberPositive.d.ts +9 -0
- package/dist/library.d.ts +5 -0
- package/dist/library.js +584 -531
- package/dist/types/sortTypes.d.ts +20 -0
- package/package.json +17 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.8.0] - 2026-07-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **domContentLoaded**: Introduced `domContentLoaded` DOM helper function to safely execute callbacks once the DOM is loaded, with comprehensive unit tests (`domContentLoaded.test.ts`).
|
|
9
|
+
- **toNumberPositive**: Introduced `toNumberPositive` utility function to convert inputs to positive finite numbers (`> 0`) or return a default fallback (`0`), with unit tests (`toNumberPositive.test.ts`).
|
|
10
|
+
- **sortList**: Introduced `sortList` standalone utility function for multi-column array sorting by property paths, directions (`'asc'`, `'desc'`), or custom comparison functions using locale-aware `Intl.Collator`.
|
|
11
|
+
- **sortTypes**: Added `SortDir`, `SortColumnItem`, and `SortFunction` type definitions, extending `SortColumnItem` to allow optional `dir` and `column` properties for 3-state sorting cycles (`asc` -> `desc` -> `undefined`).
|
|
12
|
+
- **getRandomItem**: Introduced a new `getRandomItem` utility function to safely retrieve a random element from arrays, objects, or primitive values, returning `undefined` when empty or missing, with full bilingual JSDocs.
|
|
13
|
+
- **Tests**: Added unit test suites for `sortList` (`sortList.test.ts`), `getRandomItem` (`getRandomItem.test.ts`), `domContentLoaded` (`domContentLoaded.test.ts`), and `toNumberPositive` (`toNumberPositive.test.ts`).
|
|
14
|
+
|
|
5
15
|
## [1.7.1] - 2026-07-14
|
|
6
16
|
|
|
7
17
|
### Changed
|
package/README.md
CHANGED
package/ai-description.md
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
The library provides an isomorphic utility framework for managing high-level application concerns in JavaScript/TypeScript environments (SSR and DOM). It includes robust abstractions for API communication (REST/Fetch), structured state management, DOM-safe event handling, internationalization, and reactive data storage.
|
|
1
|
+
An isomorphic TypeScript utility framework designed to provide core runtime services for web applications across browser and Server-Side Rendering (SSR) environments. Its primary functions include wrapping the Fetch API with request/response caching and hydration, state management isolated by request context, comprehensive internationalization (i18n, unit conversions, phone masking, date/number formatting), SEO meta tag synchronization, reactive URL hash and query parameter tracking, managed DOM event lifecycle handling, in-memory list search with string highlighting, and centralized error handling.
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
* **API & Networking**: `Api` (singleton interface), `ApiInstance` (core requester), `ApiCache` (request memoization), `ApiError` (centralized error handling), `ApiHydration` (SSR data serialization), and `ApiHeaders`.
|
|
6
|
-
* **State & Storage**: `DataStorage` (persistent storage with prefixes/expiration), `ServerStorage` (SSR-safe context isolation), `CookieStorage` (isomorphic cookie management), `Query`/`Hash` (URL-state management), and `Global` (app-wide data).
|
|
7
|
-
* **UI & Events**: `EventItem` (DOM-safe, optimized event management with `ResizeObserver` and `scroll-sync`), `LoadingInstance` (global loading state), and `ScrollbarWidth` (layout utility).
|
|
8
|
-
* **Localization & Formatting**: `Geo` (locale/timezone management), `GeoIntl` (Intl API wrapper), `GeoUnit` (metric/imperial conversion), `Translate` (i18n), and `Formatters` (currency, number, date, and pluralization utility).
|
|
9
|
-
* **Utilities & Data**: `Formatters`, `SearchList` (search matching/caching), `ResumableTimer`, and a suite of functional utilities for object cloning, string/date manipulation, and DOM operations.
|
|
3
|
+
Api and ApiInstance manage Fetch-based HTTP communication featuring retries, custom headers, preparation hooks, and mock response emulation. ApiCache handles client and server data caching. ServerStorage and DataStorage provide isomorphic request-isolated state retention and browser storage abstraction with SSR hydration support. CookieStorage and Cookie manage client/server cookies consistently. Geo, GeoIntl, GeoPhone, GeoUnit, and Datetime form a localization engine handling Intl formatting, country phone masking, unit conversions, and date calculations. Translate and TranslateInstance manage synchronous and asynchronous translation batching. Query, QueryInstance, Hash, and HashInstance offer reactive, watchable interfaces for URL query strings and hash parameters. EventItem wraps DOM event listeners with ResizeObserver and requestAnimationFrame scroll optimizations. SearchList provides in-memory text search, regex generation, and matching string highlights. Meta, MetaOg, and MetaTwitter manage standard HTML, Open Graph, and Twitter Card metadata tags in the DOM or as HTML strings. ErrorCenter provides centralized error tracking and routing.
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
Review `ai-types.md` whenever the following requirements arise:
|
|
13
|
-
1. **API Integration**: You need to implement new request methods, custom error handling for specific HTTP status codes, or configure global API middleware (`wrapper`/`preparation`).
|
|
14
|
-
2. **I18n/Formatting**: You are dealing with complex localization rules, unit conversions (Geo/Units), or pluralization requirements.
|
|
15
|
-
3. **SSR Consistency**: You are implementing features that must function identically on both the server and client (Hydration, `ServerStorage`, or `Datetime` formatting).
|
|
16
|
-
4. **Complex State**: You need to map or query data structures in memory using `SearchList` or utilize `BroadcastChannel` for cross-context messaging.
|
|
5
|
+
Studying ai-types.md is mandatory when implementing or typing API configurations (ApiFetch, ApiConfig, ApiHydrationItem), setting up application error handling (ErrorCenterCauseItem, ErrorCenterHandlerList), configuring complex list transformations (FormattersOptionsList, FormattersType), managing geographic and phone mask parameters (GeoItem, GeoDate, GeoPhoneValue), defining search parameters (SearchOptions, SearchColumns), configuring SEO meta types (MetaOpenGraphTag, MetaTwitterCard), or working with core generic utility types (ArrayToItem, NormalOrPromise, ObjectOrArray, NumberOrString).
|
|
17
6
|
|
|
18
|
-
|
|
19
|
-
The library acts as a foundational service layer between the application logic and the runtime environment. It is designed to be framework-agnostic but is particularly optimized for SSR-heavy environments (like Vue/React) where hydration of state (via `ServerStorage`) and safe access to global browser objects (via `isDomRuntime`) are critical. It wraps native `fetch`, `localStorage`, `sessionStorage`, `BroadcastChannel`, and `Intl` APIs into structured, type-safe, and singleton-accessible services.
|
|
7
|
+
The library operates as a foundational layer directly above native browser and Node.js runtime APIs (Fetch API, Intl API, DOM Window/Element interfaces, ResizeObserver, BroadcastChannel, Web Storage). It integrates with frontend SSR frameworks (such as Vue, React, Nuxt, or Next.js) by decoupling server-side request state via ServerStorage and generating safe client hydration scripts (getElementSafeScript) to prevent client-server hydration mismatches.
|
package/ai-doc.md
CHANGED
|
@@ -2,140 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
Framework-agnostic utility library. **Vue developers MUST search `@dxtmisha/functional` first**; use this ONLY if no reactive/Vue-specific analog exists.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 1. Coding Standards & Conventions
|
|
6
|
+
- **Class Structure**: Properties/Variables (`public`->`protected`->`private`) -> Constructor -> Public Methods (Getters -> Setters -> Core actions) -> Protected Methods -> Private Methods.
|
|
7
|
+
- **Style/Types**: `PascalCase` classes, `camelCase` methods/props, `UPPER_SNAKE_CASE` constants. No `any` (use `unknown`/generics). Explicit return types for ALL methods. Export all interfaces. Type files: `*Types.ts`. Use `@effect/schema` for schemas.
|
|
8
|
+
- **SSR Safety**: Isomorphic code. Do NOT store request state in globals. Use `isDomRuntime()` before `window`/`document`. Use `ServerStorage.get('key', () => new Class())` for request-isolated singletons.
|
|
6
9
|
|
|
7
|
-
##
|
|
10
|
+
## 2. API Reference & Examples
|
|
8
11
|
|
|
9
|
-
###
|
|
10
|
-
1. **Properties/Variables**: Top of class, ordered by visibility (`public` -> `protected` -> `private`). Initialize inline if possible.
|
|
11
|
-
2. **Constructor**: Follows properties. Parameter properties (e.g. `protected url: string`) allowed.
|
|
12
|
-
3. **Public Methods**:
|
|
13
|
-
1. Getters, checkers, status methods (`is*`, `get*`).
|
|
14
|
-
2. Setters & configuration (`set*`).
|
|
15
|
-
3. Core executors & actions (`request()`, `fetch()`, `show()`).
|
|
16
|
-
4. **Protected Methods**: Internal helpers for subclasses.
|
|
17
|
-
5. **Private Methods**: Internal helper logic at the very bottom.
|
|
18
|
-
|
|
19
|
-
### 2. Style & Type Conventions
|
|
20
|
-
- **Naming**: Classes = `PascalCase`, Methods/Properties = `camelCase`, Constants = `UPPER_SNAKE_CASE`.
|
|
21
|
-
- **TypeScript**: No `any` (use `unknown`/generics). Explicit return types on ALL methods (including `void`). Export all types/interfaces. Suffix type files with `Types` (e.g., `*Types.ts`). Use `@effect/schema` for API schemas if present.
|
|
22
|
-
- **SSR Safety**: Isomorphic code. Do not store request-specific state in static/global variables. Use `isDomRuntime()` before accessing `window`/`document`/`location`. Use `ServerStorage.get(key, () => new Instance())` for request-isolated singletons.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## API Reference & Examples
|
|
27
|
-
|
|
28
|
-
### 1. HTTP Client (`Api`, `ApiInstance`, `ApiCache`)
|
|
12
|
+
### HTTP Client & Caching
|
|
29
13
|
```typescript
|
|
30
14
|
import { Api, ApiCache } from '@dxtmisha/functional-basic';
|
|
31
|
-
|
|
32
|
-
// Config
|
|
33
|
-
Api.setOrigin('https://api.example.com');
|
|
34
|
-
Api.setUrl('/api/v1');
|
|
35
|
-
Api.setRequestDefault({ client: 'web' });
|
|
15
|
+
Api.setOrigin('https://api.example.com'); Api.setUrl('/api/v1'); Api.setRequestDefault({ client: 'web' });
|
|
36
16
|
Api.setHeaders(() => ({ Authorization: `Bearer ${localStorage.getItem('token') || ''}` }));
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Api.setEnd(async (res, fetchOpts) => res.status === 401 ? { reset: true } : {});
|
|
41
|
-
|
|
42
|
-
// Requests
|
|
43
|
-
const users = await Api.request<User[]>('users'); // default GET
|
|
44
|
-
const profile = await Api.get<User>({ path: 'profile' });
|
|
17
|
+
Api.setPreparation(async (opts) => { if (opts.auth) opts.headers['X-Auth'] = '1'; });
|
|
18
|
+
Api.setEnd(async (res) => res.status === 401 ? { reset: true } : {});
|
|
19
|
+
const users = await Api.request<User[]>('users'); // GET
|
|
45
20
|
const updated = await Api.post<User>({ path: 'profile', request: { name: 'New' } });
|
|
46
|
-
|
|
47
|
-
// Cache
|
|
48
|
-
await ApiCache.set('key', { data: 1 }, 60000); // ms age
|
|
49
|
-
const cached = await ApiCache.get<{ data: number }>('key');
|
|
21
|
+
await ApiCache.set('k', { a: 1 }, 60000); const cache = await ApiCache.get<{a: number}>('k');
|
|
50
22
|
```
|
|
51
23
|
|
|
52
|
-
###
|
|
24
|
+
### Storage & State
|
|
53
25
|
```typescript
|
|
54
26
|
import { DataStorage, CookieStorage, Cookie, ServerStorage } from '@dxtmisha/functional-basic';
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const user = userStorage.get({ id: 'guest' }); // fallback default
|
|
61
|
-
userStorage.remove();
|
|
62
|
-
|
|
63
|
-
// Cookies
|
|
64
|
-
CookieStorage.set('theme', 'dark', { age: 31536000, secure: true, sameSite: 'lax' });
|
|
65
|
-
const theme = CookieStorage.get<string>('theme', 'light');
|
|
66
|
-
CookieStorage.remove('theme');
|
|
67
|
-
|
|
68
|
-
const tokenCookie = new Cookie<string>('auth_token');
|
|
69
|
-
tokenCookie.set('xyz123', { secure: true });
|
|
70
|
-
const token = tokenCookie.get();
|
|
71
|
-
|
|
72
|
-
// SSR Request-Isolated Storage
|
|
73
|
-
const myService = ServerStorage.get('myService', () => new MyService());
|
|
27
|
+
DataStorage.setPrefix('app_');
|
|
28
|
+
const ls = new DataStorage<{ id: string }>('user', false); ls.set({ id: '1' }); ls.get({ id: '0' }); ls.remove();
|
|
29
|
+
CookieStorage.set('t', 'dark', { age: 31536000, secure: true }); CookieStorage.get<string>('t', 'light');
|
|
30
|
+
const c = new Cookie<string>('auth'); c.set('xyz', { secure: true }); c.get();
|
|
31
|
+
const srv = ServerStorage.get('svc', () => new Svc()); // SSR isolated
|
|
74
32
|
```
|
|
75
33
|
|
|
76
|
-
###
|
|
34
|
+
### Geolocation, Formatting & Localization
|
|
77
35
|
```typescript
|
|
78
36
|
import { Geo, GeoIntl, GeoFlag, GeoPhone } from '@dxtmisha/functional-basic';
|
|
79
|
-
|
|
80
|
-
// Geo state
|
|
81
|
-
const country = Geo.getCountry(); // e.g., 'VN'
|
|
82
|
-
const lang = Geo.getLanguage(); // e.g., 'vi'
|
|
83
|
-
Geo.set('en-US');
|
|
84
|
-
|
|
85
|
-
// Formatters (Intl)
|
|
37
|
+
const country = Geo.getCountry(); const lang = Geo.getLanguage(); Geo.set('en-US');
|
|
86
38
|
const intl = new GeoIntl('en-US');
|
|
87
|
-
intl.number(
|
|
88
|
-
intl.
|
|
89
|
-
intl.sizeFile(1024 * 1024 * 5); // '5.00 MB'
|
|
90
|
-
intl.date(new Date(), 'date'); // 'Jun 18, 2026'
|
|
91
|
-
intl.date(new Date(), 'time'); // '10:48 PM'
|
|
92
|
-
intl.relative(new Date(Date.now() - 3600000)); // '1 hour ago'
|
|
93
|
-
intl.plural(3, 'apple|apples'); // '3 apples' ('one|other' or 'one|few|many|other')
|
|
94
|
-
|
|
95
|
-
// Flags & Phones
|
|
39
|
+
intl.number(1234.5); intl.currency(99, 'USD'); intl.sizeFile(1024*1024); intl.date(new Date(), 'date');
|
|
40
|
+
intl.relative(new Date(Date.now() - 3600000)); intl.plural(3, 'apple|apples');
|
|
96
41
|
const flag = new GeoFlag().getFlag('VN');
|
|
97
|
-
const
|
|
98
|
-
const mask = GeoPhone.toMask('84900000000');
|
|
42
|
+
const phone = GeoPhone.getByPhone('+84900000000'); const mask = GeoPhone.toMask('84900000000');
|
|
99
43
|
```
|
|
100
44
|
|
|
101
|
-
###
|
|
45
|
+
### DOM, Events & Helpers
|
|
102
46
|
```typescript
|
|
103
|
-
import { EventItem, goScrollSmooth, writeClipboardData, getClipboardData } from '@dxtmisha/functional-basic';
|
|
47
|
+
import { EventItem, goScrollSmooth, writeClipboardData, getClipboardData, SearchList, Formatters, FormattersType, isFilled, isDomRuntime, copyObject, anyToString, sleep } from '@dxtmisha/functional-basic';
|
|
104
48
|
|
|
105
|
-
//
|
|
106
|
-
const
|
|
107
|
-
clickListener.start();
|
|
108
|
-
clickListener.stop(); // Call on destroy/cleanup!
|
|
49
|
+
// Safe Events (leak-proof)
|
|
50
|
+
const listener = new EventItem(window, 'click', console.log, { passive: true }); listener.start(); listener.stop();
|
|
109
51
|
|
|
110
52
|
// DOM / Clipboard
|
|
111
|
-
goScrollSmooth(document.getElementById('
|
|
112
|
-
await writeClipboardData('text');
|
|
113
|
-
const text = await getClipboardData();
|
|
114
|
-
```
|
|
53
|
+
goScrollSmooth(document.getElementById('t')); await writeClipboardData('txt'); await getClipboardData();
|
|
115
54
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Search List with highlights
|
|
121
|
-
const searcher = new SearchList([{ name: 'John Doe' }], ['name'], 'john');
|
|
122
|
-
const results = searcher.to(); // returns matching items with highlighted markup in matching keys
|
|
123
|
-
|
|
124
|
-
// Object Formatter
|
|
125
|
-
const formatter = new Formatters({
|
|
126
|
-
price: { type: FormattersType.currency, options: 'USD' },
|
|
127
|
-
date: { type: FormattersType.date, options: { month: 'long', year: 'numeric' } }
|
|
128
|
-
}, { price: 12000, date: '2026-06-18' });
|
|
129
|
-
const formatted = formatter.to(); // { price: '$12,000.00', date: 'June 2026' }
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### 6. General Helpers
|
|
133
|
-
```typescript
|
|
134
|
-
import { isFilled, isDomRuntime, copyObject, anyToString, sleep } from '@dxtmisha/functional-basic';
|
|
55
|
+
// Search & Formatters
|
|
56
|
+
const res = new SearchList([{ n: 'John' }], ['n'], 'jo').to(); // Highlights matches
|
|
57
|
+
const fmt = new Formatters({ p: { type: FormattersType.currency, options: 'USD' } }, { p: 12 }).to();
|
|
135
58
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const cloned = copyObject({ a: 1 });
|
|
139
|
-
const str = anyToString(123);
|
|
140
|
-
await sleep(500);
|
|
59
|
+
// General
|
|
60
|
+
isFilled([]); // false (strings, arrays, objects, numbers, booleans)
|
|
61
|
+
isDomRuntime(); const cloned = copyObject({ a: 1 }); const str = anyToString(123); await sleep(500);
|
|
141
62
|
```
|