@avstantso/core 1.2.2 → 1.3.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 +24 -0
- package/README.md +124 -1
- package/dist/_global/_register.d.ts +2 -1
- package/dist/_global/deep-ops.d.ts +505 -0
- package/dist/_global/func.d.ts +15 -2
- package/dist/_global/index.d.ts +2 -0
- package/dist/_global/low-level.d.ts +116 -0
- package/dist/export.d.ts +2 -1
- package/dist/index.js +575 -53
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [1.3.0] - 2026-07-04
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- `DeepOps.freeze`
|
|
9
|
+
- `DeepOps.clone`
|
|
10
|
+
- `DeepOps.merge`
|
|
11
|
+
|
|
12
|
+
## [1.2.4] - 2026-07-04
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `TS.Func.Type`
|
|
17
|
+
- `Func.Types`
|
|
18
|
+
- `Func.determineType`
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [1.2.3] - 2026-06-19
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Makes singleton idempotent for VM-reload. Like it need for Jest
|
|
26
|
+
|
|
27
|
+
|
|
4
28
|
## [1.2.2] - 2026-02-04
|
|
5
29
|
|
|
6
30
|
### Changed
|
package/README.md
CHANGED
|
@@ -45,11 +45,12 @@ avstantso.Catch(error); // Handle errors using centralized error handling
|
|
|
45
45
|
You can also import specific members directly without relying on the global singleton:
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
|
-
import { Func, Generic, X } from '@avstantso/core';
|
|
48
|
+
import { Func, Generic, X, DeepOps } from '@avstantso/core';
|
|
49
49
|
|
|
50
50
|
// Use imported members directly
|
|
51
51
|
const isExtended = Func.isExt(myFunction);
|
|
52
52
|
const casted = Generic.Cast<MyType>(value);
|
|
53
|
+
const cloned = DeepOps.clone(someValue);
|
|
53
54
|
|
|
54
55
|
// X is a no-op function useful for Promise chains
|
|
55
56
|
promise.then(X).catch(X); // Ignore both success and error
|
|
@@ -59,6 +60,7 @@ promise.then(X).catch(X); // Ignore both success and error
|
|
|
59
60
|
- `Func` - Function utilities (same as `avstantso.Func`)
|
|
60
61
|
- `Generic` - Generic type utilities (same as `avstantso.Generic`)
|
|
61
62
|
- `X` - No-op function for ignoring Promise results
|
|
63
|
+
- `DeepOps` - Deep freeze/clone/merge utilities (same as `avstantso.DeepOps`)
|
|
62
64
|
|
|
63
65
|
## API Reference
|
|
64
66
|
|
|
@@ -356,6 +358,82 @@ const cloned = deepClone(original);
|
|
|
356
358
|
// Custom Date instance is properly cloned using registered clone method
|
|
357
359
|
```
|
|
358
360
|
|
|
361
|
+
### AVStantso.DeepOps - Deep Freeze / Clone / Merge
|
|
362
|
+
|
|
363
|
+
Deep, recursive `freeze`/`clone`/`merge` operations over plain objects, `Array`/`Set`/`Map`, functions, atomic objects (`Date`/`Buffer`, see [`AtomicObjects`](#avstantsoatomicobjects---custom-object-registry)), and scalars. Each operation walks a value's node types (`scalar`, `atomic`, `plain`, `rich`, `func`) and can be tuned per-call via `walk{NodeType}Inc`/`walk{NodeType}Exc` options; sensible defaults are applied for each operation (see method docs below).
|
|
364
|
+
|
|
365
|
+
**Members:**
|
|
366
|
+
|
|
367
|
+
- `avstantso.DeepOps.NodeTypes` - `Key2Key` record of the node type names (`scalar`, `atomic`, `plain`, `rich`, `func`)
|
|
368
|
+
- `avstantso.DeepOps.is.options.*` - Typeguards for the various options shapes
|
|
369
|
+
- `avstantso.DeepOps.freeze(obj, options?)` - Recursively `Object.freeze` a value
|
|
370
|
+
- `avstantso.DeepOps.clone(obj, options?)` - Recursively clone a value
|
|
371
|
+
- `avstantso.DeepOps.merge(target, ...sources, options)` - Recursively merge one or more sources into a clone of `target`
|
|
372
|
+
|
|
373
|
+
##### `avstantso.DeepOps.freeze(obj, options?)`
|
|
374
|
+
|
|
375
|
+
Recursively `Object.freeze`s `obj` in place and returns the same reference. By default, walks plain objects, `Array`/`Set`/`Map`/class instances, and functions (except `class` and `generator` functions, which are left unfrozen) — scalars and atomic objects (`Date`/`Buffer`) are **not** walked/frozen by default.
|
|
376
|
+
|
|
377
|
+
**Options:**
|
|
378
|
+
- `ownProps?: boolean` - If `true`, also walks own non-enumerable properties (not just enumerable ones)
|
|
379
|
+
- `walk{Scalar|Atomic|Plain|Rich|Func}{Inc|Exc}` - Fine-tune which node types are walked, optionally per sub-kind (JS type, class, or function kind) or via a custom walker function
|
|
380
|
+
|
|
381
|
+
**Custom per-object behavior:** an object with a method at `avstantso.DeepOps.freeze.symbol` (same symbol as `avstantso.symbolFreeze`) has that method called instead of the default freeze logic for that object.
|
|
382
|
+
|
|
383
|
+
**Example:**
|
|
384
|
+
```typescript
|
|
385
|
+
const config = { db: { host: 'localhost', port: 5432 }, tags: ['a', 'b'] };
|
|
386
|
+
avstantso.DeepOps.freeze(config);
|
|
387
|
+
|
|
388
|
+
config.db.port = 5433; // throws TypeError: Cannot assign to read only property
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
##### `avstantso.DeepOps.clone(obj, options?)`
|
|
392
|
+
|
|
393
|
+
Recursively clones `obj` into a brand-new structure. By default, walks scalars, atomic objects (cloned via their registered `AtomicObjects` descriptor), plain objects, `Array`/`Set`/`Map` (**not** arbitrary class instances), and functions (except `class`/`generator`, which are omitted from the clone entirely).
|
|
394
|
+
|
|
395
|
+
**Options:**
|
|
396
|
+
- `copyFuncs?: boolean` - If `true`, copies function references as-is instead of wrapping them in a new delegating function
|
|
397
|
+
- `ownProps?: boolean` / `walk{NodeType}{Inc|Exc}` - Same as `freeze`
|
|
398
|
+
|
|
399
|
+
**Custom per-object behavior:** an object with a method at `avstantso.DeepOps.clone.symbol` has that method called, and its return value used as the clone, bypassing default clone logic for that object.
|
|
400
|
+
|
|
401
|
+
**Example:**
|
|
402
|
+
```typescript
|
|
403
|
+
const original = { name: 'John', birthDate: new Date('1990-01-01'), tags: ['dev', 'ts'] };
|
|
404
|
+
const cloned = avstantso.DeepOps.clone(original);
|
|
405
|
+
|
|
406
|
+
cloned.tags.push('new'); // does not affect original.tags
|
|
407
|
+
cloned.birthDate.setFullYear(2000); // does not affect original.birthDate
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
##### `avstantso.DeepOps.merge(target, ...sources, options)`
|
|
411
|
+
|
|
412
|
+
Recursively merges one or more `sources` into a **clone** of `target` (returned as a new object — `target` itself is never mutated) and returns the result. Options are always the last argument. Uses the same default node walking as `clone`.
|
|
413
|
+
|
|
414
|
+
Merge behavior by node type:
|
|
415
|
+
- **Plain objects** - keys are merged recursively; keys only in a source are added (unless `onlyExists`)
|
|
416
|
+
- **Arrays** - merged **by index** by default (source overwrites overlapping indices, extra source items are appended); pass `concatArrays: true` to concatenate instead (`[...target, ...source]`)
|
|
417
|
+
- **Sets** - source items are added if not already present
|
|
418
|
+
- **Maps** - existing keys are merged recursively / overwritten, new keys added (unless `onlyExists`)
|
|
419
|
+
- **Scalars/atomics, or a type mismatch between target/source** - the source always wins (cloned)
|
|
420
|
+
|
|
421
|
+
**Options** (each may be a value or a `(node, key?) => value` callback, e.g. to target specific keys):
|
|
422
|
+
- `onlyExists?: Common.Option<boolean>` - If truthy, only merges into keys/indices that already exist in the target
|
|
423
|
+
- `isUniqueArrItems?: Common.Option<boolean>` - If truthy, dedupes the resulting array (`[...new Set(arr)]`)
|
|
424
|
+
- `nullAsDelete?: Common.Option<boolean>` - If truthy, a `null` source value deletes the corresponding target key instead of setting it to `null`
|
|
425
|
+
- `concatArrays?: Common.Option<boolean>` - If truthy, arrays are merged by concatenation instead of by index
|
|
426
|
+
|
|
427
|
+
**Example:**
|
|
428
|
+
```typescript
|
|
429
|
+
const result = avstantso.DeepOps.merge(
|
|
430
|
+
{ a: 1, tags: ['x'], history: [1, 2] },
|
|
431
|
+
{ a: 2, tags: ['x', 'y'], history: [3] },
|
|
432
|
+
{ isUniqueArrItems: (arr, key) => key === 'tags' }
|
|
433
|
+
);
|
|
434
|
+
// { a: 2, tags: ['x', 'y'], history: [1, 3] }
|
|
435
|
+
```
|
|
436
|
+
|
|
359
437
|
### AVStantso.Generic - Generic Type Utilities
|
|
360
438
|
|
|
361
439
|
Utilities for working with generic types.
|
|
@@ -380,12 +458,57 @@ Runtime utilities for working with functions.
|
|
|
380
458
|
|
|
381
459
|
**Members:**
|
|
382
460
|
|
|
461
|
+
- `avstantso.Func.Types` - `Key2Key` record of runtime function type names
|
|
462
|
+
- `avstantso.Func.determineType(func)` - Determines the runtime type of a function
|
|
383
463
|
- `avstantso.Func.OwnPropertyDescriptors` - Property descriptors of the base Function prototype
|
|
384
464
|
- `avstantso.Func.isPropAllowed(key)` - Check if a property name can be used for function extension
|
|
385
465
|
- `avstantso.Func.isPropAllowed.Not(key)` - Check if property name is reserved
|
|
386
466
|
- `avstantso.Func.isExt(func)` - Check if function has extended properties
|
|
387
467
|
- `avstantso.Func.Dynamic<N, F>(name, func)` - Creates a named function wrapper for better debugging in development environments
|
|
388
468
|
|
|
469
|
+
##### `avstantso.Func.determineType(func)`
|
|
470
|
+
|
|
471
|
+
Determines the runtime type of a function: `'plain'`, `'async'`, `'generator'`, `'class'`, `'arrow'`, or `'bound'`.
|
|
472
|
+
|
|
473
|
+
**Parameters:**
|
|
474
|
+
- `func` - Function to inspect
|
|
475
|
+
|
|
476
|
+
**Returns:** `AVStantso.TS.Func.Type` - one of the runtime function type names
|
|
477
|
+
|
|
478
|
+
**Example:**
|
|
479
|
+
```typescript
|
|
480
|
+
function plain() {}
|
|
481
|
+
avstantso.Func.determineType(plain); // 'plain'
|
|
482
|
+
|
|
483
|
+
const arrow = () => {};
|
|
484
|
+
avstantso.Func.determineType(arrow); // 'arrow'
|
|
485
|
+
|
|
486
|
+
const bound = plain.bind(null);
|
|
487
|
+
avstantso.Func.determineType(bound); // 'bound'
|
|
488
|
+
|
|
489
|
+
class MyClass {}
|
|
490
|
+
avstantso.Func.determineType(MyClass); // 'class'
|
|
491
|
+
|
|
492
|
+
async function asyncFn() {}
|
|
493
|
+
avstantso.Func.determineType(asyncFn); // 'async'
|
|
494
|
+
|
|
495
|
+
function* genFn() {}
|
|
496
|
+
avstantso.Func.determineType(genFn); // 'generator'
|
|
497
|
+
|
|
498
|
+
async function* asyncGenFn() {}
|
|
499
|
+
avstantso.Func.determineType(asyncGenFn); // 'generator'
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
##### `avstantso.Func.Types`
|
|
503
|
+
|
|
504
|
+
A `Key2Key` record (each key mapped to itself) listing all runtime function type names recognized by `determineType`: `plain`, `async`, `generator`, `class`, `arrow`, `bound`.
|
|
505
|
+
|
|
506
|
+
**Example:**
|
|
507
|
+
```typescript
|
|
508
|
+
avstantso.Func.Types.plain; // 'plain'
|
|
509
|
+
avstantso.Func.Types.async; // 'async'
|
|
510
|
+
```
|
|
511
|
+
|
|
389
512
|
**Example - Type Guard Function Factory:**
|
|
390
513
|
```typescript
|
|
391
514
|
const Types = ['insert', 'update', 'delete'] as const;
|
|
@@ -123,7 +123,8 @@ declare namespace AVStantso {
|
|
|
123
123
|
*/
|
|
124
124
|
freeze?(): void;
|
|
125
125
|
/**
|
|
126
|
-
* @summary `symbol` for custom freeze
|
|
126
|
+
* @summary `symbol` for custom freeze.\
|
|
127
|
+
* Same symbol of {@link Code.DeepOps.Freeze.symbol}
|
|
127
128
|
* @see freeze
|
|
128
129
|
*/
|
|
129
130
|
symbolFreeze: symbol;
|