Repository: effector/effector (ts)

Issue:
`serialize: ignore` in `createStore` serializes after fork with `values`
<!--
Please fill out this entire template so that we can evaluate your proposal as quickly as possible.
-->

## Proposal

```ts
const $notSerializableInJson = createStore(someApiClient, { serialize: 'omit' })

const scope = fork({values: [
	[$notSerializableInJson, {foo: async () => 'baz'}],
]})

// Do something...

const data = serialize(scope)

console.log(data) // {}
```

<!--
  Explain why you need this change and why this cannot be addressed with effector's current set of features.
-->

## Use case
In some frameworks(Next.js, Remix), we cannot return non-JSON data

Current behavior
```ts
const $apiClient = createStore({foo: async () => 'bar'}, {sid: 'client', serialize: 'ignore'})

const scope = fork({values: [
	[$apiClient, {foo: async () => 'baz'}],
]})

// Do something...

const data = serialize(scope)

console.log(data)
/**
 * client: Object {1}
 *  foo: async function foo()
 */

// Current solution
const data = serialize(scope, { ignore: [ $apiClient ] })
console.log(data) // {}
```




Pull request:
`serialize: ignore` must ignore serialize event `fork({values})` is used
Fixes #861

Patch:
diff --git a/src/effector/createUnit.ts b/src/effector/createUnit.ts
index fff0eb22..f6dd0383 100644
--- a/src/effector/createUnit.ts
+++ b/src/effector/createUnit.ts
@@ -193,7 +193,12 @@ export function createEvent<Payload = any>(
   }
   return finalEvent
 }
-function on(store: Store<State>, methodName: string, nodeSet: CommonUnit | CommonUnit[], fn: Function) {
+function on<State>(
+  store: Store<State>,
+  methodName: string,
+  nodeSet: CommonUnit | CommonUnit[],
+  fn: Function,
+) {
   assertNodeSet(nodeSet, methodName, 'first argument')
   assert(isFunction(fn), 'second argument should be a function')
   deprecate(
@@ -205,9 +210,7 @@ function on(store: Store<State>, methodName: string, nodeSet: CommonUnit | Commo
     store.off(trigger)
     getSubscribers(store).set(
       trigger,
-      createSubscription(
-        updateStore(trigger, store, 'on', callARegStack, fn),
-      ),
+      createSubscription(updateStore(trigger, store, 'on', callARegStack, fn)),
     )
   })
   return store
@@ -251,7 +254,9 @@ export function createStore<State>(
         scope: forkPage!,
       }),
     reset(...units: CommonUnit[]) {
-      forEach(units, unit => on(store, '.reset', unit, () => store.defaultState))
+      forEach(units, unit =>
+        on(store, '.reset', unit, () => store.defaultState),
+      )
       return store
     },
     on(nodeSet: CommonUnit | CommonUnit[], fn: Function) {
@@ -341,7 +346,7 @@ export function createStore<State>(
   const customSerialize = !serializeMeta || ignored ? false : serializeMeta
   const sid: string | null = getMeta(store, 'sid')
   if (sid) {
-    if (!ignored) setMeta(store, 'storeChange', true)
+    setMeta(store, 'storeChange', true)
     plainState.sid = sid
 
     if (customSerialize) {
diff --git a/src/effector/fork/createScope.ts b/src/effector/fork/createScope.ts
index c564ce54..d46eeaf7 100644
--- a/src/effector/fork/createScope.ts
+++ b/src/effector/fork/createScope.ts
@@ -71,8 +71,15 @@ export function createScope(unit?: Domain): Scope {
             forkPage.sidValuesMap[sid] = value
 
             const serialize = getMeta(storeNode, 'serialize')
-            if (serialize && serialize !== 'ignore') {
-              forkPage.sidSerializeMap[sid] = serialize.write
+            if (serialize) {
+              if (serialize === 'ignore') {
+                forkPage.sidSerializeSettings.set(sid, {ignore: true})
+              } else {
+                forkPage.sidSerializeSettings.set(sid, {
+                  ignore: false,
+                  write: serialize.write,
+                })
+              }
             }
           }
         }
@@ -104,7 +111,7 @@ export function createScope(unit?: Domain): Scope {
     reg: page,
     sidValuesMap: {},
     sidIdMap: {},
-    sidSerializeMap: {},
+    sidSerializeSettings: new Map(),
     getState(store: StateRef | Store<any>) {
       if ('current' in store) {
         return getPageRef(currentPage, resultScope, null, store).current
diff --git a/src/effector/fork/fork.ts b/src/effector/fork/fork.ts
index 863bff3f..e93faba4 100644
--- a/src/effector/fork/fork.ts
+++ b/src/effector/fork/fork.ts
@@ -13,13 +13,13 @@ type ForkConfig = {
 
 export function fork(
   domainOrConfig?: Domain | ForkConfig,
-  optiionalConfig?: ForkConfig,
+  optionalConfig?: ForkConfig,
 ) {
   let config: ForkConfig | void = domainOrConfig as any
   let domain: Domain
   if (is.domain(domainOrConfig)) {
     domain = domainOrConfig
-    config = optiionalConfig
+    config = optionalConfig
   }
 
   const scope = createScope(domain!)
diff --git a/src/effector/fork/serialize.ts b/src/effector/fork/serialize.ts
index 6910e472..fc38ca9b 100644
--- a/src/effector/fork/serialize.ts
+++ b/src/effector/fork/serialize.ts
@@ -22,7 +22,12 @@ export function serialize(
   forIn(scope.sidValuesMap, (value, sid) => {
     if (includes(ignoredStores, sid)) return
     const id = scope.sidIdMap[sid]
-    const serializer = scope.sidSerializeMap[sid] || noopSerializer
+    const serializeSettings = scope.sidSerializeSettings.get(sid) ?? {
+      ignore: false,
+      write: noopSerializer,
+    }
+    if (serializeSettings.ignore) return
+    const serializer = serializeSettings.write
     // if (!scope.changedStores.has(id)) return
     if (id && id in scope.reg) {
       result[sid] = serializer(scope.reg[id].current)
diff --git a/src/effector/unit.h.ts b/src/effector/unit.h.ts
index 83c57dfe..e010932d 100644
--- a/src/effector/unit.h.ts
+++ b/src/effector/unit.h.ts
@@ -174,7 +174,10 @@ export interface Scope extends Unit {
   /** value could be set only for stores with sid (they can be created by createStore, restore and combine) */
   sidValuesMap: Record<string, any>
   sidIdMap: Record<string, string>
-  sidSerializeMap: Record<string, (p: any) => any>
+  sidSerializeSettings: Map<
+    string,
+    {ignore: true} | {ignore: false; write: (value: any) => any}
+  >
   additionalLinks: Record<string, Node[]>
   handlers: Record<string, (params: unknown) => any>
   fxCount: Node


Test patch:
diff --git a/src/effector/__tests__/fork/serialize.test.ts b/src/effector/__tests__/fork/serialize.test.ts
index c5d6d123..3ab36d9c 100644
--- a/src/effector/__tests__/fork/serialize.test.ts
+++ b/src/effector/__tests__/fork/serialize.test.ts
@@ -69,6 +69,22 @@ test('serialize: ignore', async () => {
   expect(serialize(scope)).toEqual({b: 1})
 })
 
+test('serialize: ignore with fork(values)', async () => {
+  const inc = createEvent()
+  const $a = createStore(0, {sid: 'a', serialize: 'ignore'})
+  const $b = createStore(0, {sid: 'b'})
+  $a.on(inc, x => x + 1)
+  $b.on(inc, x => x + 1)
+
+  const scope = fork({
+    values: [[$a, 100]]
+  })
+
+  await allSettled(inc, {scope})
+
+  expect(serialize(scope)).toEqual({b: 1})
+})
+
 describe('serialize: custom', () => {
   test('base case', async () => {
     expect.assertions(4)