@vicin/sigil 1.0.0 → 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.
Files changed (54) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +210 -57
  3. package/dist/index.d.mts +777 -0
  4. package/dist/index.d.ts +777 -3
  5. package/dist/index.js +701 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +684 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +12 -4
  10. package/dist/core/classes.d.ts +0 -48
  11. package/dist/core/classes.d.ts.map +0 -1
  12. package/dist/core/classes.js +0 -18
  13. package/dist/core/classes.js.map +0 -1
  14. package/dist/core/decorator.d.ts +0 -28
  15. package/dist/core/decorator.d.ts.map +0 -1
  16. package/dist/core/decorator.js +0 -48
  17. package/dist/core/decorator.js.map +0 -1
  18. package/dist/core/enhancers.d.ts +0 -58
  19. package/dist/core/enhancers.d.ts.map +0 -1
  20. package/dist/core/enhancers.js +0 -101
  21. package/dist/core/enhancers.js.map +0 -1
  22. package/dist/core/helpers.d.ts +0 -192
  23. package/dist/core/helpers.d.ts.map +0 -1
  24. package/dist/core/helpers.js +0 -349
  25. package/dist/core/helpers.js.map +0 -1
  26. package/dist/core/index.d.ts +0 -9
  27. package/dist/core/index.d.ts.map +0 -1
  28. package/dist/core/index.js +0 -8
  29. package/dist/core/index.js.map +0 -1
  30. package/dist/core/mixin.d.ts +0 -115
  31. package/dist/core/mixin.d.ts.map +0 -1
  32. package/dist/core/mixin.js +0 -209
  33. package/dist/core/mixin.js.map +0 -1
  34. package/dist/core/options.d.ts +0 -74
  35. package/dist/core/options.d.ts.map +0 -1
  36. package/dist/core/options.js +0 -39
  37. package/dist/core/options.js.map +0 -1
  38. package/dist/core/registry.d.ts +0 -104
  39. package/dist/core/registry.d.ts.map +0 -1
  40. package/dist/core/registry.js +0 -174
  41. package/dist/core/registry.js.map +0 -1
  42. package/dist/core/symbols.d.ts +0 -96
  43. package/dist/core/symbols.d.ts.map +0 -1
  44. package/dist/core/symbols.js +0 -96
  45. package/dist/core/symbols.js.map +0 -1
  46. package/dist/core/types.d.ts +0 -169
  47. package/dist/core/types.d.ts.map +0 -1
  48. package/dist/core/types.js +0 -2
  49. package/dist/core/types.js.map +0 -1
  50. package/dist/index.d.ts.map +0 -1
  51. package/dist/utils/index.d.ts +0 -2
  52. package/dist/utils/index.d.ts.map +0 -1
  53. package/dist/utils/index.js +0 -2
  54. package/dist/utils/index.js.map +0 -1
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) GitHub, Inc. and contributors
1
+ Copyright (c) 2026 Ziad Taha and contributors
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
package/README.md CHANGED
@@ -1,19 +1,19 @@
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 organize 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.
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 it's own unique label and symbol that can be used as an id throughout the codebase.
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` exposes a global registry of all sigilized classes, this means that all sigilized classes can be accessed anywhere in the code. so **avoid** siglizing security sensitive classes or any class that you want to make it unaccessable outside it's module. read more [Registery](#registry).
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
- - **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 marginally 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.
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
 
18
18
  ---
19
19
 
@@ -34,17 +34,26 @@
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)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
41
+ - [Author](#author)
38
42
 
39
43
  ---
40
44
 
41
45
  ## Features
42
46
 
43
47
  - Attach **stable runtime identity** to classes using `Symbol.for(label)`.
48
+
44
49
  - **Type-level branding** so distinct domain identities are enforced by TypeScript.
50
+
45
51
  - **Lineage tracking** (arrays + sets of symbols) for O(1) and O(n) checks.
52
+
46
53
  - Easy to use: decorator (`@WithSigil`), mixin (`Sigilify`), and HOF (Higher order function) helpers (`withSigil`, `withSigilTyped`, `typed`).
54
+
47
55
  - **Global registry** to centralize classes (query any class by it's label in run-time) and guard against duplicate labels.
56
+
48
57
  - Minimal runtime overhead in production (DEV checks can be toggled off).
49
58
 
50
59
  ---
@@ -54,11 +63,11 @@
54
63
  ### Install
55
64
 
56
65
  ```bash
57
- npm install sigil
66
+ npm install @vicin/sigil
58
67
  # or
59
- yarn add sigil
68
+ yarn add @vicin/sigil
60
69
  # or
61
- pnpm add sigil
70
+ pnpm add @vicin/sigil
62
71
  ```
63
72
 
64
73
  **Requirements**: TypeScript 5.0+ (for stage-3 decorators) and Node.js 18+ recommended.
@@ -68,7 +77,7 @@ pnpm add sigil
68
77
  Use the `Sigil` base class or the `Sigilify` mixin to opt a class into the Sigil runtime contract.
69
78
 
70
79
  ```ts
71
- import { Sigil, Sigilify } from 'sigil';
80
+ import { Sigil, Sigilify } from '@vicin/sigil';
72
81
 
73
82
  // Using the pre-sigilified base class:
74
83
  class User extends Sigil {}
@@ -84,7 +93,7 @@ This adds runtime metadata to the constructor and allows you to use runtime help
84
93
  Apply a label with the `@WithSigil` decorator. This is handy for small classes or when you prefer decorator syntax.
85
94
 
86
95
  ```ts
87
- import { Sigil, WithSigil } from 'sigil';
96
+ import { Sigil, WithSigil } from '@vicin/sigil';
88
97
 
89
98
  @WithSigil('@myorg/mypkg.User')
90
99
  class User extends Sigil {}
@@ -97,7 +106,7 @@ class User extends Sigil {}
97
106
  HOFs work well in many build setups and are idempotent-safe for HMR flows.
98
107
 
99
108
  ```ts
100
- import { Sigil, withSigil } from 'sigil';
109
+ import { Sigil, withSigil } from '@vicin/sigil';
101
110
 
102
111
  class _User extends Sigil {}
103
112
  const User = withSigil(_User, '@myorg/mypkg.User');
@@ -111,7 +120,7 @@ console.log(User.SigilLabel); // "@myorg/mypkg.User"
111
120
  If you want TypeScript to treat identities nominally (so `UserId` !== `PostId` despite identical shape), use the typed helpers.
112
121
 
113
122
  ```ts
114
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
123
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
115
124
 
116
125
  class _User extends Sigil {}
117
126
  const User = withSigilTyped(_User, '@myorg/mypkg.User');
@@ -128,14 +137,14 @@ Migration old code into `Sigil` can be done seamlessly with this set-up:
128
137
  1. Set `SigilOptions.autofillLabels` to `true` at the start of the app so no errors are thrown in the migration stage:
129
138
 
130
139
  ```ts
131
- import { updateOptions } from 'sigil';
140
+ import { updateOptions } from '@vicin/sigil';
132
141
  updateOptions({ autofillLabels: true });
133
142
  ```
134
143
 
135
144
  2. Make you base classes extends `Sigil`:
136
145
 
137
146
  ```ts
138
- import { Sigil } from 'sigil';
147
+ import { Sigil } from '@vicin/sigil';
139
148
 
140
149
  class MyBaseClass {} // original
141
150
 
@@ -160,7 +169,7 @@ But there is more to add to your system, which will be discussed in the [Core co
160
169
 
161
170
  ---
162
171
 
163
- ### Whe `Sigil` exists
172
+ ### Why `Sigil` exists
164
173
 
165
174
  `Sigil` was born out of real-world friction in a large **monorepo** built with **Domain-Driven Design (DDD)**.
166
175
 
@@ -215,7 +224,7 @@ Basic patterns:
215
224
  Mixin / factory:
216
225
 
217
226
  ```ts
218
- import { Sigilify } from 'sigil';
227
+ import { Sigilify } from '@vicin/sigil';
219
228
 
220
229
  const MyClass = Sigilify(class {}, 'MyClass');
221
230
  ```
@@ -223,7 +232,7 @@ const MyClass = Sigilify(class {}, 'MyClass');
223
232
  Direct base-class extend:
224
233
 
225
234
  ```ts
226
- import { Sigil, WithSigil } from 'sigil';
235
+ import { Sigil, WithSigil } from '@vicin/sigil';
227
236
 
228
237
  class MyClass extends Sigil {}
229
238
  ```
@@ -234,7 +243,7 @@ Once you opt into the runtime contract, Sigil enforces consistency: in DEV mode,
234
243
  Decorator style:
235
244
 
236
245
  ```ts
237
- import { Sigil, WithSigil } from 'sigil';
246
+ import { Sigil, WithSigil } from '@vicin/sigil';
238
247
 
239
248
  @WithSigil('MyClass') // <-- Note `@WithSigil` used here cause it extended alreay sigilized class (Sigil). Error is thrown without it.
240
249
  class MyClass extends Sigil {}
@@ -243,7 +252,7 @@ class MyClass extends Sigil {}
243
252
  HOF (preferred for many workflows):
244
253
 
245
254
  ```ts
246
- import { Sigil, withSigil } from 'sigil';
255
+ import { Sigil, withSigil } from '@vicin/sigil';
247
256
 
248
257
  class _MyClass extends Sigil {}
249
258
  const MyClass = withSigil(_MyClass, 'MyClass');
@@ -262,7 +271,7 @@ Runtime metadata alone does not change TypeScript types. To get compile-time nom
262
271
  Example using a typed HOF:
263
272
 
264
273
  ```ts
265
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
274
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
266
275
 
267
276
  // Untyped (runtime) base you extend as normal TS class code:
268
277
  class _User extends Sigil {}
@@ -288,10 +297,10 @@ The typed approach requires redefinition of public class, so you have:
288
297
 
289
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.
290
299
 
291
- Example of appraoch for class chain:
300
+ Example of approach for class chain:
292
301
 
293
302
  ```ts
294
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
303
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
295
304
 
296
305
  // Untyped base classes used for implementation:
297
306
  class _User extends Sigil {}
@@ -334,7 +343,7 @@ The update of Sigil brand types happens via HOF that are defined below actual cl
334
343
  Example:
335
344
 
336
345
  ```ts
337
- import { Sigil, withSigilTyped } from 'sigil';
346
+ import { Sigil, withSigilTyped } from '@vicin/sigil';
338
347
 
339
348
  class _X extends Sigil {
340
349
  // All logic for class
@@ -367,7 +376,7 @@ Earlier example used `InstanceType<>` to get instance of the class. It works wel
367
376
  So alternative in introduced which is `GetInstance`.
368
377
 
369
378
  ```ts
370
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
379
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
371
380
 
372
381
  class _X extends Sigil {}
373
382
 
@@ -386,7 +395,7 @@ One of the downsides of defining typed class at the bottom is that we need to re
386
395
  Example of generic propagation:
387
396
 
388
397
  ```ts
389
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
398
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
390
399
 
391
400
  // Untyped base classes used for implementation:
392
401
  class _X<G> extends Sigil {}
@@ -400,10 +409,10 @@ export type X<G> = GetInstance<typeof X<G>>; // <-- Generics re-defined here, ju
400
409
  ### Anonymous classes
401
410
 
402
411
  You may see error: `Property 'x' of exported anonymous class type may not be private or protected.`, although this is rare to occur.
403
- 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 entirly all you need is exporting the untyped classes even if they are un-used as a good convention.
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.
404
413
 
405
414
  ```ts
406
- import { Sigil, withSigilTyped, GetInstance } from 'sigil';
415
+ import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
407
416
 
408
417
  export class _X extends Sigil {} // <-- Just add 'export' here
409
418
 
@@ -434,8 +443,12 @@ export {
434
443
  isSigilInstance,
435
444
  } from './helpers';
436
445
  export { Sigilify } from './mixin';
437
- export { updateOptions, type SigilOptions } from './options';
438
- export { REGISTRY } from './registry';
446
+ export {
447
+ updateOptions,
448
+ SigilRegistry,
449
+ getActiveRegistry,
450
+ DEFAULT_LABEL_REGEX,
451
+ } from './options';
439
452
  export type {
440
453
  ISigil,
441
454
  ISigilInstance,
@@ -443,6 +456,7 @@ export type {
443
456
  TypedSigil,
444
457
  GetInstance,
445
458
  SigilBrandOf,
459
+ SigilOptions,
446
460
  } from './types';
447
461
  ```
448
462
 
@@ -457,8 +471,9 @@ export type {
457
471
  - `typed(Class, label?, parent?)`: type-only narrowing helper (no runtime mutation) — asserts runtime label in DEV.
458
472
  - `isSigilCtor(value)`: `true` if `value` is a sigil constructor.
459
473
  - `isSigilInstance(value)`: `true` if `value` is an instance of a sigil constructor.
460
- - `REGISTRY`: singleton wrapper around global `Sigil` registry.
461
- - `updateOptions(opts)`: change global runtime options before sigil decoration (e.g., `autofillLabels`, `devMarker`, etc.).
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.).
462
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`
463
478
 
464
479
  ### Instance & static helpers provided by Sigilified constructors
@@ -484,25 +499,34 @@ Instances of sigilified classes expose instance helpers:
484
499
 
485
500
  ## Options & configuration
486
501
 
487
- 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:
488
503
 
489
504
  ```ts
490
- import { updateOptions } from 'sigil';
505
+ import { updateOptions, SigilRegistry } from '@vicin/sigil';
506
+
507
+ // Values defined in this example are defaults:
491
508
 
492
509
  updateOptions({
493
510
  autofillLabels: false, // auto-generate labels for subclasses that would otherwise inherit
494
511
  skipLabelInheritanceCheck: false, // skip DEV-only inheritance checks -- ALMOST NEVER WANT TO SET THIS TO TRUE, Use 'autofillLabels: true' instead. --
495
512
  labelValidation: null, // or a RegExp / function to validate labels
496
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
497
517
  });
498
518
  ```
499
519
 
520
+ Global options can be overridden per class by `opts` field in decorator and HOF.
521
+
500
522
  **Notes**:
501
523
 
502
- - 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.
503
525
  - `devMarker` drives DEV-only checks — when `false`, many runtime validations are no-ops (useful for production builds).
504
526
  - `autofillLabels` is useful for some HMR/test setups where you prefer not to throw on collisions and want autogenerated labels.
505
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.
506
530
 
507
531
  ---
508
532
 
@@ -510,28 +534,82 @@ updateOptions({
510
534
 
511
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`.
512
536
 
513
- 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 `REGISTRY` class instance.
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`.
538
+
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).
540
+
541
+ ### Get registry
514
542
 
515
- Registry is stored in `globalThis` under `Symbol.for(__SIGIL_REGISTRY__)` so there is single source of truth across the runtime, but this also exposes that map anywhere in the code.
543
+ You can interact with registry using `getActiveRegistry`, this function returns registry currently in use:
516
544
 
517
- **Note:**
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
552
+
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';
557
+
558
+ const myMap = new Map();
559
+ const myRegistry = new SigilRegistry(myMap);
560
+ updateOptions({ registry: myRegistry });
518
561
 
519
- - If you intentionally want to disable registry checks (for certain test environments), call `REGISTRY.replaceRegistry(null)` to opt out. although you should not do this in most cases.
562
+ // Now 'Sigil' register new labels and constructors to 'myMap'.
563
+ ```
520
564
 
521
- ### Registry map
565
+ By default `Sigil` will merge old registry map into `myMap`, to prevent this behavior:
522
566
 
523
- `Sigil` registry uses `Map<string, ISigil>` where `string` is `SigilLabel` and `ISigil` is reference to class constructor. If you want to pass your own external `Map` -**although this is not advised except if you have a strong reason**- you can use `REGISTRY.replaceRegistry` and `Sigil` will:
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
+ ```
524
604
 
525
- - 1. Transfere all old classes to the new passed map.
526
- - 2. Attach this new map to `globalThis`.
527
- - 3. Store every new class defined in the passed map.
605
+ Pick whatever pattern you like!
528
606
 
529
- ### Class typing
607
+ ### Class typing in registry
530
608
 
531
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:
532
610
 
533
611
  ```ts
534
- import { REGISTRY } from 'sigil';
612
+ import { getActiveRegistry } from '@vicin/sigil';
535
613
  import { MySigilClass1 } from './file1';
536
614
  import { MySigilClass2 } from './file2';
537
615
 
@@ -542,25 +620,25 @@ interface MyClasses {
542
620
 
543
621
  class MySigilRegistry {
544
622
  listLabels(): (keyof MyClasses)[] {
545
- return REGISTRY.listLabels();
623
+ return getActiveRegistry()?.listLabels();
546
624
  }
547
625
  has(label: string): boolean {
548
- return REGISTRY.has(label);
626
+ return getActiveRegistry()?.has(label);
549
627
  }
550
628
  get<L extends keyof MyClasses>(label: L): MyClasses[L] {
551
- return REGISTRY.get(label) as any;
629
+ return getActiveRegistry()?.get(label) as any;
552
630
  }
553
631
  unregister(label: string): boolean {
554
- return REGISTRY.unregister();
632
+ return getActiveRegistry()?.unregister(label);
555
633
  }
556
634
  clear(): void {
557
- REGISTRY.clear();
635
+ getActiveRegistry()?.clear();
558
636
  }
559
637
  replaceRegistry(newRegistry: Map<string, ISigil> | null): void {
560
- REGISTRY.replaceRegistry(newRegistry);
638
+ getActiveRegistry()?.replaceRegistry(newRegistry);
561
639
  }
562
640
  get size(): number {
563
- return REGISTRY.size;
641
+ return getActiveRegistry()?.size;
564
642
  }
565
643
  }
566
644
 
@@ -569,10 +647,30 @@ export const MY_SIGIL_REGISTRY = new MySigilRegistry();
569
647
 
570
648
  Now you have fully typed central class registry!
571
649
 
572
- ### Hot module relodes
650
+ ### I don't care about nominal types or central registry, i just want a runtime replacement of 'instanceof'
573
651
 
574
- - Sigil keeps a global registry (backed by `Symbol.for("@Sigil.__SIGIL_REGISTRY__")` on `globalThis`) so label uniqueness checks survive module reloads.
575
- - In development with HMR, duplicate class definitions are common; Sigil prints friendly warnings in DEV rather than throwing to keep the feedback loop fast.
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
+ ```
672
+
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`.
576
674
 
577
675
  ---
578
676
 
@@ -588,7 +686,7 @@ A: Use `@WithSigil("@your/label")`, or wrap the subclass with `withSigil` / `wit
588
686
 
589
687
  **Q: I got this error: 'Property 'x' of exported anonymous class type may not be private or protected.', How to fix it?**
590
688
 
591
- A: This error come's 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 it 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.
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.
592
690
 
593
691
  **Q: I need nominal types in TypeScript. Which helper do I use?**
594
692
 
@@ -596,9 +694,9 @@ A: Use `withSigilTyped` to both attach runtime metadata and apply compile-time b
596
694
 
597
695
  **Q: How do I inspect currently registered labels?**
598
696
 
599
- A: Use `REGISTRY.list()` to get an array of registered labels.
697
+ A: Use `getActiveRegistry()?.list()` to get an array of registered labels.
600
698
 
601
- **Q: What if i want to omit labeling in some classes while inforce others?**
699
+ **Q: What if i want to omit labeling in some classes while enforce others?**
602
700
 
603
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.
604
702
 
@@ -613,8 +711,63 @@ A: You can set `SigilOptions.autofillLabels` to `true`. or if you more strict en
613
711
 
614
712
  ---
615
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
+
616
744
  ## Phantom
617
745
 
618
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.
619
747
 
620
748
  `Phantom` works seamlessly in conjunction with `Sigil`, use `Sigil` for nominal identity on classes (runtime-safe checks across bundles), and `Phantom` for primitives/objects. Together, they provide **end-to-end type safety**: e.g., a Sigil-branded `User` class could hold a Phantom-branded `UserId` string property, enforcing domain boundaries at both compile and runtime.
749
+
750
+ - **GitHub: [@phantom](https://github.com/ZiadTaha62/phantom)**
751
+ - **NPM: [@phantom](https://www.npmjs.com/package/@vicin/phantom)**
752
+
753
+ ---
754
+
755
+ ## Contributing
756
+
757
+ Any contributions you make are **greatly appreciated**.
758
+
759
+ Please see our [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
760
+
761
+ ## License
762
+
763
+ Distributed under the MIT License. See `LICENSE` for more information.
764
+
765
+ ---
766
+
767
+ ## Author
768
+
769
+ Built with ❤️ by **Ziad Taha**.
770
+
771
+ - **GitHub: [@ZiadTaha62](https://github.com/ZiadTaha62)**
772
+ - **NPM: [@ziadtaha62](https://www.npmjs.com/~ziadtaha62)**
773
+ - **Vicin: [@vicin](https://www.npmjs.com/org/vicin)**