@vicin/sigil 1.3.0 → 2.0.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 +6 -0
- package/README.md +22 -132
- package/dist/index.d.mts +119 -400
- package/dist/index.d.ts +119 -400
- package/dist/index.global.js +1789 -387
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +77 -391
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -389
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@vicin/sigil) [](https://www.npmjs.com/package/@vicin/sigil) [](LICENSE)  [](https://github.com/ZiadTaha62/sigil/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
-
> - 🎉
|
|
5
|
+
> - 🎉 v2.0.0 is out! Happy coding! 😄💻
|
|
6
6
|
> - 📄 **Changelog:** [CHANGELOG.md](./CHANGELOG.md)
|
|
7
7
|
|
|
8
|
-
`Sigil` is a lightweight TypeScript library for creating nominal identity classes with compile-time branding and reliable runtime type checks. It organizes class identities across your codebase and gives you the power of **nominal typing
|
|
8
|
+
`Sigil` is a lightweight TypeScript library for creating nominal identity classes with compile-time branding and reliable runtime type checks. It organizes class identities across your codebase and gives you the power of **nominal typing** and **safe cross-bundle class checks** where each class constructor is stored under a unique label.
|
|
9
9
|
|
|
10
10
|
> **Key ideas:**
|
|
11
11
|
>
|
|
12
12
|
> - **Nominal Typing at Compile Time:** Distinguishes structurally similar types (e.g., UserId vs. PostId).
|
|
13
13
|
> - **Reliable Runtime Checks:** Uses symbols instead of instanceof for cross-bundle reliability.
|
|
14
14
|
> - **Inheritance Awareness:** Tracks lineages for subtype/supertype checks.
|
|
15
|
-
> - **Central Registry:** Stores class references by unique labels for easy lookup.
|
|
16
15
|
|
|
17
16
|
## Important Notes Before Using
|
|
18
17
|
|
|
19
|
-
- **
|
|
20
|
-
- **Performance:** Minimal overhead, but `.isOfType()` is slower than native `instanceof`. Avoid in ultra-hot paths.
|
|
18
|
+
- **Explicit class identity:** `Sigil` uses passed class label to identify classes, which means that dev is responsible for uniqueness of classes by passing unique labels.
|
|
19
|
+
- **Performance:** Minimal overhead, but `.isOfType()` is slightly slower than native `instanceof`. Avoid in ultra-hot paths.
|
|
21
20
|
- **Private Constructors:** HOF pattern allows extending private constructors in types (TypeScript limitation).
|
|
22
|
-
- **Simple instanceof Fix:** If you just need runtime checks without extras, see the [minimal mode](#minimal-mode)
|
|
21
|
+
- **Simple instanceof Fix:** If you just need runtime checks without extras, see the [minimal mode](#minimal-mode).
|
|
22
|
+
|
|
23
|
+
## Why Registry is dropped
|
|
24
|
+
|
|
25
|
+
Although registry added label checks and central class management but it also introduced complexity, especially when mutiple packages tried to use it simultaneously, So in v2 we decided to omit it entirely and minimize API surface.
|
|
23
26
|
|
|
24
27
|
---
|
|
25
28
|
|
|
@@ -44,11 +47,8 @@
|
|
|
44
47
|
- [Decorator pattern](#2-decorator-pattern)
|
|
45
48
|
- [API reference](#api-reference)
|
|
46
49
|
- [Options & configuration](#options--configuration)
|
|
47
|
-
- [Registry](#registry)
|
|
48
|
-
- [Security guidance](#security-guidance)
|
|
49
50
|
- [Minimal mode](#minimal-mode)
|
|
50
51
|
- [Troubleshooting & FAQ](#troubleshooting--faq)
|
|
51
|
-
- [Deprecated API](#deprecated-api)
|
|
52
52
|
- [Phantom](#phantom)
|
|
53
53
|
- [Contributing](#contributing)
|
|
54
54
|
- [License](#license)
|
|
@@ -190,9 +190,7 @@ This section states clearly what `Sigil` provides and what it does **not** provi
|
|
|
190
190
|
|
|
191
191
|
**2. Reliable runtime identity (when used as intended).**
|
|
192
192
|
|
|
193
|
-
**3.
|
|
194
|
-
|
|
195
|
-
**4. Nominal typing that is inheritance-aware**
|
|
193
|
+
**3. Nominal typing that is inheritance-aware**
|
|
196
194
|
|
|
197
195
|
### What Sigil does not guarantee
|
|
198
196
|
|
|
@@ -211,7 +209,6 @@ This section states clearly what `Sigil` provides and what it does **not** provi
|
|
|
211
209
|
- **Type lineage**: Array of symbols for ancestry.
|
|
212
210
|
- **Type set**: Set of symbols for fast checks.
|
|
213
211
|
- **Brand**: TypeScript marker (`__SIGIL_BRAND__`) for nominal types.
|
|
214
|
-
- **Registry**: A global Map of registered `Sigil` classes keyed by their labels.
|
|
215
212
|
|
|
216
213
|
---
|
|
217
214
|
|
|
@@ -222,7 +219,7 @@ Sigil addresses issues in large monorepos and Domain-Driven Design (DDD):
|
|
|
222
219
|
- **Unreliable `instanceof`:** Bundling and HMR cause class redefinitions, breaking checks.
|
|
223
220
|
- **Manual Branding Overhead:** Custom identifiers lead to boilerplate and maintenance issues.
|
|
224
221
|
|
|
225
|
-
`Sigil` abstracts these into a **centralized system**, making identity management **explicit** and **error-resistant
|
|
222
|
+
`Sigil` abstracts these into a **centralized system**, making identity management **explicit** and **error-resistant** if defined the right way.
|
|
226
223
|
|
|
227
224
|
### Implementation Mechanics
|
|
228
225
|
|
|
@@ -386,11 +383,10 @@ class X extends Sigil {
|
|
|
386
383
|
- `isDecorated(ctor)`
|
|
387
384
|
- `isInheritanceChecked(ctor)`
|
|
388
385
|
|
|
389
|
-
- **Options
|
|
390
|
-
- `updateOptions(opts
|
|
391
|
-
- `SigilRegistry`
|
|
392
|
-
- `getActiveRegistry`
|
|
386
|
+
- **Options:**
|
|
387
|
+
- `updateOptions(opts)`
|
|
393
388
|
- `DEFAULT_LABEL_REGEX`
|
|
389
|
+
|
|
394
390
|
- **Types:**
|
|
395
391
|
- `ISigil<Label, ParentSigil?>`
|
|
396
392
|
- `ISigilStatic<Label, ParentSigil?>`
|
|
@@ -411,9 +407,7 @@ class X extends Sigil {
|
|
|
411
407
|
- `withSigilTyped(Class, label?, opts?)`: like `withSigil` but narrows the TypeScript type to include brands.
|
|
412
408
|
- `isSigilCtor(value)`: `true` if `value` is a `Sigil` constructor.
|
|
413
409
|
- `isSigilInstance(value)`: `true` if `value` is an instance of a `Sigil` constructor.
|
|
414
|
-
- `
|
|
415
|
-
- `getActiveRegistry`: Getter of active registry being used by `Sigil`.
|
|
416
|
-
- `updateOptions(opts, mergeRegistries?)`: change global runtime options before `Sigil` decoration (e.g., `autofillLabels`, `devMarker`, etc.).
|
|
410
|
+
- `updateOptions(opts)`: change global runtime options before `Sigil` decoration (e.g., `autofillLabels`, `devMarker`, etc.).
|
|
417
411
|
- `DEFAULT_LABEL_REGEX`: regex that ensures structure of `@scope/package.ClassName` to all labels, it's advised to use it as your `SigilOptions.labelValidation`
|
|
418
412
|
|
|
419
413
|
### Instance & static helpers provided by Sigilified constructors
|
|
@@ -421,11 +415,10 @@ class X extends Sigil {
|
|
|
421
415
|
When a constructor is decorated/sigilified it will expose the following **static** getters/methods:
|
|
422
416
|
|
|
423
417
|
- `SigilLabel` — the human label string.
|
|
424
|
-
- `
|
|
425
|
-
- `
|
|
426
|
-
- `SigilTypeSet` — readonly `Set<symbol>` for O(1) checks.
|
|
418
|
+
- `SigilLabelLineage` — readonly array of labels representing parent → child.
|
|
419
|
+
- `SigilLabelSet` — readonly `Set<string>` for O(1) checks.
|
|
427
420
|
- `isSigilified(obj)` — runtime predicate that delegates to `isSigilInstance`.
|
|
428
|
-
- `isOfType(other)` — O(1) membership test using `other`'s `
|
|
421
|
+
- `isOfType(other)` — O(1) membership test using `other`'s `__LABEL_SET__`.
|
|
429
422
|
- `isOfTypeStrict(other)` — strict lineage comparison element-by-element.
|
|
430
423
|
|
|
431
424
|
Instances of sigilified classes expose instance helpers:
|
|
@@ -434,7 +427,7 @@ Instances of sigilified classes expose instance helpers:
|
|
|
434
427
|
- `getSigilType()` — runtime symbol.
|
|
435
428
|
- `getSigilTypeLineage()` — returns lineage array.
|
|
436
429
|
- `getSigilTypeSet()` — returns readonly Set.
|
|
437
|
-
- `isOfType(other)` — O(1) membership test using `other`'s `
|
|
430
|
+
- `isOfType(other)` — O(1) membership test using `other`'s `__LABEL_SET__`.
|
|
438
431
|
- `isOfTypeStrict(other)` — strict lineage comparison element-by-element.
|
|
439
432
|
|
|
440
433
|
---
|
|
@@ -444,16 +437,13 @@ Instances of sigilified classes expose instance helpers:
|
|
|
444
437
|
Customize behavior globally at startup:
|
|
445
438
|
|
|
446
439
|
```ts
|
|
447
|
-
import { updateOptions
|
|
440
|
+
import { updateOptions } from '@vicin/sigil';
|
|
448
441
|
|
|
449
442
|
updateOptions({
|
|
450
443
|
autofillLabels: false, // Automatically label unlabeled subclasses
|
|
451
444
|
skipLabelInheritanceCheck: false, // Bypass dev inheritance checks -- ALMOST NEVER WANT TO SET THIS TO TRUE, Use 'autofillLabels: true' instead.
|
|
452
445
|
labelValidation: null, // Function or regex, Enforce label format
|
|
453
446
|
devMarker: process.env.NODE_ENV !== 'production', // Toggle dev safeguards
|
|
454
|
-
registry: new SigilRegistry(), // Custom registry instance
|
|
455
|
-
useGlobalRegistry: true, // Store in 'globalThis' for cross-bundle access
|
|
456
|
-
storeConstructor: true, // Include constructors in registry
|
|
457
447
|
});
|
|
458
448
|
```
|
|
459
449
|
|
|
@@ -461,81 +451,15 @@ Values defined in previous example are defaults, per-class overrides available i
|
|
|
461
451
|
|
|
462
452
|
---
|
|
463
453
|
|
|
464
|
-
## Registry
|
|
465
|
-
|
|
466
|
-
The registry ensures **unique labels** and supports central class management for ops as serialization.
|
|
467
|
-
|
|
468
|
-
- **Access:** `const registry = getActiveRegistry();` – Returns current `SigilRegistry` or `null`.
|
|
469
|
-
- **Operations:** `has(label)`, `get(label)`, `listLabels()`, `register(label, ctor, opts?)`, `unregister(label)`, `clear()`, `size`.
|
|
470
|
-
- **Replacement:** `updateOptions({ registry: new SigilRegistry(myMap) }, merge?);` – Optionally merge existing entries.
|
|
471
|
-
- **Disable:** Set `registry: null` to skip all registry functions.
|
|
472
|
-
- **Global Storage:** Defaults to `globalThis[Symbol.for('__SIGIL_REGISTRY__')];` disable with `useGlobalRegistry: false` if single-bundle guaranteed.
|
|
473
|
-
- **Constructor Privacy:** Set `storeConstructor: false` globally or per-class to replace constructors with null in the map.
|
|
474
|
-
|
|
475
|
-
### Class typing in registry
|
|
476
|
-
|
|
477
|
-
Unfortunately concrete types of classes is not supported and all classes are stored as `ISigil` type. if you want concrete typing, you can wrap registry:
|
|
478
|
-
|
|
479
|
-
```ts
|
|
480
|
-
import { getActiveRegistry } from '@vicin/sigil';
|
|
481
|
-
import type { MySigilClass1 } from './file1';
|
|
482
|
-
import type { MySigilClass2 } from './file2';
|
|
483
|
-
|
|
484
|
-
interface MyClasses {
|
|
485
|
-
MySigilClass1: typeof MySigilClass1;
|
|
486
|
-
MySigilClass2: typeof MySigilClass2;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
export class MySigilRegistry {
|
|
490
|
-
listLabels(): (keyof MyClasses)[] {
|
|
491
|
-
return getActiveRegistry()?.listLabels();
|
|
492
|
-
}
|
|
493
|
-
has(label: string): boolean {
|
|
494
|
-
return getActiveRegistry()?.has(label);
|
|
495
|
-
}
|
|
496
|
-
get<L extends keyof MyClasses>(label: L): MyClasses[L] {
|
|
497
|
-
return getActiveRegistry()?.get(label) as any;
|
|
498
|
-
}
|
|
499
|
-
unregister(label: string): boolean {
|
|
500
|
-
return getActiveRegistry()?.unregister(label);
|
|
501
|
-
}
|
|
502
|
-
clear(): void {
|
|
503
|
-
getActiveRegistry()?.clear();
|
|
504
|
-
}
|
|
505
|
-
replaceRegistry(newRegistry: Map<string, ISigil> | null): void {
|
|
506
|
-
getActiveRegistry()?.replaceRegistry(newRegistry);
|
|
507
|
-
}
|
|
508
|
-
get size(): number {
|
|
509
|
-
return getActiveRegistry()?.size;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
Now you have fully typed central class registry!
|
|
515
|
-
|
|
516
|
-
---
|
|
517
|
-
|
|
518
|
-
## Security guidance
|
|
519
|
-
|
|
520
|
-
- **Recommended for Untrusted Environments:** `updateOptions({ storeConstructor: false });` – Prevents constructors from being stored in the registry map (labels remain, but constructors are `null`).
|
|
521
|
-
|
|
522
|
-
- **Trusted Environments:** Enable full registry for centralization (default behavior).
|
|
523
|
-
|
|
524
|
-
- **Per-Class Control:** Use `{ storeConstructor: false }` for sensitive classes in decorator or HOF function.
|
|
525
|
-
|
|
526
|
-
Always remember, Registry is metadata-only; avoid for sensitive data. Global access possible if enabled.
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
454
|
## Minimal mode
|
|
531
455
|
|
|
532
|
-
`updateOptions({ autofillLabels: true
|
|
456
|
+
`updateOptions({ autofillLabels: true });` – Enables background operation without explicit labels:
|
|
533
457
|
|
|
534
458
|
```ts
|
|
535
459
|
import { Sigil, updateOptions } from '@vicin/sigil';
|
|
536
460
|
|
|
537
461
|
// run at the start of the app
|
|
538
|
-
updateOptions({ autofillLabels: true
|
|
462
|
+
updateOptions({ autofillLabels: true });
|
|
539
463
|
|
|
540
464
|
// No decorators or HOF needed to use 'isOfType' ('instanceof' replacement)
|
|
541
465
|
class A extends Sigil {}
|
|
@@ -550,40 +474,6 @@ class C extends B {}
|
|
|
550
474
|
- **Dev Extension Errors:** Add labels or enable autofillLabels.
|
|
551
475
|
- **Anonymous Class Errors:** Export untyped bases.
|
|
552
476
|
- **Selective Labeling:** Use `autofillLabels: true` or empty `@WithSigil()` for auto-generation.
|
|
553
|
-
- **Registry Inspection:** `getActiveRegistry()?.listLabels()`.
|
|
554
|
-
|
|
555
|
-
---
|
|
556
|
-
|
|
557
|
-
## Deprecated API
|
|
558
|
-
|
|
559
|
-
### REGISTRY
|
|
560
|
-
|
|
561
|
-
`Sigil` has moved from static reference registry to dynamic access and updates, now devs can create `SigilRegistry` class and pass it to `SigilOptions` to be used by the library internals. however change is done gracefully and `REGISTRY` is still supported with no change in behavior but it's **marked with `deprecated` and will be removed in v2.0.0**.
|
|
562
|
-
|
|
563
|
-
```ts
|
|
564
|
-
import { REGISTRY, getActiveRegistry } from '@vicin/sigil';
|
|
565
|
-
|
|
566
|
-
// from:
|
|
567
|
-
const present = REGISTRY.has('label');
|
|
568
|
-
// to:
|
|
569
|
-
const registry = getActiveRegistry();
|
|
570
|
-
const present = registry ? registry.has('label') : false;
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
```ts
|
|
574
|
-
import { REGISTRY, updateOptions, SigilRegistry } from '@vicin/sigil';
|
|
575
|
-
|
|
576
|
-
// from:
|
|
577
|
-
const newRegistry = new Map();
|
|
578
|
-
REGISTRY.replaceRegistry(newRegistry);
|
|
579
|
-
// to
|
|
580
|
-
const newRegistry = new SigilRegistry(); // can pass external map to constructor, this map will hold all classes
|
|
581
|
-
updateOptions({ registry: newRegistry });
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### typed
|
|
585
|
-
|
|
586
|
-
Obsolete; mixins now handle typing natively. **marked with `deprecated` and will be removed in v2.0.0**
|
|
587
477
|
|
|
588
478
|
---
|
|
589
479
|
|