@vicin/sigil 1.0.0 → 1.0.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/LICENSE +1 -1
- package/README.md +57 -24
- package/package.json +6 -1
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c)
|
|
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
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
**Note: You should read these parts before implementing `Sigil` in you code:**
|
|
13
13
|
|
|
14
|
-
- **Security note:** `Sigil`
|
|
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. so **avoid** siglizing classes you want to make it unaccessable outside it's module. 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
|
|
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
|
|
|
@@ -35,16 +35,24 @@
|
|
|
35
35
|
- [Troubleshooting & FAQ](#troubleshooting--faq)
|
|
36
36
|
- [Best practices](#best-practices)
|
|
37
37
|
- [Phantom](#phantom)
|
|
38
|
+
- [Contributing](#contributing)
|
|
39
|
+
- [License](#license)
|
|
40
|
+
- [Author](#author)
|
|
38
41
|
|
|
39
42
|
---
|
|
40
43
|
|
|
41
44
|
## Features
|
|
42
45
|
|
|
43
46
|
- Attach **stable runtime identity** to classes using `Symbol.for(label)`.
|
|
47
|
+
|
|
44
48
|
- **Type-level branding** so distinct domain identities are enforced by TypeScript.
|
|
49
|
+
|
|
45
50
|
- **Lineage tracking** (arrays + sets of symbols) for O(1) and O(n) checks.
|
|
51
|
+
|
|
46
52
|
- Easy to use: decorator (`@WithSigil`), mixin (`Sigilify`), and HOF (Higher order function) helpers (`withSigil`, `withSigilTyped`, `typed`).
|
|
53
|
+
|
|
47
54
|
- **Global registry** to centralize classes (query any class by it's label in run-time) and guard against duplicate labels.
|
|
55
|
+
|
|
48
56
|
- Minimal runtime overhead in production (DEV checks can be toggled off).
|
|
49
57
|
|
|
50
58
|
---
|
|
@@ -54,11 +62,11 @@
|
|
|
54
62
|
### Install
|
|
55
63
|
|
|
56
64
|
```bash
|
|
57
|
-
npm install sigil
|
|
65
|
+
npm install @vicin/sigil
|
|
58
66
|
# or
|
|
59
|
-
yarn add sigil
|
|
67
|
+
yarn add @vicin/sigil
|
|
60
68
|
# or
|
|
61
|
-
pnpm add sigil
|
|
69
|
+
pnpm add @vicin/sigil
|
|
62
70
|
```
|
|
63
71
|
|
|
64
72
|
**Requirements**: TypeScript 5.0+ (for stage-3 decorators) and Node.js 18+ recommended.
|
|
@@ -68,7 +76,7 @@ pnpm add sigil
|
|
|
68
76
|
Use the `Sigil` base class or the `Sigilify` mixin to opt a class into the Sigil runtime contract.
|
|
69
77
|
|
|
70
78
|
```ts
|
|
71
|
-
import { Sigil, Sigilify } from 'sigil';
|
|
79
|
+
import { Sigil, Sigilify } from '@vicin/sigil';
|
|
72
80
|
|
|
73
81
|
// Using the pre-sigilified base class:
|
|
74
82
|
class User extends Sigil {}
|
|
@@ -84,7 +92,7 @@ This adds runtime metadata to the constructor and allows you to use runtime help
|
|
|
84
92
|
Apply a label with the `@WithSigil` decorator. This is handy for small classes or when you prefer decorator syntax.
|
|
85
93
|
|
|
86
94
|
```ts
|
|
87
|
-
import { Sigil, WithSigil } from 'sigil';
|
|
95
|
+
import { Sigil, WithSigil } from '@vicin/sigil';
|
|
88
96
|
|
|
89
97
|
@WithSigil('@myorg/mypkg.User')
|
|
90
98
|
class User extends Sigil {}
|
|
@@ -97,7 +105,7 @@ class User extends Sigil {}
|
|
|
97
105
|
HOFs work well in many build setups and are idempotent-safe for HMR flows.
|
|
98
106
|
|
|
99
107
|
```ts
|
|
100
|
-
import { Sigil, withSigil } from 'sigil';
|
|
108
|
+
import { Sigil, withSigil } from '@vicin/sigil';
|
|
101
109
|
|
|
102
110
|
class _User extends Sigil {}
|
|
103
111
|
const User = withSigil(_User, '@myorg/mypkg.User');
|
|
@@ -111,7 +119,7 @@ console.log(User.SigilLabel); // "@myorg/mypkg.User"
|
|
|
111
119
|
If you want TypeScript to treat identities nominally (so `UserId` !== `PostId` despite identical shape), use the typed helpers.
|
|
112
120
|
|
|
113
121
|
```ts
|
|
114
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
122
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
115
123
|
|
|
116
124
|
class _User extends Sigil {}
|
|
117
125
|
const User = withSigilTyped(_User, '@myorg/mypkg.User');
|
|
@@ -128,14 +136,14 @@ Migration old code into `Sigil` can be done seamlessly with this set-up:
|
|
|
128
136
|
1. Set `SigilOptions.autofillLabels` to `true` at the start of the app so no errors are thrown in the migration stage:
|
|
129
137
|
|
|
130
138
|
```ts
|
|
131
|
-
import { updateOptions } from 'sigil';
|
|
139
|
+
import { updateOptions } from '@vicin/sigil';
|
|
132
140
|
updateOptions({ autofillLabels: true });
|
|
133
141
|
```
|
|
134
142
|
|
|
135
143
|
2. Make you base classes extends `Sigil`:
|
|
136
144
|
|
|
137
145
|
```ts
|
|
138
|
-
import { Sigil } from 'sigil';
|
|
146
|
+
import { Sigil } from '@vicin/sigil';
|
|
139
147
|
|
|
140
148
|
class MyBaseClass {} // original
|
|
141
149
|
|
|
@@ -215,7 +223,7 @@ Basic patterns:
|
|
|
215
223
|
Mixin / factory:
|
|
216
224
|
|
|
217
225
|
```ts
|
|
218
|
-
import { Sigilify } from 'sigil';
|
|
226
|
+
import { Sigilify } from '@vicin/sigil';
|
|
219
227
|
|
|
220
228
|
const MyClass = Sigilify(class {}, 'MyClass');
|
|
221
229
|
```
|
|
@@ -223,7 +231,7 @@ const MyClass = Sigilify(class {}, 'MyClass');
|
|
|
223
231
|
Direct base-class extend:
|
|
224
232
|
|
|
225
233
|
```ts
|
|
226
|
-
import { Sigil, WithSigil } from 'sigil';
|
|
234
|
+
import { Sigil, WithSigil } from '@vicin/sigil';
|
|
227
235
|
|
|
228
236
|
class MyClass extends Sigil {}
|
|
229
237
|
```
|
|
@@ -234,7 +242,7 @@ Once you opt into the runtime contract, Sigil enforces consistency: in DEV mode,
|
|
|
234
242
|
Decorator style:
|
|
235
243
|
|
|
236
244
|
```ts
|
|
237
|
-
import { Sigil, WithSigil } from 'sigil';
|
|
245
|
+
import { Sigil, WithSigil } from '@vicin/sigil';
|
|
238
246
|
|
|
239
247
|
@WithSigil('MyClass') // <-- Note `@WithSigil` used here cause it extended alreay sigilized class (Sigil). Error is thrown without it.
|
|
240
248
|
class MyClass extends Sigil {}
|
|
@@ -243,7 +251,7 @@ class MyClass extends Sigil {}
|
|
|
243
251
|
HOF (preferred for many workflows):
|
|
244
252
|
|
|
245
253
|
```ts
|
|
246
|
-
import { Sigil, withSigil } from 'sigil';
|
|
254
|
+
import { Sigil, withSigil } from '@vicin/sigil';
|
|
247
255
|
|
|
248
256
|
class _MyClass extends Sigil {}
|
|
249
257
|
const MyClass = withSigil(_MyClass, 'MyClass');
|
|
@@ -262,7 +270,7 @@ Runtime metadata alone does not change TypeScript types. To get compile-time nom
|
|
|
262
270
|
Example using a typed HOF:
|
|
263
271
|
|
|
264
272
|
```ts
|
|
265
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
273
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
266
274
|
|
|
267
275
|
// Untyped (runtime) base you extend as normal TS class code:
|
|
268
276
|
class _User extends Sigil {}
|
|
@@ -291,7 +299,7 @@ This separation is necessary as typescript decorators doesn't affect type system
|
|
|
291
299
|
Example of appraoch for class chain:
|
|
292
300
|
|
|
293
301
|
```ts
|
|
294
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
302
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
295
303
|
|
|
296
304
|
// Untyped base classes used for implementation:
|
|
297
305
|
class _User extends Sigil {}
|
|
@@ -334,7 +342,7 @@ The update of Sigil brand types happens via HOF that are defined below actual cl
|
|
|
334
342
|
Example:
|
|
335
343
|
|
|
336
344
|
```ts
|
|
337
|
-
import { Sigil, withSigilTyped } from 'sigil';
|
|
345
|
+
import { Sigil, withSigilTyped } from '@vicin/sigil';
|
|
338
346
|
|
|
339
347
|
class _X extends Sigil {
|
|
340
348
|
// All logic for class
|
|
@@ -367,7 +375,7 @@ Earlier example used `InstanceType<>` to get instance of the class. It works wel
|
|
|
367
375
|
So alternative in introduced which is `GetInstance`.
|
|
368
376
|
|
|
369
377
|
```ts
|
|
370
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
378
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
371
379
|
|
|
372
380
|
class _X extends Sigil {}
|
|
373
381
|
|
|
@@ -386,7 +394,7 @@ One of the downsides of defining typed class at the bottom is that we need to re
|
|
|
386
394
|
Example of generic propagation:
|
|
387
395
|
|
|
388
396
|
```ts
|
|
389
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
397
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
390
398
|
|
|
391
399
|
// Untyped base classes used for implementation:
|
|
392
400
|
class _X<G> extends Sigil {}
|
|
@@ -403,7 +411,7 @@ You may see error: `Property 'x' of exported anonymous class type may not be pri
|
|
|
403
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 entirly all you need is exporting the untyped classes even if they are un-used as a good convention.
|
|
404
412
|
|
|
405
413
|
```ts
|
|
406
|
-
import { Sigil, withSigilTyped, GetInstance } from 'sigil';
|
|
414
|
+
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
407
415
|
|
|
408
416
|
export class _X extends Sigil {} // <-- Just add 'export' here
|
|
409
417
|
|
|
@@ -487,7 +495,7 @@ Instances of sigilified classes expose instance helpers:
|
|
|
487
495
|
Sigil exposes a small set of runtime options that control DEV behavior. These can be modified at app startup via `updateOptions(...)`.
|
|
488
496
|
|
|
489
497
|
```ts
|
|
490
|
-
import { updateOptions } from 'sigil';
|
|
498
|
+
import { updateOptions } from '@vicin/sigil';
|
|
491
499
|
|
|
492
500
|
updateOptions({
|
|
493
501
|
autofillLabels: false, // auto-generate labels for subclasses that would otherwise inherit
|
|
@@ -531,7 +539,7 @@ Registry is stored in `globalThis` under `Symbol.for(__SIGIL_REGISTRY__)` so the
|
|
|
531
539
|
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
540
|
|
|
533
541
|
```ts
|
|
534
|
-
import { REGISTRY } from 'sigil';
|
|
542
|
+
import { REGISTRY } from '@vicin/sigil';
|
|
535
543
|
import { MySigilClass1 } from './file1';
|
|
536
544
|
import { MySigilClass2 } from './file2';
|
|
537
545
|
|
|
@@ -551,7 +559,7 @@ class MySigilRegistry {
|
|
|
551
559
|
return REGISTRY.get(label) as any;
|
|
552
560
|
}
|
|
553
561
|
unregister(label: string): boolean {
|
|
554
|
-
return REGISTRY.unregister();
|
|
562
|
+
return REGISTRY.unregister(label);
|
|
555
563
|
}
|
|
556
564
|
clear(): void {
|
|
557
565
|
REGISTRY.clear();
|
|
@@ -618,3 +626,28 @@ A: You can set `SigilOptions.autofillLabels` to `true`. or if you more strict en
|
|
|
618
626
|
`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
627
|
|
|
620
628
|
`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.
|
|
629
|
+
|
|
630
|
+
- **GitHub: [@phantom](https://github.com/ZiadTaha62/phantom)**
|
|
631
|
+
- **NPM: [@phantom](https://www.npmjs.com/package/@vicin/phantom)**
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## Contributing
|
|
636
|
+
|
|
637
|
+
Any contributions you make are **greatly appreciated**.
|
|
638
|
+
|
|
639
|
+
Please see our [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
640
|
+
|
|
641
|
+
## License
|
|
642
|
+
|
|
643
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Author
|
|
648
|
+
|
|
649
|
+
Built with ❤️ by **Ziad Taha**.
|
|
650
|
+
|
|
651
|
+
- **GitHub: [@ZiadTaha62](https://github.com/ZiadTaha62)**
|
|
652
|
+
- **NPM: [@ziadtaha62](https://www.npmjs.com/~ziadtaha62)**
|
|
653
|
+
- **Vicin: [@vicin](https://www.npmjs.com/org/vicin)**
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vicin/sigil",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Lightweight TypeScript library for nominal identity classes",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/ZiadTaha62/sigil.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/ZiadTaha62/sigil#readme",
|
|
10
15
|
"scripts": {
|
|
11
16
|
"build": "tsc",
|
|
12
17
|
"lint": "eslint --fix",
|