@xivdyetools/types 1.0.0 → 1.1.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/dist/color/branded.d.ts +32 -5
- package/dist/color/branded.d.ts.map +1 -1
- package/dist/color/branded.js +33 -5
- package/dist/color/branded.js.map +1 -1
- package/dist/dye/dye.d.ts +9 -0
- package/dist/dye/dye.d.ts.map +1 -1
- package/dist/dye/dye.js +9 -0
- package/dist/dye/dye.js.map +1 -1
- package/dist/preset/core.d.ts +7 -1
- package/dist/preset/core.d.ts.map +1 -1
- package/package.json +9 -4
package/dist/color/branded.d.ts
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
* Branded types prevent accidental type confusion between similar types.
|
|
5
5
|
* For example, a DyeId cannot be accidentally used where a plain number is expected.
|
|
6
6
|
*
|
|
7
|
+
* ## TYPES-101: Runtime Validation Limitation
|
|
8
|
+
*
|
|
9
|
+
* **Important:** Branded types in TypeScript rely on runtime validation via helper
|
|
10
|
+
* functions. The type system cannot enforce that all values are created through
|
|
11
|
+
* these helpers. Developers can bypass validation using type assertions:
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // UNSAFE: Bypasses validation
|
|
15
|
+
* const unsafeHex = "#invalid" as HexColor;
|
|
16
|
+
*
|
|
17
|
+
* // SAFE: Uses validation
|
|
18
|
+
* const safeHex = createHexColor("#FF0000");
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* **Best Practice:** Always use the `create*` helper functions to ensure values
|
|
22
|
+
* are validated. Never use type assertions (`as HexColor`) to create branded values.
|
|
23
|
+
*
|
|
7
24
|
* @module color/branded
|
|
8
25
|
*/
|
|
9
26
|
/**
|
|
@@ -38,7 +55,12 @@ export declare function createHexColor(hex: string): HexColor;
|
|
|
38
55
|
* Dye ID (branded type for type safety)
|
|
39
56
|
*
|
|
40
57
|
* Prevents accidental mixing of dye IDs with other numeric values.
|
|
41
|
-
* Valid dye IDs
|
|
58
|
+
* Valid dye IDs include:
|
|
59
|
+
* - Regular FFXIV dye IDs: 1-200
|
|
60
|
+
* - Synthetic IDs for Facewear dyes: negative numbers <= -1000
|
|
61
|
+
*
|
|
62
|
+
* Synthetic IDs are generated by DyeDatabase for Facewear dyes that
|
|
63
|
+
* lack real itemIDs in the game data.
|
|
42
64
|
*/
|
|
43
65
|
export type DyeId = number & {
|
|
44
66
|
readonly __brand: 'DyeId';
|
|
@@ -46,14 +68,19 @@ export type DyeId = number & {
|
|
|
46
68
|
/**
|
|
47
69
|
* Helper to create branded DyeId type with validation
|
|
48
70
|
*
|
|
49
|
-
*
|
|
71
|
+
* TYPES-102: Updated to accept synthetic negative IDs for Facewear dyes.
|
|
72
|
+
* Synthetic IDs are generated as -(1000 + nameHash) to avoid collision
|
|
73
|
+
* with real item IDs.
|
|
74
|
+
*
|
|
75
|
+
* @param id - Numeric dye ID (1-200 for regular, <= -1000 for synthetic)
|
|
50
76
|
* @returns Branded DyeId or null if invalid
|
|
51
77
|
*
|
|
52
78
|
* @example
|
|
53
79
|
* ```typescript
|
|
54
|
-
* const dyeId = createDyeId(1);
|
|
55
|
-
* const
|
|
56
|
-
* const
|
|
80
|
+
* const dyeId = createDyeId(1); // Returns 1 as DyeId
|
|
81
|
+
* const synthetic = createDyeId(-1500); // Returns -1500 as DyeId (Facewear)
|
|
82
|
+
* const invalid = createDyeId(0); // Returns null
|
|
83
|
+
* const bad = createDyeId(999); // Returns null
|
|
57
84
|
* ```
|
|
58
85
|
*/
|
|
59
86
|
export declare function createDyeId(id: number): DyeId | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branded.d.ts","sourceRoot":"","sources":["../../src/color/branded.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"branded.d.ts","sourceRoot":"","sources":["../../src/color/branded.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAWpD;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAWpD;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC;AAEvD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAI1C;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAErE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAG/D"}
|
package/dist/color/branded.js
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
* Branded types prevent accidental type confusion between similar types.
|
|
5
5
|
* For example, a DyeId cannot be accidentally used where a plain number is expected.
|
|
6
6
|
*
|
|
7
|
+
* ## TYPES-101: Runtime Validation Limitation
|
|
8
|
+
*
|
|
9
|
+
* **Important:** Branded types in TypeScript rely on runtime validation via helper
|
|
10
|
+
* functions. The type system cannot enforce that all values are created through
|
|
11
|
+
* these helpers. Developers can bypass validation using type assertions:
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // UNSAFE: Bypasses validation
|
|
15
|
+
* const unsafeHex = "#invalid" as HexColor;
|
|
16
|
+
*
|
|
17
|
+
* // SAFE: Uses validation
|
|
18
|
+
* const safeHex = createHexColor("#FF0000");
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* **Best Practice:** Always use the `create*` helper functions to ensure values
|
|
22
|
+
* are validated. Never use type assertions (`as HexColor`) to create branded values.
|
|
23
|
+
*
|
|
7
24
|
* @module color/branded
|
|
8
25
|
*/
|
|
9
26
|
/**
|
|
@@ -36,18 +53,29 @@ export function createHexColor(hex) {
|
|
|
36
53
|
/**
|
|
37
54
|
* Helper to create branded DyeId type with validation
|
|
38
55
|
*
|
|
39
|
-
*
|
|
56
|
+
* TYPES-102: Updated to accept synthetic negative IDs for Facewear dyes.
|
|
57
|
+
* Synthetic IDs are generated as -(1000 + nameHash) to avoid collision
|
|
58
|
+
* with real item IDs.
|
|
59
|
+
*
|
|
60
|
+
* @param id - Numeric dye ID (1-200 for regular, <= -1000 for synthetic)
|
|
40
61
|
* @returns Branded DyeId or null if invalid
|
|
41
62
|
*
|
|
42
63
|
* @example
|
|
43
64
|
* ```typescript
|
|
44
|
-
* const dyeId = createDyeId(1);
|
|
45
|
-
* const
|
|
46
|
-
* const
|
|
65
|
+
* const dyeId = createDyeId(1); // Returns 1 as DyeId
|
|
66
|
+
* const synthetic = createDyeId(-1500); // Returns -1500 as DyeId (Facewear)
|
|
67
|
+
* const invalid = createDyeId(0); // Returns null
|
|
68
|
+
* const bad = createDyeId(999); // Returns null
|
|
47
69
|
* ```
|
|
48
70
|
*/
|
|
49
71
|
export function createDyeId(id) {
|
|
50
|
-
if (!Number.isInteger(id)
|
|
72
|
+
if (!Number.isInteger(id)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
// TYPES-102: Accept regular IDs (1-200) or synthetic Facewear IDs (<= -1000)
|
|
76
|
+
const isRegularId = id >= 1 && id <= 200;
|
|
77
|
+
const isSyntheticId = id <= -1000;
|
|
78
|
+
if (!isRegularId && !isSyntheticId) {
|
|
51
79
|
return null;
|
|
52
80
|
}
|
|
53
81
|
return id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branded.js","sourceRoot":"","sources":["../../src/color/branded.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"branded.js","sourceRoot":"","sources":["../../src/color/branded.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAYH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,2CAA2C;IAC3C,IAAI,CAAC,oCAAoC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,oCAAoC,CAAC,CAAC;IACxF,CAAC;IACD,iEAAiE;IACjE,MAAM,UAAU,GACd,GAAG,CAAC,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE;QACzE,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IACxB,OAAO,UAAsB,CAAC;AAChC,CAAC;AAeD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,6EAA6E;IAC7E,MAAM,WAAW,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC;IACzC,MAAM,aAAa,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC;IAClC,IAAI,CAAC,WAAW,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAW,CAAC;AACrB,CAAC;AASD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,2BAA2B;IAC3B,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC7C,OAAO,UAAiB,CAAC;AAC3B,CAAC;AASD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;IACvD,OAAO,OAAqB,CAAC;AAC/B,CAAC"}
|
package/dist/dye/dye.d.ts
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* FFXIV dye object definitions with color and metadata.
|
|
5
5
|
*
|
|
6
|
+
* ## TYPES-103: Field Presence Guarantee
|
|
7
|
+
*
|
|
8
|
+
* All fields in the `Dye` interface are required and non-nullable.
|
|
9
|
+
* The dye database (`xivdyetools-core`) ensures complete data initialization.
|
|
10
|
+
*
|
|
11
|
+
* **For consumers:** You can safely access all Dye fields without null checks.
|
|
12
|
+
* If you're receiving dye data from external sources (JSON APIs, user input),
|
|
13
|
+
* validate the data matches the interface before casting to `Dye`.
|
|
14
|
+
*
|
|
6
15
|
* @module dye/dye
|
|
7
16
|
*/
|
|
8
17
|
import type { RGB, HSV } from '../color/rgb.js';
|
package/dist/dye/dye.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dye.d.ts","sourceRoot":"","sources":["../../src/dye/dye.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dye.d.ts","sourceRoot":"","sources":["../../src/dye/dye.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,GAAG;IAClB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IAEf,4BAA4B;IAC5B,EAAE,EAAE,MAAM,CAAC;IAEX,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IAEb,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IAEZ,+BAA+B;IAC/B,GAAG,EAAE,GAAG,CAAC;IAET,+BAA+B;IAC/B,GAAG,EAAE,GAAG,CAAC;IAET,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IAEjB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IAEH,qCAAqC;IACrC,UAAU,EAAE,OAAO,CAAC;IAEpB,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAElB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAEhB,wDAAwD;IACxD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAa,SAAQ,GAAG;IACvC,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAgB,SAAQ,GAAG;IAC1C,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/dye/dye.js
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* FFXIV dye object definitions with color and metadata.
|
|
5
5
|
*
|
|
6
|
+
* ## TYPES-103: Field Presence Guarantee
|
|
7
|
+
*
|
|
8
|
+
* All fields in the `Dye` interface are required and non-nullable.
|
|
9
|
+
* The dye database (`xivdyetools-core`) ensures complete data initialization.
|
|
10
|
+
*
|
|
11
|
+
* **For consumers:** You can safely access all Dye fields without null checks.
|
|
12
|
+
* If you're receiving dye data from external sources (JSON APIs, user input),
|
|
13
|
+
* validate the data matches the interface before casting to `Dye`.
|
|
14
|
+
*
|
|
6
15
|
* @module dye/dye
|
|
7
16
|
*/
|
|
8
17
|
export {};
|
package/dist/dye/dye.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dye.js","sourceRoot":"","sources":["../../src/dye/dye.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dye.js","sourceRoot":"","sources":["../../src/dye/dye.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
package/dist/preset/core.d.ts
CHANGED
|
@@ -12,8 +12,14 @@ import type { Dye } from '../dye/dye.js';
|
|
|
12
12
|
export type PresetCategory = 'jobs' | 'grand-companies' | 'seasons' | 'events' | 'aesthetics' | 'community';
|
|
13
13
|
/**
|
|
14
14
|
* Status of a preset submission in the moderation workflow
|
|
15
|
+
*
|
|
16
|
+
* - `pending`: Awaiting moderation review
|
|
17
|
+
* - `approved`: Approved and visible in listings
|
|
18
|
+
* - `rejected`: Rejected by moderator
|
|
19
|
+
* - `flagged`: Auto-flagged by content moderation
|
|
20
|
+
* - `hidden`: Hidden due to user ban (restored on unban)
|
|
15
21
|
*/
|
|
16
|
-
export type PresetStatus = 'pending' | 'approved' | 'rejected' | 'flagged';
|
|
22
|
+
export type PresetStatus = 'pending' | 'approved' | 'rejected' | 'flagged' | 'hidden';
|
|
17
23
|
/**
|
|
18
24
|
* Sort options for preset listings
|
|
19
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/preset/core.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,iBAAiB,GACjB,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,WAAW,CAAC;AAEhB
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/preset/core.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,iBAAiB,GACjB,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,WAAW,CAAC;AAEhB;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,0BAA0B;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IAEpB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAErB,oDAAoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IAEX,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,sCAAsC;IACtC,QAAQ,EAAE,cAAc,CAAC;IAEzB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf,sBAAsB;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,+DAA+D;IAC/D,YAAY,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAEhB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IAEpB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAEjD,0BAA0B;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xivdyetools/types",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Shared TypeScript type definitions for the xivdyetools ecosystem",
|
|
5
5
|
"author": "XIV Dye Tools",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,11 +50,16 @@
|
|
|
50
50
|
"build": "tsc -p tsconfig.build.json",
|
|
51
51
|
"type-check": "tsc --noEmit",
|
|
52
52
|
"clean": "rimraf dist",
|
|
53
|
-
"prepublishOnly": "npm run clean && npm run build"
|
|
53
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"test:coverage": "vitest run --coverage"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|
|
56
|
-
"
|
|
57
|
-
"rimraf": "^5.0.5"
|
|
59
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
60
|
+
"rimraf": "^5.0.5",
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"vitest": "^4.0.15"
|
|
58
63
|
},
|
|
59
64
|
"engines": {
|
|
60
65
|
"node": ">=18.0.0"
|