@diphyx/harlemify 0.0.2 → 0.0.3

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
@@ -11,6 +11,9 @@ API state management for Nuxt powered by [Harlem](https://harlemjs.com/)
11
11
  - CRUD operations with endpoint status tracking
12
12
  - Type-safe endpoint URL parameters
13
13
  - SSR support via Harlem SSR plugin
14
+ - Configurable primary key indicator
15
+ - Lifecycle hooks (before/after) for API operations
16
+ - Abort controller support for request cancellation
14
17
 
15
18
  ## Installation
16
19
 
@@ -47,24 +50,43 @@ const UserSchema = z.object({
47
50
 
48
51
  export type User = z.infer<typeof UserSchema>;
49
52
 
50
- export const userStore = createStore("user", UserSchema, {
51
- [Endpoint.GET_UNITS]: {
52
- action: ApiAction.GET,
53
- url: "/users",
54
- },
55
- [Endpoint.POST_UNITS]: {
56
- action: ApiAction.POST,
57
- url: "/users",
58
- },
59
- [Endpoint.PATCH_UNITS]: {
60
- action: ApiAction.PATCH,
61
- url: (params) => `/users/${params.id}`,
53
+ export const userStore = createStore(
54
+ "user",
55
+ UserSchema,
56
+ {
57
+ [Endpoint.GET_UNITS]: {
58
+ action: ApiAction.GET,
59
+ url: "/users",
60
+ },
61
+ [Endpoint.POST_UNITS]: {
62
+ action: ApiAction.POST,
63
+ url: "/users",
64
+ },
65
+ [Endpoint.PATCH_UNITS]: {
66
+ action: ApiAction.PATCH,
67
+ url: (params) => `/users/${params.id}`,
68
+ },
69
+ [Endpoint.DELETE_UNITS]: {
70
+ action: ApiAction.DELETE,
71
+ url: (params) => `/users/${params.id}`,
72
+ },
62
73
  },
63
- [Endpoint.DELETE_UNITS]: {
64
- action: ApiAction.DELETE,
65
- url: (params) => `/users/${params.id}`,
74
+ {
75
+ indicator: "id",
76
+ hooks: {
77
+ before() {
78
+ console.log("Request starting...");
79
+ },
80
+ after(error) {
81
+ if (error) {
82
+ console.error("Request failed:", error);
83
+ } else {
84
+ console.log("Request completed");
85
+ }
86
+ },
87
+ },
66
88
  },
67
- });
89
+ );
68
90
  ```
69
91
 
70
92
  ## Documentation
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": "0.0.2",
7
+ "version": "0.0.3",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "unknown"
@@ -24,10 +24,12 @@ export interface ApiRequestOptions<A extends ApiAction = ApiAction, H extends Ap
24
24
  headers?: H;
25
25
  query?: Q;
26
26
  body?: B;
27
+ timeout?: number;
27
28
  responseType?: ApiResponseType;
28
29
  retry?: number | false;
29
30
  retryDelay?: number;
30
31
  retryStatusCodes?: number[];
32
+ signal?: AbortSignal;
31
33
  }
32
34
  export interface ApiOptions {
33
35
  url?: string;
@@ -66,6 +66,7 @@ export function createApi(options) {
66
66
  retry: requestOptions?.retry,
67
67
  retryDelay: requestOptions?.retryDelay,
68
68
  retryStatusCodes: requestOptions?.retryStatusCodes,
69
+ signal: requestOptions?.signal,
69
70
  onRequestError({ request: request2, options: options2, error }) {
70
71
  throw new ApiRequestError({
71
72
  method: options2.method,
@@ -6,10 +6,20 @@ export declare enum StoreMemoryPosition {
6
6
  LAST = "last"
7
7
  }
8
8
  import { ApiAction, type ApiActionOptions, type ApiOptions } from "./api.js";
9
- export declare function createStore<T extends z.ZodRawShape, K extends keyof z.infer<z.ZodObject<T>> = "id" & keyof z.infer<z.ZodObject<T>>>(name: string, schema: z.ZodObject<T>, endpoints?: Partial<Record<Endpoint, EndpointDefinition<Partial<z.infer<z.ZodObject<T>>>>>>, options?: {
9
+ export declare class StoreConfigurationError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ export interface StoreHooks {
13
+ before?: () => Promise<void> | void;
14
+ after?: (error?: Error) => Promise<void> | void;
15
+ }
16
+ export interface StoreOptions {
10
17
  api?: ApiOptions;
18
+ indicator?: string;
19
+ hooks?: StoreHooks;
11
20
  extensions?: Extension<BaseState>[];
12
- }): {
21
+ }
22
+ export declare function createStore<T extends z.ZodRawShape, K extends keyof z.infer<z.ZodObject<T>> = "id" & keyof z.infer<z.ZodObject<T>>>(name: string, schema: z.ZodObject<T>, endpoints?: Partial<Record<Endpoint, EndpointDefinition<Partial<z.infer<z.ZodObject<T>>>>>>, options?: StoreOptions): {
13
23
  api: () => {
14
24
  get: <T_1, H extends import("./api").ApiRequestHeader = import("./api").ApiRequestHeader, Q extends import("./api").ApiRequestQuery = import("./api").ApiRequestQuery>(url: string, options?: ApiActionOptions<ApiAction.GET, H, Q, never>) => Promise<T_1>;
15
25
  post: <T_1, H extends import("./api").ApiRequestHeader = import("./api").ApiRequestHeader, Q extends import("./api").ApiRequestQuery = import("./api").ApiRequestQuery, B extends import("./api").ApiRequestBody = import("./api").ApiRequestBody>(url: string, options?: ApiActionOptions<ApiAction.POST, H, Q, B>) => Promise<T_1>;
@@ -24,51 +34,10 @@ export declare function createStore<T extends z.ZodRawShape, K extends keyof z.i
24
34
  };
25
35
  endpoints: Record<Endpoint, EndpointMemory>;
26
36
  }>, never>;
27
- memorizedUnit: import("@vue/reactivity").ComputedRef<import("@vue/reactivity").DeepReadonly<z.core.$InferObjectOutput<T, {}>> | null>;
28
- memorizedUnits: import("@vue/reactivity").ComputedRef<readonly import("@vue/reactivity").DeepReadonly<z.core.$InferObjectOutput<T, {}>>[]>;
37
+ memorizedUnit: import("vue").ComputedRef<import("vue").DeepReadonly<z.core.$InferObjectOutput<T, {}>> | null>;
38
+ memorizedUnits: import("vue").ComputedRef<readonly import("vue").DeepReadonly<z.core.$InferObjectOutput<T, {}>>[]>;
29
39
  hasMemorizedUnits: (...units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => Record<string | number, boolean>;
30
- endpointsStatus: {
31
- getUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
32
- getUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
33
- getUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
34
- getUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
35
- getUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
36
- getUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
37
- getUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
38
- getUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
39
- postUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
40
- postUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
41
- postUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
42
- postUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
43
- postUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
44
- postUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
45
- postUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
46
- postUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
47
- putUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
48
- putUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
49
- putUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
50
- putUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
51
- putUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
52
- putUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
53
- putUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
54
- putUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
55
- patchUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
56
- patchUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
57
- patchUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
58
- patchUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
59
- patchUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
60
- patchUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
61
- patchUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
62
- patchUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
63
- deleteUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
64
- deleteUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
65
- deleteUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
66
- deleteUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
67
- deleteUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
68
- deleteUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
69
- deleteUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
70
- deleteUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
71
- };
40
+ endpointsStatus: import("../utils/endpoint").EndpointsStatusMap<import("vue").ComputedRef<boolean>>;
72
41
  setMemorizedUnit: import("@harlem/core").Mutation<z.core.$InferObjectOutput<T, {}> | null, void>;
73
42
  setMemorizedUnits: (payload: z.core.$InferObjectOutput<T, {}>[]) => void;
74
43
  editMemorizedUnit: import("@harlem/core").Mutation<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, void>;
@@ -82,7 +51,7 @@ export declare function createStore<T extends z.ZodRawShape, K extends keyof z.i
82
51
  purgeEndpointMemory: (payload?: unknown) => void;
83
52
  getUnit: (unit?: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, options?: Omit<ApiActionOptions<ApiAction.GET>, "body">) => Promise<z.core.$InferObjectOutput<T, {}>>;
84
53
  getUnits: (options?: Omit<ApiActionOptions<ApiAction.GET>, "body">) => Promise<z.core.$InferObjectOutput<T, {}>[]>;
85
- postUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>, options?: ApiActionOptions<ApiAction.POST> & {
54
+ postUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>, actionOptions?: ApiActionOptions<ApiAction.POST> & {
86
55
  validate?: boolean;
87
56
  }) => Promise<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>>;
88
57
  postUnits: (units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>)[], options?: ApiActionOptions<ApiAction.POST> & {
@@ -19,16 +19,41 @@ export var StoreMemoryPosition = /* @__PURE__ */ ((StoreMemoryPosition2) => {
19
19
  import {
20
20
  createApi
21
21
  } from "./api.js";
22
+ export class StoreConfigurationError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "StoreConfigurationError";
26
+ }
27
+ }
22
28
  export function createStore(name, schema, endpoints, options) {
23
- const { indicator } = resolveSchema(schema);
29
+ const { indicator } = resolveSchema(schema, {
30
+ indicator: options?.indicator
31
+ });
32
+ const hooks = options?.hooks;
24
33
  let apiClient;
34
+ let apiInitError = null;
25
35
  function api() {
36
+ if (apiInitError) {
37
+ throw apiInitError;
38
+ }
26
39
  if (!apiClient) {
27
- const config = useRuntimeConfig();
28
- apiClient = createApi({
29
- ...config.public.harlemify?.api,
30
- ...options?.api
31
- });
40
+ try {
41
+ const config = useRuntimeConfig();
42
+ if (!config) {
43
+ throw new StoreConfigurationError(
44
+ `Runtime config is not available. Ensure the store "${name}" is used within a Nuxt context.`
45
+ );
46
+ }
47
+ apiClient = createApi({
48
+ ...config.public.harlemify?.api,
49
+ ...options?.api
50
+ });
51
+ } catch (error) {
52
+ apiInitError = error instanceof Error ? error : new StoreConfigurationError(
53
+ `Failed to initialize API client for store "${name}": ${String(error)}`
54
+ );
55
+ throw apiInitError;
56
+ }
32
57
  }
33
58
  return apiClient;
34
59
  }
@@ -149,95 +174,83 @@ export function createStore(name, schema, endpoints, options) {
149
174
  memory
150
175
  });
151
176
  }
152
- async function getUnit(unit, options2) {
153
- const endpoint = getEndpoint(endpoints, Endpoint.GET_UNIT);
154
- patchEndpointMemoryTo(Endpoint.GET_UNIT, {
177
+ async function withEndpointStatus(key, operation) {
178
+ await hooks?.before?.();
179
+ patchEndpointMemoryTo(key, {
155
180
  status: EndpointStatus.PENDING
156
181
  });
157
182
  try {
158
- const response = await api().get(
159
- resolveEndpointUrl(endpoint, unit),
160
- options2
161
- );
162
- setMemorizedUnit(response);
163
- patchEndpointMemoryTo(Endpoint.GET_UNIT, {
183
+ const result = await operation();
184
+ patchEndpointMemoryTo(key, {
164
185
  status: EndpointStatus.SUCCESS
165
186
  });
166
- return response;
187
+ await hooks?.after?.();
188
+ return result;
167
189
  } catch (error) {
168
- patchEndpointMemoryTo(Endpoint.GET_UNIT, {
190
+ patchEndpointMemoryTo(key, {
169
191
  status: EndpointStatus.FAILED
170
192
  });
193
+ await hooks?.after?.(error);
171
194
  throw error;
172
195
  }
173
196
  }
197
+ async function getUnit(unit, options2) {
198
+ const endpoint = getEndpoint(endpoints, Endpoint.GET_UNIT);
199
+ return withEndpointStatus(Endpoint.GET_UNIT, async () => {
200
+ const response = await api().get(
201
+ resolveEndpointUrl(endpoint, unit),
202
+ options2
203
+ );
204
+ setMemorizedUnit(response);
205
+ return response;
206
+ });
207
+ }
174
208
  async function getUnits(options2) {
175
209
  const endpoint = getEndpoint(endpoints, Endpoint.GET_UNITS);
176
- patchEndpointMemoryTo(Endpoint.GET_UNITS, {
177
- status: EndpointStatus.PENDING
178
- });
179
- try {
210
+ return withEndpointStatus(Endpoint.GET_UNITS, async () => {
180
211
  const response = await api().get(
181
212
  resolveEndpointUrl(endpoint),
182
213
  options2
183
214
  );
184
215
  setMemorizedUnits(response);
185
- patchEndpointMemoryTo(Endpoint.GET_UNITS, {
186
- status: EndpointStatus.SUCCESS
187
- });
188
216
  return response;
189
- } catch (error) {
190
- patchEndpointMemoryTo(Endpoint.GET_UNITS, {
191
- status: EndpointStatus.FAILED
192
- });
193
- throw error;
194
- }
217
+ });
195
218
  }
196
- async function postUnit(unit, options2) {
219
+ async function postUnit(unit, actionOptions) {
197
220
  const endpoint = getEndpoint(endpoints, Endpoint.POST_UNIT);
198
- const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
199
- if (options2?.validate) {
221
+ const resolvedSchema = resolveSchema(schema, {
222
+ indicator,
223
+ endpoint,
224
+ unit
225
+ });
226
+ if (actionOptions?.validate) {
200
227
  schema.pick(resolvedSchema.keys).parse(unit);
201
228
  }
202
- patchEndpointMemoryTo(Endpoint.POST_UNIT, {
203
- status: EndpointStatus.PENDING
204
- });
205
- try {
229
+ return withEndpointStatus(Endpoint.POST_UNIT, async () => {
206
230
  const response = await api().post(
207
231
  resolveEndpointUrl(endpoint, unit),
208
232
  {
209
- ...options2,
210
- body: options2?.body ?? resolvedSchema.values
233
+ ...actionOptions,
234
+ body: actionOptions?.body ?? resolvedSchema.values
211
235
  }
212
236
  );
213
237
  setMemorizedUnit({
214
238
  ...unit,
215
239
  ...response
216
240
  });
217
- patchEndpointMemoryTo(Endpoint.POST_UNIT, {
218
- status: EndpointStatus.SUCCESS
219
- });
220
241
  return response;
221
- } catch (error) {
222
- patchEndpointMemoryTo(Endpoint.POST_UNIT, {
223
- status: EndpointStatus.FAILED
224
- });
225
- throw error;
226
- }
242
+ });
227
243
  }
228
244
  async function postUnits(units, options2) {
229
245
  const endpoint = getEndpoint(endpoints, Endpoint.POST_UNITS);
230
- patchEndpointMemoryTo(Endpoint.POST_UNITS, {
231
- status: EndpointStatus.PENDING
232
- });
233
- try {
246
+ return withEndpointStatus(Endpoint.POST_UNITS, async () => {
234
247
  const responses = [];
235
248
  for (const unit of units) {
236
- const resolvedSchema = resolveSchema(
237
- schema,
238
- endpoint.action,
249
+ const resolvedSchema = resolveSchema(schema, {
250
+ indicator,
251
+ endpoint,
239
252
  unit
240
- );
253
+ });
241
254
  if (options2?.validate) {
242
255
  schema.pick(resolvedSchema.keys).parse(unit);
243
256
  }
@@ -263,27 +276,20 @@ export function createStore(name, schema, endpoints, options) {
263
276
  setMemorizedUnits(clonedUnits);
264
277
  responses.push(response);
265
278
  }
266
- patchEndpointMemoryTo(Endpoint.POST_UNITS, {
267
- status: EndpointStatus.SUCCESS
268
- });
269
279
  return responses;
270
- } catch (error) {
271
- patchEndpointMemoryTo(Endpoint.POST_UNITS, {
272
- status: EndpointStatus.FAILED
273
- });
274
- throw error;
275
- }
280
+ });
276
281
  }
277
282
  async function putUnit(unit, options2) {
278
283
  const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNIT);
279
- const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
284
+ const resolvedSchema = resolveSchema(schema, {
285
+ indicator,
286
+ endpoint,
287
+ unit
288
+ });
280
289
  if (options2?.validate) {
281
290
  schema.pick(resolvedSchema.keys).parse(unit);
282
291
  }
283
- patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
284
- status: EndpointStatus.PENDING
285
- });
286
- try {
292
+ return withEndpointStatus(Endpoint.PUT_UNIT, async () => {
287
293
  const response = await api().put(
288
294
  resolveEndpointUrl(endpoint, unit),
289
295
  {
@@ -295,30 +301,19 @@ export function createStore(name, schema, endpoints, options) {
295
301
  ...unit,
296
302
  ...response
297
303
  });
298
- patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
299
- status: EndpointStatus.SUCCESS
300
- });
301
304
  return response;
302
- } catch (error) {
303
- patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
304
- status: EndpointStatus.FAILED
305
- });
306
- throw error;
307
- }
305
+ });
308
306
  }
309
307
  async function putUnits(units, options2) {
310
308
  const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNITS);
311
- patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
312
- status: EndpointStatus.PENDING
313
- });
314
- try {
309
+ return withEndpointStatus(Endpoint.PUT_UNITS, async () => {
315
310
  const responses = [];
316
311
  for (const unit of units) {
317
- const resolvedSchema = resolveSchema(
318
- schema,
319
- endpoint.action,
312
+ const resolvedSchema = resolveSchema(schema, {
313
+ indicator,
314
+ endpoint,
320
315
  unit
321
- );
316
+ });
322
317
  if (options2?.validate) {
323
318
  schema.pick(resolvedSchema.keys).parse(unit);
324
319
  }
@@ -337,27 +332,20 @@ export function createStore(name, schema, endpoints, options) {
337
332
  ]);
338
333
  responses.push(response);
339
334
  }
340
- patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
341
- status: EndpointStatus.SUCCESS
342
- });
343
335
  return responses;
344
- } catch (error) {
345
- patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
346
- status: EndpointStatus.FAILED
347
- });
348
- throw error;
349
- }
336
+ });
350
337
  }
351
338
  async function patchUnit(unit, options2) {
352
339
  const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNIT);
353
- const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
340
+ const resolvedSchema = resolveSchema(schema, {
341
+ indicator,
342
+ endpoint,
343
+ unit
344
+ });
354
345
  if (options2?.validate) {
355
346
  schema.pick(resolvedSchema.keys).partial().parse(unit);
356
347
  }
357
- patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
358
- status: EndpointStatus.PENDING
359
- });
360
- try {
348
+ return withEndpointStatus(Endpoint.PATCH_UNIT, async () => {
361
349
  const response = await api().patch(resolveEndpointUrl(endpoint, unit), {
362
350
  ...options2,
363
351
  body: options2?.body ?? resolvedSchema.values
@@ -366,30 +354,19 @@ export function createStore(name, schema, endpoints, options) {
366
354
  ...unit,
367
355
  ...response
368
356
  });
369
- patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
370
- status: EndpointStatus.SUCCESS
371
- });
372
357
  return response;
373
- } catch (error) {
374
- patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
375
- status: EndpointStatus.FAILED
376
- });
377
- throw error;
378
- }
358
+ });
379
359
  }
380
360
  async function patchUnits(units, options2) {
381
361
  const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNITS);
382
- patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
383
- status: EndpointStatus.PENDING
384
- });
385
- try {
362
+ return withEndpointStatus(Endpoint.PATCH_UNITS, async () => {
386
363
  const responses = [];
387
364
  for (const unit of units) {
388
- const resolvedSchema = resolveSchema(
389
- schema,
390
- endpoint.action,
365
+ const resolvedSchema = resolveSchema(schema, {
366
+ indicator,
367
+ endpoint,
391
368
  unit
392
- );
369
+ });
393
370
  if (options2?.validate) {
394
371
  schema.pick(resolvedSchema.keys).partial().parse(unit);
395
372
  }
@@ -405,45 +382,23 @@ export function createStore(name, schema, endpoints, options) {
405
382
  ]);
406
383
  responses.push(response);
407
384
  }
408
- patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
409
- status: EndpointStatus.SUCCESS
410
- });
411
385
  return responses;
412
- } catch (error) {
413
- patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
414
- status: EndpointStatus.FAILED
415
- });
416
- throw error;
417
- }
386
+ });
418
387
  }
419
388
  async function deleteUnit(unit, options2) {
420
389
  const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNIT);
421
- patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
422
- status: EndpointStatus.PENDING
423
- });
424
- try {
390
+ return withEndpointStatus(Endpoint.DELETE_UNIT, async () => {
425
391
  await api().del(
426
392
  resolveEndpointUrl(endpoint, unit),
427
393
  options2
428
394
  );
429
395
  dropMemorizedUnit(unit);
430
- patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
431
- status: EndpointStatus.SUCCESS
432
- });
433
396
  return true;
434
- } catch (error) {
435
- patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
436
- status: EndpointStatus.FAILED
437
- });
438
- throw error;
439
- }
397
+ });
440
398
  }
441
399
  async function deleteUnits(units, options2) {
442
400
  const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNITS);
443
- patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
444
- status: EndpointStatus.PENDING
445
- });
446
- try {
401
+ return withEndpointStatus(Endpoint.DELETE_UNITS, async () => {
447
402
  for (const unit of units) {
448
403
  await api().del(
449
404
  resolveEndpointUrl(endpoint, unit),
@@ -451,16 +406,8 @@ export function createStore(name, schema, endpoints, options) {
451
406
  );
452
407
  dropMemorizedUnits([unit]);
453
408
  }
454
- patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
455
- status: EndpointStatus.SUCCESS
456
- });
457
409
  return true;
458
- } catch (error) {
459
- patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
460
- status: EndpointStatus.FAILED
461
- });
462
- throw error;
463
- }
410
+ });
464
411
  }
465
412
  return {
466
413
  api,
@@ -25,50 +25,15 @@ export interface EndpointDefinition<T = Record<string, unknown>> {
25
25
  export interface EndpointMemory {
26
26
  status: EndpointStatus;
27
27
  }
28
- export declare function makeEndpointStatusKey<K extends Endpoint, S extends EndpointStatus>(key: K, status: S): `${K}Is${Capitalize<S>}`;
28
+ type CapitalizeString<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S;
29
+ export type EndpointStatusKey<K extends Endpoint = Endpoint, S extends EndpointStatus = EndpointStatus> = `${K}Is${CapitalizeString<S>}`;
30
+ export declare function makeEndpointStatusKey<K extends Endpoint, S extends EndpointStatus>(key: K, status: S): EndpointStatusKey<K, S>;
29
31
  export declare function getEndpoint<T = Record<string, unknown>>(endpoints: Partial<Record<Endpoint, EndpointDefinition<T>>> | undefined, key: Endpoint): EndpointDefinition<T>;
30
32
  export declare function resolveEndpointUrl<T>(endpoint: EndpointDefinition<T>, params?: {
31
33
  [key: string]: unknown;
32
34
  }): string;
33
- export declare function makeEndpointsStatus<T>(getter: (name: string, fn: (state: BaseState) => boolean) => T): {
34
- getUnitIsIdle: T;
35
- getUnitIsPending: T;
36
- getUnitIsSuccess: T;
37
- getUnitIsFailed: T;
38
- getUnitsIsIdle: T;
39
- getUnitsIsPending: T;
40
- getUnitsIsSuccess: T;
41
- getUnitsIsFailed: T;
42
- postUnitIsIdle: T;
43
- postUnitIsPending: T;
44
- postUnitIsSuccess: T;
45
- postUnitIsFailed: T;
46
- postUnitsIsIdle: T;
47
- postUnitsIsPending: T;
48
- postUnitsIsSuccess: T;
49
- postUnitsIsFailed: T;
50
- putUnitIsIdle: T;
51
- putUnitIsPending: T;
52
- putUnitIsSuccess: T;
53
- putUnitIsFailed: T;
54
- putUnitsIsIdle: T;
55
- putUnitsIsPending: T;
56
- putUnitsIsSuccess: T;
57
- putUnitsIsFailed: T;
58
- patchUnitIsIdle: T;
59
- patchUnitIsPending: T;
60
- patchUnitIsSuccess: T;
61
- patchUnitIsFailed: T;
62
- patchUnitsIsIdle: T;
63
- patchUnitsIsPending: T;
64
- patchUnitsIsSuccess: T;
65
- patchUnitsIsFailed: T;
66
- deleteUnitIsIdle: T;
67
- deleteUnitIsPending: T;
68
- deleteUnitIsSuccess: T;
69
- deleteUnitIsFailed: T;
70
- deleteUnitsIsIdle: T;
71
- deleteUnitsIsPending: T;
72
- deleteUnitsIsSuccess: T;
73
- deleteUnitsIsFailed: T;
35
+ export type EndpointsStatusMap<T> = {
36
+ [K in Endpoint as EndpointStatusKey<K, EndpointStatus>]: T;
74
37
  };
38
+ export declare function makeEndpointsStatus<T>(getter: (name: string, fn: (state: BaseState) => boolean) => T): EndpointsStatusMap<T>;
39
+ export {};
@@ -19,7 +19,8 @@ export var EndpointStatus = /* @__PURE__ */ ((EndpointStatus2) => {
19
19
  return EndpointStatus2;
20
20
  })(EndpointStatus || {});
21
21
  export function makeEndpointStatusKey(key, status) {
22
- return `${key}Is${status.charAt(0).toUpperCase() + status.slice(1)}`;
22
+ const capitalizedStatus = status.charAt(0).toUpperCase() + status.slice(1);
23
+ return `${key}Is${capitalizedStatus}`;
23
24
  }
24
25
  export function getEndpoint(endpoints, key) {
25
26
  const endpoint = endpoints?.[key];
@@ -1,11 +1,17 @@
1
1
  import { z } from "zod";
2
2
  import type { ApiAction } from "../core/api.js";
3
+ import type { EndpointDefinition } from "./endpoint.js";
3
4
  export interface SchemaMeta {
4
5
  indicator?: boolean;
5
6
  actions?: ApiAction[];
6
7
  }
7
8
  export declare function getMeta(field: any): SchemaMeta | undefined;
8
- export declare function resolveSchema<T extends z.ZodRawShape, S extends z.infer<z.ZodObject<T>>>(schema: z.ZodObject<T>, action?: ApiAction, input?: Partial<S>): {
9
+ export interface ResolveSchemaOptions<S> {
10
+ indicator?: keyof S;
11
+ endpoint?: EndpointDefinition<Partial<S>>;
12
+ unit?: Partial<S>;
13
+ }
14
+ export declare function resolveSchema<T extends z.ZodRawShape, S extends z.infer<z.ZodObject<T>>>(schema: z.ZodObject<T>, options?: ResolveSchemaOptions<S>): {
9
15
  indicator: keyof S;
10
16
  keys: Record<keyof S, true>;
11
17
  values: Partial<S>;
@@ -1,9 +1,9 @@
1
1
  export function getMeta(field) {
2
2
  return field.meta();
3
3
  }
4
- export function resolveSchema(schema, action, input) {
4
+ export function resolveSchema(schema, options) {
5
5
  const output = {
6
- indicator: "id",
6
+ indicator: options?.indicator ?? "id",
7
7
  keys: {},
8
8
  values: {}
9
9
  };
@@ -12,13 +12,13 @@ export function resolveSchema(schema, action, input) {
12
12
  if (meta?.indicator) {
13
13
  output.indicator = key;
14
14
  }
15
- if (!action || !meta?.actions) {
15
+ if (!options?.endpoint?.action || !meta?.actions) {
16
16
  continue;
17
17
  }
18
- if (meta?.actions.includes(action)) {
18
+ if (meta?.actions.includes(options.endpoint.action)) {
19
19
  output.keys[key] = true;
20
- if (input && key in input) {
21
- output.values[key] = input[key];
20
+ if (options?.unit && key in options.unit) {
21
+ output.values[key] = options.unit[key];
22
22
  }
23
23
  }
24
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diphyx/harlemify",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "API state management for Nuxt powered by Harlem",
5
5
  "keywords": [
6
6
  "nuxt",
@@ -52,7 +52,8 @@
52
52
  "eslint": "^9.0.0",
53
53
  "nuxt": "^3.14.0",
54
54
  "typescript": "^5.6.0",
55
- "vitest": "^2.0.0"
55
+ "vitest": "^2.0.0",
56
+ "vue": "^3.5.26"
56
57
  },
57
58
  "scripts": {
58
59
  "cleanup": "pnpm nuxt cleanup && pnpm nuxt cleanup playground",