@reforgium/statum 3.1.0 → 3.1.1

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
@@ -1,3 +1,18 @@
1
+ ## [3.1.1]: 2026-04-04
2
+
3
+ ### Refactor:
4
+ - Moved `Serializer` core into `@reforgium/internal`; `statum` now keeps serializer presets and public wrappers while reusing the shared codec engine underneath.
5
+ - Moved low-level storage implementations and `storageStrategy(...)` into `@reforgium/internal`; `statum` cache exports now act as compatibility wrappers over the shared foundation layer.
6
+
7
+ ### Fix:
8
+ - Package build and workspace resolution were aligned with hidden `@reforgium/internal` usage.
9
+ - `PagedQueryStore` transport generic ordering was corrected for package build stability.
10
+
11
+ ### Docs:
12
+ - `README` now reflects `storageStrategy(...)` naming and clarifies that serializer/storage core are shared through `@reforgium/internal`.
13
+
14
+ ---
15
+
1
16
  ## [3.1.0]: 2026-04-01
2
17
 
3
18
  ### Feat:
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **Signals-first query and data stores for Angular (18+).**
7
7
 
8
- `@reforgium/statum` provides **API-oriented stores**, **cache strategies**, and a
8
+ `@reforgium/statum` provides **API-oriented stores**, **cache strategies**, and a
9
9
  **serialization layer** for Angular applications that talk to HTTP backends.
10
10
 
11
11
  Designed for **request orchestration, pagination, dictionaries, and entity state**.
@@ -94,6 +94,9 @@ The safest long-term entry points are:
94
94
  - **Stores** - reusable state containers over HttpClient
95
95
  - **Serializer** - configurable data transformation utility
96
96
 
97
+ Low-level serializer and storage primitives are shared through hidden `@reforgium/internal`.
98
+ `statum` remains the user-facing package; you do not need to install or reason about `@reforgium/internal` directly in normal usage.
99
+
97
100
  ## Behavioral Guarantees
98
101
 
99
102
  The core stores are designed around explicit, testable runtime guarantees.
@@ -151,9 +154,9 @@ Those numbers are machine-specific and should be treated as a local envelope, no
151
154
 
152
155
  ## Cache
153
156
 
154
- ### cacheStrategy
157
+ ### storageStrategy
155
158
 
156
- `cacheStrategy<T>(kind)` is a factory that returns a cache storage implementing `StorageInterface<T>`
159
+ `storageStrategy<T>(kind)` is a factory that returns a cache storage implementing `StorageInterface<T>`
157
160
 
158
161
  Available strategies:
159
162
 
@@ -180,11 +183,11 @@ interface StorageInterface<T> {
180
183
  Example:
181
184
 
182
185
  ```ts
183
- import { cacheStrategy } from '@reforgium/statum';
186
+ import { storageStrategy } from '@reforgium/statum';
184
187
 
185
188
  type User = { id: number; name: string };
186
189
 
187
- const storage = cacheStrategy<User>('persist');
190
+ const storage = storageStrategy<User>('persist');
188
191
 
189
192
  storage.set('user:1', { id: 1, name: 'John' });
190
193
 
@@ -651,6 +654,8 @@ Use this when list fetching and local item mutations need different lifecycles.
651
654
 
652
655
  Utility for serialization/deserialization between layers (UI -> API, objects -> query string).
653
656
 
657
+ `statum` keeps the user-facing serializer API and presets, while the low-level codec engine is shared from hidden `@reforgium/internal`.
658
+
654
659
  Core API:
655
660
 
656
661
  | Method | Description |
@@ -1,389 +1,10 @@
1
- import { formatDate, isNullable, isDatePeriod, parseToDate, buildQueryParams, isNumber, isObject, parseToDatePeriod, parseQueryParamsByMode, fillUrlWithParams, mergeQueryParams, deepEqual, normalizeSortInput, sortInputToTokens, debounceSignal } from '@reforgium/internal';
1
+ import { Serializer, fillUrlWithParams, mergeQueryParams, LruCache, deepEqual, isNullable, normalizeSortInput, sortInputToTokens, debounceSignal, storageStrategy } from '@reforgium/internal';
2
+ export { LocalStorage, LruCache, MemoryStorage, Serializer, SerializerFieldError, SessionStorage, storageStrategy } from '@reforgium/internal';
2
3
  import { HttpClient, HttpParams } from '@angular/common/http';
3
4
  import { InjectionToken, makeEnvironmentProviders, inject, signal, computed, EnvironmentInjector, DestroyRef, runInInjectionContext, effect, untracked } from '@angular/core';
4
5
  import { Subject, filter, timer, merge, map } from 'rxjs';
5
6
  import { debounce, tap, throttle, finalize } from 'rxjs/operators';
6
7
 
7
- const serializeString = (config, value) => config.mapString.format?.(value) ?? (config.mapString.trim ? value.trim() : value);
8
- const serializeNumber = (config, value) => config.mapNumber.format?.(value) ?? Number(value);
9
- const serializeBoolean = (config, value) => config.mapBoolean.format?.(value) ?? (value ? (config.mapBoolean.true ?? true) : (config.mapBoolean.false ?? false));
10
- const serializeDate = (config, value) => config.mapDate.format?.(value) ?? formatDate(value, config.mapDate.dateFormat);
11
-
12
- class SerializerFieldError extends Error {
13
- field;
14
- stage;
15
- originalError;
16
- constructor(field, stage, originalError) {
17
- const originalMessage = originalError instanceof Error ? originalError.message : String(originalError);
18
- super(`Serializer ${stage} error for field "${field}": ${originalMessage}`);
19
- this.field = field;
20
- this.stage = stage;
21
- this.originalError = originalError;
22
- this.name = 'SerializerFieldError';
23
- }
24
- }
25
- /**
26
- * Universal serializer/deserializer for values used in forms, filters, and DTOs.
27
- *
28
- * Supports types: `string | number | boolean | Date | [Date, Date] (period) | array | object | nullable`.
29
- * Capabilities:
30
- * - normalize values according to config (trim strings, parse numbers from strings, boolean strings, etc.),
31
- * - transform date periods into paired keys (`from/to`) or a single joined string,
32
- * - build/parse a query string (or JSON) to and from an object.
33
- *
34
- * Example:
35
- * ```ts
36
- * type Filters = { q?: string; active?: boolean; created?: [Date, Date] | null };
37
- * const s = new Serializer<Filters>({
38
- * mapPeriod: { transformMode: { mode: 'split', dateFromKeyPostfix: 'From', dateToKeyPostfix: 'To' } }
39
- * });
40
- *
41
- * // -> { q: 'john', createdFrom: '2025-01-01', createdTo: '2025-01-31' }
42
- * const plain = s.serialize({
43
- * q: ' john ',
44
- * active: undefined,
45
- * created: [new Date('2025-01-01'), new Date('2025-01-31')]
46
- * });
47
- *
48
- * // -> 'q=john&createdFrom=2025-01-01&createdTo=2025-01-31'
49
- * const qs = s.toQuery({ q: 'john', created: [new Date('2025-01-01'), new Date('2025-01-31')] });
50
- *
51
- * // <- { q: 'john', created: [Date, Date] }
52
- * const parsed = s.deserialize('q=john&createdFrom=2025-01-01&createdTo=2025-01-31');
53
- * ```
54
- */
55
- class Serializer {
56
- config;
57
- /**
58
- * Creates a serializer with a partially overridden configuration.
59
- * Provide only the options you want to change (the rest are taken from defaults).
60
- *
61
- * @param config partial transformation configuration
62
- */
63
- constructor(config = {}) {
64
- this.config = this.mergeConfig({
65
- mapString: { trim: true },
66
- mapNumber: { fromString: false },
67
- mapBoolean: {},
68
- mapArray: { concatType: 'comma' },
69
- mapObject: { deep: true },
70
- mapDate: { dateFormat: 'yyyy-MM-dd' },
71
- mapPeriod: {
72
- dateFormat: 'yyyy-MM-dd',
73
- transformMode: { mode: 'split', dateFromKeyPostfix: 'From', dateToKeyPostfix: 'To' },
74
- },
75
- mapNullable: { remove: true, includeEmptyString: false },
76
- }, config);
77
- }
78
- /**
79
- * Converts a domain object into a flat serialized representation
80
- * (ready to send to an API or build a query string).
81
- *
82
- * Rules are taken from `config`:
83
- * — strings can be trimmed (if enabled),
84
- * — numbers can be converted from strings/numbers,
85
- * — boolean supports custom true/false representations,
86
- * — dates are formatted by `dateFormat`,
87
- * — date periods can be split/joined,
88
- * — `nullable` can be removed from the result (`remove`) or formatted.
89
- *
90
- * @param obj source object
91
- * @returns a flat dictionary with string/primitive values
92
- */
93
- serialize(obj, _seen = new WeakSet()) {
94
- const result = {};
95
- if (obj != null && typeof obj === 'object') {
96
- _seen.add(obj);
97
- }
98
- for (const [key, value] of Object.entries(obj ?? {})) {
99
- const fields = this.config.mapFields?.[key];
100
- if (fields && 'format' in fields) {
101
- try {
102
- result[key] = fields.format(value, obj);
103
- }
104
- catch (error) {
105
- throw new SerializerFieldError(key, 'format', error);
106
- }
107
- continue;
108
- }
109
- if (fields?.type === 'nullable' || isNullable(value, this.config.mapNullable?.includeEmptyString)) {
110
- if (this.config.mapNullable.remove) {
111
- continue;
112
- }
113
- }
114
- if (fields?.type === 'period' || isDatePeriod(value)) {
115
- const transform = this.config.mapPeriod.transformMode;
116
- const [from, to] = value;
117
- if (transform?.mode === 'split') {
118
- result[`${key}${transform.dateFromKeyPostfix}`] = formatDate(from, this.config.mapPeriod.dateFormat);
119
- result[`${key}${transform.dateToKeyPostfix}`] = formatDate(to, this.config.mapPeriod.dateFormat);
120
- continue;
121
- }
122
- }
123
- result[key] = this.serializeElement(value, key, _seen);
124
- }
125
- if (obj != null && typeof obj === 'object') {
126
- _seen.delete(obj);
127
- }
128
- return result;
129
- }
130
- /**
131
- * Parse serialized data into a domain object.
132
- *
133
- * Source can be:
134
- * — a query string (`key=value&arr=1,2`) or `JSON.stringify(obj)`,
135
- * — an already prepared flat object.
136
- *
137
- * Transformations are reverse of `serialize`: strings → number/boolean/Date/period,
138
- * arrays are collected according to strategy (`comma`/`pipe`/`multi`), objects — deeply or as JSON.
139
- *
140
- * @param val query string or object
141
- * @returns a domain object of the specified type
142
- */
143
- deserialize = (val) => {
144
- const data = typeof val === 'string' ? this.parseInputString(val) : val;
145
- const result = {};
146
- for (const [key, value] of Object.entries(data ?? {})) {
147
- const field = this.config.mapFields?.[key];
148
- if (field && 'parse' in field) {
149
- try {
150
- result[key] = field.parse(value, data);
151
- }
152
- catch (error) {
153
- throw new SerializerFieldError(key, 'parse', error);
154
- }
155
- continue;
156
- }
157
- if (field?.type === 'nullable' ||
158
- (field?.type !== 'array' && isNullable(value, this.config.mapNullable?.includeEmptyString))) {
159
- if (this.config.mapNullable.remove) {
160
- continue;
161
- }
162
- }
163
- const periodTransform = this.config.mapPeriod.transformMode;
164
- if (periodTransform.mode === 'split') {
165
- const isFrom = (key || '').endsWith(periodTransform.dateFromKeyPostfix);
166
- const isTo = (key || '').endsWith(periodTransform.dateToKeyPostfix);
167
- const keyJoint = (key || '')
168
- .replace(periodTransform.dateFromKeyPostfix, '')
169
- .replace(periodTransform.dateToKeyPostfix, '');
170
- const field = this.config.mapFields?.[keyJoint];
171
- const fieldType = field && 'type' in field ? field.type : undefined;
172
- if (fieldType === 'period' && (isFrom || isTo)) {
173
- result[keyJoint] ??= [null, null];
174
- if (isFrom) {
175
- result[keyJoint][0] = parseToDate(value, this.config.mapPeriod.dateFormat);
176
- }
177
- else if (isTo) {
178
- result[keyJoint][1] = parseToDate(value, this.config.mapPeriod.dateFormat);
179
- }
180
- continue;
181
- }
182
- }
183
- result[key] = this.deserializeElement(value, key);
184
- }
185
- return result;
186
- };
187
- /** Parse only query-string input. */
188
- deserializeQuery = (query) => {
189
- return this.deserialize(this.parseQuery(query));
190
- };
191
- /** Parse only JSON object input. */
192
- deserializeJson = (json) => {
193
- return this.deserialize(this.parseJsonObject(json));
194
- };
195
- /**
196
- * Build a query string from a domain object using `serialize` rules
197
- * and the array joining strategy (`concatType`).
198
- *
199
- * @param val domain object
200
- * @returns query string (suitable for URL or history API)
201
- */
202
- toQuery = (val) => {
203
- return buildQueryParams(this.serialize(val), this.config.mapArray.concatType, this.resolveArrayFieldModes());
204
- };
205
- /**
206
- * Returns a new serializer instance with a merged configuration.
207
- * Useful for ad-hoc overrides for a specific call.
208
- *
209
- * @param config partial config changes
210
- * @returns new `Serializer` with the provided `config` applied
211
- */
212
- withConfig(config) {
213
- return new Serializer(this.mergeConfig(this.config, config));
214
- }
215
- serializeElement(value, key, _seen = new WeakSet()) {
216
- const fields = this.config.mapFields?.[key || ''];
217
- if (fields && 'format' in fields) {
218
- return;
219
- }
220
- if (fields?.type === 'nullable' || isNullable(value, this.config.mapNullable?.includeEmptyString)) {
221
- const nullableVal = value ?? null;
222
- return this.config.mapNullable.format?.(nullableVal) || this.config.mapNullable.replaceWith || nullableVal;
223
- }
224
- if (fields?.type === 'string') {
225
- return serializeString(this.config, value);
226
- }
227
- if (fields?.type === 'number' || isNumber(value, this.config.mapNumber.fromString)) {
228
- return serializeNumber(this.config, value);
229
- }
230
- if (typeof value === 'string') {
231
- return serializeString(this.config, value);
232
- }
233
- if (fields?.type === 'boolean' || typeof value === 'boolean') {
234
- return serializeBoolean(this.config, value);
235
- }
236
- if (fields?.type === 'date' || value instanceof Date) {
237
- return serializeDate(this.config, value);
238
- }
239
- if (fields?.type === 'period' || isDatePeriod(value)) {
240
- const mapPeriod = this.config.mapPeriod;
241
- if (mapPeriod.format) {
242
- return mapPeriod.format(value);
243
- }
244
- else {
245
- const [from, to] = value;
246
- const transform = mapPeriod.transformMode;
247
- if (transform.mode === 'join') {
248
- const period = [
249
- formatDate(from, this.config.mapPeriod.dateFormat),
250
- formatDate(to, this.config.mapPeriod.dateFormat),
251
- ];
252
- return period.join(transform.concat);
253
- }
254
- }
255
- }
256
- if (fields?.type === 'array' || Array.isArray(value)) {
257
- return value.map((it) => this.serializeElement(it, undefined, _seen));
258
- }
259
- if (fields?.type === 'object' || isObject(value)) {
260
- if (this.config.mapObject.deep && !this.config.mapObject.format && value != null && _seen.has(value)) {
261
- return undefined;
262
- }
263
- return (this.config.mapObject.format?.(value) ??
264
- (this.config.mapObject.deep ? this.serialize(value, _seen) : JSON.stringify(value)));
265
- }
266
- }
267
- deserializeElement(value, key) {
268
- const field = this.config.mapFields?.[key || ''];
269
- if (field && 'format' in field) {
270
- return;
271
- }
272
- if (field?.type === 'array' || Array.isArray(value)) {
273
- const array = Array.isArray(value) ? value : [value];
274
- if (this.config.mapArray.removeNullable) {
275
- if (!isNullable(value, this.config.mapNullable?.includeEmptyString)) {
276
- return array.map((it) => this.deserializeElement(it));
277
- }
278
- else {
279
- return;
280
- }
281
- }
282
- else {
283
- return !value ? [] : array.map((it) => this.deserializeElement(it));
284
- }
285
- }
286
- if (field?.type === 'object') {
287
- try {
288
- if (this.config.mapObject.deep) {
289
- return isObject(value) ? this.deserialize(value) : value;
290
- }
291
- else {
292
- return typeof value === 'string' ? JSON.parse(value) : value;
293
- }
294
- }
295
- catch {
296
- return value;
297
- }
298
- }
299
- if (field?.type === 'nullable' || isNullable(value, this.config.mapNullable?.includeEmptyString)) {
300
- return this.config.mapNullable.parse?.(value) || value;
301
- }
302
- if (field?.type === 'boolean' ||
303
- typeof value === 'boolean' ||
304
- value === this.config.mapBoolean.true ||
305
- value === this.config.mapBoolean.false) {
306
- return this.config.mapBoolean.parse?.(value) ?? (value === this.config.mapBoolean.true || value === true);
307
- }
308
- const maybeDate = parseToDate(value, this.config.mapDate.dateFormat);
309
- if (field?.type === 'date' || maybeDate) {
310
- return this.config.mapDate.parse?.(value) || maybeDate;
311
- }
312
- const periodTransform = this.config.mapPeriod.transformMode;
313
- if (periodTransform.mode === 'join') {
314
- const maybePeriod = parseToDatePeriod(value, this.config.mapPeriod.dateFormat);
315
- if (field?.type === 'period' || (maybePeriod || []).some(Boolean)) {
316
- return this.config.mapPeriod.parse?.(value) || maybePeriod;
317
- }
318
- }
319
- if (field?.type === 'number' || isNumber(value, this.config.mapNumber.fromString)) {
320
- return this.config.mapNumber.parse?.(value) || Number(String(value).trim());
321
- }
322
- if (field?.type === 'string' || typeof value === 'string') {
323
- const parsed = this.config.mapString.parse?.(value);
324
- if (parsed !== undefined) {
325
- return parsed;
326
- }
327
- return this.config.mapString.trim ? String(value).trim() : value;
328
- }
329
- return value;
330
- }
331
- parseQuery(val) {
332
- return parseQueryParamsByMode(val, undefined, this.resolveArrayFieldModes());
333
- }
334
- parseJsonObject(val) {
335
- try {
336
- const parsed = JSON.parse(val);
337
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
338
- return parsed;
339
- }
340
- }
341
- catch (error) {
342
- throw new Error(`Invalid JSON input: ${error instanceof Error ? error.message : String(error)}`);
343
- }
344
- throw new Error('Invalid JSON input: expected a JSON object');
345
- }
346
- parseInputString(val) {
347
- try {
348
- return this.parseJsonObject(val);
349
- }
350
- catch {
351
- return this.parseQuery(val);
352
- }
353
- }
354
- mergeConfig(base, override) {
355
- return {
356
- ...base,
357
- ...override,
358
- mapString: { ...base.mapString, ...override.mapString },
359
- mapNumber: { ...base.mapNumber, ...override.mapNumber },
360
- mapBoolean: { ...base.mapBoolean, ...override.mapBoolean },
361
- mapDate: { ...base.mapDate, ...override.mapDate },
362
- mapPeriod: {
363
- ...base.mapPeriod,
364
- ...override.mapPeriod,
365
- transformMode: {
366
- ...base.mapPeriod.transformMode,
367
- ...override.mapPeriod?.transformMode,
368
- },
369
- },
370
- mapNullable: { ...base.mapNullable, ...override.mapNullable },
371
- mapArray: { ...base.mapArray, ...override.mapArray },
372
- mapObject: { ...base.mapObject, ...override.mapObject },
373
- mapFields: { ...(base.mapFields || {}), ...(override.mapFields || {}) },
374
- };
375
- }
376
- resolveArrayFieldModes() {
377
- const result = {};
378
- Object.entries(this.config.mapFields || {}).forEach(([key, field]) => {
379
- if ('type' in field && field.type === 'array') {
380
- result[key] = field.concatType ?? this.config.mapArray.concatType;
381
- }
382
- });
383
- return result;
384
- }
385
- }
386
-
387
8
  const createQuerySerializer = (config = {}) => {
388
9
  return new Serializer({
389
10
  mapArray: { concatType: 'comma' },
@@ -409,197 +30,6 @@ const createStrictSerializer = (config = {}) => {
409
30
  });
410
31
  };
411
32
 
412
- class LruCache {
413
- map = new Map();
414
- _limit = 100;
415
- constructor(limit = 100) {
416
- this.limit = limit;
417
- }
418
- get limit() {
419
- return this._limit;
420
- }
421
- get length() {
422
- return this.map.size;
423
- }
424
- set limit(value) {
425
- this._limit = Math.max(1, Math.floor(value || 0));
426
- }
427
- get(key) {
428
- if (!this.map.has(key)) {
429
- return null;
430
- }
431
- const val = this.map.get(key);
432
- this.map.delete(key);
433
- this.map.set(key, val);
434
- return val;
435
- }
436
- set(key, value) {
437
- if (this.map.has(key)) {
438
- this.map.delete(key);
439
- }
440
- else if (this.map.size >= this.limit) {
441
- const oldest = this.map.keys().next().value;
442
- oldest !== undefined && this.map.delete(oldest);
443
- }
444
- this.map.set(key, value);
445
- }
446
- remove(key) {
447
- return this.map.delete(key);
448
- }
449
- clear() {
450
- this.map.clear();
451
- }
452
- has(key) {
453
- return this.map.has(key);
454
- }
455
- keys() {
456
- return Array.from(this.map.keys());
457
- }
458
- values() {
459
- return Array.from(this.map.values());
460
- }
461
- entries() {
462
- return Array.from(this.map.entries());
463
- }
464
- toArray() {
465
- return Array.from(this.map.values());
466
- }
467
- fromArray(entries) {
468
- this.map.clear();
469
- for (const [k, v] of entries) {
470
- this.set(k, v);
471
- }
472
- }
473
- }
474
-
475
- class LocalStorage {
476
- prefix;
477
- constructor(prefix = 're') {
478
- this.prefix = prefix;
479
- }
480
- get length() {
481
- return Object.keys(localStorage).filter((key) => key.startsWith(this.safePrefix())).length;
482
- }
483
- get(key) {
484
- const storageKey = this.getSafePrefix(key);
485
- const raw = localStorage.getItem(storageKey);
486
- if (raw == null) {
487
- return null;
488
- }
489
- try {
490
- const parsed = JSON.parse(raw);
491
- return parsed ?? null;
492
- }
493
- catch {
494
- localStorage.removeItem(storageKey);
495
- return null;
496
- }
497
- }
498
- set(key, value) {
499
- const str = JSON.stringify(value);
500
- localStorage.setItem(this.getSafePrefix(key), str);
501
- }
502
- remove(key) {
503
- return localStorage.removeItem(this.getSafePrefix(key));
504
- }
505
- clear() {
506
- const keys = Object.keys(localStorage).filter((key) => key.startsWith(this.safePrefix()));
507
- keys.forEach((key) => localStorage.removeItem(key));
508
- }
509
- getSafePrefix(key) {
510
- return this.prefix ? `${this.prefix}:${key}` : String(key);
511
- }
512
- safePrefix() {
513
- return this.prefix ? `${this.prefix}:` : '';
514
- }
515
- }
516
-
517
- class MemoryStorage {
518
- cache = new Map();
519
- get length() {
520
- return this.cache.size;
521
- }
522
- get(key) {
523
- return this.cache.get(key) ?? null;
524
- }
525
- set(key, value) {
526
- this.cache.set(key, value);
527
- }
528
- remove(key) {
529
- this.cache.delete(key);
530
- }
531
- clear() {
532
- this.cache.clear();
533
- }
534
- }
535
-
536
- class SessionStorage {
537
- prefix;
538
- constructor(prefix = 're') {
539
- this.prefix = prefix;
540
- }
541
- get length() {
542
- return Object.keys(sessionStorage).filter((key) => key.startsWith(this.safePrefix())).length;
543
- }
544
- get(key) {
545
- const storageKey = this.getSafePrefix(key);
546
- const raw = sessionStorage.getItem(storageKey);
547
- if (raw == null) {
548
- return null;
549
- }
550
- try {
551
- const parsed = JSON.parse(raw);
552
- return parsed ?? null;
553
- }
554
- catch {
555
- sessionStorage.removeItem(storageKey);
556
- return null;
557
- }
558
- }
559
- set(key, value) {
560
- const str = JSON.stringify(value);
561
- sessionStorage.setItem(this.getSafePrefix(key), str);
562
- }
563
- remove(key) {
564
- return sessionStorage.removeItem(this.getSafePrefix(key));
565
- }
566
- clear() {
567
- const keys = Object.keys(sessionStorage).filter((key) => key.startsWith(this.safePrefix()));
568
- keys.forEach((key) => sessionStorage.removeItem(key));
569
- }
570
- getSafePrefix(key) {
571
- return this.prefix ? `${this.prefix}:${key}` : String(key);
572
- }
573
- safePrefix() {
574
- return this.prefix ? `${this.prefix}:` : '';
575
- }
576
- }
577
-
578
- /**
579
- * Factory for data storage strategies.
580
- *
581
- * Returns a `StorageInterface` implementation depending on the selected strategy:
582
- * - `'memory'` — in-memory storage (for the session lifetime);
583
- * - `'session'` — `sessionStorage`, lives until the tab is closed;
584
- * - `'persist'` — `localStorage`, persists between sessions;
585
- * - `'lru'` — size-limited cache (Least Recently Used).
586
- *
587
- * Used to choose an appropriate storage implementation
588
- * depending on the scenario: temporary data, long-term, cache, etc.
589
- *
590
- * @param strategy storage strategy type (`memory`, `session`, `persist`, `lru`)
591
- * @returns instance implementing `StorageInterface<Key, Type>`
592
- */
593
- const storageStrategy = (strategy, options = {}) => {
594
- const fabrics = {
595
- memory: () => new MemoryStorage(),
596
- session: () => new SessionStorage(),
597
- persist: () => new LocalStorage(),
598
- lru: () => new LruCache(options.lruLimit ?? 100),
599
- };
600
- return fabrics[strategy]();
601
- };
602
-
603
33
  // noinspection ES6PreferShortImport
604
34
  const STATUM_CONFIG = new InjectionToken('RE_STATUM_CONFIG');
605
35
  const provideStatum = (config) => makeEnvironmentProviders([{ provide: STATUM_CONFIG, useValue: config }]);
@@ -847,10 +277,10 @@ class KeyedScheduler {
847
277
  class ResourceStore {
848
278
  http = inject(HttpClient);
849
279
  serializer;
850
- #value = signal(null, ...(ngDevMode ? [{ debugName: "#value" }] : []));
851
- #status = signal('idle', ...(ngDevMode ? [{ debugName: "#status" }] : []));
852
- #error = signal(null, ...(ngDevMode ? [{ debugName: "#error" }] : []));
853
- #activeRequests = signal(0, ...(ngDevMode ? [{ debugName: "#activeRequests" }] : []));
280
+ #value = signal(null, ...(ngDevMode ? [{ debugName: "#value" }] : /* istanbul ignore next */ []));
281
+ #status = signal('idle', ...(ngDevMode ? [{ debugName: "#status" }] : /* istanbul ignore next */ []));
282
+ #error = signal(null, ...(ngDevMode ? [{ debugName: "#error" }] : /* istanbul ignore next */ []));
283
+ #activeRequests = signal(0, ...(ngDevMode ? [{ debugName: "#activeRequests" }] : /* istanbul ignore next */ []));
854
284
  /**
855
285
  * Current resource value.
856
286
  * Returns `null` if no data yet or the request failed.
@@ -868,7 +298,7 @@ class ResourceStore {
868
298
  * Convenience loading flag: `true` when `loading` or `stale`.
869
299
  * Useful for spinners and disabling buttons.
870
300
  */
871
- loading = computed(() => this.#activeRequests() > 0 || this.#status() === 'stale', ...(ngDevMode ? [{ debugName: "loading" }] : []));
301
+ loading = computed(() => this.#activeRequests() > 0 || this.#status() === 'stale', ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
872
302
  routes;
873
303
  opts;
874
304
  maxEntries;
@@ -1409,22 +839,22 @@ class PagedQueryStore {
1409
839
  #transport;
1410
840
  #cache;
1411
841
  /** Current page data (reactive). */
1412
- items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
842
+ items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1413
843
  /** Merged cache of pages (flat list) handy for search/export. */
1414
- cached = signal([], ...(ngDevMode ? [{ debugName: "cached" }] : []));
844
+ cached = signal([], ...(ngDevMode ? [{ debugName: "cached" }] : /* istanbul ignore next */ []));
1415
845
  /** Loading flag of the current operation. */
1416
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
846
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
1417
847
  /** Last request error (if any). */
1418
- error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
848
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
1419
849
  /** Increments when the current dataset is reset/replaced. Useful for external consumers with local buffers. */
1420
- version = signal(0, ...(ngDevMode ? [{ debugName: "version" }] : []));
1421
- #page = signal(0, ...(ngDevMode ? [{ debugName: "#page" }] : []));
1422
- #pageSize = signal(20, ...(ngDevMode ? [{ debugName: "#pageSize" }] : []));
1423
- #totalElements = signal(0, ...(ngDevMode ? [{ debugName: "#totalElements" }] : []));
1424
- #filters = signal({}, ...(ngDevMode ? [{ debugName: "#filters" }] : []));
1425
- #query = signal({}, ...(ngDevMode ? [{ debugName: "#query" }] : []));
1426
- #sort = signal([], ...(ngDevMode ? [{ debugName: "#sort" }] : []));
1427
- #routeParams = signal({}, ...(ngDevMode ? [{ debugName: "#routeParams" }] : []));
850
+ version = signal(0, ...(ngDevMode ? [{ debugName: "version" }] : /* istanbul ignore next */ []));
851
+ #page = signal(0, ...(ngDevMode ? [{ debugName: "#page" }] : /* istanbul ignore next */ []));
852
+ #pageSize = signal(20, ...(ngDevMode ? [{ debugName: "#pageSize" }] : /* istanbul ignore next */ []));
853
+ #totalElements = signal(0, ...(ngDevMode ? [{ debugName: "#totalElements" }] : /* istanbul ignore next */ []));
854
+ #filters = signal({}, ...(ngDevMode ? [{ debugName: "#filters" }] : /* istanbul ignore next */ []));
855
+ #query = signal({}, ...(ngDevMode ? [{ debugName: "#query" }] : /* istanbul ignore next */ []));
856
+ #sort = signal([], ...(ngDevMode ? [{ debugName: "#sort" }] : /* istanbul ignore next */ []));
857
+ #routeParams = signal({}, ...(ngDevMode ? [{ debugName: "#routeParams" }] : /* istanbul ignore next */ []));
1428
858
  pageState = this.#page.asReadonly();
1429
859
  pageSizeState = this.#pageSize.asReadonly();
1430
860
  totalElementsState = this.#totalElements.asReadonly();
@@ -1855,18 +1285,18 @@ class DictStore {
1855
1285
  metaStorage;
1856
1286
  ttlMs;
1857
1287
  revalidate;
1858
- cacheUpdatedAt = signal(null, ...(ngDevMode ? [{ debugName: "cacheUpdatedAt" }] : []));
1288
+ cacheUpdatedAt = signal(null, ...(ngDevMode ? [{ debugName: "cacheUpdatedAt" }] : /* istanbul ignore next */ []));
1859
1289
  /**
1860
1290
  * Search text.
1861
1291
  * With `fixed: true` filters the local cache; with `fixed: false` triggers server search.
1862
1292
  */
1863
- searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
1293
+ searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : /* istanbul ignore next */ []));
1864
1294
  debouncedSearchText;
1865
1295
  /**
1866
1296
  * Additional filters for server request (or presets).
1867
1297
  */
1868
- filters = signal({}, ...(ngDevMode ? [{ debugName: "filters" }] : []));
1869
- cachedItems = signal([], ...(ngDevMode ? [{ debugName: "cachedItems" }] : []));
1298
+ filters = signal({}, ...(ngDevMode ? [{ debugName: "filters" }] : /* istanbul ignore next */ []));
1299
+ cachedItems = signal([], ...(ngDevMode ? [{ debugName: "cachedItems" }] : /* istanbul ignore next */ []));
1870
1300
  /**
1871
1301
  * Current list of dictionary items.
1872
1302
  * Source — local cache (fixed=true) or data from `PagedQueryStore`.
@@ -1877,7 +1307,7 @@ class DictStore {
1877
1307
  return this.debouncedSearchText() || !cached.length ? this.#helper.items() : cached;
1878
1308
  }
1879
1309
  return cached.length ? this.filterLocal() : this.#helper.items();
1880
- }, ...(ngDevMode ? [{ debugName: "items" }] : []));
1310
+ }, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1881
1311
  /**
1882
1312
  * Ready-to-use dropdown options: `{ label, value }`.
1883
1313
  * Respects `maxOptionsSize` for truncating the list.
@@ -1885,9 +1315,9 @@ class DictStore {
1885
1315
  options = computed(() => {
1886
1316
  const options = this.items().map((it) => ({ label: String(it[this.labelKey] ?? ''), value: it[this.valueKey] }));
1887
1317
  return this.maxOptionsSize ? options.slice(0, this.maxOptionsSize) : options;
1888
- }, ...(ngDevMode ? [{ debugName: "options" }] : []));
1318
+ }, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1889
1319
  _lastPromise = null;
1890
- _armed = signal(false, ...(ngDevMode ? [{ debugName: "_armed" }] : []));
1320
+ _armed = signal(false, ...(ngDevMode ? [{ debugName: "_armed" }] : /* istanbul ignore next */ []));
1891
1321
  // todo add i18n support
1892
1322
  /**
1893
1323
  * @param apiUrl dictionary endpoint (e.g., `'/api/dicts/countries'`)
@@ -2141,8 +1571,8 @@ class DictLocalStore {
2141
1571
  * Represents the full, unfiltered list of dictionary entries.
2142
1572
  * Used as the base data set for search and option generation.
2143
1573
  */
2144
- items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
2145
- #draftItems = signal([], ...(ngDevMode ? [{ debugName: "#draftItems" }] : []));
1574
+ items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1575
+ #draftItems = signal([], ...(ngDevMode ? [{ debugName: "#draftItems" }] : /* istanbul ignore next */ []));
2146
1576
  /**
2147
1577
  * Computed list of options in `{ label, value }` format.
2148
1578
  *
@@ -2157,7 +1587,7 @@ class DictLocalStore {
2157
1587
  value: it[this.valueKey],
2158
1588
  }));
2159
1589
  return this.maxOptionsSize ? options.slice(0, this.maxOptionsSize) : options;
2160
- }, ...(ngDevMode ? [{ debugName: "options" }] : []));
1590
+ }, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
2161
1591
  labelKey;
2162
1592
  valueKey;
2163
1593
  maxOptionsSize;
@@ -2242,14 +1672,14 @@ class DictLocalStore {
2242
1672
  class EntityStore {
2243
1673
  idKey;
2244
1674
  sortIds;
2245
- byId = signal({}, ...(ngDevMode ? [{ debugName: "byId" }] : []));
2246
- ids = signal([], ...(ngDevMode ? [{ debugName: "ids" }] : []));
1675
+ byId = signal({}, ...(ngDevMode ? [{ debugName: "byId" }] : /* istanbul ignore next */ []));
1676
+ ids = signal([], ...(ngDevMode ? [{ debugName: "ids" }] : /* istanbul ignore next */ []));
2247
1677
  items = computed(() => {
2248
1678
  const byId = this.byId();
2249
1679
  return this.ids()
2250
1680
  .map((id) => byId[String(id)])
2251
1681
  .filter((item) => item !== undefined);
2252
- }, ...(ngDevMode ? [{ debugName: "items" }] : []));
1682
+ }, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
2253
1683
  constructor(config) {
2254
1684
  this.idKey = config.idKey;
2255
1685
  this.sortIds = config.sortIds;
@@ -2372,5 +1802,5 @@ class EntityStore {
2372
1802
  * Generated bundle index. Do not edit.
2373
1803
  */
2374
1804
 
2375
- export { AbortError, CacheMissError, DictLocalStore, DictStore, EntityStore, LruCache, PagedQueryStore, RESOURCE_PROFILES, ResourceStore, STATUM_CONFIG, Serializer, SerializerFieldError, createBodySerializer, createQuerySerializer, createResourceProfile, createStrictSerializer, isAbort, provideStatum, storageStrategy };
1805
+ export { AbortError, CacheMissError, DictLocalStore, DictStore, EntityStore, PagedQueryStore, RESOURCE_PROFILES, ResourceStore, STATUM_CONFIG, createBodySerializer, createQuerySerializer, createResourceProfile, createStrictSerializer, isAbort, provideStatum };
2376
1806
  //# sourceMappingURL=reforgium-statum.mjs.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "3.1.0",
2
+ "version": "3.1.1",
3
3
  "name": "@reforgium/statum",
4
- "description": "Signals-first query and data stores for Angular",
4
+ "description": "Signals-first API state and query stores for Angular",
5
5
  "author": "rtommievich",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -21,10 +21,21 @@
21
21
  },
22
22
  "keywords": [
23
23
  "reforgium",
24
- "state",
24
+ "angular",
25
+ "angular-library",
26
+ "signals",
27
+ "signal-store",
25
28
  "store",
26
- "signal",
27
- "angular"
29
+ "data-store",
30
+ "resource-store",
31
+ "entity-store",
32
+ "dictionary",
33
+ "query",
34
+ "pagination",
35
+ "cache",
36
+ "http",
37
+ "api",
38
+ "serializer"
28
39
  ],
29
40
  "types": "../../dist/@reforgium/statum/index.d.ts",
30
41
  "files": [
@@ -42,7 +53,7 @@
42
53
  "peerDependencies": {
43
54
  "@angular/common": ">=18.0.0",
44
55
  "@angular/core": ">=18.0.0",
45
- "@reforgium/internal": ">=1.3.0",
56
+ "@reforgium/internal": ">=2.0.0",
46
57
  "rxjs": ">=7.0.0"
47
58
  },
48
59
  "module": "fesm2022/reforgium-statum.mjs",
@@ -1,286 +1,14 @@
1
- import { AnyType, AnyDict, RestMethods, QueryParams, PageableRequest, PageableResponse } from '@reforgium/internal';
1
+ import { DataType, SerializerConfig, Serializer, RestMethods, AnyDict, AnyType, QueryParams, PageableRequest, PageableResponse, StorageStrategy } from '@reforgium/internal';
2
+ export { DataType, FieldConcatType, FieldConfig, FormatConfig, LocalStorage, LruCache, MemoryStorage, ParseFormatConfig, SerializedType, Serializer, SerializerConfig, SerializerFieldError, SessionStorage, StorageInterface, StorageStrategy, StorageStrategyOptions, Types, storageStrategy } from '@reforgium/internal';
2
3
  import * as _angular_core from '@angular/core';
3
4
  import { Signal, WritableSignal, EnvironmentProviders, InjectionToken } from '@angular/core';
4
5
  import { HttpResponse } from '@angular/common/http';
5
6
  import * as _reforgium_statum from '@reforgium/statum';
6
7
 
7
- /**
8
- * List of supported primitive and composite data types
9
- * that can be serialized/deserialized.
10
- *
11
- * Used in field serialization configuration.
12
- */
13
- type Types = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'date' | 'period' | 'nullable';
14
- type FieldConcatType = 'comma' | 'multi' | 'json';
15
- /**
16
- * Allowed "flat" values after serialization.
17
- * Such values are safe to pass in a query string or JSON.
18
- */
19
- type SerializedPrimitives = string | number | boolean | null;
20
- type SerializedTypeType = SerializedPrimitives | SerializedPrimitives[];
21
- type DatePeriod = [Date | null, Date | null];
22
- /**
23
- * Generic configuration shape to define `parse`/`format`
24
- * for converting a value from/to a serialized form.
25
- *
26
- * @template TypeFrom — domain type before serialization
27
- * @template TypeTo — serialized type (by default a primitive/array of primitives)
28
- */
29
- type ParseFormatConfig<TypeFrom, TypeTo = SerializedTypeType> = {
30
- parse?: (val: TypeTo) => TypeFrom;
31
- format?: (val: TypeFrom) => TypeTo;
32
- };
33
- /**
34
- * Simplified configuration when only `format` is needed.
35
- */
36
- type FormatConfig<TypeFrom, TypeTo = SerializedTypeType> = {
37
- format?: (val: TypeFrom) => TypeTo;
38
- };
39
- type FieldConfig = ({
40
- type: Types;
41
- concatType?: FieldConcatType;
42
- } | FieldsTypeConfig<DataType, AnyType>) & {
43
- concatType?: FieldConcatType;
44
- };
45
- type FieldsTypeConfig<EntityType extends DataType, TypeFrom, TypeTo = SerializedTypeType> = {
46
- parse: (val: TypeTo, data: SerializedType) => TypeFrom;
47
- format: (val: TypeFrom, data: EntityType) => TypeTo;
48
- };
49
- type PeriodSplitMode = {
50
- mode: 'split';
51
- dateFromKeyPostfix: string;
52
- dateToKeyPostfix: string;
53
- };
54
- type PeriodJoinMode = {
55
- mode: 'join';
56
- concat: string;
57
- };
58
- type SerializerConfig = {
59
- mapString: {
60
- trim?: boolean;
61
- } & ParseFormatConfig<string>;
62
- mapNumber: {
63
- fromString?: boolean;
64
- } & ParseFormatConfig<number>;
65
- mapBoolean: {
66
- true?: string;
67
- false?: string;
68
- } & ParseFormatConfig<boolean>;
69
- mapDate: {
70
- dateFormat?: string;
71
- } & ParseFormatConfig<Date>;
72
- /**
73
- * Settings for date period transformation.
74
- * Allows choosing a mode:
75
- * - split: split the period into two keys (e.g., createdFrom/createdTo)
76
- * - join: join into a string using a delimiter
77
- * And set the date format.
78
- */
79
- mapPeriod: {
80
- transformMode?: PeriodSplitMode | PeriodJoinMode;
81
- dateFormat?: string;
82
- } & ParseFormatConfig<DatePeriod>;
83
- /**
84
- * Settings for handling "empty" values.
85
- * - remove: remove the field from the result;
86
- * - replaceWith: replace with a fixed value;
87
- * - includeEmptyString: treat an empty string as "nullable".
88
- */
89
- mapNullable: {
90
- remove?: boolean;
91
- replaceWith?: string;
92
- includeEmptyString?: boolean;
93
- } & ParseFormatConfig<null | undefined>;
94
- /**
95
- * Array settings:
96
- * - concatType: serialization strategy (`comma` | `multi` | `json`);
97
- * - removeNullable: remove empty items;
98
- * - format: custom array formatter.
99
- */
100
- mapArray: {
101
- concatType: FieldConcatType;
102
- removeNullable?: boolean;
103
- } & FormatConfig<AnyType[]>;
104
- /**
105
- * Object settings:
106
- * - deep: deep (recursive) serialization or treat as JSON string;
107
- * - format: custom object formatter.
108
- */
109
- mapObject: {
110
- deep: boolean;
111
- } & FormatConfig<DataType>;
112
- /**
113
- * Per-field overrides by key.
114
- */
115
- mapFields?: Record<string, FieldConfig>;
116
- };
117
- /**
118
- * Flat serialization result: dictionary key → primitive(s).
119
- */
120
- type SerializedType = Record<string, SerializedTypeType>;
121
- /**
122
- * Generic dictionary of domain data.
123
- */
124
- type DataType = AnyDict;
125
-
126
- declare class SerializerFieldError extends Error {
127
- readonly field: string;
128
- readonly stage: 'parse' | 'format';
129
- readonly originalError: unknown;
130
- constructor(field: string, stage: 'parse' | 'format', originalError: unknown);
131
- }
132
- /**
133
- * Universal serializer/deserializer for values used in forms, filters, and DTOs.
134
- *
135
- * Supports types: `string | number | boolean | Date | [Date, Date] (period) | array | object | nullable`.
136
- * Capabilities:
137
- * - normalize values according to config (trim strings, parse numbers from strings, boolean strings, etc.),
138
- * - transform date periods into paired keys (`from/to`) or a single joined string,
139
- * - build/parse a query string (or JSON) to and from an object.
140
- *
141
- * Example:
142
- * ```ts
143
- * type Filters = { q?: string; active?: boolean; created?: [Date, Date] | null };
144
- * const s = new Serializer<Filters>({
145
- * mapPeriod: { transformMode: { mode: 'split', dateFromKeyPostfix: 'From', dateToKeyPostfix: 'To' } }
146
- * });
147
- *
148
- * // -> { q: 'john', createdFrom: '2025-01-01', createdTo: '2025-01-31' }
149
- * const plain = s.serialize({
150
- * q: ' john ',
151
- * active: undefined,
152
- * created: [new Date('2025-01-01'), new Date('2025-01-31')]
153
- * });
154
- *
155
- * // -> 'q=john&createdFrom=2025-01-01&createdTo=2025-01-31'
156
- * const qs = s.toQuery({ q: 'john', created: [new Date('2025-01-01'), new Date('2025-01-31')] });
157
- *
158
- * // <- { q: 'john', created: [Date, Date] }
159
- * const parsed = s.deserialize('q=john&createdFrom=2025-01-01&createdTo=2025-01-31');
160
- * ```
161
- */
162
- declare class Serializer<EntityType extends DataType> {
163
- readonly config: SerializerConfig;
164
- /**
165
- * Creates a serializer with a partially overridden configuration.
166
- * Provide only the options you want to change (the rest are taken from defaults).
167
- *
168
- * @param config partial transformation configuration
169
- */
170
- constructor(config?: Partial<SerializerConfig>);
171
- /**
172
- * Converts a domain object into a flat serialized representation
173
- * (ready to send to an API or build a query string).
174
- *
175
- * Rules are taken from `config`:
176
- * — strings can be trimmed (if enabled),
177
- * — numbers can be converted from strings/numbers,
178
- * — boolean supports custom true/false representations,
179
- * — dates are formatted by `dateFormat`,
180
- * — date periods can be split/joined,
181
- * — `nullable` can be removed from the result (`remove`) or formatted.
182
- *
183
- * @param obj source object
184
- * @returns a flat dictionary with string/primitive values
185
- */
186
- serialize(obj: EntityType, _seen?: WeakSet<object>): SerializedType;
187
- /**
188
- * Parse serialized data into a domain object.
189
- *
190
- * Source can be:
191
- * — a query string (`key=value&arr=1,2`) or `JSON.stringify(obj)`,
192
- * — an already prepared flat object.
193
- *
194
- * Transformations are reverse of `serialize`: strings → number/boolean/Date/period,
195
- * arrays are collected according to strategy (`comma`/`pipe`/`multi`), objects — deeply or as JSON.
196
- *
197
- * @param val query string or object
198
- * @returns a domain object of the specified type
199
- */
200
- deserialize: (val: string | AnyDict) => EntityType;
201
- /** Parse only query-string input. */
202
- deserializeQuery: (query: string) => EntityType;
203
- /** Parse only JSON object input. */
204
- deserializeJson: (json: string) => EntityType;
205
- /**
206
- * Build a query string from a domain object using `serialize` rules
207
- * and the array joining strategy (`concatType`).
208
- *
209
- * @param val domain object
210
- * @returns query string (suitable for URL or history API)
211
- */
212
- toQuery: (val: EntityType) => string;
213
- /**
214
- * Returns a new serializer instance with a merged configuration.
215
- * Useful for ad-hoc overrides for a specific call.
216
- *
217
- * @param config partial config changes
218
- * @returns new `Serializer` with the provided `config` applied
219
- */
220
- withConfig(config: Partial<SerializerConfig>): Serializer<EntityType>;
221
- private serializeElement;
222
- private deserializeElement;
223
- private parseQuery;
224
- private parseJsonObject;
225
- private parseInputString;
226
- private mergeConfig;
227
- private resolveArrayFieldModes;
228
- }
229
-
230
8
  declare const createQuerySerializer: <EntityType extends DataType = DataType>(config?: Partial<SerializerConfig>) => Serializer<EntityType>;
231
9
  declare const createBodySerializer: <EntityType extends DataType = DataType>(config?: Partial<SerializerConfig>) => Serializer<EntityType>;
232
10
  declare const createStrictSerializer: <EntityType extends DataType = DataType>(config?: Partial<SerializerConfig>) => Serializer<EntityType>;
233
11
 
234
- type StorageStrategy = 'memory' | 'lru' | 'session' | 'persist';
235
- type StorageStrategyOptions = {
236
- lruLimit?: number;
237
- };
238
-
239
- type StorageInterface<Key, Type> = {
240
- prefix?: string;
241
- get(key: Key): Type | null;
242
- set(key: Key, value: Type): void;
243
- remove(key: Key): void;
244
- clear(): void;
245
- get length(): number;
246
- };
247
-
248
- declare class LruCache<KeyT, ValueT> implements StorageInterface<KeyT, ValueT> {
249
- private map;
250
- private _limit;
251
- constructor(limit?: number);
252
- get limit(): number;
253
- get length(): number;
254
- set limit(value: number);
255
- get(key: KeyT): NonNullable<ValueT> | null;
256
- set(key: KeyT, value: ValueT): void;
257
- remove(key: KeyT): boolean;
258
- clear(): void;
259
- has(key: KeyT): boolean;
260
- keys(): KeyT[];
261
- values(): ValueT[];
262
- entries(): [KeyT, ValueT][];
263
- toArray(): ValueT[];
264
- fromArray(entries: [KeyT, ValueT][]): void;
265
- }
266
-
267
- /**
268
- * Factory for data storage strategies.
269
- *
270
- * Returns a `StorageInterface` implementation depending on the selected strategy:
271
- * - `'memory'` — in-memory storage (for the session lifetime);
272
- * - `'session'` — `sessionStorage`, lives until the tab is closed;
273
- * - `'persist'` — `localStorage`, persists between sessions;
274
- * - `'lru'` — size-limited cache (Least Recently Used).
275
- *
276
- * Used to choose an appropriate storage implementation
277
- * depending on the scenario: temporary data, long-term, cache, etc.
278
- *
279
- * @param strategy storage strategy type (`memory`, `session`, `persist`, `lru`)
280
- * @returns instance implementing `StorageInterface<Key, Type>`
281
- */
282
- declare const storageStrategy: <Key = string, Type extends AnyType = AnyDict>(strategy: StorageStrategy, options?: StorageStrategyOptions) => StorageInterface<Key, Type>;
283
-
284
12
  /**
285
13
  * Object for request body (payload).
286
14
  * Commonly used with POST/PUT/PATCH/DELETE.
@@ -1221,6 +949,6 @@ type StatumConfig = PagedQueryProviderConfig & SerializerProviderConfig & DictPr
1221
949
  declare const STATUM_CONFIG: InjectionToken<StatumConfig>;
1222
950
  declare const provideStatum: (config: StatumConfig) => EnvironmentProviders;
1223
951
 
1224
- export { AbortError, CacheMissError, DictLocalStore, DictStore, EntityStore, LruCache, PagedQueryStore, RESOURCE_PROFILES, ResourceStore, STATUM_CONFIG, Serializer, SerializerFieldError, createBodySerializer, createQuerySerializer, createResourceProfile, createStrictSerializer, isAbort, provideStatum, storageStrategy };
1225
- export type { DataType, DictLocalConfig, DictStoreConfig, DictStoreProviderConfig, EntityId, EntityStoreConfig, FetchInput, FetchParams, FieldConfig, OffsetPaginationType, PagedQueryStoreConfig, PagedQueryStoreProviderConfig, QuerySortInput, QuerySortOrder, QuerySortRule, RefetchWithInput, ResourceProfileName, ResourceRoutesMap, ResourceStatus, ResourceStoreOptions, ResourceTraceEvent, RetryConfig, SerializedType, SerializerConfig, SetRouteParamsOptions, StatumConfig, StorageInterface, StorageStrategy, StorageStrategyOptions, Types, UpdateByOffsetOptions, UpdatePageInput, UpdatePageOptions };
952
+ export { AbortError, CacheMissError, DictLocalStore, DictStore, EntityStore, PagedQueryStore, RESOURCE_PROFILES, ResourceStore, STATUM_CONFIG, createBodySerializer, createQuerySerializer, createResourceProfile, createStrictSerializer, isAbort, provideStatum };
953
+ export type { DictLocalConfig, DictStoreConfig, DictStoreProviderConfig, EntityId, EntityStoreConfig, FetchInput, FetchParams, OffsetPaginationType, PagedQueryStoreConfig, PagedQueryStoreProviderConfig, QuerySortInput, QuerySortOrder, QuerySortRule, RefetchWithInput, ResourceProfileName, ResourceRoutesMap, ResourceStatus, ResourceStoreOptions, ResourceTraceEvent, RetryConfig, SetRouteParamsOptions, StatumConfig, UpdateByOffsetOptions, UpdatePageInput, UpdatePageOptions };
1226
954
  //# sourceMappingURL=reforgium-statum.d.ts.map