@diphyx/harlemify 5.4.0 → 5.4.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/README.md +31 -85
- package/dist/module.json +1 -1
- package/dist/runtime/composables/view.d.ts +2 -19
- package/dist/runtime/composables/view.js +3 -9
- package/dist/runtime/core/utils/base.d.ts +0 -7
- package/dist/runtime/core/utils/base.js +0 -45
- package/dist/runtime/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,66 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
> Factory-driven state management for Nuxt powered by [Harlem](https://harlemjs.com/)
|
|
4
4
|
|
|
5
|
-

|
|
6
|
-

|
|
7
|
-
|
|
8
5
|
Define your data **shape** once with Zod — get typed **models**, computed **views**, and async **actions** with a single `createStore` call.
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
- **Schema-first** — Define your data shape once, get TypeScript types and validation automatically
|
|
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
|
|
13
15
|
|
|
14
|
-
##
|
|
16
|
+
## Install
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
```bash
|
|
19
|
+
npm install @diphyx/harlemify
|
|
20
|
+
```
|
|
17
21
|
|
|
18
22
|
```typescript
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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...
|
|
23
|
+
// nuxt.config.ts
|
|
24
|
+
export default defineNuxtConfig({
|
|
25
|
+
modules: ["@diphyx/harlemify"],
|
|
26
|
+
});
|
|
49
27
|
```
|
|
50
28
|
|
|
51
|
-
##
|
|
52
|
-
|
|
53
|
-
With Harlemify, define a data shape once and get everything else for free:
|
|
29
|
+
## Usage
|
|
54
30
|
|
|
55
31
|
```typescript
|
|
56
|
-
const userShape = shape((factory) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
});
|
|
32
|
+
const userShape = shape((factory) => ({
|
|
33
|
+
id: factory.number().meta({
|
|
34
|
+
identifier: true,
|
|
35
|
+
}),
|
|
36
|
+
name: factory.string(),
|
|
37
|
+
email: factory.email(),
|
|
38
|
+
}));
|
|
65
39
|
|
|
66
40
|
export const userStore = createStore({
|
|
67
41
|
name: "users",
|
|
@@ -83,49 +57,27 @@ export const userStore = createStore({
|
|
|
83
57
|
{
|
|
84
58
|
url: "/users",
|
|
85
59
|
},
|
|
86
|
-
{ model: "list", mode: ModelManyMode.SET },
|
|
87
|
-
),
|
|
88
|
-
get: api.get(
|
|
89
|
-
{
|
|
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",
|
|
99
|
-
},
|
|
100
|
-
{ model: "list", mode: ModelManyMode.ADD },
|
|
101
|
-
),
|
|
102
|
-
delete: api.delete(
|
|
103
60
|
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
61
|
+
model: "list",
|
|
62
|
+
mode: ModelManyMode.SET,
|
|
107
63
|
},
|
|
108
|
-
{ model: "list", mode: ModelManyMode.REMOVE },
|
|
109
64
|
),
|
|
110
65
|
};
|
|
111
66
|
},
|
|
112
67
|
});
|
|
113
68
|
```
|
|
114
69
|
|
|
115
|
-
Use it in any component with built-in composables:
|
|
116
|
-
|
|
117
70
|
```vue
|
|
118
71
|
<script setup>
|
|
119
|
-
const { execute, loading
|
|
120
|
-
const { data
|
|
72
|
+
const { execute, loading } = useStoreAction(userStore, "list");
|
|
73
|
+
const { data } = useStoreView(userStore, "users");
|
|
121
74
|
|
|
122
75
|
await execute();
|
|
123
76
|
</script>
|
|
124
77
|
|
|
125
78
|
<template>
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
|
|
79
|
+
<ul v-if="!loading">
|
|
80
|
+
<li v-for="user in data" :key="user.id">{{ user.name }}</li>
|
|
129
81
|
</ul>
|
|
130
82
|
</template>
|
|
131
83
|
```
|
|
@@ -142,14 +94,8 @@ await execute();
|
|
|
142
94
|
|
|
143
95
|
## Documentation
|
|
144
96
|
|
|
145
|
-
Full docs with guides, API reference, and examples:
|
|
146
|
-
|
|
147
97
|
[https://diphyx.github.io/harlemify/](https://diphyx.github.io/harlemify/)
|
|
148
98
|
|
|
149
|
-
## Contributing
|
|
150
|
-
|
|
151
|
-
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
|
|
152
|
-
|
|
153
99
|
## License
|
|
154
100
|
|
|
155
|
-
|
|
101
|
+
MIT
|
package/dist/module.json
CHANGED
|
@@ -6,27 +6,10 @@ export interface UseStoreViewTrackOptions {
|
|
|
6
6
|
debounce?: number;
|
|
7
7
|
throttle?: number;
|
|
8
8
|
}
|
|
9
|
-
export
|
|
10
|
-
proxy?: boolean;
|
|
11
|
-
}
|
|
12
|
-
export type UseStoreViewData<T> = {
|
|
13
|
-
value: T;
|
|
14
|
-
} & (T extends Record<string, unknown> ? {
|
|
15
|
-
[K in keyof T]: T[K];
|
|
16
|
-
} : Record<string, unknown>);
|
|
17
|
-
export type UseStoreViewProxy<T> = {
|
|
18
|
-
data: UseStoreViewData<T>;
|
|
19
|
-
track: (handler: (value: T) => void, options?: UseStoreViewTrackOptions) => WatchStopHandle;
|
|
20
|
-
};
|
|
21
|
-
export type UseStoreViewComputed<T> = {
|
|
9
|
+
export type UseStoreView<T> = {
|
|
22
10
|
data: ComputedRef<T>;
|
|
23
11
|
track: (handler: (value: T) => void, options?: UseStoreViewTrackOptions) => WatchStopHandle;
|
|
24
12
|
};
|
|
25
13
|
export declare function useStoreView<V extends Record<string, ViewCall>, K extends keyof V & string, T = V[K] extends ComputedRef<infer R> ? R : unknown>(store: {
|
|
26
14
|
view: V;
|
|
27
|
-
}, key: K
|
|
28
|
-
proxy: false;
|
|
29
|
-
}): UseStoreViewComputed<T>;
|
|
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: {
|
|
31
|
-
view: V;
|
|
32
|
-
}, key: K, options?: UseStoreViewOptions): UseStoreViewProxy<T>;
|
|
15
|
+
}, key: K): UseStoreView<T>;
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import { watch } from "vue";
|
|
2
|
-
import { debounce, throttle
|
|
3
|
-
function
|
|
4
|
-
if (proxy !== false) {
|
|
5
|
-
return toReactiveProxy(source);
|
|
6
|
-
}
|
|
7
|
-
return source;
|
|
8
|
-
}
|
|
9
|
-
export function useStoreView(store, key, options) {
|
|
2
|
+
import { debounce, throttle } from "../core/utils/base.js";
|
|
3
|
+
export function useStoreView(store, key) {
|
|
10
4
|
if (!store.view[key]) {
|
|
11
5
|
throw new Error(`View "${key}" not found in store`);
|
|
12
6
|
}
|
|
13
7
|
const source = store.view[key];
|
|
14
|
-
const data =
|
|
8
|
+
const data = source;
|
|
15
9
|
function resolveCallback(callback, callbackOptions) {
|
|
16
10
|
if (callbackOptions?.debounce) {
|
|
17
11
|
return debounce(callback, callbackOptions.debounce);
|
|
@@ -6,12 +6,5 @@ export declare function ensureArray<T>(value: T | T[]): T[];
|
|
|
6
6
|
export declare function isObject(value: unknown): value is object;
|
|
7
7
|
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
8
8
|
export declare function isEmptyRecord(record: Record<string, unknown> | undefined): record is undefined;
|
|
9
|
-
type ReferenceProxy<T> = {
|
|
10
|
-
value: T;
|
|
11
|
-
} & Record<string | symbol, unknown>;
|
|
12
|
-
export declare function toReactiveProxy<T>(reference: {
|
|
13
|
-
value: T;
|
|
14
|
-
}): ReferenceProxy<T>;
|
|
15
9
|
export declare function debounce<T extends (...args: any[]) => any>(callback: T, delay: number): T;
|
|
16
10
|
export declare function throttle<T extends (...args: any[]) => any>(callback: T, delay: number): T;
|
|
17
|
-
export {};
|
|
@@ -43,51 +43,6 @@ export function isEmptyRecord(record) {
|
|
|
43
43
|
}
|
|
44
44
|
return false;
|
|
45
45
|
}
|
|
46
|
-
export function toReactiveProxy(reference) {
|
|
47
|
-
function get(_target, prop) {
|
|
48
|
-
if (prop === "value") {
|
|
49
|
-
return reference.value;
|
|
50
|
-
}
|
|
51
|
-
if (!isObject(reference.value)) {
|
|
52
|
-
return void 0;
|
|
53
|
-
}
|
|
54
|
-
return reference.value[prop];
|
|
55
|
-
}
|
|
56
|
-
function has(_target, prop) {
|
|
57
|
-
if (prop === "value") {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
if (!isObject(reference.value)) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
return prop in reference.value;
|
|
64
|
-
}
|
|
65
|
-
function ownKeys() {
|
|
66
|
-
if (!isObject(reference.value)) {
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
return Reflect.ownKeys(reference.value);
|
|
70
|
-
}
|
|
71
|
-
function getOwnPropertyDescriptor(_target, prop) {
|
|
72
|
-
if (!isObject(reference.value) || !(prop in reference.value)) {
|
|
73
|
-
return void 0;
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
configurable: true,
|
|
77
|
-
enumerable: true,
|
|
78
|
-
value: reference.value[prop]
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return new Proxy(
|
|
82
|
-
{},
|
|
83
|
-
{
|
|
84
|
-
get,
|
|
85
|
-
has,
|
|
86
|
-
ownKeys,
|
|
87
|
-
getOwnPropertyDescriptor
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
46
|
export function debounce(callback, delay) {
|
|
92
47
|
let timer = null;
|
|
93
48
|
return (...args) => {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ export type { UseStoreActionOptions, UseStoreAction } from "./composables/action
|
|
|
17
17
|
export { useStoreModel } from "./composables/model.js";
|
|
18
18
|
export type { UseStoreModelOptions, UseStoreModel } from "./composables/model.js";
|
|
19
19
|
export { useStoreView } from "./composables/view.js";
|
|
20
|
-
export type {
|
|
20
|
+
export type { UseStoreView, UseStoreViewTrackOptions } from "./composables/view.js";
|
|
21
21
|
export type { RuntimeConfig } from "./config.js";
|