;
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ childs,
+ },
+ };
+ });
+ entities.edit.add([
+ { id: 'a', childs: [{ id: 'b', childs: [{ id: 'c', childs: [] }] }] },
+ { id: 'c', childs: [] },
+ ]);
+ const Entity = () => {
+ const { id } = useEntityItem(entities);
+ const { remove } = useEditKeyval(entities);
+ return (
+
+
+ {useEntityList({
+ keyval: entities,
+ field: 'childs',
+ fn: () => ,
+ })}
+
+ );
+ };
+ const App = () => {
+ return (
+
+ {useEntityList(entities, () => (
+
+ ))}
+
+ );
+ };
+ render();
+ const result = screen.getByTestId('list');
+ /** select first (deep child) entity with that id */
+ const targetButton = screen.getAllByTestId('entity-c')[0];
+ fireEvent.click(targetButton);
+ expect(result.innerHTML).toBe(
+ renderToString(
+ <>
+
+
+
+
+ >,
+ ),
+ );
+ expect(entities.$items.getState()).toEqual([
+ { id: 'a', childs: [{ id: 'b', childs: [] }] },
+ { id: 'c', childs: [] },
+ ]);
+});
diff --git a/packages/react/src/__tests__/useEntityItem.test.tsx b/packages/react/src/__tests__/useEntityItem.test.tsx
new file mode 100644
index 0000000..ae80acf
--- /dev/null
+++ b/packages/react/src/__tests__/useEntityItem.test.tsx
@@ -0,0 +1,111 @@
+import { expect, test } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { createStore } from 'effector';
+import { keyval } from '@effector/model';
+import { useEntityItem, EntityProvider } from '@effector/model-react';
+
+test('with key from provider', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore('');
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ };
+ });
+ entities.edit.add({ id: 'a', value: 'A' });
+ const Entity = () => {
+ const { value } = useEntityItem(entities);
+ return {value}
;
+ };
+ render(
+
+
+ ,
+ );
+ const result = screen.getByTestId('entity');
+ expect(result.innerText).toBe('A');
+});
+test('with key from argument', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore('');
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ };
+ });
+ entities.edit.add({ id: 'a', value: 'A' });
+ const Entity = ({ id }: { id: string }) => {
+ const { value } = useEntityItem(entities, id);
+ return {value}
;
+ };
+ render();
+ const result = screen.getByTestId('entity');
+ expect(result.innerText).toBe('A');
+});
+test('render default item when key is not found', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore('default');
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ };
+ });
+ const Entity = ({ id }: { id: string }) => {
+ const { value } = useEntityItem(entities, id);
+ return {value}
;
+ };
+ render();
+ const result = screen.getByTestId('entity');
+ expect(result.innerText).toBe('default');
+});
+test('nested components use nearest provider', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore('');
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ };
+ });
+ entities.edit.add([
+ { id: 'a', value: 'A' },
+ { id: 'b', value: 'B' },
+ ]);
+ const EntityA = () => {
+ const { value } = useEntityItem(entities);
+ return (
+
+ {value}
+
+
+
+
+ );
+ };
+ const EntityB = () => {
+ const { value } = useEntityItem(entities);
+ return {value}
;
+ };
+ render(
+
+
+ ,
+ );
+ const result = screen.getByTestId('entity');
+ expect(result.innerHTML).toMatchInlineSnapshot(`"AB
"`);
+});
diff --git a/packages/react/src/__tests__/useEntityList.test.tsx b/packages/react/src/__tests__/useEntityList.test.tsx
new file mode 100644
index 0000000..7569a76
--- /dev/null
+++ b/packages/react/src/__tests__/useEntityList.test.tsx
@@ -0,0 +1,97 @@
+import { expect, test } from 'vitest';
+import { renderToString } from 'react-dom/server';
+import { render, screen } from '@testing-library/react';
+import { createStore } from 'effector';
+import { keyval, type KeyvalWithState } from '@effector/model';
+import { useEntityList, useEntityItem } from '@effector/model-react';
+
+test('useEntityList', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore('');
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ };
+ });
+ entities.edit.add([
+ { id: 'a', value: 'A' },
+ { id: 'b', value: 'B' },
+ ]);
+ const Entity = () => {
+ const { value } = useEntityItem(entities);
+ return {value}
;
+ };
+ const App = () => (
+
+ {useEntityList(entities, () => (
+
+ ))}
+
+ );
+ render();
+ const result = screen.getByTestId('list');
+ expect(result.innerHTML).toMatchInlineSnapshot(`"A
B
"`);
+});
+
+test('tree support', () => {
+ type Entity = {
+ id: string;
+ childs: Entity[];
+ };
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const childs = keyval(entities) as KeyvalWithState;
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ childs,
+ },
+ };
+ });
+ entities.edit.add([
+ { id: 'a', childs: [{ id: 'e', childs: [] }] },
+ { id: 'b', childs: [{ id: 'c', childs: [{ id: 'd', childs: [] }] }] },
+ ]);
+ const Entity = () => {
+ const { id } = useEntityItem(entities);
+ return (
+
+ {useEntityList({
+ keyval: entities,
+ field: 'childs',
+ fn: () => ,
+ })}
+
+ );
+ };
+ const App = () => {
+ return (
+
+ {useEntityList(entities, () => (
+
+ ))}
+
+ );
+ };
+ render();
+ const result = screen.getByTestId('list');
+ expect(result.innerHTML).toBe(
+ renderToString(
+ <>
+
+
+ >,
+ ),
+ );
+});
diff --git a/packages/react/src/__tests__/useItemApi.test.tsx b/packages/react/src/__tests__/useItemApi.test.tsx
new file mode 100644
index 0000000..ab73089
--- /dev/null
+++ b/packages/react/src/__tests__/useItemApi.test.tsx
@@ -0,0 +1,76 @@
+import { expect, test } from 'vitest';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { createEvent, createStore, sample } from 'effector';
+import { keyval } from '@effector/model';
+import {
+ useEntityItem,
+ useItemApi,
+ EntityProvider,
+} from '@effector/model-react';
+
+test('with key from provider', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore(0);
+ const inc = createEvent();
+ sample({ clock: inc, source: $value, target: $value, fn: (x) => x + 1 });
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ api: { inc },
+ optional: ['value'],
+ };
+ });
+ entities.edit.add({ id: 'a' });
+ const Entity = () => {
+ const { id, value } = useEntityItem(entities);
+ const { inc } = useItemApi(entities);
+ return (
+
+ );
+ };
+ render(
+
+
+ ,
+ );
+ const result = screen.getByTestId('entity');
+ fireEvent.click(result);
+ expect(result.innerText).toMatchInlineSnapshot(`"a: 1"`);
+});
+test('with key from argument', () => {
+ const entities = keyval(() => {
+ const $id = createStore('');
+ const $value = createStore(0);
+ const inc = createEvent();
+ sample({ clock: inc, source: $value, target: $value, fn: (x) => x + 1 });
+ return {
+ key: 'id',
+ state: {
+ id: $id,
+ value: $value,
+ },
+ api: { inc },
+ optional: ['value'],
+ };
+ });
+ entities.edit.add({ id: 'a' });
+ const Entity = ({ id }: { id: string }) => {
+ const { value } = useEntityItem(entities, id);
+ const { inc } = useItemApi(entities, id);
+ return (
+
+ );
+ };
+ render();
+ const result = screen.getByTestId('entity');
+ fireEvent.click(result);
+ expect(result.innerText).toMatchInlineSnapshot(`"a: 1"`);
+});
diff --git a/packages/react/src/index.tsx b/packages/react/src/index.tsx
index 4b3296a..6be6fca 100644
--- a/packages/react/src/index.tsx
+++ b/packages/react/src/index.tsx
@@ -14,15 +14,15 @@ import {
} from 'effector';
import { useList, useStoreMap, useUnit } from 'effector-react';
-import type {
- Model,
- Instance,
- Keyval,
- StoreDef,
- EventDef,
- EffectDef,
- AnyDef,
- Show,
+import {
+ type Model,
+ type Instance,
+ type Keyval,
+ type StoreDef,
+ type EventDef,
+ type EffectDef,
+ type AnyDef,
+ isKeyval,
} from '@effector/model';
import { spawn } from '@effector/model';
@@ -30,6 +30,7 @@ type ModelStack =
| {
type: 'entity';
model: Keyval;
+ clone: Keyval | null;
value: string | number;
parent: ModelStack | null;
}
@@ -47,14 +48,15 @@ export function EntityProvider({
value,
children,
}: {
- model: Keyval;
+ model: Keyval;
value: string | number;
children: ReactNode;
}) {
const currentStack = useContext(ModelStackContext);
const nextStack = {
type: 'entity' as const,
- model,
+ model: model.cloneOf || model,
+ clone: model.isClone ? model : null,
value,
parent: currentStack,
};
@@ -135,73 +137,42 @@ export function ModelProvider<
);
}
-export function useModel(
- model: Model,
-): [
- state: Show<
- {
- [K in keyof Input]: Input[K] extends Store
- ? V
- : Input[K] extends StoreDef
- ? V
- : Input[K] extends Event
- ? (params: V) => V
- : Input[K] extends EventDef
- ? (params: V) => V
- : Input[K] extends Effect
- ? (params: V) => Promise
- : Input[K] extends EffectDef
- ? (params: V) => Promise
- : Input[K] extends (params: infer V) => infer D
- ? (params: V) => Promise>
- : Input[K];
- } & {
- [K in keyof T]: T[K] extends Store ? V : never;
- }
- >,
- api: {
- [K in keyof Api]: Api[K] extends Event
- ? (params: V) => V
- : Api[K] extends Effect
- ? (params: V) => Promise
- : never;
- },
-] {
- const stack = useContext(ModelStackContext);
- let currentStack = stack;
- let instance: Instance | undefined;
- while (instance === undefined && currentStack) {
- if (currentStack.model === model) {
- instance = currentStack.value as Instance;
- }
- currentStack = currentStack.parent;
- }
- if (instance === undefined)
- throw Error('model not found, add ModelProvider first');
- const state = useUnit(instance.props as any);
- const api = useUnit(instance.api as any);
- return [state as any, api as any];
-}
-
function useGetKeyvalKey(
args:
| [keyval: Keyval]
| [keyval: Keyval, key: string | number],
+ allowUndefinedKey?: false,
+): [keyval: Keyval, key: string | number];
+function useGetKeyvalKey(
+ args:
+ | [keyval: Keyval]
+ | [keyval: Keyval, key: string | number],
+ allowUndefinedKey: true,
+): [keyval: Keyval, key: string | number | void];
+function useGetKeyvalKey(
+ args:
+ | [keyval: Keyval]
+ | [keyval: Keyval, key: string | number],
+ allowUndefinedKey: boolean = false,
): [keyval: Keyval, key: string | number] {
if (args.length === 1) {
- const [keyval] = args;
+ let [keyval] = args;
const stack = useContext(ModelStackContext);
let currentStack = stack;
let key: string | number | undefined;
while (key === undefined && currentStack) {
if (currentStack.model === keyval) {
key = currentStack.value as string | number;
+ if (currentStack.type === 'entity' && currentStack.clone) {
+ // @ts-expect-error typecast
+ keyval = currentStack.clone;
+ }
}
currentStack = currentStack.parent;
}
- if (key === undefined)
+ if (key === undefined && !allowUndefinedKey)
throw Error('model not found, add EntityProvider first');
- return [keyval, key];
+ return [keyval, key!];
} else {
return args;
}
@@ -230,7 +201,7 @@ export function useEntityItem(
});
if (idx === -1) {
// NOTE probably need to throw error here
- return keyval.defaultState;
+ return keyval.defaultState();
}
return result as T;
}
@@ -238,31 +209,61 @@ export function useEntityItem(
export function useEntityList(
keyval: Keyval,
View: () => ReactNode,
+): ReactNode;
+export function useEntityList(config: {
+ keyval: Keyval;
+ field: keyof T;
+ fn: () => ReactNode;
+}): ReactNode;
+export function useEntityList(
+ ...[keyvalOrConfig, viewFn]:
+ | [keyval: Keyval, View: () => ReactNode]
+ | [
+ config: {
+ keyval: Keyval;
+ field: keyof T;
+ fn: () => ReactNode;
+ },
+ ]
) {
- return useList(keyval.$keys, (key) => (
-
-
-
- ));
-}
+ let View: () => ReactNode;
+ let keyvalToIterate: Keyval;
+ if (isKeyval(keyvalOrConfig)) {
+ [keyvalToIterate, View] = [keyvalOrConfig, viewFn!];
+ } else {
+ const {
+ keyval: keyvalRaw,
+ field,
+ fn,
+ } = keyvalOrConfig as Exclude<
+ typeof keyvalOrConfig,
+ Keyval
+ >;
+ View = fn;
+ /**
+ * keyvalRaw is always a root keyval, used as a tag
+ * keyval is current instance in which computation will happens
+ * instanceKeyval is child instance from a field
+ */
+ const [keyval, currentKey] = useGetKeyvalKey([keyvalRaw]);
+ const instanceKeyval = useStoreMap({
+ store: keyval.__$listState,
+ keys: [currentKey, field],
+ fn({ instances, keys }, [key, field]) {
+ const idx = keys.findIndex((e) => e === key);
+ return instances[idx].keyvalShape[field];
+ },
+ });
+ keyvalToIterate = instanceKeyval;
+ }
-export function useEntityByKey(
- keyval: Keyval,
- key: string | number,
- View: (params: { value: T }) => ReactNode,
-) {
- const idx = useStoreMap({
- store: keyval.$keys,
- keys: [key],
- fn: (keys, [value]) => keys.indexOf(value),
+ return useList(keyvalToIterate.$keys, (key) => {
+ return (
+
+
+
+ );
});
- const result = useStoreMap({
- store: keyval.$items,
- keys: [idx, key],
- fn: (values, [idx]) => (idx === -1 ? null : values[idx]),
- });
- if (idx === -1) return null;
- return ;
}
export function useItemApi(
@@ -296,7 +297,7 @@ export function useEditItemField(
| [keyval: Keyval]
| [keyval: Keyval, key: string | number]
): {
- [K in keyof Input]: (params: Input[K]) => void;
+ [K in keyof Input]-?: (params: Input[K]) => void;
} {
const [keyval, key] = useGetKeyvalKey(args);
const commonApi = useUnit(keyval.editField);
@@ -314,3 +315,10 @@ export function useEditItemField(
return result;
}, [keyval, key, commonApi]);
}
+
+export function useEditKeyval(
+ keyval: Keyval,
+) {
+ const [currentKeyval] = useGetKeyvalKey([keyval], true);
+ return useUnit(currentKeyval.edit);
+}
diff --git a/packages/react/vite.config.mts b/packages/react/vite.config.mts
index d9b365e..68f7728 100644
--- a/packages/react/vite.config.mts
+++ b/packages/react/vite.config.mts
@@ -1,7 +1,18 @@
+import { fileURLToPath } from 'url';
+import { dirname, resolve } from 'path';
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
- test: { typecheck: { ignoreSourceErrors: true } },
+ test: {
+ typecheck: { ignoreSourceErrors: true },
+ setupFiles: [relativePath('./src/__tests__/testsSetup.ts')],
+ environment: 'happy-dom',
+ include: [relativePath('./src/__tests__/**/*.test.tsx')],
+ },
plugins: [tsconfigPaths()],
});
+
+function relativePath(path: string) {
+ return resolve(dirname(fileURLToPath(import.meta.url)), path);
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 985015b..9265cea 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,7 +62,7 @@ importers:
version: 19.5.7(@babel/core@7.25.2)(@babel/traverse@7.25.3)(@types/babel__core@7.20.5)(@types/node@20.14.15)(nx@19.5.7)(ts-node@10.9.1(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)
'@nrwl/vite':
specifier: 19.5.7
- version: 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0))
+ version: 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0))
'@nrwl/web':
specifier: 19.5.7
version: 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)
@@ -215,7 +215,7 @@ importers:
version: 5.0.1(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))
vitest:
specifier: ^2.0.5
- version: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0)
+ version: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0)
apps/food-order:
devDependencies:
@@ -251,6 +251,19 @@ importers:
specifier: ^4.2.1
version: 4.5.10(@types/node@20.14.15)(sugarss@2.0.0)
+ apps/tree-todo-list:
+ dependencies:
+ effector-action:
+ specifier: ^1.1.0
+ version: 1.1.0(effector@23.3.0)(patronum@2.3.0(effector@23.3.0))
+ devDependencies:
+ '@vitejs/plugin-react':
+ specifier: ^3.1.0
+ version: 3.1.0(vite@4.5.10(@types/node@20.14.15)(sugarss@2.0.0))
+ vite:
+ specifier: ^4.2.1
+ version: 4.5.10(@types/node@20.14.15)(sugarss@2.0.0)
+
packages/core:
dependencies:
effector:
@@ -260,11 +273,24 @@ importers:
packages/react:
dependencies:
effector:
- specifier: ^23.2.2
- version: 23.2.2
+ specifier: ^23.3.0
+ version: 23.3.0
+ devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^6.6.3
+ version: 6.6.3
+ '@testing-library/react':
+ specifier: ^16.3.0
+ version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ happy-dom:
+ specifier: ^17.4.4
+ version: 17.4.4
packages:
+ '@adobe/css-tools@4.4.2':
+ resolution: {integrity: sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==}
+
'@ampproject/remapping@2.2.0':
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
@@ -1938,6 +1964,29 @@ packages:
'@swc/helpers@0.5.12':
resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==}
+ '@testing-library/dom@10.4.0':
+ resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.6.3':
+ resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.0':
+ resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
@@ -1957,6 +2006,9 @@ packages:
'@tybys/wasm-util@0.9.0':
resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -2278,6 +2330,9 @@ packages:
aria-query@5.1.3:
resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
arr-diff@4.0.0:
resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==}
engines: {node: '>=0.10.0'}
@@ -2591,6 +2646,10 @@ packages:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
+ chalk@3.0.0:
+ resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
+ engines: {node: '>=8'}
+
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -2791,6 +2850,9 @@ packages:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -2941,6 +3003,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
detect-indent@6.1.0:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
@@ -2973,6 +3039,12 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dom-serializer@0.2.2:
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
@@ -3021,6 +3093,12 @@ packages:
duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+ effector-action@1.1.0:
+ resolution: {integrity: sha512-CHzv5LB6dW81GRiQKoVIxVEDeRPVTKipy2LWH8AjcSAV7Dmv7x9fvU0SQt6FmsmMV8LHw3v2jomL3kpVp3yNdA==}
+ peerDependencies:
+ effector: '>=23'
+ patronum: '>=2.1.0'
+
effector-react@23.3.0:
resolution: {integrity: sha512-QR0+x1EnbiWhO80Yc0GVF+I9xCYoxBm3t+QLB5Wg+1uY1Q1BrSWDmKvJaJJZ/+9BU4RAr25yS5J2EkdWnicu8g==}
engines: {node: '>=11.0.0'}
@@ -3028,10 +3106,6 @@ packages:
effector: ^23.0.0
react: '>=16.8.0 <20.0.0'
- effector@23.2.2:
- resolution: {integrity: sha512-gzwATi9pgZQx0TNhM2LESmoUpEO+vhibLZPCvVzi7spMvKFwKnfJV2PFj4xqNFFSC35TXaznx30ne62dCQ6ZRQ==}
- engines: {node: '>=11.0.0'}
-
effector@23.3.0:
resolution: {integrity: sha512-ZnQ3POaNARlxT9+kxrK58PO/xmStBdxfPq0rceglENg8Ryxx/yx+1RsV/ziznrFPhLkZYc7NdDA1OKxnMW98/g==}
engines: {node: '>=11.0.0'}
@@ -3763,6 +3837,10 @@ packages:
gud@1.0.0:
resolution: {integrity: sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==}
+ happy-dom@17.4.4:
+ resolution: {integrity: sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==}
+ engines: {node: '>=18.0.0'}
+
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -4515,6 +4593,10 @@ packages:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.27.0:
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
engines: {node: '>=12'}
@@ -5383,6 +5465,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
pretty-format@29.7.0:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -5430,6 +5516,9 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
@@ -5524,6 +5613,10 @@ packages:
resolution: {integrity: sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==}
engines: {node: '>=4'}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
reflect.getprototypeof@1.0.6:
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
@@ -6643,10 +6736,18 @@ packages:
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
whatwg-encoding@2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}
+ whatwg-mimetype@3.0.0:
+ resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+ engines: {node: '>=12'}
+
which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
@@ -6754,6 +6855,8 @@ packages:
snapshots:
+ '@adobe/css-tools@4.4.2': {}
+
'@ampproject/remapping@2.2.0':
dependencies:
'@jridgewell/gen-mapping': 0.1.1
@@ -8356,9 +8459,9 @@ snapshots:
- '@swc/core'
- debug
- '@nrwl/vite@19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0))':
+ '@nrwl/vite@19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0))':
dependencies:
- '@nx/vite': 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0))
+ '@nx/vite': 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0))
transitivePeerDependencies:
- '@babel/traverse'
- '@swc-node/register'
@@ -8624,9 +8727,9 @@ snapshots:
- typescript
- verdaccio
- '@nx/vite@19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0))':
+ '@nx/vite@19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0))':
dependencies:
- '@nrwl/vite': 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0))
+ '@nrwl/vite': 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.15)(sugarss@2.0.0))(vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0))
'@nx/devkit': 19.5.7(nx@19.5.7)
'@nx/js': 19.5.7(@babel/traverse@7.25.3)(@types/node@20.14.15)(nx@19.5.7)(typescript@5.5.4)
'@phenomnomnominal/tsquery': 5.0.1(typescript@5.5.4)
@@ -8634,7 +8737,7 @@ snapshots:
enquirer: 2.3.6
tsconfig-paths: 4.2.0
vite: 5.4.0(@types/node@20.14.15)(sugarss@2.0.0)
- vitest: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0)
+ vitest: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0)
transitivePeerDependencies:
- '@babel/traverse'
- '@swc-node/register'
@@ -8893,6 +8996,37 @@ snapshots:
dependencies:
tslib: 2.5.0
+ '@testing-library/dom@10.4.0':
+ dependencies:
+ '@babel/code-frame': 7.24.7
+ '@babel/runtime': 7.25.0
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ chalk: 4.1.2
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.6.3':
+ dependencies:
+ '@adobe/css-tools': 4.4.2
+ aria-query: 5.1.3
+ chalk: 3.0.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ lodash: 4.17.21
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@testing-library/dom': 10.4.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@trysound/sax@0.2.0': {}
'@tsconfig/node10@1.0.11': {}
@@ -8907,6 +9041,8 @@ snapshots:
dependencies:
tslib: 2.5.0
+ '@types/aria-query@5.0.4': {}
+
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.21.4
@@ -9206,7 +9342,7 @@ snapshots:
pathe: 1.1.2
sirv: 2.0.4
tinyrainbow: 1.2.0
- vitest: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0)
+ vitest: 2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0)
'@vitest/utils@2.0.5':
dependencies:
@@ -9300,6 +9436,10 @@ snapshots:
dependencies:
deep-equal: 2.2.0
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
arr-diff@4.0.0: {}
arr-flatten@1.1.0: {}
@@ -9682,6 +9822,11 @@ snapshots:
escape-string-regexp: 1.0.5
supports-color: 5.5.0
+ chalk@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -9883,6 +10028,8 @@ snapshots:
css-what@6.1.0: {}
+ css.escape@1.5.1: {}
+
cssesc@3.0.0: {}
cssnano-preset-default@5.2.14(postcss@8.4.41):
@@ -10059,6 +10206,8 @@ snapshots:
delayed-stream@1.0.0: {}
+ dequal@2.0.3: {}
+
detect-indent@6.1.0: {}
detect-node-es@1.1.0: {}
@@ -10086,6 +10235,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dom-serializer@0.2.2:
dependencies:
domelementtype: 2.3.0
@@ -10136,14 +10289,17 @@ snapshots:
duplexer@0.1.2: {}
+ effector-action@1.1.0(effector@23.3.0)(patronum@2.3.0(effector@23.3.0)):
+ dependencies:
+ effector: 23.3.0
+ patronum: 2.3.0(effector@23.3.0)
+
effector-react@23.3.0(effector@23.3.0)(react@18.3.1):
dependencies:
effector: 23.3.0
react: 18.3.1
use-sync-external-store: 1.2.0(react@18.3.1)
- effector@23.2.2: {}
-
effector@23.3.0: {}
ejs@3.1.9:
@@ -11152,6 +11308,11 @@ snapshots:
gud@1.0.0: {}
+ happy-dom@17.4.4:
+ dependencies:
+ webidl-conversions: 7.0.0
+ whatwg-mimetype: 3.0.0
+
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -11848,6 +12009,8 @@ snapshots:
dependencies:
yallist: 4.0.0
+ lz-string@1.5.0: {}
+
magic-string@0.27.0:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -12750,6 +12913,12 @@ snapshots:
prettier@3.3.3: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
pretty-format@29.7.0:
dependencies:
'@jest/schemas': 29.6.3
@@ -12790,6 +12959,8 @@ snapshots:
react-is@16.13.1: {}
+ react-is@17.0.2: {}
+
react-is@18.2.0: {}
react-number-format@5.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
@@ -12900,6 +13071,11 @@ snapshots:
indent-string: 3.2.0
strip-indent: 2.0.0
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
reflect.getprototypeof@1.0.6:
dependencies:
call-bind: 1.0.7
@@ -14196,7 +14372,7 @@ snapshots:
fsevents: 2.3.3
sugarss: 2.0.0
- vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(sugarss@2.0.0):
+ vitest@2.0.5(@types/node@20.14.15)(@vitest/ui@2.0.5)(happy-dom@17.4.4)(sugarss@2.0.0):
dependencies:
'@ampproject/remapping': 2.3.0
'@vitest/expect': 2.0.5
@@ -14220,6 +14396,7 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.15
'@vitest/ui': 2.0.5(vitest@2.0.5)
+ happy-dom: 17.4.4
transitivePeerDependencies:
- less
- lightningcss
@@ -14238,10 +14415,14 @@ snapshots:
dependencies:
defaults: 1.0.4
+ webidl-conversions@7.0.0: {}
+
whatwg-encoding@2.0.0:
dependencies:
iconv-lite: 0.6.3
+ whatwg-mimetype@3.0.0: {}
+
which-boxed-primitive@1.0.2:
dependencies:
is-bigint: 1.0.4
diff --git a/tsconfig.base.json b/tsconfig.base.json
index d549625..1115365 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -13,8 +13,8 @@
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
- "@effector/model": ["packages/core/index.ts"],
- "@effector/model-react": ["packages/react/index.ts"]
+ "@effector/model": ["./packages/core/index.ts"],
+ "@effector/model-react": ["./packages/react/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]