@pihanga2/core 0.3.3 → 0.3.5

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/src/reducer.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Action, Reducer } from "@reduxjs/toolkit"
1
+ import {Action, Reducer} from "@reduxjs/toolkit";
2
2
  import {
3
3
  DispatchF,
4
4
  PiReducer,
@@ -9,110 +9,137 @@ import {
9
9
  ReduceOnceF,
10
10
  ReduxAction,
11
11
  ReduxState,
12
- } from "./types"
13
- import { produce } from "immer"
14
- import { RegisterCardState, UPDATE_STATE_ACTION } from "./card"
15
- import StackTrace from "stacktrace-js"
16
- import { getLogger } from "./logger"
17
- import { Dispatch } from "react"
18
- import { currentRoute } from "./router"
12
+ } from "./types";
13
+ import {produce} from "immer";
14
+ import {RegisterCardState, UPDATE_STATE_ACTION} from "./card";
15
+ import StackTrace from "stacktrace-js";
16
+ import {getLogger} from "./logger";
17
+ import {Dispatch} from "react";
18
+ import {currentRoute} from "./router";
19
19
 
20
- const logger = getLogger("reducer")
20
+ const logger = getLogger("reducer");
21
21
 
22
22
  type ReducerDef<S extends ReduxState, A extends ReduxAction> = {
23
- mapperMany?: ReduceF<S, A>
24
- mapperOnce?: ReduceOnceF<S, A>
25
- priority?: number
26
- key?: string
27
- definedIn?: StackTrace.StackFrame
28
- }
23
+ mapperMulti?: ReduceF<S, A>;
24
+ mapperOnce?: ReduceOnceF<S, A>;
25
+ priority?: number;
26
+ key?: string;
27
+ definedIn?: StackTrace.StackFrame;
28
+ targetMapper?: ReduceF<S, A>;
29
+ };
30
+
31
+ type Source = {
32
+ file?: string;
33
+ line?: number;
34
+ column?: number;
35
+ functionName?: string;
36
+ };
29
37
 
30
38
  export function createReducer(
31
39
  initialState: ReduxState,
32
- dispatcher: Dispatch<any>,
40
+ dispatcher: Dispatch<any>
33
41
  ): [Reducer<ReduxState, Action>, PiReducer] {
34
- const mappings: { [k: string]: ReducerDef<ReduxState, Action>[] } = {}
35
- mappings[UPDATE_STATE_ACTION] = [{ mapperMany: RegisterCardState.reducer }]
42
+ const mappings: {[k: string]: ReducerDef<ReduxState, Action>[]} = {};
43
+ mappings[UPDATE_STATE_ACTION] = [
44
+ {
45
+ mapperMulti: RegisterCardState.reducer,
46
+ key: "@builtin:card:UPDATE_STATE_ACTION",
47
+ },
48
+ ];
36
49
 
37
50
  const delayedDispatcher = (a: any): void => {
38
- setTimeout(() => dispatcher(a), 0)
39
- }
51
+ setTimeout(() => dispatcher(a), 0);
52
+ };
40
53
  const reducer = (
41
54
  state: ReduxState | undefined,
42
- action: Action,
55
+ action: Action
43
56
  ): ReduxState => {
44
- const s = state || initialState
45
- const ra = mappings[action.type]
46
- const rany = mappings["*"]
57
+ const s = state || initialState;
58
+ const ra = mappings[action.type];
59
+ const rany = mappings["*"];
47
60
  if ((!ra || ra.length === 0) && (!rany || rany.length === 0)) {
48
- return s
61
+ return s;
49
62
  }
50
63
 
51
64
  const nextState = produce<ReduxState, ReduxState>(s, (draft) => {
65
+ if (!draft.pihanga) {
66
+ draft.pihanga = {};
67
+ }
68
+ draft.pihanga.reducers = [];
52
69
  if (ra) {
53
- const rout = _reduce(ra, draft, action, delayedDispatcher)
54
- mappings[action.type] = rout
70
+ const rout = _reduce(ra, draft, action, delayedDispatcher);
71
+ mappings[action.type] = rout;
55
72
  }
56
73
  if (rany) {
57
- const rout2 = _reduce(rany, draft, action, delayedDispatcher)
58
- mappings["*"] = rout2
74
+ const rout2 = _reduce(rany, draft, action, delayedDispatcher);
75
+ mappings["*"] = rout2;
59
76
  }
60
- return
61
- })
62
- return nextState
63
- }
77
+ return;
78
+ });
79
+ return nextState;
80
+ };
64
81
 
65
82
  const registerReducer: PiRegisterReducerF = <
66
83
  S extends ReduxState,
67
- A extends ReduxAction,
84
+ A extends ReduxAction
68
85
  >(
69
86
  eventType: string,
70
87
  mapper: ReduceF<S, A>,
71
88
  priority: number = 0,
72
- key: string | undefined = undefined
89
+ key?: string,
90
+ targetMapper?: ReduceF<S, A>
73
91
  ): PiReducerCancelF => {
74
- return addReducer(eventType, { mapperMany: mapper, priority, key })
75
- }
92
+ return addReducer(eventType, {
93
+ mapperMulti: mapper,
94
+ priority,
95
+ key,
96
+ targetMapper,
97
+ });
98
+ };
76
99
 
77
100
  const registerOneShot: PiRegisterOneShotReducerF = <
78
101
  S extends ReduxState,
79
- A extends ReduxAction,
102
+ A extends ReduxAction
80
103
  >(
81
104
  eventType: string,
82
105
  mapper: (state: S, action: A, dispatch: DispatchF) => boolean,
83
106
  priority: number = 0,
84
107
  key: string | undefined = undefined
85
108
  ): PiReducerCancelF => {
86
- return addReducer(eventType, { mapperOnce: mapper, priority, key })
87
- }
109
+ return addReducer(eventType, {mapperOnce: mapper, priority, key});
110
+ };
88
111
 
89
- const nonCancelF = () => {}
112
+ const nonCancelF = () => {};
90
113
 
91
114
  function addReducer<S extends ReduxState, A extends ReduxAction>(
92
115
  eventType: string,
93
- reducerDef: ReducerDef<S, A>,
116
+ reducerDef: ReducerDef<S, A>
94
117
  ): PiReducerCancelF {
95
- let m = mappings[eventType] || []
96
- const key = reducerDef.key
97
- m = removeReducer(key, m)
98
- m.push(reducerDef as any as ReducerDef<ReduxState, Action<any>>) // keep typing happy
99
- m.sort((a, b) => (b.priority || 0) - (a.priority || 0))
100
- mappings[eventType] = m
101
-
102
- const callback = (frames: StackTrace.StackFrame[]) => {
103
- // const stack = frames.map((sf) => `${sf.fileName}:${sf.lineNumber}`)
104
- // console.log(stack)
105
- reducerDef.definedIn = frames[2]
106
- }
118
+ let m = mappings[eventType] || [];
119
+ const key = reducerDef.key;
120
+ m = removeReducer(key, m);
121
+ m.push(reducerDef as any as ReducerDef<ReduxState, Action<any>>); // keep typing happy
122
+ m.sort((a, b) => (b.priority || 0) - (a.priority || 0));
123
+ mappings[eventType] = m;
107
124
 
108
- const errback = (err: any) => {
109
- logger.warn(err.message)
125
+ if (!reducerDef.key) {
126
+ const frames = StackTrace.getSync();
127
+ const sf = _get_source_frame(frames);
128
+ if (sf) {
129
+ // reducerDef.definedIn = sf;
130
+ reducerDef.key = sf.toString();
131
+ } else {
132
+ reducerDef.definedIn = sf;
133
+ console.log(">> cannot find source frame", eventType, frames);
134
+ }
110
135
  }
111
- StackTrace.get().then(callback).catch(errback)
112
- return key ? () => {
113
- let m = mappings[eventType] || []
114
- mappings[eventType] = removeReducer(key, m)
115
- } : nonCancelF
136
+
137
+ return key
138
+ ? () => {
139
+ let m = mappings[eventType] || [];
140
+ mappings[eventType] = removeReducer(key, m);
141
+ }
142
+ : nonCancelF;
116
143
  }
117
144
 
118
145
  const piReducer: PiReducer = {
@@ -120,9 +147,9 @@ export function createReducer(
120
147
  registerOneShot,
121
148
  dispatch: dispatcher,
122
149
  dispatchFromReducer: delayedDispatcher,
123
- }
150
+ };
124
151
 
125
- return [reducer, piReducer]
152
+ return [reducer, piReducer];
126
153
  }
127
154
 
128
155
  function removeReducer(
@@ -130,9 +157,9 @@ function removeReducer(
130
157
  m: ReducerDef<ReduxState, Action>[]
131
158
  ) {
132
159
  if (key) {
133
- return m.filter((r) => r.key !== key)
160
+ return m.filter((r) => r.key !== key);
134
161
  } else {
135
- return m
162
+ return m;
136
163
  }
137
164
  }
138
165
 
@@ -140,23 +167,53 @@ function _reduce(
140
167
  ra: ReducerDef<ReduxState, Action>[],
141
168
  draft: ReduxState,
142
169
  action: Action,
143
- delayedDispatcher: (a: any) => void,
170
+ delayedDispatcher: (a: any) => void
144
171
  ): ReducerDef<ReduxState, Action<any>>[] {
145
- const rout: ReducerDef<ReduxState, Action<any>>[] = []
172
+ const rout: ReducerDef<ReduxState, Action<any>>[] = [];
146
173
  ra.forEach((m) => {
147
174
  try {
148
- if (m.mapperMany) {
149
- m.mapperMany(draft, action, delayedDispatcher)
150
- rout.push(m)
175
+ // if (m.definedIn || m.key) {
176
+ // console.log(">>> executing reducer", action.type, m.key, m.definedIn);
177
+ // }
178
+ if (m.mapperMulti) {
179
+ draft.pihanga?.reducers?.push(m.definedIn || m.key || "unknown");
180
+ m.mapperMulti(draft, action, delayedDispatcher);
181
+ rout.push(m);
151
182
  } else if (m.mapperOnce) {
152
- const flag = m.mapperOnce(draft, action, delayedDispatcher)
183
+ draft.pihanga?.reducers?.push(m.definedIn || m.key || "unknown");
184
+ const flag = m.mapperOnce(draft, action, delayedDispatcher);
153
185
  if (!flag) {
154
- rout.push(m)
186
+ rout.push(m);
155
187
  }
156
188
  }
157
189
  } catch (err: any) {
158
- logger.error(err.message, m.definedIn)
190
+ logger.error(err.message, m.definedIn);
191
+ }
192
+ });
193
+ return rout;
194
+ }
195
+
196
+ function _get_source_frame(
197
+ frames: StackTrace.StackFrame[]
198
+ ): StackTrace.StackFrame | undefined {
199
+ // Heuristic: frame 0 = Error, 1 = getCallerSiteInBrowser, 2 = your function, 3 = its caller
200
+ for (let i = 3; i < frames.length; i++) {
201
+ const f = frames[i];
202
+ const fn = f.fileName;
203
+ if (_is_src_file(fn)) {
204
+ return f;
159
205
  }
160
- })
161
- return rout
162
- }
206
+ }
207
+ return undefined;
208
+ }
209
+
210
+ function _is_src_file(url: string | undefined): boolean {
211
+ if (!url) return false;
212
+ const m = url.match(/^(?:[a-z][a-z0-9+.-]*:)?\/\/[^\/]+\/([^\/?#]+)/);
213
+ if (m) {
214
+ const p1 = m[1];
215
+ const flag = !(p1.startsWith("@") || p1 === "node_modules");
216
+ return flag;
217
+ }
218
+ return true;
219
+ }
package/src/redux.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { PiRegister } from "."
2
- import { pihanga as logger } from "./logger"
3
- import { CardAction, ReduceF, ReduxState } from "./types"
1
+ import {PiRegister} from ".";
2
+ import {pihanga as logger} from "./logger";
3
+ import {CardAction, ReduceF, ReduxState} from "./types";
4
4
 
5
- const ns2Actions: { [k: string]: boolean } = {}
5
+ const ns2Actions: {[k: string]: boolean} = {};
6
6
 
7
7
  /**
8
8
  * Register a set of actions for a particular namespace.
@@ -18,32 +18,32 @@ const ns2Actions: { [k: string]: boolean } = {}
18
18
  */
19
19
  export function registerActions<T extends string>(
20
20
  namespace: string,
21
- actions: readonly T[],
22
- ): { [S in Uppercase<T>]: string } {
21
+ actions: readonly T[]
22
+ ): {[S in Uppercase<T>]: string} {
23
23
  if (ns2Actions[namespace]) {
24
- logger.warn(`Overwriting action namespace "${namespace}"`)
24
+ logger.warn(`Overwriting action namespace "${namespace}"`);
25
25
  }
26
- const ah: any = {}
26
+ const ah: any = {};
27
27
  actions.forEach((a) => {
28
- ah[a.toUpperCase()] = `${namespace}/${a}`
29
- })
30
- logger.info(`Register action ns "${namespace}"`)
31
- ns2Actions[namespace] = true
32
- return ah as { [S in Uppercase<T>]: string }
28
+ ah[a.toUpperCase()] = `${namespace}/${a}`;
29
+ });
30
+ logger.info(`Register action ns "${namespace}"`);
31
+ ns2Actions[namespace] = true;
32
+ return ah as {[S in Uppercase<T>]: string};
33
33
  }
34
34
 
35
- export function actionTypesToEvents(actionTypes: { [k: string]: string }): {
36
- [k: string]: string
35
+ export function actionTypesToEvents(actionTypes: {[k: string]: string}): {
36
+ [k: string]: string;
37
37
  } {
38
38
  return Object.entries(actionTypes).reduce((p, el) => {
39
- const [k, v] = el
39
+ const [k, v] = el;
40
40
  const n = k
41
41
  .split("_")
42
42
  .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
43
- .join("")
44
- p[`on${n}`] = v
45
- return p
46
- }, {} as { [k: string]: string })
43
+ .join("");
44
+ p[`on${n}`] = v;
45
+ return p;
46
+ }, {} as {[k: string]: string});
47
47
  }
48
48
 
49
49
  /**
@@ -53,12 +53,13 @@ export function actionTypesToEvents(actionTypes: { [k: string]: string }): {
53
53
  * @param {string} actionType
54
54
  * @returns a function to register a reducer for 'actionType'
55
55
  */
56
- export function createOnAction<E>(actionType: string): <S extends ReduxState>(
56
+ export function createOnAction<E>(
57
+ actionType: string
58
+ ): <S extends ReduxState>(
57
59
  register: PiRegister,
58
- //f: (state: S, ev: CardAction & E) => S,
59
- f: ReduceF<S, CardAction & E>,
60
+ f: ReduceF<S, CardAction & E>
60
61
  ) => void {
61
62
  return (register, f) => {
62
- register.reducer.register(actionType, f)
63
- }
63
+ register.reducer.register(actionType, f);
64
+ };
64
65
  }
@@ -0,0 +1,31 @@
1
+ // type RegisterCbk = (register: PiRegister) => void;
2
+
3
+ // // These remain private and shared across all imports
4
+ // let registerF: PiRegister | null = null;
5
+ // let pendingRegistrations: RegisterCbk[] = [];
6
+ // const MAX_BUFFER_SIZE = 100; // Prevent memory leaks
7
+
8
+ // export function registerCallback<T>(cb: RegisterCbk): void {
9
+ // registerF = cb;
10
+
11
+ // // Flush buffer
12
+ // while (pendingRegistrations.length > 0) {
13
+ // const cbk = pendingRegistrations.shift();
14
+ // if (cbk) {
15
+ // cbk(registerF);
16
+ // }
17
+ // }
18
+ // }
19
+
20
+ // export function register(cbk: RegisterCbk): void {
21
+ // if (registerF) {
22
+ // cbk(registerF);
23
+ // } else {
24
+ // pendingRegistrations.push(cbk);
25
+ // }
26
+ // }
27
+
28
+ // // Added: Helper to clear the buffer if needed
29
+ // export function clearPendingRegistration(): void {
30
+ // pendingRegistrations = [];
31
+ // }
@@ -1,5 +1,5 @@
1
1
  import equal from "deep-equal";
2
- import { getLogger } from "./logger";
2
+ import {getLogger} from "./logger";
3
3
  import {
4
4
  CSSModuleClasses,
5
5
  CardAction,
@@ -18,7 +18,7 @@ import {
18
18
  StateMapper,
19
19
  StateMapperContext,
20
20
  } from "./types";
21
- import { Action, AnyAction, Dispatch } from "@reduxjs/toolkit";
21
+ import {Action, AnyAction, Dispatch} from "@reduxjs/toolkit";
22
22
 
23
23
  const logger = getLogger("card-register");
24
24
 
@@ -28,11 +28,11 @@ export function isCardRef(p: any): boolean {
28
28
 
29
29
  export type Mapping = {
30
30
  cardType: string;
31
- props: { [k: string]: unknown };
31
+ props: {[k: string]: unknown};
32
32
  eventMappers: {
33
33
  [k: string]: (ev: Action, ctxtProps: CardProp) => Action | null;
34
34
  };
35
- cardEvents: { [key: string]: string };
35
+ cardEvents: {[key: string]: string};
36
36
  parameters: PiCardDef; // original
37
37
  };
38
38
 
@@ -40,20 +40,20 @@ export type MetaCard = {
40
40
  type: string;
41
41
  registerCard: RegisterCardF;
42
42
  mapper: MetaCardMapperF;
43
- events?: { [key: string]: string };
43
+ events?: {[key: string]: string};
44
44
  };
45
45
 
46
- export const cardTypes: { [k: string]: PiRegisterComponent } = {};
47
- export const metacardTypes: { [k: string]: MetaCard } = {};
46
+ export const cardTypes: {[k: string]: PiRegisterComponent} = {};
47
+ export const metacardTypes: {[k: string]: MetaCard} = {};
48
48
 
49
49
  export let framework: string; // name of active UI framework
50
- export const cardMappings: { [k: string]: Mapping } = {};
50
+ export const cardMappings: {[k: string]: Mapping} = {};
51
51
  export const dispatch2registerReducer: [
52
52
  React.Dispatch<any>,
53
53
  PiRegisterReducerF
54
54
  ][] = [];
55
55
 
56
- export function registerCardComponent(card: PiRegisterComponent): void {
56
+ export function addCardComponent(card: PiRegisterComponent): void {
57
57
  if (cardTypes[card.name]) {
58
58
  logger.warn(`Overwriting definition for card type "${card.name}"`);
59
59
  }
@@ -71,17 +71,17 @@ export function registerCardComponent(card: PiRegisterComponent): void {
71
71
 
72
72
  export function registerMetacard(registerCard: RegisterCardF) {
73
73
  function f<C>(declaration: PiRegisterMetaCard) {
74
- const { type, mapper, events } = declaration;
74
+ const {type, mapper, events} = declaration;
75
75
  if (metacardTypes[type]) {
76
76
  logger.warn(`Overwriting definition for meta card type "${type}"`);
77
77
  }
78
78
  logger.info(`Register meta card type "${type}"`);
79
- metacardTypes[type] = { type, registerCard, mapper, events };
79
+ metacardTypes[type] = {type, registerCard, mapper, events};
80
80
  }
81
81
  return f;
82
82
  }
83
83
 
84
- export function registerCard(
84
+ export function addCard(
85
85
  registerReducer: PiRegisterReducerF,
86
86
  dispatchF: React.Dispatch<any>
87
87
  ) {
@@ -100,7 +100,7 @@ export function updateOrRegisterCard(
100
100
  dispatch2registerReducer.push([dispatchF, registerReducer]);
101
101
  return (
102
102
  name: string,
103
- parameters: { [key: string]: GenericCardParameterT }
103
+ parameters: {[key: string]: GenericCardParameterT}
104
104
  ): PiCardRef => {
105
105
  return _updateCard(name, parameters, registerReducer);
106
106
  };
@@ -110,7 +110,7 @@ export function _registerCard(
110
110
  name: string,
111
111
  parameters: PiCardDef,
112
112
  registerReducer: PiRegisterReducerF,
113
- overrideEvents?: { [key: string]: string }
113
+ overrideEvents?: {[key: string]: string}
114
114
  ): PiCardRef {
115
115
  if (cardMappings[name]) {
116
116
  logger.warn(`Overwriting definition for card "${name}"`);
@@ -137,9 +137,9 @@ export function _registerCard(
137
137
 
138
138
  export function _updateCard(
139
139
  name: string,
140
- parameters: { [key: string]: GenericCardParameterT },
140
+ parameters: {[key: string]: GenericCardParameterT},
141
141
  registerReducer: PiRegisterReducerF,
142
- overrideEvents?: { [key: string]: string }
142
+ overrideEvents?: {[key: string]: string}
143
143
  ): PiCardRef {
144
144
  const mappings = cardMappings[name];
145
145
  if (!mappings) {
@@ -152,7 +152,7 @@ export function _updateCard(
152
152
  return _registerCard(name, p, registerReducer, overrideEvents);
153
153
  }
154
154
 
155
- const p = { ...mappings.parameters, ...parameters };
155
+ const p = {...mappings.parameters, ...parameters};
156
156
  _createCardMapping(name, p, registerReducer, mappings.cardEvents);
157
157
  return name;
158
158
  }
@@ -161,10 +161,10 @@ export function _createCardMapping(
161
161
  name: string,
162
162
  parameters: PiCardDef,
163
163
  registerReducer: PiRegisterReducerF,
164
- cardEvents: { [key: string]: string }
164
+ cardEvents: {[key: string]: string}
165
165
  ) {
166
- const props = {} as { [k: string]: unknown };
167
- const eventMappers = {} as { [k: string]: (ev: Action) => Action };
166
+ const props = {} as {[k: string]: unknown};
167
+ const eventMappers = {} as {[k: string]: (ev: Action) => Action};
168
168
 
169
169
  Object.entries(parameters).forEach(([k, v]) => {
170
170
  if (k === "cardType") return;
@@ -272,8 +272,8 @@ export function createCardDeclaration<Props = {}, Events = {}>(
272
272
  function processEventParameter(
273
273
  propName: string,
274
274
  value: unknown,
275
- events: { [key: string]: string },
276
- eventMappers: { [k: string]: (ev: Action) => Action },
275
+ events: {[key: string]: string},
276
+ eventMappers: {[k: string]: (ev: Action) => Action},
277
277
  registerReducer: PiRegisterReducerF,
278
278
  cardName: string
279
279
  ): boolean {
@@ -304,7 +304,8 @@ function processEventParameter(
304
304
  return s;
305
305
  },
306
306
  0,
307
- `${cardName}|${propName}`
307
+ `on card ${cardName} for ${propName}`,
308
+ r
308
309
  );
309
310
  }
310
311
  if (propName === `${evName}Mapper`) {
@@ -350,9 +351,9 @@ export function memo<P, T, S extends ReduxState, C = any>(
350
351
  filterF: (state: S, context: StateMapperContext<C>) => P,
351
352
  mapperF: (partial: P, context: StateMapperContext<C>, state: S) => T
352
353
  ): (state: S, context: StateMapperContext<C>) => T {
353
- const lastFilter: { [k: string]: P } = {};
354
- const lastValue: { [k: string]: T } = {};
355
- const isNotFirst: { [k: string]: boolean } = {};
354
+ const lastFilter: {[k: string]: P} = {};
355
+ const lastValue: {[k: string]: T} = {};
356
+ const isNotFirst: {[k: string]: boolean} = {};
356
357
 
357
358
  return (state: S, context: StateMapperContext<C>): T => {
358
359
  const k = context.cardKey || "-";