@vicin/sigil 1.0.1 → 1.1.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/README.md +157 -37
- package/dist/index.d.mts +777 -0
- package/dist/index.d.ts +777 -3
- package/dist/index.js +701 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +684 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -4
- package/dist/core/classes.d.ts +0 -48
- package/dist/core/classes.d.ts.map +0 -1
- package/dist/core/classes.js +0 -18
- package/dist/core/classes.js.map +0 -1
- package/dist/core/decorator.d.ts +0 -28
- package/dist/core/decorator.d.ts.map +0 -1
- package/dist/core/decorator.js +0 -48
- package/dist/core/decorator.js.map +0 -1
- package/dist/core/enhancers.d.ts +0 -58
- package/dist/core/enhancers.d.ts.map +0 -1
- package/dist/core/enhancers.js +0 -101
- package/dist/core/enhancers.js.map +0 -1
- package/dist/core/helpers.d.ts +0 -192
- package/dist/core/helpers.d.ts.map +0 -1
- package/dist/core/helpers.js +0 -349
- package/dist/core/helpers.js.map +0 -1
- package/dist/core/index.d.ts +0 -9
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -8
- package/dist/core/index.js.map +0 -1
- package/dist/core/mixin.d.ts +0 -115
- package/dist/core/mixin.d.ts.map +0 -1
- package/dist/core/mixin.js +0 -209
- package/dist/core/mixin.js.map +0 -1
- package/dist/core/options.d.ts +0 -74
- package/dist/core/options.d.ts.map +0 -1
- package/dist/core/options.js +0 -39
- package/dist/core/options.js.map +0 -1
- package/dist/core/registry.d.ts +0 -104
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js +0 -174
- package/dist/core/registry.js.map +0 -1
- package/dist/core/symbols.d.ts +0 -96
- package/dist/core/symbols.d.ts.map +0 -1
- package/dist/core/symbols.js +0 -96
- package/dist/core/symbols.js.map +0 -1
- package/dist/core/types.d.ts +0 -169
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -2
- package/dist/core/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Sigil
|
|
2
2
|
|
|
3
|
-
`Sigil` is a lightweight TypeScript library for creating **nominal identity classes** with compile-time branding and reliable runtime type checks. It
|
|
3
|
+
`Sigil` is a lightweight TypeScript library for creating **nominal identity classes** with compile-time branding and reliable runtime type checks. It organizes classes across your code and gives you power of **nominal typing**, **safe class checks across bundles** and **centralized registry** where reference to every class constructor is stored and enforced to have it's own unique label and symbol.
|
|
4
4
|
|
|
5
5
|
> **Key ideas:**
|
|
6
6
|
>
|
|
7
7
|
> - **Compile-time nominal typing** via type brands so two structurally-identical types can remain distinct.
|
|
8
8
|
> - **Reliable runtime guards** using `Symbol.for(...)` and lineage sets instead of `instanceof`.
|
|
9
9
|
> - **Inheritance-aware identity**: lineages and sets let you test for subtype/supertype relationships.
|
|
10
|
-
> - **Centralized class registry**: every class have
|
|
10
|
+
> - **Centralized class registry**: every class have its own unique label and symbol that can be used as an id throughout the codebase.
|
|
11
11
|
|
|
12
12
|
**Note: You should read these parts before implementing `Sigil` in you code:**
|
|
13
13
|
|
|
14
|
-
- **Security note:** `Sigil` stores constructor references in the global registry. While it doesn't expose private instance data, it does mean any module can get constructor of the class.
|
|
14
|
+
- **Security note:** By default, `Sigil` stores constructor references in the global registry. While it doesn't expose private instance data, it does mean any module can get constructor of the class. if you have sensitive classes that you want to be unaccessable outside it's module update global or per class options (e.g. `updateOptions({ storeConstructor: false })` or `@WithSigil("label", { storeConstructor: false })`). read more [Registery](#registry).
|
|
15
15
|
|
|
16
16
|
- **Performance note:** `Sigil` attaches couple methods to every sigilized class instance, this is negligible in almost all cases, also `.isOfType()` although being reliable and performance optimized but it still less performant that native `instanceof` checks, so if you want maximum performance in cases like hot-path code it is not advised to use `Sigil` as it's built for consistency and maintainability mainly at the cost of minimal performance overhead.
|
|
17
17
|
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
- [Registry](#registry)
|
|
35
35
|
- [Troubleshooting & FAQ](#troubleshooting--faq)
|
|
36
36
|
- [Best practices](#best-practices)
|
|
37
|
+
- [Deprecated API](#deprecated-api)
|
|
37
38
|
- [Phantom](#phantom)
|
|
38
39
|
- [Contributing](#contributing)
|
|
39
40
|
- [License](#license)
|
|
@@ -168,7 +169,7 @@ But there is more to add to your system, which will be discussed in the [Core co
|
|
|
168
169
|
|
|
169
170
|
---
|
|
170
171
|
|
|
171
|
-
###
|
|
172
|
+
### Why `Sigil` exists
|
|
172
173
|
|
|
173
174
|
`Sigil` was born out of real-world friction in a large **monorepo** built with **Domain-Driven Design (DDD)**.
|
|
174
175
|
|
|
@@ -296,7 +297,7 @@ The typed approach requires redefinition of public class, so you have:
|
|
|
296
297
|
|
|
297
298
|
This separation is necessary as typescript decorators doesn't affect type system. so to reflect type update the class should be passed to HOF.
|
|
298
299
|
|
|
299
|
-
Example of
|
|
300
|
+
Example of approach for class chain:
|
|
300
301
|
|
|
301
302
|
```ts
|
|
302
303
|
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
@@ -408,7 +409,7 @@ export type X<G> = GetInstance<typeof X<G>>; // <-- Generics re-defined here, ju
|
|
|
408
409
|
### Anonymous classes
|
|
409
410
|
|
|
410
411
|
You may see error: `Property 'x' of exported anonymous class type may not be private or protected.`, although this is rare to occur.
|
|
411
|
-
This comes from the fact that all typed classes are `anonymous class` as they are return of HOF and ts compiler struggle to type them safely. to avoid these error
|
|
412
|
+
This comes from the fact that all typed classes are `anonymous class` as they are return of HOF and ts compiler struggle to type them safely. to avoid these error entirely all you need is exporting the untyped classes even if they are un-used as a good convention.
|
|
412
413
|
|
|
413
414
|
```ts
|
|
414
415
|
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
@@ -442,8 +443,12 @@ export {
|
|
|
442
443
|
isSigilInstance,
|
|
443
444
|
} from './helpers';
|
|
444
445
|
export { Sigilify } from './mixin';
|
|
445
|
-
export {
|
|
446
|
-
|
|
446
|
+
export {
|
|
447
|
+
updateOptions,
|
|
448
|
+
SigilRegistry,
|
|
449
|
+
getActiveRegistry,
|
|
450
|
+
DEFAULT_LABEL_REGEX,
|
|
451
|
+
} from './options';
|
|
447
452
|
export type {
|
|
448
453
|
ISigil,
|
|
449
454
|
ISigilInstance,
|
|
@@ -451,6 +456,7 @@ export type {
|
|
|
451
456
|
TypedSigil,
|
|
452
457
|
GetInstance,
|
|
453
458
|
SigilBrandOf,
|
|
459
|
+
SigilOptions,
|
|
454
460
|
} from './types';
|
|
455
461
|
```
|
|
456
462
|
|
|
@@ -465,8 +471,9 @@ export type {
|
|
|
465
471
|
- `typed(Class, label?, parent?)`: type-only narrowing helper (no runtime mutation) — asserts runtime label in DEV.
|
|
466
472
|
- `isSigilCtor(value)`: `true` if `value` is a sigil constructor.
|
|
467
473
|
- `isSigilInstance(value)`: `true` if `value` is an instance of a sigil constructor.
|
|
468
|
-
- `
|
|
469
|
-
- `
|
|
474
|
+
- `SigilRegistry`: `Sigil` Registy class used to centralize classes across app.
|
|
475
|
+
- `getActiveRegistry`: Getter of active registry being used by `Sigil`.
|
|
476
|
+
- `updateOptions(opts, mergeRegistries?)`: change global runtime options before sigil decoration (e.g., `autofillLabels`, `devMarker`, etc.).
|
|
470
477
|
- `DEFAULT_LABEL_REGEX`: regex that insures structure of `@scope/package.ClassName` to all labels, it's advised to use it as your `SigilOptions.labelValidation`
|
|
471
478
|
|
|
472
479
|
### Instance & static helpers provided by Sigilified constructors
|
|
@@ -492,25 +499,34 @@ Instances of sigilified classes expose instance helpers:
|
|
|
492
499
|
|
|
493
500
|
## Options & configuration
|
|
494
501
|
|
|
495
|
-
Sigil exposes a small set of runtime options that control DEV behavior. These can be modified at app startup via `updateOptions(...)
|
|
502
|
+
Sigil exposes a small set of runtime options that control registry and DEV behavior. These can be modified at app startup via `updateOptions(...)` to set global options:
|
|
496
503
|
|
|
497
504
|
```ts
|
|
498
|
-
import { updateOptions } from '@vicin/sigil';
|
|
505
|
+
import { updateOptions, SigilRegistry } from '@vicin/sigil';
|
|
506
|
+
|
|
507
|
+
// Values defined in this example are defaults:
|
|
499
508
|
|
|
500
509
|
updateOptions({
|
|
501
510
|
autofillLabels: false, // auto-generate labels for subclasses that would otherwise inherit
|
|
502
511
|
skipLabelInheritanceCheck: false, // skip DEV-only inheritance checks -- ALMOST NEVER WANT TO SET THIS TO TRUE, Use 'autofillLabels: true' instead. --
|
|
503
512
|
labelValidation: null, // or a RegExp / function to validate labels
|
|
504
513
|
devMarker: process.env.NODE_ENV !== 'production', // boolean used to block dev only checks in non-dev enviroments
|
|
514
|
+
registry: new SigilRegistry(), // setting active registry used by 'Sigil'
|
|
515
|
+
useGlobalRegistry: true, // append registry into 'globalThis' to insure single source in the runtime in cross bundles.
|
|
516
|
+
storeConstructor: true, // store reference of the constructor in registry
|
|
505
517
|
});
|
|
506
518
|
```
|
|
507
519
|
|
|
520
|
+
Global options can be overridden per class by `opts` field in decorator and HOF.
|
|
521
|
+
|
|
508
522
|
**Notes**:
|
|
509
523
|
|
|
510
|
-
- It's advised to use `updateOptions({ labelValidation: DEFAULT_LABEL_REGEX })` at app entry to validate labels against `@scope/package.ClassName` structure.
|
|
524
|
+
- It's advised to use `updateOptions({ labelValidation: DEFAULT_LABEL_REGEX })` at app entry point to validate labels against `@scope/package.ClassName` structure.
|
|
511
525
|
- `devMarker` drives DEV-only checks — when `false`, many runtime validations are no-ops (useful for production builds).
|
|
512
526
|
- `autofillLabels` is useful for some HMR/test setups where you prefer not to throw on collisions and want autogenerated labels.
|
|
513
527
|
- `skipLabelInheritanceCheck = true` can result on subtle bugs if enabled, so avoid setting it to true.
|
|
528
|
+
- When `SigilOptions.registry` is updated, old registry entries is merged and registered into new registry, to disable this behavrio pass `false` to `mergeRegistries` (`updateOptions({ registry: newRegistry }, false)`)
|
|
529
|
+
- `useGlobalRegistry` makes Sigil registry a central manager of classes and reliable way to enforce single label usage, so avoid setting it to `false` except if you have a strong reason. if you want to avoid making class constructor accessible via `globalThis` use `storeConstructor = true` instead.
|
|
514
530
|
|
|
515
531
|
---
|
|
516
532
|
|
|
@@ -518,28 +534,82 @@ updateOptions({
|
|
|
518
534
|
|
|
519
535
|
`Sigil` with default options forces devs to `SigilLabel` every class defined, that allows central class registery that store a reference for every class keyed by its label, also it prevent two classes in the codebase from having the same `SigilLabel`.
|
|
520
536
|
|
|
521
|
-
This is mainly useful in large codebases or frameworks where they need central registry or if you need class transport across API, workers, etc... where you can use `SigilLabel` reliably to serialize class identity. to interact with registry `Sigil` exposes `
|
|
537
|
+
This is mainly useful in large codebases or frameworks where they need central registry or if you need class transport across API, workers, etc... where you can use `SigilLabel` reliably to serialize class identity. to interact with registry `Sigil` exposes `getActiveRegistry` and `SigilRegistry` class. also you can update registry related options with `updateOptions`.
|
|
522
538
|
|
|
523
|
-
|
|
539
|
+
By default, registry is stored in `globalThis` under `Symbol.for(__SIGIL_REGISTRY__)` so one instance is used across runtime even with multiple bundles, but this also exposes that map anywhere in the code, see [globalThis and security](#globalthis-and-security).
|
|
524
540
|
|
|
525
|
-
|
|
541
|
+
### Get registry
|
|
526
542
|
|
|
527
|
-
|
|
543
|
+
You can interact with registry using `getActiveRegistry`, this function returns registry currently in use:
|
|
528
544
|
|
|
529
|
-
|
|
545
|
+
```ts
|
|
546
|
+
import { getActiveRegistry } from '@vicin/sigil';
|
|
547
|
+
const registry = getActiveRegistry();
|
|
548
|
+
if (registry) console.log(registry.listLabels()); // check for presence as it can be 'null' if 'updateOptions({ registry: null })' is used
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Replace registry
|
|
530
552
|
|
|
531
|
-
|
|
553
|
+
In most cases you don't need to replace registry, but if you wanted to define a `Map` and make `Sigil` use it aa a register (e.g. define custom side effects) you can use `SigilRegistry`:
|
|
554
|
+
|
|
555
|
+
```ts
|
|
556
|
+
import { SigilRegistry, updateOptions } from '@vicin/sigil';
|
|
532
557
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
558
|
+
const myMap = new Map();
|
|
559
|
+
const myRegistry = new SigilRegistry(myMap);
|
|
560
|
+
updateOptions({ registry: myRegistry });
|
|
536
561
|
|
|
537
|
-
|
|
562
|
+
// Now 'Sigil' register new labels and constructors to 'myMap'.
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
By default `Sigil` will merge old registry map into `myMap`, to prevent this behavior:
|
|
566
|
+
|
|
567
|
+
```ts
|
|
568
|
+
updateOptions({ registry: myRegistry }, false); // <-- add false here
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Also you can set registry to `null`, but this is not advised as it disable all registry operations entirely:
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
import { updateOptions } from '@vicin/sigil';
|
|
575
|
+
updateOptions({ registry: null }); // No label checks and registry map is freed from memory
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### globalThis and security
|
|
579
|
+
|
|
580
|
+
By default registry is stored in `globalThis`. to disable this behavior you can:
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
import { updateOptions } from '@vicin/sigil';
|
|
584
|
+
updateOptions({ useGlobalRegistry: false });
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
Before applying this change, for registry to function normally you should insure that `Sigil` is not bundles twice in your app.
|
|
588
|
+
however if you can't insure that only bundle of `Sigil` is used and don't want class constructors to be accessible globally do this:
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
import { updateOptions } from '@vicin/sigil';
|
|
592
|
+
updateOptions({ storeConstructor: false });
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Now registry only stores label of this classes and all class constructors are in the map are replaced with `null`.
|
|
596
|
+
If you need even more control and like the global registry for classes but want to obscure only some of your classes you can pass this option per class and keep global options as is:
|
|
597
|
+
|
|
598
|
+
```ts
|
|
599
|
+
import { withSigil, Sigil } from '@vicin/sigil';
|
|
600
|
+
|
|
601
|
+
class _X extends Sigil {}
|
|
602
|
+
const X = withSigil(_X, 'X', { storeConstructor: false });
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Pick whatever pattern you like!
|
|
606
|
+
|
|
607
|
+
### Class typing in registry
|
|
538
608
|
|
|
539
609
|
Unfortunately concrete types of classes is not supported and all classes are stored as `ISigil` type. if you want concrete typings you can wrap registery:
|
|
540
610
|
|
|
541
611
|
```ts
|
|
542
|
-
import {
|
|
612
|
+
import { getActiveRegistry } from '@vicin/sigil';
|
|
543
613
|
import { MySigilClass1 } from './file1';
|
|
544
614
|
import { MySigilClass2 } from './file2';
|
|
545
615
|
|
|
@@ -550,25 +620,25 @@ interface MyClasses {
|
|
|
550
620
|
|
|
551
621
|
class MySigilRegistry {
|
|
552
622
|
listLabels(): (keyof MyClasses)[] {
|
|
553
|
-
return
|
|
623
|
+
return getActiveRegistry()?.listLabels();
|
|
554
624
|
}
|
|
555
625
|
has(label: string): boolean {
|
|
556
|
-
return
|
|
626
|
+
return getActiveRegistry()?.has(label);
|
|
557
627
|
}
|
|
558
628
|
get<L extends keyof MyClasses>(label: L): MyClasses[L] {
|
|
559
|
-
return
|
|
629
|
+
return getActiveRegistry()?.get(label) as any;
|
|
560
630
|
}
|
|
561
631
|
unregister(label: string): boolean {
|
|
562
|
-
return
|
|
632
|
+
return getActiveRegistry()?.unregister(label);
|
|
563
633
|
}
|
|
564
634
|
clear(): void {
|
|
565
|
-
|
|
635
|
+
getActiveRegistry()?.clear();
|
|
566
636
|
}
|
|
567
637
|
replaceRegistry(newRegistry: Map<string, ISigil> | null): void {
|
|
568
|
-
|
|
638
|
+
getActiveRegistry()?.replaceRegistry(newRegistry);
|
|
569
639
|
}
|
|
570
640
|
get size(): number {
|
|
571
|
-
return
|
|
641
|
+
return getActiveRegistry()?.size;
|
|
572
642
|
}
|
|
573
643
|
}
|
|
574
644
|
|
|
@@ -577,10 +647,30 @@ export const MY_SIGIL_REGISTRY = new MySigilRegistry();
|
|
|
577
647
|
|
|
578
648
|
Now you have fully typed central class registry!
|
|
579
649
|
|
|
580
|
-
###
|
|
650
|
+
### I don't care about nominal types or central registry, i just want a runtime replacement of 'instanceof'
|
|
651
|
+
|
|
652
|
+
You can run this at the start of your app:
|
|
653
|
+
|
|
654
|
+
```ts
|
|
655
|
+
import { updateOptions } from '@vicin/sigil';
|
|
656
|
+
updateOptions({ autofillLabels: true, storeConstructor: false });
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
now you can omit all `HOF`, `Decorators` and make `Sigil` work in the background:
|
|
660
|
+
|
|
661
|
+
```ts
|
|
662
|
+
import { Sigil } from '@vicin/sigil';
|
|
663
|
+
|
|
664
|
+
class X extends Sigil {}
|
|
665
|
+
class Y extends X {}
|
|
666
|
+
class Z extends Y {}
|
|
667
|
+
|
|
668
|
+
Z.isOfType(new Y()); // true
|
|
669
|
+
Z.isOfType(new X()); // true
|
|
670
|
+
Y.isOfType(new Y()); // false
|
|
671
|
+
```
|
|
581
672
|
|
|
582
|
-
|
|
583
|
-
- In development with HMR, duplicate class definitions are common; Sigil prints friendly warnings in DEV rather than throwing to keep the feedback loop fast.
|
|
673
|
+
No class constructors are stored globally and no code overhead, moreover if you can insure that `Sigil` is not bundles twice you can disable `useGlobalRegistry` and no trace of sigil in `globalThis`.
|
|
584
674
|
|
|
585
675
|
---
|
|
586
676
|
|
|
@@ -596,7 +686,7 @@ A: Use `@WithSigil("@your/label")`, or wrap the subclass with `withSigil` / `wit
|
|
|
596
686
|
|
|
597
687
|
**Q: I got this error: 'Property 'x' of exported anonymous class type may not be private or protected.', How to fix it?**
|
|
598
688
|
|
|
599
|
-
A: This error
|
|
689
|
+
A: This error comes from the fact that all typed classes (return from `withSigil`, `withSigilTyped` or `typed`) are 'anonymous class' as they are the return of HOF. all you need to do is to export untyped classes (`_Class`) that have private or protected properties. or even export all untyped classes as a good convention even if they are not used.
|
|
600
690
|
|
|
601
691
|
**Q: I need nominal types in TypeScript. Which helper do I use?**
|
|
602
692
|
|
|
@@ -604,9 +694,9 @@ A: Use `withSigilTyped` to both attach runtime metadata and apply compile-time b
|
|
|
604
694
|
|
|
605
695
|
**Q: How do I inspect currently registered labels?**
|
|
606
696
|
|
|
607
|
-
A: Use `
|
|
697
|
+
A: Use `getActiveRegistry()?.list()` to get an array of registered labels.
|
|
608
698
|
|
|
609
|
-
**Q: What if i want to omit labeling in some classes while
|
|
699
|
+
**Q: What if i want to omit labeling in some classes while enforce others?**
|
|
610
700
|
|
|
611
701
|
A: You can set `SigilOptions.autofillLabels` to `true`. or if you more strict enviroment you can define empty `@WithSigil()` decorator above classes you don't care about labeling and `Sigil` will generate random label for it, but still throw if you forgot to use a decorator or HOF on a class.
|
|
612
702
|
|
|
@@ -621,6 +711,36 @@ A: You can set `SigilOptions.autofillLabels` to `true`. or if you more strict en
|
|
|
621
711
|
|
|
622
712
|
---
|
|
623
713
|
|
|
714
|
+
## Deprecated API
|
|
715
|
+
|
|
716
|
+
### REGISTRY
|
|
717
|
+
|
|
718
|
+
`Sigil` have moved from static reference registry to dynamic access and updates, now devs can create `SigilRegistry` class and pass it to `SigilOptions` to be 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**.
|
|
719
|
+
|
|
720
|
+
```ts
|
|
721
|
+
import { REGISTRY, getActiveRegistry } from '@vicin/sigil';
|
|
722
|
+
|
|
723
|
+
// from:
|
|
724
|
+
const present = REGISTRY.has('label');
|
|
725
|
+
|
|
726
|
+
// to:
|
|
727
|
+
const present = getActiveRegistry()?.has('label'); // Active registy can be 'null' if 'SigilOptions.registy' is set to null so we used the '?' mark
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
```ts
|
|
731
|
+
import { REGISTRY, updateOptions, SigilRegistry } from '@vicin/sigil';
|
|
732
|
+
|
|
733
|
+
// from:
|
|
734
|
+
const newRegistry = new Map();
|
|
735
|
+
REGISTRY.replaceRegistry(newRegistry);
|
|
736
|
+
|
|
737
|
+
// to
|
|
738
|
+
const newRegistry = new SigilRegistry(); // can pass external map to constructor if needed.
|
|
739
|
+
updateOptions({ registry: newRegistry });
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
624
744
|
## Phantom
|
|
625
745
|
|
|
626
746
|
`Phantom` is another lightweight TypeScript library I created for achieving **nominal typing** on primitives and objects through type-only metadata. It solves the problem of structural typing in TypeScript allowing accidental misuse of identical shapes (e.g., confusing `UserId` and `PostId` as both strings) by enabling compile-time distinctions with features like **brands**, **constrained identities**, **variants for states**, **additive traits**, and **reversible transformations**. This makes it ideal for domain-driven design (DDD) without runtime overhead.
|