@vicin/sigil 3.2.1 → 3.4.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 CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [3.4.0] - 2026-02-28
6
+
7
+ ### Changed
8
+
9
+ - Updated internal logic to handle edge-cases
10
+ - Edge cases part added to tests
11
+ - Updated README.md
12
+
13
+ ### Added
14
+
15
+ - `AttachSigil` / `attachSigil` which are `WithSigil` / `withSigil` renamed for clarity
16
+
17
+ ### Deprecated
18
+
19
+ - `WithSigil` / `withSigil` renamed for clarity. old names will be removed in v4
20
+ - `SigilLabelSet` / `getSigilLabelSet` methods to minimize api surface and bundle size as they are redundant (internall 'new Set(this.SigilLabelLineage)' only). will be removed in v4
21
+
22
+ ## [3.3.0] - 2026-02-27
23
+
24
+ ### Changed
25
+
26
+ - Label registry now stores user defined labels only, and `getSigilLabels` has no `includeAuto` argument any more
27
+
28
+ ### Added
29
+
30
+ - Options `skipLabelUniquenessCheck` is added to avoid false-positive HMR throws
31
+
5
32
  ## [3.2.1] - 2026-02-27
6
33
 
7
34
  ### Changed
@@ -17,7 +44,7 @@ All notable changes to this project will be documented in this file.
17
44
 
18
45
  ### Deprecated
19
46
 
20
- - `DEFAULT_LABEL_REGEX` is deprecated and will be removed in v4, use `RECOMMENDED_LABEL_REGEX` instead
47
+ - `DEFAULT_LABEL_REGEX` is deprecated use `RECOMMENDED_LABEL_REGEX` instead, will be removed in v4
21
48
 
22
49
  ## [3.1.4] - 2026-02-26
23
50
 
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## Features
11
11
 
12
- - ✅ **Drop-in `instanceof` replacement** that works across bundles and monorepos, Also add check for **exact class instance**
12
+ - ✅ **Drop-in `instanceof` replacement** that works across bundles, HMR and monorepos, and adds an **exact-class-instance check**
13
13
  - ✅ **Simple nominal typing** with just one line of code for each class (e.g., `UserId` vs. `PostId`)
14
14
  - ✅ **Tiny less than 1.6 KB minified and brotlied** measured using [size-limit](https://www.npmjs.com/package/size-limit)
15
15
  - ✅ **Performant as native instanceof** but with guaranteed checks
@@ -24,7 +24,7 @@
24
24
  - [Install](#install)
25
25
  - [Basic usage](#basic-usage)
26
26
  - [Decorator pattern](#decorator-pattern)
27
- - [HOF pattern](#hof-higher-order-function-pattern)
27
+ - [Function pattern](#function-pattern)
28
28
  - [Migration](#migration)
29
29
  - [Core concepts](#core-concepts)
30
30
  - [Terminology](#terminology)
@@ -36,6 +36,8 @@
36
36
  - [Options & configuration](#options--configuration)
37
37
  - [Minimal mode](#minimal-mode)
38
38
  - [Strict mode](#strict-mode)
39
+ - [Hot module reload](#hot-module-reload)
40
+ - [Edge cases](#edge-cases)
39
41
  - [Benchmarks](#benchmarks)
40
42
  - [Bundle Size](#bundle-size)
41
43
  - [Tests](#tests)
@@ -57,7 +59,9 @@ yarn add @vicin/sigil
57
59
  pnpm add @vicin/sigil
58
60
  ```
59
61
 
60
- Requires TypeScript 5.0+ for decorators; HOFs work on older versions. Node.js 18+ recommended.
62
+ Requires TypeScript 5.0+ for decorators; attach functions work on older versions. Node.js 18+ recommended.
63
+
64
+ > No tsconfig changes are needed as we use **Stage 3 decorators** which are supported by default in TypeScript 5.0+
61
65
 
62
66
  ### Basic usage
63
67
 
@@ -91,26 +95,28 @@ After opting into the `Sigil` contract, labels are passed to child classes to un
91
95
 
92
96
  ##### Decorator pattern
93
97
 
94
- Apply a label with the `@WithSigil` decorator:
98
+ Apply a label with the `@AttachSigil` decorator:
95
99
 
96
100
  ```ts
97
- import { Sigil, WithSigil } from '@vicin/sigil';
101
+ import { Sigil, AttachSigil } from '@vicin/sigil';
98
102
 
99
- @WithSigil('@myorg/mypkg.User')
103
+ @AttachSigil('@myorg/mypkg.User')
100
104
  class User extends Sigil {}
101
105
  ```
102
106
 
103
- ##### HOF (Higher-Order Function) pattern
107
+ ##### Function pattern
104
108
 
105
- Apply a label using `withSigil` HOF:
109
+ Apply a label using `attachSigil` function:
106
110
 
107
111
  ```ts
108
- import { Sigil, withSigil } from '@vicin/sigil';
112
+ import { Sigil, attachSigil } from '@vicin/sigil';
109
113
 
110
- class _User extends Sigil {}
111
- const User = withSigil(_User, '@myorg/mypkg.User');
114
+ class User extends Sigil {}
115
+ attachSigil(User, '@myorg/mypkg.User');
112
116
  ```
113
117
 
118
+ > Note: Function pattern is susceptible to some [Edge case subtle pitfalls](#edge-cases) if not used appropriately, so we advise to use decorator pattern
119
+
114
120
  ### Migration
115
121
 
116
122
  Migrating old code into `Sigil` can be done with extra couple lines of code only:
@@ -149,12 +155,12 @@ Congratulations — you’ve opted into `Sigil` and you can start replacing `ins
149
155
 
150
156
  ### Purpose and Origins
151
157
 
152
- Sigil addresses issues in large monorepos and Domain-Driven Design (DDD):
158
+ Sigil addresses issues in large monorepos, HMR:
153
159
 
154
160
  - **Unreliable `instanceof`:** Bundling cause class redefinitions, breaking checks.
155
161
 
156
162
  ```ts
157
- // Can be broken in monorepo
163
+ // Can be broken in monorepo or HMR set-ups
158
164
  if (obj instanceof User) { ... }
159
165
 
160
166
  // With Sigil
@@ -162,7 +168,9 @@ if (User.isOfType(obj)) { ... } // This still works even if User was bundled twi
162
168
  if (User.isExactType(obj)) { ... } // Or check for exactly same constructor not its children
163
169
  ```
164
170
 
165
- - **Manual Branding Overhead:** Custom identifiers lead to boilerplate and maintenance issues, `Sigil` add reliable inheritance-aware nominal branding with just one line of code.
171
+ Also by utilizing unique passed labels it solves another problem in Domain-Driven Design (DDD):
172
+
173
+ - **Manual Branding Overhead:** Custom identifiers lead to boilerplate and maintenance issues, `Sigil` adds reliable inheritance-aware nominal branding with just one line of code.
166
174
 
167
175
  ```ts
168
176
  import { sigil } from '@vicin/sigil';
@@ -175,48 +183,43 @@ type test1 = User extends Sigil ? true : false; // true
175
183
  type test2 = Sigil extends User ? true : false; // false
176
184
  ```
177
185
 
178
- `Sigil` makes identity management **explicit** and **error-resistant** if defined the right way.
179
-
180
186
  ### Implementation Mechanics
181
187
 
182
188
  - **Runtime Contract:** Established via extending `Sigil` or using `Sigilify` mixin.
183
- - **Update metadata:** With each new child, use decorators or HOF to attach run-time metadata and `ExtendSigil` to update nominal type.
189
+ - **Update metadata:** With each new child, use decorator (`AttachSigil`) or function (`attachSigil`) to attach run-time metadata, also use `ExtendSigil` on `[sigil]` field to update nominal type.
184
190
 
185
191
  ```ts
186
- import { Sigil, WithSigil, sigil, ExtendSigil } from '@vicin/sigil';
192
+ import { Sigil, AttachSigil, sigil, ExtendSigil } from '@vicin/sigil';
187
193
 
188
- @WithSigil('@scope/package.MyClass') // <-- Run-time values update
194
+ @AttachSigil('@scope/package.MyClass') // <-- Run-time values update
189
195
  class MyClass extends Sigil {
190
196
  declare [sigil]: ExtendSigil<'MyClass', Sigil>; // <-- compile-time type update
191
197
  }
192
198
  ```
193
199
 
194
- You can avoid decorators and use HOF if needed:
200
+ You can avoid decorators and use normal functions if needed:
195
201
 
196
202
  ```ts
197
- import { Sigil, withSigil, sigil, ExtendSigil } from '@vicin/sigil';
203
+ import { Sigil, attachSigil, sigil, ExtendSigil } from '@vicin/sigil';
198
204
 
199
- class _MyClass extends Sigil {
205
+ class MyClass extends Sigil {
200
206
  declare [sigil]: ExtendSigil<'MyClass', Sigil>;
201
207
  }
202
208
 
203
- const MyClass = withSigil(_MyClass, '@scope/package.MyClass');
204
- type MyClass = InstanceType<typeof MyClass>;
209
+ attachSigil(MyClass, '@scope/package.MyClass');
205
210
  ```
206
211
 
207
- Note that you can't use `InstanceType` on `private` or `protected` classes, however you can use `GetPrototype<T>` in such cases.
208
-
209
212
  ### Example
210
213
 
211
214
  ```ts
212
- import { Sigil, WithSigil } from '@vicin/sigil';
215
+ import { Sigil, AttachSigil } from '@vicin/sigil';
213
216
 
214
- @WithSigil('@myorg/User')
217
+ @AttachSigil('@myorg/User')
215
218
  class User extends Sigil {
216
219
  declare [sigil]: ExtendSigil<'User', Sigil>;
217
220
  }
218
221
 
219
- @WithSigil('@myorg/Admin')
222
+ @AttachSigil('@myorg/Admin')
220
223
  class Admin extends User {
221
224
  declare [sigil]: ExtendSigil<'Admin', User>;
222
225
  }
@@ -247,51 +250,40 @@ type test1 = Admin extends User ? true : false; // true
247
250
  type test2 = User extends Admin ? true : false; // false
248
251
 
249
252
  // Passed label must be unique (enforced by Sigil) so can be used as stable Id for class
250
- // Also 'SigilLabelLineage' and 'SigilLabelSet' are useful for logging & debugging
253
+ // Also 'SigilLabelLineage' is useful for logging & debugging
251
254
  console.log(Admin.SigilLabel); // '@myorg/Admin'
252
255
  console.log(Admin.SigilEffectiveLabel); // '@myorg/Admin'
253
256
  console.log(Admin.SigilLabelLineage); // ['Sigil', '@myorg/User', '@myorg/Admin']
254
- console.log(Admin.SigilLabelSet); // Set(['Sigil', '@myorg/User', '@myorg/Admin'])
255
257
  console.log(admin.getSigilLabel()); // '@myorg/Admin'
256
258
  console.log(admin.getSigilEffectiveLabel()); // '@myorg/Admin'
257
259
  console.log(admin.getSigilLabelLineage()); // ['Sigil', '@myorg/User', '@myorg/Admin']
258
- console.log(admin.getSigilLabelSet()); // Set(['Sigil', '@myorg/User', '@myorg/Admin'])
259
260
  ```
260
261
 
261
262
  ### Errors & throws
262
263
 
263
264
  Run-time errors that can be thrown by `Sigil`:
264
265
 
265
- #### Double `Sigilify() / SigilifyAbstract()`
266
+ #### Double Sigilify
266
267
 
267
268
  ```ts
268
269
  class A {}
269
- const B = Sigilify(A, 'A');
270
- const C = Sigilify(B, 'B'); // Throws: [Sigil Error] Class 'Sigilified' with label 'A' is already sigilified
270
+ Sigilify(Sigilify(A, 'A'), 'B'); // Throws: [Sigil Error] Class 'Sigilified' with label 'A' is already sigilified
271
271
 
272
- abstract class AbsA {}
273
- const AbsB = SigilifyAbstract(AbsA, 'AbsA');
274
- const AbsC = SigilifyAbstract(AbsB, 'AbsB'); // Throws: [Sigil Error] Class 'Sigilified' with label 'AbsA' is already sigilified
275
- ```
276
-
277
- #### `@WithSigil() / withSigil()` on non-Sigil class
278
-
279
- ```ts
280
- @WithSigil('A')
281
- class A {} // Throws: [Sigil Error] 'WithSigil' decorator accept only Sigil classes but used on class 'A'
272
+ @AttachSigil('B')
273
+ @AttachSigil('A')
274
+ class A extends Sigil {} // Throws: [Sigil Error] Class 'A' with label 'A' is already sigilified
282
275
 
283
- withSigil(class A {}); // Throws: [Sigil Error] 'WithSigil' decorator accept only Sigil classes but used on class 'A'
276
+ class A extends Sigil {}
277
+ attachSigil(attachSigil(A, 'A'), 'B'); // Throws: [Sigil Error] Class 'A' with label 'A' is already sigilified
284
278
  ```
285
279
 
286
- #### Double `@WithSigil() / withSigil()`
280
+ #### `@AttachSigil() / attachSigil()` on non-Sigil class
287
281
 
288
282
  ```ts
289
- @WithSigil('B')
290
- @WithSigil('A')
291
- class A extends Sigil {} // Throws: [Sigil Error] Class 'A' with label 'A' is already sigilified
283
+ @AttachSigil('A') // Throws: [Sigil Error] 'AttachSigil' decorator accept only Sigil classes but used on class 'A'
284
+ class A {}
292
285
 
293
- class _A extends Sigil {}
294
- withSigil(withSigil(_A, 'A'), 'B'); // Throws: [Sigil Error] Class 'A' with label 'A' is already sigilified
286
+ attachSigil(class A {}); // Throws: [Sigil Error] 'AttachSigil' function accept only Sigil classes but used on class 'A'
295
287
  ```
296
288
 
297
289
  #### No label is passed with `autofillLabels: false`
@@ -306,10 +298,10 @@ new A(); // Throws: [Sigil Error] Class 'A' is not sigilified, Make sure to sigi
306
298
  #### Same label is passed twice to `Sigil`
307
299
 
308
300
  ```ts
309
- @WithSigil('Label')
301
+ @AttachSigil('Label')
310
302
  class A extends Sigil {}
311
303
 
312
- @WithSigil('Label')
304
+ @AttachSigil('Label')
313
305
  class B extends Sigil {} // Throws: [Sigil Error] Passed label 'Label' to class 'B' is re-used, passed labels must be unique
314
306
  ```
315
307
 
@@ -318,15 +310,15 @@ class B extends Sigil {} // Throws: [Sigil Error] Passed label 'Label' to class
318
310
  ```ts
319
311
  updateSigilOptions({ labelValidation: RECOMMENDED_LABEL_REGEX });
320
312
 
321
- @WithSigil('InvalidLabel')
313
+ @AttachSigil('InvalidLabel')
322
314
  class A extends Sigil {} // Throws: [Sigil Error] Invalid Sigil label 'InvalidLabel'. Make sure that supplied label matches validation regex or function
323
315
  ```
324
316
 
325
317
  #### Using '@Sigil-auto' prefix
326
318
 
327
319
  ```ts
328
- @WithSigil('@Sigil-auto:label')
329
- class X extends Sigil {} // Throws: '@Sigil-auto' is a prefex reserved by the library
320
+ @AttachSigil('@Sigil-auto:label')
321
+ class X extends Sigil {} // Throws: '@Sigil-auto' is a prefix reserved by the library
330
322
  ```
331
323
 
332
324
  #### Invalid options passed to `updateOptions`
@@ -334,6 +326,7 @@ class X extends Sigil {} // Throws: '@Sigil-auto' is a prefex reserved by the li
334
326
  ```ts
335
327
  updateSigilOptions({ autofillLabels: {} as any }); // Throws: 'updateSigilOptions.autofillLabels' must be boolean
336
328
  updateSigilOptions({ labelValidation: 123 as any }); // Throws: 'updateSigilOptions.labelValidation' must be null, function or RegExp
329
+ updateSigilOptions({ skipLabelUniquenessCheck: 'str' as any }); // Throws: 'updateSigilOptions.skipLabelUniquenessCheck' must be boolean
337
330
  ```
338
331
 
339
332
  ---
@@ -351,15 +344,15 @@ updateSigilOptions({ labelValidation: 123 as any }); // Throws: 'updateSigilOpti
351
344
  - `SigilError`
352
345
 
353
346
  - **Decorator:**
354
- - `WithSigil(label, opts?)`
347
+ - `AttachSigil(label, opts?)`
355
348
 
356
- - **HOFs:**
357
- - `withSigil(Class, label, opts?)`
349
+ - **Attach function:**
350
+ - `attachSigil(Class, label, opts?)`
358
351
 
359
352
  - **Helpers:**
360
353
  - `isSigilCtor(ctor)`
361
354
  - `isSigilInstance(inst)`
362
- - `getSigilLabels(includeAuto?)`
355
+ - `getSigilLabels()`
363
356
 
364
357
  - **Options:**
365
358
  - `updateSigilOptions(opts)`
@@ -380,11 +373,11 @@ updateSigilOptions({ labelValidation: 123 as any }); // Throws: 'updateSigilOpti
380
373
  - `SigilError`: an `Error` class decorated with a `Sigil` so it can be identified at runtime.
381
374
  - `Sigilify(Base, label, opts?)`: mixin function that returns a new constructor with `Sigil` types and instance helpers.
382
375
  - `SigilifyAbstract(Base, label, opts?)`: Same as `Sigilify` but for abstract classes.
383
- - `WithSigil(label, opts?)`: class decorator that attaches `Sigil` metadata at declaration time.
384
- - `withSigil(Class, label, opts?)`: HOF that validates and decorates an existing class constructor.
376
+ - `AttachSigil(label, opts?)`: class decorator that attaches `Sigil` metadata at declaration time.
377
+ - `attachSigil(Class, label, opts?)`: function that validates and decorates an existing class constructor.
385
378
  - `isSigilCtor(value)`: `true` if `value` is a `Sigil` constructor.
386
379
  - `isSigilInstance(value)`: `true` if `value` is an instance of a `Sigil` constructor.
387
- - `getSigilLabels(includeAuto?)`: Get all `Sigil` labels registered, can include auto-generated labels as well.
380
+ - `getSigilLabels()`: Get `Sigil` labels registered.
388
381
  - `updateSigilOptions(opts)`: change global runtime options of `Sigil` library (e.g., `autofillLabels`).
389
382
  - `RECOMMENDED_LABEL_REGEX`: regex that ensures structure of `@scope/package.ClassName` to all labels, it's advised to use it as your `SigilOptions.labelValidation`
390
383
 
@@ -395,7 +388,6 @@ When a constructor is sigilified it will expose the following **static** getters
395
388
  - `SigilLabel` — the identity label string.
396
389
  - `SigilEffectiveLabel` — the human label string.
397
390
  - `SigilLabelLineage` — readonly array of labels representing parent → child for debugging.
398
- - `SigilLabelSet` — readonly `Set<string>` of sigil labels for debugging.
399
391
  - `isOfType(other)` — check if other is an instance of this constructor or its children.
400
392
  - `isExactType(other) `— check if other is an instance exactly this constructor.
401
393
 
@@ -404,7 +396,6 @@ Instances of sigilified classes expose instance helpers:
404
396
  - `getSigilLabel()` — returns the identity label.
405
397
  - `getSigilEffectiveLabel()` — returns the human label.
406
398
  - `getSigilLabelLineage()` — returns lineage array.
407
- - `getSigilLabelSet()` — returns readonly Set.
408
399
  - `isOfType(other)` — check if other is an instance of the same class or its children as this.
409
400
  - `isExactType(other) `— check if other is an instance exactly the same constructor.
410
401
 
@@ -420,21 +411,22 @@ import { updateSigilOptions } from '@vicin/sigil';
420
411
  updateSigilOptions({
421
412
  autofillLabels: true, // Automatically label unlabeled subclasses
422
413
  labelValidation: null, // Function or regex, Enforce label format
414
+ skipLabelUniquenessCheck: false, // Skip uniqueness check for labels, should be used in HMR set-ups only
423
415
  });
424
416
  ```
425
417
 
426
- Values defined in previous example are defaults, per-class overrides available in mixin, decorators, and HOFs.
418
+ Values defined in previous example are defaults, per-class overrides available in mixin and attach function / decorator.
427
419
 
428
420
  ---
429
421
 
430
422
  ## Minimal mode
431
423
 
432
- You can ignore all decorators and HOFs and just make base class extend `Sigil`:
424
+ By default `Sigil` works with minimal mode, You can ignore all decorators and functions and just make base class extend `Sigil`:
433
425
 
434
426
  ```ts
435
427
  import { Sigil, updateSigilOptions } from '@vicin/sigil';
436
428
 
437
- // No decorators or HOF needed to use 'isOfType' ('instanceof' replacement)
429
+ // No decorators or functions needed to use 'isOfType' ('instanceof' replacement)
438
430
  class A extends Sigil {}
439
431
  class B extends A {}
440
432
  class C extends B {}
@@ -454,6 +446,80 @@ Now if you forgot to pass a label error is thrown at the moment you create class
454
446
 
455
447
  ---
456
448
 
449
+ ## Hot module reload
450
+
451
+ HMR can cause class re-definitions, which will throw in default `Sigil` set-up as same label will be passed multiple times triggering duplicate label error.
452
+ To avoid this you can set global options to skip label uniqueness check at the start of app:
453
+
454
+ ```ts
455
+ import { updateSigilOptions } from '@vicin/sigil';
456
+
457
+ updateSigilOptions({ skipLabelUniquenessCheck: true });
458
+ ```
459
+
460
+ But this can cause unexpected behavior if same label is used for two different classes as checks are disabled globally.
461
+ If you need more strict mode you can pass this options to the re-loaded class only:
462
+
463
+ ```ts
464
+ @AttachSigil('HmrClassLabel', { skipLabelUniquenessCheck: true })
465
+ class HmrClass extends Sigil {}
466
+ ```
467
+
468
+ With this approach `skipLabelUniquenessCheck` affects only `HmrClass`, and if `HmrClassLabel` or any other label is re-used error is thrown immediately
469
+
470
+ ---
471
+
472
+ ## Edge cases
473
+
474
+ ### Accessing Sigil `metadata` before running 'attachSigil' function
475
+
476
+ If you didn't make sure that `attachSigil` runs right after class declaration and used one of `Sigil` methods this will occur:
477
+
478
+ ```ts
479
+ class A extends Sigil {}
480
+
481
+ console.log(A.SigilLabel); // returns auto-generated label (e.g. @Sigil-auto:A:6:a3f15bhl) or throws in strict mode
482
+
483
+ attachSigil(A, 'A');
484
+
485
+ console.log(A.SigilLabel); // A
486
+ ```
487
+
488
+ To avoid this bug entirely you can use the return of `attachSigil` in your code so you are enforced to respect order:
489
+
490
+ ```ts
491
+ class _A extends Sigil {}
492
+
493
+ const A = attachSigil(_A, 'A');
494
+ type A = InstanceType<typeof A>;
495
+
496
+ console.log(A.SigilLabel); // A
497
+ ```
498
+
499
+ Note that you can't use `InstanceType` on `private` or `protected` classes, however you can use `GetPrototype<T>` in such cases.
500
+
501
+ #### Static blocks & IIFE static initializer
502
+
503
+ Decorators ensure that metadata is appended before static blocks or IIFE static initializers, however `attachSigil` function runs after them so accessing label inside them will return auto-generated label or throw:
504
+
505
+ ```ts
506
+ class A extends Sigil {
507
+ static IIFE = (() => {
508
+ const label = A.SigilLabel; // returns auto-generated label (e.g. @Sigil-auto:A:6:a3f15bhl) or throws in strict mode
509
+ })();
510
+
511
+ static {
512
+ const label = this.SigilLabel; // returns auto-generated label (e.g. @Sigil-auto:A:6:a3f15bhl) or throws in strict mode
513
+ }
514
+ }
515
+
516
+ attachSigil(A, 'A');
517
+ ```
518
+
519
+ This behavior can't be avoided, so make sure not to call any `Sigil` method inside them or move to decorators (`@AttachSigil`)
520
+
521
+ ---
522
+
457
523
  ## Benchmarks
458
524
 
459
525
  Sigil is built for **real-world performance**. Below are the latest micro-benchmark results (run on **Node.js v20.12.0**).
@@ -503,7 +569,7 @@ npm run bench
503
569
 
504
570
  ## Bundle Size
505
571
 
506
- **Less than 1.6 KB (1.51 KB)** (minified + Brotli, including all dependencies)
572
+ **Less than 1.6 KB (1.52 KB)** (minified + Brotli, including all dependencies)
507
573
 
508
574
  This makes Sigil one of the smallest full-featured solutions for nominal typing + reliable runtime identity.
509
575
 
@@ -520,7 +586,7 @@ npm run size
520
586
 
521
587
  ## Tests
522
588
 
523
- Reliability is a core pillar of `Sigil`. The library is backed by a comprehensive suite of unit tests that cover everything from basic mixins to lazy evaluation.
589
+ Reliability is a core pillar of `Sigil`. The library is backed by a comprehensive suite of unit tests ( 71 total tests ) that cover everything from basic mixins to edge cases.
524
590
 
525
591
  **Coverage Status**
526
592
 
@@ -535,11 +601,12 @@ We maintain **100%** test coverage across the entire codebase to ensure that run
535
601
 
536
602
  **Key Test Areas**
537
603
 
538
- - **Mixins, Decorators & HOFs:** Validating `Sigilify`, `WithSigil` and `withSigil` behaviors.
604
+ - **Mixins, Attach function & decorator:** Validating `Sigilify`, `AttachSigil` and `attachSigil` behaviors.
539
605
  - **Sigil methods:** Ensuring `Sigil` class methods (e.g. `SigilLabel`, `getSigilLabel`) work as expected.
540
- - **Lazy Evaluation:** Ensuring metadata is attached before being accessed via `Sigil` methods.
606
+ - **Lazy Evaluation:** Ensuring metadata is attached before being accessed via `Sigil` methods even when no attach function or decorator is used.
541
607
  - **Lineage:** Verifying that `isOfType` and `isExactType` work across complex inheritance chains.
542
608
  - **Error Handling:** Strict validation for all errors and throws.
609
+ - **Edge cases**: Known edge cases.
543
610
 
544
611
  **Running Tests**
545
612