@diphyx/harlemify 0.0.3 → 1.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/LICENSE +1 -1
- package/README.md +27 -51
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -0
- package/dist/runtime/composables/use.d.ts +30 -0
- package/dist/runtime/composables/use.js +25 -0
- package/dist/runtime/core/api.d.ts +12 -17
- package/dist/runtime/core/api.js +8 -15
- package/dist/runtime/core/store.d.ts +58 -59
- package/dist/runtime/core/store.js +163 -301
- package/dist/runtime/index.d.ts +7 -4
- package/dist/runtime/index.js +3 -14
- package/dist/runtime/plugin.js +1 -5
- package/dist/runtime/utils/endpoint.d.ts +14 -15
- package/dist/runtime/utils/endpoint.js +18 -20
- package/dist/runtime/utils/schema.d.ts +3 -4
- package/dist/runtime/utils/schema.js +2 -2
- package/dist/runtime/utils/transform.d.ts +6 -0
- package/dist/runtime/utils/transform.js +20 -0
- package/package.json +1 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
# Harlemify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Schema-driven state management for Nuxt powered by [Harlem](https://harlemjs.com/)
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
Define your data schema once with Zod, and Harlemify handles the rest: type-safe API calls, reactive state, request monitoring, and automatic memory management. Your schema becomes the single source of truth for types, validation, and API payloads.
|
|
8
|
+
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
|
-
- Zod schema validation
|
|
10
|
-
- Automatic API client with runtime
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- SSR
|
|
14
|
-
- Configurable primary key indicator
|
|
15
|
-
- Lifecycle hooks (before/after) for API operations
|
|
16
|
-
- Abort controller support for request cancellation
|
|
11
|
+
- **Schema-Driven** - Zod schema defines types, validation, and API payloads
|
|
12
|
+
- **Automatic API Client** - Built-in HTTP client with runtime configuration
|
|
13
|
+
- **Reactive Memory** - Unit and collection caching with Vue reactivity
|
|
14
|
+
- **Request Monitoring** - Track pending, success, and failed states
|
|
15
|
+
- **SSR Support** - Server-side rendering via Harlem SSR plugin
|
|
17
16
|
|
|
18
17
|
## Installation
|
|
19
18
|
|
|
@@ -37,58 +36,35 @@ export default defineNuxtConfig({
|
|
|
37
36
|
|
|
38
37
|
```typescript
|
|
39
38
|
// stores/user.ts
|
|
40
|
-
import { z
|
|
39
|
+
import { z } from "zod";
|
|
40
|
+
import { createStore, Endpoint, EndpointMethod } from "@diphyx/harlemify";
|
|
41
41
|
|
|
42
42
|
const UserSchema = z.object({
|
|
43
|
-
id: z.number().meta({
|
|
44
|
-
indicator: true,
|
|
45
|
-
}),
|
|
43
|
+
id: z.number().meta({ indicator: true }),
|
|
46
44
|
name: z.string().meta({
|
|
47
|
-
|
|
45
|
+
methods: [EndpointMethod.POST, EndpointMethod.PATCH],
|
|
48
46
|
}),
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
export type User = z.infer<typeof UserSchema>;
|
|
52
50
|
|
|
53
|
-
export const userStore = createStore(
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
},
|
|
73
|
-
},
|
|
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
|
-
},
|
|
88
|
-
},
|
|
89
|
-
);
|
|
51
|
+
export const userStore = createStore("user", UserSchema, {
|
|
52
|
+
[Endpoint.GET_UNITS]: { method: EndpointMethod.GET, url: "/users" },
|
|
53
|
+
[Endpoint.POST_UNITS]: { method: EndpointMethod.POST, url: "/users" },
|
|
54
|
+
[Endpoint.PATCH_UNITS]: { method: EndpointMethod.PATCH, url: (p) => `/users/${p.id}` },
|
|
55
|
+
[Endpoint.DELETE_UNITS]: { method: EndpointMethod.DELETE, url: (p) => `/users/${p.id}` },
|
|
56
|
+
});
|
|
90
57
|
```
|
|
91
58
|
|
|
59
|
+
## Why Harlemify?
|
|
60
|
+
|
|
61
|
+
| | |
|
|
62
|
+
| --------------- | ------------------------------------------------- |
|
|
63
|
+
| **Type-Safe** | Full TypeScript support with Zod schema inference |
|
|
64
|
+
| **Declarative** | Define schema once, derive everything else |
|
|
65
|
+
| **Reactive** | Powered by Vue's reactivity through Harlem |
|
|
66
|
+
| **Simple** | Minimal boilerplate, maximum productivity |
|
|
67
|
+
|
|
92
68
|
## Documentation
|
|
93
69
|
|
|
94
70
|
Full documentation available at [https://diphyx.github.io/harlemify/](https://diphyx.github.io/harlemify/)
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -15,6 +15,8 @@ const module = defineNuxtModule({
|
|
|
15
15
|
const { resolve } = createResolver(import.meta.url);
|
|
16
16
|
addPlugin(resolve("./runtime", "plugin"));
|
|
17
17
|
addImportsDir(resolve("./runtime", "core"));
|
|
18
|
+
addImportsDir(resolve("./runtime", "composables"));
|
|
19
|
+
addImportsDir(resolve("./runtime", "utils"));
|
|
18
20
|
updateRuntimeConfig({
|
|
19
21
|
public: {
|
|
20
22
|
harlemify: options
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ComputedRef } from "vue";
|
|
2
|
+
import { EndpointMethod } from "../utils/endpoint.js";
|
|
3
|
+
import type { Store } from "../core/store.js";
|
|
4
|
+
import type { Pluralize, Capitalize } from "../utils/transform.js";
|
|
5
|
+
import type { EndpointStatusFlag } from "../utils/endpoint.js";
|
|
6
|
+
type Indicator<T, I extends keyof T> = Required<Pick<T, I>>;
|
|
7
|
+
type PartialWithIndicator<T, I extends keyof T> = Indicator<T, I> & Partial<T>;
|
|
8
|
+
type MemoryState<E extends string, T> = {
|
|
9
|
+
[P in E]: ComputedRef<T | null>;
|
|
10
|
+
} & {
|
|
11
|
+
[P in Pluralize<E>]: ComputedRef<T[]>;
|
|
12
|
+
};
|
|
13
|
+
type MemoryAction<A extends string, E extends string, U> = {
|
|
14
|
+
[K in `${A}${Capitalize<E>}`]: (unit: U) => void;
|
|
15
|
+
} & {
|
|
16
|
+
[K in `${A}${Capitalize<Pluralize<E>>}`]: (units: U[]) => void;
|
|
17
|
+
};
|
|
18
|
+
type EndpointAction<E extends string, T> = {
|
|
19
|
+
[M in EndpointMethod as `${M}${Capitalize<E>}`]: (unit?: Partial<T>) => Promise<T>;
|
|
20
|
+
} & {
|
|
21
|
+
[M in EndpointMethod as `${M}${Capitalize<Pluralize<E>>}`]: (units?: Partial<T>[]) => Promise<T[]>;
|
|
22
|
+
};
|
|
23
|
+
type EndpointMonitor<E extends string> = {
|
|
24
|
+
[M in EndpointMethod as `${M}${Capitalize<E>}${EndpointStatusFlag}`]: ComputedRef<boolean>;
|
|
25
|
+
} & {
|
|
26
|
+
[M in EndpointMethod as `${M}${Capitalize<Pluralize<E>>}${EndpointStatusFlag}`]: ComputedRef<boolean>;
|
|
27
|
+
};
|
|
28
|
+
type StoreAlias<E extends string, T, I extends keyof T = keyof T> = MemoryState<E, T> & MemoryAction<"set", E, T | null> & MemoryAction<"edit", E, PartialWithIndicator<T, I>> & MemoryAction<"drop", E, PartialWithIndicator<T, I>> & EndpointAction<E, T> & EndpointMonitor<E>;
|
|
29
|
+
export declare function useStoreAlias<E extends string, T, I extends keyof T = keyof T>(store: Store<E, T, I>): StoreAlias<E, T, I>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { capitalize } from "../utils/transform.js";
|
|
2
|
+
import { EndpointMethod, EndpointStatus, makeEndpointStatusFlag } from "../utils/endpoint.js";
|
|
3
|
+
import { StoreMemoryAction } from "../core/store.js";
|
|
4
|
+
export function useStoreAlias(store) {
|
|
5
|
+
const capitalizedUnit = capitalize(store.alias.unit);
|
|
6
|
+
const capitalizedUnits = capitalize(store.alias.units);
|
|
7
|
+
const output = {
|
|
8
|
+
[store.alias.unit]: store.unit,
|
|
9
|
+
[store.alias.units]: store.units
|
|
10
|
+
};
|
|
11
|
+
for (const action of Object.values(StoreMemoryAction)) {
|
|
12
|
+
output[`${action}${capitalizedUnit}`] = store.memory[`${action}Unit`];
|
|
13
|
+
output[`${action}${capitalizedUnits}`] = store.memory[`${action}Units`];
|
|
14
|
+
}
|
|
15
|
+
for (const method of Object.values(EndpointMethod)) {
|
|
16
|
+
output[`${method}${capitalizedUnit}`] = store.endpoint[`${method}Unit`];
|
|
17
|
+
output[`${method}${capitalizedUnits}`] = store.endpoint[`${method}Units`];
|
|
18
|
+
for (const status of Object.values(EndpointStatus)) {
|
|
19
|
+
const statusFlag = makeEndpointStatusFlag(status);
|
|
20
|
+
output[`${method}${capitalizedUnit}${statusFlag}`] = store.monitor[`${method}Unit${statusFlag}`];
|
|
21
|
+
output[`${method}${capitalizedUnits}${statusFlag}`] = store.monitor[`${method}Units${statusFlag}`];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return output;
|
|
25
|
+
}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
GET = "get",
|
|
4
|
-
POST = "post",
|
|
5
|
-
PUT = "put",
|
|
6
|
-
PATCH = "patch",
|
|
7
|
-
DELETE = "delete"
|
|
8
|
-
}
|
|
1
|
+
import type { MaybeRefOrGetter } from "vue";
|
|
2
|
+
import { EndpointMethod } from "../utils/endpoint.js";
|
|
9
3
|
export declare enum ApiResponseType {
|
|
10
4
|
JSON = "json",
|
|
11
5
|
TEXT = "text",
|
|
@@ -19,7 +13,7 @@ export declare enum ApiErrorSource {
|
|
|
19
13
|
export type ApiRequestHeader = MaybeRefOrGetter<Record<string, unknown>>;
|
|
20
14
|
export type ApiRequestQuery = MaybeRefOrGetter<Record<string, unknown>>;
|
|
21
15
|
export type ApiRequestBody = MaybeRefOrGetter<string | number | ArrayBuffer | FormData | Blob | Record<string, any>>;
|
|
22
|
-
export interface ApiRequestOptions<A extends
|
|
16
|
+
export interface ApiRequestOptions<A extends EndpointMethod = EndpointMethod, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody> {
|
|
23
17
|
action?: A;
|
|
24
18
|
headers?: H;
|
|
25
19
|
query?: Q;
|
|
@@ -37,7 +31,7 @@ export interface ApiOptions {
|
|
|
37
31
|
query?: ApiRequestQuery;
|
|
38
32
|
timeout?: number;
|
|
39
33
|
}
|
|
40
|
-
export type
|
|
34
|
+
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">;
|
|
41
35
|
export interface ApiErrorOptions {
|
|
42
36
|
source: ApiErrorSource;
|
|
43
37
|
method: string;
|
|
@@ -56,10 +50,11 @@ export declare class ApiRequestError extends ApiError {
|
|
|
56
50
|
export declare class ApiResponseError extends ApiError {
|
|
57
51
|
constructor(options: Omit<ApiErrorOptions, "source">);
|
|
58
52
|
}
|
|
59
|
-
export
|
|
60
|
-
get: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?:
|
|
61
|
-
post: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?:
|
|
62
|
-
put: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?:
|
|
63
|
-
patch: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery, B extends ApiRequestBody = ApiRequestBody>(url: string, options?:
|
|
64
|
-
del: <T, H extends ApiRequestHeader = ApiRequestHeader, Q extends ApiRequestQuery = ApiRequestQuery>(url: string, options?:
|
|
65
|
-
}
|
|
53
|
+
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>;
|
|
59
|
+
}
|
|
60
|
+
export declare function createApi(options?: ApiOptions): Api;
|
package/dist/runtime/core/api.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { toValue } from "vue";
|
|
2
|
-
|
|
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 || {});
|
|
2
|
+
import { EndpointMethod } from "../utils/endpoint.js";
|
|
10
3
|
export var ApiResponseType = /* @__PURE__ */ ((ApiResponseType2) => {
|
|
11
4
|
ApiResponseType2["JSON"] = "json";
|
|
12
5
|
ApiResponseType2["TEXT"] = "text";
|
|
@@ -24,7 +17,7 @@ export class ApiError extends Error {
|
|
|
24
17
|
method;
|
|
25
18
|
url;
|
|
26
19
|
constructor(options) {
|
|
27
|
-
super(options.message
|
|
20
|
+
super(options.message ?? "Unknown error");
|
|
28
21
|
this.name = "ApiError";
|
|
29
22
|
this.source = options.source;
|
|
30
23
|
this.method = options.method;
|
|
@@ -51,7 +44,7 @@ export function createApi(options) {
|
|
|
51
44
|
async function request(url, requestOptions) {
|
|
52
45
|
return $fetch(url, {
|
|
53
46
|
baseURL: options?.url,
|
|
54
|
-
method: requestOptions?.action ??
|
|
47
|
+
method: requestOptions?.action ?? EndpointMethod.GET,
|
|
55
48
|
headers: {
|
|
56
49
|
...toValue(options?.headers),
|
|
57
50
|
...toValue(requestOptions?.headers)
|
|
@@ -86,31 +79,31 @@ export function createApi(options) {
|
|
|
86
79
|
async function get(url, options2) {
|
|
87
80
|
return request(url, {
|
|
88
81
|
...options2,
|
|
89
|
-
action:
|
|
82
|
+
action: EndpointMethod.GET
|
|
90
83
|
});
|
|
91
84
|
}
|
|
92
85
|
async function post(url, options2) {
|
|
93
86
|
return request(url, {
|
|
94
87
|
...options2,
|
|
95
|
-
action:
|
|
88
|
+
action: EndpointMethod.POST
|
|
96
89
|
});
|
|
97
90
|
}
|
|
98
91
|
async function put(url, options2) {
|
|
99
92
|
return request(url, {
|
|
100
93
|
...options2,
|
|
101
|
-
action:
|
|
94
|
+
action: EndpointMethod.PUT
|
|
102
95
|
});
|
|
103
96
|
}
|
|
104
97
|
async function patch(url, options2) {
|
|
105
98
|
return request(url, {
|
|
106
99
|
...options2,
|
|
107
|
-
action:
|
|
100
|
+
action: EndpointMethod.PATCH
|
|
108
101
|
});
|
|
109
102
|
}
|
|
110
103
|
async function del(url, options2) {
|
|
111
104
|
return request(url, {
|
|
112
105
|
...options2,
|
|
113
|
-
action:
|
|
106
|
+
action: EndpointMethod.DELETE
|
|
114
107
|
});
|
|
115
108
|
}
|
|
116
109
|
return {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { ComputedRef } from "vue";
|
|
3
|
+
import type { Extension, BaseState } from "@harlem/core";
|
|
4
|
+
import { Endpoint, EndpointStatus } from "../utils/endpoint.js";
|
|
5
|
+
import type { EndpointMethodOptions, ApiOptions } from "./api.js";
|
|
6
|
+
import type { EndpointDefinition, EndpointStatusName } from "../utils/endpoint.js";
|
|
7
|
+
import type { Pluralize } from "../utils/transform.js";
|
|
8
|
+
export declare enum StoreMemoryAction {
|
|
9
|
+
SET = "set",
|
|
10
|
+
EDIT = "edit",
|
|
11
|
+
DROP = "drop"
|
|
12
|
+
}
|
|
4
13
|
export declare enum StoreMemoryPosition {
|
|
5
14
|
FIRST = "first",
|
|
6
15
|
LAST = "last"
|
|
7
16
|
}
|
|
8
|
-
import { ApiAction, type ApiActionOptions, type ApiOptions } from "./api.js";
|
|
9
|
-
export declare class StoreConfigurationError extends Error {
|
|
10
|
-
constructor(message: string);
|
|
11
|
-
}
|
|
12
17
|
export interface StoreHooks {
|
|
13
18
|
before?: () => Promise<void> | void;
|
|
14
19
|
after?: (error?: Error) => Promise<void> | void;
|
|
@@ -19,57 +24,51 @@ export interface StoreOptions {
|
|
|
19
24
|
hooks?: StoreHooks;
|
|
20
25
|
extensions?: Extension<BaseState>[];
|
|
21
26
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
type Indicator<T, I extends keyof T> = Required<Pick<T, I>>;
|
|
28
|
+
type WithIndicator<T, I extends keyof T> = Indicator<T, I> & T;
|
|
29
|
+
type PartialWithIndicator<T, I extends keyof T> = Indicator<T, I> & Partial<T>;
|
|
30
|
+
type StoreEndpointReadOptions = Omit<EndpointMethodOptions<any>, "body">;
|
|
31
|
+
type StoreEndpointWriteOptions = EndpointMethodOptions<any> & {
|
|
32
|
+
validate?: boolean;
|
|
33
|
+
};
|
|
34
|
+
type StoreEndpointWriteMultipleOptions = StoreEndpointWriteOptions & {
|
|
35
|
+
position?: StoreMemoryPosition;
|
|
36
|
+
};
|
|
37
|
+
type StoreMemory<T = unknown, I extends keyof T = keyof T> = {
|
|
38
|
+
setUnit: (unit: T | null) => void;
|
|
39
|
+
setUnits: (units: T[]) => void;
|
|
40
|
+
editUnit: (unit: PartialWithIndicator<T, I>) => void;
|
|
41
|
+
editUnits: (units: PartialWithIndicator<T, I>[]) => void;
|
|
42
|
+
dropUnit: (unit: PartialWithIndicator<T, I>) => void;
|
|
43
|
+
dropUnits: (units: PartialWithIndicator<T, I>[]) => void;
|
|
44
|
+
};
|
|
45
|
+
type StoreEndpoint<T = unknown, I extends keyof T = keyof T> = {
|
|
46
|
+
getUnit: (unit?: PartialWithIndicator<T, I>, options?: StoreEndpointReadOptions) => Promise<T>;
|
|
47
|
+
getUnits: (options?: StoreEndpointReadOptions) => Promise<T[]>;
|
|
48
|
+
postUnit: (unit: WithIndicator<T, I>, options?: StoreEndpointWriteOptions) => Promise<T>;
|
|
49
|
+
postUnits: (units: WithIndicator<T, I>[], options?: StoreEndpointWriteMultipleOptions) => Promise<T[]>;
|
|
50
|
+
putUnit: (unit: WithIndicator<T, I>, options?: StoreEndpointWriteOptions) => Promise<T>;
|
|
51
|
+
putUnits: (units: WithIndicator<T, I>[], options?: StoreEndpointWriteOptions) => Promise<T[]>;
|
|
52
|
+
patchUnit: (unit: PartialWithIndicator<T, I>, options?: StoreEndpointWriteOptions) => Promise<Partial<T>>;
|
|
53
|
+
patchUnits: (units: PartialWithIndicator<T, I>[], options?: StoreEndpointWriteOptions) => Promise<Partial<T>[]>;
|
|
54
|
+
deleteUnit: (unit: PartialWithIndicator<T, I>, options?: StoreEndpointReadOptions) => Promise<boolean>;
|
|
55
|
+
deleteUnits: (units: PartialWithIndicator<T, I>[], options?: StoreEndpointReadOptions) => Promise<boolean>;
|
|
56
|
+
};
|
|
57
|
+
type StoreMonitor = {
|
|
58
|
+
[K in Endpoint as EndpointStatusName<K, EndpointStatus>]: ComputedRef<boolean>;
|
|
59
|
+
};
|
|
60
|
+
export type Store<E extends string = string, T = unknown, I extends keyof T = keyof T> = {
|
|
61
|
+
store: any;
|
|
62
|
+
alias: {
|
|
63
|
+
unit: E;
|
|
64
|
+
units: Pluralize<E>;
|
|
29
65
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}>, never>;
|
|
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, {}>>[]>;
|
|
39
|
-
hasMemorizedUnits: (...units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => Record<string | number, boolean>;
|
|
40
|
-
endpointsStatus: import("../utils/endpoint").EndpointsStatusMap<import("vue").ComputedRef<boolean>>;
|
|
41
|
-
setMemorizedUnit: import("@harlem/core").Mutation<z.core.$InferObjectOutput<T, {}> | null, void>;
|
|
42
|
-
setMemorizedUnits: (payload: z.core.$InferObjectOutput<T, {}>[]) => void;
|
|
43
|
-
editMemorizedUnit: import("@harlem/core").Mutation<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, void>;
|
|
44
|
-
editMemorizedUnits: (payload: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => void;
|
|
45
|
-
dropMemorizedUnit: import("@harlem/core").Mutation<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, void>;
|
|
46
|
-
dropMemorizedUnits: (payload: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[]) => void;
|
|
47
|
-
patchEndpointMemory: (payload: {
|
|
48
|
-
key: Endpoint;
|
|
49
|
-
memory: EndpointMemory;
|
|
50
|
-
}) => void;
|
|
51
|
-
purgeEndpointMemory: (payload?: unknown) => void;
|
|
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, {}>>;
|
|
53
|
-
getUnits: (options?: Omit<ApiActionOptions<ApiAction.GET>, "body">) => Promise<z.core.$InferObjectOutput<T, {}>[]>;
|
|
54
|
-
postUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>, actionOptions?: ApiActionOptions<ApiAction.POST> & {
|
|
55
|
-
validate?: boolean;
|
|
56
|
-
}) => Promise<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>>;
|
|
57
|
-
postUnits: (units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>)[], options?: ApiActionOptions<ApiAction.POST> & {
|
|
58
|
-
validate?: boolean;
|
|
59
|
-
position?: StoreMemoryPosition;
|
|
60
|
-
}) => Promise<(Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>)[]>;
|
|
61
|
-
putUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>, options?: ApiActionOptions<ApiAction.PUT> & {
|
|
62
|
-
validate?: boolean;
|
|
63
|
-
}) => Promise<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>>;
|
|
64
|
-
putUnits: (units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>)[], options?: ApiActionOptions<ApiAction.PUT> & {
|
|
65
|
-
validate?: boolean;
|
|
66
|
-
}) => Promise<(Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & z.core.$InferObjectOutput<T, {}>)[]>;
|
|
67
|
-
patchUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, options?: ApiActionOptions<ApiAction.PATCH> & {
|
|
68
|
-
validate?: boolean;
|
|
69
|
-
}) => Promise<Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>>;
|
|
70
|
-
patchUnits: (units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[], options?: ApiActionOptions<ApiAction.PATCH> & {
|
|
71
|
-
validate?: boolean;
|
|
72
|
-
}) => Promise<(Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[]>;
|
|
73
|
-
deleteUnit: (unit: Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>, options?: Omit<ApiActionOptions<ApiAction.DELETE>, "body">) => Promise<boolean>;
|
|
74
|
-
deleteUnits: (units: (Required<Pick<z.core.$InferObjectOutput<T, {}>, K>> & Partial<z.core.$InferObjectOutput<T, {}>>)[], options?: Omit<ApiActionOptions<ApiAction.DELETE>, "body">) => Promise<boolean>;
|
|
66
|
+
indicator: I;
|
|
67
|
+
unit: ComputedRef<T | null>;
|
|
68
|
+
units: ComputedRef<T[]>;
|
|
69
|
+
memory: StoreMemory<T, I>;
|
|
70
|
+
endpoint: StoreEndpoint<T, I>;
|
|
71
|
+
monitor: StoreMonitor;
|
|
75
72
|
};
|
|
73
|
+
export declare function createStore<E extends string, T extends z.ZodRawShape, S extends z.infer<z.ZodObject<T>> = z.infer<z.ZodObject<T>>, I extends keyof S = "id" & keyof S>(entity: E, schema: z.ZodObject<T>, endpoints?: Partial<Record<Endpoint, EndpointDefinition<Partial<S>>>>, options?: StoreOptions): Store<E, S, I>;
|
|
74
|
+
export {};
|
|
@@ -1,277 +1,181 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
} from "@harlem/core";
|
|
2
|
+
import { createStore as createHarlemStore } from "@harlem/core";
|
|
3
|
+
import { createApi } from "./api.js";
|
|
5
4
|
import { resolveSchema } from "../utils/schema.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
5
|
+
import { pluralize } from "../utils/transform.js";
|
|
6
|
+
import { Endpoint, EndpointStatus, getEndpoint, makeEndpointStatusName, resolveEndpointUrl } from "../utils/endpoint.js";
|
|
7
|
+
export var StoreMemoryAction = /* @__PURE__ */ ((StoreMemoryAction2) => {
|
|
8
|
+
StoreMemoryAction2["SET"] = "set";
|
|
9
|
+
StoreMemoryAction2["EDIT"] = "edit";
|
|
10
|
+
StoreMemoryAction2["DROP"] = "drop";
|
|
11
|
+
return StoreMemoryAction2;
|
|
12
|
+
})(StoreMemoryAction || {});
|
|
14
13
|
export var StoreMemoryPosition = /* @__PURE__ */ ((StoreMemoryPosition2) => {
|
|
15
14
|
StoreMemoryPosition2["FIRST"] = "first";
|
|
16
15
|
StoreMemoryPosition2["LAST"] = "last";
|
|
17
16
|
return StoreMemoryPosition2;
|
|
18
17
|
})(StoreMemoryPosition || {});
|
|
19
|
-
|
|
20
|
-
createApi
|
|
21
|
-
} from "./api.js";
|
|
22
|
-
export class StoreConfigurationError extends Error {
|
|
23
|
-
constructor(message) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = "StoreConfigurationError";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export function createStore(name, schema, endpoints, options) {
|
|
18
|
+
export function createStore(entity, schema, endpoints, options) {
|
|
29
19
|
const { indicator } = resolveSchema(schema, {
|
|
30
20
|
indicator: options?.indicator
|
|
31
21
|
});
|
|
32
|
-
const hooks = options?.hooks;
|
|
33
22
|
let apiClient;
|
|
34
|
-
let apiInitError = null;
|
|
35
23
|
function api() {
|
|
36
|
-
if (apiInitError) {
|
|
37
|
-
throw apiInitError;
|
|
38
|
-
}
|
|
39
24
|
if (!apiClient) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
}
|
|
25
|
+
const config = useRuntimeConfig();
|
|
26
|
+
apiClient = createApi({
|
|
27
|
+
...config.public.harlemify?.api ?? {},
|
|
28
|
+
...options?.api
|
|
29
|
+
});
|
|
57
30
|
}
|
|
58
31
|
return apiClient;
|
|
59
32
|
}
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
unit: null,
|
|
65
|
-
units: []
|
|
66
|
-
},
|
|
67
|
-
endpoints: {}
|
|
33
|
+
const state = {
|
|
34
|
+
memory: {
|
|
35
|
+
unit: null,
|
|
36
|
+
units: []
|
|
68
37
|
},
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
38
|
+
endpoints: {}
|
|
39
|
+
};
|
|
40
|
+
const store = createHarlemStore(entity, state, {
|
|
41
|
+
extensions: options?.extensions ?? []
|
|
42
|
+
});
|
|
43
|
+
const alias = {
|
|
44
|
+
unit: entity,
|
|
45
|
+
units: pluralize(entity)
|
|
46
|
+
};
|
|
47
|
+
const memorizedUnit = store.getter("memorizedUnit", (state2) => {
|
|
48
|
+
return state2.memory.unit;
|
|
49
|
+
});
|
|
50
|
+
const memorizedUnits = store.getter("memorizedUnits", (state2) => {
|
|
51
|
+
return state2.memory.units;
|
|
75
52
|
});
|
|
76
|
-
const
|
|
77
|
-
|
|
53
|
+
const setMemorizedUnit = store.mutation("setMemorizedUnit", (state2, unit = null) => {
|
|
54
|
+
state2.memory.unit = unit;
|
|
78
55
|
});
|
|
79
|
-
|
|
80
|
-
|
|
56
|
+
const setMemorizedUnits = store.mutation("setMemorizedUnits", (state2, units = []) => {
|
|
57
|
+
state2.memory.units = units;
|
|
58
|
+
});
|
|
59
|
+
const editMemorizedUnit = store.mutation("editMemorizedUnit", (state2, unit) => {
|
|
60
|
+
if (state2.memory.unit?.[indicator] === unit[indicator]) {
|
|
61
|
+
state2.memory.unit = defu(unit, state2.memory.unit);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const editMemorizedUnits = store.mutation("editMemorizedUnits", (state2, units) => {
|
|
81
65
|
for (const unit of units) {
|
|
82
|
-
const
|
|
66
|
+
const index = state2.memory.units.findIndex((memorizedUnit2) => {
|
|
83
67
|
return memorizedUnit2[indicator] === unit[indicator];
|
|
84
68
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return output;
|
|
88
|
-
}
|
|
89
|
-
const setMemorizedUnit = store.mutation(
|
|
90
|
-
"setMemorizedUnit",
|
|
91
|
-
(state, unit = null) => {
|
|
92
|
-
state.memory.unit = unit;
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
const setMemorizedUnits = store.mutation(
|
|
96
|
-
"setMemorizedUnits",
|
|
97
|
-
(state, units = []) => {
|
|
98
|
-
state.memory.units = units;
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
const editMemorizedUnit = store.mutation(
|
|
102
|
-
"editMemorizedUnit",
|
|
103
|
-
(state, unit) => {
|
|
104
|
-
if (state.memory.unit?.[indicator] === unit[indicator]) {
|
|
105
|
-
state.memory.unit = defu(unit, state.memory.unit);
|
|
69
|
+
if (index !== -1) {
|
|
70
|
+
state2.memory.units[index] = defu(unit, state2.memory.units[index]);
|
|
106
71
|
}
|
|
107
72
|
}
|
|
108
|
-
);
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
73
|
+
});
|
|
74
|
+
const dropMemorizedUnit = store.mutation("dropMemorizedUnit", (state2, unit) => {
|
|
75
|
+
if (state2.memory.unit?.[indicator] === unit[indicator]) {
|
|
76
|
+
state2.memory.unit = null;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const dropMemorizedUnits = store.mutation("dropMemorizedUnits", (state2, units) => {
|
|
80
|
+
state2.memory.units = state2.memory.units.filter((memorizedUnit2) => {
|
|
112
81
|
for (const unit of units) {
|
|
113
|
-
|
|
114
|
-
return
|
|
115
|
-
});
|
|
116
|
-
if (index !== -1) {
|
|
117
|
-
state.memory.units[index] = defu(
|
|
118
|
-
unit,
|
|
119
|
-
state.memory.units[index]
|
|
120
|
-
);
|
|
82
|
+
if (memorizedUnit2[indicator] === unit[indicator]) {
|
|
83
|
+
return false;
|
|
121
84
|
}
|
|
122
85
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
133
|
-
const dropMemorizedUnits = store.mutation(
|
|
134
|
-
"dropMemorizedUnits",
|
|
135
|
-
(state, units) => {
|
|
136
|
-
state.memory.units = state.memory.units.filter((memorizedUnit2) => {
|
|
137
|
-
for (const unit of units) {
|
|
138
|
-
if (memorizedUnit2[indicator] === unit[indicator]) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return true;
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
const monitor = {};
|
|
90
|
+
for (const endpoint of Object.values(Endpoint)) {
|
|
91
|
+
for (const endpointStatus of Object.values(EndpointStatus)) {
|
|
92
|
+
const statusKey = makeEndpointStatusName(endpoint, endpointStatus);
|
|
93
|
+
monitor[statusKey] = store.getter(statusKey, (state2) => {
|
|
94
|
+
return state2.endpoints[endpoint] === endpointStatus;
|
|
143
95
|
});
|
|
144
96
|
}
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
key,
|
|
151
|
-
memory
|
|
152
|
-
}) => {
|
|
153
|
-
state.endpoints[key] = memory;
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
const purgeEndpointMemory = store.mutation(
|
|
157
|
-
"purgeEndpointMemory",
|
|
158
|
-
(state) => {
|
|
159
|
-
state.endpoints = {};
|
|
97
|
+
}
|
|
98
|
+
const patchMonitor = store.mutation(
|
|
99
|
+
"patchMonitor",
|
|
100
|
+
(state2, payload) => {
|
|
101
|
+
state2.endpoints[payload.endpoint] = payload.status;
|
|
160
102
|
}
|
|
161
103
|
);
|
|
162
|
-
function
|
|
163
|
-
if (
|
|
164
|
-
const statusKey =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
);
|
|
168
|
-
if (endpointsStatus[statusKey].value) {
|
|
169
|
-
throw new Error(`Endpoint "${key}" is already pending`);
|
|
104
|
+
function patchMonitorTo(endpoint, status) {
|
|
105
|
+
if (status === EndpointStatus.PENDING) {
|
|
106
|
+
const statusKey = makeEndpointStatusName(endpoint, EndpointStatus.PENDING);
|
|
107
|
+
if (monitor[statusKey].value) {
|
|
108
|
+
throw new Error(`Endpoint "${endpoint}" is already pending`);
|
|
170
109
|
}
|
|
171
110
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
111
|
+
patchMonitor({
|
|
112
|
+
endpoint,
|
|
113
|
+
status
|
|
175
114
|
});
|
|
176
115
|
}
|
|
177
|
-
async function
|
|
178
|
-
await hooks?.before?.();
|
|
179
|
-
|
|
180
|
-
status: EndpointStatus.PENDING
|
|
181
|
-
});
|
|
116
|
+
async function withStatus(key, operation) {
|
|
117
|
+
await options?.hooks?.before?.();
|
|
118
|
+
patchMonitorTo(key, EndpointStatus.PENDING);
|
|
182
119
|
try {
|
|
183
120
|
const result = await operation();
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
await hooks?.after?.();
|
|
121
|
+
patchMonitorTo(key, EndpointStatus.SUCCESS);
|
|
122
|
+
await options?.hooks?.after?.();
|
|
188
123
|
return result;
|
|
189
124
|
} catch (error) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
await hooks?.after?.(error);
|
|
125
|
+
patchMonitorTo(key, EndpointStatus.FAILED);
|
|
126
|
+
await options?.hooks?.after?.(error);
|
|
194
127
|
throw error;
|
|
195
128
|
}
|
|
196
129
|
}
|
|
197
|
-
async function
|
|
130
|
+
async function getUnitEndpoint(unit, options2) {
|
|
198
131
|
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNIT);
|
|
199
|
-
return
|
|
200
|
-
const response = await api().get(
|
|
201
|
-
resolveEndpointUrl(endpoint, unit),
|
|
202
|
-
options2
|
|
203
|
-
);
|
|
132
|
+
return withStatus(Endpoint.GET_UNIT, async () => {
|
|
133
|
+
const response = await api().get(resolveEndpointUrl(endpoint, unit), options2);
|
|
204
134
|
setMemorizedUnit(response);
|
|
205
135
|
return response;
|
|
206
136
|
});
|
|
207
137
|
}
|
|
208
|
-
async function
|
|
138
|
+
async function getUnitsEndpoint(options2) {
|
|
209
139
|
const endpoint = getEndpoint(endpoints, Endpoint.GET_UNITS);
|
|
210
|
-
return
|
|
211
|
-
const response = await api().get(
|
|
212
|
-
resolveEndpointUrl(endpoint),
|
|
213
|
-
options2
|
|
214
|
-
);
|
|
140
|
+
return withStatus(Endpoint.GET_UNITS, async () => {
|
|
141
|
+
const response = await api().get(resolveEndpointUrl(endpoint), options2);
|
|
215
142
|
setMemorizedUnits(response);
|
|
216
143
|
return response;
|
|
217
144
|
});
|
|
218
145
|
}
|
|
219
|
-
async function
|
|
146
|
+
async function postUnitEndpoint(unit, actionOptions) {
|
|
220
147
|
const endpoint = getEndpoint(endpoints, Endpoint.POST_UNIT);
|
|
221
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
222
|
-
indicator,
|
|
223
|
-
endpoint,
|
|
224
|
-
unit
|
|
225
|
-
});
|
|
148
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
226
149
|
if (actionOptions?.validate) {
|
|
227
150
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
228
151
|
}
|
|
229
|
-
return
|
|
230
|
-
const response = await api().post(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
...actionOptions,
|
|
234
|
-
body: actionOptions?.body ?? resolvedSchema.values
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
setMemorizedUnit({
|
|
238
|
-
...unit,
|
|
239
|
-
...response
|
|
152
|
+
return withStatus(Endpoint.POST_UNIT, async () => {
|
|
153
|
+
const response = await api().post(resolveEndpointUrl(endpoint, unit), {
|
|
154
|
+
...actionOptions,
|
|
155
|
+
body: actionOptions?.body ?? resolvedSchema.values
|
|
240
156
|
});
|
|
157
|
+
setMemorizedUnit({ ...unit, ...response });
|
|
241
158
|
return response;
|
|
242
159
|
});
|
|
243
160
|
}
|
|
244
|
-
async function
|
|
161
|
+
async function postUnitsEndpoint(units, options2) {
|
|
245
162
|
const endpoint = getEndpoint(endpoints, Endpoint.POST_UNITS);
|
|
246
|
-
return
|
|
163
|
+
return withStatus(Endpoint.POST_UNITS, async () => {
|
|
247
164
|
const responses = [];
|
|
248
165
|
for (const unit of units) {
|
|
249
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
250
|
-
indicator,
|
|
251
|
-
endpoint,
|
|
252
|
-
unit
|
|
253
|
-
});
|
|
166
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
254
167
|
if (options2?.validate) {
|
|
255
168
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
256
169
|
}
|
|
257
|
-
const response = await api().post(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
body: options2?.body ?? resolvedSchema.values
|
|
262
|
-
}
|
|
263
|
-
);
|
|
170
|
+
const response = await api().post(resolveEndpointUrl(endpoint, unit), {
|
|
171
|
+
...options2,
|
|
172
|
+
body: options2?.body ?? resolvedSchema.values
|
|
173
|
+
});
|
|
264
174
|
const clonedUnits = [...memorizedUnits.value];
|
|
265
175
|
if (options2?.position === "last" /* LAST */) {
|
|
266
|
-
clonedUnits.push({
|
|
267
|
-
...unit,
|
|
268
|
-
...response
|
|
269
|
-
});
|
|
176
|
+
clonedUnits.push({ ...unit, ...response });
|
|
270
177
|
} else {
|
|
271
|
-
clonedUnits.unshift({
|
|
272
|
-
...unit,
|
|
273
|
-
...response
|
|
274
|
-
});
|
|
178
|
+
clonedUnits.unshift({ ...unit, ...response });
|
|
275
179
|
}
|
|
276
180
|
setMemorizedUnits(clonedUnits);
|
|
277
181
|
responses.push(response);
|
|
@@ -279,94 +183,61 @@ export function createStore(name, schema, endpoints, options) {
|
|
|
279
183
|
return responses;
|
|
280
184
|
});
|
|
281
185
|
}
|
|
282
|
-
async function
|
|
186
|
+
async function putUnitEndpoint(unit, options2) {
|
|
283
187
|
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNIT);
|
|
284
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
285
|
-
indicator,
|
|
286
|
-
endpoint,
|
|
287
|
-
unit
|
|
288
|
-
});
|
|
188
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
289
189
|
if (options2?.validate) {
|
|
290
190
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
291
191
|
}
|
|
292
|
-
return
|
|
293
|
-
const response = await api().put(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
...options2,
|
|
297
|
-
body: options2?.body ?? resolvedSchema.values
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
setMemorizedUnit({
|
|
301
|
-
...unit,
|
|
302
|
-
...response
|
|
192
|
+
return withStatus(Endpoint.PUT_UNIT, async () => {
|
|
193
|
+
const response = await api().put(resolveEndpointUrl(endpoint, unit), {
|
|
194
|
+
...options2,
|
|
195
|
+
body: options2?.body ?? resolvedSchema.values
|
|
303
196
|
});
|
|
197
|
+
setMemorizedUnit({ ...unit, ...response });
|
|
304
198
|
return response;
|
|
305
199
|
});
|
|
306
200
|
}
|
|
307
|
-
async function
|
|
201
|
+
async function putUnitsEndpoint(units, options2) {
|
|
308
202
|
const endpoint = getEndpoint(endpoints, Endpoint.PUT_UNITS);
|
|
309
|
-
return
|
|
203
|
+
return withStatus(Endpoint.PUT_UNITS, async () => {
|
|
310
204
|
const responses = [];
|
|
311
205
|
for (const unit of units) {
|
|
312
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
313
|
-
indicator,
|
|
314
|
-
endpoint,
|
|
315
|
-
unit
|
|
316
|
-
});
|
|
206
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
317
207
|
if (options2?.validate) {
|
|
318
208
|
schema.pick(resolvedSchema.keys).parse(unit);
|
|
319
209
|
}
|
|
320
|
-
const response = await api().put(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
);
|
|
327
|
-
editMemorizedUnits([
|
|
328
|
-
{
|
|
329
|
-
...unit,
|
|
330
|
-
...response
|
|
331
|
-
}
|
|
332
|
-
]);
|
|
210
|
+
const response = await api().put(resolveEndpointUrl(endpoint, unit), {
|
|
211
|
+
...options2,
|
|
212
|
+
body: options2?.body ?? resolvedSchema.values
|
|
213
|
+
});
|
|
214
|
+
editMemorizedUnits([{ ...unit, ...response }]);
|
|
333
215
|
responses.push(response);
|
|
334
216
|
}
|
|
335
217
|
return responses;
|
|
336
218
|
});
|
|
337
219
|
}
|
|
338
|
-
async function
|
|
220
|
+
async function patchUnitEndpoint(unit, options2) {
|
|
339
221
|
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNIT);
|
|
340
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
341
|
-
indicator,
|
|
342
|
-
endpoint,
|
|
343
|
-
unit
|
|
344
|
-
});
|
|
222
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
345
223
|
if (options2?.validate) {
|
|
346
224
|
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
347
225
|
}
|
|
348
|
-
return
|
|
226
|
+
return withStatus(Endpoint.PATCH_UNIT, async () => {
|
|
349
227
|
const response = await api().patch(resolveEndpointUrl(endpoint, unit), {
|
|
350
228
|
...options2,
|
|
351
229
|
body: options2?.body ?? resolvedSchema.values
|
|
352
230
|
});
|
|
353
|
-
editMemorizedUnit({
|
|
354
|
-
...unit,
|
|
355
|
-
...response
|
|
356
|
-
});
|
|
231
|
+
editMemorizedUnit({ ...unit, ...response });
|
|
357
232
|
return response;
|
|
358
233
|
});
|
|
359
234
|
}
|
|
360
|
-
async function
|
|
235
|
+
async function patchUnitsEndpoint(units, options2) {
|
|
361
236
|
const endpoint = getEndpoint(endpoints, Endpoint.PATCH_UNITS);
|
|
362
|
-
return
|
|
237
|
+
return withStatus(Endpoint.PATCH_UNITS, async () => {
|
|
363
238
|
const responses = [];
|
|
364
239
|
for (const unit of units) {
|
|
365
|
-
const resolvedSchema = resolveSchema(schema, {
|
|
366
|
-
indicator,
|
|
367
|
-
endpoint,
|
|
368
|
-
unit
|
|
369
|
-
});
|
|
240
|
+
const resolvedSchema = resolveSchema(schema, { indicator, endpoint, unit });
|
|
370
241
|
if (options2?.validate) {
|
|
371
242
|
schema.pick(resolvedSchema.keys).partial().parse(unit);
|
|
372
243
|
}
|
|
@@ -374,65 +245,56 @@ export function createStore(name, schema, endpoints, options) {
|
|
|
374
245
|
...options2,
|
|
375
246
|
body: options2?.body ?? resolvedSchema.values
|
|
376
247
|
});
|
|
377
|
-
editMemorizedUnits([
|
|
378
|
-
{
|
|
379
|
-
...unit,
|
|
380
|
-
...response
|
|
381
|
-
}
|
|
382
|
-
]);
|
|
248
|
+
editMemorizedUnits([{ ...unit, ...response }]);
|
|
383
249
|
responses.push(response);
|
|
384
250
|
}
|
|
385
251
|
return responses;
|
|
386
252
|
});
|
|
387
253
|
}
|
|
388
|
-
async function
|
|
254
|
+
async function deleteUnitEndpoint(unit, options2) {
|
|
389
255
|
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNIT);
|
|
390
|
-
return
|
|
391
|
-
await api().del(
|
|
392
|
-
resolveEndpointUrl(endpoint, unit),
|
|
393
|
-
options2
|
|
394
|
-
);
|
|
256
|
+
return withStatus(Endpoint.DELETE_UNIT, async () => {
|
|
257
|
+
await api().del(resolveEndpointUrl(endpoint, unit), options2);
|
|
395
258
|
dropMemorizedUnit(unit);
|
|
396
259
|
return true;
|
|
397
260
|
});
|
|
398
261
|
}
|
|
399
|
-
async function
|
|
262
|
+
async function deleteUnitsEndpoint(units, options2) {
|
|
400
263
|
const endpoint = getEndpoint(endpoints, Endpoint.DELETE_UNITS);
|
|
401
|
-
return
|
|
264
|
+
return withStatus(Endpoint.DELETE_UNITS, async () => {
|
|
402
265
|
for (const unit of units) {
|
|
403
|
-
await api().del(
|
|
404
|
-
resolveEndpointUrl(endpoint, unit),
|
|
405
|
-
options2
|
|
406
|
-
);
|
|
266
|
+
await api().del(resolveEndpointUrl(endpoint, unit), options2);
|
|
407
267
|
dropMemorizedUnits([unit]);
|
|
408
268
|
}
|
|
409
269
|
return true;
|
|
410
270
|
});
|
|
411
271
|
}
|
|
412
272
|
return {
|
|
413
|
-
api,
|
|
414
273
|
store,
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
274
|
+
alias,
|
|
275
|
+
indicator,
|
|
276
|
+
unit: memorizedUnit,
|
|
277
|
+
units: memorizedUnits,
|
|
278
|
+
memory: {
|
|
279
|
+
setUnit: setMemorizedUnit,
|
|
280
|
+
setUnits: setMemorizedUnits,
|
|
281
|
+
editUnit: editMemorizedUnit,
|
|
282
|
+
editUnits: editMemorizedUnits,
|
|
283
|
+
dropUnit: dropMemorizedUnit,
|
|
284
|
+
dropUnits: dropMemorizedUnits
|
|
285
|
+
},
|
|
286
|
+
endpoint: {
|
|
287
|
+
getUnit: getUnitEndpoint,
|
|
288
|
+
getUnits: getUnitsEndpoint,
|
|
289
|
+
postUnit: postUnitEndpoint,
|
|
290
|
+
postUnits: postUnitsEndpoint,
|
|
291
|
+
putUnit: putUnitEndpoint,
|
|
292
|
+
putUnits: putUnitsEndpoint,
|
|
293
|
+
patchUnit: patchUnitEndpoint,
|
|
294
|
+
patchUnits: patchUnitsEndpoint,
|
|
295
|
+
deleteUnit: deleteUnitEndpoint,
|
|
296
|
+
deleteUnits: deleteUnitsEndpoint
|
|
297
|
+
},
|
|
298
|
+
monitor
|
|
437
299
|
};
|
|
438
300
|
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { type SchemaMeta, getMeta, resolveSchema } from "./utils/schema.js";
|
|
1
|
+
export { EndpointMethod, Endpoint, EndpointStatus } from "./utils/endpoint.js";
|
|
2
|
+
export { getMeta, resolveSchema } from "./utils/schema.js";
|
|
3
|
+
export { createApi, ApiResponseType, ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/api.js";
|
|
5
4
|
export { createStore, StoreMemoryPosition } from "./core/store.js";
|
|
5
|
+
export { useStoreAlias } from "./composables/use.js";
|
|
6
|
+
export type { EndpointDefinition } from "./utils/endpoint.js";
|
|
7
|
+
export type { SchemaMeta } from "./utils/schema.js";
|
|
8
|
+
export type { ApiRequestHeader, ApiRequestQuery, ApiRequestBody, ApiRequestOptions, ApiOptions, EndpointMethodOptions, ApiErrorOptions, } from "./core/api.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
export {
|
|
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";
|
|
1
|
+
export { EndpointMethod, Endpoint, EndpointStatus } from "./utils/endpoint.js";
|
|
15
2
|
export { getMeta, resolveSchema } from "./utils/schema.js";
|
|
3
|
+
export { createApi, ApiResponseType, ApiErrorSource, ApiError, ApiRequestError, ApiResponseError } from "./core/api.js";
|
|
16
4
|
export { createStore, StoreMemoryPosition } from "./core/store.js";
|
|
5
|
+
export { useStoreAlias } from "./composables/use.js";
|
package/dist/runtime/plugin.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { createVuePlugin } from "@harlem/core";
|
|
2
|
-
import {
|
|
3
|
-
createServerSSRPlugin,
|
|
4
|
-
createClientSSRPlugin,
|
|
5
|
-
getBridgingScript
|
|
6
|
-
} from "@harlem/plugin-ssr";
|
|
2
|
+
import { createServerSSRPlugin, createClientSSRPlugin, getBridgingScript } from "@harlem/plugin-ssr";
|
|
7
3
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
8
4
|
const plugins = [];
|
|
9
5
|
if (import.meta.server) {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { Capitalize } from "./transform.js";
|
|
2
|
+
export declare enum EndpointMethod {
|
|
3
|
+
GET = "get",
|
|
4
|
+
POST = "post",
|
|
5
|
+
PUT = "put",
|
|
6
|
+
PATCH = "patch",
|
|
7
|
+
DELETE = "delete"
|
|
8
|
+
}
|
|
3
9
|
export declare enum Endpoint {
|
|
4
10
|
GET_UNIT = "getUnit",
|
|
5
11
|
GET_UNITS = "getUnits",
|
|
@@ -19,21 +25,14 @@ export declare enum EndpointStatus {
|
|
|
19
25
|
FAILED = "failed"
|
|
20
26
|
}
|
|
21
27
|
export interface EndpointDefinition<T = Record<string, unknown>> {
|
|
22
|
-
|
|
28
|
+
method: EndpointMethod;
|
|
23
29
|
url: string | ((params: T) => string);
|
|
24
30
|
}
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export
|
|
30
|
-
export declare function makeEndpointStatusKey<K extends Endpoint, S extends EndpointStatus>(key: K, status: S): EndpointStatusKey<K, S>;
|
|
31
|
-
export declare function getEndpoint<T = Record<string, unknown>>(endpoints: Partial<Record<Endpoint, EndpointDefinition<T>>> | undefined, key: Endpoint): EndpointDefinition<T>;
|
|
31
|
+
export type EndpointStatusFlag<S extends EndpointStatus = EndpointStatus> = `Is${Capitalize<S>}`;
|
|
32
|
+
export type EndpointStatusName<K extends Endpoint = Endpoint, S extends EndpointStatus = EndpointStatus> = `${K}${EndpointStatusFlag<S>}`;
|
|
33
|
+
export declare function makeEndpointStatusFlag<S extends EndpointStatus>(status: S): EndpointStatusFlag<S>;
|
|
34
|
+
export declare function makeEndpointStatusName<K extends Endpoint, S extends EndpointStatus>(key: K, status: S): EndpointStatusName<K, S>;
|
|
35
|
+
export declare function getEndpoint<T = Record<string, unknown>>(endpoints: Partial<Record<Endpoint, EndpointDefinition<T>>> | undefined, endpoint: Endpoint): EndpointDefinition<T>;
|
|
32
36
|
export declare function resolveEndpointUrl<T>(endpoint: EndpointDefinition<T>, params?: {
|
|
33
37
|
[key: string]: unknown;
|
|
34
38
|
}): string;
|
|
35
|
-
export type EndpointsStatusMap<T> = {
|
|
36
|
-
[K in Endpoint as EndpointStatusKey<K, EndpointStatus>]: T;
|
|
37
|
-
};
|
|
38
|
-
export declare function makeEndpointsStatus<T>(getter: (name: string, fn: (state: BaseState) => boolean) => T): EndpointsStatusMap<T>;
|
|
39
|
-
export {};
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { capitalize } from "./transform.js";
|
|
2
|
+
export var EndpointMethod = /* @__PURE__ */ ((EndpointMethod2) => {
|
|
3
|
+
EndpointMethod2["GET"] = "get";
|
|
4
|
+
EndpointMethod2["POST"] = "post";
|
|
5
|
+
EndpointMethod2["PUT"] = "put";
|
|
6
|
+
EndpointMethod2["PATCH"] = "patch";
|
|
7
|
+
EndpointMethod2["DELETE"] = "delete";
|
|
8
|
+
return EndpointMethod2;
|
|
9
|
+
})(EndpointMethod || {});
|
|
1
10
|
export var Endpoint = /* @__PURE__ */ ((Endpoint2) => {
|
|
2
11
|
Endpoint2["GET_UNIT"] = "getUnit";
|
|
3
12
|
Endpoint2["GET_UNITS"] = "getUnits";
|
|
@@ -18,16 +27,17 @@ export var EndpointStatus = /* @__PURE__ */ ((EndpointStatus2) => {
|
|
|
18
27
|
EndpointStatus2["FAILED"] = "failed";
|
|
19
28
|
return EndpointStatus2;
|
|
20
29
|
})(EndpointStatus || {});
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
return `${key}Is${capitalizedStatus}`;
|
|
30
|
+
export function makeEndpointStatusFlag(status) {
|
|
31
|
+
return `Is${capitalize(status)}`;
|
|
24
32
|
}
|
|
25
|
-
export function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
export function makeEndpointStatusName(key, status) {
|
|
34
|
+
return `${key}${makeEndpointStatusFlag(status)}`;
|
|
35
|
+
}
|
|
36
|
+
export function getEndpoint(endpoints, endpoint) {
|
|
37
|
+
if (!endpoints || !(endpoint in endpoints)) {
|
|
38
|
+
throw new Error(`Endpoint "${endpoint}" is not configured`);
|
|
29
39
|
}
|
|
30
|
-
return endpoint;
|
|
40
|
+
return endpoints[endpoint];
|
|
31
41
|
}
|
|
32
42
|
export function resolveEndpointUrl(endpoint, params) {
|
|
33
43
|
if (typeof endpoint.url === "function") {
|
|
@@ -35,15 +45,3 @@ export function resolveEndpointUrl(endpoint, params) {
|
|
|
35
45
|
}
|
|
36
46
|
return endpoint.url;
|
|
37
47
|
}
|
|
38
|
-
export function makeEndpointsStatus(getter) {
|
|
39
|
-
const output = {};
|
|
40
|
-
for (const key of Object.values(Endpoint)) {
|
|
41
|
-
for (const status of Object.values(EndpointStatus)) {
|
|
42
|
-
const statusKey = makeEndpointStatusKey(key, status);
|
|
43
|
-
output[statusKey] = getter(statusKey, (state) => {
|
|
44
|
-
return state.endpoints[key]?.status === status;
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return output;
|
|
49
|
-
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type {
|
|
3
|
-
import type { EndpointDefinition } from "./endpoint.js";
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { EndpointDefinition, EndpointMethod } from "./endpoint.js";
|
|
4
3
|
export interface SchemaMeta {
|
|
5
4
|
indicator?: boolean;
|
|
6
|
-
|
|
5
|
+
methods?: EndpointMethod[];
|
|
7
6
|
}
|
|
8
7
|
export declare function getMeta(field: any): SchemaMeta | undefined;
|
|
9
8
|
export interface ResolveSchemaOptions<S> {
|
|
@@ -12,10 +12,10 @@ export function resolveSchema(schema, options) {
|
|
|
12
12
|
if (meta?.indicator) {
|
|
13
13
|
output.indicator = key;
|
|
14
14
|
}
|
|
15
|
-
if (!options?.endpoint?.
|
|
15
|
+
if (!options?.endpoint?.method || !meta?.methods) {
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
18
|
-
if (meta?.
|
|
18
|
+
if (meta?.methods.includes(options.endpoint.method)) {
|
|
19
19
|
output.keys[key] = true;
|
|
20
20
|
if (options?.unit && key in options.unit) {
|
|
21
21
|
output.values[key] = options.unit[key];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type IsVowel<C extends string> = C extends "a" | "e" | "i" | "o" | "u" ? true : false;
|
|
2
|
+
export type Pluralize<S extends string> = S extends `${infer Base}y` ? Base extends `${infer _Rest}${infer Last}` ? IsVowel<Last> extends true ? `${S}s` : `${Base}ies` : `${S}s` : S extends `${string}${"s" | "x" | "z"}` ? `${S}es` : S extends `${string}${"ch" | "sh"}` ? `${S}es` : `${S}s`;
|
|
3
|
+
export declare function pluralize<S extends string>(word: S): Pluralize<S>;
|
|
4
|
+
export type Capitalize<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S;
|
|
5
|
+
export declare function capitalize<S extends string>(str: S): Capitalize<S>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function pluralize(word) {
|
|
2
|
+
if (!word) return word;
|
|
3
|
+
const vowels = ["a", "e", "i", "o", "u"];
|
|
4
|
+
const lastChar = word.slice(-1).toLowerCase();
|
|
5
|
+
const lastTwo = word.slice(-2).toLowerCase();
|
|
6
|
+
const beforeLast = word.slice(-2, -1).toLowerCase();
|
|
7
|
+
if (lastChar === "y" && !vowels.includes(beforeLast)) {
|
|
8
|
+
return word.slice(0, -1) + "ies";
|
|
9
|
+
}
|
|
10
|
+
if (["s", "x", "z"].includes(lastChar)) {
|
|
11
|
+
return word + "es";
|
|
12
|
+
}
|
|
13
|
+
if (["ch", "sh"].includes(lastTwo)) {
|
|
14
|
+
return word + "es";
|
|
15
|
+
}
|
|
16
|
+
return word + "s";
|
|
17
|
+
}
|
|
18
|
+
export function capitalize(str) {
|
|
19
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
20
|
+
}
|