aspi 2.0.1 → 2.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/README.md +69 -7
- package/dist/index.cjs +65 -12
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +65 -12
- package/package.json +13 -14
package/README.md
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
A tiny, type‑safe wrapper around the native **fetch** API that gives you a clean, monadic interface for HTTP requests.
|
|
4
4
|
It ships with **zero runtime dependencies**, a **tiny bundle size**, and full **TypeScript** support out of the box.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
**Why use Aspi?**
|
|
7
|
+
|
|
8
|
+
- End‑to‑end TypeScript typings (request + response)
|
|
9
|
+
- No extra weight – only a thin wrapper around `fetch`
|
|
10
|
+
- Chain‑of‑responsibility middleware support via `use`
|
|
11
|
+
- Result‑based error handling (values as errors)
|
|
12
|
+
- Built‑in retry, header helpers, query‑string handling, and schema validation (Zod, Arktype, Valibot)
|
|
13
|
+
- Flexible error mapping with `error` and convenience shortcuts
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
@@ -66,6 +67,67 @@ getTodo(1);
|
|
|
66
67
|
|
|
67
68
|
---
|
|
68
69
|
|
|
70
|
+
## Why Aspi?
|
|
71
|
+
|
|
72
|
+
Most real‑world codebases end up with one or more of these issues:
|
|
73
|
+
|
|
74
|
+
1. **Inconsistent error handling**
|
|
75
|
+
- Some utilities throw raw `Error`/`AxiosError`.
|
|
76
|
+
- Others return `{ ok: false, error }` or `null` or a custom union.
|
|
77
|
+
- Callers don’t know whether to use `try/catch`, check `ok`, or both.
|
|
78
|
+
|
|
79
|
+
2. **Retry logic duplicated everywhere**
|
|
80
|
+
- Each service rolls its own `while (attempt <= retries)` loop.
|
|
81
|
+
- Status codes, backoff strategies, and retry limits slowly diverge over time.
|
|
82
|
+
- There is no single place to see “how do we retry HTTP calls in this app?”.
|
|
83
|
+
|
|
84
|
+
3. **Validation pushed far from the network boundary**
|
|
85
|
+
- Request payloads are sometimes validated, sometimes not.
|
|
86
|
+
- Response validation happens deep in the business logic (if at all).
|
|
87
|
+
- JSON parse errors leak as raw `SyntaxError`, not structured errors.
|
|
88
|
+
|
|
89
|
+
4. **Configuration scattered across factories and interceptors**
|
|
90
|
+
- Base URL helpers, auth decorators, error mappers, retry plugins, and logging interceptors all live in different files.
|
|
91
|
+
- Global state / interceptors can make it hard to tell what a given request will actually do.
|
|
92
|
+
|
|
93
|
+
5. **Type systems are bolted on, not designed in**
|
|
94
|
+
- Generic HTTP clients often expose `any` for responses.
|
|
95
|
+
- Error flows are not encoded in the type system, forcing manual guards and casting.
|
|
96
|
+
|
|
97
|
+
## How Aspi fixes them
|
|
98
|
+
|
|
99
|
+
Aspi’s design centers around three things:
|
|
100
|
+
|
|
101
|
+
1. **Mode‑driven responses**
|
|
102
|
+
|
|
103
|
+
You decide at call‑site how you want to consume responses:
|
|
104
|
+
- `withResult()` → `json/text/blob` return a `Result.Result<Ok, ErrorUnion>`.
|
|
105
|
+
- `throwable()` → `json/text/blob` return `AspiPlainResponse` and throw on failure.
|
|
106
|
+
- Default → `json/text/blob` return `[ok, err]` tuples.
|
|
107
|
+
|
|
108
|
+
All error variants are **tagged** so they can be safely narrowed by `error.tag`.
|
|
109
|
+
|
|
110
|
+
2. **Centralized, configurable retry layer**
|
|
111
|
+
|
|
112
|
+
Retry behavior is described declaratively:
|
|
113
|
+
- `retries`: max attempts.
|
|
114
|
+
- `retryDelay`: number or function `(attempt, maxAttempts, request, response) => delayMs`.
|
|
115
|
+
- `retryOn`: list of HTTP status codes that should trigger a retry.
|
|
116
|
+
- `retryWhile`: predicate `(request, response) => boolean` for custom retry conditions.
|
|
117
|
+
- `onRetry`: hook invoked after each retry attempt.
|
|
118
|
+
|
|
119
|
+
This configuration can be applied globally (`Aspi.setRetry`) and overridden per request (`Request.setRetry`).
|
|
120
|
+
|
|
121
|
+
3. **Validation at the transport boundary**
|
|
122
|
+
|
|
123
|
+
Using a `StandardSchemaV1` interface, Aspi integrates with schema libraries (e.g. Zod, Valibot) to:
|
|
124
|
+
- Validate request bodies with `bodySchema` + `bodyJson` **before** the network call.
|
|
125
|
+
- Validate responses with `schema()` + `json()` **after** JSON parsing.
|
|
126
|
+
|
|
127
|
+
These failures appear as tagged `parseError` values with structured issue lists, not random runtime exceptions.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
69
131
|
## Using the `Result` monad
|
|
70
132
|
|
|
71
133
|
If you prefer a single `Result` value instead of a tuple, call **`.withResult()`** before a body‑parser method.
|
package/dist/index.cjs
CHANGED
|
@@ -20,7 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
Aspi: () =>
|
|
23
|
+
Aspi: () => Aspi,
|
|
24
24
|
AspiError: () => AspiError,
|
|
25
25
|
CustomError: () => CustomError,
|
|
26
26
|
Request: () => Request,
|
|
@@ -322,8 +322,8 @@ var Request = class {
|
|
|
322
322
|
...requestOptions.requestConfig,
|
|
323
323
|
method
|
|
324
324
|
};
|
|
325
|
-
this.#retryConfig = requestOptions
|
|
326
|
-
this.#customErrorCbs = requestOptions
|
|
325
|
+
this.#retryConfig = { ...requestOptions?.retryConfig || {} };
|
|
326
|
+
this.#customErrorCbs = { ...requestOptions?.errorCbs || {} };
|
|
327
327
|
this.#throwOnError = requestOptions.throwOnError || false;
|
|
328
328
|
this.#shouldBeResult = requestOptions.shouldBeResult || false;
|
|
329
329
|
}
|
|
@@ -680,7 +680,29 @@ var Request = class {
|
|
|
680
680
|
* request.setQueryParams(qp);
|
|
681
681
|
*/
|
|
682
682
|
setQueryParams(params) {
|
|
683
|
-
|
|
683
|
+
let qp;
|
|
684
|
+
if (params instanceof URLSearchParams) {
|
|
685
|
+
qp = new URLSearchParams(params);
|
|
686
|
+
} else if (typeof params === "string") {
|
|
687
|
+
qp = new URLSearchParams(params);
|
|
688
|
+
} else if (Array.isArray(params)) {
|
|
689
|
+
qp = new URLSearchParams();
|
|
690
|
+
for (const entry of params) {
|
|
691
|
+
if (Array.isArray(entry) && entry.length === 2) {
|
|
692
|
+
qp.append(String(entry[0]), String(entry[1]));
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} else if (typeof params === "object" && params !== null) {
|
|
696
|
+
qp = new URLSearchParams();
|
|
697
|
+
for (const [key, value] of Object.entries(
|
|
698
|
+
params
|
|
699
|
+
)) {
|
|
700
|
+
qp.append(key, String(value));
|
|
701
|
+
}
|
|
702
|
+
} else {
|
|
703
|
+
qp = new URLSearchParams();
|
|
704
|
+
}
|
|
705
|
+
this.#queryParams = qp;
|
|
684
706
|
return this;
|
|
685
707
|
}
|
|
686
708
|
/**
|
|
@@ -921,11 +943,41 @@ var Request = class {
|
|
|
921
943
|
return this.#mapResponse(output);
|
|
922
944
|
}
|
|
923
945
|
#url() {
|
|
946
|
+
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
947
|
+
const absolute = new URL(this.#path);
|
|
948
|
+
if (this.#queryParams) {
|
|
949
|
+
for (const [k, v] of this.#queryParams.entries()) {
|
|
950
|
+
absolute.searchParams.append(k, v);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return absolute.toString();
|
|
954
|
+
}
|
|
924
955
|
const passedBaseUrl = typeof this.#localRequestInit.baseUrl === "string" ? this.#localRequestInit.baseUrl : this.#localRequestInit.baseUrl.toString();
|
|
925
|
-
const
|
|
926
|
-
const
|
|
927
|
-
const
|
|
928
|
-
|
|
956
|
+
const base = passedBaseUrl.replace(/\/+$/, "");
|
|
957
|
+
const [rawPathAndQuery, fragment] = this.#path.split("#", 2);
|
|
958
|
+
const [rawPath, existingQuery] = rawPathAndQuery.split("?", 2);
|
|
959
|
+
let path = rawPath || "";
|
|
960
|
+
path = path.replace(/^\/+/, "");
|
|
961
|
+
path = path.replace(/\/{2,}/g, "/");
|
|
962
|
+
path = path.replace(/\/+$/, "");
|
|
963
|
+
if (path) {
|
|
964
|
+
path = "/" + path.replace(/^\/+/, "");
|
|
965
|
+
}
|
|
966
|
+
const qs = new URLSearchParams(existingQuery ?? "");
|
|
967
|
+
if (this.#queryParams) {
|
|
968
|
+
for (const [k, v] of this.#queryParams.entries()) {
|
|
969
|
+
qs.append(k, v);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const queryString = qs.toString();
|
|
973
|
+
let url = base + path;
|
|
974
|
+
if (queryString) {
|
|
975
|
+
url += `?${queryString}`;
|
|
976
|
+
}
|
|
977
|
+
if (fragment) {
|
|
978
|
+
url += `#${fragment}`;
|
|
979
|
+
}
|
|
980
|
+
url = url.replace(/\/+$/, "");
|
|
929
981
|
return url;
|
|
930
982
|
}
|
|
931
983
|
/**
|
|
@@ -1217,8 +1269,9 @@ var Request = class {
|
|
|
1217
1269
|
return {
|
|
1218
1270
|
response,
|
|
1219
1271
|
status: response.status,
|
|
1220
|
-
|
|
1221
|
-
responseData
|
|
1272
|
+
statusLabel: getHttpErrorStatus(response.status),
|
|
1273
|
+
responseData,
|
|
1274
|
+
statusText: response.statusText
|
|
1222
1275
|
};
|
|
1223
1276
|
}
|
|
1224
1277
|
/**
|
|
@@ -1286,7 +1339,7 @@ var Request = class {
|
|
|
1286
1339
|
};
|
|
1287
1340
|
|
|
1288
1341
|
// src/aspi.ts
|
|
1289
|
-
var
|
|
1342
|
+
var Aspi = class {
|
|
1290
1343
|
#globalRequestInit;
|
|
1291
1344
|
#middlewares = [];
|
|
1292
1345
|
#retryConfig;
|
|
@@ -1613,7 +1666,7 @@ var Aspi2 = class {
|
|
|
1613
1666
|
* api.internalServerError((req, res) => ({ message: 'Server error occurred' }));
|
|
1614
1667
|
*/
|
|
1615
1668
|
internalServerError(cb) {
|
|
1616
|
-
return this.error("
|
|
1669
|
+
return this.error("internalServerError", "INTERNAL_SERVER_ERROR", cb);
|
|
1617
1670
|
}
|
|
1618
1671
|
/**
|
|
1619
1672
|
* Sets the aspi to throw an error if the response status is not successful.
|
package/dist/index.d.cts
CHANGED
|
@@ -295,8 +295,9 @@ interface AspiRequest<T extends AspiRequestInit> {
|
|
|
295
295
|
*/
|
|
296
296
|
type AspiResponse<TData = any, IsError extends boolean = false> = Merge<{
|
|
297
297
|
status: HttpErrorCodes;
|
|
298
|
-
|
|
298
|
+
statusLabel: HttpErrorStatus;
|
|
299
299
|
response: Response;
|
|
300
|
+
statusText: string;
|
|
300
301
|
}, IsError extends true ? {
|
|
301
302
|
responseData: TData;
|
|
302
303
|
} : {
|
|
@@ -1081,7 +1082,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1081
1082
|
* const qp = new URLSearchParams({ page: '1' });
|
|
1082
1083
|
* request.setQueryParams(qp);
|
|
1083
1084
|
*/
|
|
1084
|
-
setQueryParams<T
|
|
1085
|
+
setQueryParams<T = any>(params: T): Request<Method, TRequest, Merge<Omit<Opts, "queryParams">, {
|
|
1085
1086
|
queryParams: T;
|
|
1086
1087
|
}>>;
|
|
1087
1088
|
/**
|
|
@@ -1718,7 +1719,7 @@ declare class Aspi<TRequest extends AspiRequestInit = AspiRequestInit, Opts exte
|
|
|
1718
1719
|
* api.internalServerError((req, res) => ({ message: 'Server error occurred' }));
|
|
1719
1720
|
*/
|
|
1720
1721
|
internalServerError<A extends {}>(cb: CustomErrorCb<TRequest, A>): Aspi<TRequest, Prettify<Opts & {
|
|
1721
|
-
error: { [K in keyof Opts["error"]
|
|
1722
|
+
error: { [K in "internalServerError" | keyof Opts["error"]]: K extends "internalServerError" ? CustomError<"internalServerError", A> : Opts["error"][K]; };
|
|
1722
1723
|
}>>;
|
|
1723
1724
|
/**
|
|
1724
1725
|
* Sets the aspi to throw an error if the response status is not successful.
|
package/dist/index.d.ts
CHANGED
|
@@ -295,8 +295,9 @@ interface AspiRequest<T extends AspiRequestInit> {
|
|
|
295
295
|
*/
|
|
296
296
|
type AspiResponse<TData = any, IsError extends boolean = false> = Merge<{
|
|
297
297
|
status: HttpErrorCodes;
|
|
298
|
-
|
|
298
|
+
statusLabel: HttpErrorStatus;
|
|
299
299
|
response: Response;
|
|
300
|
+
statusText: string;
|
|
300
301
|
}, IsError extends true ? {
|
|
301
302
|
responseData: TData;
|
|
302
303
|
} : {
|
|
@@ -1081,7 +1082,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1081
1082
|
* const qp = new URLSearchParams({ page: '1' });
|
|
1082
1083
|
* request.setQueryParams(qp);
|
|
1083
1084
|
*/
|
|
1084
|
-
setQueryParams<T
|
|
1085
|
+
setQueryParams<T = any>(params: T): Request<Method, TRequest, Merge<Omit<Opts, "queryParams">, {
|
|
1085
1086
|
queryParams: T;
|
|
1086
1087
|
}>>;
|
|
1087
1088
|
/**
|
|
@@ -1718,7 +1719,7 @@ declare class Aspi<TRequest extends AspiRequestInit = AspiRequestInit, Opts exte
|
|
|
1718
1719
|
* api.internalServerError((req, res) => ({ message: 'Server error occurred' }));
|
|
1719
1720
|
*/
|
|
1720
1721
|
internalServerError<A extends {}>(cb: CustomErrorCb<TRequest, A>): Aspi<TRequest, Prettify<Opts & {
|
|
1721
|
-
error: { [K in keyof Opts["error"]
|
|
1722
|
+
error: { [K in "internalServerError" | keyof Opts["error"]]: K extends "internalServerError" ? CustomError<"internalServerError", A> : Opts["error"][K]; };
|
|
1722
1723
|
}>>;
|
|
1723
1724
|
/**
|
|
1724
1725
|
* Sets the aspi to throw an error if the response status is not successful.
|
package/dist/index.js
CHANGED
|
@@ -294,8 +294,8 @@ var Request = class {
|
|
|
294
294
|
...requestOptions.requestConfig,
|
|
295
295
|
method
|
|
296
296
|
};
|
|
297
|
-
this.#retryConfig = requestOptions
|
|
298
|
-
this.#customErrorCbs = requestOptions
|
|
297
|
+
this.#retryConfig = { ...requestOptions?.retryConfig || {} };
|
|
298
|
+
this.#customErrorCbs = { ...requestOptions?.errorCbs || {} };
|
|
299
299
|
this.#throwOnError = requestOptions.throwOnError || false;
|
|
300
300
|
this.#shouldBeResult = requestOptions.shouldBeResult || false;
|
|
301
301
|
}
|
|
@@ -652,7 +652,29 @@ var Request = class {
|
|
|
652
652
|
* request.setQueryParams(qp);
|
|
653
653
|
*/
|
|
654
654
|
setQueryParams(params) {
|
|
655
|
-
|
|
655
|
+
let qp;
|
|
656
|
+
if (params instanceof URLSearchParams) {
|
|
657
|
+
qp = new URLSearchParams(params);
|
|
658
|
+
} else if (typeof params === "string") {
|
|
659
|
+
qp = new URLSearchParams(params);
|
|
660
|
+
} else if (Array.isArray(params)) {
|
|
661
|
+
qp = new URLSearchParams();
|
|
662
|
+
for (const entry of params) {
|
|
663
|
+
if (Array.isArray(entry) && entry.length === 2) {
|
|
664
|
+
qp.append(String(entry[0]), String(entry[1]));
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} else if (typeof params === "object" && params !== null) {
|
|
668
|
+
qp = new URLSearchParams();
|
|
669
|
+
for (const [key, value] of Object.entries(
|
|
670
|
+
params
|
|
671
|
+
)) {
|
|
672
|
+
qp.append(key, String(value));
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
qp = new URLSearchParams();
|
|
676
|
+
}
|
|
677
|
+
this.#queryParams = qp;
|
|
656
678
|
return this;
|
|
657
679
|
}
|
|
658
680
|
/**
|
|
@@ -893,11 +915,41 @@ var Request = class {
|
|
|
893
915
|
return this.#mapResponse(output);
|
|
894
916
|
}
|
|
895
917
|
#url() {
|
|
918
|
+
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
919
|
+
const absolute = new URL(this.#path);
|
|
920
|
+
if (this.#queryParams) {
|
|
921
|
+
for (const [k, v] of this.#queryParams.entries()) {
|
|
922
|
+
absolute.searchParams.append(k, v);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return absolute.toString();
|
|
926
|
+
}
|
|
896
927
|
const passedBaseUrl = typeof this.#localRequestInit.baseUrl === "string" ? this.#localRequestInit.baseUrl : this.#localRequestInit.baseUrl.toString();
|
|
897
|
-
const
|
|
898
|
-
const
|
|
899
|
-
const
|
|
900
|
-
|
|
928
|
+
const base = passedBaseUrl.replace(/\/+$/, "");
|
|
929
|
+
const [rawPathAndQuery, fragment] = this.#path.split("#", 2);
|
|
930
|
+
const [rawPath, existingQuery] = rawPathAndQuery.split("?", 2);
|
|
931
|
+
let path = rawPath || "";
|
|
932
|
+
path = path.replace(/^\/+/, "");
|
|
933
|
+
path = path.replace(/\/{2,}/g, "/");
|
|
934
|
+
path = path.replace(/\/+$/, "");
|
|
935
|
+
if (path) {
|
|
936
|
+
path = "/" + path.replace(/^\/+/, "");
|
|
937
|
+
}
|
|
938
|
+
const qs = new URLSearchParams(existingQuery ?? "");
|
|
939
|
+
if (this.#queryParams) {
|
|
940
|
+
for (const [k, v] of this.#queryParams.entries()) {
|
|
941
|
+
qs.append(k, v);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const queryString = qs.toString();
|
|
945
|
+
let url = base + path;
|
|
946
|
+
if (queryString) {
|
|
947
|
+
url += `?${queryString}`;
|
|
948
|
+
}
|
|
949
|
+
if (fragment) {
|
|
950
|
+
url += `#${fragment}`;
|
|
951
|
+
}
|
|
952
|
+
url = url.replace(/\/+$/, "");
|
|
901
953
|
return url;
|
|
902
954
|
}
|
|
903
955
|
/**
|
|
@@ -1189,8 +1241,9 @@ var Request = class {
|
|
|
1189
1241
|
return {
|
|
1190
1242
|
response,
|
|
1191
1243
|
status: response.status,
|
|
1192
|
-
|
|
1193
|
-
responseData
|
|
1244
|
+
statusLabel: getHttpErrorStatus(response.status),
|
|
1245
|
+
responseData,
|
|
1246
|
+
statusText: response.statusText
|
|
1194
1247
|
};
|
|
1195
1248
|
}
|
|
1196
1249
|
/**
|
|
@@ -1258,7 +1311,7 @@ var Request = class {
|
|
|
1258
1311
|
};
|
|
1259
1312
|
|
|
1260
1313
|
// src/aspi.ts
|
|
1261
|
-
var
|
|
1314
|
+
var Aspi = class {
|
|
1262
1315
|
#globalRequestInit;
|
|
1263
1316
|
#middlewares = [];
|
|
1264
1317
|
#retryConfig;
|
|
@@ -1585,7 +1638,7 @@ var Aspi2 = class {
|
|
|
1585
1638
|
* api.internalServerError((req, res) => ({ message: 'Server error occurred' }));
|
|
1586
1639
|
*/
|
|
1587
1640
|
internalServerError(cb) {
|
|
1588
|
-
return this.error("
|
|
1641
|
+
return this.error("internalServerError", "INTERNAL_SERVER_ERROR", cb);
|
|
1589
1642
|
}
|
|
1590
1643
|
/**
|
|
1591
1644
|
* Sets the aspi to throw an error if the response status is not successful.
|
|
@@ -1614,7 +1667,7 @@ var Aspi2 = class {
|
|
|
1614
1667
|
}
|
|
1615
1668
|
};
|
|
1616
1669
|
export {
|
|
1617
|
-
|
|
1670
|
+
Aspi,
|
|
1618
1671
|
AspiError,
|
|
1619
1672
|
CustomError,
|
|
1620
1673
|
Request,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aspi",
|
|
3
3
|
"description": "Rest API client for typescript projects with chain of responsibility design pattern.",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"devDependencies": {
|
|
@@ -32,17 +32,6 @@
|
|
|
32
32
|
"url": "git+https://github.com/harshtalks/aspi.git"
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://github.com/harshtalks/aspi",
|
|
35
|
-
"scripts": {
|
|
36
|
-
"ci": "bun run test:run && bun run build && bun run check-format && bun run lint",
|
|
37
|
-
"format": "prettier --write .",
|
|
38
|
-
"check-format": "prettier --check .",
|
|
39
|
-
"build": "tsup",
|
|
40
|
-
"lint": "tsc",
|
|
41
|
-
"local-release": "changeset version && changeset publish",
|
|
42
|
-
"prepublishOnly": "bun run ci",
|
|
43
|
-
"test": "vitest",
|
|
44
|
-
"test:run": "vitest run"
|
|
45
|
-
},
|
|
46
35
|
"exports": {
|
|
47
36
|
"./package.json": "./package.json",
|
|
48
37
|
".": {
|
|
@@ -52,5 +41,15 @@
|
|
|
52
41
|
},
|
|
53
42
|
"files": [
|
|
54
43
|
"dist"
|
|
55
|
-
]
|
|
56
|
-
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"ci": "bun run test:run && bun run build && bun run check-format && bun run lint",
|
|
47
|
+
"format": "prettier --write .",
|
|
48
|
+
"check-format": "prettier --check .",
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"lint": "tsc",
|
|
51
|
+
"local-release": "changeset version && changeset publish",
|
|
52
|
+
"test": "vitest",
|
|
53
|
+
"test:run": "vitest run"
|
|
54
|
+
}
|
|
55
|
+
}
|