@diphyx/harlemify 1.0.4 → 2.0.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 +89 -3
- package/dist/module.d.mts +12 -2
- package/dist/module.d.ts +12 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -1
- package/dist/runtime/core/adapter.d.ts +33 -0
- package/dist/runtime/core/adapter.js +33 -0
- package/dist/runtime/core/api.d.ts +17 -40
- package/dist/runtime/core/api.js +14 -68
- package/dist/runtime/core/errors.d.ts +22 -0
- package/dist/runtime/core/errors.js +33 -0
- package/dist/runtime/core/store.d.ts +3 -2
- package/dist/runtime/core/store.js +53 -32
- package/dist/runtime/index.d.ts +6 -2
- package/dist/runtime/index.js +3 -1
- package/dist/runtime/shared.d.ts +4 -2
- package/dist/runtime/utils/endpoint.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Define your data schema once with Zod, and Harlemify handles the rest: type-safe
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Schema-Driven** - Zod schema defines types, validation, and API payloads
|
|
12
|
-
- **
|
|
12
|
+
- **Custom Adapters** - Pluggable HTTP adapters for custom request handling
|
|
13
13
|
- **Reactive Memory** - Unit and collection caching with Vue reactivity
|
|
14
14
|
- **Request Monitoring** - Track pending, success, and failed states
|
|
15
15
|
- **SSR Support** - Server-side rendering via Harlem SSR plugin
|
|
@@ -28,7 +28,10 @@ export default defineNuxtConfig({
|
|
|
28
28
|
modules: ["@diphyx/harlemify"],
|
|
29
29
|
harlemify: {
|
|
30
30
|
api: {
|
|
31
|
-
|
|
31
|
+
adapter: {
|
|
32
|
+
baseURL: "https://api.example.com",
|
|
33
|
+
timeout: 10000,
|
|
34
|
+
},
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
});
|
|
@@ -56,6 +59,89 @@ export const userStore = createStore("user", UserSchema, {
|
|
|
56
59
|
});
|
|
57
60
|
```
|
|
58
61
|
|
|
62
|
+
## Custom Adapters
|
|
63
|
+
|
|
64
|
+
Harlemify supports custom HTTP adapters for advanced use cases like streaming responses, file uploads with progress, or custom authentication.
|
|
65
|
+
|
|
66
|
+
### Adapter Hierarchy
|
|
67
|
+
|
|
68
|
+
Adapters are resolved in the following order (highest to lowest priority):
|
|
69
|
+
|
|
70
|
+
1. **Endpoint adapter** - Per-endpoint custom adapter
|
|
71
|
+
2. **Store adapter** - Store-level adapter option
|
|
72
|
+
3. **Module adapter** - Global config in `nuxt.config.ts`
|
|
73
|
+
4. **Default adapter** - Built-in fetch adapter
|
|
74
|
+
|
|
75
|
+
### Built-in Adapter
|
|
76
|
+
|
|
77
|
+
Use `defineApiAdapter` to create an adapter with custom options:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { defineApiAdapter } from "@diphyx/harlemify";
|
|
81
|
+
|
|
82
|
+
const customAdapter = defineApiAdapter({
|
|
83
|
+
baseURL: "/api",
|
|
84
|
+
timeout: 5000,
|
|
85
|
+
retry: 3,
|
|
86
|
+
retryDelay: 1000,
|
|
87
|
+
retryStatusCodes: [500, 502, 503],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Custom Adapter
|
|
92
|
+
|
|
93
|
+
Create a fully custom adapter for advanced scenarios:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import type { ApiAdapter, ApiAdapterRequest } from "@diphyx/harlemify";
|
|
97
|
+
|
|
98
|
+
const streamingAdapter: ApiAdapter<MyType> = async (request: ApiAdapterRequest) => {
|
|
99
|
+
// Custom fetch logic with streaming, progress, etc.
|
|
100
|
+
const response = await fetch(request.url, {
|
|
101
|
+
method: request.method,
|
|
102
|
+
headers: request.headers,
|
|
103
|
+
body: JSON.stringify(request.body),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const data = await response.json();
|
|
107
|
+
return { data, status: response.status };
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Using Adapters
|
|
112
|
+
|
|
113
|
+
**Store-level adapter:**
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
export const userStore = createStore(
|
|
117
|
+
"user",
|
|
118
|
+
UserSchema,
|
|
119
|
+
{
|
|
120
|
+
[Endpoint.GET_UNITS]: { method: EndpointMethod.GET, url: "/users" },
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
adapter: customAdapter, // Used for all endpoints in this store
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Endpoint-level adapter:**
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
export const userStore = createStore("user", UserSchema, {
|
|
132
|
+
[Endpoint.GET_UNIT]: {
|
|
133
|
+
method: EndpointMethod.GET,
|
|
134
|
+
url: (p) => `/users/${p.id}`,
|
|
135
|
+
adapter: detailAdapter, // Custom adapter for this endpoint only
|
|
136
|
+
},
|
|
137
|
+
[Endpoint.GET_UNITS]: {
|
|
138
|
+
method: EndpointMethod.GET,
|
|
139
|
+
url: "/users",
|
|
140
|
+
// Uses store or global adapter
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
59
145
|
## Why Harlemify?
|
|
60
146
|
|
|
61
147
|
| | |
|
|
@@ -63,7 +149,7 @@ export const userStore = createStore("user", UserSchema, {
|
|
|
63
149
|
| **Type-Safe** | Full TypeScript support with Zod schema inference |
|
|
64
150
|
| **Declarative** | Define schema once, derive everything else |
|
|
65
151
|
| **Reactive** | Powered by Vue's reactivity through Harlem |
|
|
66
|
-
| **
|
|
152
|
+
| **Extensible** | Custom adapters for any HTTP requirement |
|
|
67
153
|
|
|
68
154
|
## Documentation
|
|
69
155
|
|
package/dist/module.d.mts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
+
interface ApiFetchAdapterOptions {
|
|
4
|
+
baseURL?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
retry?: number | false;
|
|
7
|
+
retryDelay?: number;
|
|
8
|
+
retryStatusCodes?: number[];
|
|
9
|
+
responseType?: "json" | "text" | "blob" | "arrayBuffer";
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
type SharedConfig = {
|
|
4
13
|
api?: {
|
|
5
|
-
|
|
6
|
-
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
query?: Record<string, unknown>;
|
|
16
|
+
adapter?: ApiFetchAdapterOptions;
|
|
7
17
|
};
|
|
8
18
|
};
|
|
9
19
|
|
package/dist/module.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
+
interface ApiFetchAdapterOptions {
|
|
4
|
+
baseURL?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
retry?: number | false;
|
|
7
|
+
retryDelay?: number;
|
|
8
|
+
retryStatusCodes?: number[];
|
|
9
|
+
responseType?: "json" | "text" | "blob" | "arrayBuffer";
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
type SharedConfig = {
|
|
4
13
|
api?: {
|
|
5
|
-
|
|
6
|
-
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
query?: Record<string, unknown>;
|
|
16
|
+
adapter?: ApiFetchAdapterOptions;
|
|
7
17
|
};
|
|
8
18
|
};
|
|
9
19
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { EndpointMethod } from "../utils/endpoint.js";
|
|
2
|
+
export interface ApiAdapterRequest {
|
|
3
|
+
method: EndpointMethod;
|
|
4
|
+
url: string;
|
|
5
|
+
body?: unknown;
|
|
6
|
+
query?: Record<string, unknown>;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export interface ApiAdapterResponse<T = unknown> {
|
|
11
|
+
data: T;
|
|
12
|
+
status?: number;
|
|
13
|
+
}
|
|
14
|
+
export type ApiAdapter<T = unknown> = (request: ApiAdapterRequest) => Promise<ApiAdapterResponse<T>>;
|
|
15
|
+
export type DefineApiAdapter<T = unknown, O = unknown> = (options?: O) => ApiAdapter<T>;
|
|
16
|
+
export interface ApiFetchAdapterOptions {
|
|
17
|
+
baseURL?: string;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
retry?: number | false;
|
|
20
|
+
retryDelay?: number;
|
|
21
|
+
retryStatusCodes?: number[];
|
|
22
|
+
responseType?: "json" | "text" | "blob" | "arrayBuffer";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a built-in fetch adapter with configurable options.
|
|
26
|
+
*
|
|
27
|
+
* Adapter hierarchy (highest to lowest priority):
|
|
28
|
+
* 1. endpoint.adapter - Per-endpoint adapter
|
|
29
|
+
* 2. storeOptions.adapter - Store-level adapter
|
|
30
|
+
* 3. sharedConfig.api.adapter - Module config adapter options
|
|
31
|
+
* 4. Default adapter (no options)
|
|
32
|
+
*/
|
|
33
|
+
export declare function defineApiAdapter<T = unknown>(options?: ApiFetchAdapterOptions): ApiAdapter<T>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ApiRequestError, ApiResponseError } from "./errors.js";
|
|
2
|
+
export function defineApiAdapter(options) {
|
|
3
|
+
return async (request) => {
|
|
4
|
+
const data = await $fetch(request.url, {
|
|
5
|
+
baseURL: options?.baseURL,
|
|
6
|
+
method: request.method,
|
|
7
|
+
headers: request.headers,
|
|
8
|
+
query: request.query,
|
|
9
|
+
body: request.body,
|
|
10
|
+
timeout: options?.timeout,
|
|
11
|
+
signal: request.signal,
|
|
12
|
+
retry: options?.retry,
|
|
13
|
+
retryDelay: options?.retryDelay,
|
|
14
|
+
retryStatusCodes: options?.retryStatusCodes,
|
|
15
|
+
responseType: options?.responseType,
|
|
16
|
+
onRequestError({ request: request2, options: options2, error }) {
|
|
17
|
+
throw new ApiRequestError({
|
|
18
|
+
method: options2.method,
|
|
19
|
+
url: request2.toString(),
|
|
20
|
+
message: error?.message
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
onResponseError({ request: request2, options: options2, error }) {
|
|
24
|
+
throw new ApiResponseError({
|
|
25
|
+
method: options2.method,
|
|
26
|
+
url: request2.toString(),
|
|
27
|
+
message: error?.message
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return { data };
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import type { MaybeRefOrGetter } from "vue";
|
|
2
2
|
import { EndpointMethod } from "../utils/endpoint.js";
|
|
3
|
-
|
|
4
|
-
JSON = "json",
|
|
5
|
-
TEXT = "text",
|
|
6
|
-
BLOB = "blob",
|
|
7
|
-
ARRAY_BUFFER = "arrayBuffer"
|
|
8
|
-
}
|
|
9
|
-
export declare enum ApiErrorSource {
|
|
10
|
-
REQUEST = "request",
|
|
11
|
-
RESPONSE = "response"
|
|
12
|
-
}
|
|
3
|
+
import { type ApiAdapter } from "./adapter.js";
|
|
13
4
|
export type ApiRequestHeader = MaybeRefOrGetter<Record<string, unknown>>;
|
|
14
5
|
export type ApiRequestQuery = MaybeRefOrGetter<Record<string, unknown>>;
|
|
15
6
|
export type ApiRequestBody = MaybeRefOrGetter<string | number | ArrayBuffer | FormData | Blob | Record<string, any>>;
|
|
@@ -18,43 +9,29 @@ export interface ApiRequestOptions<A extends EndpointMethod = EndpointMethod, H
|
|
|
18
9
|
headers?: H;
|
|
19
10
|
query?: Q;
|
|
20
11
|
body?: B;
|
|
21
|
-
timeout?: number;
|
|
22
|
-
responseType?: ApiResponseType;
|
|
23
|
-
retry?: number | false;
|
|
24
|
-
retryDelay?: number;
|
|
25
|
-
retryStatusCodes?: number[];
|
|
26
12
|
signal?: AbortSignal;
|
|
27
13
|
}
|
|
28
14
|
export interface ApiOptions {
|
|
29
|
-
url?: string;
|
|
30
15
|
headers?: ApiRequestHeader;
|
|
31
16
|
query?: ApiRequestQuery;
|
|
32
|
-
|
|
17
|
+
adapter?: ApiAdapter<any>;
|
|
33
18
|
}
|
|
34
19
|
export type EndpointMethodOptions<A extends EndpointMethod, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody> = Omit<ApiRequestOptions<A, H, Q, B>, "action">;
|
|
35
|
-
export interface ApiErrorOptions {
|
|
36
|
-
source: ApiErrorSource;
|
|
37
|
-
method: string;
|
|
38
|
-
url: string;
|
|
39
|
-
message?: string;
|
|
40
|
-
}
|
|
41
|
-
export declare class ApiError extends Error {
|
|
42
|
-
source: ApiErrorSource;
|
|
43
|
-
method: string;
|
|
44
|
-
url: string;
|
|
45
|
-
constructor(options: ApiErrorOptions);
|
|
46
|
-
}
|
|
47
|
-
export declare class ApiRequestError extends ApiError {
|
|
48
|
-
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
49
|
-
}
|
|
50
|
-
export declare class ApiResponseError extends ApiError {
|
|
51
|
-
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
52
|
-
}
|
|
53
20
|
export interface Api {
|
|
54
|
-
get: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: EndpointMethodOptions<EndpointMethod.GET, H, Q, never>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
21
|
+
get: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: EndpointMethodOptions<EndpointMethod.GET, H, Q, never> & {
|
|
22
|
+
adapter?: ApiAdapter<T>;
|
|
23
|
+
}) => Promise<T>;
|
|
24
|
+
post: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.POST, H, Q, B> & {
|
|
25
|
+
adapter?: ApiAdapter<T>;
|
|
26
|
+
}) => Promise<T>;
|
|
27
|
+
put: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.PUT, H, Q, B> & {
|
|
28
|
+
adapter?: ApiAdapter<T>;
|
|
29
|
+
}) => Promise<T>;
|
|
30
|
+
patch: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.PATCH, H, Q, B> & {
|
|
31
|
+
adapter?: ApiAdapter<T>;
|
|
32
|
+
}) => Promise<T>;
|
|
33
|
+
del: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: EndpointMethodOptions<EndpointMethod.DELETE, H, Q, never> & {
|
|
34
|
+
adapter?: ApiAdapter<T>;
|
|
35
|
+
}) => Promise<T>;
|
|
59
36
|
}
|
|
60
37
|
export declare function createApi(options?: ApiOptions): Api;
|
package/dist/runtime/core/api.js
CHANGED
|
@@ -1,80 +1,26 @@
|
|
|
1
1
|
import { toValue } from "vue";
|
|
2
2
|
import { EndpointMethod } from "../utils/endpoint.js";
|
|
3
|
-
|
|
4
|
-
ApiResponseType2["JSON"] = "json";
|
|
5
|
-
ApiResponseType2["TEXT"] = "text";
|
|
6
|
-
ApiResponseType2["BLOB"] = "blob";
|
|
7
|
-
ApiResponseType2["ARRAY_BUFFER"] = "arrayBuffer";
|
|
8
|
-
return ApiResponseType2;
|
|
9
|
-
})(ApiResponseType || {});
|
|
10
|
-
export var ApiErrorSource = /* @__PURE__ */ ((ApiErrorSource2) => {
|
|
11
|
-
ApiErrorSource2["REQUEST"] = "request";
|
|
12
|
-
ApiErrorSource2["RESPONSE"] = "response";
|
|
13
|
-
return ApiErrorSource2;
|
|
14
|
-
})(ApiErrorSource || {});
|
|
15
|
-
export class ApiError extends Error {
|
|
16
|
-
source;
|
|
17
|
-
method;
|
|
18
|
-
url;
|
|
19
|
-
constructor(options) {
|
|
20
|
-
super(options.message ?? "Unknown error");
|
|
21
|
-
this.name = "ApiError";
|
|
22
|
-
this.source = options.source;
|
|
23
|
-
this.method = options.method;
|
|
24
|
-
this.url = options.url;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export class ApiRequestError extends ApiError {
|
|
28
|
-
constructor(options) {
|
|
29
|
-
super({
|
|
30
|
-
...options,
|
|
31
|
-
source: "request" /* REQUEST */
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
export class ApiResponseError extends ApiError {
|
|
36
|
-
constructor(options) {
|
|
37
|
-
super({
|
|
38
|
-
...options,
|
|
39
|
-
source: "response" /* RESPONSE */
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
3
|
+
import { defineApiAdapter } from "./adapter.js";
|
|
43
4
|
export function createApi(options) {
|
|
5
|
+
const defaultAdapter = options?.adapter ?? defineApiAdapter();
|
|
44
6
|
async function request(url, requestOptions) {
|
|
45
|
-
|
|
46
|
-
|
|
7
|
+
const adapter = requestOptions?.adapter ?? defaultAdapter;
|
|
8
|
+
const adapterRequest = {
|
|
47
9
|
method: requestOptions?.action ?? EndpointMethod.GET,
|
|
10
|
+
url,
|
|
11
|
+
body: toValue(requestOptions?.body),
|
|
12
|
+
query: {
|
|
13
|
+
...toValue(options?.query),
|
|
14
|
+
...toValue(requestOptions?.query)
|
|
15
|
+
},
|
|
48
16
|
headers: {
|
|
49
17
|
...toValue(options?.headers),
|
|
50
18
|
...toValue(requestOptions?.headers)
|
|
51
19
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
body: toValue(requestOptions?.body),
|
|
57
|
-
timeout: requestOptions?.timeout ?? options?.timeout,
|
|
58
|
-
responseType: requestOptions?.responseType,
|
|
59
|
-
retry: requestOptions?.retry,
|
|
60
|
-
retryDelay: requestOptions?.retryDelay,
|
|
61
|
-
retryStatusCodes: requestOptions?.retryStatusCodes,
|
|
62
|
-
signal: requestOptions?.signal,
|
|
63
|
-
onRequestError({ request: request2, options: options2, error }) {
|
|
64
|
-
throw new ApiRequestError({
|
|
65
|
-
method: options2.method,
|
|
66
|
-
url: request2.toString(),
|
|
67
|
-
message: error?.message
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
onResponseError({ request: request2, options: options2, error }) {
|
|
71
|
-
throw new ApiResponseError({
|
|
72
|
-
method: options2.method,
|
|
73
|
-
url: request2.toString(),
|
|
74
|
-
message: error?.message
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
});
|
|
20
|
+
signal: requestOptions?.signal
|
|
21
|
+
};
|
|
22
|
+
const response = await adapter(adapterRequest);
|
|
23
|
+
return response.data;
|
|
78
24
|
}
|
|
79
25
|
async function get(url, options2) {
|
|
80
26
|
return request(url, {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare enum ApiErrorSource {
|
|
2
|
+
REQUEST = "request",
|
|
3
|
+
RESPONSE = "response"
|
|
4
|
+
}
|
|
5
|
+
export interface ApiErrorOptions {
|
|
6
|
+
source: ApiErrorSource;
|
|
7
|
+
method: string;
|
|
8
|
+
url: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ApiError extends Error {
|
|
12
|
+
source: ApiErrorSource;
|
|
13
|
+
method: string;
|
|
14
|
+
url: string;
|
|
15
|
+
constructor(options: ApiErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
export declare class ApiRequestError extends ApiError {
|
|
18
|
+
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
19
|
+
}
|
|
20
|
+
export declare class ApiResponseError extends ApiError {
|
|
21
|
+
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
22
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export var ApiErrorSource = /* @__PURE__ */ ((ApiErrorSource2) => {
|
|
2
|
+
ApiErrorSource2["REQUEST"] = "request";
|
|
3
|
+
ApiErrorSource2["RESPONSE"] = "response";
|
|
4
|
+
return ApiErrorSource2;
|
|
5
|
+
})(ApiErrorSource || {});
|
|
6
|
+
export class ApiError extends Error {
|
|
7
|
+
source;
|
|
8
|
+
method;
|
|
9
|
+
url;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super(options.message ?? "Unknown error");
|
|
12
|
+
this.name = "ApiError";
|
|
13
|
+
this.source = options.source;
|
|
14
|
+
this.method = options.method;
|
|
15
|
+
this.url = options.url;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class ApiRequestError extends ApiError {
|
|
19
|
+
constructor(options) {
|
|
20
|
+
super({
|
|
21
|
+
...options,
|
|
22
|
+
source: "request" /* REQUEST */
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class ApiResponseError extends ApiError {
|
|
27
|
+
constructor(options) {
|
|
28
|
+
super({
|
|
29
|
+
...options,
|
|
30
|
+
source: "response" /* RESPONSE */
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -2,7 +2,8 @@ import type { z } from "zod";
|
|
|
2
2
|
import type { ComputedRef } from "vue";
|
|
3
3
|
import type { Extension, BaseState } from "@harlem/core";
|
|
4
4
|
import { Endpoint, EndpointStatus } from "../utils/endpoint.js";
|
|
5
|
-
import type { EndpointMethodOptions
|
|
5
|
+
import type { EndpointMethodOptions } from "./api.js";
|
|
6
|
+
import type { ApiAdapter } from "./adapter.js";
|
|
6
7
|
import type { EndpointDefinition, EndpointStatusName } from "../utils/endpoint.js";
|
|
7
8
|
import type { Pluralize } from "../utils/transform.js";
|
|
8
9
|
export declare enum StoreMemoryAction {
|
|
@@ -19,7 +20,7 @@ export interface StoreHooks {
|
|
|
19
20
|
after?: (error?: Error) => Promise<void> | void;
|
|
20
21
|
}
|
|
21
22
|
export interface StoreOptions {
|
|
22
|
-
|
|
23
|
+
adapter?: ApiAdapter<any>;
|
|
23
24
|
indicator?: string;
|
|
24
25
|
hooks?: StoreHooks;
|
|
25
26
|
extensions?: Extension<BaseState>[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
2
|
import { createStore as createHarlemStore } from "@harlem/core";
|
|
3
3
|
import { createApi } from "./api.js";
|
|
4
|
+
import { defineApiAdapter } from "./adapter.js";
|
|
4
5
|
import { sharedConfig } from "../shared.js";
|
|
5
6
|
import { resolveSchema } from "../utils/schema.js";
|
|
6
7
|
import { pluralize } from "../utils/transform.js";
|
|
@@ -20,12 +21,14 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
20
21
|
const { indicator } = resolveSchema(schema, {
|
|
21
22
|
indicator: options?.indicator
|
|
22
23
|
});
|
|
24
|
+
const defaultAdapter = options?.adapter ?? defineApiAdapter(sharedConfig.api?.adapter);
|
|
23
25
|
let apiClient;
|
|
24
26
|
function api() {
|
|
25
27
|
if (!apiClient) {
|
|
26
28
|
apiClient = createApi({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
headers: sharedConfig.api?.headers,
|
|
30
|
+
query: sharedConfig.api?.query,
|
|
31
|
+
adapter: defaultAdapter
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
34
|
return apiClient;
|
|
@@ -127,18 +130,24 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
127
130
|
throw error;
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
|
-
async function getUnitEndpoint(unit,
|
|
133
|
+
async function getUnitEndpoint(unit, requestOptions) {
|
|
131
134
|
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNIT);
|
|
132
135
|
return withStatus(Endpoint.GET_UNIT, async () => {
|
|
133
|
-
const response = await api().get(resolveEndpointUrl(endpoint, unit),
|
|
136
|
+
const response = await api().get(resolveEndpointUrl(endpoint, unit), {
|
|
137
|
+
...requestOptions,
|
|
138
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
139
|
+
});
|
|
134
140
|
setMemorizedUnit(response);
|
|
135
141
|
return response;
|
|
136
142
|
});
|
|
137
143
|
}
|
|
138
|
-
async function getUnitsEndpoint(
|
|
144
|
+
async function getUnitsEndpoint(requestOptions) {
|
|
139
145
|
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNITS);
|
|
140
146
|
return withStatus(Endpoint.GET_UNITS, async () => {
|
|
141
|
-
const response = await api().get(resolveEndpointUrl(endpoint),
|
|
147
|
+
const response = await api().get(resolveEndpointUrl(endpoint), {
|
|
148
|
+
...requestOptions,
|
|
149
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
150
|
+
});
|
|
142
151
|
setMemorizedUnits(response);
|
|
143
152
|
return response;
|
|
144
153
|
});
|
|
@@ -152,27 +161,29 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
152
161
|
return withStatus(Endpoint.POST_UNIT, async () => {
|
|
153
162
|
const response = await api().post(resolveEndpointUrl(endpoint, unit), {
|
|
154
163
|
...actionOptions,
|
|
155
|
-
body: actionOptions?.body ?? resolvedSchema.values
|
|
164
|
+
body: actionOptions?.body ?? resolvedSchema.values,
|
|
165
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
156
166
|
});
|
|
157
167
|
setMemorizedUnit({ ...unit, ...response });
|
|
158
168
|
return response;
|
|
159
169
|
});
|
|
160
170
|
}
|
|
161
|
-
async function postUnitsEndpoint(units,
|
|
171
|
+
async function postUnitsEndpoint(units, requestOptions) {
|
|
162
172
|
const endpoint = getEndpoint(endpoints, Endpoint.POST_UNITS);
|
|
163
173
|
return withStatus(Endpoint.POST_UNITS, async () => {
|
|
164
174
|
const responses = [];
|
|
165
175
|
for (const unit of units) {
|
|
166
176
|
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
167
|
-
if (
|
|
177
|
+
if (requestOptions?.validate) {
|
|
168
178
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
169
179
|
}
|
|
170
180
|
const response = await api().post(resolveEndpointUrl(endpoint, unit), {
|
|
171
|
-
...
|
|
172
|
-
body:
|
|
181
|
+
...requestOptions,
|
|
182
|
+
body: requestOptions?.body ?? resolvedSchema.values,
|
|
183
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
173
184
|
});
|
|
174
185
|
const clonedUnits = [...memorizedUnits.value];
|
|
175
|
-
if (
|
|
186
|
+
if (requestOptions?.position === "last" /* LAST */) {
|
|
176
187
|
clonedUnits.push({ ...unit, ...response });
|
|
177
188
|
} else {
|
|
178
189
|
clonedUnits.unshift({ ...unit, ...response });
|
|
@@ -183,33 +194,35 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
183
194
|
return responses;
|
|
184
195
|
});
|
|
185
196
|
}
|
|
186
|
-
async function putUnitEndpoint(unit,
|
|
197
|
+
async function putUnitEndpoint(unit, requestOptions) {
|
|
187
198
|
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNIT);
|
|
188
199
|
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
189
|
-
if (
|
|
200
|
+
if (requestOptions?.validate) {
|
|
190
201
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
191
202
|
}
|
|
192
203
|
return withStatus(Endpoint.PUT_UNIT, async () => {
|
|
193
204
|
const response = await api().put(resolveEndpointUrl(endpoint, unit), {
|
|
194
|
-
...
|
|
195
|
-
body:
|
|
205
|
+
...requestOptions,
|
|
206
|
+
body: requestOptions?.body ?? resolvedSchema.values,
|
|
207
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
196
208
|
});
|
|
197
209
|
setMemorizedUnit({ ...unit, ...response });
|
|
198
210
|
return response;
|
|
199
211
|
});
|
|
200
212
|
}
|
|
201
|
-
async function putUnitsEndpoint(units,
|
|
213
|
+
async function putUnitsEndpoint(units, requestOptions) {
|
|
202
214
|
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNITS);
|
|
203
215
|
return withStatus(Endpoint.PUT_UNITS, async () => {
|
|
204
216
|
const responses = [];
|
|
205
217
|
for (const unit of units) {
|
|
206
218
|
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
207
|
-
if (
|
|
219
|
+
if (requestOptions?.validate) {
|
|
208
220
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
209
221
|
}
|
|
210
222
|
const response = await api().put(resolveEndpointUrl(endpoint, unit), {
|
|
211
|
-
...
|
|
212
|
-
body:
|
|
223
|
+
...requestOptions,
|
|
224
|
+
body: requestOptions?.body ?? resolvedSchema.values,
|
|
225
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
213
226
|
});
|
|
214
227
|
editMemorizedUnits([{ ...unit, ...response }]);
|
|
215
228
|
responses.push(response);
|
|
@@ -217,33 +230,35 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
217
230
|
return responses;
|
|
218
231
|
});
|
|
219
232
|
}
|
|
220
|
-
async function patchUnitEndpoint(unit,
|
|
233
|
+
async function patchUnitEndpoint(unit, requestOptions) {
|
|
221
234
|
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNIT);
|
|
222
235
|
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
223
|
-
if (
|
|
236
|
+
if (requestOptions?.validate) {
|
|
224
237
|
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
225
238
|
}
|
|
226
239
|
return withStatus(Endpoint.PATCH_UNIT, async () => {
|
|
227
240
|
const response = await api().patch(resolveEndpointUrl(endpoint, unit), {
|
|
228
|
-
...
|
|
229
|
-
body:
|
|
241
|
+
...requestOptions,
|
|
242
|
+
body: requestOptions?.body ?? resolvedSchema.values,
|
|
243
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
230
244
|
});
|
|
231
245
|
editMemorizedUnit({ ...unit, ...response });
|
|
232
246
|
return response;
|
|
233
247
|
});
|
|
234
248
|
}
|
|
235
|
-
async function patchUnitsEndpoint(units,
|
|
249
|
+
async function patchUnitsEndpoint(units, requestOptions) {
|
|
236
250
|
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNITS);
|
|
237
251
|
return withStatus(Endpoint.PATCH_UNITS, async () => {
|
|
238
252
|
const responses = [];
|
|
239
253
|
for (const unit of units) {
|
|
240
254
|
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
241
|
-
if (
|
|
255
|
+
if (requestOptions?.validate) {
|
|
242
256
|
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
243
257
|
}
|
|
244
258
|
const response = await api().patch(resolveEndpointUrl(endpoint, unit), {
|
|
245
|
-
...
|
|
246
|
-
body:
|
|
259
|
+
...requestOptions,
|
|
260
|
+
body: requestOptions?.body ?? resolvedSchema.values,
|
|
261
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
247
262
|
});
|
|
248
263
|
editMemorizedUnits([{ ...unit, ...response }]);
|
|
249
264
|
responses.push(response);
|
|
@@ -251,19 +266,25 @@ export function createStore(entity, schema, endpoints, options) {
|
|
|
251
266
|
return responses;
|
|
252
267
|
});
|
|
253
268
|
}
|
|
254
|
-
async function deleteUnitEndpoint(unit,
|
|
269
|
+
async function deleteUnitEndpoint(unit, requestOptions) {
|
|
255
270
|
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNIT);
|
|
256
271
|
return withStatus(Endpoint.DELETE_UNIT, async () => {
|
|
257
|
-
await api().del(resolveEndpointUrl(endpoint, unit),
|
|
272
|
+
await api().del(resolveEndpointUrl(endpoint, unit), {
|
|
273
|
+
...requestOptions,
|
|
274
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
275
|
+
});
|
|
258
276
|
dropMemorizedUnit(unit);
|
|
259
277
|
return true;
|
|
260
278
|
});
|
|
261
279
|
}
|
|
262
|
-
async function deleteUnitsEndpoint(units,
|
|
280
|
+
async function deleteUnitsEndpoint(units, requestOptions) {
|
|
263
281
|
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNITS);
|
|
264
282
|
return withStatus(Endpoint.DELETE_UNITS, async () => {
|
|
265
283
|
for (const unit of units) {
|
|
266
|
-
await api().del(resolveEndpointUrl(endpoint, unit),
|
|
284
|
+
await api().del(resolveEndpointUrl(endpoint, unit), {
|
|
285
|
+
...requestOptions,
|
|
286
|
+
...endpoint.adapter && { adapter: endpoint.adapter }
|
|
287
|
+
});
|
|
267
288
|
dropMemorizedUnits([unit]);
|
|
268
289
|
}
|
|
269
290
|
return true;
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export { EndpointMethod, Endpoint, EndpointStatus } from "./utils/endpoint.js";
|
|
2
2
|
export { getMeta, resolveSchema } from "./utils/schema.js";
|
|
3
|
-
export { createApi
|
|
3
|
+
export { createApi } from "./core/api.js";
|
|
4
|
+
export { ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/errors.js";
|
|
4
5
|
export { createStore, StoreMemoryPosition } from "./core/store.js";
|
|
6
|
+
export { defineApiAdapter } from "./core/adapter.js";
|
|
5
7
|
export { useStoreAlias } from "./composables/use.js";
|
|
6
8
|
export type { SharedConfig } from "./shared.js";
|
|
7
9
|
export type { EndpointDefinition } from "./utils/endpoint.js";
|
|
8
10
|
export type { SchemaMeta } from "./utils/schema.js";
|
|
9
|
-
export type { ApiRequestHeader, ApiRequestQuery, ApiRequestBody, ApiRequestOptions, ApiOptions, EndpointMethodOptions,
|
|
11
|
+
export type { Api, ApiRequestHeader, ApiRequestQuery, ApiRequestBody, ApiRequestOptions, ApiOptions, EndpointMethodOptions, } from "./core/api.js";
|
|
12
|
+
export type { ApiErrorOptions } from "./core/errors.js";
|
|
13
|
+
export type { ApiAdapter, DefineApiAdapter, ApiAdapterRequest, ApiAdapterResponse, ApiFetchAdapterOptions, } from "./core/adapter.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { EndpointMethod, Endpoint, EndpointStatus } from "./utils/endpoint.js";
|
|
2
2
|
export { getMeta, resolveSchema } from "./utils/schema.js";
|
|
3
|
-
export { createApi
|
|
3
|
+
export { createApi } from "./core/api.js";
|
|
4
|
+
export { ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/errors.js";
|
|
4
5
|
export { createStore, StoreMemoryPosition } from "./core/store.js";
|
|
6
|
+
export { defineApiAdapter } from "./core/adapter.js";
|
|
5
7
|
export { useStoreAlias } from "./composables/use.js";
|
package/dist/runtime/shared.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { ApiFetchAdapterOptions } from "./core/adapter.js";
|
|
1
2
|
export type SharedConfig = {
|
|
2
3
|
api?: {
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
query?: Record<string, unknown>;
|
|
6
|
+
adapter?: ApiFetchAdapterOptions;
|
|
5
7
|
};
|
|
6
8
|
};
|
|
7
9
|
export declare const sharedConfig: SharedConfig;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ApiAdapter } from "../core/adapter.js";
|
|
1
2
|
import type { Capitalize } from "./transform.js";
|
|
2
3
|
export declare enum EndpointMethod {
|
|
3
4
|
GET = "get",
|
|
@@ -27,6 +28,7 @@ export declare enum EndpointStatus {
|
|
|
27
28
|
export interface EndpointDefinition<T = Record<string, unknown>> {
|
|
28
29
|
method: EndpointMethod;
|
|
29
30
|
url: string | ((params: T) => string);
|
|
31
|
+
adapter?: ApiAdapter<any>;
|
|
30
32
|
}
|
|
31
33
|
export type EndpointStatusFlag<S extends EndpointStatus = EndpointStatus> = `Is${Capitalize<S>}`;
|
|
32
34
|
export type EndpointStatusName<K extends Endpoint = Endpoint, S extends EndpointStatus = EndpointStatus> = `${K}${EndpointStatusFlag<S>}`;
|