react-better-model 0.2.2 → 1.0.0-beta

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.
Files changed (99) hide show
  1. package/AGENTS.md +43 -0
  2. package/LICENSE +21 -4
  3. package/lib/ModelBase.d.ts +15 -0
  4. package/lib/ModelBase.d.ts.map +1 -0
  5. package/lib/ModelBase.js +91 -0
  6. package/lib/ModelBase.js.map +1 -0
  7. package/lib/ModelWithHooks.d.ts +9 -0
  8. package/lib/ModelWithHooks.d.ts.map +1 -0
  9. package/lib/ModelWithHooks.js +108 -0
  10. package/lib/ModelWithHooks.js.map +1 -0
  11. package/lib/common-types.d.ts +9 -0
  12. package/lib/common-types.d.ts.map +1 -0
  13. package/lib/{types.js → common-types.js} +1 -1
  14. package/lib/common-types.js.map +1 -0
  15. package/lib/create-model.d.ts +18 -0
  16. package/lib/create-model.d.ts.map +1 -0
  17. package/lib/create-model.js +78 -0
  18. package/lib/create-model.js.map +1 -0
  19. package/lib/create-model.test.d.ts +2 -0
  20. package/lib/create-model.test.d.ts.map +1 -0
  21. package/lib/create-model.test.js +80 -0
  22. package/lib/create-model.test.js.map +1 -0
  23. package/lib/index.d.ts +4 -8
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +22 -9
  26. package/lib/index.js.map +1 -1
  27. package/lib/src/ModelBase.d.ts +16 -0
  28. package/lib/src/ModelBase.d.ts.map +1 -0
  29. package/lib/src/ModelBase.js +97 -0
  30. package/lib/src/ModelBase.js.map +1 -0
  31. package/lib/src/ModelWithHooks.d.ts +8 -0
  32. package/lib/src/ModelWithHooks.d.ts.map +1 -0
  33. package/lib/src/ModelWithHooks.js +57 -0
  34. package/lib/src/ModelWithHooks.js.map +1 -0
  35. package/lib/src/common-types.d.ts +9 -0
  36. package/lib/src/common-types.d.ts.map +1 -0
  37. package/lib/src/common-types.js +3 -0
  38. package/lib/src/common-types.js.map +1 -0
  39. package/lib/src/create-model.d.ts +18 -0
  40. package/lib/src/create-model.d.ts.map +1 -0
  41. package/lib/src/create-model.js +78 -0
  42. package/lib/src/create-model.js.map +1 -0
  43. package/lib/src/index.d.ts +5 -0
  44. package/lib/src/index.d.ts.map +1 -0
  45. package/lib/src/index.js +34 -0
  46. package/lib/src/index.js.map +1 -0
  47. package/lib/src/utils.d.ts +4 -0
  48. package/lib/src/utils.d.ts.map +1 -0
  49. package/lib/src/utils.js +36 -0
  50. package/lib/src/utils.js.map +1 -0
  51. package/lib/tests/ModelBase.test.d.ts +2 -0
  52. package/lib/tests/ModelBase.test.d.ts.map +1 -0
  53. package/lib/tests/ModelBase.test.js +73 -0
  54. package/lib/tests/ModelBase.test.js.map +1 -0
  55. package/lib/tests/ModelWithHooks.test.d.ts +2 -0
  56. package/lib/tests/ModelWithHooks.test.d.ts.map +1 -0
  57. package/lib/tests/ModelWithHooks.test.js +108 -0
  58. package/lib/tests/ModelWithHooks.test.js.map +1 -0
  59. package/lib/tests/create-model.test.d.ts +2 -0
  60. package/lib/tests/create-model.test.d.ts.map +1 -0
  61. package/lib/tests/create-model.test.js +104 -0
  62. package/lib/tests/create-model.test.js.map +1 -0
  63. package/lib/tests/utils.test.d.ts +2 -0
  64. package/lib/tests/utils.test.d.ts.map +1 -0
  65. package/lib/tests/utils.test.js +30 -0
  66. package/lib/tests/utils.test.js.map +1 -0
  67. package/package.json +13 -11
  68. package/readme.md +212 -105
  69. package/lib/Model.d.ts +0 -23
  70. package/lib/Model.d.ts.map +0 -1
  71. package/lib/Model.js +0 -108
  72. package/lib/Model.js.map +0 -1
  73. package/lib/hooks/event-hooks.d.ts +0 -6
  74. package/lib/hooks/event-hooks.d.ts.map +0 -1
  75. package/lib/hooks/event-hooks.js +0 -19
  76. package/lib/hooks/event-hooks.js.map +0 -1
  77. package/lib/hooks/model-hooks.d.ts +0 -4
  78. package/lib/hooks/model-hooks.d.ts.map +0 -1
  79. package/lib/hooks/model-hooks.js +0 -9
  80. package/lib/hooks/model-hooks.js.map +0 -1
  81. package/lib/hooks/state-hooks.d.ts +0 -5
  82. package/lib/hooks/state-hooks.d.ts.map +0 -1
  83. package/lib/hooks/state-hooks.js +0 -19
  84. package/lib/hooks/state-hooks.js.map +0 -1
  85. package/lib/types.d.ts +0 -13
  86. package/lib/types.d.ts.map +0 -1
  87. package/lib/types.js.map +0 -1
  88. package/lib/utils/compose-providers.d.ts +0 -3
  89. package/lib/utils/compose-providers.d.ts.map +0 -1
  90. package/lib/utils/compose-providers.js +0 -19
  91. package/lib/utils/compose-providers.js.map +0 -1
  92. package/lib/utils/create-model.d.ts +0 -21
  93. package/lib/utils/create-model.d.ts.map +0 -1
  94. package/lib/utils/create-model.js +0 -84
  95. package/lib/utils/create-model.js.map +0 -1
  96. package/lib/utils/js-event-types.d.ts +0 -6
  97. package/lib/utils/js-event-types.d.ts.map +0 -1
  98. package/lib/utils/js-event-types.js +0 -9
  99. package/lib/utils/js-event-types.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@testing-library/react");
8
+ const src_1 = require("../src");
9
+ class HookModel extends src_1.Model {
10
+ constructor(initial = { count: 0, flag: false }) {
11
+ super(initial);
12
+ this.state = initial;
13
+ }
14
+ }
15
+ const { Provider, useModel } = (0, src_1.createModel)(HookModel);
16
+ describe('ModelWithHooks', () => {
17
+ it('useState subscribes to one key and skips updates when the value is unchanged', () => {
18
+ const model = new HookModel();
19
+ const countRenders = jest.fn();
20
+ const flagRenders = jest.fn();
21
+ function CountView() {
22
+ const [count, setCount] = useModel().useState('count');
23
+ countRenders(count);
24
+ return (react_1.default.createElement("button", { "data-testid": "noop", onClick: () => setCount(count) },
25
+ "count: ",
26
+ count));
27
+ }
28
+ function FlagView() {
29
+ const [flag] = useModel().useState('flag');
30
+ flagRenders(flag);
31
+ return react_1.default.createElement("div", { "data-testid": "flag" }, String(flag));
32
+ }
33
+ const view = (0, react_2.render)(react_1.default.createElement(Provider, { value: model },
34
+ react_1.default.createElement(CountView, null),
35
+ react_1.default.createElement(FlagView, null)));
36
+ expect(countRenders).toHaveBeenCalledTimes(1);
37
+ expect(flagRenders).toHaveBeenCalledTimes(1);
38
+ react_2.fireEvent.click(view.getByTestId('noop'));
39
+ expect(countRenders).toHaveBeenCalledTimes(1);
40
+ (0, react_2.act)(() => {
41
+ model.setState({ flag: true });
42
+ });
43
+ expect(flagRenders).toHaveBeenCalledTimes(2);
44
+ expect(countRenders).toHaveBeenCalledTimes(1);
45
+ (0, react_2.act)(() => {
46
+ model.setState({ count: 5 });
47
+ });
48
+ expect(countRenders).toHaveBeenCalledTimes(2);
49
+ });
50
+ it('useMapper recomputes only when mapped keys change', () => {
51
+ const model = new HookModel();
52
+ const derivedRenders = jest.fn();
53
+ function Derived() {
54
+ const doubled = useModel().useMapper((state) => state.count * 2);
55
+ derivedRenders(doubled);
56
+ return react_1.default.createElement("div", { "data-testid": "derived" }, doubled);
57
+ }
58
+ (0, react_2.render)(react_1.default.createElement(Provider, { value: model },
59
+ react_1.default.createElement(Derived, null)));
60
+ expect(derivedRenders).toHaveBeenCalledTimes(1);
61
+ expect(derivedRenders).toHaveBeenLastCalledWith(0);
62
+ (0, react_2.act)(() => {
63
+ model.setState({ flag: true });
64
+ });
65
+ expect(derivedRenders).toHaveBeenCalledTimes(1);
66
+ (0, react_2.act)(() => {
67
+ model.setState({ count: 3 });
68
+ });
69
+ expect(derivedRenders).toHaveBeenCalledTimes(2);
70
+ expect(derivedRenders).toHaveBeenLastCalledWith(6);
71
+ });
72
+ it('useMapper re-tracks dependencies when mapper function changes', () => {
73
+ const model = new HookModel();
74
+ const renders = [];
75
+ function Derived({ keys }) {
76
+ const mapper = react_1.default.useCallback((state) => keys.map(k => state[k]).join(':'), [keys]);
77
+ const value = useModel().useMapper(mapper);
78
+ renders.push(value);
79
+ return react_1.default.createElement("div", { "data-testid": "derived" }, value);
80
+ }
81
+ const view = (0, react_2.render)(react_1.default.createElement(Provider, { value: model },
82
+ react_1.default.createElement(Derived, { keys: ['count'] })));
83
+ (0, react_2.act)(() => model.setState({ count: 1 }));
84
+ expect(renders.pop()).toBe('1');
85
+ view.rerender(react_1.default.createElement(Provider, { value: model },
86
+ react_1.default.createElement(Derived, { keys: ['flag'] })));
87
+ (0, react_2.act)(() => model.setState({ flag: true }));
88
+ expect(renders.pop()).toBe('true');
89
+ });
90
+ it('useEvent subscribes and dispatches typed events', () => {
91
+ const onPing = jest.fn();
92
+ function Listener() {
93
+ const dispatch = useModel().useEvent('ping', onPing);
94
+ return (react_1.default.createElement("button", { "data-testid": "ping", onClick: () => dispatch(9) }, "ping"));
95
+ }
96
+ const view = (0, react_2.render)(react_1.default.createElement(Provider, null,
97
+ react_1.default.createElement(Listener, null)));
98
+ react_2.fireEvent.click(view.getByTestId('ping'));
99
+ expect(onPing).toHaveBeenCalledTimes(1);
100
+ expect(onPing).toHaveBeenCalledWith(9);
101
+ view.unmount();
102
+ (0, react_2.act)(() => {
103
+ // Subscription should be cleaned up on unmount.
104
+ view.unmount();
105
+ });
106
+ });
107
+ });
108
+ //# sourceMappingURL=ModelWithHooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelWithHooks.test.js","sourceRoot":"","sources":["../../tests/ModelWithHooks.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create-model.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-model.test.d.ts","sourceRoot":"","sources":["../../tests/create-model.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const react_1 = __importDefault(require("react"));
16
+ const react_2 = require("@testing-library/react");
17
+ const src_1 = require("../src");
18
+ class CounterModel extends src_1.Model {
19
+ constructor(initial = { count: 0 }) {
20
+ super(initial);
21
+ this.increment = () => this.setState({ count: this.state.count + 1 });
22
+ this.state = initial;
23
+ }
24
+ }
25
+ describe('createModel', () => {
26
+ it('creates a single model instance per Provider lifecycle', () => {
27
+ class CountingModel extends src_1.Model {
28
+ constructor(initial = { value: 0 }) {
29
+ super(initial);
30
+ this.state = initial;
31
+ CountingModel.instances += 1;
32
+ }
33
+ }
34
+ CountingModel.instances = 0;
35
+ const { Provider, useModel } = (0, src_1.createModel)(CountingModel);
36
+ let lastModel;
37
+ function Consumer() {
38
+ const model = useModel();
39
+ lastModel = model;
40
+ return react_1.default.createElement("div", { "data-testid": "value" }, model.state.value);
41
+ }
42
+ const view = (0, react_2.render)(react_1.default.createElement(Provider, null,
43
+ react_1.default.createElement(Consumer, null)));
44
+ expect(CountingModel.instances).toBe(1);
45
+ const firstModel = lastModel;
46
+ view.rerender(react_1.default.createElement(Provider, null,
47
+ react_1.default.createElement(Consumer, null)));
48
+ expect(lastModel).toBe(firstModel);
49
+ expect(CountingModel.instances).toBe(1);
50
+ });
51
+ it('uses a provided model instance and wires onChange callbacks', () => __awaiter(void 0, void 0, void 0, function* () {
52
+ const { Provider, useModel } = (0, src_1.createModel)(CounterModel);
53
+ const provided = new CounterModel();
54
+ const handleChange = jest.fn();
55
+ function Consumer() {
56
+ const model = useModel();
57
+ return (react_1.default.createElement("button", { "data-testid": "increment", onClick: () => model.increment() }, "increment"));
58
+ }
59
+ const { getByTestId } = (0, react_2.render)(react_1.default.createElement(Provider, { value: provided, onChange: handleChange },
60
+ react_1.default.createElement(Consumer, null)));
61
+ yield (0, react_2.act)(() => __awaiter(void 0, void 0, void 0, function* () {
62
+ /* allow useEffect to register subscriptions */
63
+ }));
64
+ react_2.fireEvent.click(getByTestId('increment'));
65
+ yield (0, react_2.waitFor)(() => expect(handleChange).toHaveBeenCalledTimes(1));
66
+ expect(handleChange).toHaveBeenCalledWith({ count: 1 }, { count: 0 });
67
+ expect(provided.state.count).toBe(1);
68
+ }));
69
+ it('syncs external state prop into the model instance', () => __awaiter(void 0, void 0, void 0, function* () {
70
+ const { Provider, useModel } = (0, src_1.createModel)(CounterModel);
71
+ function Consumer() {
72
+ const model = useModel();
73
+ const [count] = model.useState('count');
74
+ return react_1.default.createElement("div", { "data-testid": "count" }, count);
75
+ }
76
+ const view = (0, react_2.render)(react_1.default.createElement(Provider, { state: { count: 5 } },
77
+ react_1.default.createElement(Consumer, null)));
78
+ yield (0, react_2.waitFor)(() => expect(view.getByTestId('count').textContent).toBe('5'));
79
+ view.rerender(react_1.default.createElement(Provider, { state: { count: 7 } },
80
+ react_1.default.createElement(Consumer, null)));
81
+ yield (0, react_2.waitFor)(() => expect(view.getByTestId('count').textContent).toBe('7'));
82
+ }));
83
+ it('useModelInstance creates a memoized instance with optional initial state', () => {
84
+ class InitializableModel extends src_1.Model {
85
+ constructor(initialState = { value: 1 }) {
86
+ super(initialState);
87
+ this.state = initialState;
88
+ }
89
+ }
90
+ const { useModelInstance } = (0, src_1.createModel)(InitializableModel);
91
+ const seen = [];
92
+ function Consumer({ value }) {
93
+ const instance = useModelInstance(value !== undefined ? { value } : undefined);
94
+ seen.push(instance);
95
+ return react_1.default.createElement("div", { "data-testid": "value" }, instance.state.value);
96
+ }
97
+ const view = (0, react_2.render)(react_1.default.createElement(Consumer, null));
98
+ expect(view.getByTestId('value').textContent).toBe('1');
99
+ view.rerender(react_1.default.createElement(Consumer, { value: 5 }));
100
+ expect(view.getByTestId('value').textContent).toBe('1');
101
+ expect(seen[0]).toBe(seen[1]);
102
+ });
103
+ });
104
+ //# sourceMappingURL=create-model.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-model.test.js","sourceRoot":"","sources":["../../tests/create-model.test.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,kDAAyB;AACzB,kDAAwE;AAExE,gCAA2C;AAI3C,MAAM,YAAa,SAAQ,WAAK;IAG9B,YAAY,UAAwB,EAAE,KAAK,EAAE,CAAC,EAAE;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAA;QAIhB,cAAS,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;QAH9D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAA;IACtB,CAAC;CAGF;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,aAAc,SAAQ,WAAK;YAI/B,YAAY,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE;gBAChC,KAAK,CAAC,OAAO,CAAC,CAAA;gBACd,IAAI,CAAC,KAAK,GAAG,OAAO,CAAA;gBACpB,aAAa,CAAC,SAAS,IAAI,CAAC,CAAA;YAC9B,CAAC;;QAPM,uBAAS,GAAG,CAAC,CAAA;QAUtB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAA,iBAAW,EAAC,aAAa,CAAC,CAAA;QACzD,IAAI,SAAoC,CAAA;QAExC,SAAS,QAAQ;YACf,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;YACxB,SAAS,GAAG,KAAK,CAAA;YACjB,OAAO,sDAAiB,OAAO,IAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAO,CAAA;QAC3D,CAAC;QAED,MAAM,IAAI,GAAG,IAAA,cAAM,EACjB,8BAAC,QAAQ;YACP,8BAAC,QAAQ,OAAG,CACH,CACZ,CAAA;QAED,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACvC,MAAM,UAAU,GAAG,SAAS,CAAA;QAE5B,IAAI,CAAC,QAAQ,CACX,8BAAC,QAAQ;YACP,8BAAC,QAAQ,OAAG,CACH,CACZ,CAAA;QAED,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAClC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAS,EAAE;QAC3E,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAA,iBAAW,EAAC,YAAY,CAAC,CAAA;QACxD,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAA;QACnC,MAAM,YAAY,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;QAE9B,SAAS,QAAQ;YACf,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;YAExB,OAAO,CACL,yDAAoB,WAAW,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,gBAEvD,CACV,CAAA;QACH,CAAC;QAED,MAAM,EAAE,WAAW,EAAE,GAAG,IAAA,cAAM,EAC5B,8BAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY;YAC/C,8BAAC,QAAQ,OAAG,CACH,CACZ,CAAA;QAED,MAAM,IAAA,WAAG,EAAC,GAAS,EAAE;YACnB,+CAA+C;QACjD,CAAC,CAAA,CAAC,CAAA;QAEF,iBAAS,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAA;QAEzC,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAA;QAClE,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;QACrE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACtC,CAAC,CAAA,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAS,EAAE;QACjE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAA,iBAAW,EAAC,YAAY,CAAC,CAAA;QAExD,SAAS,QAAQ;YACf,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;YACxB,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YACvC,OAAO,sDAAiB,OAAO,IAAE,KAAK,CAAO,CAAA;QAC/C,CAAC;QAED,MAAM,IAAI,GAAG,IAAA,cAAM,EACjB,8BAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YAC3B,8BAAC,QAAQ,OAAG,CACH,CACZ,CAAA;QAED,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAE5E,IAAI,CAAC,QAAQ,CACX,8BAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YAC3B,8BAAC,QAAQ,OAAG,CACH,CACZ,CAAA;QAED,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9E,CAAC,CAAA,CAAC,CAAA;IAEF,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,kBAAmB,SAAQ,WAAK;YAGpC,YAAY,YAAY,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE;gBACrC,KAAK,CAAC,YAAY,CAAC,CAAA;gBACnB,IAAI,CAAC,KAAK,GAAG,YAAY,CAAA;YAC3B,CAAC;SACF;QAED,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAA,iBAAW,EAAC,kBAAkB,CAAC,CAAA;QAC5D,MAAM,IAAI,GAAyB,EAAE,CAAA;QAErC,SAAS,QAAQ,CAAC,EAAE,KAAK,EAAsB;YAC7C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YAC9E,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACnB,OAAO,sDAAiB,OAAO,IAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAO,CAAA;QAC9D,CAAC;QAED,MAAM,IAAI,GAAG,IAAA,cAAM,EACjB,8BAAC,QAAQ,OAAG,CACb,CAAA;QAED,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEvD,IAAI,CAAC,QAAQ,CAAC,8BAAC,QAAQ,IAAC,KAAK,EAAE,CAAC,GAAI,CAAC,CAAA;QAErC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACvD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../tests/utils.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../src/utils");
4
+ describe('shallowEqual', () => {
5
+ it('returns true for the same reference', () => {
6
+ const obj = { a: 1 };
7
+ expect((0, utils_1.shallowEqual)(obj, obj)).toBe(true);
8
+ });
9
+ it('returns true for shallowly equal objects with different references', () => {
10
+ const a = { x: 1, y: 'hi' };
11
+ const b = { x: 1, y: 'hi' };
12
+ expect((0, utils_1.shallowEqual)(a, b)).toBe(true);
13
+ });
14
+ it('returns false when a value differs', () => {
15
+ const a = { x: 1 };
16
+ const b = { x: 2 };
17
+ expect((0, utils_1.shallowEqual)(a, b)).toBe(false);
18
+ });
19
+ it('returns false when keys differ', () => {
20
+ const a = { x: 1 };
21
+ const b = { x: 1, y: 2 };
22
+ expect((0, utils_1.shallowEqual)(a, b)).toBe(false);
23
+ });
24
+ it('uses Object.is semantics (NaN equals NaN)', () => {
25
+ const a = { x: NaN };
26
+ const b = { x: NaN };
27
+ expect((0, utils_1.shallowEqual)(a, b)).toBe(true);
28
+ });
29
+ });
30
+ //# sourceMappingURL=utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../tests/utils.test.ts"],"names":[],"mappings":";;AAAA,wCAA2C;AAE3C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QACpB,MAAM,CAAC,IAAA,oBAAY,EAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;QAC3B,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;QAC3B,MAAM,CAAC,IAAA,oBAAY,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QAClB,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QAClB,MAAM,CAAC,IAAA,oBAAY,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QAClB,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QACxB,MAAM,CAAC,IAAA,oBAAY,EAAC,CAAQ,EAAE,CAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;QACpB,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;QACpB,MAAM,CAAC,IAAA,oBAAY,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,36 +1,38 @@
1
1
  {
2
2
  "name": "react-better-model",
3
- "version": "0.2.2",
4
- "description": "",
3
+ "version": "1.0.0-beta",
4
+ "description": "Model to manage state outside component and share it with children via context",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "keywords": [
8
8
  "React",
9
9
  "Shared",
10
10
  "Model",
11
- "Store",
11
+ "State",
12
12
  "Events"
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git@github.com:ingvardm/react-model-hooks.git"
16
+ "url": "git+ssh://git@github.com/ingvardm/react-model-hooks.git"
17
17
  },
18
18
  "files": [
19
- "lib"
19
+ "lib",
20
+ "AGENTS.md"
20
21
  ],
21
22
  "scripts": {
22
23
  "build": "npx tsc",
23
- "prepare": "yarn build"
24
+ "prepare": "yarn build",
25
+ "test": "npx jest --runInBand"
24
26
  },
25
- "author": "Igor Poughtach <ingvardm@gmail.com>",
26
- "license": "MIT",
27
+ "author": "Igor Pougatch <ingvardm@gmail.com>",
28
+ "license": "Unlicense",
27
29
  "peerDependencies": {
28
- "react": "*"
30
+ "react": "^18.0.0"
29
31
  },
30
32
  "devDependencies": {
31
- "@types/react": "^17.0.34",
33
+ "@types/react": "^18.0.0",
32
34
  "typescript": "^4.4.4"
33
35
  },
34
36
  "dependencies": {},
35
37
  "private": false
36
- }
38
+ }