@decohere/core 0.0.1-dev.126 → 0.0.1-dev.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.
Files changed (252) hide show
  1. package/package.json +40 -15
  2. package/src/caching/cache-manager.ts +102 -0
  3. package/src/caching/cache.decorator.ts +56 -0
  4. package/src/caching/cache.ts +11 -0
  5. package/{es6/src/caching/index.js → src/caching/index.ts} +1 -1
  6. package/src/caching/invalidate-cache.decorator.ts +81 -0
  7. package/src/caching/memoize.decorator.ts +34 -0
  8. package/src/caching/node-cache-memoize.adapter.ts +47 -0
  9. package/src/cli/action.ts +77 -0
  10. package/src/cli/command.ts +173 -0
  11. package/src/cli/index.ts +30 -0
  12. package/src/cli/module.type.ts +48 -0
  13. package/src/cli/options.ts +81 -0
  14. package/src/collections/find-duplicates.ts +16 -0
  15. package/src/collections/merge-arrays.ts +9 -0
  16. package/src/collections/sum.ts +71 -0
  17. package/src/collections/unique-objects.ts +23 -0
  18. package/src/composition/index.ts +48 -0
  19. package/src/configuration/configuration.types.ts +13 -0
  20. package/src/diagnostics/index.ts +1 -0
  21. package/src/diagnostics/trace.ts +97 -0
  22. package/{es6/src/extensibility/decorator-factory-base.js → src/extensibility/decorator-factory-base.ts} +17 -9
  23. package/src/extensibility/decorator-factory.ts +44 -0
  24. package/src/extensibility/extension.ts +34 -0
  25. package/src/extensibility/mixwith.ts +42 -0
  26. package/src/extensibility/pipeline.ts +60 -0
  27. package/{types/src/extensibility/step.d.ts → src/extensibility/step.ts} +6 -4
  28. package/src/interop/docker/docker.ts +57 -0
  29. package/src/interop/errors/interop.error.ts +1 -0
  30. package/src/interop/git/git.ts +98 -0
  31. package/src/interop/git/git.types.ts +17 -0
  32. package/src/interop/index.ts +22 -0
  33. package/src/interop/node/index.ts +1 -0
  34. package/src/interop/node/node.ts +38 -0
  35. package/src/interop/npm/npm.ts +168 -0
  36. package/src/interop/npm/npm.types.ts +28 -0
  37. package/src/interop/pnpm/pnpm.ts +22 -0
  38. package/src/interop/pnpm/pnpm.types.ts +3 -0
  39. package/src/interop/shell/shell.argv.ts +31 -0
  40. package/src/interop/shell/shell.ts +100 -0
  41. package/src/interop/shell/shell.types.ts +9 -0
  42. package/src/interop/types/detect-package-manager.ts +45 -0
  43. package/src/interop/types/package-manager.ts +7 -0
  44. package/src/interop/yarn/yarn.ts +28 -0
  45. package/src/interop/yarn/yarn.types.ts +3 -0
  46. package/src/io/copy-file.ts +13 -0
  47. package/src/io/delete-file.ts +4 -0
  48. package/src/io/directory-exists.ts +9 -0
  49. package/src/io/file-exists.ts +10 -0
  50. package/src/io/find-directories.ts +13 -0
  51. package/src/io/find-file.ts +37 -0
  52. package/{es6/src/io/io.js → src/io/io.ts} +3 -0
  53. package/src/io/read-file.ts +25 -0
  54. package/src/io/remove-dir.ts +11 -0
  55. package/src/io/types/directory.type.ts +4 -0
  56. package/src/io/types/file.ts +34 -0
  57. package/src/io/url-to-path.ts +17 -0
  58. package/src/io/watch.ts +290 -0
  59. package/src/io/write-file.ts +21 -0
  60. package/src/objects/deep-merge.ts +131 -0
  61. package/{es6/src/reflection/index.js → src/reflection/index.ts} +1 -0
  62. package/src/reflection/types/reflection.types.ts +28 -0
  63. package/src/reflection/utilities.ts +92 -0
  64. package/src/semver/index.ts +1 -0
  65. package/src/semver/semver.ts +96 -0
  66. package/src/system/camel-case.ts +3 -0
  67. package/src/system/debounce.ts +13 -0
  68. package/src/system/decorator.ts +41 -0
  69. package/src/system/environment.ts +6 -0
  70. package/{types/src/system/function.types.d.ts → src/system/function.types.ts} +10 -2
  71. package/src/system/retry.decorator.ts +43 -0
  72. package/src/system/sleep.ts +6 -0
  73. package/src/system/type.utils.ts +5 -0
  74. package/src/testing/flush-promises.ts +7 -0
  75. package/src/testing/glob-with-memfs.ts +25 -0
  76. package/src/testing/index.ts +5 -0
  77. package/src/testing/memfs.ts +28 -0
  78. package/src/testing/mock-logger.ts +42 -0
  79. package/src/testing/spy-on-constructor.ts +33 -0
  80. package/tests/caching/cache-manager.spec.ts +147 -0
  81. package/tests/caching/cache-test-class.ts +91 -0
  82. package/tests/caching/cache.decorator.spec.ts +259 -0
  83. package/tests/caching/invalidate-cache-test-class.ts +52 -0
  84. package/tests/caching/invalidate-cache.decorator.spec.ts +120 -0
  85. package/tests/caching/memoize-test-class.ts +99 -0
  86. package/tests/caching/memoize.decorator.spec.ts +203 -0
  87. package/tests/cli/command.model.spec.ts +91 -0
  88. package/tests/collections/merge-arrays.spec.ts +50 -0
  89. package/tests/collections/sum.spec.ts +86 -0
  90. package/tests/collections/unique-objects.spec.ts +233 -0
  91. package/tests/extensibility/extension.spec.ts +29 -0
  92. package/tests/extensibility/pipeline.spec.ts +379 -0
  93. package/tests/interop/detect-package-manager.spec.ts +70 -0
  94. package/tests/interop/npm.spec.ts +129 -0
  95. package/tests/interop/pnpm.spec.ts +30 -0
  96. package/tests/interop/shell.spec.ts +47 -0
  97. package/tests/interop/yarn.spec.ts +18 -0
  98. package/tests/io/find-file.spec.ts +188 -0
  99. package/tests/io/watch.real-fs.spec.ts +76 -0
  100. package/tests/io/watch.spec.ts +291 -0
  101. package/tests/mock-prototype.ts +12 -0
  102. package/tests/semver/semver.spec.ts +51 -0
  103. package/tests/system/decorator.spec.ts +250 -0
  104. package/tests/system/retry.decorator.spec.ts +57 -0
  105. package/LICENSE +0 -15
  106. package/commonjs/src/caching/cache-manager.js +0 -98
  107. package/commonjs/src/caching/cache.decorator.js +0 -69
  108. package/commonjs/src/caching/cache.js +0 -6
  109. package/commonjs/src/caching/index.js +0 -23
  110. package/commonjs/src/caching/invalidate-cache.decorator.js +0 -92
  111. package/commonjs/src/caching/map-cache.js +0 -39
  112. package/commonjs/src/caching/memoize.decorator.js +0 -36
  113. package/commonjs/src/collections/collections.js +0 -21
  114. package/commonjs/src/collections/find-duplicates.js +0 -35
  115. package/commonjs/src/collections/index.js +0 -52
  116. package/commonjs/src/collections/merge-arrays.js +0 -34
  117. package/commonjs/src/collections/sum.js +0 -63
  118. package/commonjs/src/collections/unique-objects.js +0 -39
  119. package/commonjs/src/composition/index.js +0 -4
  120. package/commonjs/src/configuration/configuration.types.js +0 -11
  121. package/commonjs/src/configuration/index.js +0 -18
  122. package/commonjs/src/extensibility/decorator-factory-base.js +0 -22
  123. package/commonjs/src/extensibility/decorator-factory.js +0 -31
  124. package/commonjs/src/extensibility/extension.js +0 -37
  125. package/commonjs/src/extensibility/index.js +0 -23
  126. package/commonjs/src/extensibility/mixwith.js +0 -36
  127. package/commonjs/src/extensibility/pipeline.js +0 -56
  128. package/commonjs/src/extensibility/step.js +0 -18
  129. package/commonjs/src/io/copy-file.js +0 -25
  130. package/commonjs/src/io/delete-file.js +0 -21
  131. package/commonjs/src/io/directory-exists.js +0 -24
  132. package/commonjs/src/io/file-exists.js +0 -19
  133. package/commonjs/src/io/index.js +0 -66
  134. package/commonjs/src/io/io.js +0 -25
  135. package/commonjs/src/io/read-file.js +0 -39
  136. package/commonjs/src/io/remove-dir.js +0 -22
  137. package/commonjs/src/io/types/directory.type.js +0 -4
  138. package/commonjs/src/io/types/file.js +0 -28
  139. package/commonjs/src/io/types/index.js +0 -19
  140. package/commonjs/src/io/url-to-path.js +0 -33
  141. package/commonjs/src/io/write-file.js +0 -30
  142. package/commonjs/src/objects/deep-merge.js +0 -67
  143. package/commonjs/src/objects/index.js +0 -11
  144. package/commonjs/src/reflection/index.js +0 -20
  145. package/commonjs/src/reflection/types/index.js +0 -18
  146. package/commonjs/src/reflection/types/reflection.types.js +0 -26
  147. package/commonjs/src/reflection/utilities.js +0 -118
  148. package/commonjs/src/system/camel-case.js +0 -13
  149. package/commonjs/src/system/debounce.js +0 -19
  150. package/commonjs/src/system/decorator.js +0 -46
  151. package/commonjs/src/system/environment.js +0 -23
  152. package/commonjs/src/system/function.types.js +0 -18
  153. package/commonjs/src/system/index.js +0 -24
  154. package/commonjs/src/system/retry.decorator.js +0 -40
  155. package/commonjs/src/system/sleep.js +0 -13
  156. package/commonjs/src/system/type.utils.js +0 -4
  157. package/es6/src/caching/cache-manager.js +0 -82
  158. package/es6/src/caching/cache.decorator.js +0 -51
  159. package/es6/src/caching/cache.js +0 -3
  160. package/es6/src/caching/invalidate-cache.decorator.js +0 -71
  161. package/es6/src/caching/map-cache.js +0 -35
  162. package/es6/src/caching/memoize.decorator.js +0 -27
  163. package/es6/src/collections/find-duplicates.js +0 -17
  164. package/es6/src/collections/merge-arrays.js +0 -16
  165. package/es6/src/collections/sum.js +0 -45
  166. package/es6/src/collections/unique-objects.js +0 -21
  167. package/es6/src/composition/index.js +0 -1
  168. package/es6/src/configuration/configuration.types.js +0 -1
  169. package/es6/src/extensibility/decorator-factory.js +0 -21
  170. package/es6/src/extensibility/extension.js +0 -27
  171. package/es6/src/extensibility/mixwith.js +0 -26
  172. package/es6/src/extensibility/pipeline.js +0 -46
  173. package/es6/src/extensibility/step.js +0 -16
  174. package/es6/src/io/copy-file.js +0 -10
  175. package/es6/src/io/delete-file.js +0 -6
  176. package/es6/src/io/directory-exists.js +0 -9
  177. package/es6/src/io/file-exists.js +0 -9
  178. package/es6/src/io/read-file.js +0 -13
  179. package/es6/src/io/remove-dir.js +0 -7
  180. package/es6/src/io/types/directory.type.js +0 -1
  181. package/es6/src/io/types/file.js +0 -18
  182. package/es6/src/io/url-to-path.js +0 -15
  183. package/es6/src/io/write-file.js +0 -15
  184. package/es6/src/objects/deep-merge.js +0 -57
  185. package/es6/src/reflection/types/reflection.types.js +0 -8
  186. package/es6/src/reflection/utilities.js +0 -74
  187. package/es6/src/system/camel-case.js +0 -3
  188. package/es6/src/system/debounce.js +0 -9
  189. package/es6/src/system/decorator.js +0 -30
  190. package/es6/src/system/environment.js +0 -5
  191. package/es6/src/system/function.types.js +0 -26
  192. package/es6/src/system/retry.decorator.js +0 -30
  193. package/es6/src/system/sleep.js +0 -3
  194. package/es6/src/system/type.utils.js +0 -1
  195. package/types/src/caching/cache-manager.d.ts +0 -59
  196. package/types/src/caching/cache.d.ts +0 -11
  197. package/types/src/caching/cache.decorator.d.ts +0 -18
  198. package/types/src/caching/index.d.ts +0 -6
  199. package/types/src/caching/invalidate-cache.decorator.d.ts +0 -17
  200. package/types/src/caching/map-cache.d.ts +0 -23
  201. package/types/src/caching/memoize.decorator.d.ts +0 -16
  202. package/types/src/collections/collections.d.ts +0 -4
  203. package/types/src/collections/find-duplicates.d.ts +0 -2
  204. package/types/src/collections/index.d.ts +0 -1
  205. package/types/src/collections/merge-arrays.d.ts +0 -2
  206. package/types/src/collections/sum.d.ts +0 -4
  207. package/types/src/collections/unique-objects.d.ts +0 -2
  208. package/types/src/composition/index.d.ts +0 -37
  209. package/types/src/configuration/configuration.types.d.ts +0 -10
  210. package/types/src/configuration/index.d.ts +0 -1
  211. package/types/src/extensibility/decorator-factory-base.d.ts +0 -39
  212. package/types/src/extensibility/decorator-factory.d.ts +0 -19
  213. package/types/src/extensibility/extension.d.ts +0 -9
  214. package/types/src/extensibility/index.d.ts +0 -6
  215. package/types/src/extensibility/mixwith.d.ts +0 -6
  216. package/types/src/extensibility/pipeline.d.ts +0 -12
  217. package/types/src/io/copy-file.d.ts +0 -4
  218. package/types/src/io/delete-file.d.ts +0 -1
  219. package/types/src/io/directory-exists.d.ts +0 -1
  220. package/types/src/io/file-exists.d.ts +0 -1
  221. package/types/src/io/index.d.ts +0 -2
  222. package/types/src/io/io.d.ts +0 -8
  223. package/types/src/io/read-file.d.ts +0 -4
  224. package/types/src/io/remove-dir.d.ts +0 -5
  225. package/types/src/io/types/directory.type.d.ts +0 -4
  226. package/types/src/io/types/file.d.ts +0 -20
  227. package/types/src/io/types/index.d.ts +0 -2
  228. package/types/src/io/url-to-path.d.ts +0 -2
  229. package/types/src/io/write-file.d.ts +0 -5
  230. package/types/src/objects/deep-merge.d.ts +0 -41
  231. package/types/src/objects/index.d.ts +0 -1
  232. package/types/src/reflection/index.d.ts +0 -3
  233. package/types/src/reflection/types/index.d.ts +0 -1
  234. package/types/src/reflection/types/reflection.types.d.ts +0 -21
  235. package/types/src/reflection/utilities.d.ts +0 -27
  236. package/types/src/system/camel-case.d.ts +0 -1
  237. package/types/src/system/debounce.d.ts +0 -1
  238. package/types/src/system/decorator.d.ts +0 -15
  239. package/types/src/system/environment.d.ts +0 -6
  240. package/types/src/system/index.d.ts +0 -7
  241. package/types/src/system/retry.decorator.d.ts +0 -6
  242. package/types/src/system/sleep.d.ts +0 -1
  243. package/types/src/system/type.utils.d.ts +0 -1
  244. /package/{es6/src/collections/collections.js → src/collections/collections.ts} +0 -0
  245. /package/{es6/src/collections/index.js → src/collections/index.ts} +0 -0
  246. /package/{es6/src/configuration/index.js → src/configuration/index.ts} +0 -0
  247. /package/{es6/src/extensibility/index.js → src/extensibility/index.ts} +0 -0
  248. /package/{es6/src/io/index.js → src/io/index.ts} +0 -0
  249. /package/{es6/src/io/types/index.js → src/io/types/index.ts} +0 -0
  250. /package/{es6/src/objects/index.js → src/objects/index.ts} +0 -0
  251. /package/{es6/src/reflection/types/index.js → src/reflection/types/index.ts} +0 -0
  252. /package/{es6/src/system/index.js → src/system/index.ts} +0 -0
@@ -0,0 +1,91 @@
1
+ import { vi } from "vitest";
2
+ import { cache } from "../../src/caching/cache.decorator.js";
3
+
4
+ // Simple cache implementation for testing
5
+ class MockCache {
6
+ public get isMemoize(): boolean {
7
+ return false;
8
+ }
9
+ private data = new Map<string, unknown>();
10
+
11
+ public get(key: string): unknown {
12
+ return this.data.get(key);
13
+ }
14
+
15
+ public set(key: string, value: unknown): void {
16
+ this.data.set(key, value);
17
+ }
18
+
19
+ public has(key: string): boolean {
20
+ return this.data.has(key);
21
+ }
22
+
23
+ public del(key: string): void {
24
+ this.data.delete(key);
25
+ }
26
+
27
+ public flushAll(): void {
28
+ this.data.clear();
29
+ }
30
+ }
31
+
32
+ export const CACHE = new MockCache();
33
+
34
+ export class CacheTestClass {
35
+ public callCount = 0;
36
+ public counter = vi.fn();
37
+
38
+ @cache({ cache: CACHE })
39
+ public syncMethod(value: number): number {
40
+ this.callCount++;
41
+ this.counter();
42
+ return value * 2;
43
+ }
44
+
45
+ @cache({ cache: CACHE })
46
+ public async asyncMethod(value: number): Promise<number> {
47
+ this.callCount++;
48
+ this.counter();
49
+ return value * 3;
50
+ }
51
+
52
+ @cache({ cache: CACHE })
53
+ public multiArgMethod(arg1: number, arg2: number): number {
54
+ this.callCount++;
55
+ this.counter();
56
+ return arg1 + arg2;
57
+ }
58
+
59
+ @cache({ cache: CACHE })
60
+ public undefinedMethod(): undefined {
61
+ this.callCount++;
62
+ this.counter();
63
+ return undefined;
64
+ }
65
+
66
+ @cache({ cache: CACHE })
67
+ public falsyMethod(): number {
68
+ this.callCount++;
69
+ this.counter();
70
+ return 0;
71
+ }
72
+
73
+ @cache({ name: "TEST_CACHE" })
74
+ public namedCacheMethod(value: number): number {
75
+ this.callCount++;
76
+ this.counter();
77
+ return value * 5;
78
+ }
79
+
80
+ @cache()
81
+ public defaultOptionsMethod(value: string): string {
82
+ this.callCount++;
83
+ this.counter();
84
+ return `test-${value}`;
85
+ }
86
+
87
+ public reset(): void {
88
+ this.callCount = 0;
89
+ this.counter.mockReset();
90
+ }
91
+ }
@@ -0,0 +1,259 @@
1
+ import path from "node:path";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { Caching } from "../../src/caching/cache-manager.js";
4
+ import { pathToFileUrl } from "../../src/io/url-to-path.js";
5
+ import { CACHE } from "./cache-test-class.js";
6
+
7
+ // Test class import path constant
8
+ const CACHE_TEST_CLASS_PATH = "./cache-test-class.js";
9
+
10
+ // Function-based import URL for easier refactoring
11
+ const importUrl = () => getImportUrl(CACHE_TEST_CLASS_PATH);
12
+
13
+ // Set up the test cache in Caching manager
14
+ Caching.setCache("TEST_CACHE", CACHE);
15
+
16
+ // Helper function to create a versioned import URL for cache busting
17
+ function getImportUrl(filename: string): string {
18
+ const modulePath = path.resolve(__dirname, filename);
19
+ const moduleUrl = pathToFileUrl(modulePath);
20
+ return `${moduleUrl}?version=${Date.now()}`;
21
+ }
22
+
23
+ describe("cache decorator", () => {
24
+ beforeEach(() => {
25
+ vi.resetAllMocks();
26
+ });
27
+
28
+ afterEach(() => {
29
+ Caching.flushAllCaches();
30
+ CACHE.flushAll();
31
+ delete process.env.FORGE_DISABLE_CACHE;
32
+ });
33
+
34
+ describe("FORGE_DISABLE_CACHE environment variable behavior", () => {
35
+ it("should cache normally by default", async () => {
36
+ const { CacheTestClass } = await import(importUrl());
37
+ const instance = new CacheTestClass();
38
+
39
+ const result1 = instance.syncMethod(5);
40
+ const result2 = instance.syncMethod(5);
41
+
42
+ expect(result1).toBe(10);
43
+ expect(result2).toBe(10);
44
+ expect(instance.callCount).toBe(1); // Should be cached
45
+ });
46
+
47
+ it("should disable caching when FORGE_DISABLE_CACHE=true", async () => {
48
+ process.env.FORGE_DISABLE_CACHE = "true";
49
+
50
+ const { CacheTestClass } = await import(importUrl());
51
+ const instance = new CacheTestClass();
52
+
53
+ const result1 = instance.syncMethod(5);
54
+ const result2 = instance.syncMethod(5);
55
+
56
+ expect(result1).toBe(10);
57
+ expect(result2).toBe(10);
58
+ expect(instance.callCount).toBe(2); // Should NOT be cached
59
+ });
60
+
61
+ it("should disable caching for async methods when FORGE_DISABLE_CACHE=true", async () => {
62
+ process.env.FORGE_DISABLE_CACHE = "true";
63
+
64
+ const { CacheTestClass } = await import(importUrl());
65
+ const instance = new CacheTestClass();
66
+
67
+ const result1 = await instance.asyncMethod(5);
68
+ const result2 = await instance.asyncMethod(5);
69
+
70
+ expect(result1).toBe(15);
71
+ expect(result2).toBe(15);
72
+ expect(instance.callCount).toBe(2); // Should NOT be cached
73
+ });
74
+
75
+ it("should not interact with cache when disabled", async () => {
76
+ process.env.FORGE_DISABLE_CACHE = "true";
77
+
78
+ const setSpy = vi.spyOn(CACHE, "set");
79
+ const getSpy = vi.spyOn(CACHE, "get");
80
+ const hasSpy = vi.spyOn(CACHE, "has");
81
+
82
+ const { CacheTestClass } = await import(importUrl());
83
+ const instance = new CacheTestClass();
84
+
85
+ instance.defaultOptionsMethod("value");
86
+ instance.defaultOptionsMethod("value");
87
+
88
+ expect(setSpy).not.toHaveBeenCalled();
89
+ expect(getSpy).not.toHaveBeenCalled();
90
+ expect(hasSpy).not.toHaveBeenCalled();
91
+ });
92
+ });
93
+
94
+ describe("synchronous methods", () => {
95
+ it("should cache sync method results", async () => {
96
+ const { CacheTestClass } = await import(importUrl());
97
+ const instance = new CacheTestClass();
98
+
99
+ const result1 = instance.syncMethod(5);
100
+ const result2 = instance.syncMethod(5);
101
+
102
+ expect(result1).toBe(10);
103
+ expect(result2).toBe(10);
104
+ expect(instance.callCount).toBe(1);
105
+ expect(instance.counter).toHaveBeenCalledTimes(1);
106
+ });
107
+
108
+ it("should cache different arguments separately", async () => {
109
+ const { CacheTestClass } = await import(importUrl());
110
+ const instance = new CacheTestClass();
111
+
112
+ const result1 = instance.syncMethod(5);
113
+ const result2 = instance.syncMethod(10);
114
+ const result3 = instance.syncMethod(5);
115
+
116
+ expect(result1).toBe(10);
117
+ expect(result2).toBe(20);
118
+ expect(result3).toBe(10);
119
+ expect(instance.callCount).toBe(2); // Called for 5 and 10, but cached for second 5
120
+ });
121
+
122
+ it("should cache multiple arguments", async () => {
123
+ const { CacheTestClass } = await import(importUrl());
124
+ const instance = new CacheTestClass();
125
+
126
+ const result1 = instance.multiArgMethod(3, 4);
127
+ const result2 = instance.multiArgMethod(3, 4);
128
+ const result3 = instance.multiArgMethod(5, 6);
129
+
130
+ expect(result1).toBe(7);
131
+ expect(result2).toBe(7);
132
+ expect(result3).toBe(11);
133
+ expect(instance.callCount).toBe(2); // Called for (3,4) and (5,6)
134
+ });
135
+
136
+ it("should cache undefined return values", async () => {
137
+ const { CacheTestClass } = await import(importUrl());
138
+ const instance = new CacheTestClass();
139
+
140
+ const result1 = instance.undefinedMethod();
141
+ const result2 = instance.undefinedMethod();
142
+
143
+ expect(result1).toBeUndefined();
144
+ expect(result2).toBeUndefined();
145
+ expect(instance.callCount).toBe(1); // Should be cached
146
+ });
147
+
148
+ it("should cache falsy return values", async () => {
149
+ const { CacheTestClass } = await import(importUrl());
150
+ const instance = new CacheTestClass();
151
+
152
+ const result1 = instance.falsyMethod();
153
+ const result2 = instance.falsyMethod();
154
+
155
+ expect(result1).toBe(0);
156
+ expect(result2).toBe(0);
157
+ expect(instance.callCount).toBe(1); // Should be cached
158
+ });
159
+ });
160
+
161
+ describe("asynchronous methods", () => {
162
+ it("should cache async method results", async () => {
163
+ const { CacheTestClass } = await import(importUrl());
164
+ const instance = new CacheTestClass();
165
+
166
+ const result1 = await instance.asyncMethod(5);
167
+ const result2 = await instance.asyncMethod(5);
168
+
169
+ expect(result1).toBe(15);
170
+ expect(result2).toBe(15);
171
+ expect(instance.callCount).toBe(1);
172
+ });
173
+
174
+ it("should return Promise for async methods", async () => {
175
+ const { CacheTestClass } = await import(importUrl());
176
+ const instance = new CacheTestClass();
177
+
178
+ const result = instance.asyncMethod(5);
179
+
180
+ expect(result).toBeInstanceOf(Promise);
181
+ await result; // Wait for completion
182
+ });
183
+
184
+ it("should cache different async arguments separately", async () => {
185
+ const { CacheTestClass } = await import(importUrl());
186
+ const instance = new CacheTestClass();
187
+
188
+ const result1 = await instance.asyncMethod(5);
189
+ const result2 = await instance.asyncMethod(10);
190
+ const result3 = await instance.asyncMethod(5);
191
+
192
+ expect(result1).toBe(15);
193
+ expect(result2).toBe(30);
194
+ expect(result3).toBe(15);
195
+ expect(instance.callCount).toBe(2);
196
+ });
197
+ });
198
+
199
+ describe("cache isolation and sharing", () => {
200
+ it("should isolate cache per instance", async () => {
201
+ const { CacheTestClass } = await import(importUrl());
202
+ const instance1 = new CacheTestClass();
203
+ const instance2 = new CacheTestClass();
204
+
205
+ const result1 = instance1.syncMethod(5);
206
+ const result2 = instance2.syncMethod(5);
207
+
208
+ expect(result1).toBe(10);
209
+ expect(result2).toBe(10);
210
+ // Each instance has its own cache entry — both execute the method
211
+ expect(instance1.callCount).toBe(1);
212
+ expect(instance2.callCount).toBe(1);
213
+ });
214
+ });
215
+
216
+ describe("named cache support", () => {
217
+ it("should use named cache from Caching manager", async () => {
218
+ const { CacheTestClass } = await import(importUrl());
219
+ const instance = new CacheTestClass();
220
+
221
+ const result1 = instance.namedCacheMethod(5);
222
+ const result2 = instance.namedCacheMethod(5);
223
+
224
+ expect(result1).toBe(25);
225
+ expect(result2).toBe(25);
226
+ expect(instance.callCount).toBe(1); // Should be cached
227
+ });
228
+
229
+ it("should clear named cache via Caching manager", async () => {
230
+ const { CacheTestClass } = await import(importUrl());
231
+ const instance = new CacheTestClass();
232
+
233
+ const result1 = instance.namedCacheMethod(5);
234
+ expect(result1).toBe(25);
235
+ expect(instance.callCount).toBe(1);
236
+
237
+ // Clear the cache
238
+ Caching.flushAllCaches();
239
+
240
+ const result2 = instance.namedCacheMethod(5);
241
+ expect(result2).toBe(25);
242
+ expect(instance.callCount).toBe(2); // Should be called again after clear
243
+ });
244
+ });
245
+
246
+ describe("default cache options", () => {
247
+ it("should work with default options", async () => {
248
+ const { CacheTestClass } = await import(importUrl());
249
+ const instance = new CacheTestClass();
250
+
251
+ const result1 = instance.defaultOptionsMethod("value");
252
+ const result2 = instance.defaultOptionsMethod("value");
253
+
254
+ expect(result1).toBe("test-value");
255
+ expect(result2).toBe("test-value");
256
+ expect(instance.callCount).toBe(1); // Should be cached with default cache
257
+ });
258
+ });
259
+ });
@@ -0,0 +1,52 @@
1
+ import type { ICache } from "../../src/caching/cache.js";
2
+ import { invalidateCache, memoize } from "../../src/caching/index.js";
3
+
4
+ class MockCache implements ICache {
5
+ public get isMemoize(): boolean {
6
+ return true;
7
+ }
8
+
9
+ private data = new Map<string, unknown>();
10
+
11
+ public get(key: string): unknown {
12
+ return this.data.get(key);
13
+ }
14
+
15
+ public set(key: string, value: unknown): void {
16
+ this.data.set(key, value);
17
+ }
18
+
19
+ public has(key: string): boolean {
20
+ return this.data.has(key);
21
+ }
22
+
23
+ public del(key: string): void {
24
+ this.data.delete(key);
25
+ }
26
+
27
+ public flushAll(): void {
28
+ this.data.clear();
29
+ }
30
+ }
31
+
32
+ export const TEST_CACHE = new MockCache();
33
+
34
+ export class InvalidateCacheTestClass {
35
+ public callCount = 0;
36
+
37
+ @memoize({ cache: TEST_CACHE })
38
+ public getValue(): string {
39
+ this.callCount++;
40
+ return `value_${this.callCount}`;
41
+ }
42
+
43
+ @invalidateCache("getValue", { cache: TEST_CACHE })
44
+ public setValue(_value: string): void {
45
+ // synchronous write — invalidation happens after this returns
46
+ }
47
+
48
+ @invalidateCache("getValue", { cache: TEST_CACHE })
49
+ public async setValueAsync(_value: string): Promise<void> {
50
+ // async write — invalidation happens after the promise resolves
51
+ }
52
+ }
@@ -0,0 +1,120 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import type { ICache } from "../../src/caching/cache.js";
3
+ import { Caching } from "../../src/caching/cache-manager.js";
4
+ import { invalidateCache, invalidateMemoize, memoize } from "../../src/caching/index.js";
5
+
6
+ class TestCache implements ICache {
7
+ public get isMemoize() {
8
+ return true;
9
+ }
10
+ private data = new Map<string, unknown>();
11
+ public get(key: string) {
12
+ return this.data.get(key);
13
+ }
14
+ public set(key: string, value: unknown) {
15
+ this.data.set(key, value);
16
+ }
17
+ public has(key: string) {
18
+ return this.data.has(key);
19
+ }
20
+ public del(key: string) {
21
+ this.data.delete(key);
22
+ }
23
+ public flushAll() {
24
+ this.data.clear();
25
+ }
26
+ }
27
+
28
+ const TEST_CACHE = new TestCache();
29
+
30
+ // Function reference: apply invalidateCache after class definition so the prototype is available
31
+ class MemoizedClass {
32
+ public callCount = 0;
33
+
34
+ @memoize({ cache: TEST_CACHE })
35
+ public getValue(): string {
36
+ this.callCount++;
37
+ return `value_${this.callCount}`;
38
+ }
39
+
40
+ public invalidate(): void {}
41
+ public async invalidateAsync(): Promise<void> {}
42
+ }
43
+
44
+ for (const key of ["invalidate", "invalidateAsync"] as const) {
45
+ const desc = Object.getOwnPropertyDescriptor(MemoizedClass.prototype, key)!;
46
+ invalidateMemoize(MemoizedClass.prototype.getValue, { cache: TEST_CACHE })(
47
+ MemoizedClass.prototype,
48
+ key,
49
+ desc,
50
+ );
51
+ Object.defineProperty(MemoizedClass.prototype, key, desc);
52
+ }
53
+
54
+ // String overload: raw cache key eviction (no generateCacheKey semantics)
55
+ class RawKeyClass {
56
+ @invalidateCache("myRawKey", { cache: TEST_CACHE })
57
+ public clearEntry(): void {}
58
+ }
59
+
60
+ describe("invalidateCache decorator", () => {
61
+ afterEach(() => {
62
+ TEST_CACHE.flushAll();
63
+ Caching.flushAllCaches();
64
+ });
65
+
66
+ describe("function reference", () => {
67
+ it("should evict cache for the referenced method after synchronous invalidator runs", () => {
68
+ const instance = new MemoizedClass();
69
+
70
+ instance.getValue(); // callCount = 1, cached
71
+ instance.getValue(); // from cache
72
+ expect(instance.callCount).toBe(1);
73
+
74
+ instance.invalidate();
75
+
76
+ instance.getValue(); // evicted — re-executes
77
+ expect(instance.callCount).toBe(2);
78
+ });
79
+
80
+ it("should evict cache for the referenced method after async invalidator resolves", async () => {
81
+ const instance = new MemoizedClass();
82
+
83
+ instance.getValue(); // callCount = 1, cached
84
+ instance.getValue(); // from cache
85
+ expect(instance.callCount).toBe(1);
86
+
87
+ await instance.invalidateAsync();
88
+
89
+ instance.getValue(); // evicted — re-executes
90
+ expect(instance.callCount).toBe(2);
91
+ });
92
+
93
+ it("should use the custom cache supplied via options", () => {
94
+ const instance = new MemoizedClass();
95
+
96
+ instance.getValue(); // callCount = 1, cached in TEST_CACHE
97
+ expect(instance.callCount).toBe(1);
98
+
99
+ instance.invalidate(); // evicts TEST_CACHE entry
100
+
101
+ instance.getValue(); // callCount = 2
102
+ expect(instance.callCount).toBe(2);
103
+
104
+ TEST_CACHE.flushAll();
105
+ instance.getValue(); // callCount = 3
106
+ expect(instance.callCount).toBe(3);
107
+ });
108
+ });
109
+
110
+ describe("string (raw cache key)", () => {
111
+ it("should delete the exact cache key when passed a string", () => {
112
+ TEST_CACHE.set("myRawKey", "someValue");
113
+ expect(TEST_CACHE.has("myRawKey")).toBe(true);
114
+
115
+ new RawKeyClass().clearEntry();
116
+
117
+ expect(TEST_CACHE.has("myRawKey")).toBe(false);
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,99 @@
1
+ import { vi } from "vitest";
2
+ import { memoize } from "../../src/caching/index.js";
3
+
4
+ // Simple memoization cache implementation for testing
5
+ class MockMemoizeCache {
6
+ public get isMemoize(): boolean {
7
+ return true;
8
+ }
9
+ private data = new Map<string, unknown>();
10
+
11
+ public get(key: string): unknown {
12
+ return this.data.get(key);
13
+ }
14
+
15
+ public set(key: string, value: unknown): void {
16
+ this.data.set(key, value);
17
+ }
18
+
19
+ public has(key: string): boolean {
20
+ return this.data.has(key);
21
+ }
22
+
23
+ public del(key: string): void {
24
+ this.data.delete(key);
25
+ }
26
+
27
+ public flushAll(): void {
28
+ this.data.clear();
29
+ }
30
+ }
31
+
32
+ export const MEMOIZE_CACHE = new MockMemoizeCache();
33
+
34
+ export class MemoizeTestClass {
35
+ public callCount = 0;
36
+ public counter = vi.fn();
37
+
38
+ public reset(): void {
39
+ this.callCount = 0;
40
+ this.counter.mockClear();
41
+ MEMOIZE_CACHE.flushAll();
42
+ }
43
+
44
+ @memoize({ cache: MEMOIZE_CACHE })
45
+ public syncMethod(value: number): number {
46
+ this.callCount++;
47
+ this.counter();
48
+ return value * 2;
49
+ }
50
+
51
+ @memoize({ cache: MEMOIZE_CACHE })
52
+ public async asyncMethod(value: number): Promise<number> {
53
+ this.callCount++;
54
+ this.counter();
55
+ return value * 3;
56
+ }
57
+
58
+ @memoize({ cache: MEMOIZE_CACHE })
59
+ public multiArgMethod(arg1: number, arg2: number): number {
60
+ this.callCount++;
61
+ this.counter();
62
+ return arg1 + arg2;
63
+ }
64
+
65
+ @memoize({ cache: MEMOIZE_CACHE })
66
+ public undefinedMethod(): undefined {
67
+ this.callCount++;
68
+ this.counter();
69
+ return undefined;
70
+ }
71
+
72
+ @memoize({ cache: MEMOIZE_CACHE })
73
+ public falsyMethod(): number {
74
+ this.callCount++;
75
+ this.counter();
76
+ return 0;
77
+ }
78
+
79
+ @memoize({ cache: MEMOIZE_CACHE, name: "custom-cache" })
80
+ public namedCacheMethod(value: number): number {
81
+ this.callCount++;
82
+ this.counter();
83
+ return value * 4;
84
+ }
85
+
86
+ @memoize()
87
+ public defaultCacheMethod(value: number): number {
88
+ this.callCount++;
89
+ this.counter();
90
+ return value * 5;
91
+ }
92
+
93
+ @memoize()
94
+ public objectMethod(id: number): { id: number; value: number } {
95
+ this.callCount++;
96
+ this.counter();
97
+ return { id, value: id * 10 };
98
+ }
99
+ }