@diphyx/harlemify 0.0.1
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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +19 -0
- package/dist/module.d.ts +19 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +31 -0
- package/dist/runtime/core/api.d.ts +63 -0
- package/dist/runtime/core/api.js +122 -0
- package/dist/runtime/core/store.d.ts +125 -0
- package/dist/runtime/core/store.js +440 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.js +16 -0
- package/dist/runtime/plugin.d.ts +2 -0
- package/dist/runtime/plugin.js +20 -0
- package/dist/runtime/utils/endpoint.d.ts +72 -0
- package/dist/runtime/utils/endpoint.js +48 -0
- package/dist/runtime/utils/schema.d.ts +12 -0
- package/dist/runtime/utils/schema.js +26 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +7 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amir Reza Dalir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Harlemify
|
|
2
|
+
|
|
3
|
+
API state management for Nuxt powered by [Harlem](https://harlemjs.com/)
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Zod schema validation with field metadata
|
|
10
|
+
- Automatic API client with runtime config
|
|
11
|
+
- CRUD operations with endpoint status tracking
|
|
12
|
+
- SSR support via Harlem SSR plugin
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @diphyx/harlemify
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// nuxt.config.ts
|
|
24
|
+
export default defineNuxtConfig({
|
|
25
|
+
modules: ["@diphyx/harlemify"],
|
|
26
|
+
harlemify: {
|
|
27
|
+
api: {
|
|
28
|
+
url: "https://api.example.com",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// stores/user.ts
|
|
36
|
+
import { z, createStore, Endpoint, ApiAction } from "@diphyx/harlemify";
|
|
37
|
+
|
|
38
|
+
const UserSchema = z.object({
|
|
39
|
+
id: z.number().meta({
|
|
40
|
+
indicator: true,
|
|
41
|
+
}),
|
|
42
|
+
name: z.string().meta({
|
|
43
|
+
actions: [ApiAction.POST, ApiAction.PUT],
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const userStore = createStore("user", UserSchema, {
|
|
48
|
+
[Endpoint.GET_UNITS]: {
|
|
49
|
+
action: ApiAction.GET,
|
|
50
|
+
url: "/users",
|
|
51
|
+
},
|
|
52
|
+
[Endpoint.POST_UNIT]: {
|
|
53
|
+
action: ApiAction.POST,
|
|
54
|
+
url: "/users",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
Full documentation available at [https://diphyx.github.io/harlemify/](https://diphyx.github.io/harlemify/)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/dist/module.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { MaybeRefOrGetter } from 'vue';
|
|
3
|
+
|
|
4
|
+
type ApiRequestHeader = MaybeRefOrGetter<Record<string, unknown>>;
|
|
5
|
+
type ApiRequestQuery = MaybeRefOrGetter<Record<string, unknown>>;
|
|
6
|
+
interface ApiOptions {
|
|
7
|
+
url?: string;
|
|
8
|
+
headers?: ApiRequestHeader;
|
|
9
|
+
query?: ApiRequestQuery;
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ModuleOptions {
|
|
14
|
+
api?: Pick<ApiOptions, "url" | "timeout">;
|
|
15
|
+
}
|
|
16
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
17
|
+
|
|
18
|
+
export { _default as default };
|
|
19
|
+
export type { ModuleOptions };
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { MaybeRefOrGetter } from 'vue';
|
|
3
|
+
|
|
4
|
+
type ApiRequestHeader = MaybeRefOrGetter<Record<string, unknown>>;
|
|
5
|
+
type ApiRequestQuery = MaybeRefOrGetter<Record<string, unknown>>;
|
|
6
|
+
interface ApiOptions {
|
|
7
|
+
url?: string;
|
|
8
|
+
headers?: ApiRequestHeader;
|
|
9
|
+
query?: ApiRequestQuery;
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ModuleOptions {
|
|
14
|
+
api?: Pick<ApiOptions, "url" | "timeout">;
|
|
15
|
+
}
|
|
16
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
17
|
+
|
|
18
|
+
export { _default as default };
|
|
19
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addPlugin, addImportsDir, updateRuntimeConfig } from '@nuxt/kit';
|
|
2
|
+
|
|
3
|
+
const module = defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "harlemify",
|
|
6
|
+
configKey: "harlemify",
|
|
7
|
+
compatibility: {
|
|
8
|
+
nuxt: ">=3.0.0 || >=4.0.0"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
defaults: {
|
|
12
|
+
api: {}
|
|
13
|
+
},
|
|
14
|
+
setup(options, _) {
|
|
15
|
+
const { resolve } = createResolver(import.meta.url);
|
|
16
|
+
addPlugin(resolve("./runtime", "plugin"));
|
|
17
|
+
addImportsDir(resolve("./runtime", "core"));
|
|
18
|
+
updateRuntimeConfig({
|
|
19
|
+
public: {
|
|
20
|
+
harlemify: options
|
|
21
|
+
},
|
|
22
|
+
options: {
|
|
23
|
+
build: {
|
|
24
|
+
transpile: [/@harlem\//]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export { module as default };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from "vue";
|
|
2
|
+
export declare enum ApiAction {
|
|
3
|
+
GET = "get",
|
|
4
|
+
POST = "post",
|
|
5
|
+
PUT = "put",
|
|
6
|
+
PATCH = "patch",
|
|
7
|
+
DELETE = "delete"
|
|
8
|
+
}
|
|
9
|
+
export declare enum ApiResponseType {
|
|
10
|
+
JSON = "json",
|
|
11
|
+
TEXT = "text",
|
|
12
|
+
BLOB = "blob",
|
|
13
|
+
ARRAY_BUFFER = "arrayBuffer"
|
|
14
|
+
}
|
|
15
|
+
export declare enum ApiErrorSource {
|
|
16
|
+
REQUEST = "request",
|
|
17
|
+
RESPONSE = "response"
|
|
18
|
+
}
|
|
19
|
+
export type ApiRequestHeader = MaybeRefOrGetter<Record<string, unknown>>;
|
|
20
|
+
export type ApiRequestQuery = MaybeRefOrGetter<Record<string, unknown>>;
|
|
21
|
+
export type ApiRequestBody = MaybeRefOrGetter<string | number | ArrayBuffer | FormData | Blob | Record<string, any>>;
|
|
22
|
+
export interface ApiRequestOptions<A extends ApiAction = ApiAction, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody> {
|
|
23
|
+
action?: A;
|
|
24
|
+
headers?: H;
|
|
25
|
+
query?: Q;
|
|
26
|
+
body?: B;
|
|
27
|
+
responseType?: ApiResponseType;
|
|
28
|
+
retry?: number | false;
|
|
29
|
+
retryDelay?: number;
|
|
30
|
+
retryStatusCodes?: number[];
|
|
31
|
+
}
|
|
32
|
+
export interface ApiOptions {
|
|
33
|
+
url?: string;
|
|
34
|
+
headers?: ApiRequestHeader;
|
|
35
|
+
query?: ApiRequestQuery;
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
export type ApiActionOptions<A extends ApiAction, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody> = Omit<ApiRequestOptions<A, H, Q, B>, "action">;
|
|
39
|
+
export interface ApiErrorOptions {
|
|
40
|
+
source: ApiErrorSource;
|
|
41
|
+
method: string;
|
|
42
|
+
url: string;
|
|
43
|
+
message?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare class ApiError extends Error {
|
|
46
|
+
source: ApiErrorSource;
|
|
47
|
+
method: string;
|
|
48
|
+
url: string;
|
|
49
|
+
constructor(options: ApiErrorOptions);
|
|
50
|
+
}
|
|
51
|
+
export declare class ApiRequestError extends ApiError {
|
|
52
|
+
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
53
|
+
}
|
|
54
|
+
export declare class ApiResponseError extends ApiError {
|
|
55
|
+
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
56
|
+
}
|
|
57
|
+
export declare function createApi(options?: ApiOptions): {
|
|
58
|
+
get: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: ApiActionOptions<ApiAction.GET, H, Q, never>) => Promise<T>;
|
|
59
|
+
post: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: ApiActionOptions<ApiAction.POST, H, Q, B>) => Promise<T>;
|
|
60
|
+
put: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: ApiActionOptions<ApiAction.PUT, H, Q, B>) => Promise<T>;
|
|
61
|
+
patch: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?: ApiActionOptions<ApiAction.PATCH, H, Q, B>) => Promise<T>;
|
|
62
|
+
del: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?: ApiActionOptions<ApiAction.DELETE, H, Q, never>) => Promise<T>;
|
|
63
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { toValue } from "vue";
|
|
2
|
+
export var ApiAction = /* @__PURE__ */ ((ApiAction2) => {
|
|
3
|
+
ApiAction2["GET"] = "get";
|
|
4
|
+
ApiAction2["POST"] = "post";
|
|
5
|
+
ApiAction2["PUT"] = "put";
|
|
6
|
+
ApiAction2["PATCH"] = "patch";
|
|
7
|
+
ApiAction2["DELETE"] = "delete";
|
|
8
|
+
return ApiAction2;
|
|
9
|
+
})(ApiAction || {});
|
|
10
|
+
export var ApiResponseType = /* @__PURE__ */ ((ApiResponseType2) => {
|
|
11
|
+
ApiResponseType2["JSON"] = "json";
|
|
12
|
+
ApiResponseType2["TEXT"] = "text";
|
|
13
|
+
ApiResponseType2["BLOB"] = "blob";
|
|
14
|
+
ApiResponseType2["ARRAY_BUFFER"] = "arrayBuffer";
|
|
15
|
+
return ApiResponseType2;
|
|
16
|
+
})(ApiResponseType || {});
|
|
17
|
+
export var ApiErrorSource = /* @__PURE__ */ ((ApiErrorSource2) => {
|
|
18
|
+
ApiErrorSource2["REQUEST"] = "request";
|
|
19
|
+
ApiErrorSource2["RESPONSE"] = "response";
|
|
20
|
+
return ApiErrorSource2;
|
|
21
|
+
})(ApiErrorSource || {});
|
|
22
|
+
export class ApiError extends Error {
|
|
23
|
+
source;
|
|
24
|
+
method;
|
|
25
|
+
url;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
super(options.message || "Unknown error");
|
|
28
|
+
this.name = "ApiError";
|
|
29
|
+
this.source = options.source;
|
|
30
|
+
this.method = options.method;
|
|
31
|
+
this.url = options.url;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class ApiRequestError extends ApiError {
|
|
35
|
+
constructor(options) {
|
|
36
|
+
super({
|
|
37
|
+
...options,
|
|
38
|
+
source: "request" /* REQUEST */
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class ApiResponseError extends ApiError {
|
|
43
|
+
constructor(options) {
|
|
44
|
+
super({
|
|
45
|
+
...options,
|
|
46
|
+
source: "response" /* RESPONSE */
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function createApi(options) {
|
|
51
|
+
async function request(url, requestOptions) {
|
|
52
|
+
return $fetch(url, {
|
|
53
|
+
baseURL: options?.url,
|
|
54
|
+
method: requestOptions?.action ?? "get" /* GET */,
|
|
55
|
+
headers: {
|
|
56
|
+
...toValue(options?.headers),
|
|
57
|
+
...toValue(requestOptions?.headers)
|
|
58
|
+
},
|
|
59
|
+
query: {
|
|
60
|
+
...toValue(requestOptions?.query),
|
|
61
|
+
...toValue(options?.query)
|
|
62
|
+
},
|
|
63
|
+
body: toValue(requestOptions?.body),
|
|
64
|
+
timeout: requestOptions?.timeout ?? options?.timeout,
|
|
65
|
+
responseType: requestOptions?.responseType,
|
|
66
|
+
retry: requestOptions?.retry,
|
|
67
|
+
retryDelay: requestOptions?.retryDelay,
|
|
68
|
+
retryStatusCodes: requestOptions?.retryStatusCodes,
|
|
69
|
+
onRequestError({ request: request2, options: options2, error }) {
|
|
70
|
+
throw new ApiRequestError({
|
|
71
|
+
method: options2.method,
|
|
72
|
+
url: request2.toString(),
|
|
73
|
+
message: error?.message
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
onResponseError({ request: request2, options: options2, error }) {
|
|
77
|
+
throw new ApiResponseError({
|
|
78
|
+
method: options2.method,
|
|
79
|
+
url: request2.toString(),
|
|
80
|
+
message: error?.message
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function get(url, options2) {
|
|
86
|
+
return request(url, {
|
|
87
|
+
...options2,
|
|
88
|
+
action: "get" /* GET */
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function post(url, options2) {
|
|
92
|
+
return request(url, {
|
|
93
|
+
...options2,
|
|
94
|
+
action: "post" /* POST */
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async function put(url, options2) {
|
|
98
|
+
return request(url, {
|
|
99
|
+
...options2,
|
|
100
|
+
action: "put" /* PUT */
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async function patch(url, options2) {
|
|
104
|
+
return request(url, {
|
|
105
|
+
...options2,
|
|
106
|
+
action: "patch" /* PATCH */
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async function del(url, options2) {
|
|
110
|
+
return request(url, {
|
|
111
|
+
...options2,
|
|
112
|
+
action: "delete" /* DELETE */
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
get,
|
|
117
|
+
post,
|
|
118
|
+
put,
|
|
119
|
+
patch,
|
|
120
|
+
del
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Extension, type BaseState } from "@harlem/core";
|
|
3
|
+
import { Endpoint, type EndpointDefinition, type EndpointMemory } from "../utils/endpoint.js";
|
|
4
|
+
import { ApiAction, type ApiActionOptions, type ApiOptions } from "./api.js";
|
|
5
|
+
export declare function createStore<T extends z.ZodRawShape>(name: string, schema: z.ZodObject<T>, endpoints?: Partial<Record<Endpoint, EndpointDefinition>>, options?: {
|
|
6
|
+
api?: ApiOptions;
|
|
7
|
+
extensions?: Extension<BaseState>[];
|
|
8
|
+
}): {
|
|
9
|
+
api: {
|
|
10
|
+
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>;
|
|
11
|
+
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>;
|
|
12
|
+
put: <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.PUT, H, Q, B>) => Promise<T_1>;
|
|
13
|
+
patch: <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.PATCH, H, Q, B>) => Promise<T_1>;
|
|
14
|
+
del: <T_1, H extends import("./api").ApiRequestHeader = import("./api").ApiRequestHeader, Q extends import("./api").ApiRequestQuery = import("./api").ApiRequestQuery>(url: string, options?: ApiActionOptions<ApiAction.DELETE, H, Q, never>) => Promise<T_1>;
|
|
15
|
+
};
|
|
16
|
+
store: Omit<import("@harlem/core").Store<{
|
|
17
|
+
memory: {
|
|
18
|
+
unit: z.core.$InferObjectOutput<T, {}> | null;
|
|
19
|
+
units: z.core.$InferObjectOutput<T, {}>[];
|
|
20
|
+
};
|
|
21
|
+
endpoints: Record<Endpoint, EndpointMemory>;
|
|
22
|
+
}>, never>;
|
|
23
|
+
memorizedUnit: import("@vue/reactivity").ComputedRef<import("@vue/reactivity").DeepReadonly<z.core.$InferObjectOutput<T, {}>> | null>;
|
|
24
|
+
memorizedUnits: import("@vue/reactivity").ComputedRef<readonly import("@vue/reactivity").DeepReadonly<z.core.$InferObjectOutput<T, {}>>[]>;
|
|
25
|
+
hasMemorizedUnits: (...units: ({
|
|
26
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
27
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => Record<keyof z.core.$InferObjectOutput<T, {}>, boolean>;
|
|
28
|
+
endpointsStatus: {
|
|
29
|
+
getUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
30
|
+
getUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
31
|
+
getUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
32
|
+
getUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
33
|
+
getUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
34
|
+
getUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
35
|
+
getUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
36
|
+
getUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
37
|
+
postUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
38
|
+
postUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
39
|
+
postUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
40
|
+
postUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
41
|
+
postUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
42
|
+
postUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
43
|
+
postUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
44
|
+
postUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
45
|
+
putUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
46
|
+
putUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
47
|
+
putUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
48
|
+
putUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
49
|
+
putUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
50
|
+
putUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
51
|
+
putUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
52
|
+
putUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
53
|
+
patchUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
54
|
+
patchUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
55
|
+
patchUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
56
|
+
patchUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
57
|
+
patchUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
58
|
+
patchUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
59
|
+
patchUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
60
|
+
patchUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
61
|
+
deleteUnitIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
62
|
+
deleteUnitIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
63
|
+
deleteUnitIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
64
|
+
deleteUnitIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
65
|
+
deleteUnitsIsIdle: import("@vue/reactivity").ComputedRef<boolean>;
|
|
66
|
+
deleteUnitsIsPending: import("@vue/reactivity").ComputedRef<boolean>;
|
|
67
|
+
deleteUnitsIsSuccess: import("@vue/reactivity").ComputedRef<boolean>;
|
|
68
|
+
deleteUnitsIsFailed: import("@vue/reactivity").ComputedRef<boolean>;
|
|
69
|
+
};
|
|
70
|
+
setMemorizedUnit: import("@harlem/core").Mutation<z.core.$InferObjectOutput<T, {}> | null, void>;
|
|
71
|
+
setMemorizedUnits: (payload: z.core.$InferObjectOutput<T, {}>[]) => void;
|
|
72
|
+
editMemorizedUnit: import("@harlem/core").Mutation<{
|
|
73
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
74
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>, void>;
|
|
75
|
+
editMemorizedUnits: (payload: ({
|
|
76
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
77
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => void;
|
|
78
|
+
dropMemorizedUnit: import("@harlem/core").Mutation<{
|
|
79
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
80
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>, void>;
|
|
81
|
+
dropMemorizedUnits: (payload: ({
|
|
82
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
83
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => void;
|
|
84
|
+
patchEndpointMemory: (payload: {
|
|
85
|
+
key: Endpoint;
|
|
86
|
+
memory: EndpointMemory;
|
|
87
|
+
}) => void;
|
|
88
|
+
purgeEndpointMemory: (payload?: unknown) => void;
|
|
89
|
+
getUnit: (unit: {
|
|
90
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
91
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>, options?: Omit<ApiActionOptions<ApiAction.GET>, "body">) => Promise<z.core.$InferObjectOutput<T, {}>>;
|
|
92
|
+
getUnits: (options?: Omit<ApiActionOptions<ApiAction.GET>, "body">) => Promise<z.core.$InferObjectOutput<T, {}>[]>;
|
|
93
|
+
postUnit: (unit: z.core.$InferObjectOutput<T, {}>, options?: ApiActionOptions<ApiAction.POST> & {
|
|
94
|
+
validate?: boolean;
|
|
95
|
+
}) => Promise<z.core.$InferObjectOutput<T, {}>>;
|
|
96
|
+
postUnits: (units: z.core.$InferObjectOutput<T, {}>[], options?: ApiActionOptions<ApiAction.POST> & {
|
|
97
|
+
validate?: boolean;
|
|
98
|
+
}) => Promise<z.core.$InferObjectOutput<T, {}>[]>;
|
|
99
|
+
putUnit: (unit: z.core.$InferObjectOutput<T, {}>, options?: ApiActionOptions<ApiAction.PUT> & {
|
|
100
|
+
validate?: boolean;
|
|
101
|
+
}) => Promise<z.core.$InferObjectOutput<T, {}>>;
|
|
102
|
+
putUnits: (units: z.core.$InferObjectOutput<T, {}>[], options?: ApiActionOptions<ApiAction.PUT> & {
|
|
103
|
+
validate?: boolean;
|
|
104
|
+
}) => Promise<z.core.$InferObjectOutput<T, {}>[]>;
|
|
105
|
+
patchUnit: (unit: {
|
|
106
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
107
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>, options?: ApiActionOptions<ApiAction.PATCH> & {
|
|
108
|
+
validate?: boolean;
|
|
109
|
+
}) => Promise<{
|
|
110
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
111
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>>;
|
|
112
|
+
patchUnits: (units: ({
|
|
113
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
114
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[], options?: ApiActionOptions<ApiAction.PATCH> & {
|
|
115
|
+
validate?: boolean;
|
|
116
|
+
}) => Promise<({
|
|
117
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
118
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[]>;
|
|
119
|
+
deleteUnit: (unit: {
|
|
120
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
121
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>, options?: Omit<ApiActionOptions<ApiAction.DELETE>, "body">) => Promise<boolean>;
|
|
122
|
+
deleteUnits: (units: ({
|
|
123
|
+
[x: string]: keyof z.core.$InferObjectOutput<T, {}>;
|
|
124
|
+
} & Partial<z.core.$InferObjectOutput<T, {}>>)[], options?: Omit<ApiActionOptions<ApiAction.DELETE>, "body">) => Promise<boolean>;
|
|
125
|
+
};
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import {
|
|
3
|
+
createStore as createHarlemStore
|
|
4
|
+
} from "@harlem/core";
|
|
5
|
+
import { resolveSchema } from "../utils/schema.js";
|
|
6
|
+
import {
|
|
7
|
+
makeEndpointStatusKey,
|
|
8
|
+
getEndpoint,
|
|
9
|
+
resolveEndpointUrl,
|
|
10
|
+
makeEndpointsStatus,
|
|
11
|
+
Endpoint,
|
|
12
|
+
EndpointStatus
|
|
13
|
+
} from "../utils/endpoint.js";
|
|
14
|
+
import {
|
|
15
|
+
createApi
|
|
16
|
+
} from "./api.js";
|
|
17
|
+
export function createStore(name, schema, endpoints, options) {
|
|
18
|
+
const config = useRuntimeConfig();
|
|
19
|
+
const api = createApi({
|
|
20
|
+
...config.public.harlemify.api,
|
|
21
|
+
...options?.api
|
|
22
|
+
});
|
|
23
|
+
const { indicator } = resolveSchema(schema);
|
|
24
|
+
const store = createHarlemStore(
|
|
25
|
+
name,
|
|
26
|
+
{
|
|
27
|
+
memory: {
|
|
28
|
+
unit: null,
|
|
29
|
+
units: []
|
|
30
|
+
},
|
|
31
|
+
endpoints: {}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
extensions: options?.extensions ?? []
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
const memorizedUnit = store.getter("memorizedUnit", (state) => {
|
|
38
|
+
return state.memory.unit;
|
|
39
|
+
});
|
|
40
|
+
const memorizedUnits = store.getter("memorizedUnits", (state) => {
|
|
41
|
+
return state.memory.units;
|
|
42
|
+
});
|
|
43
|
+
function hasMemorizedUnits(...units) {
|
|
44
|
+
const output = {};
|
|
45
|
+
for (const unit of units) {
|
|
46
|
+
const exists = memorizedUnits.value.some((memorizedUnit2) => {
|
|
47
|
+
return memorizedUnit2[indicator] === unit[indicator];
|
|
48
|
+
});
|
|
49
|
+
output[unit[indicator]] = exists;
|
|
50
|
+
}
|
|
51
|
+
return output;
|
|
52
|
+
}
|
|
53
|
+
const setMemorizedUnit = store.mutation(
|
|
54
|
+
"setMemorizedUnit",
|
|
55
|
+
(state, unit = null) => {
|
|
56
|
+
state.memory.unit = unit;
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
const setMemorizedUnits = store.mutation(
|
|
60
|
+
"setMemorizedUnits",
|
|
61
|
+
(state, units = []) => {
|
|
62
|
+
state.memory.units = units;
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
const editMemorizedUnit = store.mutation(
|
|
66
|
+
"editMemorizedUnit",
|
|
67
|
+
(state, unit) => {
|
|
68
|
+
if (state.memory.unit?.[indicator] === unit[indicator]) {
|
|
69
|
+
state.memory.unit = defu(unit, state.memory.unit);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
const editMemorizedUnits = store.mutation(
|
|
74
|
+
"editMemorizedUnits",
|
|
75
|
+
(state, units) => {
|
|
76
|
+
for (const unit of units) {
|
|
77
|
+
const index = state.memory.units.findIndex((memorizedUnit2) => {
|
|
78
|
+
return memorizedUnit2[indicator] === unit[indicator];
|
|
79
|
+
});
|
|
80
|
+
if (index !== -1) {
|
|
81
|
+
state.memory.units[index] = defu(
|
|
82
|
+
unit,
|
|
83
|
+
state.memory.units[index]
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
const dropMemorizedUnit = store.mutation(
|
|
90
|
+
"dropMemorizedUnit",
|
|
91
|
+
(state, unit) => {
|
|
92
|
+
if (state.memory.unit?.[indicator] === unit[indicator]) {
|
|
93
|
+
state.memory.unit = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
const dropMemorizedUnits = store.mutation(
|
|
98
|
+
"dropMemorizedUnits",
|
|
99
|
+
(state, units) => {
|
|
100
|
+
state.memory.units = state.memory.units.filter((memorizedUnit2) => {
|
|
101
|
+
for (const unit of units) {
|
|
102
|
+
if (memorizedUnit2[indicator] === unit[indicator]) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
const endpointsStatus = makeEndpointsStatus(store.getter);
|
|
111
|
+
const patchEndpointMemory = store.mutation(
|
|
112
|
+
"patchEndpointMemory",
|
|
113
|
+
(state, {
|
|
114
|
+
key,
|
|
115
|
+
memory
|
|
116
|
+
}) => {
|
|
117
|
+
state.endpoints[key] = memory;
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
const purgeEndpointMemory = store.mutation(
|
|
121
|
+
"purgeEndpointMemory",
|
|
122
|
+
(state) => {
|
|
123
|
+
state.endpoints = {};
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
function patchEndpointMemoryTo(key, memory) {
|
|
127
|
+
if (memory.status === EndpointStatus.PENDING) {
|
|
128
|
+
const statusKey = makeEndpointStatusKey(
|
|
129
|
+
key,
|
|
130
|
+
EndpointStatus.PENDING
|
|
131
|
+
);
|
|
132
|
+
if (endpointsStatus[statusKey].value) {
|
|
133
|
+
throw new Error(`Endpoint "${key}" is already pending`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
patchEndpointMemory({
|
|
137
|
+
key,
|
|
138
|
+
memory
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function getUnit(unit, options2) {
|
|
142
|
+
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNIT);
|
|
143
|
+
patchEndpointMemoryTo(Endpoint.GET_UNIT, {
|
|
144
|
+
status: EndpointStatus.PENDING
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url, {
|
|
148
|
+
[indicator]: unit[indicator]
|
|
149
|
+
});
|
|
150
|
+
const response = await api.get(resolvedUrl, options2);
|
|
151
|
+
setMemorizedUnit(response);
|
|
152
|
+
patchEndpointMemoryTo(Endpoint.GET_UNIT, {
|
|
153
|
+
status: EndpointStatus.SUCCESS
|
|
154
|
+
});
|
|
155
|
+
return response;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
patchEndpointMemoryTo(Endpoint.GET_UNIT, {
|
|
158
|
+
status: EndpointStatus.FAILED
|
|
159
|
+
});
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function getUnits(options2) {
|
|
164
|
+
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNITS);
|
|
165
|
+
patchEndpointMemoryTo(Endpoint.GET_UNITS, {
|
|
166
|
+
status: EndpointStatus.PENDING
|
|
167
|
+
});
|
|
168
|
+
try {
|
|
169
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url);
|
|
170
|
+
const response = await api.get(resolvedUrl, options2);
|
|
171
|
+
setMemorizedUnits(response);
|
|
172
|
+
patchEndpointMemoryTo(Endpoint.GET_UNITS, {
|
|
173
|
+
status: EndpointStatus.SUCCESS
|
|
174
|
+
});
|
|
175
|
+
return response;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
patchEndpointMemoryTo(Endpoint.GET_UNITS, {
|
|
178
|
+
status: EndpointStatus.FAILED
|
|
179
|
+
});
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function postUnit(unit, options2) {
|
|
184
|
+
const endpoint = getEndpoint(endpoints, Endpoint.POST_UNIT);
|
|
185
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
186
|
+
if (options2?.validate) {
|
|
187
|
+
schema.pick(resolvedSchema.keys).parse(unit);
|
|
188
|
+
}
|
|
189
|
+
patchEndpointMemoryTo(Endpoint.POST_UNIT, {
|
|
190
|
+
status: EndpointStatus.PENDING
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url, {
|
|
194
|
+
[indicator]: unit[indicator]
|
|
195
|
+
});
|
|
196
|
+
const response = await api.post(resolvedUrl, {
|
|
197
|
+
...options2,
|
|
198
|
+
body: options2?.body ?? resolvedSchema.values
|
|
199
|
+
});
|
|
200
|
+
setMemorizedUnit(response);
|
|
201
|
+
patchEndpointMemoryTo(Endpoint.POST_UNIT, {
|
|
202
|
+
status: EndpointStatus.SUCCESS
|
|
203
|
+
});
|
|
204
|
+
return response;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
patchEndpointMemoryTo(Endpoint.POST_UNIT, {
|
|
207
|
+
status: EndpointStatus.FAILED
|
|
208
|
+
});
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function postUnits(units, options2) {
|
|
213
|
+
const endpoint = getEndpoint(endpoints, Endpoint.POST_UNITS);
|
|
214
|
+
const body = units.map((unit) => {
|
|
215
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
216
|
+
if (options2?.validate) {
|
|
217
|
+
schema.pick(resolvedSchema.keys).parse(unit);
|
|
218
|
+
}
|
|
219
|
+
return resolvedSchema.values;
|
|
220
|
+
});
|
|
221
|
+
patchEndpointMemoryTo(Endpoint.POST_UNITS, {
|
|
222
|
+
status: EndpointStatus.PENDING
|
|
223
|
+
});
|
|
224
|
+
try {
|
|
225
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url);
|
|
226
|
+
const response = await api.post(resolvedUrl, {
|
|
227
|
+
...options2,
|
|
228
|
+
body: options2?.body ?? body
|
|
229
|
+
});
|
|
230
|
+
setMemorizedUnits([
|
|
231
|
+
...memorizedUnits.value,
|
|
232
|
+
...response
|
|
233
|
+
]);
|
|
234
|
+
patchEndpointMemoryTo(Endpoint.POST_UNITS, {
|
|
235
|
+
status: EndpointStatus.SUCCESS
|
|
236
|
+
});
|
|
237
|
+
return response;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
patchEndpointMemoryTo(Endpoint.POST_UNITS, {
|
|
240
|
+
status: EndpointStatus.FAILED
|
|
241
|
+
});
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function putUnit(unit, options2) {
|
|
246
|
+
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNIT);
|
|
247
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
248
|
+
if (options2?.validate) {
|
|
249
|
+
schema.pick(resolvedSchema.keys).parse(unit);
|
|
250
|
+
}
|
|
251
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
|
|
252
|
+
status: EndpointStatus.PENDING
|
|
253
|
+
});
|
|
254
|
+
try {
|
|
255
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url, {
|
|
256
|
+
[indicator]: unit[indicator]
|
|
257
|
+
});
|
|
258
|
+
const response = await api.put(resolvedUrl, {
|
|
259
|
+
...options2,
|
|
260
|
+
body: options2?.body ?? resolvedSchema.values
|
|
261
|
+
});
|
|
262
|
+
setMemorizedUnit(response);
|
|
263
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
|
|
264
|
+
status: EndpointStatus.SUCCESS
|
|
265
|
+
});
|
|
266
|
+
return response;
|
|
267
|
+
} catch (error) {
|
|
268
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNIT, {
|
|
269
|
+
status: EndpointStatus.FAILED
|
|
270
|
+
});
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async function putUnits(units, options2) {
|
|
275
|
+
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNITS);
|
|
276
|
+
const body = units.map((unit) => {
|
|
277
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
278
|
+
if (options2?.validate) {
|
|
279
|
+
schema.pick(resolvedSchema.keys).parse(unit);
|
|
280
|
+
}
|
|
281
|
+
return resolvedSchema.values;
|
|
282
|
+
});
|
|
283
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
|
|
284
|
+
status: EndpointStatus.PENDING
|
|
285
|
+
});
|
|
286
|
+
try {
|
|
287
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url);
|
|
288
|
+
const response = await api.put(resolvedUrl, {
|
|
289
|
+
...options2,
|
|
290
|
+
body: options2?.body ?? body
|
|
291
|
+
});
|
|
292
|
+
setMemorizedUnits(response);
|
|
293
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
|
|
294
|
+
status: EndpointStatus.SUCCESS
|
|
295
|
+
});
|
|
296
|
+
return response;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
patchEndpointMemoryTo(Endpoint.PUT_UNITS, {
|
|
299
|
+
status: EndpointStatus.FAILED
|
|
300
|
+
});
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async function patchUnit(unit, options2) {
|
|
305
|
+
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNIT);
|
|
306
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
307
|
+
if (options2?.validate) {
|
|
308
|
+
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
309
|
+
}
|
|
310
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
|
|
311
|
+
status: EndpointStatus.PENDING
|
|
312
|
+
});
|
|
313
|
+
try {
|
|
314
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url, {
|
|
315
|
+
[indicator]: unit[indicator]
|
|
316
|
+
});
|
|
317
|
+
const response = await api.patch(
|
|
318
|
+
resolvedUrl,
|
|
319
|
+
{
|
|
320
|
+
...options2,
|
|
321
|
+
body: options2?.body ?? resolvedSchema.values
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
editMemorizedUnit(response);
|
|
325
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
|
|
326
|
+
status: EndpointStatus.SUCCESS
|
|
327
|
+
});
|
|
328
|
+
return response;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNIT, {
|
|
331
|
+
status: EndpointStatus.FAILED
|
|
332
|
+
});
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async function patchUnits(units, options2) {
|
|
337
|
+
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNITS);
|
|
338
|
+
const body = units.map((unit) => {
|
|
339
|
+
const resolvedSchema = resolveSchema(schema, endpoint.action, unit);
|
|
340
|
+
if (options2?.validate) {
|
|
341
|
+
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
342
|
+
}
|
|
343
|
+
return resolvedSchema.values;
|
|
344
|
+
});
|
|
345
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
|
|
346
|
+
status: EndpointStatus.PENDING
|
|
347
|
+
});
|
|
348
|
+
try {
|
|
349
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url);
|
|
350
|
+
const response = await api.patch(resolvedUrl, {
|
|
351
|
+
...options2,
|
|
352
|
+
body: options2?.body ?? body
|
|
353
|
+
});
|
|
354
|
+
editMemorizedUnits(response);
|
|
355
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
|
|
356
|
+
status: EndpointStatus.SUCCESS
|
|
357
|
+
});
|
|
358
|
+
return response;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
patchEndpointMemoryTo(Endpoint.PATCH_UNITS, {
|
|
361
|
+
status: EndpointStatus.FAILED
|
|
362
|
+
});
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function deleteUnit(unit, options2) {
|
|
367
|
+
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNIT);
|
|
368
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
|
|
369
|
+
status: EndpointStatus.PENDING
|
|
370
|
+
});
|
|
371
|
+
try {
|
|
372
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url, {
|
|
373
|
+
[indicator]: unit[indicator]
|
|
374
|
+
});
|
|
375
|
+
await api.del(
|
|
376
|
+
resolvedUrl,
|
|
377
|
+
options2
|
|
378
|
+
);
|
|
379
|
+
dropMemorizedUnit(unit);
|
|
380
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
|
|
381
|
+
status: EndpointStatus.SUCCESS
|
|
382
|
+
});
|
|
383
|
+
return true;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNIT, {
|
|
386
|
+
status: EndpointStatus.FAILED
|
|
387
|
+
});
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async function deleteUnits(units, options2) {
|
|
392
|
+
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNITS);
|
|
393
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
|
|
394
|
+
status: EndpointStatus.PENDING
|
|
395
|
+
});
|
|
396
|
+
try {
|
|
397
|
+
const resolvedUrl = resolveEndpointUrl(endpoint.url);
|
|
398
|
+
await api.del(
|
|
399
|
+
resolvedUrl,
|
|
400
|
+
options2
|
|
401
|
+
);
|
|
402
|
+
dropMemorizedUnits(units);
|
|
403
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
|
|
404
|
+
status: EndpointStatus.SUCCESS
|
|
405
|
+
});
|
|
406
|
+
return true;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
patchEndpointMemoryTo(Endpoint.DELETE_UNITS, {
|
|
409
|
+
status: EndpointStatus.FAILED
|
|
410
|
+
});
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
api,
|
|
416
|
+
store,
|
|
417
|
+
memorizedUnit,
|
|
418
|
+
memorizedUnits,
|
|
419
|
+
hasMemorizedUnits,
|
|
420
|
+
endpointsStatus,
|
|
421
|
+
setMemorizedUnit,
|
|
422
|
+
setMemorizedUnits,
|
|
423
|
+
editMemorizedUnit,
|
|
424
|
+
editMemorizedUnits,
|
|
425
|
+
dropMemorizedUnit,
|
|
426
|
+
dropMemorizedUnits,
|
|
427
|
+
patchEndpointMemory,
|
|
428
|
+
purgeEndpointMemory,
|
|
429
|
+
getUnit,
|
|
430
|
+
getUnits,
|
|
431
|
+
postUnit,
|
|
432
|
+
postUnits,
|
|
433
|
+
putUnit,
|
|
434
|
+
putUnits,
|
|
435
|
+
patchUnit,
|
|
436
|
+
patchUnits,
|
|
437
|
+
deleteUnit,
|
|
438
|
+
deleteUnits
|
|
439
|
+
};
|
|
440
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { z } from "zod";
|
|
2
|
+
export { ApiAction, ApiResponseType, ApiErrorSource, type ApiRequestHeader, type ApiRequestQuery, type ApiRequestBody, type ApiRequestOptions, type ApiOptions, type ApiActionOptions, type ApiErrorOptions, createApi, ApiError, ApiRequestError, ApiResponseError, } from "./core/api.js";
|
|
3
|
+
export { Endpoint, EndpointStatus, type EndpointDefinition, type EndpointMemory, } from "./utils/endpoint.js";
|
|
4
|
+
export { type SchemaMeta, getMeta, resolveSchema } from "./utils/schema.js";
|
|
5
|
+
export { createStore } from "./core/store.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { z } from "zod";
|
|
2
|
+
export {
|
|
3
|
+
ApiAction,
|
|
4
|
+
ApiResponseType,
|
|
5
|
+
ApiErrorSource,
|
|
6
|
+
createApi,
|
|
7
|
+
ApiError,
|
|
8
|
+
ApiRequestError,
|
|
9
|
+
ApiResponseError
|
|
10
|
+
} from "./core/api.js";
|
|
11
|
+
export {
|
|
12
|
+
Endpoint,
|
|
13
|
+
EndpointStatus
|
|
14
|
+
} from "./utils/endpoint.js";
|
|
15
|
+
export { getMeta, resolveSchema } from "./utils/schema.js";
|
|
16
|
+
export { createStore } from "./core/store.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createVuePlugin } from "@harlem/core";
|
|
2
|
+
import {
|
|
3
|
+
createClientSSRPlugin,
|
|
4
|
+
createServerSSRPlugin
|
|
5
|
+
} from "@harlem/plugin-ssr";
|
|
6
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
7
|
+
const plugins = [];
|
|
8
|
+
if (nuxtApp.payload.serverRendered) {
|
|
9
|
+
if (import.meta.server) {
|
|
10
|
+
plugins.push(createServerSSRPlugin());
|
|
11
|
+
}
|
|
12
|
+
if (import.meta.client) {
|
|
13
|
+
plugins.push(createClientSSRPlugin());
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const harlem = createVuePlugin({
|
|
17
|
+
plugins
|
|
18
|
+
});
|
|
19
|
+
nuxtApp.vueApp.use(harlem);
|
|
20
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { BaseState } from "@harlem/core";
|
|
2
|
+
import type { ApiAction } from "../core/api.js";
|
|
3
|
+
export declare enum Endpoint {
|
|
4
|
+
GET_UNIT = "getUnit",
|
|
5
|
+
GET_UNITS = "getUnits",
|
|
6
|
+
POST_UNIT = "postUnit",
|
|
7
|
+
POST_UNITS = "postUnits",
|
|
8
|
+
PUT_UNIT = "putUnit",
|
|
9
|
+
PUT_UNITS = "putUnits",
|
|
10
|
+
PATCH_UNIT = "patchUnit",
|
|
11
|
+
PATCH_UNITS = "patchUnits",
|
|
12
|
+
DELETE_UNIT = "deleteUnit",
|
|
13
|
+
DELETE_UNITS = "deleteUnits"
|
|
14
|
+
}
|
|
15
|
+
export declare enum EndpointStatus {
|
|
16
|
+
IDLE = "idle",
|
|
17
|
+
PENDING = "pending",
|
|
18
|
+
SUCCESS = "success",
|
|
19
|
+
FAILED = "failed"
|
|
20
|
+
}
|
|
21
|
+
export interface EndpointDefinition {
|
|
22
|
+
action: ApiAction;
|
|
23
|
+
url: string | ((keys: Record<PropertyKey, unknown>) => string);
|
|
24
|
+
}
|
|
25
|
+
export interface EndpointMemory {
|
|
26
|
+
status: EndpointStatus;
|
|
27
|
+
}
|
|
28
|
+
export declare function makeEndpointStatusKey<K extends Endpoint, S extends EndpointStatus>(key: K, status: S): `${K}Is${Capitalize<S>}`;
|
|
29
|
+
export declare function getEndpoint(endpoints: Partial<Record<Endpoint, EndpointDefinition>> | undefined, key: Endpoint): EndpointDefinition;
|
|
30
|
+
export declare function resolveEndpointUrl<T extends Record<PropertyKey, unknown>>(url: string | ((parameters: T) => string), parameters?: T): string;
|
|
31
|
+
export declare function makeEndpointsStatus<T>(getter: (name: string, fn: (state: BaseState) => boolean) => T): {
|
|
32
|
+
getUnitIsIdle: T;
|
|
33
|
+
getUnitIsPending: T;
|
|
34
|
+
getUnitIsSuccess: T;
|
|
35
|
+
getUnitIsFailed: T;
|
|
36
|
+
getUnitsIsIdle: T;
|
|
37
|
+
getUnitsIsPending: T;
|
|
38
|
+
getUnitsIsSuccess: T;
|
|
39
|
+
getUnitsIsFailed: T;
|
|
40
|
+
postUnitIsIdle: T;
|
|
41
|
+
postUnitIsPending: T;
|
|
42
|
+
postUnitIsSuccess: T;
|
|
43
|
+
postUnitIsFailed: T;
|
|
44
|
+
postUnitsIsIdle: T;
|
|
45
|
+
postUnitsIsPending: T;
|
|
46
|
+
postUnitsIsSuccess: T;
|
|
47
|
+
postUnitsIsFailed: T;
|
|
48
|
+
putUnitIsIdle: T;
|
|
49
|
+
putUnitIsPending: T;
|
|
50
|
+
putUnitIsSuccess: T;
|
|
51
|
+
putUnitIsFailed: T;
|
|
52
|
+
putUnitsIsIdle: T;
|
|
53
|
+
putUnitsIsPending: T;
|
|
54
|
+
putUnitsIsSuccess: T;
|
|
55
|
+
putUnitsIsFailed: T;
|
|
56
|
+
patchUnitIsIdle: T;
|
|
57
|
+
patchUnitIsPending: T;
|
|
58
|
+
patchUnitIsSuccess: T;
|
|
59
|
+
patchUnitIsFailed: T;
|
|
60
|
+
patchUnitsIsIdle: T;
|
|
61
|
+
patchUnitsIsPending: T;
|
|
62
|
+
patchUnitsIsSuccess: T;
|
|
63
|
+
patchUnitsIsFailed: T;
|
|
64
|
+
deleteUnitIsIdle: T;
|
|
65
|
+
deleteUnitIsPending: T;
|
|
66
|
+
deleteUnitIsSuccess: T;
|
|
67
|
+
deleteUnitIsFailed: T;
|
|
68
|
+
deleteUnitsIsIdle: T;
|
|
69
|
+
deleteUnitsIsPending: T;
|
|
70
|
+
deleteUnitsIsSuccess: T;
|
|
71
|
+
deleteUnitsIsFailed: T;
|
|
72
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export var Endpoint = /* @__PURE__ */ ((Endpoint2) => {
|
|
2
|
+
Endpoint2["GET_UNIT"] = "getUnit";
|
|
3
|
+
Endpoint2["GET_UNITS"] = "getUnits";
|
|
4
|
+
Endpoint2["POST_UNIT"] = "postUnit";
|
|
5
|
+
Endpoint2["POST_UNITS"] = "postUnits";
|
|
6
|
+
Endpoint2["PUT_UNIT"] = "putUnit";
|
|
7
|
+
Endpoint2["PUT_UNITS"] = "putUnits";
|
|
8
|
+
Endpoint2["PATCH_UNIT"] = "patchUnit";
|
|
9
|
+
Endpoint2["PATCH_UNITS"] = "patchUnits";
|
|
10
|
+
Endpoint2["DELETE_UNIT"] = "deleteUnit";
|
|
11
|
+
Endpoint2["DELETE_UNITS"] = "deleteUnits";
|
|
12
|
+
return Endpoint2;
|
|
13
|
+
})(Endpoint || {});
|
|
14
|
+
export var EndpointStatus = /* @__PURE__ */ ((EndpointStatus2) => {
|
|
15
|
+
EndpointStatus2["IDLE"] = "idle";
|
|
16
|
+
EndpointStatus2["PENDING"] = "pending";
|
|
17
|
+
EndpointStatus2["SUCCESS"] = "success";
|
|
18
|
+
EndpointStatus2["FAILED"] = "failed";
|
|
19
|
+
return EndpointStatus2;
|
|
20
|
+
})(EndpointStatus || {});
|
|
21
|
+
export function makeEndpointStatusKey(key, status) {
|
|
22
|
+
return `${key}Is${status.charAt(0).toUpperCase() + status.slice(1)}`;
|
|
23
|
+
}
|
|
24
|
+
export function getEndpoint(endpoints, key) {
|
|
25
|
+
const endpoint = endpoints?.[key];
|
|
26
|
+
if (!endpoint) {
|
|
27
|
+
throw new Error(`Endpoint "${key}" is not configured`);
|
|
28
|
+
}
|
|
29
|
+
return endpoint;
|
|
30
|
+
}
|
|
31
|
+
export function resolveEndpointUrl(url, parameters = {}) {
|
|
32
|
+
if (typeof url === "function") {
|
|
33
|
+
return url(parameters);
|
|
34
|
+
}
|
|
35
|
+
return url;
|
|
36
|
+
}
|
|
37
|
+
export function makeEndpointsStatus(getter) {
|
|
38
|
+
const output = {};
|
|
39
|
+
for (const key of Object.values(Endpoint)) {
|
|
40
|
+
for (const status of Object.values(EndpointStatus)) {
|
|
41
|
+
const statusKey = makeEndpointStatusKey(key, status);
|
|
42
|
+
output[statusKey] = getter(statusKey, (state) => {
|
|
43
|
+
return state.endpoints[key]?.status === status;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return output;
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ApiAction } from "../core/api.js";
|
|
3
|
+
export interface SchemaMeta {
|
|
4
|
+
indicator?: boolean;
|
|
5
|
+
actions?: ApiAction[];
|
|
6
|
+
}
|
|
7
|
+
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
|
+
indicator: keyof S;
|
|
10
|
+
keys: Record<keyof S, true>;
|
|
11
|
+
values: Partial<S>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function getMeta(field) {
|
|
2
|
+
return field.meta();
|
|
3
|
+
}
|
|
4
|
+
export function resolveSchema(schema, action, input) {
|
|
5
|
+
const output = {
|
|
6
|
+
indicator: "id",
|
|
7
|
+
keys: {},
|
|
8
|
+
values: {}
|
|
9
|
+
};
|
|
10
|
+
for (const key of Object.keys(schema.shape)) {
|
|
11
|
+
const meta = getMeta(schema.shape[key]);
|
|
12
|
+
if (meta?.indicator) {
|
|
13
|
+
output.indicator = key;
|
|
14
|
+
}
|
|
15
|
+
if (!action || !meta?.actions) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (meta?.actions.includes(action)) {
|
|
19
|
+
output.keys[key] = true;
|
|
20
|
+
if (input && key in input) {
|
|
21
|
+
output.values[key] = input[key];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return output;
|
|
26
|
+
}
|
package/dist/types.d.mts
ADDED
package/dist/types.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@diphyx/harlemify",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "API state management for Nuxt powered by Harlem",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nuxt",
|
|
7
|
+
"nuxt-module",
|
|
8
|
+
"harlem",
|
|
9
|
+
"state-management",
|
|
10
|
+
"api",
|
|
11
|
+
"zod",
|
|
12
|
+
"vue",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/diphyx/harlemify.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://diphyx.github.io/harlemify/",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/diphyx/harlemify/issues"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Amir Reza Dalir",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/types.d.ts",
|
|
29
|
+
"import": "./dist/module.mjs",
|
|
30
|
+
"require": "./dist/module.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/module.cjs",
|
|
34
|
+
"module": "./dist/module.mjs",
|
|
35
|
+
"types": "./dist/types.d.ts",
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@nuxt/kit": "^3.14.0",
|
|
41
|
+
"@harlem/core": "^3.0.0",
|
|
42
|
+
"@harlem/plugin-ssr": "^3.0.0",
|
|
43
|
+
"defu": "^6.0.0",
|
|
44
|
+
"zod": "^4.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@nuxt/devtools": "^1.6.0",
|
|
48
|
+
"@nuxt/eslint-config": "^0.6.0",
|
|
49
|
+
"@nuxt/module-builder": "^0.8.0",
|
|
50
|
+
"@nuxt/schema": "^3.14.0",
|
|
51
|
+
"@nuxt/test-utils": "^3.14.0",
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
|
+
"eslint": "^9.0.0",
|
|
54
|
+
"nuxt": "^3.14.0",
|
|
55
|
+
"typescript": "^5.6.0",
|
|
56
|
+
"vitest": "^2.0.0"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"postinstall": "nuxi prepare playground",
|
|
60
|
+
"build": "nuxt-module-build build",
|
|
61
|
+
"dev": "nuxi dev playground",
|
|
62
|
+
"lint": "eslint . --fix",
|
|
63
|
+
"test": "vitest run"
|
|
64
|
+
}
|
|
65
|
+
}
|