@vicin/sigil 2.2.1 β 3.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/CHANGELOG.md +31 -0
- package/README.md +110 -187
- package/dist/index.d.mts +170 -327
- package/dist/index.d.ts +170 -327
- package/dist/index.global.js +245 -2094
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +248 -381
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +249 -377
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [3.1.0] - 2026-02-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `isExactType` is added to check for exact instances (children are ingored as well)
|
|
10
|
+
|
|
11
|
+
### Removed
|
|
12
|
+
|
|
13
|
+
- `SigilOptions.skipLabelInheritanceCheck` is removed.
|
|
14
|
+
- `isOfTypeStrict` is removed.
|
|
15
|
+
|
|
16
|
+
## [3.0.0] - 2026-02-24
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `sigil` symbol used for type-only nominal branding of classes
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `__SIGIL_BRAND__` is classes replaced with `sigil` symbol
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
- `withSigilTyped` is removed as library moved into manual branding
|
|
29
|
+
|
|
30
|
+
### Breaking changes
|
|
31
|
+
|
|
32
|
+
- `withSigilTyped` is removed as library moved into manual branding
|
|
33
|
+
- `SigilBrandOf` is renamed to `SigilOf`
|
|
34
|
+
- `UpdateSigilBrand` is renamed to `ExtendSigil`
|
|
35
|
+
|
|
5
36
|
## [2.2.1] - 2026-02-23
|
|
6
37
|
|
|
7
38
|
### Added
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Sigil
|
|
2
2
|
|
|
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)
|
|
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
|
+
> - π v3.0.0 is out! Happy coding! ππ»
|
|
6
6
|
> - π **Changelog:** [CHANGELOG.md](./CHANGELOG.md)
|
|
7
7
|
|
|
8
8
|
`Sigil` replaces `instanceof` across bundles, enforces nominal class identity, and makes inheritance-aware runtime type checks reliable in large TypeScript systems. 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.
|
|
@@ -15,14 +15,17 @@
|
|
|
15
15
|
|
|
16
16
|
## Important Notes Before Using
|
|
17
17
|
|
|
18
|
-
- **Explicit class identity:** `Sigil` uses passed class label to identify classes, which means that
|
|
19
|
-
- **Performance:** Minimal overhead, but `.isOfType()` is slightly slower than native `instanceof`. Avoid in ultra-hot paths.
|
|
20
|
-
- **Private Constructors:** HOF pattern allows extending private constructors in types (TypeScript limitation).
|
|
18
|
+
- **Explicit class identity:** `Sigil` uses passed class label to identify classes, which means that the developer is responsible for uniqueness of classes by passing unique labels.
|
|
21
19
|
- **Simple instanceof Fix:** If you just need runtime checks without extras, see the [minimal mode](#minimal-mode).
|
|
22
20
|
|
|
23
|
-
##
|
|
21
|
+
## Features
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
- β
**Drop-in `instanceof` replacement** that works across bundles, HMR, and monorepos
|
|
24
|
+
- β
**True nominal typing** with zero runtime cost
|
|
25
|
+
- β
**Inheritance-aware** checks (`isOfType` knows about subclasses)
|
|
26
|
+
- β
**Tiny less than 1.5 KB minified and brotlied** measured using size-limit
|
|
27
|
+
- β
**Performant as native instanceof** but with guaranteed checks! Also can check for **exact class instance**
|
|
28
|
+
- β
Full TypeScript 5.0+ support + excellent JSDoc
|
|
26
29
|
|
|
27
30
|
---
|
|
28
31
|
|
|
@@ -33,24 +36,17 @@ In v2 we simplified the architecture to remove global registries and reduce comp
|
|
|
33
36
|
- [Basic usage](#basic-usage)
|
|
34
37
|
- [Decorator pattern](#decorator-pattern)
|
|
35
38
|
- [HOF pattern](#hof-higher-order-function-pattern)
|
|
36
|
-
- [Minimal βfirst-runβ example](#minimal-first-run-example)
|
|
37
39
|
- [Migration](#migration)
|
|
38
|
-
- [Limitations & guarantees](#limitations--guarantees)
|
|
39
|
-
- [What Sigil guarantees](#what-sigil-guarantees)
|
|
40
|
-
- [What Sigil does not guarantee](#what-sigil-does-not-guarantee)
|
|
41
40
|
- [Core concepts](#core-concepts)
|
|
42
41
|
- [Terminology](#terminology)
|
|
43
42
|
- [Purpose and Origins](#purpose-and-origins)
|
|
44
43
|
- [Implementation Mechanics](#implementation-mechanics)
|
|
45
|
-
- [
|
|
46
|
-
- [HOF pattern](#1-hof-pattern-_classclass)
|
|
47
|
-
- [Decorator pattern](#2-decorator-pattern)
|
|
44
|
+
- [Inheritance example](#inheritance-example)
|
|
48
45
|
- [API reference](#api-reference)
|
|
49
46
|
- [Options & configuration](#options--configuration)
|
|
50
47
|
- [Minimal mode](#minimal-mode)
|
|
51
48
|
- [Strict mode](#strict-mode)
|
|
52
|
-
- [
|
|
53
|
-
- [Troubleshooting & FAQ](#troubleshooting--faq)
|
|
49
|
+
- [Benchmarks](#benchmarks)
|
|
54
50
|
- [Contributing](#contributing)
|
|
55
51
|
- [License](#license)
|
|
56
52
|
- [Author](#author)
|
|
@@ -92,10 +88,8 @@ If your class is marked with `abstract`:
|
|
|
92
88
|
```ts
|
|
93
89
|
import { Sigil, SigilifyAbstract } from '@vicin/sigil';
|
|
94
90
|
|
|
95
|
-
// Using the pre-sigilified base class:
|
|
96
91
|
abstract class User extends Sigil {}
|
|
97
92
|
|
|
98
|
-
// Or use Sigilify when you want an ad-hoc class:
|
|
99
93
|
const MyClass = SigilifyAbstract(abstract class {}, '@myorg/mypkg.MyClass');
|
|
100
94
|
```
|
|
101
95
|
|
|
@@ -118,7 +112,7 @@ class User extends Sigil {}
|
|
|
118
112
|
|
|
119
113
|
##### HOF (Higher-Order Function) pattern
|
|
120
114
|
|
|
121
|
-
Apply a label using
|
|
115
|
+
Apply a label using `withSigil` HOF:
|
|
122
116
|
|
|
123
117
|
```ts
|
|
124
118
|
import { Sigil, withSigil } from '@vicin/sigil';
|
|
@@ -130,26 +124,6 @@ const user = new User();
|
|
|
130
124
|
console.log(User.SigilLabel); // "@myorg/mypkg.User"
|
|
131
125
|
```
|
|
132
126
|
|
|
133
|
-
> Note: When extending an already sigilified class (for example `Sigil`), you must decorate the subclass or use the HOF helpers in DEV mode unless you configured the library otherwise.
|
|
134
|
-
|
|
135
|
-
### Minimal βfirst-runβ example
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import { Sigil, withSigil } from '@vicin/sigil';
|
|
139
|
-
|
|
140
|
-
class _User extends Sigil {
|
|
141
|
-
constructor(public name: string) {
|
|
142
|
-
super();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
export const User = withSigil(_User, '@myorg/mypkg.User');
|
|
146
|
-
|
|
147
|
-
const u = new User('alice');
|
|
148
|
-
|
|
149
|
-
console.log(User.SigilLabel); // "@myorg/mypkg.User"
|
|
150
|
-
console.log(User.isOfType(u)); // true
|
|
151
|
-
```
|
|
152
|
-
|
|
153
127
|
### Migration
|
|
154
128
|
|
|
155
129
|
Migrating old code into `Sigil` can be done with extra couple lines of code only:
|
|
@@ -174,33 +148,15 @@ Congratulations β youβve opted into `Sigil` and you can start replacing `ins
|
|
|
174
148
|
|
|
175
149
|
---
|
|
176
150
|
|
|
177
|
-
## Limitations & guarantees
|
|
178
|
-
|
|
179
|
-
This section states clearly what `Sigil` provides and what it does **not** provide.
|
|
180
|
-
|
|
181
|
-
### What Sigil guarantees
|
|
182
|
-
|
|
183
|
-
**1. Reliable runtime identity (when used as intended).**
|
|
184
|
-
|
|
185
|
-
**2. Nominal typing that is inheritance-aware**
|
|
186
|
-
|
|
187
|
-
### What Sigil does not guarantee
|
|
188
|
-
|
|
189
|
-
**1. Doesn't work across isolated realms (e.g., iframes, workers) without custom bridging.**
|
|
190
|
-
|
|
191
|
-
**2. Not for security/access control β constructors can be discoverable.**
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
151
|
## Core concepts
|
|
196
152
|
|
|
197
153
|
### Terminology
|
|
198
154
|
|
|
199
|
-
- **Label**: An identity (string) such as `@scope/pkg.ClassName`, but can be random string (e.g. `@Sigil
|
|
200
|
-
- **
|
|
155
|
+
- **Label**: An identity (string) such as `@scope/pkg.ClassName`, but can be random string (e.g. `@Sigil-auto:ClassName:mm2gkdwn:0:g1sq`) if no label passed.
|
|
156
|
+
- **EffectiveLabel:** A human-readable (string) such as `@scope/pkg.ClassName`, if no label is passed it inherit the last defined label.
|
|
201
157
|
- **Label lineage**: Array of labels for ancestry.
|
|
202
158
|
- **Label set**: Set of labels for fast checks.
|
|
203
|
-
- **
|
|
159
|
+
- **[sigil]**: TypeScript symbol marker for nominal types.
|
|
204
160
|
|
|
205
161
|
---
|
|
206
162
|
|
|
@@ -216,143 +172,85 @@ if (obj instanceof User) { ... }
|
|
|
216
172
|
|
|
217
173
|
// With Sigil
|
|
218
174
|
if (User.isOfType(obj)) { ... } // This still works even if User was bundled twice.
|
|
175
|
+
if (User.isExactType(obj)) { ... } // Or check for exactly same constructor not its children
|
|
219
176
|
```
|
|
220
177
|
|
|
221
|
-
- **Manual Branding Overhead:** Custom identifiers lead to boilerplate and maintenance issues.
|
|
222
|
-
|
|
223
|
-
`Sigil` abstracts these into a **centralized system**, making identity management **explicit** and **error-resistant** if defined the right way.
|
|
224
|
-
|
|
225
|
-
### Implementation Mechanics
|
|
226
|
-
|
|
227
|
-
- **Runtime Contract:** Established via extending `Sigil` or using `Sigilify` mixin.
|
|
228
|
-
- **Update metadata:** With each new child, HOF or decorators are used to attach metadata and update nominal type.
|
|
229
|
-
- **Accessors & Type guards:** Classes expose `SigilLabel`; instances provide `getSigilLabel()` for querying unique identifier label. also when typed it hold nominal identity used to prevent subtle bugs.
|
|
178
|
+
- **Manual Branding Overhead:** Custom identifiers lead to boilerplate and maintenance issues, `Sigil` add reliable inheritance-aware nominal branding with just one line of code.
|
|
230
179
|
|
|
231
180
|
```ts
|
|
232
|
-
import {
|
|
233
|
-
|
|
234
|
-
// Runtime contract
|
|
235
|
-
class _MyClass extends Sigil {}
|
|
181
|
+
import { sigil } from '@vicin/sigil';
|
|
236
182
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
183
|
+
class User extends Sigil {
|
|
184
|
+
declare [sigil]: ExtendSigil<'User', Sigil>; // <-- Update nominal brand with this line
|
|
185
|
+
}
|
|
240
186
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
console.log(new MyClass().getSigilLabel()); // '@scope/package.MyClass'
|
|
244
|
-
console.log(MyClass.isOfType(new MyClass())); // true
|
|
245
|
-
function x(c: MyClass) {} // Only instances of 'MyClass' can be passed
|
|
187
|
+
type test1 = User extends Sigil ? true : false; // true
|
|
188
|
+
type test2 = Sigil extends User ? true : false; // false
|
|
246
189
|
```
|
|
247
190
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
## Nominal typing patterns
|
|
251
|
-
|
|
252
|
-
In this part we will discuss conventions to avoid any type errors and have nominal typing with just extra few definition lines.
|
|
253
|
-
We have two patterns, **HOF pattern (`_Class`/`Class`)** and **Decorator pattern**:
|
|
191
|
+
`Sigil` abstracts these into a **centralized system**, making identity management **explicit** and **error-resistant** if defined the right way.
|
|
254
192
|
|
|
255
|
-
###
|
|
193
|
+
### Implementation Mechanics
|
|
256
194
|
|
|
257
|
-
|
|
195
|
+
- **Runtime Contract:** Established via extending `Sigil` or using `Sigilify` mixin.
|
|
196
|
+
- **Update metadata:** With each new child, use decorators or HOF to attach run-time metadata and `ExtendSigil` to update nominal type.
|
|
258
197
|
|
|
259
198
|
```ts
|
|
260
|
-
import { Sigil,
|
|
199
|
+
import { Sigil, WithSigil, sigil, ExtendSigil } from '@vicin/sigil';
|
|
261
200
|
|
|
262
|
-
|
|
263
|
-
|
|
201
|
+
@WithSigil('@scope/package.MyClass') // <-- Run-time values update
|
|
202
|
+
class MyClass extends Sigil {
|
|
203
|
+
declare [sigil]: ExtendSigil<'@scope/package.MyClass', Sigil>; // <-- compile-time type update
|
|
264
204
|
}
|
|
265
|
-
export const X = withSigilTyped(_X, 'Label.X');
|
|
266
|
-
export type X = GetInstance<typeof X>;
|
|
267
205
|
```
|
|
268
206
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
You should depend on `GetInstance` to get type of instance and avoid using `InstanceType` as it returns `any` if the class constructor is `protected` or `private`.
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
export type X = GetInstance<typeof X>; // <-- works with 'private' and 'protected' constructors as well
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
Internally `GetInstance` is just `T extends { prototype: infer R }`.
|
|
278
|
-
|
|
279
|
-
#### Generic propagation
|
|
207
|
+
You can avoid decorators and use HOF but they are slightly more verbose:
|
|
280
208
|
|
|
281
209
|
```ts
|
|
282
|
-
|
|
283
|
-
export const X = withSigilTyped(_X, 'Label.X');
|
|
284
|
-
export type X<G> = GetInstance<typeof X<G>>; // <-- Redeclare generics here
|
|
285
|
-
|
|
286
|
-
class _Y<G> extends X<G> {} // and so on...
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
#### Anonymous classes
|
|
290
|
-
|
|
291
|
-
You may see error: `Property 'x' of exported anonymous class type may not be private or protected.`, although this is rare to occur.
|
|
292
|
-
This comes from the fact that all typed classes are `anonymous class` as they are return of HOF. to avoid these error entirely all you need is exporting the untyped classes even if they are un-used as a good convention.
|
|
210
|
+
import { Sigil, withSigil, sigil, ExtendSigil } from '@vicin/sigil';
|
|
293
211
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
export const X = withSigilTyped(_X, 'Label.X');
|
|
297
|
-
export type X = GetInstance<typeof X>;
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
#### Private constructors
|
|
301
|
-
|
|
302
|
-
The only limitation in HOF approach is **extending private constructors**:
|
|
303
|
-
|
|
304
|
-
```ts
|
|
305
|
-
import { Sigil, withSigilTyped, GetInstance } from '@vicin/sigil';
|
|
306
|
-
class _X extends Sigil {
|
|
307
|
-
private constructor() {}
|
|
212
|
+
class _MyClass extends Sigil {
|
|
213
|
+
declare [sigil]: ExtendSigil<'@scope/package.MyClass', Sigil>;
|
|
308
214
|
}
|
|
309
|
-
const X = withSigilTyped(_X, 'X');
|
|
310
|
-
type X = GetInstance<typeof X>;
|
|
311
|
-
|
|
312
|
-
class _Y extends X {} // <-- This is allowed!
|
|
313
|
-
const Y = withSigilTyped(_Y, 'Y');
|
|
314
|
-
type Y = GetInstance<typeof Y>;
|
|
315
215
|
|
|
316
|
-
const
|
|
216
|
+
const MyClass = withSigil(_MyClass, '@scope/package.MyClass');
|
|
217
|
+
type MyClass = InstanceType<typeof MyClass>;
|
|
317
218
|
```
|
|
318
219
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
---
|
|
220
|
+
Note that you can't use `InstanceType` on `private` or `protected` classes.
|
|
322
221
|
|
|
323
|
-
###
|
|
324
|
-
|
|
325
|
-
Inject brand directly in class body:
|
|
222
|
+
### Inheritance example
|
|
326
223
|
|
|
327
224
|
```ts
|
|
328
|
-
import { Sigil, WithSigil
|
|
225
|
+
import { Sigil, WithSigil } from '@vicin/sigil';
|
|
329
226
|
|
|
330
|
-
@WithSigil('
|
|
331
|
-
class
|
|
332
|
-
declare
|
|
227
|
+
@WithSigil('@myorg/User')
|
|
228
|
+
class User extends Sigil {
|
|
229
|
+
declare [sigil]: ExtendSigil<'@myorg/User', Sigil>;
|
|
333
230
|
}
|
|
334
231
|
|
|
335
|
-
@WithSigil('
|
|
336
|
-
class
|
|
337
|
-
declare
|
|
232
|
+
@WithSigil('@myorg/Admin')
|
|
233
|
+
class Admin extends User {
|
|
234
|
+
declare [sigil]: ExtendSigil<'@myorg/Admin', User>;
|
|
338
235
|
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
No `_Class`/`Class` pattern, no `private constructor` issue, no type hacks and only one extra line, but our branding logic now lives in class body.
|
|
342
|
-
|
|
343
|
-
#### Label Consistency
|
|
344
236
|
|
|
345
|
-
|
|
237
|
+
const admin = new Admin();
|
|
238
|
+
const user = new User();
|
|
346
239
|
|
|
347
|
-
|
|
348
|
-
|
|
240
|
+
// Instanceof like behavior
|
|
241
|
+
console.log(Admin.isOfType(admin)); // true
|
|
242
|
+
console.log(Admin.isOfType(user)); // false
|
|
243
|
+
console.log(User.isOfType(admin)); // true
|
|
244
|
+
console.log(User.isOfType(user)); // true
|
|
349
245
|
|
|
350
|
-
|
|
246
|
+
// Exact checks
|
|
247
|
+
console.log(Admin.isOfType(admin)); // true
|
|
248
|
+
console.log(Admin.isOfType(user)); // false
|
|
249
|
+
console.log(User.isOfType(user)); // true
|
|
250
|
+
console.log(User.isOfType(admin)); // false (Admin is child indeed but this checks for user specifically)
|
|
351
251
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
declare __SIGIL_BRAND__: UpdateSigilBrand<typeof label, Sigil>;
|
|
355
|
-
}
|
|
252
|
+
type test1 = Admin extends User ? true : false; // true
|
|
253
|
+
type test2 = User extends Admin ? true : false; // false
|
|
356
254
|
```
|
|
357
255
|
|
|
358
256
|
---
|
|
@@ -374,7 +272,6 @@ class X extends Sigil {
|
|
|
374
272
|
|
|
375
273
|
- **HOFs:**
|
|
376
274
|
- `withSigil(Class, label?, opts?)`
|
|
377
|
-
- `withSigilTyped(Class, label?, opts?)`
|
|
378
275
|
|
|
379
276
|
- **Helpers:**
|
|
380
277
|
- `isSigilCtor(ctor)`
|
|
@@ -392,20 +289,17 @@ class X extends Sigil {
|
|
|
392
289
|
- `ISigil<Label, ParentSigil?>`
|
|
393
290
|
- `ISigilStatic<Label, ParentSigil?>`
|
|
394
291
|
- `ISigilInstance<Label, ParentSigil?>`
|
|
395
|
-
- `
|
|
396
|
-
- `
|
|
397
|
-
- `GetInstance<T>`
|
|
398
|
-
- `UpdateSigilBrand<Label, Base>`
|
|
292
|
+
- `SigilOf<T>`
|
|
293
|
+
- `ExtendSigil<Label, Parent>`
|
|
399
294
|
- `SigilOptions`
|
|
400
295
|
|
|
401
296
|
### Key helpers (runtime)
|
|
402
297
|
|
|
403
298
|
- `Sigil`: a minimal sigilified base class you can extend from.
|
|
404
299
|
- `SigilError`: an `Error` class decorated with a `Sigil` so it can be identified at runtime.
|
|
405
|
-
- `WithSigil(label)`: class decorator that attaches `Sigil` metadata at declaration time.
|
|
406
300
|
- `Sigilify(Base, label?, opts?)`: mixin function that returns a new constructor with `Sigil` types and instance helpers.
|
|
301
|
+
- `WithSigil(label)`: class decorator that attaches `Sigil` metadata at declaration time.
|
|
407
302
|
- `withSigil(Class, label?, opts?)`: HOF that validates and decorates an existing class constructor.
|
|
408
|
-
- `withSigilTyped(Class, label?, opts?)`: like `withSigil` but narrows the TypeScript type to include brands.
|
|
409
303
|
- `isSigilCtor(value)`: `true` if `value` is a `Sigil` constructor.
|
|
410
304
|
- `isSigilInstance(value)`: `true` if `value` is an instance of a `Sigil` constructor.
|
|
411
305
|
- `updateSigilOptions(opts)`: change global runtime options before `Sigil` decoration (e.g., `autofillLabels`).
|
|
@@ -417,11 +311,11 @@ When a constructor is decorated/sigilified it will expose the following **static
|
|
|
417
311
|
|
|
418
312
|
- `SigilLabel` β the identity label string.
|
|
419
313
|
- `SigilEffectiveLabel` β the human label string.
|
|
420
|
-
- `SigilLabelLineage` β readonly array of labels representing parent β child.
|
|
421
|
-
- `SigilLabelSet` β readonly `Set<string>` for
|
|
314
|
+
- `SigilLabelLineage` β readonly array of labels representing parent β child for debugging.
|
|
315
|
+
- `SigilLabelSet` β readonly `Set<string>` for debugging.
|
|
422
316
|
- `isSigilified(obj)` β runtime predicate that delegates to `isSigilInstance`.
|
|
423
|
-
- `isOfType(other)` β
|
|
424
|
-
- `
|
|
317
|
+
- `isOfType(other)` β check if other is an instance of this constructor or its children.
|
|
318
|
+
- `isExactType(other) `β check if other is an instance exactly this constructor.
|
|
425
319
|
|
|
426
320
|
Instances of sigilified classes expose instance helpers:
|
|
427
321
|
|
|
@@ -429,8 +323,8 @@ Instances of sigilified classes expose instance helpers:
|
|
|
429
323
|
- `getSigilEffectiveLabel()` β returns the human label.
|
|
430
324
|
- `getSigilLabelLineage()` β returns lineage array.
|
|
431
325
|
- `getSigilLabelSet()` β returns readonly Set.
|
|
432
|
-
- `isOfType(other)` β
|
|
433
|
-
- `
|
|
326
|
+
- `isOfType(other)` β check if other is an instance of the same class or its children as this.
|
|
327
|
+
- `isExactType(other) `β check if other is an instance exactly the same constructor.
|
|
434
328
|
|
|
435
329
|
---
|
|
436
330
|
|
|
@@ -467,7 +361,7 @@ class C extends B {}
|
|
|
467
361
|
|
|
468
362
|
## Strict mode
|
|
469
363
|
|
|
470
|
-
If you want to
|
|
364
|
+
If you want to enforce passing a label to every class defined in your codebase, you can set `autofillLabels` to `false` at the start of app:
|
|
471
365
|
|
|
472
366
|
```ts
|
|
473
367
|
import { updateSigilOptions } from '@vicin/sigil';
|
|
@@ -479,20 +373,49 @@ Now if you forgot to pass a label error is thrown.
|
|
|
479
373
|
|
|
480
374
|
---
|
|
481
375
|
|
|
482
|
-
##
|
|
376
|
+
## Benchmarks
|
|
483
377
|
|
|
484
|
-
-
|
|
485
|
-
|
|
486
|
-
- Want clean class bodies? β Use HOF
|
|
487
|
-
- Want fewer wrapper exports? β Use Decorators
|
|
378
|
+
Sigil is built for **real-world performance**. Below are the latest micro-benchmark results (run on **Node.js v20.12.0**).
|
|
379
|
+
To run benchmarks on your machine fetch source code from [github](https://github.com/ZiadTaha62/sigil) and run `npm run bench` in your console.
|
|
488
380
|
|
|
489
|
-
|
|
381
|
+
### 1. Runtime Type Checking
|
|
382
|
+
|
|
383
|
+
| Depth | `instanceof` (per op) | `isOfType` (ctor) | `isOfType` (instance) | `isExactType` (ctor) | `isExactType` (instance) |
|
|
384
|
+
| ----- | --------------------- | ----------------- | --------------------- | -------------------- | ------------------------ |
|
|
385
|
+
| 0 | 0.000010 ms | 0.000025 ms | **0.000010 ms** | 0.000027 ms | 0.000012 ms |
|
|
386
|
+
| 3 | 0.000032 ms | 0.000045 ms | **0.000027 ms** | 0.000038 ms | **0.000025 ms** |
|
|
387
|
+
| 5 | 0.000034 ms | 0.000046 ms | **0.000028 ms** | 0.000037 ms | **0.000026 ms** |
|
|
388
|
+
| 10 | 0.000044 ms | 0.000045 ms | **0.000029 ms** | 0.000038 ms | **0.000027 ms** |
|
|
389
|
+
| 15 | 0.000058 ms | 0.000063 ms | **0.000051 ms** | 0.000069 ms | **0.000053 ms** |
|
|
390
|
+
|
|
391
|
+
> **Key takeaway**:
|
|
392
|
+
> `isOfType` has **practically the same performance as native `instanceof`**, slightly **slower** on static calls and slightly **faster** on the instance side.
|
|
393
|
+
> `isExactType` adds only a tiny negligible cost and remains extremely fast even on deep hierarchies.
|
|
394
|
+
|
|
395
|
+
### 2. Class Definition & Instance Creation
|
|
396
|
+
|
|
397
|
+
| Scenario | Definition (per class) | Instantiation (per instance) |
|
|
398
|
+
| ------------------------------- | ---------------------- | ---------------------------- |
|
|
399
|
+
| Empty plain class | 0.0122 ms | 0.00019 ms |
|
|
400
|
+
| Empty Sigil class | 0.0672 ms | 0.00059 ms |
|
|
401
|
+
| Small (5 props + 3 methods) | 0.0172 ms | 0.00327 ms |
|
|
402
|
+
| Large (15 props + 10 methods) | 0.0212 ms | 0.00922 ms |
|
|
403
|
+
| Large Sigil | 0.0780 ms | 0.01177 ms |
|
|
404
|
+
| Extended chain depth 5 β plain | 0.0897 ms | 0.01809 ms |
|
|
405
|
+
| Extended chain depth 5 β Sigil | 0.3978 ms | 0.02020 ms |
|
|
406
|
+
| Extended chain depth 10 β plain | 0.2042 ms | 0.05759 ms |
|
|
407
|
+
| Extended chain depth 10 β Sigil | 0.8127 ms | 0.06675 ms |
|
|
408
|
+
|
|
409
|
+
> **Key takeaways**:
|
|
410
|
+
>
|
|
411
|
+
> - Class definition is a **one-time cost** at module load time. Even at depth 10 the cost stays well under 1 ms per class.
|
|
412
|
+
> - Instance creation adds a small fixed overhead of ~0.4β0.6 Β΅s per object, which becomes completely negligible as your classes grow in size and complexity.
|
|
413
|
+
|
|
414
|
+
### Bundle Size
|
|
490
415
|
|
|
491
|
-
|
|
416
|
+
**less than 1.5 KB** (minified + Brotli, including all dependencies)
|
|
492
417
|
|
|
493
|
-
-
|
|
494
|
-
- **Anonymous Class Errors:** Export untyped bases.
|
|
495
|
-
- **Selective Labeling:** Use `autofillLabels: true` or empty `@WithSigil()` for auto-generation.
|
|
418
|
+
This makes Sigil one of the smallest full-featured solutions for nominal typing + reliable runtime identity.
|
|
496
419
|
|
|
497
420
|
---
|
|
498
421
|
|