@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 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
- - **Automatic API Client** - Built-in HTTP client with runtime configuration
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
- url: "https://api.example.com",
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
- | **Simple** | Minimal boilerplate, maximum productivity |
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
- url?: string;
6
- timeout?: number;
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
- url?: string;
6
- timeout?: number;
14
+ headers?: Record<string, string>;
15
+ query?: Record<string, unknown>;
16
+ adapter?: ApiFetchAdapterOptions;
7
17
  };
8
18
  };
9
19
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0 || >=4.0.0"
6
6
  },
7
- "version": "1.0.4",
7
+ "version": "2.0.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -9,7 +9,11 @@ const module = defineNuxtModule({
9
9
  }
10
10
  },
11
11
  defaults: {
12
- api: {}
12
+ api: {
13
+ headers: {},
14
+ query: {},
15
+ adapter: {}
16
+ }
13
17
  },
14
18
  setup(options, nuxt) {
15
19
  const { resolve } = createResolver(import.meta.url);
@@ -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
- export declare enum ApiResponseType {
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
- timeout?: number;
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>) => Promise<T>;
55
- post: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.POST, H, Q, B>) => Promise<T>;
56
- put: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.PUT, H, Q, B>) => Promise<T>;
57
- patch: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: EndpointMethodOptions<EndpointMethod.PATCH, H, Q, B>) => Promise<T>;
58
- del: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: EndpointMethodOptions<EndpointMethod.DELETE, H, Q, never>) => Promise<T>;
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;
@@ -1,80 +1,26 @@
1
1
  import { toValue } from "vue";
2
2
  import { EndpointMethod } from "../utils/endpoint.js";
3
- export var ApiResponseType = /* @__PURE__ */ ((ApiResponseType2) => {
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
- return $fetch(url, {
46
- baseURL: options?.url,
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
- query: {
53
- ...toValue(requestOptions?.query),
54
- ...toValue(options?.query)
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, ApiOptions } from "./api.js";
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
- api?: ApiOptions;
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
- ...sharedConfig.api ?? {},
28
- ...options?.api
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, options2) {
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), options2);
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(options2) {
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), options2);
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, options2) {
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 (options2?.validate) {
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
- ...options2,
172
- body: options2?.body ?? resolvedSchema.values
181
+ ...requestOptions,
182
+ body: requestOptions?.body ?? resolvedSchema.values,
183
+ ...endpoint.adapter && { adapter: endpoint.adapter }
173
184
  });
174
185
  const clonedUnits = [...memorizedUnits.value];
175
- if (options2?.position === "last" /* LAST */) {
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, options2) {
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 (options2?.validate) {
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
- ...options2,
195
- body: options2?.body ?? resolvedSchema.values
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, options2) {
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 (options2?.validate) {
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
- ...options2,
212
- body: options2?.body ?? resolvedSchema.values
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, options2) {
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 (options2?.validate) {
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
- ...options2,
229
- body: options2?.body ?? resolvedSchema.values
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, options2) {
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 (options2?.validate) {
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
- ...options2,
246
- body: options2?.body ?? resolvedSchema.values
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, options2) {
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), options2);
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, options2) {
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), options2);
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;
@@ -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, ApiResponseType, ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/api.js";
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, ApiErrorOptions, } from "./core/api.js";
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";
@@ -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, ApiResponseType, ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/api.js";
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";
@@ -1,7 +1,9 @@
1
+ import type { ApiFetchAdapterOptions } from "./core/adapter.js";
1
2
  export type SharedConfig = {
2
3
  api?: {
3
- url?: string;
4
- timeout?: number;
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>}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diphyx/harlemify",
3
- "version": "1.0.4",
3
+ "version": "2.0.0",
4
4
  "description": "API state management for Nuxt powered by Harlem",
5
5
  "keywords": [
6
6
  "nuxt",