@derivesome/core 1.0.0

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 (217) hide show
  1. package/.README.md.~undo-tree~ +4 -0
  2. package/.package.json.~undo-tree~ +30 -0
  3. package/.tsconfig.cjs.json.~undo-tree~ +8 -0
  4. package/.tsconfig.json.~undo-tree~ +14 -0
  5. package/README.md +355 -0
  6. package/README.md~ +355 -0
  7. package/dist/cjs/array-proxy.d.ts +56 -0
  8. package/dist/cjs/array-proxy.d.ts.map +1 -0
  9. package/dist/cjs/array-proxy.js +72 -0
  10. package/dist/cjs/array-proxy.js.map +1 -0
  11. package/dist/cjs/common/array.d.ts +11 -0
  12. package/dist/cjs/common/array.d.ts.map +1 -0
  13. package/dist/cjs/common/array.js +57 -0
  14. package/dist/cjs/common/array.js.map +1 -0
  15. package/dist/cjs/common/compare.d.ts +2 -0
  16. package/dist/cjs/common/compare.d.ts.map +1 -0
  17. package/dist/cjs/common/compare.js +8 -0
  18. package/dist/cjs/common/compare.js.map +1 -0
  19. package/dist/cjs/common/diff.d.ts +9 -0
  20. package/dist/cjs/common/diff.d.ts.map +1 -0
  21. package/dist/cjs/common/diff.js +63 -0
  22. package/dist/cjs/common/diff.js.map +1 -0
  23. package/dist/cjs/common/has.d.ts +4 -0
  24. package/dist/cjs/common/has.d.ts.map +1 -0
  25. package/dist/cjs/common/has.js +11 -0
  26. package/dist/cjs/common/has.js.map +1 -0
  27. package/dist/cjs/common/index.d.ts +12 -0
  28. package/dist/cjs/common/index.d.ts.map +1 -0
  29. package/dist/cjs/common/index.js +28 -0
  30. package/dist/cjs/common/index.js.map +1 -0
  31. package/dist/cjs/common/is.d.ts +5 -0
  32. package/dist/cjs/common/is.d.ts.map +1 -0
  33. package/dist/cjs/common/is.js +18 -0
  34. package/dist/cjs/common/is.js.map +1 -0
  35. package/dist/cjs/common/match.d.ts +23 -0
  36. package/dist/cjs/common/match.d.ts.map +1 -0
  37. package/dist/cjs/common/match.js +49 -0
  38. package/dist/cjs/common/match.js.map +1 -0
  39. package/dist/cjs/common/patch.d.ts +3 -0
  40. package/dist/cjs/common/patch.d.ts.map +1 -0
  41. package/dist/cjs/common/patch.js +44 -0
  42. package/dist/cjs/common/patch.js.map +1 -0
  43. package/dist/cjs/common/stack.d.ts +8 -0
  44. package/dist/cjs/common/stack.d.ts.map +1 -0
  45. package/dist/cjs/common/stack.js +24 -0
  46. package/dist/cjs/common/stack.js.map +1 -0
  47. package/dist/cjs/common/string.d.ts +2 -0
  48. package/dist/cjs/common/string.d.ts.map +1 -0
  49. package/dist/cjs/common/string.js +36 -0
  50. package/dist/cjs/common/string.js.map +1 -0
  51. package/dist/cjs/common/types.d.ts +27 -0
  52. package/dist/cjs/common/types.d.ts.map +1 -0
  53. package/dist/cjs/common/types.js +3 -0
  54. package/dist/cjs/common/types.js.map +1 -0
  55. package/dist/cjs/common/unique-array.d.ts +17 -0
  56. package/dist/cjs/common/unique-array.d.ts.map +1 -0
  57. package/dist/cjs/common/unique-array.js +73 -0
  58. package/dist/cjs/common/unique-array.js.map +1 -0
  59. package/dist/cjs/context.d.ts +15 -0
  60. package/dist/cjs/context.d.ts.map +1 -0
  61. package/dist/cjs/context.js +33 -0
  62. package/dist/cjs/context.js.map +1 -0
  63. package/dist/cjs/derived.d.ts +11 -0
  64. package/dist/cjs/derived.d.ts.map +1 -0
  65. package/dist/cjs/derived.js +26 -0
  66. package/dist/cjs/derived.js.map +1 -0
  67. package/dist/cjs/effect.d.ts +7 -0
  68. package/dist/cjs/effect.d.ts.map +1 -0
  69. package/dist/cjs/effect.js +7 -0
  70. package/dist/cjs/effect.js.map +1 -0
  71. package/dist/cjs/index.d.ts +8 -0
  72. package/dist/cjs/index.d.ts.map +1 -0
  73. package/dist/cjs/index.js +24 -0
  74. package/dist/cjs/index.js.map +1 -0
  75. package/dist/cjs/observable.d.ts +12 -0
  76. package/dist/cjs/observable.d.ts.map +1 -0
  77. package/dist/cjs/observable.js +24 -0
  78. package/dist/cjs/observable.js.map +1 -0
  79. package/dist/cjs/pubsub.d.ts +11 -0
  80. package/dist/cjs/pubsub.d.ts.map +1 -0
  81. package/dist/cjs/pubsub.js +50 -0
  82. package/dist/cjs/pubsub.js.map +1 -0
  83. package/dist/cjs/reference.d.ts +15 -0
  84. package/dist/cjs/reference.d.ts.map +1 -0
  85. package/dist/cjs/reference.js +49 -0
  86. package/dist/cjs/reference.js.map +1 -0
  87. package/dist/cjs/utils/findRefs.d.ts +3 -0
  88. package/dist/cjs/utils/findRefs.d.ts.map +1 -0
  89. package/dist/cjs/utils/findRefs.js +23 -0
  90. package/dist/cjs/utils/findRefs.js.map +1 -0
  91. package/dist/cjs/utils/index.d.ts +2 -0
  92. package/dist/cjs/utils/index.d.ts.map +1 -0
  93. package/dist/cjs/utils/index.js +18 -0
  94. package/dist/cjs/utils/index.js.map +1 -0
  95. package/dist/esm/array-proxy.d.ts +56 -0
  96. package/dist/esm/array-proxy.d.ts.map +1 -0
  97. package/dist/esm/array-proxy.js +68 -0
  98. package/dist/esm/array-proxy.js.map +1 -0
  99. package/dist/esm/common/array.d.ts +11 -0
  100. package/dist/esm/common/array.d.ts.map +1 -0
  101. package/dist/esm/common/array.js +48 -0
  102. package/dist/esm/common/array.js.map +1 -0
  103. package/dist/esm/common/compare.d.ts +2 -0
  104. package/dist/esm/common/compare.d.ts.map +1 -0
  105. package/dist/esm/common/compare.js +4 -0
  106. package/dist/esm/common/compare.js.map +1 -0
  107. package/dist/esm/common/diff.d.ts +9 -0
  108. package/dist/esm/common/diff.d.ts.map +1 -0
  109. package/dist/esm/common/diff.js +59 -0
  110. package/dist/esm/common/diff.js.map +1 -0
  111. package/dist/esm/common/has.d.ts +4 -0
  112. package/dist/esm/common/has.d.ts.map +1 -0
  113. package/dist/esm/common/has.js +8 -0
  114. package/dist/esm/common/has.js.map +1 -0
  115. package/dist/esm/common/index.d.ts +12 -0
  116. package/dist/esm/common/index.d.ts.map +1 -0
  117. package/dist/esm/common/index.js +12 -0
  118. package/dist/esm/common/index.js.map +1 -0
  119. package/dist/esm/common/is.d.ts +5 -0
  120. package/dist/esm/common/is.d.ts.map +1 -0
  121. package/dist/esm/common/is.js +13 -0
  122. package/dist/esm/common/is.js.map +1 -0
  123. package/dist/esm/common/match.d.ts +23 -0
  124. package/dist/esm/common/match.d.ts.map +1 -0
  125. package/dist/esm/common/match.js +46 -0
  126. package/dist/esm/common/match.js.map +1 -0
  127. package/dist/esm/common/patch.d.ts +3 -0
  128. package/dist/esm/common/patch.d.ts.map +1 -0
  129. package/dist/esm/common/patch.js +40 -0
  130. package/dist/esm/common/patch.js.map +1 -0
  131. package/dist/esm/common/stack.d.ts +8 -0
  132. package/dist/esm/common/stack.d.ts.map +1 -0
  133. package/dist/esm/common/stack.js +20 -0
  134. package/dist/esm/common/stack.js.map +1 -0
  135. package/dist/esm/common/string.d.ts +2 -0
  136. package/dist/esm/common/string.d.ts.map +1 -0
  137. package/dist/esm/common/string.js +32 -0
  138. package/dist/esm/common/string.js.map +1 -0
  139. package/dist/esm/common/types.d.ts +27 -0
  140. package/dist/esm/common/types.d.ts.map +1 -0
  141. package/dist/esm/common/types.js +2 -0
  142. package/dist/esm/common/types.js.map +1 -0
  143. package/dist/esm/common/unique-array.d.ts +17 -0
  144. package/dist/esm/common/unique-array.d.ts.map +1 -0
  145. package/dist/esm/common/unique-array.js +69 -0
  146. package/dist/esm/common/unique-array.js.map +1 -0
  147. package/dist/esm/context.d.ts +15 -0
  148. package/dist/esm/context.d.ts.map +1 -0
  149. package/dist/esm/context.js +28 -0
  150. package/dist/esm/context.js.map +1 -0
  151. package/dist/esm/derived.d.ts +11 -0
  152. package/dist/esm/derived.d.ts.map +1 -0
  153. package/dist/esm/derived.js +21 -0
  154. package/dist/esm/derived.js.map +1 -0
  155. package/dist/esm/effect.d.ts +7 -0
  156. package/dist/esm/effect.d.ts.map +1 -0
  157. package/dist/esm/effect.js +3 -0
  158. package/dist/esm/effect.js.map +1 -0
  159. package/dist/esm/index.d.ts +8 -0
  160. package/dist/esm/index.d.ts.map +1 -0
  161. package/dist/esm/index.js +8 -0
  162. package/dist/esm/index.js.map +1 -0
  163. package/dist/esm/observable.d.ts +12 -0
  164. package/dist/esm/observable.d.ts.map +1 -0
  165. package/dist/esm/observable.js +19 -0
  166. package/dist/esm/observable.js.map +1 -0
  167. package/dist/esm/package.json +1 -0
  168. package/dist/esm/pubsub.d.ts +11 -0
  169. package/dist/esm/pubsub.d.ts.map +1 -0
  170. package/dist/esm/pubsub.js +47 -0
  171. package/dist/esm/pubsub.js.map +1 -0
  172. package/dist/esm/reference.d.ts +15 -0
  173. package/dist/esm/reference.d.ts.map +1 -0
  174. package/dist/esm/reference.js +44 -0
  175. package/dist/esm/reference.js.map +1 -0
  176. package/dist/esm/utils/findRefs.d.ts +3 -0
  177. package/dist/esm/utils/findRefs.d.ts.map +1 -0
  178. package/dist/esm/utils/findRefs.js +19 -0
  179. package/dist/esm/utils/findRefs.js.map +1 -0
  180. package/dist/esm/utils/index.d.ts +2 -0
  181. package/dist/esm/utils/index.d.ts.map +1 -0
  182. package/dist/esm/utils/index.js +2 -0
  183. package/dist/esm/utils/index.js.map +1 -0
  184. package/package.json +41 -0
  185. package/package.json~ +42 -0
  186. package/src/array-proxy.ts +94 -0
  187. package/src/common/array.ts +72 -0
  188. package/src/common/compare.ts +3 -0
  189. package/src/common/diff.test.ts +114 -0
  190. package/src/common/diff.ts +85 -0
  191. package/src/common/has.ts +7 -0
  192. package/src/common/index.ts +11 -0
  193. package/src/common/is.ts +14 -0
  194. package/src/common/match.ts +90 -0
  195. package/src/common/patch.test.ts +118 -0
  196. package/src/common/patch.ts +47 -0
  197. package/src/common/stack.ts +22 -0
  198. package/src/common/string.test.ts +46 -0
  199. package/src/common/string.ts +35 -0
  200. package/src/common/types.ts +61 -0
  201. package/src/common/unique-array.ts +80 -0
  202. package/src/context.ts +36 -0
  203. package/src/derived.test.ts +45 -0
  204. package/src/derived.ts +35 -0
  205. package/src/effect.ts +9 -0
  206. package/src/index.ts +7 -0
  207. package/src/observable.ts +40 -0
  208. package/src/pubsub.test.ts +29 -0
  209. package/src/pubsub.ts +71 -0
  210. package/src/reference.test.ts +32 -0
  211. package/src/reference.ts +63 -0
  212. package/src/utils/findRefs.ts +22 -0
  213. package/src/utils/index.ts +1 -0
  214. package/tsconfig.cjs.json +8 -0
  215. package/tsconfig.cjs.json~ +0 -0
  216. package/tsconfig.json +24 -0
  217. package/tsconfig.json~ +24 -0
@@ -0,0 +1,19 @@
1
+ import { has } from "./common";
2
+ export const OBSERVABLE = Symbol("Observable");
3
+ export function observable(state, ps) {
4
+ const observe = (fn, options = {}) => {
5
+ if (options.immediate) {
6
+ fn(state.value);
7
+ }
8
+ return ps.subscribe(fn, options.cleanup);
9
+ };
10
+ return {
11
+ [OBSERVABLE]: true,
12
+ ...ps,
13
+ observe,
14
+ };
15
+ }
16
+ export function isObservable(x) {
17
+ return has(x, OBSERVABLE);
18
+ }
19
+ //# sourceMappingURL=observable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observable.js","sourceRoot":"","sources":["../../src/observable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAS/B,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAQ/C,MAAM,UAAU,UAAU,CAAI,KAAe,EAAE,EAAa;IAC1D,MAAM,OAAO,GAAG,CACd,EAAmB,EACnB,UAA4B,EAAE,EAChB,EAAE;QAChB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,OAAO;QACL,CAAC,UAAU,CAAC,EAAE,IAAI;QAClB,GAAG,EAAE;QACL,OAAO;KACR,CAAC;AACJ,CAAC;AAID,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,11 @@
1
+ import { Subscription, VoidFunction } from "./common/types";
2
+ export interface PubSub<T> {
3
+ subscribers: Set<Subscription<T>>;
4
+ effects: Set<VoidFunction>;
5
+ publish: (value: T) => void;
6
+ subscribe: (fn: Subscription<T>, cleanup?: VoidFunction) => VoidFunction;
7
+ addEffect: (effect: VoidFunction, cleanup?: VoidFunction) => VoidFunction;
8
+ dispose: () => void;
9
+ }
10
+ export declare function pubsub<T>(): PubSub<T>;
11
+ //# sourceMappingURL=pubsub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub.d.ts","sourceRoot":"","sources":["../../src/pubsub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,WAAW,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,SAAS,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,KAAK,YAAY,CAAC;IACzE,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,YAAY,KAAK,YAAY,CAAC;IAC1E,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CA0DrC"}
@@ -0,0 +1,47 @@
1
+ import { Context } from "./context";
2
+ export function pubsub() {
3
+ const subscribers = new Set();
4
+ const effects = new Set();
5
+ const trash = new Set();
6
+ const addTrash = (fn) => {
7
+ trash.add(fn);
8
+ return fn;
9
+ };
10
+ const publish = (value) => {
11
+ effects.forEach((fx) => {
12
+ Context.runEffect(fx);
13
+ });
14
+ subscribers.forEach((sub) => {
15
+ sub(value);
16
+ });
17
+ };
18
+ const addEffect = (effect, cleanup) => {
19
+ effects.add(effect);
20
+ return addTrash(() => {
21
+ effects.delete(effect);
22
+ cleanup?.();
23
+ });
24
+ };
25
+ const subscribe = (fn, cleanup) => {
26
+ subscribers.add(fn);
27
+ return addTrash(() => {
28
+ subscribers.delete(fn);
29
+ cleanup?.();
30
+ });
31
+ };
32
+ const dispose = () => {
33
+ trash.forEach((fn) => fn());
34
+ trash.clear();
35
+ subscribers.clear();
36
+ effects.clear();
37
+ };
38
+ return {
39
+ subscribers,
40
+ effects,
41
+ publish,
42
+ subscribe,
43
+ addEffect,
44
+ dispose,
45
+ };
46
+ }
47
+ //# sourceMappingURL=pubsub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub.js","sourceRoot":"","sources":["../../src/pubsub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,MAAM,UAAU,MAAM;IACpB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEtC,MAAM,QAAQ,GAAG,CAAC,EAAgB,EAAgB,EAAE;QAClD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,KAAQ,EAAQ,EAAE;QACjC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;YACrB,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YAC1B,GAAG,CAAC,KAAK,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAChB,MAAoB,EACpB,OAAsB,EACR,EAAE;QAChB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpB,OAAO,QAAQ,CAAC,GAAG,EAAE;YACnB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvB,OAAO,EAAE,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAChB,EAAmB,EACnB,OAAsB,EACR,EAAE;QAChB,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEpB,OAAO,QAAQ,CAAC,GAAG,EAAE;YACnB,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACvB,OAAO,EAAE,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,WAAW,CAAC,KAAK,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO;QACL,WAAW;QACX,OAAO;QACP,OAAO;QACP,SAAS;QACT,SAAS;QACT,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Dispatch, Loose, SetStateAction } from "./common/types";
2
+ import { Observable } from "./observable";
3
+ export declare const REFERENCE: unique symbol;
4
+ export type REFERENCE = typeof REFERENCE;
5
+ export interface Reference<T> extends Observable<T> {
6
+ [REFERENCE]: true;
7
+ get: () => T;
8
+ set: Dispatch<SetStateAction<T>>;
9
+ peek: () => T;
10
+ }
11
+ export declare function ref<T>(value: T): Reference<T>;
12
+ export declare function isReference(x: Loose): x is Reference<Loose>;
13
+ export declare function isReference(x: unknown): x is Reference<unknown>;
14
+ export declare function isReference<X extends Reference<unknown>>(x: X): x is X;
15
+ //# sourceMappingURL=reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/reference.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGxE,OAAO,EAAc,UAAU,EAAE,MAAM,cAAc,CAAC;AAGtD,eAAO,MAAM,SAAS,eAAsB,CAAC;AAC7C,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC;AAEzC,MAAM,WAAW,SAAS,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACjD,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,CAAC;CACf;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAoC7C;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;AAC7D,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;AACjE,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { has } from "./common";
2
+ import { compare } from "./common/compare";
3
+ import { isFunction } from "./common/is";
4
+ import { Context } from "./context";
5
+ import { observable } from "./observable";
6
+ import { pubsub } from "./pubsub";
7
+ export const REFERENCE = Symbol("Reference");
8
+ export function ref(value) {
9
+ const ps = pubsub();
10
+ const state = { value };
11
+ const obs = observable(state, ps);
12
+ const addEffectDependency = (fx) => {
13
+ ps.addEffect(fx.run, fx.cleanup);
14
+ };
15
+ const get = () => {
16
+ const fx = Context.scope.current.effect;
17
+ if (fx)
18
+ addEffectDependency(fx);
19
+ return state.value;
20
+ };
21
+ const set = (fn) => {
22
+ const prev = state.value;
23
+ const next = isFunction(fn) ? fn(state.value) : fn;
24
+ if (!compare(next, prev)) {
25
+ state.value = next;
26
+ ps.publish(state.value);
27
+ }
28
+ };
29
+ const peek = () => {
30
+ return state.value;
31
+ };
32
+ return {
33
+ [REFERENCE]: true,
34
+ ...ps,
35
+ ...obs,
36
+ get,
37
+ set,
38
+ peek,
39
+ };
40
+ }
41
+ export function isReference(x) {
42
+ return has(x, REFERENCE);
43
+ }
44
+ //# sourceMappingURL=reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.js","sourceRoot":"","sources":["../../src/reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAc,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAU7C,MAAM,UAAU,GAAG,CAAI,KAAQ;IAC7B,MAAM,EAAE,GAAG,MAAM,EAAK,CAAC;IACvB,MAAM,KAAK,GAAa,EAAE,KAAK,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,UAAU,CAAI,KAAK,EAAE,EAAE,CAAC,CAAC;IAErC,MAAM,mBAAmB,GAAG,CAAC,EAAU,EAAE,EAAE;QACzC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,GAAM,EAAE;QAClB,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACxC,IAAI,EAAE;YAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC,CAAC;IAEF,MAAM,GAAG,GAAgC,CAAC,EAAE,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;QACzB,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;YACnB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,GAAM,EAAE;QACnB,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO;QACL,CAAC,SAAS,CAAC,EAAE,IAAI;QACjB,GAAG,EAAE;QACL,GAAG,GAAG;QACN,GAAG;QACH,GAAG;QACH,IAAI;KACL,CAAC;AACJ,CAAC;AAKD,MAAM,UAAU,WAAW,CAAC,CAAU;IACpC,OAAO,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Reference } from "../reference";
2
+ export declare const findRefs: (x: unknown) => Reference<unknown>[];
3
+ //# sourceMappingURL=findRefs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findRefs.d.ts","sourceRoot":"","sources":["../../../src/utils/findRefs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAEtD,eAAO,MAAM,QAAQ,GAAI,GAAG,OAAO,KAAG,SAAS,CAAC,OAAO,CAAC,EAmBvD,CAAA"}
@@ -0,0 +1,19 @@
1
+ import { isReference } from "../reference";
2
+ export const findRefs = (x) => {
3
+ if (typeof x === 'undefined' || x === null)
4
+ return [];
5
+ if (isReference(x))
6
+ return [x];
7
+ if (Array.isArray(x)) {
8
+ return x.map(it => findRefs(it)).flat();
9
+ }
10
+ if (typeof x === 'object' && x !== null) {
11
+ const refs = [];
12
+ for (const [_k, v] of Object.entries(x)) {
13
+ refs.push(...findRefs(v));
14
+ }
15
+ return refs;
16
+ }
17
+ return [];
18
+ };
19
+ //# sourceMappingURL=findRefs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findRefs.js","sourceRoot":"","sources":["../../../src/utils/findRefs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAa,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAwB,EAAE;IAE3D,IAAI,OAAO,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACtD,IAAI,WAAW,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAG/B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,GAA8B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './findRefs';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './findRefs';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@derivesome/core",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "./dist/cjs/index.js",
6
+ "types": "./dist/cjs/index.d.ts",
7
+ "typings": "./dist/cjs/index.d.ts",
8
+ "scripts": {
9
+ "test": "vitest",
10
+ "build": "tsc -p tsconfig.json && echo '{\"type\":\"module\"}' > dist/esm/package.json && tsc -p tsconfig.cjs.json",
11
+ "format": "prettier ./src --write",
12
+ "check": "tsc -p ./tsconfig.json --noEmit"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "packageManager": "pnpm@10.28.2",
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "types": "./dist/esm/index.d.ts",
22
+ "default": "./dist/esm/index.js"
23
+ },
24
+ "require": {
25
+ "types": "./dist/cjs/index.d.ts",
26
+ "default": "./dist/cjs/index.js"
27
+ },
28
+ "default": {
29
+ "types": "./dist/cjs/index.d.ts",
30
+ "default": "./dist/cjs/index.js"
31
+ }
32
+ }
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.2.1",
36
+ "prettier": "^3.8.1",
37
+ "tsx": "^4.21.0",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18"
40
+ }
41
+ }
package/package.json~ ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@derivesome/core",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "",
6
+ "main": "./dist/cjs/index.js",
7
+ "types": "./dist/cjs/index.d.ts",
8
+ "typings": "./dist/cjs/index.d.ts",
9
+ "scripts": {
10
+ "test": "vitest",
11
+ "build": "tsc -p tsconfig.json && echo '{\"type\":\"module\"}' > dist/esm/package.json && tsc -p tsconfig.cjs.json",
12
+ "format": "prettier ./src --write",
13
+ "check": "tsc -p ./tsconfig.json --noEmit"
14
+ },
15
+ "keywords": [],
16
+ "author": "",
17
+ "license": "ISC",
18
+ "packageManager": "pnpm@10.28.2",
19
+ "exports": {
20
+ ".": {
21
+ "import": {
22
+ "types": "./dist/esm/index.d.ts",
23
+ "default": "./dist/esm/index.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/cjs/index.d.ts",
27
+ "default": "./dist/cjs/index.js"
28
+ },
29
+ "default": {
30
+ "types": "./dist/cjs/index.d.ts",
31
+ "default": "./dist/cjs/index.js"
32
+ }
33
+ }
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^25.2.1",
37
+ "prettier": "^3.8.1",
38
+ "tsx": "^4.21.0",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^4.0.18"
41
+ }
42
+ }
@@ -0,0 +1,94 @@
1
+ import { PubSub, pubsub } from "./pubsub";
2
+ import { VoidFunction } from "./common/types";
3
+
4
+ export type ArrayMutation<T> =
5
+ | { type: "push"; items: T[] }
6
+ | { type: "pop"; removed: T | undefined }
7
+ | { type: "shift"; removed: T | undefined }
8
+ | { type: "unshift"; items: T[] }
9
+ | { type: "splice"; start: number; deleteCount: number; removed: T[]; added: T[] }
10
+ | { type: "sort" }
11
+ | { type: "reverse" }
12
+ | { type: "fill"; value: T; start: number; end: number }
13
+ | { type: "copyWithin"; target: number; start: number; end: number }
14
+ | { type: "set"; index: number; value: T };
15
+
16
+ export class ArrayProxy<T> extends Array<T> {
17
+ readonly ps: PubSub<ArrayMutation<T>>;
18
+
19
+ constructor(...items: T[]) {
20
+ super(...items);
21
+ this.ps = pubsub<ArrayMutation<T>>();
22
+ }
23
+
24
+ subscribe(fn: (mutation: ArrayMutation<T>) => void, cleanup?: VoidFunction): VoidFunction {
25
+ return this.ps.subscribe(fn, cleanup);
26
+ }
27
+
28
+ override push(...items: T[]): number {
29
+ const result = super.push(...items);
30
+ this.ps.publish({ type: "push", items });
31
+ return result;
32
+ }
33
+
34
+ override pop(): T | undefined {
35
+ const removed = super.pop();
36
+ this.ps.publish({ type: "pop", removed });
37
+ return removed;
38
+ }
39
+
40
+ override shift(): T | undefined {
41
+ const removed = super.shift();
42
+ this.ps.publish({ type: "shift", removed });
43
+ return removed;
44
+ }
45
+
46
+ override unshift(...items: T[]): number {
47
+ const result = super.unshift(...items);
48
+ this.ps.publish({ type: "unshift", items });
49
+ return result;
50
+ }
51
+
52
+ override splice(start: number, deleteCount?: number, ...items: T[]): T[] {
53
+ const dc = deleteCount ?? this.length - start;
54
+ const removed = super.splice(start, dc, ...items);
55
+ this.ps.publish({ type: "splice", start, deleteCount: dc, removed, added: items });
56
+ return removed;
57
+ }
58
+
59
+ override sort(compareFn?: (a: T, b: T) => number): this {
60
+ super.sort(compareFn);
61
+ this.ps.publish({ type: "sort" });
62
+ return this;
63
+ }
64
+
65
+ override reverse(): this {
66
+ super.reverse();
67
+ this.ps.publish({ type: "reverse" });
68
+ return this;
69
+ }
70
+
71
+ override fill(value: T, start?: number, end?: number): this {
72
+ const s = start ?? 0;
73
+ const e = end ?? this.length;
74
+ super.fill(value, s, e);
75
+ this.ps.publish({ type: "fill", value, start: s, end: e });
76
+ return this;
77
+ }
78
+
79
+ override copyWithin(target: number, start: number, end?: number): this {
80
+ const e = end ?? this.length;
81
+ super.copyWithin(target, start, e);
82
+ this.ps.publish({ type: "copyWithin", target, start, end: e });
83
+ return this;
84
+ }
85
+
86
+ set(index: number, value: T): void {
87
+ this[index] = value;
88
+ this.ps.publish({ type: "set", index, value });
89
+ }
90
+
91
+ dispose(): void {
92
+ this.ps.dispose();
93
+ }
94
+ }
@@ -0,0 +1,72 @@
1
+ export const range = (n: number): Array<number> =>
2
+ n <= 0 ? [] : Array.from(Array(Math.floor(n)).keys());
3
+
4
+ export const enumerate = <T>(arr: T[]): Array<[number, T]> => {
5
+ return arr.map((v, i) => [i, v]);
6
+ };
7
+
8
+ export const zipMin = <A, B>(a: A[], b: B[]): Array<[A, B]> => {
9
+ const length = Math.min(a.length, b.length);
10
+ return range(length).map((i) => [a[i]!, b[i]!]);
11
+ };
12
+
13
+ export const zipMax = <A, B>(a: A[], b: B[]): Array<[A | undefined, B | undefined]> => {
14
+ const length = Math.max(a.length, b.length);
15
+ return range(length).map((i) => [a[i], b[i]]);
16
+ };
17
+
18
+ export const zipByKey = <A, B>(
19
+ a: A[],
20
+ b: B[],
21
+ getKey: <T extends A | B>(item: T) => PropertyKey | null,
22
+ ): { pairs: Array<[A, B]>, restA: A[], restB: B[] } => {
23
+ const keys = Array.from(
24
+ new Set(
25
+ [...a.map((x) => getKey(x)), ...b.map((x) => getKey(x))].filter(
26
+ (x) => x !== null,
27
+ ),
28
+ ),
29
+ );
30
+
31
+ const ma = new Map(
32
+ a
33
+ .map((x): [PropertyKey | null, A] => [getKey(x), x])
34
+ .filter(([k, _]) => k !== null),
35
+ );
36
+ const mb = new Map(
37
+ b
38
+ .map((x): [PropertyKey | null, B] => [getKey(x), x])
39
+ .filter(([k, _]) => k !== null),
40
+ );
41
+
42
+ const z: Array<[A, B]> = [];
43
+ const restA: A[] = [];
44
+ const restB: B[] = [];
45
+ const seenA = new Set<A>();
46
+ const seenB = new Set<B>();
47
+
48
+ for (const k of keys) {
49
+ const va = ma.get(k) ?? null;
50
+ const vb = mb.get(k) ?? null;
51
+
52
+ if (va !== null && vb === null) {
53
+ restA.push(va);
54
+ seenA.add(va);
55
+ } else if (va === null && vb !== null) {
56
+ restB.push(vb);
57
+ seenB.add(vb);
58
+ } else if (va !== null && vb !== null) {
59
+ z.push([va, vb]);
60
+ seenA.add(va);
61
+ seenB.add(vb);
62
+ }
63
+ }
64
+
65
+
66
+ return { pairs: z, restA, restB };
67
+ };
68
+
69
+
70
+ export const unique = <T>(arr: T[]): T[] => {
71
+ return Array.from(new Set<T>(arr));
72
+ }
@@ -0,0 +1,3 @@
1
+ export const compare = (a: unknown, b: unknown): boolean => {
2
+ return a === b;
3
+ };
@@ -0,0 +1,114 @@
1
+ import { describe, it, assert } from 'vitest';
2
+ import { diff } from './diff';
3
+
4
+ describe('diff', () => {
5
+ it('should return empty array for identical primitives', () => {
6
+ assert.deepEqual(diff(1, 1), []);
7
+ assert.deepEqual(diff('hello', 'hello'), []);
8
+ assert.deepEqual(diff(true, true), []);
9
+ });
10
+
11
+ it('should detect changed primitives', () => {
12
+ const result = diff(1, 2);
13
+ assert.deepEqual(result, [
14
+ { type: 'changed', path: [], oldValue: 1, newValue: 2 },
15
+ ]);
16
+ });
17
+
18
+ it('should return empty array for identical objects', () => {
19
+ assert.deepEqual(diff({ a: 1, b: 2 }, { a: 1, b: 2 }), []);
20
+ });
21
+
22
+ it('should detect changed properties', () => {
23
+ const result = diff({ a: 1 }, { a: 2 });
24
+ assert.deepEqual(result, [
25
+ { type: 'changed', path: ['a'], oldValue: 1, newValue: 2 },
26
+ ]);
27
+ });
28
+
29
+ it('should detect added properties', () => {
30
+ const result = diff({ a: 1 } as any, { a: 1, b: 2 } as any);
31
+ assert.deepEqual(result, [
32
+ { type: 'added', path: ['b'], newValue: 2 },
33
+ ]);
34
+ });
35
+
36
+ it('should detect removed properties', () => {
37
+ const result = diff({ a: 1, b: 2 } as any, { a: 1 } as any);
38
+ assert.deepEqual(result, [
39
+ { type: 'removed', path: ['b'], oldValue: 2 },
40
+ ]);
41
+ });
42
+
43
+ it('should handle nested object diffs', () => {
44
+ const a = { x: { y: { z: 1 } } };
45
+ const b = { x: { y: { z: 2 } } };
46
+ const result = diff(a, b);
47
+ assert.deepEqual(result, [
48
+ { type: 'changed', path: ['x', 'y', 'z'], oldValue: 1, newValue: 2 },
49
+ ]);
50
+ });
51
+
52
+ it('should handle array element changes', () => {
53
+ const result = diff([1, 2, 3], [1, 9, 3]);
54
+ assert.deepEqual(result, [
55
+ { type: 'changed', path: [1], oldValue: 2, newValue: 9 },
56
+ ]);
57
+ });
58
+
59
+ it('should detect added array elements', () => {
60
+ const result = diff([1, 2], [1, 2, 3]);
61
+ assert.deepEqual(result, [
62
+ { type: 'added', path: [2], newValue: 3 },
63
+ ]);
64
+ });
65
+
66
+ it('should detect removed array elements', () => {
67
+ const result = diff([1, 2, 3], [1, 2]);
68
+ assert.deepEqual(result, [
69
+ { type: 'removed', path: [2], oldValue: 3 },
70
+ ]);
71
+ });
72
+
73
+ it('should handle nested arrays inside objects', () => {
74
+ const a = { items: [1, 2] };
75
+ const b = { items: [1, 3] };
76
+ const result = diff(a, b);
77
+ assert.deepEqual(result, [
78
+ { type: 'changed', path: ['items', 1], oldValue: 2, newValue: 3 },
79
+ ]);
80
+ });
81
+
82
+ it('should detect type change from object to primitive', () => {
83
+ const result = diff({ a: { b: 1 } } as any, { a: 42 } as any);
84
+ assert.deepEqual(result, [
85
+ { type: 'changed', path: ['a'], oldValue: { b: 1 }, newValue: 42 },
86
+ ]);
87
+ });
88
+
89
+ it('should detect type change from array to object', () => {
90
+ const result = diff({ a: [1, 2] } as any, { a: { x: 1 } } as any);
91
+ assert.deepEqual(result, [
92
+ { type: 'changed', path: ['a'], oldValue: [1, 2], newValue: { x: 1 } },
93
+ ]);
94
+ });
95
+
96
+ it('should handle symbol keys', () => {
97
+ const sym = Symbol('test');
98
+ const a = { [sym]: 1 };
99
+ const b = { [sym]: 2 };
100
+ const result = diff(a, b);
101
+ assert.deepEqual(result, [
102
+ { type: 'changed', path: [sym], oldValue: 1, newValue: 2 },
103
+ ]);
104
+ });
105
+
106
+ it('should handle deeply nested mixed structures', () => {
107
+ const a = { users: [{ name: 'Alice', scores: [10, 20] }] };
108
+ const b = { users: [{ name: 'Alice', scores: [10, 30] }] };
109
+ const result = diff(a, b);
110
+ assert.deepEqual(result, [
111
+ { type: 'changed', path: ['users', 0, 'scores', 1], oldValue: 20, newValue: 30 },
112
+ ]);
113
+ });
114
+ });
@@ -0,0 +1,85 @@
1
+ import { isPlainObject } from './is';
2
+
3
+ export type DiffType = 'added' | 'removed' | 'changed';
4
+
5
+ export type Diff = {
6
+ type: DiffType;
7
+ path: PropertyKey[];
8
+ oldValue?: unknown;
9
+ newValue?: unknown;
10
+ };
11
+
12
+ export const diff = <T>(a: T, b: T): Diff[] => {
13
+ const diffs: Diff[] = [];
14
+ diffRecursive(a, b, [], diffs);
15
+ return diffs;
16
+ };
17
+
18
+ const diffRecursive = (a: unknown, b: unknown, path: PropertyKey[], diffs: Diff[]): void => {
19
+ if (a === b) return;
20
+
21
+ const aIsArray = Array.isArray(a);
22
+ const bIsArray = Array.isArray(b);
23
+
24
+ if (aIsArray && bIsArray) {
25
+ diffArrays(a, b, path, diffs);
26
+ return;
27
+ }
28
+
29
+ const aIsObject = isPlainObject(a);
30
+ const bIsObject = isPlainObject(b);
31
+
32
+ if (aIsObject && bIsObject) {
33
+ diffObjects(a, b, path, diffs);
34
+ return;
35
+ }
36
+
37
+ diffs.push({ type: 'changed', path, oldValue: a, newValue: b });
38
+ };
39
+
40
+ const diffObjects = (
41
+ a: Record<PropertyKey, unknown>,
42
+ b: Record<PropertyKey, unknown>,
43
+ path: PropertyKey[],
44
+ diffs: Diff[],
45
+ ): void => {
46
+ const aKeys = Reflect.ownKeys(a);
47
+ const bKeys = Reflect.ownKeys(b);
48
+ const bKeySet = new Set(bKeys);
49
+
50
+ for (const key of aKeys) {
51
+ const childPath = [...path, key];
52
+ if (!bKeySet.has(key)) {
53
+ diffs.push({ type: 'removed', path: childPath, oldValue: a[key] });
54
+ } else {
55
+ diffRecursive(a[key], b[key], childPath, diffs);
56
+ }
57
+ }
58
+
59
+ const aKeySet = new Set(aKeys);
60
+ for (const key of bKeys) {
61
+ if (!aKeySet.has(key)) {
62
+ diffs.push({ type: 'added', path: [...path, key], newValue: b[key] });
63
+ }
64
+ }
65
+ };
66
+
67
+ const diffArrays = (
68
+ a: unknown[],
69
+ b: unknown[],
70
+ path: PropertyKey[],
71
+ diffs: Diff[],
72
+ ): void => {
73
+ const maxLen = Math.max(a.length, b.length);
74
+
75
+ for (let i = 0; i < maxLen; i++) {
76
+ const childPath = [...path, i];
77
+ if (i >= a.length) {
78
+ diffs.push({ type: 'added', path: childPath, newValue: b[i] });
79
+ } else if (i >= b.length) {
80
+ diffs.push({ type: 'removed', path: childPath, oldValue: a[i] });
81
+ } else {
82
+ diffRecursive(a[i], b[i], childPath, diffs);
83
+ }
84
+ }
85
+ };