@diphyx/harlemify 5.3.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 CHANGED
@@ -2,40 +2,66 @@
2
2
 
3
3
  > Factory-driven state management for Nuxt powered by [Harlem](https://harlemjs.com/)
4
4
 
5
+ ![Version](https://img.shields.io/badge/version-5.3.0-42b883)
6
+ ![License](https://img.shields.io/badge/license-MIT-blue)
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
- - **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
10
+ Built on top of [Harlem](https://harlemjs.com/), a powerful and extensible state management library for Vue 3.
15
11
 
16
- ## Install
12
+ ---
17
13
 
18
- ```bash
19
- npm install @diphyx/harlemify
20
- ```
14
+ ## The Problem
15
+
16
+ Every Nuxt app has the same boilerplate for every API resource:
21
17
 
22
18
  ```typescript
23
- // nuxt.config.ts
24
- export default defineNuxtConfig({
25
- modules: ["@diphyx/harlemify"],
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
- ## Usage
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
- id: factory.number().meta({
34
- identifier: true,
35
- }),
36
- name: factory.string(),
37
- email: factory.email(),
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
- model: "list",
62
- mode: ModelManyMode.SET,
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
- <ul v-if="!loading">
80
- <li v-for="user in data.value" :key="user.id">{{ user.name }}</li>
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
@@ -2,9 +2,9 @@
2
2
  "name": "harlemify",
3
3
  "configKey": "harlemify",
4
4
  "compatibility": {
5
- "nuxt": ">=3.0.0 || >=4.0.0"
5
+ "nuxt": "^3.14.0 || ^4.0.0"
6
6
  },
7
- "version": "5.3.0",
7
+ "version": "5.4.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "unknown"
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: ">=3.0.0 || >=4.0.0"
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
 
@@ -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>;
@@ -0,0 +1,10 @@
1
+ export function useStoreCompose(store, key) {
2
+ const compose = store.compose[key];
3
+ if (!compose) {
4
+ throw new Error(`Compose "${key}" not found in store`);
5
+ }
6
+ return {
7
+ execute: (...args) => compose(...args),
8
+ active: compose.active
9
+ };
10
+ }
@@ -6,9 +6,8 @@ export interface UseStoreViewTrackOptions {
6
6
  debounce?: number;
7
7
  throttle?: number;
8
8
  }
9
- export interface UseStoreViewOptions<T> {
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<T> & {
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<T>): UseStoreViewProxy<T>;
32
+ }, key: K, options?: UseStoreViewOptions): UseStoreViewProxy<T>;
@@ -1,17 +1,5 @@
1
- import { computed, watch } from "vue";
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 = resolveDefault(store.view[key], options?.default);
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
- ModelType
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
- identifier: config?.identifier,
12
- ...options
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
- identifier: config?.identifier,
23
- ...options
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>>(config: StoreConfig<MD, VD, AD>): Store<MD, VD, AD>;
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
- const logger = createConsola({
10
- level: runtimeConfig.logger,
11
- defaults: {
12
- tag: `harlemify:${config.name}`
13
- }
14
- });
15
- logger.info("Creating store");
16
- const modelFactory = createModelFactory(runtimeConfig.model, logger);
17
- const viewFactory = createViewFactory(runtimeConfig.view, logger);
18
- const actionFactory = createActionFactory(runtimeConfig.action, logger);
19
- const modelDefinitions = config.model(modelFactory);
20
- const viewDefinitions = config.view(viewFactory);
21
- const actionDefinitions = config.action(actionFactory);
22
- const state = createStoreState(modelDefinitions);
23
- const source = createStoreSource(config.name, state);
24
- const model = createStoreModel(modelDefinitions, source);
25
- const view = createStoreView(viewDefinitions, source);
26
- const action = createStoreAction(actionDefinitions, model, view);
27
- logger.info("Store created");
28
- return {
29
- model,
30
- view,
31
- action
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
  }
@@ -64,7 +64,7 @@ export type ActionHandlerCallback<MD extends ModelDefinitions, VD extends ViewDe
64
64
  model: StoreModel<MD>;
65
65
  view: StoreView<MD, VD>;
66
66
  payload: P;
67
- }) => Promise<R>;
67
+ }) => Promise<R> | R;
68
68
  export interface ActionHandlerDefinition<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, P = unknown, R = void> extends BaseDefinition {
69
69
  callback: ActionHandlerCallback<MD, VD, P, R>;
70
70
  options?: ActionHandlerOptions<P>;
@@ -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
@@ -23,33 +23,35 @@ export declare enum ModelManyMode {
23
23
  REMOVE = "remove",
24
24
  ADD = "add"
25
25
  }
26
+ export declare enum ModelSilent {
27
+ PRE = "pre",
28
+ POST = "post"
29
+ }
26
30
  export type ModelDefaultIdentifier<S extends Shape> = "id" extends keyof S ? "id" : keyof S;
27
31
  export type AtLeastOne<S extends Shape> = {
28
32
  [K in keyof S]: Pick<S, K>;
29
33
  }[keyof S];
30
- export interface ModelOneDefinitionOptions<S extends Shape> {
31
- identifier?: keyof S;
32
- default?: S;
34
+ export interface ModelDefinitionOptions {
35
+ pre?: () => void;
36
+ post?: () => void;
33
37
  }
34
- export type ModelManyDefinitionOptions<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST> = {
35
- kind?: T;
36
- default?: [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>;
37
- } & ([T] extends [ModelManyKind.LIST] ? {
38
- identifier?: I;
39
- } : {});
40
38
  export interface ModelOneDefinition<S extends Shape> extends BaseDefinition {
41
39
  shape: ShapeType<S>;
42
40
  type: ModelType.ONE;
43
- options?: ModelOneDefinitionOptions<S>;
41
+ default: () => S;
42
+ options?: ModelDefinitionOptions;
44
43
  }
45
44
  export interface ModelManyDefinition<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST> extends BaseDefinition {
46
45
  shape: ShapeType<S>;
47
46
  type: ModelType.MANY;
48
- options?: ModelManyDefinitionOptions<S, I, T>;
47
+ kind: T;
48
+ identifier: [T] extends [ModelManyKind.LIST] ? I : never;
49
+ default: () => [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>;
50
+ options?: ModelDefinitionOptions;
49
51
  }
50
52
  export type ModelDefinition<S extends Shape> = ModelOneDefinition<S> | ModelManyDefinition<S, any, any>;
51
53
  export type ModelDefinitions = Record<string, ModelDefinition<any>>;
52
- export type ModelDefinitionInfer<MD extends ModelDefinitions, K extends keyof MD> = MD[K] extends ModelOneDefinition<infer S> ? S | null : MD[K] extends ModelManyDefinition<infer S, any, infer T> ? [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]> : never;
54
+ export type ModelDefinitionInfer<MD extends ModelDefinitions, K extends keyof MD> = MD[K] extends ModelOneDefinition<infer S> ? S : MD[K] extends ModelManyDefinition<infer S, any, infer T> ? [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]> : never;
53
55
  export type ModelDefinitionInferTuple<MD extends ModelDefinitions, K extends readonly (keyof MD)[]> = {
54
56
  [I in keyof K]: K[I] extends keyof MD ? ModelDefinitionInfer<MD, K[I]> : never;
55
57
  };
@@ -57,36 +59,47 @@ export type ModelDefinitionsInfer<MD extends ModelDefinitions> = {
57
59
  [K in keyof MD]: ModelDefinitionInfer<MD, K>;
58
60
  };
59
61
  export interface ModelFactory {
60
- one<S extends Shape>(shape: ShapeType<S>, options?: ModelOneDefinitionOptions<S>): ModelOneDefinition<S>;
61
- many<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST>(shape: ShapeType<S>, options?: ModelManyDefinitionOptions<S, I, T>): ModelManyDefinition<S, I, T>;
62
+ one<S extends Shape>(shape: ShapeType<S>, options?: ModelDefinitionOptions & {
63
+ default?: () => S;
64
+ }): ModelOneDefinition<S>;
65
+ many<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST>(shape: ShapeType<S>, options?: ModelDefinitionOptions & {
66
+ kind?: T;
67
+ identifier?: [T] extends [ModelManyKind.LIST] ? I : never;
68
+ default?: () => [T] extends [ModelManyKind.LIST] ? S[] : Record<string, S[]>;
69
+ }): ModelManyDefinition<S, I, T>;
62
70
  }
63
71
  export interface ModelOneCommitOptions {
64
72
  deep?: boolean;
73
+ silent?: true | ModelSilent;
65
74
  }
66
75
  export interface ModelManyCommitOptions {
67
76
  by?: string;
68
77
  prepend?: boolean;
69
78
  unique?: boolean;
70
79
  deep?: boolean;
80
+ silent?: true | ModelSilent;
71
81
  }
72
82
  export interface ModelOneCommit<S extends Shape> {
73
- set: (value: S) => void;
74
- reset: () => void;
75
- patch: (value: Partial<S>, options?: ModelOneCommitOptions) => void;
83
+ set: (payload: S, options?: Pick<ModelOneCommitOptions, "silent">) => void;
84
+ reset: (options?: Pick<ModelOneCommitOptions, "silent">) => void;
85
+ patch: (payload: Partial<S>, options?: Pick<ModelOneCommitOptions, "deep" | "silent">) => void;
76
86
  }
77
87
  export interface ModelManyListCommit<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>> {
78
- set: (value: S[]) => void;
79
- reset: () => void;
80
- patch: (value: Partial<S> | Partial<S>[], options?: ModelManyCommitOptions) => void;
81
- remove: (value: Pick<S, I> | Pick<S, I>[] | AtLeastOne<S> | AtLeastOne<S>[]) => void;
82
- add: (value: S | S[], options?: ModelManyCommitOptions) => void;
88
+ set: (payload: S[], options?: Pick<ModelManyCommitOptions, "silent">) => void;
89
+ reset: (options?: Pick<ModelManyCommitOptions, "silent">) => void;
90
+ patch: (payload: Partial<S> | Partial<S>[], options?: Pick<ModelManyCommitOptions, "by" | "deep" | "silent">) => void;
91
+ remove: (payload: Pick<S, I> | Pick<S, I>[] | AtLeastOne<S> | AtLeastOne<S>[], options?: Pick<ModelManyCommitOptions, "silent">) => void;
92
+ add: (payload: S | S[], options?: Pick<ModelManyCommitOptions, "by" | "prepend" | "unique" | "silent">) => void;
83
93
  }
84
94
  export interface ModelManyRecordCommit<S extends Shape> {
85
- set: (value: Record<string, S[]>) => void;
86
- reset: () => void;
87
- patch: (value: Record<string, S[]>, options?: ModelOneCommitOptions) => void;
88
- remove: (key: string) => void;
89
- add: (key: string, value: S[]) => void;
95
+ set: (payload: Record<string, S[]>, options?: Pick<ModelOneCommitOptions, "silent">) => void;
96
+ reset: (options?: Pick<ModelOneCommitOptions, "silent">) => void;
97
+ patch: (payload: Record<string, S[]>, options?: Pick<ModelOneCommitOptions, "deep" | "silent">) => void;
98
+ remove: (payload: string, options?: Pick<ModelOneCommitOptions, "silent">) => void;
99
+ add: (payload: {
100
+ key: string;
101
+ value: S[];
102
+ }, options?: Pick<ModelOneCommitOptions, "silent">) => void;
90
103
  }
91
104
  export type ModelManyCommit<S extends Shape, I extends keyof S = ModelDefaultIdentifier<S>, T extends ModelManyKind = ModelManyKind.LIST> = [T] extends [ModelManyKind.LIST] ? ModelManyListCommit<S, I> : ModelManyRecordCommit<S>;
92
105
  export type ModelOneCall<S extends Shape> = ModelOneCommit<S> & {
@@ -22,3 +22,8 @@ export var ModelManyMode = /* @__PURE__ */ ((ModelManyMode2) => {
22
22
  ModelManyMode2["ADD"] = "add";
23
23
  return ModelManyMode2;
24
24
  })(ModelManyMode || {});
25
+ export var ModelSilent = /* @__PURE__ */ ((ModelSilent2) => {
26
+ ModelSilent2["PRE"] = "pre";
27
+ ModelSilent2["POST"] = "post";
28
+ return ModelSilent2;
29
+ })(ModelSilent || {});
@@ -7,12 +7,6 @@ export type ShapeInfer<T extends z.ZodType<any>> = z.infer<T>;
7
7
  export type ShapeCall<T extends ShapeRawDefinition> = z.ZodObject<T> & {
8
8
  defaults: (overrides?: Partial<z.infer<z.ZodObject<T>>>) => z.infer<z.ZodObject<T>>;
9
9
  };
10
- export interface ShapeResolved<T extends ShapeDefinition = ShapeDefinition> {
11
- identifier?: keyof T["shape"] & string;
12
- defaults: Record<string, unknown>;
13
- fields: (keyof T["shape"] & string)[];
14
- aliases: Record<string, string>;
15
- }
16
10
  export interface ShapeFieldDefinition {
17
11
  meta?: {
18
12
  identifier?: boolean;
@@ -1,14 +1,18 @@
1
1
  import type { ModelDefinitions, ModelFactory, StoreModel } from "./model.js";
2
2
  import type { ViewDefinitions, ViewFactory, StoreView } from "./view.js";
3
3
  import type { ActionDefinitions, ActionFactory, StoreAction } from "./action.js";
4
- export interface StoreConfig<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>> {
4
+ import type { ComposeDefinitions, ComposeContext, StoreCompose } from "./compose.js";
5
+ export interface StoreConfig<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>, CD extends ComposeDefinitions = ComposeDefinitions> {
5
6
  name: string;
6
7
  model: (factory: ModelFactory) => MD;
7
8
  view: (factory: ViewFactory<MD>) => VD;
8
9
  action: (factory: ActionFactory<MD, VD>) => AD;
10
+ compose?: (context: ComposeContext<MD, VD, AD>) => CD;
11
+ lazy?: boolean;
9
12
  }
10
- export interface Store<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>> {
13
+ export interface Store<MD extends ModelDefinitions, VD extends ViewDefinitions<MD>, AD extends ActionDefinitions<MD, VD>, CD extends ComposeDefinitions = ComposeDefinitions> {
11
14
  model: StoreModel<MD>;
12
15
  view: StoreView<MD, VD>;
13
16
  action: StoreAction<MD, VD, AD>;
17
+ compose: StoreCompose<CD>;
14
18
  }
@@ -0,0 +1,3 @@
1
+ import type { ConsolaInstance } from "consola";
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?: ConsolaInstance): StoreCompose<CD>;