@diphyx/harlemify 6.1.1 → 6.3.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 +3 -1
- package/dist/module.json +1 -1
- package/dist/runtime/core/layers/action.d.ts +2 -2
- package/dist/runtime/core/layers/model.d.ts +2 -2
- package/dist/runtime/core/layers/view.d.ts +2 -2
- package/dist/runtime/core/store.js +2 -7
- package/dist/runtime/core/types/action.d.ts +25 -15
- package/dist/runtime/core/types/base.d.ts +7 -2
- package/dist/runtime/core/types/model.d.ts +11 -7
- package/dist/runtime/core/utils/action.js +14 -11
- package/dist/runtime/core/utils/base.d.ts +2 -0
- package/dist/runtime/core/utils/base.js +20 -8
- package/dist/runtime/core/utils/compose.d.ts +2 -2
- package/dist/runtime/core/utils/logger.d.ts +2 -0
- package/dist/runtime/core/utils/logger.js +22 -0
- package/dist/runtime/core/utils/model.js +64 -24
- package/dist/runtime/core/utils/shape.js +2 -3
- package/dist/runtime/core/utils/view.js +2 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +8 -1
- package/package.json +16 -12
package/README.md
CHANGED
|
@@ -23,9 +23,11 @@ If you are an AI coding agent working on a project that uses `@diphyx/harlemify`
|
|
|
23
23
|
## Install
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm install @diphyx/harlemify
|
|
26
|
+
npm install @diphyx/harlemify zod
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
> `zod` is a peer dependency — install it in your project.
|
|
30
|
+
|
|
29
31
|
```typescript
|
|
30
32
|
// nuxt.config.ts
|
|
31
33
|
export default defineNuxtConfig({
|
package/dist/module.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "../types/base.js";
|
|
2
2
|
import type { ModelDefinitions } from "../types/model.js";
|
|
3
3
|
import type { ViewDefinitions } from "../types/view.js";
|
|
4
4
|
import { type RuntimeActionConfig, type ActionFactory } from "../types/action.js";
|
|
5
|
-
export declare function createActionFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>>(config?: RuntimeActionConfig, logger?:
|
|
5
|
+
export declare function createActionFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>>(config?: RuntimeActionConfig, logger?: Logger): ActionFactory<MD, VD>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "../types/base.js";
|
|
2
2
|
import { type RuntimeModelConfig, type ModelFactory } from "../types/model.js";
|
|
3
|
-
export declare function createModelFactory(config?: RuntimeModelConfig, logger?:
|
|
3
|
+
export declare function createModelFactory(config?: RuntimeModelConfig, logger?: Logger): ModelFactory;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "../types/base.js";
|
|
2
2
|
import type { ModelDefinitions } from "../types/model.js";
|
|
3
3
|
import type { RuntimeViewConfig, ViewFactory } from "../types/view.js";
|
|
4
|
-
export declare function createViewFactory<MD extends ModelDefinitions>(config?: RuntimeViewConfig, logger?:
|
|
4
|
+
export declare function createViewFactory<MD extends ModelDefinitions>(config?: RuntimeViewConfig, logger?: Logger): ViewFactory<MD>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { effectScope } from "vue";
|
|
2
|
-
import { createConsola } from "consola";
|
|
3
2
|
import { createStore as createStoreSource } from "@harlem/core";
|
|
4
3
|
import { runtimeConfig } from "../config.js";
|
|
5
4
|
import { createModelFactory } from "./layers/model.js";
|
|
@@ -7,14 +6,10 @@ import { createViewFactory } from "./layers/view.js";
|
|
|
7
6
|
import { createActionFactory } from "./layers/action.js";
|
|
8
7
|
import { createStoreState, createStoreModel, createStoreView, createStoreAction } from "./utils/store.js";
|
|
9
8
|
import { createStoreCompose } from "./utils/compose.js";
|
|
9
|
+
import { createLogger } from "./utils/logger.js";
|
|
10
10
|
export function createStore(config) {
|
|
11
11
|
function init() {
|
|
12
|
-
const logger =
|
|
13
|
-
level: runtimeConfig.logger,
|
|
14
|
-
defaults: {
|
|
15
|
-
tag: `harlemify:${config.name}`
|
|
16
|
-
}
|
|
17
|
-
});
|
|
12
|
+
const logger = createLogger(`harlemify:${config.name}`, runtimeConfig.logger);
|
|
18
13
|
logger.info("Creating store");
|
|
19
14
|
const modelFactory = createModelFactory(runtimeConfig.model, logger);
|
|
20
15
|
const viewFactory = createViewFactory(runtimeConfig.view, logger);
|
|
@@ -46,16 +46,26 @@ export interface ActionApiRequest<MD extends ModelDefinitions, VD extends ViewDe
|
|
|
46
46
|
concurrent?: ActionConcurrent;
|
|
47
47
|
}
|
|
48
48
|
export type ActionApiRequestShortcut<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = Omit<ActionApiRequest<MD, VD>, "method">;
|
|
49
|
-
export interface
|
|
49
|
+
export interface ActionApiCommitContext<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
50
|
+
request: Readonly<{
|
|
51
|
+
url: string;
|
|
52
|
+
method: ActionApiMethod;
|
|
53
|
+
headers: Readonly<Record<string, string>>;
|
|
54
|
+
query: Readonly<Record<string, unknown>>;
|
|
55
|
+
body: unknown;
|
|
56
|
+
}>;
|
|
57
|
+
view: DeepReadonly<StoreView<MD, VD>>;
|
|
58
|
+
}
|
|
59
|
+
export interface ActionApiCommit<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, K extends keyof MD = keyof MD> {
|
|
50
60
|
model: K;
|
|
51
61
|
mode: ModelOneMode | ModelManyMode;
|
|
52
|
-
|
|
62
|
+
transform?: (data: unknown, context: ActionApiCommitContext<MD, VD>) => unknown;
|
|
53
63
|
options?: ModelOneCommitOptions | ModelManyCommitOptions;
|
|
54
64
|
}
|
|
55
|
-
export type ActionApiCommitReturn<MD extends ModelDefinitions, C extends readonly ActionApiCommit<MD>[]> = C extends readonly [] ? unknown : {
|
|
65
|
+
export type ActionApiCommitReturn<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, C extends readonly ActionApiCommit<MD, VD>[]> = C extends readonly [] ? unknown : {
|
|
56
66
|
[E in C[number] as E["model"] & string]: E["model"] extends keyof MD ? ModelDefinitionInfer<MD, E["model"]> : never;
|
|
57
67
|
};
|
|
58
|
-
export interface ActionApiDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, C extends readonly ActionApiCommit<MD>[] = readonly ActionApiCommit<MD>[]> extends BaseDefinition {
|
|
68
|
+
export interface ActionApiDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, C extends readonly ActionApiCommit<MD, VD>[] = readonly ActionApiCommit<MD, VD>[]> extends BaseDefinition {
|
|
59
69
|
request: ActionApiRequest<MD, VD>;
|
|
60
70
|
commits: C;
|
|
61
71
|
}
|
|
@@ -74,25 +84,25 @@ export interface ActionHandlerDefinition<MD extends ModelDefinitions, VD extends
|
|
|
74
84
|
}
|
|
75
85
|
export type ActionDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = ActionApiDefinition<MD, VD, any> | ActionHandlerDefinition<MD, VD, any, any>;
|
|
76
86
|
export type ActionDefinitions<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = Record<string, ActionDefinition<MD, VD>>;
|
|
77
|
-
type ActionApiCommitTuple<MD extends ModelDefinitions
|
|
78
|
-
ActionApiCommit<MD, keyof MD>,
|
|
79
|
-
...ActionApiCommit<MD, keyof MD>[]
|
|
87
|
+
type ActionApiCommitTuple<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> = readonly [
|
|
88
|
+
ActionApiCommit<MD, VD, keyof MD>,
|
|
89
|
+
...ActionApiCommit<MD, VD, keyof MD>[]
|
|
80
90
|
];
|
|
81
91
|
export interface ActionApiFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
82
92
|
(request: ActionApiRequest<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
83
|
-
<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequest<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
93
|
+
<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequest<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
84
94
|
get(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
85
|
-
get<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
95
|
+
get<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
86
96
|
head(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
87
|
-
head<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
97
|
+
head<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
88
98
|
post(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
89
|
-
post<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
99
|
+
post<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
90
100
|
put(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
91
|
-
put<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
101
|
+
put<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
92
102
|
patch(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
93
|
-
patch<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
103
|
+
patch<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
94
104
|
delete(request: ActionApiRequestShortcut<MD, VD>): ActionApiDefinition<MD, VD, []>;
|
|
95
|
-
delete<const C extends ActionApiCommitTuple<MD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
105
|
+
delete<const C extends ActionApiCommitTuple<MD, VD>>(request: ActionApiRequestShortcut<MD, VD>, ...commits: C): ActionApiDefinition<MD, VD, C>;
|
|
96
106
|
}
|
|
97
107
|
export interface ActionHandlerFactory<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>> {
|
|
98
108
|
<P = unknown, R = void>(callback: ActionHandlerCallback<MD, VD, P, R>, options?: ActionHandlerOptions<P>): ActionHandlerDefinition<MD, VD, P, R>;
|
|
@@ -160,6 +170,6 @@ export interface ActionHandlerCall<P = unknown, T = void> extends ActionCallBase
|
|
|
160
170
|
}
|
|
161
171
|
export type ActionCall<T = void> = ActionApiCall<T> | ActionHandlerCall<any, T>;
|
|
162
172
|
export type StoreAction<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>> = {
|
|
163
|
-
[K in keyof AD]: AD[K] extends ActionApiDefinition<MD, VD, infer C> ? C extends readonly ActionApiCommit<MD>[] ? ActionApiCall<ActionApiCommitReturn<MD, C>> : ActionApiCall : AD[K] extends ActionHandlerDefinition<MD, VD, infer P, infer R> ? ActionHandlerCall<P, R> : never;
|
|
173
|
+
[K in keyof AD]: AD[K] extends ActionApiDefinition<MD, VD, infer C> ? C extends readonly ActionApiCommit<MD, VD>[] ? ActionApiCall<ActionApiCommitReturn<MD, VD, C>> : ActionApiCall : AD[K] extends ActionHandlerDefinition<MD, VD, infer P, infer R> ? ActionHandlerCall<P, R> : never;
|
|
164
174
|
};
|
|
165
175
|
export {};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
export interface Logger {
|
|
2
|
+
info(message: string, ...args: unknown[]): void;
|
|
3
|
+
debug(message: string, ...args: unknown[]): void;
|
|
4
|
+
warn(message: string, ...args: unknown[]): void;
|
|
5
|
+
error(message: string, ...args: unknown[]): void;
|
|
6
|
+
}
|
|
2
7
|
export interface BaseDefinition {
|
|
3
8
|
key: string;
|
|
4
|
-
logger?:
|
|
9
|
+
logger?: Logger;
|
|
5
10
|
}
|
|
@@ -31,15 +31,19 @@ export type ModelDefaultIdentifier<S extends Shape> = "id" extends keyof S ? "id
|
|
|
31
31
|
export type AtLeastOne<S extends Shape> = {
|
|
32
32
|
[K in keyof S]: Pick<S, K>;
|
|
33
33
|
}[keyof S];
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
export type ModelHookContext<T> = {
|
|
35
|
+
mode: ModelOneMode | ModelManyMode;
|
|
36
|
+
state: T;
|
|
37
|
+
};
|
|
38
|
+
export interface ModelDefinitionOptions<T = unknown> {
|
|
39
|
+
pre?: (context: ModelHookContext<T>) => void;
|
|
40
|
+
post?: (context: ModelHookContext<T>) => void;
|
|
37
41
|
}
|
|
38
42
|
export interface ModelOneDefinition<S extends Shape> extends BaseDefinition {
|
|
39
43
|
shape: ShapeType<S>;
|
|
40
44
|
type: ModelType.ONE;
|
|
41
45
|
default: () => S;
|
|
42
|
-
options?: ModelDefinitionOptions
|
|
46
|
+
options?: ModelDefinitionOptions<S>;
|
|
43
47
|
}
|
|
44
48
|
export interface ModelManyDefinition<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST> extends BaseDefinition {
|
|
45
49
|
shape: ShapeType<S>;
|
|
@@ -47,7 +51,7 @@ export interface ModelManyDefinition<S extends Shape, I extends keyof S = ModelD
|
|
|
47
51
|
kind: T;
|
|
48
52
|
identifier: [T] extends [ModelManyKind.LIST] ? I : never;
|
|
49
53
|
default: () => [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>;
|
|
50
|
-
options?: ModelDefinitionOptions
|
|
54
|
+
options?: ModelDefinitionOptions<[T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>>;
|
|
51
55
|
}
|
|
52
56
|
export type ModelDefinition<S extends Shape> = ModelOneDefinition<S> | ModelManyDefinition<S, any, any>;
|
|
53
57
|
export type ModelDefinitions = Record<string, ModelDefinition<any>>;
|
|
@@ -59,10 +63,10 @@ export type ModelDefinitionsInfer<MD extends ModelDefinitions> = {
|
|
|
59
63
|
[K in keyof MD]: ModelDefinitionInfer<MD, K>;
|
|
60
64
|
};
|
|
61
65
|
export interface ModelFactory {
|
|
62
|
-
one<S extends Shape>(shape: ShapeType<S>, options?: ModelDefinitionOptions & {
|
|
66
|
+
one<S extends Shape>(shape: ShapeType<S>, options?: ModelDefinitionOptions<S> & {
|
|
63
67
|
default?: () => S;
|
|
64
68
|
}): ModelOneDefinition<S>;
|
|
65
|
-
many<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST>(shape: ShapeType<S>, options?: ModelDefinitionOptions & {
|
|
69
|
+
many<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST>(shape: ShapeType<S>, options?: ModelDefinitionOptions<[T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>> & {
|
|
66
70
|
kind?: T;
|
|
67
71
|
identifier?: [T] extends [ModelManyKind.LIST] ? I : never;
|
|
68
72
|
default?: () => [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defu } from "defu";
|
|
2
1
|
import { ref, computed, readonly, toValue, nextTick } from "vue";
|
|
3
2
|
import {
|
|
4
3
|
ActionApiMethod,
|
|
@@ -6,7 +5,7 @@ import {
|
|
|
6
5
|
ActionStatus,
|
|
7
6
|
ActionConcurrent
|
|
8
7
|
} from "../types/action.js";
|
|
9
|
-
import { trimStart, trimEnd, isEmptyRecord, isPlainObject } from "./base.js";
|
|
8
|
+
import { trimStart, trimEnd, isEmptyRecord, isPlainObject, merge } from "./base.js";
|
|
10
9
|
import {
|
|
11
10
|
ActionApiError,
|
|
12
11
|
ActionHandlerError,
|
|
@@ -38,12 +37,12 @@ function resolveApiUrl(definition, view, options) {
|
|
|
38
37
|
function resolveApiHeaders(definition, view, options) {
|
|
39
38
|
const initial = resolveValue(definition.request.headers, view, {});
|
|
40
39
|
const custom = options?.headers ?? {};
|
|
41
|
-
return
|
|
40
|
+
return merge(custom, initial);
|
|
42
41
|
}
|
|
43
42
|
function resolveApiQuery(definition, view, options) {
|
|
44
43
|
const initial = resolveValue(definition.request.query, view, {});
|
|
45
44
|
const custom = options?.query ?? {};
|
|
46
|
-
return
|
|
45
|
+
return merge(custom, initial);
|
|
47
46
|
}
|
|
48
47
|
function resolveApiBody(definition, view, target, options) {
|
|
49
48
|
if (definition.request.method === ActionApiMethod.GET || definition.request.method === ActionApiMethod.HEAD) {
|
|
@@ -51,7 +50,7 @@ function resolveApiBody(definition, view, target, options) {
|
|
|
51
50
|
}
|
|
52
51
|
const initial = resolveValue(definition.request.body, view, {});
|
|
53
52
|
const custom = options?.body ?? {};
|
|
54
|
-
const body =
|
|
53
|
+
const body = merge(custom, initial);
|
|
55
54
|
if (!isPlainObject(body)) {
|
|
56
55
|
return body;
|
|
57
56
|
}
|
|
@@ -97,9 +96,9 @@ function resolveCommitMode(commit, options) {
|
|
|
97
96
|
}
|
|
98
97
|
return commit.mode;
|
|
99
98
|
}
|
|
100
|
-
function
|
|
101
|
-
if (typeof commit.
|
|
102
|
-
return commit.
|
|
99
|
+
function resolveCommitTransform(commit, data, context) {
|
|
100
|
+
if (typeof commit.transform === "function") {
|
|
101
|
+
return commit.transform(data, context);
|
|
103
102
|
}
|
|
104
103
|
return data;
|
|
105
104
|
}
|
|
@@ -177,7 +176,7 @@ async function executeHandler(definition, handler) {
|
|
|
177
176
|
throw handlerError;
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
|
-
function executeCommit(definition, model, data, options) {
|
|
179
|
+
function executeCommit(definition, model, data, context, options) {
|
|
181
180
|
const commits = definition.commits;
|
|
182
181
|
if (!commits || commits.length === 0) {
|
|
183
182
|
return data;
|
|
@@ -192,7 +191,7 @@ function executeCommit(definition, model, data, options) {
|
|
|
192
191
|
});
|
|
193
192
|
}
|
|
194
193
|
const mode = resolveCommitMode(commit, options);
|
|
195
|
-
let value =
|
|
194
|
+
let value = resolveCommitTransform(commit, data, context);
|
|
196
195
|
if (!isEmptyRecord(target.aliases())) {
|
|
197
196
|
value = resolveAliasInbound(value, target.aliases());
|
|
198
197
|
}
|
|
@@ -296,7 +295,11 @@ export function createAction(definition, model, view) {
|
|
|
296
295
|
},
|
|
297
296
|
options
|
|
298
297
|
);
|
|
299
|
-
|
|
298
|
+
const context = {
|
|
299
|
+
request: { url, method, headers, query, body },
|
|
300
|
+
view
|
|
301
|
+
};
|
|
302
|
+
data = executeCommit(definition, model, response, context, options);
|
|
300
303
|
} else if (isHandlerDefinition(definition)) {
|
|
301
304
|
const payload = resolveHandlerPayload(definition, options);
|
|
302
305
|
data = await executeHandler(definition, {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BaseDefinition } from "../types/base.js";
|
|
2
|
+
export declare function snapshot<T>(value: T): T;
|
|
2
3
|
export declare function wrapBaseDefinition<T extends Omit<BaseDefinition, "key">>(definition: T): T & BaseDefinition;
|
|
3
4
|
export declare function trimStart(value: string, char: string): string;
|
|
4
5
|
export declare function trimEnd(value: string, char: string): string;
|
|
@@ -6,5 +7,6 @@ export declare function ensureArray<T>(value: T | T[]): T[];
|
|
|
6
7
|
export declare function isObject(value: unknown): value is object;
|
|
7
8
|
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
8
9
|
export declare function isEmptyRecord(record: Record<string, unknown> | undefined): record is undefined;
|
|
10
|
+
export declare function merge<T>(priority: unknown, base: T): T;
|
|
9
11
|
export declare function debounce<T extends (...args: any[]) => any>(callback: T, delay: number): T;
|
|
10
12
|
export declare function throttle<T extends (...args: any[]) => any>(callback: T, delay: number): T;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { objectClone, typeIsObject } from "@harlem/utilities";
|
|
2
|
+
export function snapshot(value) {
|
|
3
|
+
return objectClone(value);
|
|
4
|
+
}
|
|
1
5
|
export function wrapBaseDefinition(definition) {
|
|
2
6
|
let key = "";
|
|
3
7
|
return Object.defineProperties(definition, {
|
|
@@ -23,16 +27,10 @@ export function ensureArray(value) {
|
|
|
23
27
|
return Array.isArray(value) ? value : [value];
|
|
24
28
|
}
|
|
25
29
|
export function isObject(value) {
|
|
26
|
-
return value
|
|
30
|
+
return typeIsObject(value);
|
|
27
31
|
}
|
|
28
32
|
export function isPlainObject(value) {
|
|
29
|
-
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
if (Array.isArray(value)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
33
|
+
return isObject(value);
|
|
36
34
|
}
|
|
37
35
|
export function isEmptyRecord(record) {
|
|
38
36
|
if (!record) {
|
|
@@ -43,6 +41,20 @@ export function isEmptyRecord(record) {
|
|
|
43
41
|
}
|
|
44
42
|
return false;
|
|
45
43
|
}
|
|
44
|
+
export function merge(priority, base) {
|
|
45
|
+
if (!isPlainObject(priority) || !isPlainObject(base)) {
|
|
46
|
+
return priority;
|
|
47
|
+
}
|
|
48
|
+
const output = { ...base };
|
|
49
|
+
for (const key of Object.keys(priority)) {
|
|
50
|
+
const value = priority[key];
|
|
51
|
+
if (value === null || value === void 0) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
output[key] = merge(value, output[key]);
|
|
55
|
+
}
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
46
58
|
export function debounce(callback, delay) {
|
|
47
59
|
let timer = null;
|
|
48
60
|
return (...args) => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "../types/base.js";
|
|
2
2
|
import type { ComposeDefinitions, StoreCompose } from "../types/compose.js";
|
|
3
|
-
export declare function createStoreCompose<CD extends ComposeDefinitions>(composeConfig: ((...args: any[]) => CD) | undefined, context: Record<string, unknown>, logger?:
|
|
3
|
+
export declare function createStoreCompose<CD extends ComposeDefinitions>(composeConfig: ((...args: any[]) => CD) | undefined, context: Record<string, unknown>, logger?: Logger): StoreCompose<CD>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const LEVELS = {
|
|
2
|
+
error: 0,
|
|
3
|
+
warn: 1,
|
|
4
|
+
info: 3,
|
|
5
|
+
debug: 4
|
|
6
|
+
};
|
|
7
|
+
export function createLogger(tag, level = -999) {
|
|
8
|
+
function report(method, sink) {
|
|
9
|
+
return (message, ...args) => {
|
|
10
|
+
if (LEVELS[method] > level) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
sink(`[${tag}]`, message, ...args);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
info: report("info", console.info),
|
|
18
|
+
debug: report("debug", console.debug),
|
|
19
|
+
warn: report("warn", console.warn),
|
|
20
|
+
error: report("error", console.error)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ensureArray } from "./base.js";
|
|
1
|
+
import { ensureArray, snapshot, merge } from "./base.js";
|
|
3
2
|
import { resolveShapeAliases } from "./shape.js";
|
|
4
3
|
import {
|
|
5
4
|
ModelType,
|
|
@@ -8,12 +7,17 @@ import {
|
|
|
8
7
|
ModelManyMode,
|
|
9
8
|
ModelSilent
|
|
10
9
|
} from "../types/model.js";
|
|
11
|
-
function callHook(definition, hook, silent) {
|
|
10
|
+
function callHook(definition, source, hook, mode, silent) {
|
|
12
11
|
if (silent === true || silent === hook) {
|
|
13
12
|
return;
|
|
14
13
|
}
|
|
14
|
+
const handler = definition.options?.[hook];
|
|
15
|
+
if (!handler) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
15
18
|
try {
|
|
16
|
-
definition.
|
|
19
|
+
const state = snapshot(source.state[definition.key]);
|
|
20
|
+
handler({ mode, state });
|
|
17
21
|
} catch (error) {
|
|
18
22
|
definition.logger?.error(`Model ${hook} hook error`, {
|
|
19
23
|
model: definition.key,
|
|
@@ -21,14 +25,14 @@ function callHook(definition, hook, silent) {
|
|
|
21
25
|
});
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
|
-
function wrapOperation(definition,
|
|
28
|
+
function wrapOperation(definition, source, mode, operation, silent) {
|
|
25
29
|
definition.logger?.debug("Model mutation", {
|
|
26
30
|
model: definition.key,
|
|
27
|
-
mutation
|
|
31
|
+
mutation: mode
|
|
28
32
|
});
|
|
29
|
-
callHook(definition, ModelSilent.PRE, silent);
|
|
33
|
+
callHook(definition, source, ModelSilent.PRE, mode, silent);
|
|
30
34
|
operation();
|
|
31
|
-
callHook(definition, ModelSilent.POST, silent);
|
|
35
|
+
callHook(definition, source, ModelSilent.POST, mode, silent);
|
|
32
36
|
}
|
|
33
37
|
function createOneCommit(definition, source) {
|
|
34
38
|
const setOperation = source.mutation(`${definition.key}:set`, (state, { payload }) => {
|
|
@@ -39,7 +43,7 @@ function createOneCommit(definition, source) {
|
|
|
39
43
|
});
|
|
40
44
|
const patchOperation = source.mutation(`${definition.key}:patch`, (state, { payload, options }) => {
|
|
41
45
|
if (options?.deep) {
|
|
42
|
-
state[definition.key] =
|
|
46
|
+
state[definition.key] = merge(payload, state[definition.key]);
|
|
43
47
|
return;
|
|
44
48
|
}
|
|
45
49
|
state[definition.key] = {
|
|
@@ -49,13 +53,19 @@ function createOneCommit(definition, source) {
|
|
|
49
53
|
});
|
|
50
54
|
return {
|
|
51
55
|
set(payload, options) {
|
|
52
|
-
wrapOperation(definition,
|
|
56
|
+
wrapOperation(definition, source, ModelOneMode.SET, () => setOperation({ payload }), options?.silent);
|
|
53
57
|
},
|
|
54
58
|
reset(options) {
|
|
55
|
-
wrapOperation(definition,
|
|
59
|
+
wrapOperation(definition, source, ModelOneMode.RESET, () => resetOperation(), options?.silent);
|
|
56
60
|
},
|
|
57
61
|
patch(payload, options) {
|
|
58
|
-
wrapOperation(
|
|
62
|
+
wrapOperation(
|
|
63
|
+
definition,
|
|
64
|
+
source,
|
|
65
|
+
ModelOneMode.PATCH,
|
|
66
|
+
() => patchOperation({ payload, options }),
|
|
67
|
+
options?.silent
|
|
68
|
+
);
|
|
59
69
|
}
|
|
60
70
|
};
|
|
61
71
|
}
|
|
@@ -77,7 +87,7 @@ function createManyListCommit(definition, source) {
|
|
|
77
87
|
return item;
|
|
78
88
|
}
|
|
79
89
|
if (options?.deep) {
|
|
80
|
-
return
|
|
90
|
+
return merge(found, item);
|
|
81
91
|
}
|
|
82
92
|
return {
|
|
83
93
|
...item,
|
|
@@ -120,19 +130,37 @@ function createManyListCommit(definition, source) {
|
|
|
120
130
|
});
|
|
121
131
|
return {
|
|
122
132
|
set(payload, options) {
|
|
123
|
-
wrapOperation(definition,
|
|
133
|
+
wrapOperation(definition, source, ModelManyMode.SET, () => setOperation({ payload }), options?.silent);
|
|
124
134
|
},
|
|
125
135
|
reset(options) {
|
|
126
|
-
wrapOperation(definition,
|
|
136
|
+
wrapOperation(definition, source, ModelManyMode.RESET, () => resetOperation(), options?.silent);
|
|
127
137
|
},
|
|
128
138
|
patch(payload, options) {
|
|
129
|
-
wrapOperation(
|
|
139
|
+
wrapOperation(
|
|
140
|
+
definition,
|
|
141
|
+
source,
|
|
142
|
+
ModelManyMode.PATCH,
|
|
143
|
+
() => patchOperation({ payload, options }),
|
|
144
|
+
options?.silent
|
|
145
|
+
);
|
|
130
146
|
},
|
|
131
147
|
remove(payload, options) {
|
|
132
|
-
wrapOperation(
|
|
148
|
+
wrapOperation(
|
|
149
|
+
definition,
|
|
150
|
+
source,
|
|
151
|
+
ModelManyMode.REMOVE,
|
|
152
|
+
() => removeOperation({ payload }),
|
|
153
|
+
options?.silent
|
|
154
|
+
);
|
|
133
155
|
},
|
|
134
156
|
add(payload, options) {
|
|
135
|
-
wrapOperation(
|
|
157
|
+
wrapOperation(
|
|
158
|
+
definition,
|
|
159
|
+
source,
|
|
160
|
+
ModelManyMode.ADD,
|
|
161
|
+
() => addOperation({ payload, options }),
|
|
162
|
+
options?.silent
|
|
163
|
+
);
|
|
136
164
|
}
|
|
137
165
|
};
|
|
138
166
|
}
|
|
@@ -145,7 +173,7 @@ function createManyRecordCommit(definition, source) {
|
|
|
145
173
|
});
|
|
146
174
|
const patchOperation = source.mutation(`${definition.key}:patch`, (state, { payload, options }) => {
|
|
147
175
|
if (options?.deep) {
|
|
148
|
-
state[definition.key] =
|
|
176
|
+
state[definition.key] = merge(payload, state[definition.key]);
|
|
149
177
|
return;
|
|
150
178
|
}
|
|
151
179
|
state[definition.key] = {
|
|
@@ -170,19 +198,31 @@ function createManyRecordCommit(definition, source) {
|
|
|
170
198
|
});
|
|
171
199
|
return {
|
|
172
200
|
set(payload, options) {
|
|
173
|
-
wrapOperation(definition,
|
|
201
|
+
wrapOperation(definition, source, ModelManyMode.SET, () => setOperation({ payload }), options?.silent);
|
|
174
202
|
},
|
|
175
203
|
reset(options) {
|
|
176
|
-
wrapOperation(definition,
|
|
204
|
+
wrapOperation(definition, source, ModelManyMode.RESET, () => resetOperation(), options?.silent);
|
|
177
205
|
},
|
|
178
206
|
patch(payload, options) {
|
|
179
|
-
wrapOperation(
|
|
207
|
+
wrapOperation(
|
|
208
|
+
definition,
|
|
209
|
+
source,
|
|
210
|
+
ModelManyMode.PATCH,
|
|
211
|
+
() => patchOperation({ payload, options }),
|
|
212
|
+
options?.silent
|
|
213
|
+
);
|
|
180
214
|
},
|
|
181
215
|
remove(payload, options) {
|
|
182
|
-
wrapOperation(
|
|
216
|
+
wrapOperation(
|
|
217
|
+
definition,
|
|
218
|
+
source,
|
|
219
|
+
ModelManyMode.REMOVE,
|
|
220
|
+
() => removeOperation({ payload }),
|
|
221
|
+
options?.silent
|
|
222
|
+
);
|
|
183
223
|
},
|
|
184
224
|
add(payload, options) {
|
|
185
|
-
wrapOperation(definition,
|
|
225
|
+
wrapOperation(definition, source, ModelManyMode.ADD, () => addOperation({ payload }), options?.silent);
|
|
186
226
|
}
|
|
187
227
|
};
|
|
188
228
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { defu } from "defu";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { isPlainObject, isEmptyRecord } from "./base.js";
|
|
2
|
+
import { isPlainObject, isEmptyRecord, merge } from "./base.js";
|
|
4
3
|
function resolveShapeFields(shape) {
|
|
5
4
|
if (!("shape" in shape) || typeof shape.shape !== "object" || !shape.shape) {
|
|
6
5
|
return void 0;
|
|
@@ -183,7 +182,7 @@ export function decorateShape(object) {
|
|
|
183
182
|
defaults(overrides) {
|
|
184
183
|
const zero = resolveZeroValues(object);
|
|
185
184
|
if (overrides) {
|
|
186
|
-
return
|
|
185
|
+
return merge(overrides, zero);
|
|
187
186
|
}
|
|
188
187
|
return zero;
|
|
189
188
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { snapshot } from "./base.js";
|
|
1
2
|
import { ViewClone } from "../types/view.js";
|
|
2
3
|
function resolveClonedValue(definition, value) {
|
|
3
4
|
if (!definition.options?.clone) {
|
|
4
5
|
return value;
|
|
5
6
|
}
|
|
6
7
|
if (definition.options.clone === ViewClone.DEEP) {
|
|
7
|
-
return
|
|
8
|
+
return snapshot(value);
|
|
8
9
|
}
|
|
9
10
|
if (Array.isArray(value)) {
|
|
10
11
|
return [...value];
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export type { ModelOneCommitOptions, ModelManyCommitOptions } from "./core/types
|
|
|
7
7
|
export { ViewClone } from "./core/types/view.js";
|
|
8
8
|
export type { ViewDefinitionOptions } from "./core/types/view.js";
|
|
9
9
|
export { ActionStatus, ActionConcurrent, ActionType, ActionApiMethod } from "./core/types/action.js";
|
|
10
|
-
export type { ActionCall, ActionApiCall, ActionHandlerCall, ActionCallOptions, ActionCallBaseOptions, ActionApiCallOptions, ActionHandlerCallOptions, ActionCallTransformerOptions, ActionCallBindOptions, ActionCallCommitOptions, ActionHandlerOptions, ActionResolvedApi, } from "./core/types/action.js";
|
|
11
|
-
export { ActionApiError, ActionHandlerError, ActionCommitError, ActionConcurrentError, isError, toError } from "./core/utils/error.js";
|
|
10
|
+
export type { ActionCall, ActionApiCall, ActionApiCommitContext, ActionHandlerCall, ActionCallOptions, ActionCallBaseOptions, ActionApiCallOptions, ActionHandlerCallOptions, ActionCallTransformerOptions, ActionCallBindOptions, ActionCallCommitOptions, ActionHandlerOptions, ActionResolvedApi, } from "./core/types/action.js";
|
|
11
|
+
export { ActionApiError, ActionHandlerError, ActionCommitError, ActionConcurrentError, isError, toError, } from "./core/utils/error.js";
|
|
12
12
|
export type { ComposeCallback, ComposeCall, ComposeDefinitions, ComposeContext, StoreCompose, } from "./core/types/compose.js";
|
|
13
13
|
export { useStoreCompose } from "./composables/compose.js";
|
|
14
14
|
export type { UseStoreCompose } from "./composables/compose.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -3,7 +3,14 @@ export { shape } from "./core/layers/shape.js";
|
|
|
3
3
|
export { ModelType, ModelManyKind, ModelOneMode, ModelManyMode, ModelSilent } from "./core/types/model.js";
|
|
4
4
|
export { ViewClone } from "./core/types/view.js";
|
|
5
5
|
export { ActionStatus, ActionConcurrent, ActionType, ActionApiMethod } from "./core/types/action.js";
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
ActionApiError,
|
|
8
|
+
ActionHandlerError,
|
|
9
|
+
ActionCommitError,
|
|
10
|
+
ActionConcurrentError,
|
|
11
|
+
isError,
|
|
12
|
+
toError
|
|
13
|
+
} from "./core/utils/error.js";
|
|
7
14
|
export { useStoreCompose } from "./composables/compose.js";
|
|
8
15
|
export { useIsolatedActionStatus, useIsolatedActionError, useStoreAction } from "./composables/action.js";
|
|
9
16
|
export { useStoreModel } from "./composables/model.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diphyx/harlemify",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "API state management for Nuxt powered by Harlem",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -45,29 +45,33 @@
|
|
|
45
45
|
"prepare": "pnpm nuxt prepare && pnpm nuxt prepare playground",
|
|
46
46
|
"dev": "pnpm nuxt dev playground",
|
|
47
47
|
"build": "nuxt-module-build build",
|
|
48
|
-
"lint": "eslint
|
|
48
|
+
"lint": "eslint ./ --fix",
|
|
49
|
+
"format": "prettier ./ --write",
|
|
49
50
|
"test": "vitest run",
|
|
50
51
|
"test:e2e": "playwright test"
|
|
51
52
|
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"zod": "^4.4.3"
|
|
55
|
+
},
|
|
52
56
|
"dependencies": {
|
|
53
57
|
"@harlem/core": "^3.1.8",
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"defu": "^6.1.4",
|
|
57
|
-
"zod": "^4.3.6"
|
|
58
|
+
"@harlem/utilities": "^3.1.8",
|
|
59
|
+
"@nuxt/kit": "^4.4.6"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
60
62
|
"@nuxt/eslint-config": "^0.6.2",
|
|
61
63
|
"@nuxt/module-builder": "^0.8.4",
|
|
62
|
-
"@nuxt/schema": "^4.
|
|
64
|
+
"@nuxt/schema": "^4.4.6",
|
|
63
65
|
"@nuxt/test-utils": "^3.23.0",
|
|
64
|
-
"@playwright/test": "^1.
|
|
65
|
-
"@types/node": "^22.19.
|
|
66
|
+
"@playwright/test": "^1.60.0",
|
|
67
|
+
"@types/node": "^22.19.19",
|
|
66
68
|
"@vitest/coverage-v8": "^2.1.9",
|
|
67
|
-
"eslint": "^9.39.
|
|
68
|
-
"nuxt": "^4.
|
|
69
|
+
"eslint": "^9.39.4",
|
|
70
|
+
"nuxt": "^4.4.6",
|
|
71
|
+
"prettier": "^3.8.3",
|
|
69
72
|
"typescript": "^5.9.3",
|
|
70
73
|
"vitest": "^2.1.9",
|
|
71
|
-
"vue": "^3.5.
|
|
74
|
+
"vue": "^3.5.35",
|
|
75
|
+
"zod": "^4.4.3"
|
|
72
76
|
}
|
|
73
77
|
}
|