@vegait/accounts-sdk 0.1.0-beta.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/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @vegait/accounts-sdk
2
+
3
+ Cola de integracao frontend do Accounts: persona, catalogo de produtos VEGA,
4
+ tipos de claims e o adapter da casca de identidade (`buildIdentityModel`).
5
+ Puro TS — sem React, sem dependencia do design system.
6
+
7
+ ## Uso
8
+
9
+ ```ts
10
+ import { buildIdentityModel, decodeAccessToken } from "@vegait/accounts-sdk";
11
+
12
+ const claims = decodeAccessToken(accessToken);
13
+ const model = buildIdentityModel(claims, {
14
+ currentProductCode: "accounts",
15
+ urls: { "media-hub": mediaHubUrl }, // o host resolve o env; o SDK nunca toca process.env
16
+ });
17
+ // host mapeia `model` -> props da IdentityBar do @vegait/lyra-ds-v2
18
+ ```
19
+
20
+ ## ⚠️ Seguranca: `derivePersona` NAO e autorizacao
21
+
22
+ `derivePersona` e o `IdentityModel` servem para **gating de apresentacao**
23
+ (o que mostrar/esconder na UI). **Nunca** os use como controle de acesso.
24
+ A autorizacao real e feita **server-side** (scopes/claims OIDC, RBAC+ABAC no
25
+ `accounts.api`). Gating de UI e defense-in-depth, nunca o unico controle.
package/dist/index.cjs ADDED
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PRODUCT_CATALOG: () => PRODUCT_CATALOG,
24
+ buildIdentityModel: () => buildIdentityModel,
25
+ decodeAccessToken: () => decodeAccessToken,
26
+ decodeJwtPayload: () => decodeJwtPayload,
27
+ derivePersona: () => derivePersona,
28
+ getAccessibleProducts: () => getAccessibleProducts,
29
+ getCatalogWithAccess: () => getCatalogWithAccess,
30
+ normalizeClaimArray: () => normalizeClaimArray,
31
+ parseJsonArrayClaim: () => parseJsonArrayClaim,
32
+ resolveProduct: () => resolveProduct
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/claims.ts
37
+ function decodeJwtPayload(token) {
38
+ const parts = token.split(".");
39
+ if (parts.length !== 3) {
40
+ throw new Error("Invalid JWT format");
41
+ }
42
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
43
+ const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
44
+ const binary = atob(padded);
45
+ const bytes = new Uint8Array(binary.length);
46
+ for (let i = 0; i < binary.length; i++) {
47
+ bytes[i] = binary.charCodeAt(i);
48
+ }
49
+ const json = new TextDecoder("utf-8").decode(bytes);
50
+ return JSON.parse(json);
51
+ }
52
+ function normalizeClaimArray(value) {
53
+ if (!value) return [];
54
+ return Array.isArray(value) ? value : [value];
55
+ }
56
+ function parseJsonArrayClaim(value) {
57
+ if (!value) return [];
58
+ try {
59
+ const parsed = JSON.parse(value);
60
+ return Array.isArray(parsed) ? parsed.filter((v) => typeof v === "string") : [];
61
+ } catch {
62
+ return [];
63
+ }
64
+ }
65
+ function decodeAccessToken(token) {
66
+ if (!token) return null;
67
+ try {
68
+ return decodeJwtPayload(token);
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+
74
+ // src/persona.ts
75
+ function derivePersona(claims) {
76
+ if (claims?.platform_role) return "vega";
77
+ if (claims?.can_manage_scope_members === "true") return "gestor";
78
+ return "usuario";
79
+ }
80
+
81
+ // src/productCatalog.ts
82
+ var PRODUCT_CATALOG = {
83
+ accounts: {
84
+ code: "accounts",
85
+ name: "Accounts",
86
+ description: "Identidade unificada do ecossistema",
87
+ url: "/",
88
+ accent: "#1F2E52",
89
+ ssoEnabled: true
90
+ // e o proprio IdP; excluido da gaveta de qualquer forma
91
+ },
92
+ "media-hub": {
93
+ code: "media-hub",
94
+ name: "Media Hub",
95
+ description: "Sinalizacao digital nas unidades",
96
+ url: "https://media-hub.vega.com.br/",
97
+ accent: "#EF802D",
98
+ ssoEnabled: true
99
+ },
100
+ sig: {
101
+ code: "sig",
102
+ name: "SIG",
103
+ description: "Housekeeping e governanca das unidades",
104
+ url: "https://sig.app.vegait.com/",
105
+ accent: "#B0C925",
106
+ ssoEnabled: true
107
+ },
108
+ gdi: {
109
+ code: "gdi",
110
+ name: "GDI",
111
+ description: "Gestao de dispositivos e internet",
112
+ url: "https://gdi.vega.com.br/",
113
+ accent: "#7B8B7E",
114
+ ssoEnabled: false
115
+ },
116
+ sim: {
117
+ code: "sim",
118
+ name: "SIM",
119
+ description: "Service Issue Management",
120
+ url: "https://sim.vega.com.br/",
121
+ accent: "#5B7DB1",
122
+ ssoEnabled: false
123
+ },
124
+ hxp: {
125
+ code: "hxp",
126
+ name: "HXP",
127
+ description: "Hotel Experience Platform",
128
+ url: "https://hxp.vega.com.br/",
129
+ accent: "#9B6A4F",
130
+ ssoEnabled: false
131
+ },
132
+ feedback: {
133
+ code: "feedback",
134
+ name: "Feedback",
135
+ description: "Pesquisas e NPS de hospedes",
136
+ url: "https://feedback.vega.com.br/",
137
+ accent: "#C26A8C",
138
+ ssoEnabled: false
139
+ },
140
+ behavior: {
141
+ code: "behavior",
142
+ name: "Behavior",
143
+ description: "Inteligencia comportamental",
144
+ url: "https://behavior.vega.com.br/",
145
+ accent: "#4A7C7E",
146
+ ssoEnabled: false
147
+ }
148
+ };
149
+ var LEGACY_CODE_ALIASES = {
150
+ governanca: "sig"
151
+ };
152
+ function resolveProduct(code, urls) {
153
+ const canonical = LEGACY_CODE_ALIASES[code] ?? code;
154
+ const base = PRODUCT_CATALOG[canonical] ?? {
155
+ code,
156
+ name: code,
157
+ description: "",
158
+ url: "#",
159
+ accent: "#1F2E52",
160
+ ssoEnabled: false
161
+ };
162
+ const override = urls?.[canonical];
163
+ return override ? { ...base, url: override } : base;
164
+ }
165
+ function getCatalogWithAccess(claims, opts) {
166
+ const urls = opts?.urls;
167
+ const isVega = claims != null && derivePersona(claims) === "vega";
168
+ const accessibleCodes = new Set(
169
+ parseJsonArrayClaim(claims?.products).map(
170
+ (code) => LEGACY_CODE_ALIASES[code] ?? code
171
+ )
172
+ );
173
+ return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => ({
174
+ ...urls?.[p.code] ? { ...p, url: urls[p.code] } : p,
175
+ accessible: isVega || accessibleCodes.has(p.code)
176
+ }));
177
+ }
178
+ function getAccessibleProducts(claims, opts) {
179
+ if (!claims) return [];
180
+ const urls = opts?.urls;
181
+ if (derivePersona(claims) === "vega") {
182
+ return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts" && p.ssoEnabled).map((p) => urls?.[p.code] ? { ...p, url: urls[p.code] } : p);
183
+ }
184
+ return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.ssoEnabled);
185
+ }
186
+
187
+ // src/identityModel.ts
188
+ function buildIdentityModel(claims, cfg) {
189
+ const persona = derivePersona(claims);
190
+ return {
191
+ persona,
192
+ user: { name: claims?.name ?? "", email: claims?.email ?? "" },
193
+ products: getAccessibleProducts(claims, { urls: cfg?.urls }),
194
+ catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),
195
+ currentProductCode: cfg?.currentProductCode,
196
+ capabilities: {
197
+ manageUsers: persona === "gestor" || persona === "vega",
198
+ adminConsole: persona === "vega",
199
+ switchContext: persona === "gestor" || persona === "vega",
200
+ manageNetwork: persona === "gestor"
201
+ }
202
+ };
203
+ }
204
+ // Annotate the CommonJS export names for ESM import in node:
205
+ 0 && (module.exports = {
206
+ PRODUCT_CATALOG,
207
+ buildIdentityModel,
208
+ decodeAccessToken,
209
+ decodeJwtPayload,
210
+ derivePersona,
211
+ getAccessibleProducts,
212
+ getCatalogWithAccess,
213
+ normalizeClaimArray,
214
+ parseJsonArrayClaim,
215
+ resolveProduct
216
+ });
@@ -0,0 +1,68 @@
1
+ declare function decodeJwtPayload<T = Record<string, unknown>>(token: string): T;
2
+ type AccountsClaims = {
3
+ sub?: string;
4
+ email?: string;
5
+ name?: string;
6
+ platform_role?: string;
7
+ can_manage_scope_members?: string;
8
+ org_id?: string | string[];
9
+ unit_ids?: string;
10
+ active_unit_id?: string;
11
+ products?: string;
12
+ exp?: number;
13
+ iat?: number;
14
+ iss?: string;
15
+ aud?: string | string[];
16
+ };
17
+ declare function normalizeClaimArray(value: string | string[] | undefined): string[];
18
+ declare function parseJsonArrayClaim(value: string | undefined): string[];
19
+ declare function decodeAccessToken(token: string | null | undefined): AccountsClaims | null;
20
+
21
+ type Persona = "usuario" | "gestor" | "vega";
22
+ declare function derivePersona(claims: AccountsClaims | null): Persona;
23
+
24
+ type ProductMeta = {
25
+ code: string;
26
+ name: string;
27
+ description: string;
28
+ url: string;
29
+ accent: string;
30
+ ssoEnabled: boolean;
31
+ };
32
+ declare const PRODUCT_CATALOG: Record<string, ProductMeta>;
33
+ declare function resolveProduct(code: string, urls?: Record<string, string>): ProductMeta;
34
+ type CatalogProduct = ProductMeta & {
35
+ accessible: boolean;
36
+ };
37
+ declare function getCatalogWithAccess(claims: AccountsClaims | null, opts?: {
38
+ urls?: Record<string, string>;
39
+ }): CatalogProduct[];
40
+ declare function getAccessibleProducts(claims: AccountsClaims | null, opts?: {
41
+ urls?: Record<string, string>;
42
+ }): ProductMeta[];
43
+
44
+ type IdentityModel = {
45
+ persona: Persona;
46
+ user: {
47
+ name: string;
48
+ email: string;
49
+ };
50
+ products: ProductMeta[];
51
+ /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle
52
+ * mostrar tudo e desabilitar o que a persona nao acessa. `products`
53
+ * permanece so-acessiveis por retrocompat (SIG consome hoje). */
54
+ catalog: CatalogProduct[];
55
+ currentProductCode?: string;
56
+ capabilities: {
57
+ manageUsers: boolean;
58
+ adminConsole: boolean;
59
+ switchContext: boolean;
60
+ manageNetwork: boolean;
61
+ };
62
+ };
63
+ declare function buildIdentityModel(claims: AccountsClaims | null, cfg?: {
64
+ currentProductCode?: string;
65
+ urls?: Record<string, string>;
66
+ }): IdentityModel;
67
+
68
+ export { type AccountsClaims, type CatalogProduct, type IdentityModel, PRODUCT_CATALOG, type Persona, type ProductMeta, buildIdentityModel, decodeAccessToken, decodeJwtPayload, derivePersona, getAccessibleProducts, getCatalogWithAccess, normalizeClaimArray, parseJsonArrayClaim, resolveProduct };
@@ -0,0 +1,68 @@
1
+ declare function decodeJwtPayload<T = Record<string, unknown>>(token: string): T;
2
+ type AccountsClaims = {
3
+ sub?: string;
4
+ email?: string;
5
+ name?: string;
6
+ platform_role?: string;
7
+ can_manage_scope_members?: string;
8
+ org_id?: string | string[];
9
+ unit_ids?: string;
10
+ active_unit_id?: string;
11
+ products?: string;
12
+ exp?: number;
13
+ iat?: number;
14
+ iss?: string;
15
+ aud?: string | string[];
16
+ };
17
+ declare function normalizeClaimArray(value: string | string[] | undefined): string[];
18
+ declare function parseJsonArrayClaim(value: string | undefined): string[];
19
+ declare function decodeAccessToken(token: string | null | undefined): AccountsClaims | null;
20
+
21
+ type Persona = "usuario" | "gestor" | "vega";
22
+ declare function derivePersona(claims: AccountsClaims | null): Persona;
23
+
24
+ type ProductMeta = {
25
+ code: string;
26
+ name: string;
27
+ description: string;
28
+ url: string;
29
+ accent: string;
30
+ ssoEnabled: boolean;
31
+ };
32
+ declare const PRODUCT_CATALOG: Record<string, ProductMeta>;
33
+ declare function resolveProduct(code: string, urls?: Record<string, string>): ProductMeta;
34
+ type CatalogProduct = ProductMeta & {
35
+ accessible: boolean;
36
+ };
37
+ declare function getCatalogWithAccess(claims: AccountsClaims | null, opts?: {
38
+ urls?: Record<string, string>;
39
+ }): CatalogProduct[];
40
+ declare function getAccessibleProducts(claims: AccountsClaims | null, opts?: {
41
+ urls?: Record<string, string>;
42
+ }): ProductMeta[];
43
+
44
+ type IdentityModel = {
45
+ persona: Persona;
46
+ user: {
47
+ name: string;
48
+ email: string;
49
+ };
50
+ products: ProductMeta[];
51
+ /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle
52
+ * mostrar tudo e desabilitar o que a persona nao acessa. `products`
53
+ * permanece so-acessiveis por retrocompat (SIG consome hoje). */
54
+ catalog: CatalogProduct[];
55
+ currentProductCode?: string;
56
+ capabilities: {
57
+ manageUsers: boolean;
58
+ adminConsole: boolean;
59
+ switchContext: boolean;
60
+ manageNetwork: boolean;
61
+ };
62
+ };
63
+ declare function buildIdentityModel(claims: AccountsClaims | null, cfg?: {
64
+ currentProductCode?: string;
65
+ urls?: Record<string, string>;
66
+ }): IdentityModel;
67
+
68
+ export { type AccountsClaims, type CatalogProduct, type IdentityModel, PRODUCT_CATALOG, type Persona, type ProductMeta, buildIdentityModel, decodeAccessToken, decodeJwtPayload, derivePersona, getAccessibleProducts, getCatalogWithAccess, normalizeClaimArray, parseJsonArrayClaim, resolveProduct };
package/dist/index.js ADDED
@@ -0,0 +1,180 @@
1
+ // src/claims.ts
2
+ function decodeJwtPayload(token) {
3
+ const parts = token.split(".");
4
+ if (parts.length !== 3) {
5
+ throw new Error("Invalid JWT format");
6
+ }
7
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
8
+ const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
9
+ const binary = atob(padded);
10
+ const bytes = new Uint8Array(binary.length);
11
+ for (let i = 0; i < binary.length; i++) {
12
+ bytes[i] = binary.charCodeAt(i);
13
+ }
14
+ const json = new TextDecoder("utf-8").decode(bytes);
15
+ return JSON.parse(json);
16
+ }
17
+ function normalizeClaimArray(value) {
18
+ if (!value) return [];
19
+ return Array.isArray(value) ? value : [value];
20
+ }
21
+ function parseJsonArrayClaim(value) {
22
+ if (!value) return [];
23
+ try {
24
+ const parsed = JSON.parse(value);
25
+ return Array.isArray(parsed) ? parsed.filter((v) => typeof v === "string") : [];
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+ function decodeAccessToken(token) {
31
+ if (!token) return null;
32
+ try {
33
+ return decodeJwtPayload(token);
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ // src/persona.ts
40
+ function derivePersona(claims) {
41
+ if (claims?.platform_role) return "vega";
42
+ if (claims?.can_manage_scope_members === "true") return "gestor";
43
+ return "usuario";
44
+ }
45
+
46
+ // src/productCatalog.ts
47
+ var PRODUCT_CATALOG = {
48
+ accounts: {
49
+ code: "accounts",
50
+ name: "Accounts",
51
+ description: "Identidade unificada do ecossistema",
52
+ url: "/",
53
+ accent: "#1F2E52",
54
+ ssoEnabled: true
55
+ // e o proprio IdP; excluido da gaveta de qualquer forma
56
+ },
57
+ "media-hub": {
58
+ code: "media-hub",
59
+ name: "Media Hub",
60
+ description: "Sinalizacao digital nas unidades",
61
+ url: "https://media-hub.vega.com.br/",
62
+ accent: "#EF802D",
63
+ ssoEnabled: true
64
+ },
65
+ sig: {
66
+ code: "sig",
67
+ name: "SIG",
68
+ description: "Housekeeping e governanca das unidades",
69
+ url: "https://sig.app.vegait.com/",
70
+ accent: "#B0C925",
71
+ ssoEnabled: true
72
+ },
73
+ gdi: {
74
+ code: "gdi",
75
+ name: "GDI",
76
+ description: "Gestao de dispositivos e internet",
77
+ url: "https://gdi.vega.com.br/",
78
+ accent: "#7B8B7E",
79
+ ssoEnabled: false
80
+ },
81
+ sim: {
82
+ code: "sim",
83
+ name: "SIM",
84
+ description: "Service Issue Management",
85
+ url: "https://sim.vega.com.br/",
86
+ accent: "#5B7DB1",
87
+ ssoEnabled: false
88
+ },
89
+ hxp: {
90
+ code: "hxp",
91
+ name: "HXP",
92
+ description: "Hotel Experience Platform",
93
+ url: "https://hxp.vega.com.br/",
94
+ accent: "#9B6A4F",
95
+ ssoEnabled: false
96
+ },
97
+ feedback: {
98
+ code: "feedback",
99
+ name: "Feedback",
100
+ description: "Pesquisas e NPS de hospedes",
101
+ url: "https://feedback.vega.com.br/",
102
+ accent: "#C26A8C",
103
+ ssoEnabled: false
104
+ },
105
+ behavior: {
106
+ code: "behavior",
107
+ name: "Behavior",
108
+ description: "Inteligencia comportamental",
109
+ url: "https://behavior.vega.com.br/",
110
+ accent: "#4A7C7E",
111
+ ssoEnabled: false
112
+ }
113
+ };
114
+ var LEGACY_CODE_ALIASES = {
115
+ governanca: "sig"
116
+ };
117
+ function resolveProduct(code, urls) {
118
+ const canonical = LEGACY_CODE_ALIASES[code] ?? code;
119
+ const base = PRODUCT_CATALOG[canonical] ?? {
120
+ code,
121
+ name: code,
122
+ description: "",
123
+ url: "#",
124
+ accent: "#1F2E52",
125
+ ssoEnabled: false
126
+ };
127
+ const override = urls?.[canonical];
128
+ return override ? { ...base, url: override } : base;
129
+ }
130
+ function getCatalogWithAccess(claims, opts) {
131
+ const urls = opts?.urls;
132
+ const isVega = claims != null && derivePersona(claims) === "vega";
133
+ const accessibleCodes = new Set(
134
+ parseJsonArrayClaim(claims?.products).map(
135
+ (code) => LEGACY_CODE_ALIASES[code] ?? code
136
+ )
137
+ );
138
+ return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => ({
139
+ ...urls?.[p.code] ? { ...p, url: urls[p.code] } : p,
140
+ accessible: isVega || accessibleCodes.has(p.code)
141
+ }));
142
+ }
143
+ function getAccessibleProducts(claims, opts) {
144
+ if (!claims) return [];
145
+ const urls = opts?.urls;
146
+ if (derivePersona(claims) === "vega") {
147
+ return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts" && p.ssoEnabled).map((p) => urls?.[p.code] ? { ...p, url: urls[p.code] } : p);
148
+ }
149
+ return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.ssoEnabled);
150
+ }
151
+
152
+ // src/identityModel.ts
153
+ function buildIdentityModel(claims, cfg) {
154
+ const persona = derivePersona(claims);
155
+ return {
156
+ persona,
157
+ user: { name: claims?.name ?? "", email: claims?.email ?? "" },
158
+ products: getAccessibleProducts(claims, { urls: cfg?.urls }),
159
+ catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),
160
+ currentProductCode: cfg?.currentProductCode,
161
+ capabilities: {
162
+ manageUsers: persona === "gestor" || persona === "vega",
163
+ adminConsole: persona === "vega",
164
+ switchContext: persona === "gestor" || persona === "vega",
165
+ manageNetwork: persona === "gestor"
166
+ }
167
+ };
168
+ }
169
+ export {
170
+ PRODUCT_CATALOG,
171
+ buildIdentityModel,
172
+ decodeAccessToken,
173
+ decodeJwtPayload,
174
+ derivePersona,
175
+ getAccessibleProducts,
176
+ getCatalogWithAccess,
177
+ normalizeClaimArray,
178
+ parseJsonArrayClaim,
179
+ resolveProduct
180
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@vegait/accounts-sdk",
3
+ "version": "0.1.0-beta.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "tag": "beta"
7
+ },
8
+ "description": "Cola de integracao frontend do Accounts (persona, catalogo de produtos, adapter da casca de identidade).",
9
+ "type": "module",
10
+ "main": "./dist/index.cjs",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "prepublishOnly": "tsup",
27
+ "test": "vitest run",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.4.0",
33
+ "vitest": "^2.0.0"
34
+ }
35
+ }