@diphyx/harlemify 5.2.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -31
- package/dist/module.json +2 -2
- package/dist/module.mjs +5 -3
- package/dist/runtime/composables/action.d.ts +4 -4
- package/dist/runtime/composables/compose.d.ts +9 -0
- package/dist/runtime/composables/compose.js +10 -0
- package/dist/runtime/composables/model.d.ts +3 -3
- package/dist/runtime/composables/view.d.ts +3 -4
- package/dist/runtime/composables/view.js +2 -14
- package/dist/runtime/core/layers/model.js +36 -7
- package/dist/runtime/core/store.d.ts +2 -1
- package/dist/runtime/core/store.js +41 -24
- package/dist/runtime/core/types/action.d.ts +43 -30
- package/dist/runtime/core/types/action.js +5 -0
- package/dist/runtime/core/types/base.d.ts +1 -2
- package/dist/runtime/core/types/compose.d.ts +18 -0
- package/dist/runtime/core/types/compose.js +0 -0
- package/dist/runtime/core/types/model.d.ts +61 -30
- package/dist/runtime/core/types/model.js +15 -5
- package/dist/runtime/core/types/shape.d.ts +0 -6
- package/dist/runtime/core/types/store.d.ts +6 -2
- package/dist/runtime/core/utils/action.js +4 -1
- package/dist/runtime/core/utils/base.d.ts +2 -1
- package/dist/runtime/core/utils/base.js +4 -5
- package/dist/runtime/core/utils/compose.d.ts +3 -0
- package/dist/runtime/core/utils/compose.js +37 -0
- package/dist/runtime/core/utils/model.js +174 -165
- package/dist/runtime/core/utils/shape.d.ts +4 -3
- package/dist/runtime/core/utils/shape.js +84 -124
- package/dist/runtime/core/utils/store.d.ts +1 -1
- package/dist/runtime/core/utils/store.js +5 -6
- package/dist/runtime/core/utils/view.js +1 -1
- package/dist/runtime/index.d.ts +5 -2
- package/dist/runtime/index.js +3 -2
- package/dist/runtime/plugin.js +3 -17
- package/dist/runtime/plugins/ssr.d.ts +9 -0
- package/dist/runtime/plugins/ssr.js +53 -0
- package/package.json +17 -18
package/README.md
CHANGED
|
@@ -2,40 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
> Factory-driven state management for Nuxt powered by [Harlem](https://harlemjs.com/)
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
5
8
|
Define your data **shape** once with Zod — get typed **models**, computed **views**, and async **actions** with a single `createStore` call.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
- **Reactive state** — Single items and collections with built-in mutations
|
|
9
|
-
- **Computed views** — Derived read-only state that updates when models change
|
|
10
|
-
- **API integration** — Declarative HTTP actions that fetch and commit data in one step
|
|
11
|
-
- **Status tracking** — Every action exposes loading, error, and status reactively
|
|
12
|
-
- **Concurrency control** — Block, skip, cancel, or allow parallel calls per action
|
|
13
|
-
- **Vue composables** — Reactive helpers for actions, models, and views in components
|
|
14
|
-
- **SSR ready** — Server-side rendering with automatic state hydration
|
|
10
|
+
Built on top of [Harlem](https://harlemjs.com/), a powerful and extensible state management library for Vue 3.
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
---
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
## The Problem
|
|
15
|
+
|
|
16
|
+
Every Nuxt app has the same boilerplate for every API resource:
|
|
21
17
|
|
|
22
18
|
```typescript
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
// Without Harlemify — this gets written for EVERY resource
|
|
20
|
+
|
|
21
|
+
// 1. Define types manually
|
|
22
|
+
interface User {
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
email: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Define state
|
|
29
|
+
const users = ref<User[]>([]);
|
|
30
|
+
const currentUser = ref<User | null>(null);
|
|
31
|
+
const loading = ref(false);
|
|
32
|
+
const error = ref<Error | null>(null);
|
|
33
|
+
|
|
34
|
+
// 3. Write fetch logic
|
|
35
|
+
async function fetchUsers() {
|
|
36
|
+
loading.value = true;
|
|
37
|
+
error.value = null;
|
|
38
|
+
try {
|
|
39
|
+
users.value = await $fetch("/api/users");
|
|
40
|
+
} catch (e) {
|
|
41
|
+
error.value = e as Error;
|
|
42
|
+
} finally {
|
|
43
|
+
loading.value = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 4. Repeat for create, update, delete...
|
|
48
|
+
// 5. Repeat for every resource in your app...
|
|
27
49
|
```
|
|
28
50
|
|
|
29
|
-
##
|
|
51
|
+
## The Solution
|
|
52
|
+
|
|
53
|
+
With Harlemify, define a data shape once and get everything else for free:
|
|
30
54
|
|
|
31
55
|
```typescript
|
|
32
|
-
const userShape = shape((factory) =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
const userShape = shape((factory) => {
|
|
57
|
+
return {
|
|
58
|
+
id: factory.number().meta({
|
|
59
|
+
identifier: true,
|
|
60
|
+
}),
|
|
61
|
+
name: factory.string(),
|
|
62
|
+
email: factory.email(),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
39
65
|
|
|
40
66
|
export const userStore = createStore({
|
|
41
67
|
name: "users",
|
|
@@ -57,35 +83,73 @@ export const userStore = createStore({
|
|
|
57
83
|
{
|
|
58
84
|
url: "/users",
|
|
59
85
|
},
|
|
86
|
+
{ model: "list", mode: ModelManyMode.SET },
|
|
87
|
+
),
|
|
88
|
+
get: api.get(
|
|
60
89
|
{
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
url(view) {
|
|
91
|
+
return `/users/${view.user.value?.id}`;
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{ model: "current", mode: ModelOneMode.SET },
|
|
95
|
+
),
|
|
96
|
+
create: api.post(
|
|
97
|
+
{
|
|
98
|
+
url: "/users",
|
|
63
99
|
},
|
|
100
|
+
{ model: "list", mode: ModelManyMode.ADD },
|
|
101
|
+
),
|
|
102
|
+
delete: api.delete(
|
|
103
|
+
{
|
|
104
|
+
url(view) {
|
|
105
|
+
return `/users/${view.user.value?.id}`;
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{ model: "list", mode: ModelManyMode.REMOVE },
|
|
64
109
|
),
|
|
65
110
|
};
|
|
66
111
|
},
|
|
67
112
|
});
|
|
68
113
|
```
|
|
69
114
|
|
|
115
|
+
Use it in any component with built-in composables:
|
|
116
|
+
|
|
70
117
|
```vue
|
|
71
118
|
<script setup>
|
|
72
|
-
const { execute, loading } = useStoreAction(userStore, "list");
|
|
73
|
-
const { data } = useStoreView(userStore, "users");
|
|
119
|
+
const { execute, loading, error } = useStoreAction(userStore, "list");
|
|
120
|
+
const { data: users } = useStoreView(userStore, "users");
|
|
74
121
|
|
|
75
122
|
await execute();
|
|
76
123
|
</script>
|
|
77
124
|
|
|
78
125
|
<template>
|
|
79
|
-
<
|
|
80
|
-
|
|
126
|
+
<p v-if="error">{{ error.message }}</p>
|
|
127
|
+
<ul v-else-if="!loading">
|
|
128
|
+
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
|
|
81
129
|
</ul>
|
|
82
130
|
</template>
|
|
83
131
|
```
|
|
84
132
|
|
|
133
|
+
## Compatibility
|
|
134
|
+
|
|
135
|
+
| Dependency | Version |
|
|
136
|
+
| ---------- | --------------------- |
|
|
137
|
+
| Nuxt | `^3.14.0` or `^4.0.0` |
|
|
138
|
+
| Vue | `^3.5.0` |
|
|
139
|
+
| Zod | `^4.0.0` |
|
|
140
|
+
|
|
141
|
+
> **Note:** Early Nuxt 4 versions (e.g., 4.1.x) may have issues resolving the `#build` alias for module templates. If you encounter build errors related to `#build/harlemify.config`, upgrade to the latest Nuxt 4 release.
|
|
142
|
+
|
|
85
143
|
## Documentation
|
|
86
144
|
|
|
145
|
+
Full docs with guides, API reference, and examples:
|
|
146
|
+
|
|
87
147
|
[https://diphyx.github.io/harlemify/](https://diphyx.github.io/harlemify/)
|
|
88
148
|
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
|
|
152
|
+
|
|
89
153
|
## License
|
|
90
154
|
|
|
91
|
-
MIT
|
|
155
|
+
[MIT](LICENSE)
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addTemplate, addPlugin, addImportsDir, addImports } from '@nuxt/kit';
|
|
1
|
+
import { useLogger, defineNuxtModule, createResolver, addTemplate, addPlugin, addImportsDir, addImports } from '@nuxt/kit';
|
|
2
2
|
|
|
3
|
+
const logger = useLogger("harlemify");
|
|
3
4
|
const module = defineNuxtModule({
|
|
4
5
|
meta: {
|
|
5
6
|
name: "harlemify",
|
|
6
7
|
configKey: "harlemify",
|
|
7
8
|
compatibility: {
|
|
8
|
-
nuxt: "
|
|
9
|
+
nuxt: "^3.14.0 || ^4.0.0"
|
|
9
10
|
}
|
|
10
11
|
},
|
|
11
12
|
defaults: {},
|
|
12
13
|
setup(options, nuxt) {
|
|
13
14
|
const { resolve } = createResolver(import.meta.url);
|
|
14
|
-
addTemplate({
|
|
15
|
+
const template = addTemplate({
|
|
15
16
|
write: true,
|
|
16
17
|
filename: "harlemify.config.mjs",
|
|
17
18
|
getContents() {
|
|
@@ -27,6 +28,7 @@ const module = defineNuxtModule({
|
|
|
27
28
|
}
|
|
28
29
|
]);
|
|
29
30
|
nuxt.options.build.transpile.push(/@harlem\//);
|
|
31
|
+
logger.success(`Module registered, config template: ${template.dst}`);
|
|
30
32
|
}
|
|
31
33
|
});
|
|
32
34
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type ComputedRef, type Ref } from "vue";
|
|
2
|
-
import { ActionStatus, type ActionCall, type ActionCallOptions } from "../core/types/action.js";
|
|
2
|
+
import { ActionStatus, type ActionCall, type ActionApiCall, type ActionHandlerCall, type ActionCallOptions, type ActionApiCallOptions, type ActionHandlerCallOptions } from "../core/types/action.js";
|
|
3
3
|
export interface UseStoreActionOptions {
|
|
4
4
|
isolated?: boolean;
|
|
5
5
|
}
|
|
6
|
-
export type UseStoreAction<T> = {
|
|
7
|
-
execute: (options?:
|
|
6
|
+
export type UseStoreAction<T, O = ActionCallOptions> = {
|
|
7
|
+
execute: (options?: Omit<O, "bind">) => Promise<T>;
|
|
8
8
|
error: Readonly<Ref<Error | null>>;
|
|
9
9
|
status: Readonly<Ref<ActionStatus>>;
|
|
10
10
|
loading: ComputedRef<boolean>;
|
|
@@ -14,4 +14,4 @@ export declare function useIsolatedActionError(): Ref<Error | null>;
|
|
|
14
14
|
export declare function useIsolatedActionStatus(): Ref<ActionStatus>;
|
|
15
15
|
export declare function useStoreAction<A extends Record<string, ActionCall<any>>, K extends keyof A & string, T = Awaited<ReturnType<A[K]>>>(store: {
|
|
16
16
|
action: A;
|
|
17
|
-
}, key: K, options?: UseStoreActionOptions): UseStoreAction<T>;
|
|
17
|
+
}, key: K, options?: UseStoreActionOptions): UseStoreAction<T, A[K] extends ActionHandlerCall<infer P, any> ? ActionHandlerCallOptions<P> : A[K] extends ActionApiCall<any> ? ActionApiCallOptions : ActionCallOptions>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Ref } from "vue";
|
|
2
|
+
import type { ComposeCallback, ComposeDefinitions, StoreCompose } from "../core/types/compose.js";
|
|
3
|
+
export type UseStoreCompose<A extends any[] = any[]> = {
|
|
4
|
+
execute: (...args: A) => Promise<void>;
|
|
5
|
+
active: Readonly<Ref<boolean>>;
|
|
6
|
+
};
|
|
7
|
+
export declare function useStoreCompose<CD extends ComposeDefinitions, K extends keyof StoreCompose<CD> & string, A extends any[] = CD[K] extends ComposeCallback<infer P> ? P : any[]>(store: {
|
|
8
|
+
compose: StoreCompose<CD>;
|
|
9
|
+
}, key: K): UseStoreCompose<A>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelCall, ModelOneCall, ModelManyCall, ModelOneCommit, ModelManyCommit, StoreModel, ModelDefinitions } from "../core/types/model.js";
|
|
1
|
+
import type { ModelCall, ModelOneCall, ModelManyCall, ModelOneCommit, ModelManyListCommit, ModelManyRecordCommit, ModelManyCommit, StoreModel, ModelDefinitions } from "../core/types/model.js";
|
|
2
2
|
export interface UseStoreModelOptions {
|
|
3
3
|
debounce?: number;
|
|
4
4
|
throttle?: number;
|
|
@@ -8,14 +8,14 @@ type UseStoreModelOne<C extends ModelOneCommit<any>> = {
|
|
|
8
8
|
reset: C["reset"];
|
|
9
9
|
patch: C["patch"];
|
|
10
10
|
};
|
|
11
|
-
type UseStoreModelMany<C extends
|
|
11
|
+
type UseStoreModelMany<C extends ModelManyListCommit<any, any> | ModelManyRecordCommit<any>> = {
|
|
12
12
|
set: C["set"];
|
|
13
13
|
reset: C["reset"];
|
|
14
14
|
patch: C["patch"];
|
|
15
15
|
add: C["add"];
|
|
16
16
|
remove: C["remove"];
|
|
17
17
|
};
|
|
18
|
-
export type UseStoreModel<M extends ModelCall<any> = ModelCall<any>> = M extends ModelManyCall<infer S> ? UseStoreModelMany<ModelManyCommit<S>> : M extends ModelOneCall<infer S> ? UseStoreModelOne<ModelOneCommit<S>> : never;
|
|
18
|
+
export type UseStoreModel<M extends ModelCall<any> = ModelCall<any>> = M extends ModelManyCall<infer S, infer I, infer T> ? UseStoreModelMany<ModelManyCommit<S, I, T>> : M extends ModelOneCall<infer S> ? UseStoreModelOne<ModelOneCommit<S>> : never;
|
|
19
19
|
export declare function useStoreModel<M extends StoreModel<ModelDefinitions>, K extends keyof M & string>(store: {
|
|
20
20
|
model: M;
|
|
21
21
|
}, key: K, options?: UseStoreModelOptions): UseStoreModel<M[K]>;
|
|
@@ -6,9 +6,8 @@ export interface UseStoreViewTrackOptions {
|
|
|
6
6
|
debounce?: number;
|
|
7
7
|
throttle?: number;
|
|
8
8
|
}
|
|
9
|
-
export interface UseStoreViewOptions
|
|
9
|
+
export interface UseStoreViewOptions {
|
|
10
10
|
proxy?: boolean;
|
|
11
|
-
default?: T;
|
|
12
11
|
}
|
|
13
12
|
export type UseStoreViewData<T> = {
|
|
14
13
|
value: T;
|
|
@@ -25,9 +24,9 @@ export type UseStoreViewComputed<T> = {
|
|
|
25
24
|
};
|
|
26
25
|
export declare function useStoreView<V extends Record<string, ViewCall>, K extends keyof V & string, T = V[K] extends ComputedRef<infer R> ? R : unknown>(store: {
|
|
27
26
|
view: V;
|
|
28
|
-
}, key: K, options: UseStoreViewOptions
|
|
27
|
+
}, key: K, options: UseStoreViewOptions & {
|
|
29
28
|
proxy: false;
|
|
30
29
|
}): UseStoreViewComputed<T>;
|
|
31
30
|
export declare function useStoreView<V extends Record<string, ViewCall>, K extends keyof V & string, T = V[K] extends ComputedRef<infer R> ? R : unknown>(store: {
|
|
32
31
|
view: V;
|
|
33
|
-
}, key: K, options?: UseStoreViewOptions
|
|
32
|
+
}, key: K, options?: UseStoreViewOptions): UseStoreViewProxy<T>;
|
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { watch } from "vue";
|
|
2
2
|
import { debounce, throttle, toReactiveProxy } from "../core/utils/base.js";
|
|
3
|
-
function resolveDefault(view, defaultValue) {
|
|
4
|
-
if (defaultValue === void 0) {
|
|
5
|
-
return view;
|
|
6
|
-
}
|
|
7
|
-
return computed(() => {
|
|
8
|
-
const value = view.value;
|
|
9
|
-
if (value == null) {
|
|
10
|
-
return defaultValue;
|
|
11
|
-
}
|
|
12
|
-
return value;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
3
|
function resolveData(source, proxy) {
|
|
16
4
|
if (proxy !== false) {
|
|
17
5
|
return toReactiveProxy(source);
|
|
@@ -22,7 +10,7 @@ export function useStoreView(store, key, options) {
|
|
|
22
10
|
if (!store.view[key]) {
|
|
23
11
|
throw new Error(`View "${key}" not found in store`);
|
|
24
12
|
}
|
|
25
|
-
const source =
|
|
13
|
+
const source = store.view[key];
|
|
26
14
|
const data = resolveData(source, options?.proxy);
|
|
27
15
|
function resolveCallback(callback, callbackOptions) {
|
|
28
16
|
if (callbackOptions?.debounce) {
|
|
@@ -1,26 +1,55 @@
|
|
|
1
1
|
import { wrapBaseDefinition } from "../utils/base.js";
|
|
2
|
+
import { resolveShapeIdentifier } from "../utils/shape.js";
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
+
ModelType,
|
|
5
|
+
ModelManyKind
|
|
4
6
|
} from "../types/model.js";
|
|
5
7
|
export function createModelFactory(config, logger) {
|
|
6
8
|
function one(shape, options) {
|
|
9
|
+
function defaultResolver() {
|
|
10
|
+
if (options?.default) {
|
|
11
|
+
return options.default();
|
|
12
|
+
}
|
|
13
|
+
if ("defaults" in shape && typeof shape.defaults === "function") {
|
|
14
|
+
return shape.defaults();
|
|
15
|
+
}
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
7
18
|
return wrapBaseDefinition({
|
|
8
19
|
shape,
|
|
9
|
-
|
|
20
|
+
type: ModelType.ONE,
|
|
21
|
+
default: defaultResolver,
|
|
10
22
|
options: {
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
pre: options?.pre,
|
|
24
|
+
post: options?.post
|
|
13
25
|
},
|
|
14
26
|
logger
|
|
15
27
|
});
|
|
16
28
|
}
|
|
17
29
|
function many(shape, options) {
|
|
30
|
+
const kind = options?.kind ?? ModelManyKind.LIST;
|
|
31
|
+
let identifier;
|
|
32
|
+
if (kind === ModelManyKind.LIST) {
|
|
33
|
+
identifier = resolveShapeIdentifier(shape, options?.identifier, config?.identifier);
|
|
34
|
+
}
|
|
35
|
+
function defaultResolver() {
|
|
36
|
+
if (options?.default) {
|
|
37
|
+
return options.default();
|
|
38
|
+
}
|
|
39
|
+
if (kind === ModelManyKind.LIST) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
18
44
|
return wrapBaseDefinition({
|
|
19
45
|
shape,
|
|
20
|
-
|
|
46
|
+
type: ModelType.MANY,
|
|
47
|
+
kind,
|
|
48
|
+
identifier,
|
|
49
|
+
default: defaultResolver,
|
|
21
50
|
options: {
|
|
22
|
-
|
|
23
|
-
|
|
51
|
+
pre: options?.pre,
|
|
52
|
+
post: options?.post
|
|
24
53
|
},
|
|
25
54
|
logger
|
|
26
55
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModelDefinitions } from "./types/model.js";
|
|
2
2
|
import type { ViewDefinitions } from "./types/view.js";
|
|
3
3
|
import type { ActionDefinitions } from "./types/action.js";
|
|
4
|
+
import type { ComposeDefinitions } from "./types/compose.js";
|
|
4
5
|
import type { Store, StoreConfig } from "./types/store.js";
|
|
5
|
-
export declare function createStore<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD
|
|
6
|
+
export declare function createStore<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>, CD extends ComposeDefinitions = ComposeDefinitions>(config: StoreConfig<MD, VD, AD, CD>): Store<MD, VD, AD, CD>;
|
|
@@ -5,29 +5,46 @@ import { createModelFactory } from "./layers/model.js";
|
|
|
5
5
|
import { createViewFactory } from "./layers/view.js";
|
|
6
6
|
import { createActionFactory } from "./layers/action.js";
|
|
7
7
|
import { createStoreState, createStoreModel, createStoreView, createStoreAction } from "./utils/store.js";
|
|
8
|
+
import { createStoreCompose } from "./utils/compose.js";
|
|
8
9
|
export function createStore(config) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
10
|
+
function init() {
|
|
11
|
+
const logger = createConsola({
|
|
12
|
+
level: runtimeConfig.logger,
|
|
13
|
+
defaults: {
|
|
14
|
+
tag: `harlemify:${config.name}`
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
logger.info("Creating store");
|
|
18
|
+
const modelFactory = createModelFactory(runtimeConfig.model, logger);
|
|
19
|
+
const viewFactory = createViewFactory(runtimeConfig.view, logger);
|
|
20
|
+
const actionFactory = createActionFactory(runtimeConfig.action, logger);
|
|
21
|
+
const modelDefinitions = config.model(modelFactory);
|
|
22
|
+
const viewDefinitions = config.view(viewFactory);
|
|
23
|
+
const actionDefinitions = config.action(actionFactory);
|
|
24
|
+
const state = createStoreState(modelDefinitions);
|
|
25
|
+
const source = createStoreSource(config.name, state);
|
|
26
|
+
const model = createStoreModel(modelDefinitions, source);
|
|
27
|
+
const view = createStoreView(viewDefinitions, source);
|
|
28
|
+
const action = createStoreAction(actionDefinitions, model, view);
|
|
29
|
+
const compose = createStoreCompose(config.compose, { model, view, action }, logger);
|
|
30
|
+
logger.info("Store created");
|
|
31
|
+
return {
|
|
32
|
+
model,
|
|
33
|
+
view,
|
|
34
|
+
action,
|
|
35
|
+
compose
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (config.lazy) {
|
|
39
|
+
let instance;
|
|
40
|
+
return new Proxy({}, {
|
|
41
|
+
get(_, prop) {
|
|
42
|
+
if (!instance) {
|
|
43
|
+
instance = init();
|
|
44
|
+
}
|
|
45
|
+
return instance[prop];
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return init();
|
|
33
50
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComputedRef, DeepReadonly, MaybeRefOrGetter, Ref } from "vue";
|
|
2
2
|
import type { BaseDefinition } from "./base.js";
|
|
3
|
-
import type { ModelDefinitions, ModelOneCommitOptions, ModelManyCommitOptions, StoreModel } from "./model.js";
|
|
3
|
+
import type { ModelDefinitions, ModelDefinitionInfer, ModelOneCommitOptions, ModelManyCommitOptions, StoreModel } from "./model.js";
|
|
4
4
|
import { ModelOneMode, ModelManyMode } from "./model.js";
|
|
5
5
|
import type { ViewDefinitions, StoreView } from "./view.js";
|
|
6
6
|
export interface RuntimeActionConfig {
|
|
@@ -22,6 +22,10 @@ export declare enum ActionConcurrent {
|
|
|
22
22
|
CANCEL = "cancel",
|
|
23
23
|
ALLOW = "allow"
|
|
24
24
|
}
|
|
25
|
+
export declare enum ActionType {
|
|
26
|
+
API = "api",
|
|
27
|
+
HANDLER = "handler"
|
|
28
|
+
}
|
|
25
29
|
export declare enum ActionApiMethod {
|
|
26
30
|
GET = "GET",
|
|
27
31
|
HEAD = "HEAD",
|
|
@@ -42,42 +46,49 @@ export interface ActionApiRequest<MD extends ModelDefinitions, VD extends ViewDe
|
|
|
42
46
|
concurrent?: ActionConcurrent;
|
|
43
47
|
}
|
|
44
48
|
export type ActionApiRequestShortcut<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = Omit<ActionApiRequest<MD, VD>, "method">;
|
|
45
|
-
export interface ActionApiCommit<MD extends ModelDefinitions> {
|
|
46
|
-
model:
|
|
49
|
+
export interface ActionApiCommit<MD extends ModelDefinitions, K extends keyof MD = keyof MD> {
|
|
50
|
+
model: K;
|
|
47
51
|
mode: ModelOneMode | ModelManyMode;
|
|
48
52
|
value?: (data: unknown) => unknown;
|
|
49
53
|
options?: ModelOneCommitOptions | ModelManyCommitOptions;
|
|
50
54
|
}
|
|
51
|
-
export interface ActionApiDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD
|
|
55
|
+
export interface ActionApiDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, K extends keyof MD = keyof MD> extends BaseDefinition {
|
|
52
56
|
request: ActionApiRequest<MD, VD>;
|
|
53
|
-
commit?: ActionApiCommit<MD>;
|
|
57
|
+
commit?: ActionApiCommit<MD, K>;
|
|
54
58
|
}
|
|
55
|
-
export interface ActionHandlerOptions {
|
|
56
|
-
payload?:
|
|
59
|
+
export interface ActionHandlerOptions<P = unknown> {
|
|
60
|
+
payload?: P;
|
|
57
61
|
concurrent?: ActionConcurrent;
|
|
58
62
|
}
|
|
59
|
-
export type ActionHandlerCallback<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, R = void> = (context: {
|
|
63
|
+
export type ActionHandlerCallback<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, P = unknown, R = void> = (context: {
|
|
60
64
|
model: StoreModel<MD>;
|
|
61
65
|
view: StoreView<MD, VD>;
|
|
62
|
-
payload:
|
|
63
|
-
}) => Promise<R
|
|
64
|
-
export interface ActionHandlerDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, R = void> extends BaseDefinition {
|
|
65
|
-
callback: ActionHandlerCallback<MD, VD, R>;
|
|
66
|
-
options?: ActionHandlerOptions
|
|
66
|
+
payload: P;
|
|
67
|
+
}) => Promise<R> | R;
|
|
68
|
+
export interface ActionHandlerDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, P = unknown, R = void> extends BaseDefinition {
|
|
69
|
+
callback: ActionHandlerCallback<MD, VD, P, R>;
|
|
70
|
+
options?: ActionHandlerOptions<P>;
|
|
67
71
|
}
|
|
68
|
-
export type ActionDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = ActionApiDefinition<MD, VD> | ActionHandlerDefinition<MD, VD,
|
|
72
|
+
export type ActionDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = ActionApiDefinition<MD, VD> | ActionHandlerDefinition<MD, VD, any, any>;
|
|
69
73
|
export type ActionDefinitions<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = Record<string, ActionDefinition<MD, VD>>;
|
|
70
74
|
export interface ActionApiFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
71
|
-
(request: ActionApiRequest<MD, VD>, commit
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
<K extends keyof MD>(request: ActionApiRequest<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
76
|
+
(request: ActionApiRequest<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
77
|
+
get<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
78
|
+
get(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
79
|
+
head<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
80
|
+
head(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
81
|
+
post<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
82
|
+
post(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
83
|
+
put<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
84
|
+
put(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
85
|
+
patch<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
86
|
+
patch(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
87
|
+
delete<K extends keyof MD>(request: ActionApiRequestShortcut<MD, VD>, commit: ActionApiCommit<MD, K>): ActionApiDefinition<MD, VD, K>;
|
|
88
|
+
delete(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD>;
|
|
78
89
|
}
|
|
79
90
|
export interface ActionHandlerFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
80
|
-
<R>(callback: ActionHandlerCallback<MD, VD, R>, options?: ActionHandlerOptions): ActionHandlerDefinition<MD, VD, R>;
|
|
91
|
+
<P = unknown, R = void>(callback: ActionHandlerCallback<MD, VD, P, R>, options?: ActionHandlerOptions<P>): ActionHandlerDefinition<MD, VD, P, R>;
|
|
81
92
|
}
|
|
82
93
|
export interface ActionFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
83
94
|
api: ActionApiFactory<MD, VD>;
|
|
@@ -117,13 +128,13 @@ export interface ActionApiCallOptions extends ActionCallBaseOptions {
|
|
|
117
128
|
transformer?: ActionCallTransformerOptions;
|
|
118
129
|
commit?: ActionCallCommitOptions;
|
|
119
130
|
}
|
|
120
|
-
export interface ActionResolvedHandler<MD extends ModelDefinitions, VD extends ViewDefinitions<MD
|
|
131
|
+
export interface ActionResolvedHandler<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, P = unknown> {
|
|
121
132
|
model: StoreModel<MD>;
|
|
122
133
|
view: StoreView<MD, VD>;
|
|
123
|
-
payload:
|
|
134
|
+
payload: P;
|
|
124
135
|
}
|
|
125
|
-
export interface ActionHandlerCallOptions extends ActionCallBaseOptions {
|
|
126
|
-
payload?:
|
|
136
|
+
export interface ActionHandlerCallOptions<P = unknown> extends ActionCallBaseOptions {
|
|
137
|
+
payload?: P;
|
|
127
138
|
}
|
|
128
139
|
export type ActionCallOptions = ActionApiCallOptions | ActionHandlerCallOptions;
|
|
129
140
|
export interface ActionCallBase {
|
|
@@ -133,12 +144,14 @@ export interface ActionCallBase {
|
|
|
133
144
|
reset: () => void;
|
|
134
145
|
}
|
|
135
146
|
export interface ActionApiCall<T = void> extends ActionCallBase {
|
|
147
|
+
readonly actionType: ActionType.API;
|
|
136
148
|
(options?: ActionApiCallOptions): Promise<T>;
|
|
137
149
|
}
|
|
138
|
-
export interface ActionHandlerCall<T = void> extends ActionCallBase {
|
|
139
|
-
|
|
150
|
+
export interface ActionHandlerCall<P = unknown, T = void> extends ActionCallBase {
|
|
151
|
+
readonly actionType: ActionType.HANDLER;
|
|
152
|
+
(options?: ActionHandlerCallOptions<P>): Promise<T>;
|
|
140
153
|
}
|
|
141
|
-
export type ActionCall<T = void> = ActionApiCall<T> | ActionHandlerCall<T>;
|
|
154
|
+
export type ActionCall<T = void> = ActionApiCall<T> | ActionHandlerCall<any, T>;
|
|
142
155
|
export type StoreAction<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>> = {
|
|
143
|
-
[K in keyof AD]: AD[K] extends ActionApiDefinition<MD, VD> ? ActionApiCall : AD[K] extends ActionHandlerDefinition<MD, VD, infer R> ? ActionHandlerCall<R> : never;
|
|
156
|
+
[K in keyof AD]: AD[K] extends ActionApiDefinition<MD, VD, infer MK> ? MK extends keyof MD ? ActionApiCall<ModelDefinitionInfer<MD, MK>> : ActionApiCall : AD[K] extends ActionHandlerDefinition<MD, VD, infer P, infer R> ? ActionHandlerCall<P, R> : never;
|
|
144
157
|
};
|
|
@@ -12,6 +12,11 @@ export var ActionConcurrent = /* @__PURE__ */ ((ActionConcurrent2) => {
|
|
|
12
12
|
ActionConcurrent2["ALLOW"] = "allow";
|
|
13
13
|
return ActionConcurrent2;
|
|
14
14
|
})(ActionConcurrent || {});
|
|
15
|
+
export var ActionType = /* @__PURE__ */ ((ActionType2) => {
|
|
16
|
+
ActionType2["API"] = "api";
|
|
17
|
+
ActionType2["HANDLER"] = "handler";
|
|
18
|
+
return ActionType2;
|
|
19
|
+
})(ActionType || {});
|
|
15
20
|
export var ActionApiMethod = /* @__PURE__ */ ((ActionApiMethod2) => {
|
|
16
21
|
ActionApiMethod2["GET"] = "GET";
|
|
17
22
|
ActionApiMethod2["HEAD"] = "HEAD";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Ref } from "vue";
|
|
2
|
+
import type { ModelDefinitions, StoreModel } from "./model.js";
|
|
3
|
+
import type { ViewDefinitions, StoreView } from "./view.js";
|
|
4
|
+
import type { ActionDefinitions, StoreAction } from "./action.js";
|
|
5
|
+
export type ComposeCallback<A extends any[] = any[]> = (...args: A) => Promise<void> | void;
|
|
6
|
+
export type ComposeDefinitions = Record<string, ComposeCallback<any[]>>;
|
|
7
|
+
export interface ComposeContext<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>> {
|
|
8
|
+
model: StoreModel<MD>;
|
|
9
|
+
view: StoreView<MD, VD>;
|
|
10
|
+
action: StoreAction<MD, VD, AD>;
|
|
11
|
+
}
|
|
12
|
+
export type ComposeCall<A extends any[] = any[]> = {
|
|
13
|
+
(...args: A): Promise<void>;
|
|
14
|
+
active: Readonly<Ref<boolean>>;
|
|
15
|
+
};
|
|
16
|
+
export type StoreCompose<CD extends ComposeDefinitions> = {
|
|
17
|
+
[K in keyof CD]: CD[K] extends ComposeCallback<infer A> ? ComposeCall<A> : never;
|
|
18
|
+
};
|
|
File without changes
|