From d1ed23de2c4358c905a4672fffa33acacdee612e Mon Sep 17 00:00:00 2001
From: Yanick Champoux <yanick@babyl.ca>
Date: Mon, 6 Mar 2023 13:04:51 -0500
Subject: [PATCH 1/3] test updux takes in actions

---
 src/actions.test.todo | 18 ------------------
 src/actions.test.ts   | 25 +++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/actions.test.todo b/src/actions.test.todo
index d1e45ee..174cfa2 100644
--- a/src/actions.test.todo
+++ b/src/actions.test.todo
@@ -5,24 +5,6 @@ import { action } from './actions.js';
 import { Updux } from './Updux.js';
 
 
-test('Updux config accepts actions', () => {
-    const foo = new Updux({
-        actions: {
-            one: action('one', (x) => ({ x })),
-            two: action('two', (x) => x),
-        },
-    });
-
-    expect(Object.keys(foo.actions)).toHaveLength(2);
-
-    expect(foo.actions.one).toBeTypeOf('function');
-    expect(foo.actions.one('potato')).toEqual({
-        type: 'one',
-        payload: {
-            x: 'potato',
-        },
-    });
-});
 
 
 test('throw if double action', () => {
diff --git a/src/actions.test.ts b/src/actions.test.ts
index c051266..99fdeb9 100644
--- a/src/actions.test.ts
+++ b/src/actions.test.ts
@@ -39,3 +39,28 @@ test('subduxes actions', () => {
     expect(foo.actions.bar(2)).toHaveProperty('type', 'bar');
     expect(foo.actions.baz()).toHaveProperty('type', 'baz');
 });
+
+test('Updux config accepts actions', () => {
+    const foo = new Updux({
+        actions: {
+            one: createAction(
+                'one',
+                withPayload((x) => ({ x })),
+            ),
+            two: createAction(
+                'two',
+                withPayload((x) => x),
+            ),
+        },
+    });
+
+    expect(Object.keys(foo.actions)).toHaveLength(2);
+
+    expect(foo.actions.one).toBeTypeOf('function');
+    expect(foo.actions.one('potato')).toEqual({
+        type: 'one',
+        payload: {
+            x: 'potato',
+        },
+    });
+});

From 000ca9871ab2bc132c3f8800b5872d379f98a5c5 Mon Sep 17 00:00:00 2001
From: Yanick Champoux <yanick@babyl.ca>
Date: Mon, 6 Mar 2023 15:07:24 -0500
Subject: [PATCH 2/3] duplicate actions

---
 src/Updux.ts          | 14 ++++++++------
 src/actions.test.todo | 18 ------------------
 src/actions.test.ts   | 36 ++++++++++++++++++++++++++++++++++++
 src/actions.ts        |  4 ++--
 src/buildActions.ts   | 33 +++++++++++++++++++++++++++++++++
 5 files changed, 79 insertions(+), 26 deletions(-)
 create mode 100644 src/buildActions.ts

diff --git a/src/Updux.ts b/src/Updux.ts
index 7aad5e3..9fd7513 100644
--- a/src/Updux.ts
+++ b/src/Updux.ts
@@ -8,6 +8,7 @@ import {
 import { configureStore, Reducer, createAction } from '@reduxjs/toolkit';
 import { withPayload } from './actions.js';
 import { AggregateActions, Dux, UnionToIntersection } from './types.js';
+import { buildActions } from './buildActions.js';
 
 type ActionCreator = ReturnType<typeof createAction>;
 
@@ -32,7 +33,9 @@ export default class Updux<
     > = {};
     #subduxes: SUBDUXES;
 
-    #actions: Record<string, ActionCreator>;
+    #name: string;
+
+    #actions: AggregateActions<T_LocalActions, SUBDUXES>;
 
     constructor(
         config: Partial<{
@@ -45,13 +48,12 @@ export default class Updux<
         this.#localInitial = config.initial ?? ({} as T_LocalState);
         this.#localActions = config.actions ?? ({} as T_LocalActions);
         this.#subduxes = config.subduxes ?? ({} as SUBDUXES);
+
+        this.#actions = buildActions(this.#localActions, this.#subduxes);
     }
 
-    get actions(): AggregateActions<T_LocalActions, SUBDUXES> {
-        return R.mergeAll([
-            this.#localActions,
-            ...Object.values(this.#subduxes).map(R.pathOr(['actions'], {})),
-        ]) as any;
+    get actions() {
+        return this.#actions;
     }
 
     // TODO memoize?
diff --git a/src/actions.test.todo b/src/actions.test.todo
index 174cfa2..e17edf4 100644
--- a/src/actions.test.todo
+++ b/src/actions.test.todo
@@ -7,24 +7,6 @@ import { Updux } from './Updux.js';
 
 
 
-test('throw if double action', () => {
-    expect(
-        () =>
-            new Updux({
-                actions: {
-                    foo: action('foo'),
-                },
-                subduxes: {
-                    beta: {
-                        actions: {
-                            foo: action('foo'),
-                        },
-                    },
-                },
-            }),
-    ).toThrow(/action 'foo' already defined/);
-});
-
 test('action definition shortcut', () => {
     const foo = new Updux({
         actions: {
diff --git a/src/actions.test.ts b/src/actions.test.ts
index 99fdeb9..c39b8be 100644
--- a/src/actions.test.ts
+++ b/src/actions.test.ts
@@ -64,3 +64,39 @@ test('Updux config accepts actions', () => {
         },
     });
 });
+
+test('throw if double action', () => {
+    expect(
+        () =>
+            new Updux({
+                actions: {
+                    foo: createAction('foo'),
+                },
+                subduxes: {
+                    beta: {
+                        actions: {
+                            foo: createAction('foo'),
+                        },
+                    },
+                },
+            }),
+    ).toThrow(/action 'foo' defined both locally and in subdux 'beta'/);
+
+    expect(
+        () =>
+            new Updux({
+                subduxes: {
+                    gamma: {
+                        actions: {
+                            foo: createAction('foo'),
+                        },
+                    },
+                    beta: {
+                        actions: {
+                            foo: createAction('foo'),
+                        },
+                    },
+                },
+            }),
+    ).toThrow(/action 'foo' defined both in subduxes 'gamma' and 'beta'/);
+});
diff --git a/src/actions.ts b/src/actions.ts
index 9bd5453..f8d7dcf 100644
--- a/src/actions.ts
+++ b/src/actions.ts
@@ -3,7 +3,7 @@ import { createAction } from '@reduxjs/toolkit';
 export { createAction } from '@reduxjs/toolkit';
 
 interface WithPayload {
-    (): <P>(input: P) => { payload: P };
+    <P>(): (input: P) => { payload: P };
     <P, A extends any[]>(prepare: (...args: A) => P): (...input: A) => {
         payload: P;
     };
@@ -11,5 +11,5 @@ interface WithPayload {
 
 export const withPayload: WithPayload = ((prepare) =>
     (...input) => ({
-        payload: prepare ? prepare(...input) : input,
+        payload: prepare ? prepare(...input) : input[0],
     })) as any;
diff --git a/src/buildActions.ts b/src/buildActions.ts
new file mode 100644
index 0000000..21f0cef
--- /dev/null
+++ b/src/buildActions.ts
@@ -0,0 +1,33 @@
+import * as R from 'remeda';
+
+export function buildActions(localActions, subduxes) {
+    let actions: Record<string, string> = {};
+
+    for (const slice in subduxes) {
+        const subdux = subduxes[slice].actions;
+
+        if (!subdux) continue;
+
+        for (const a in subdux) {
+            if (actions[a]) {
+                throw new Error(
+                    `action '${a}' defined both in subduxes '${actions[a]}' and '${slice}'`,
+                );
+            }
+            actions[a] = slice;
+        }
+    }
+
+    for (const a in localActions) {
+        if (actions[a]) {
+            throw new Error(
+                `action '${a}' defined both locally and in subdux '${actions[a]}'`,
+            );
+        }
+    }
+
+    return R.mergeAll([
+        localActions,
+        ...Object.values(subduxes).map(R.pathOr<any, any>(['actions'], {})),
+    ]) as any;
+}

From 88808507ad9c9652e05143163e864e435b45b444 Mon Sep 17 00:00:00 2001
From: Yanick Champoux <yanick@babyl.ca>
Date: Mon, 6 Mar 2023 16:07:22 -0500
Subject: [PATCH 3/3] all action tests are done

---
 src/Updux.ts          |  7 ++++++-
 src/actions.test.todo | 23 -----------------------
 src/actions.test.ts   | 15 +++++++++++++++
 3 files changed, 21 insertions(+), 24 deletions(-)
 delete mode 100644 src/actions.test.todo

diff --git a/src/Updux.ts b/src/Updux.ts
index 9fd7513..3932ec8 100644
--- a/src/Updux.ts
+++ b/src/Updux.ts
@@ -5,7 +5,12 @@ import {
     DeepPartial,
     Action,
 } from 'redux';
-import { configureStore, Reducer, createAction } from '@reduxjs/toolkit';
+import {
+    configureStore,
+    Reducer,
+    createAction,
+    PrepareAction,
+} from '@reduxjs/toolkit';
 import { withPayload } from './actions.js';
 import { AggregateActions, Dux, UnionToIntersection } from './types.js';
 import { buildActions } from './buildActions.js';
diff --git a/src/actions.test.todo b/src/actions.test.todo
deleted file mode 100644
index e17edf4..0000000
--- a/src/actions.test.todo
+++ /dev/null
@@ -1,23 +0,0 @@
-import { test, expect } from 'vitest';
-
-import { action } from './actions.js';
-
-import { Updux } from './Updux.js';
-
-
-
-
-test('action definition shortcut', () => {
-    const foo = new Updux({
-        actions: {
-            foo: null,
-            bar: (x) => ({ x }),
-        },
-    });
-
-    expect(foo.actions.foo('hello')).toEqual({ type: 'foo', payload: 'hello' });
-    expect(foo.actions.bar('hello')).toEqual({
-        type: 'bar',
-        payload: { x: 'hello' },
-    });
-});
diff --git a/src/actions.test.ts b/src/actions.test.ts
index c39b8be..b74eb21 100644
--- a/src/actions.test.ts
+++ b/src/actions.test.ts
@@ -100,3 +100,18 @@ test('throw if double action', () => {
             }),
     ).toThrow(/action 'foo' defined both in subduxes 'gamma' and 'beta'/);
 });
+
+test.todo('action definition shortcut', () => {
+    const foo = new Updux({
+        actions: {
+            foo: undefined,
+            bar: (x) => ({ x }),
+        },
+    });
+
+    expect(foo.actions.foo('hello')).toEqual({ type: 'foo', payload: 'hello' });
+    expect(foo.actions.bar('hello')).toEqual({
+        type: 'bar',
+        payload: { x: 'hello' },
+    });
+});