@abpjs/tenant-management 0.7.6

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/index.mjs ADDED
@@ -0,0 +1,530 @@
1
+ // src/services/tenant-management.service.ts
2
+ var TenantManagementService = class {
3
+ constructor(rest) {
4
+ this.rest = rest;
5
+ }
6
+ /**
7
+ * Get all tenants (paginated)
8
+ * @returns Promise with paginated tenant response
9
+ */
10
+ getAll() {
11
+ return this.rest.request({
12
+ method: "GET",
13
+ url: "/api/multi-tenancy/tenants"
14
+ });
15
+ }
16
+ /**
17
+ * Get a tenant by ID
18
+ * @param id Tenant ID
19
+ * @returns Promise with tenant item
20
+ */
21
+ getById(id) {
22
+ return this.rest.request({
23
+ method: "GET",
24
+ url: `/api/multi-tenancy/tenants/${id}`
25
+ });
26
+ }
27
+ /**
28
+ * Create a new tenant
29
+ * @param body Tenant creation request
30
+ * @returns Promise with created tenant item
31
+ */
32
+ create(body) {
33
+ return this.rest.request({
34
+ method: "POST",
35
+ url: "/api/multi-tenancy/tenants",
36
+ body
37
+ });
38
+ }
39
+ /**
40
+ * Update an existing tenant
41
+ * @param body Tenant update request (includes id)
42
+ * @returns Promise with updated tenant item
43
+ */
44
+ update(body) {
45
+ const { id, ...data } = body;
46
+ return this.rest.request({
47
+ method: "PUT",
48
+ url: `/api/multi-tenancy/tenants/${id}`,
49
+ body: data
50
+ });
51
+ }
52
+ /**
53
+ * Delete a tenant
54
+ * @param id Tenant ID
55
+ * @returns Promise that resolves when deletion completes
56
+ */
57
+ delete(id) {
58
+ return this.rest.request({
59
+ method: "DELETE",
60
+ url: `/api/multi-tenancy/tenants/${id}`
61
+ });
62
+ }
63
+ /**
64
+ * Get default connection string for a tenant
65
+ * @param id Tenant ID
66
+ * @returns Promise with connection string (empty string if using shared database)
67
+ */
68
+ getDefaultConnectionString(id) {
69
+ return this.rest.request({
70
+ method: "GET",
71
+ url: `/api/multi-tenancy/tenants/${id}/default-connection-string`
72
+ });
73
+ }
74
+ /**
75
+ * Update default connection string for a tenant
76
+ * @param payload Request with tenant ID and connection string
77
+ * @returns Promise that resolves when update completes
78
+ */
79
+ updateDefaultConnectionString(payload) {
80
+ const { id, defaultConnectionString } = payload;
81
+ return this.rest.request({
82
+ method: "PUT",
83
+ url: `/api/multi-tenancy/tenants/${id}/default-connection-string`,
84
+ params: { defaultConnectionString }
85
+ });
86
+ }
87
+ /**
88
+ * Delete default connection string for a tenant (use shared database)
89
+ * @param id Tenant ID
90
+ * @returns Promise that resolves when deletion completes
91
+ */
92
+ deleteDefaultConnectionString(id) {
93
+ return this.rest.request({
94
+ method: "DELETE",
95
+ url: `/api/multi-tenancy/tenants/${id}/default-connection-string`
96
+ });
97
+ }
98
+ };
99
+
100
+ // src/hooks/useTenantManagement.ts
101
+ import { useState, useCallback, useMemo } from "react";
102
+ import { useRestService } from "@abpjs/core";
103
+ function useTenantManagement() {
104
+ const restService = useRestService();
105
+ const service = useMemo(() => new TenantManagementService(restService), [restService]);
106
+ const [tenants, setTenants] = useState([]);
107
+ const [selectedTenant, setSelectedTenant] = useState(null);
108
+ const [isLoading, setIsLoading] = useState(false);
109
+ const [error, setError] = useState(null);
110
+ const [defaultConnectionString, setDefaultConnectionString] = useState("");
111
+ const [useSharedDatabase, setUseSharedDatabase] = useState(true);
112
+ const fetchTenants = useCallback(async () => {
113
+ setIsLoading(true);
114
+ setError(null);
115
+ try {
116
+ const response = await service.getAll();
117
+ setTenants(response.items);
118
+ setIsLoading(false);
119
+ return { success: true };
120
+ } catch (err) {
121
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenants";
122
+ setError(errorMessage);
123
+ setIsLoading(false);
124
+ return { success: false, error: errorMessage };
125
+ }
126
+ }, [service]);
127
+ const fetchTenantById = useCallback(
128
+ async (id) => {
129
+ setIsLoading(true);
130
+ setError(null);
131
+ try {
132
+ const tenant = await service.getById(id);
133
+ setSelectedTenant(tenant);
134
+ setIsLoading(false);
135
+ return { success: true };
136
+ } catch (err) {
137
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenant";
138
+ setError(errorMessage);
139
+ setIsLoading(false);
140
+ return { success: false, error: errorMessage };
141
+ }
142
+ },
143
+ [service]
144
+ );
145
+ const createTenant = useCallback(
146
+ async (data) => {
147
+ setIsLoading(true);
148
+ setError(null);
149
+ try {
150
+ await service.create(data);
151
+ await service.getAll().then((response) => setTenants(response.items));
152
+ setIsLoading(false);
153
+ return { success: true };
154
+ } catch (err) {
155
+ const errorMessage = err instanceof Error ? err.message : "Failed to create tenant";
156
+ setError(errorMessage);
157
+ setIsLoading(false);
158
+ return { success: false, error: errorMessage };
159
+ }
160
+ },
161
+ [service]
162
+ );
163
+ const updateTenant = useCallback(
164
+ async (data) => {
165
+ setIsLoading(true);
166
+ setError(null);
167
+ try {
168
+ await service.update(data);
169
+ await service.getAll().then((response) => setTenants(response.items));
170
+ setIsLoading(false);
171
+ return { success: true };
172
+ } catch (err) {
173
+ const errorMessage = err instanceof Error ? err.message : "Failed to update tenant";
174
+ setError(errorMessage);
175
+ setIsLoading(false);
176
+ return { success: false, error: errorMessage };
177
+ }
178
+ },
179
+ [service]
180
+ );
181
+ const deleteTenant = useCallback(
182
+ async (id) => {
183
+ setIsLoading(true);
184
+ setError(null);
185
+ try {
186
+ await service.delete(id);
187
+ await service.getAll().then((response) => setTenants(response.items));
188
+ setIsLoading(false);
189
+ return { success: true };
190
+ } catch (err) {
191
+ const errorMessage = err instanceof Error ? err.message : "Failed to delete tenant";
192
+ setError(errorMessage);
193
+ setIsLoading(false);
194
+ return { success: false, error: errorMessage };
195
+ }
196
+ },
197
+ [service]
198
+ );
199
+ const fetchConnectionString = useCallback(
200
+ async (id) => {
201
+ setIsLoading(true);
202
+ setError(null);
203
+ try {
204
+ const connectionString = await service.getDefaultConnectionString(id);
205
+ setDefaultConnectionString(connectionString || "");
206
+ setUseSharedDatabase(!connectionString);
207
+ setIsLoading(false);
208
+ return { success: true };
209
+ } catch (err) {
210
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch connection string";
211
+ setError(errorMessage);
212
+ setIsLoading(false);
213
+ return { success: false, error: errorMessage };
214
+ }
215
+ },
216
+ [service]
217
+ );
218
+ const updateConnectionString = useCallback(
219
+ async (id, connectionString) => {
220
+ setIsLoading(true);
221
+ setError(null);
222
+ try {
223
+ await service.updateDefaultConnectionString({
224
+ id,
225
+ defaultConnectionString: connectionString
226
+ });
227
+ setDefaultConnectionString(connectionString);
228
+ setUseSharedDatabase(false);
229
+ setIsLoading(false);
230
+ return { success: true };
231
+ } catch (err) {
232
+ const errorMessage = err instanceof Error ? err.message : "Failed to update connection string";
233
+ setError(errorMessage);
234
+ setIsLoading(false);
235
+ return { success: false, error: errorMessage };
236
+ }
237
+ },
238
+ [service]
239
+ );
240
+ const deleteConnectionString = useCallback(
241
+ async (id) => {
242
+ setIsLoading(true);
243
+ setError(null);
244
+ try {
245
+ await service.deleteDefaultConnectionString(id);
246
+ setDefaultConnectionString("");
247
+ setUseSharedDatabase(true);
248
+ setIsLoading(false);
249
+ return { success: true };
250
+ } catch (err) {
251
+ const errorMessage = err instanceof Error ? err.message : "Failed to delete connection string";
252
+ setError(errorMessage);
253
+ setIsLoading(false);
254
+ return { success: false, error: errorMessage };
255
+ }
256
+ },
257
+ [service]
258
+ );
259
+ const reset = useCallback(() => {
260
+ setTenants([]);
261
+ setSelectedTenant(null);
262
+ setIsLoading(false);
263
+ setError(null);
264
+ setDefaultConnectionString("");
265
+ setUseSharedDatabase(true);
266
+ }, []);
267
+ return {
268
+ tenants,
269
+ selectedTenant,
270
+ isLoading,
271
+ error,
272
+ defaultConnectionString,
273
+ useSharedDatabase,
274
+ fetchTenants,
275
+ fetchTenantById,
276
+ createTenant,
277
+ updateTenant,
278
+ deleteTenant,
279
+ fetchConnectionString,
280
+ updateConnectionString,
281
+ deleteConnectionString,
282
+ setSelectedTenant,
283
+ setUseSharedDatabase,
284
+ setDefaultConnectionString,
285
+ reset
286
+ };
287
+ }
288
+
289
+ // src/components/TenantManagementModal/TenantManagementModal.tsx
290
+ import { useEffect, useCallback as useCallback2, useState as useState2 } from "react";
291
+ import { useLocalization } from "@abpjs/core";
292
+ import { Modal, Alert, Button, Checkbox, FormField } from "@abpjs/theme-shared";
293
+ import {
294
+ Box,
295
+ Flex,
296
+ Input,
297
+ Spinner,
298
+ VStack
299
+ } from "@chakra-ui/react";
300
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
301
+ function TenantManagementModal({
302
+ visible,
303
+ onVisibleChange,
304
+ tenantId,
305
+ initialView = "tenant",
306
+ onSave
307
+ }) {
308
+ const { t } = useLocalization();
309
+ const {
310
+ selectedTenant,
311
+ isLoading,
312
+ error,
313
+ defaultConnectionString,
314
+ useSharedDatabase,
315
+ fetchTenantById,
316
+ createTenant,
317
+ updateTenant,
318
+ fetchConnectionString,
319
+ updateConnectionString,
320
+ deleteConnectionString,
321
+ reset
322
+ } = useTenantManagement();
323
+ const [currentView, setCurrentView] = useState2(initialView);
324
+ const [tenantName, setTenantName] = useState2("");
325
+ const [tenantNameError, setTenantNameError] = useState2(null);
326
+ const [localConnectionString, setLocalConnectionString] = useState2("");
327
+ const [localUseSharedDatabase, setLocalUseSharedDatabase] = useState2(true);
328
+ const isEditing = !!tenantId;
329
+ const getModalTitle = () => {
330
+ if (currentView === "connectionString") {
331
+ return t("AbpTenantManagement::ConnectionStrings");
332
+ }
333
+ return isEditing ? t("AbpTenantManagement::Edit") : t("AbpTenantManagement::NewTenant");
334
+ };
335
+ useEffect(() => {
336
+ if (visible && tenantId) {
337
+ fetchTenantById(tenantId);
338
+ if (initialView === "connectionString") {
339
+ fetchConnectionString(tenantId);
340
+ }
341
+ }
342
+ if (visible) {
343
+ setCurrentView(initialView);
344
+ }
345
+ if (!visible) {
346
+ reset();
347
+ setTenantName("");
348
+ setTenantNameError(null);
349
+ setLocalConnectionString("");
350
+ setLocalUseSharedDatabase(true);
351
+ }
352
+ }, [visible, tenantId, initialView, fetchTenantById, fetchConnectionString, reset]);
353
+ useEffect(() => {
354
+ if (selectedTenant) {
355
+ setTenantName(selectedTenant.name);
356
+ }
357
+ }, [selectedTenant]);
358
+ useEffect(() => {
359
+ setLocalConnectionString(defaultConnectionString);
360
+ setLocalUseSharedDatabase(useSharedDatabase);
361
+ }, [defaultConnectionString, useSharedDatabase]);
362
+ const validateTenantName = useCallback2(
363
+ (name) => {
364
+ if (!name || name.trim().length === 0) {
365
+ setTenantNameError(t("AbpValidation::ThisFieldIsRequired"));
366
+ return false;
367
+ }
368
+ if (name.length > 256) {
369
+ setTenantNameError(
370
+ t("AbpValidation::ThisFieldMustBeBetween{0}And{1}Characters", "1", "256")
371
+ );
372
+ return false;
373
+ }
374
+ setTenantNameError(null);
375
+ return true;
376
+ },
377
+ [t]
378
+ );
379
+ const handleTenantSubmit = useCallback2(async () => {
380
+ if (!validateTenantName(tenantName)) {
381
+ return;
382
+ }
383
+ let result;
384
+ if (isEditing && tenantId) {
385
+ result = await updateTenant({ id: tenantId, name: tenantName.trim() });
386
+ } else {
387
+ result = await createTenant({ name: tenantName.trim() });
388
+ }
389
+ if (result.success) {
390
+ onSave?.();
391
+ onVisibleChange?.(false);
392
+ }
393
+ }, [
394
+ tenantName,
395
+ isEditing,
396
+ tenantId,
397
+ validateTenantName,
398
+ createTenant,
399
+ updateTenant,
400
+ onSave,
401
+ onVisibleChange
402
+ ]);
403
+ const handleConnectionStringSubmit = useCallback2(async () => {
404
+ if (!tenantId) return;
405
+ let result;
406
+ if (localUseSharedDatabase) {
407
+ result = await deleteConnectionString(tenantId);
408
+ } else {
409
+ result = await updateConnectionString(tenantId, localConnectionString);
410
+ }
411
+ if (result.success) {
412
+ onSave?.();
413
+ onVisibleChange?.(false);
414
+ }
415
+ }, [
416
+ tenantId,
417
+ localUseSharedDatabase,
418
+ localConnectionString,
419
+ deleteConnectionString,
420
+ updateConnectionString,
421
+ onSave,
422
+ onVisibleChange
423
+ ]);
424
+ const handleClose = useCallback2(() => {
425
+ onVisibleChange?.(false);
426
+ }, [onVisibleChange]);
427
+ const handleTenantNameChange = useCallback2(
428
+ (e) => {
429
+ const value = e.target.value;
430
+ setTenantName(value);
431
+ if (tenantNameError) {
432
+ validateTenantName(value);
433
+ }
434
+ },
435
+ [tenantNameError, validateTenantName]
436
+ );
437
+ const handleUseSharedDatabaseChange = useCallback2(() => {
438
+ setLocalUseSharedDatabase((prev) => {
439
+ const newValue = !prev;
440
+ if (newValue) {
441
+ setLocalConnectionString("");
442
+ }
443
+ return newValue;
444
+ });
445
+ }, []);
446
+ const handleConnectionStringChange = useCallback2((e) => {
447
+ setLocalConnectionString(e.target.value);
448
+ }, []);
449
+ const renderTenantForm = () => /* @__PURE__ */ jsx(VStack, { gap: 4, align: "stretch", children: /* @__PURE__ */ jsx(
450
+ FormField,
451
+ {
452
+ label: t("AbpTenantManagement::TenantName"),
453
+ htmlFor: "tenant-name",
454
+ invalid: !!tenantNameError,
455
+ errorText: tenantNameError || void 0,
456
+ required: true,
457
+ children: /* @__PURE__ */ jsx(
458
+ Input,
459
+ {
460
+ id: "tenant-name",
461
+ value: tenantName,
462
+ onChange: handleTenantNameChange,
463
+ placeholder: t("AbpTenantManagement::TenantName"),
464
+ maxLength: 256
465
+ }
466
+ )
467
+ }
468
+ ) });
469
+ const renderConnectionStringForm = () => /* @__PURE__ */ jsxs(VStack, { gap: 4, align: "stretch", children: [
470
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
471
+ Checkbox,
472
+ {
473
+ id: "use-shared-database",
474
+ checked: localUseSharedDatabase,
475
+ onChange: handleUseSharedDatabaseChange,
476
+ children: t("AbpTenantManagement::DisplayName:UseSharedDatabase")
477
+ }
478
+ ) }),
479
+ !localUseSharedDatabase && /* @__PURE__ */ jsx(
480
+ FormField,
481
+ {
482
+ label: t("AbpTenantManagement::DisplayName:DefaultConnectionString"),
483
+ htmlFor: "connection-string",
484
+ children: /* @__PURE__ */ jsx(
485
+ Input,
486
+ {
487
+ id: "connection-string",
488
+ value: localConnectionString,
489
+ onChange: handleConnectionStringChange,
490
+ placeholder: t("AbpTenantManagement::DisplayName:DefaultConnectionString")
491
+ }
492
+ )
493
+ }
494
+ )
495
+ ] });
496
+ return /* @__PURE__ */ jsxs(
497
+ Modal,
498
+ {
499
+ visible,
500
+ onVisibleChange,
501
+ size: "md",
502
+ header: getModalTitle(),
503
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
504
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleClose, disabled: isLoading, children: t("AbpTenantManagement::Cancel") }),
505
+ /* @__PURE__ */ jsx(
506
+ Button,
507
+ {
508
+ colorPalette: "blue",
509
+ onClick: currentView === "connectionString" ? handleConnectionStringSubmit : handleTenantSubmit,
510
+ loading: isLoading,
511
+ children: t("AbpTenantManagement::Save")
512
+ }
513
+ )
514
+ ] }),
515
+ children: [
516
+ isLoading && !selectedTenant && currentView === "tenant" && /* @__PURE__ */ jsx(Flex, { justify: "center", align: "center", py: 8, children: /* @__PURE__ */ jsx(Spinner, { size: "lg" }) }),
517
+ error && /* @__PURE__ */ jsx(Alert, { status: "error", mb: 4, children: error }),
518
+ /* @__PURE__ */ jsxs(Box, { children: [
519
+ currentView === "tenant" && renderTenantForm(),
520
+ currentView === "connectionString" && renderConnectionStringForm()
521
+ ] })
522
+ ]
523
+ }
524
+ );
525
+ }
526
+ export {
527
+ TenantManagementModal,
528
+ TenantManagementService,
529
+ useTenantManagement
530
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Tenant Management module type definitions
3
+ * Translated from @abp/ng.tenant-management v0.7.6
4
+ */
5
+ import type { ABP } from '@abpjs/core';
6
+ /**
7
+ * TenantManagement namespace containing all tenant-related types
8
+ */
9
+ export declare namespace TenantManagement {
10
+ /**
11
+ * State interface for tenant management store
12
+ */
13
+ interface State {
14
+ result: Response;
15
+ selectedItem: Item;
16
+ }
17
+ /**
18
+ * API response for tenant list (paginated)
19
+ */
20
+ type Response = ABP.PagedResponse<Item>;
21
+ /**
22
+ * Single tenant item
23
+ */
24
+ interface Item {
25
+ id: string;
26
+ name: string;
27
+ }
28
+ /**
29
+ * Request payload for creating a new tenant
30
+ */
31
+ interface AddRequest {
32
+ name: string;
33
+ }
34
+ /**
35
+ * Request payload for updating an existing tenant
36
+ */
37
+ interface UpdateRequest extends AddRequest {
38
+ id: string;
39
+ }
40
+ /**
41
+ * Request payload for updating tenant's default connection string
42
+ */
43
+ interface DefaultConnectionStringRequest {
44
+ id: string;
45
+ defaultConnectionString: string;
46
+ }
47
+ }
@@ -0,0 +1 @@
1
+ export { TenantManagementService } from './tenant-management.service';
@@ -0,0 +1,57 @@
1
+ import { RestService } from '@abpjs/core';
2
+ import { TenantManagement } from '../models';
3
+ /**
4
+ * Service for tenant management API calls
5
+ * Translated from @abp/ng.tenant-management v0.7.6
6
+ */
7
+ export declare class TenantManagementService {
8
+ private rest;
9
+ constructor(rest: RestService);
10
+ /**
11
+ * Get all tenants (paginated)
12
+ * @returns Promise with paginated tenant response
13
+ */
14
+ getAll(): Promise<TenantManagement.Response>;
15
+ /**
16
+ * Get a tenant by ID
17
+ * @param id Tenant ID
18
+ * @returns Promise with tenant item
19
+ */
20
+ getById(id: string): Promise<TenantManagement.Item>;
21
+ /**
22
+ * Create a new tenant
23
+ * @param body Tenant creation request
24
+ * @returns Promise with created tenant item
25
+ */
26
+ create(body: TenantManagement.AddRequest): Promise<TenantManagement.Item>;
27
+ /**
28
+ * Update an existing tenant
29
+ * @param body Tenant update request (includes id)
30
+ * @returns Promise with updated tenant item
31
+ */
32
+ update(body: TenantManagement.UpdateRequest): Promise<TenantManagement.Item>;
33
+ /**
34
+ * Delete a tenant
35
+ * @param id Tenant ID
36
+ * @returns Promise that resolves when deletion completes
37
+ */
38
+ delete(id: string): Promise<void>;
39
+ /**
40
+ * Get default connection string for a tenant
41
+ * @param id Tenant ID
42
+ * @returns Promise with connection string (empty string if using shared database)
43
+ */
44
+ getDefaultConnectionString(id: string): Promise<string>;
45
+ /**
46
+ * Update default connection string for a tenant
47
+ * @param payload Request with tenant ID and connection string
48
+ * @returns Promise that resolves when update completes
49
+ */
50
+ updateDefaultConnectionString(payload: TenantManagement.DefaultConnectionStringRequest): Promise<void>;
51
+ /**
52
+ * Delete default connection string for a tenant (use shared database)
53
+ * @param id Tenant ID
54
+ * @returns Promise that resolves when deletion completes
55
+ */
56
+ deleteDefaultConnectionString(id: string): Promise<void>;
57
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@abpjs/tenant-management",
3
+ "version": "0.7.6",
4
+ "description": "ABP Framework tenant-management components for React - translated from @abp/ng.tenant-management",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "peerDependencies": {
20
+ "react": "^18.2.0",
21
+ "react-dom": "^18.2.0"
22
+ },
23
+ "dependencies": {
24
+ "@chakra-ui/react": "^3.2.0",
25
+ "@emotion/react": "^11.11.0",
26
+ "@abpjs/core": "0.7.6",
27
+ "@abpjs/theme-shared": "0.7.6"
28
+ },
29
+ "devDependencies": {
30
+ "@abp/ng.tenant-management": "0.7.6",
31
+ "autoprefixer": "^10.4.16",
32
+ "postcss": "^8.4.32",
33
+ "tailwindcss": "^3.4.0"
34
+ },
35
+ "author": "tekthar.com",
36
+ "license": "LGPL-3.0",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/abpjs/abp-react"
43
+ },
44
+ "homepage": "https://docs.abpjs.io/docs/packages/tenant-management/overview",
45
+ "bugs": {
46
+ "url": "https://github.com/abpjs/abp-react/issues"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup src/index.ts --format cjs,esm --clean && tsc -p tsconfig.build.json",
50
+ "dev": "tsup src/index.ts --format cjs,esm --watch",
51
+ "lint": "eslint src",
52
+ "lint:fix": "eslint src --fix",
53
+ "format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
54
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
55
+ "type-check": "tsc --noEmit",
56
+ "test": "vitest"
57
+ }
58
+ }