@voidhash/mimic-effect 1.0.0-beta.1 → 1.0.0-beta.10

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 (142) hide show
  1. package/.turbo/turbo-build.log +116 -74
  2. package/dist/ColdStorage.cjs +9 -5
  3. package/dist/ColdStorage.d.cts.map +1 -1
  4. package/dist/ColdStorage.d.mts.map +1 -1
  5. package/dist/ColdStorage.mjs +9 -5
  6. package/dist/ColdStorage.mjs.map +1 -1
  7. package/dist/DocumentInstance.cjs +263 -0
  8. package/dist/DocumentInstance.d.cts +78 -0
  9. package/dist/DocumentInstance.d.cts.map +1 -0
  10. package/dist/DocumentInstance.d.mts +78 -0
  11. package/dist/DocumentInstance.d.mts.map +1 -0
  12. package/dist/DocumentInstance.mjs +264 -0
  13. package/dist/DocumentInstance.mjs.map +1 -0
  14. package/dist/Errors.cjs +10 -1
  15. package/dist/Errors.d.cts +18 -3
  16. package/dist/Errors.d.cts.map +1 -1
  17. package/dist/Errors.d.mts +18 -3
  18. package/dist/Errors.d.mts.map +1 -1
  19. package/dist/Errors.mjs +9 -1
  20. package/dist/Errors.mjs.map +1 -1
  21. package/dist/HotStorage.cjs +39 -12
  22. package/dist/HotStorage.d.cts +17 -1
  23. package/dist/HotStorage.d.cts.map +1 -1
  24. package/dist/HotStorage.d.mts +17 -1
  25. package/dist/HotStorage.d.mts.map +1 -1
  26. package/dist/HotStorage.mjs +39 -12
  27. package/dist/HotStorage.mjs.map +1 -1
  28. package/dist/Metrics.cjs +29 -1
  29. package/dist/Metrics.d.cts +5 -0
  30. package/dist/Metrics.d.cts.map +1 -1
  31. package/dist/Metrics.d.mts +5 -0
  32. package/dist/Metrics.d.mts.map +1 -1
  33. package/dist/Metrics.mjs +26 -1
  34. package/dist/Metrics.mjs.map +1 -1
  35. package/dist/MimicClusterServerEngine.cjs +44 -139
  36. package/dist/MimicClusterServerEngine.d.cts.map +1 -1
  37. package/dist/MimicClusterServerEngine.d.mts +1 -1
  38. package/dist/MimicClusterServerEngine.d.mts.map +1 -1
  39. package/dist/MimicClusterServerEngine.mjs +46 -141
  40. package/dist/MimicClusterServerEngine.mjs.map +1 -1
  41. package/dist/MimicServer.cjs +20 -20
  42. package/dist/MimicServer.d.cts.map +1 -1
  43. package/dist/MimicServer.d.mts.map +1 -1
  44. package/dist/MimicServer.mjs +20 -20
  45. package/dist/MimicServer.mjs.map +1 -1
  46. package/dist/MimicServerEngine.cjs +92 -11
  47. package/dist/MimicServerEngine.d.cts +12 -4
  48. package/dist/MimicServerEngine.d.cts.map +1 -1
  49. package/dist/MimicServerEngine.d.mts +12 -4
  50. package/dist/MimicServerEngine.d.mts.map +1 -1
  51. package/dist/MimicServerEngine.mjs +94 -13
  52. package/dist/MimicServerEngine.mjs.map +1 -1
  53. package/dist/PresenceManager.cjs +5 -5
  54. package/dist/PresenceManager.d.cts.map +1 -1
  55. package/dist/PresenceManager.d.mts.map +1 -1
  56. package/dist/PresenceManager.mjs +5 -5
  57. package/dist/PresenceManager.mjs.map +1 -1
  58. package/dist/Protocol.d.cts +1 -1
  59. package/dist/Protocol.d.mts +1 -1
  60. package/dist/Types.d.cts +9 -2
  61. package/dist/Types.d.cts.map +1 -1
  62. package/dist/Types.d.mts +9 -2
  63. package/dist/Types.d.mts.map +1 -1
  64. package/dist/index.cjs +5 -6
  65. package/dist/index.d.cts +3 -3
  66. package/dist/index.d.mts +3 -3
  67. package/dist/index.mjs +3 -3
  68. package/dist/testing/ColdStorageTestSuite.cjs +508 -0
  69. package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
  70. package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
  71. package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
  72. package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
  73. package/dist/testing/ColdStorageTestSuite.mjs +508 -0
  74. package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
  75. package/dist/testing/FailingStorage.cjs +162 -0
  76. package/dist/testing/FailingStorage.d.cts +43 -0
  77. package/dist/testing/FailingStorage.d.cts.map +1 -0
  78. package/dist/testing/FailingStorage.d.mts +43 -0
  79. package/dist/testing/FailingStorage.d.mts.map +1 -0
  80. package/dist/testing/FailingStorage.mjs +163 -0
  81. package/dist/testing/FailingStorage.mjs.map +1 -0
  82. package/dist/testing/HotStorageTestSuite.cjs +820 -0
  83. package/dist/testing/HotStorageTestSuite.d.cts +42 -0
  84. package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
  85. package/dist/testing/HotStorageTestSuite.d.mts +42 -0
  86. package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
  87. package/dist/testing/HotStorageTestSuite.mjs +820 -0
  88. package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
  89. package/dist/testing/StorageIntegrationTestSuite.cjs +487 -0
  90. package/dist/testing/StorageIntegrationTestSuite.d.cts +37 -0
  91. package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
  92. package/dist/testing/StorageIntegrationTestSuite.d.mts +37 -0
  93. package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
  94. package/dist/testing/StorageIntegrationTestSuite.mjs +487 -0
  95. package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
  96. package/dist/testing/assertions.cjs +117 -0
  97. package/dist/testing/assertions.mjs +112 -0
  98. package/dist/testing/assertions.mjs.map +1 -0
  99. package/dist/testing/index.cjs +14 -0
  100. package/dist/testing/index.d.cts +6 -0
  101. package/dist/testing/index.d.mts +6 -0
  102. package/dist/testing/index.mjs +7 -0
  103. package/dist/testing/types.cjs +15 -0
  104. package/dist/testing/types.d.cts +90 -0
  105. package/dist/testing/types.d.cts.map +1 -0
  106. package/dist/testing/types.d.mts +90 -0
  107. package/dist/testing/types.d.mts.map +1 -0
  108. package/dist/testing/types.mjs +16 -0
  109. package/dist/testing/types.mjs.map +1 -0
  110. package/package.json +8 -3
  111. package/src/ColdStorage.ts +21 -12
  112. package/src/DocumentInstance.ts +527 -0
  113. package/src/Errors.ts +15 -1
  114. package/src/HotStorage.ts +115 -24
  115. package/src/Metrics.ts +30 -0
  116. package/src/MimicClusterServerEngine.ts +120 -275
  117. package/src/MimicServer.ts +83 -75
  118. package/src/MimicServerEngine.ts +230 -30
  119. package/src/PresenceManager.ts +44 -34
  120. package/src/Types.ts +9 -2
  121. package/src/index.ts +5 -35
  122. package/src/testing/ColdStorageTestSuite.ts +589 -0
  123. package/src/testing/FailingStorage.ts +338 -0
  124. package/src/testing/HotStorageTestSuite.ts +1105 -0
  125. package/src/testing/StorageIntegrationTestSuite.ts +736 -0
  126. package/src/testing/assertions.ts +188 -0
  127. package/src/testing/index.ts +83 -0
  128. package/src/testing/types.ts +100 -0
  129. package/tests/ColdStorage.test.ts +8 -120
  130. package/tests/DocumentInstance.test.ts +669 -0
  131. package/tests/HotStorage.test.ts +7 -126
  132. package/tests/StorageIntegration.test.ts +259 -0
  133. package/tsdown.config.ts +1 -1
  134. package/dist/DocumentManager.cjs +0 -229
  135. package/dist/DocumentManager.d.cts +0 -59
  136. package/dist/DocumentManager.d.cts.map +0 -1
  137. package/dist/DocumentManager.d.mts +0 -59
  138. package/dist/DocumentManager.d.mts.map +0 -1
  139. package/dist/DocumentManager.mjs +0 -227
  140. package/dist/DocumentManager.mjs.map +0 -1
  141. package/src/DocumentManager.ts +0 -506
  142. package/tests/DocumentManager.test.ts +0 -335
@@ -0,0 +1,162 @@
1
+ const require_ColdStorage = require('../ColdStorage.cjs');
2
+ const require_Errors = require('../Errors.cjs');
3
+ const require_HotStorage = require('../HotStorage.cjs');
4
+ let effect = require("effect");
5
+
6
+ //#region src/testing/FailingStorage.ts
7
+ /**
8
+ * @voidhash/mimic-effect/testing - FailingStorage
9
+ *
10
+ * Mock storage implementations that simulate failures for testing error handling.
11
+ * Use these to verify that your application correctly handles storage unavailability.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { FailingStorage } from "@voidhash/mimic-effect/testing";
16
+ *
17
+ * // Create a ColdStorage that fails on load
18
+ * const failingCold = FailingStorage.makeColdStorage({ failLoad: true });
19
+ *
20
+ * // Create a HotStorage that fails after 3 successful appends
21
+ * const failingHot = FailingStorage.makeHotStorage({ failAfterN: 3, failAppend: true });
22
+ * ```
23
+ */
24
+ /**
25
+ * Create a ColdStorage layer that simulates failures.
26
+ * Wraps an in-memory storage and fails according to configuration.
27
+ */
28
+ const makeColdStorage = (config = {}) => effect.Layer.effect(require_ColdStorage.ColdStorageTag, effect.Effect.gen(function* () {
29
+ var _config$errorMessage;
30
+ const store = yield* effect.Ref.make(effect.HashMap.empty());
31
+ const operationCount = yield* effect.Ref.make(0);
32
+ const errorMessage = (_config$errorMessage = config.errorMessage) !== null && _config$errorMessage !== void 0 ? _config$errorMessage : "Simulated storage failure";
33
+ const shouldFail = (operation) => effect.Effect.gen(function* () {
34
+ const opFails = operation === "load" && config.failLoad || operation === "save" && config.failSave || operation === "delete" && config.failDelete;
35
+ if (config.failAfterN !== void 0) {
36
+ const count = yield* effect.Ref.get(operationCount);
37
+ yield* effect.Ref.update(operationCount, (n) => n + 1);
38
+ if (count < config.failAfterN) return false;
39
+ return opFails;
40
+ }
41
+ return opFails;
42
+ });
43
+ return {
44
+ load: (documentId) => effect.Effect.gen(function* () {
45
+ if (yield* shouldFail("load")) return yield* effect.Effect.fail(new require_Errors.ColdStorageError({
46
+ documentId,
47
+ operation: "load",
48
+ cause: new Error(errorMessage)
49
+ }));
50
+ const current = yield* effect.Ref.get(store);
51
+ const doc = effect.HashMap.get(current, documentId);
52
+ return doc._tag === "Some" ? doc.value : void 0;
53
+ }),
54
+ save: (documentId, document) => effect.Effect.gen(function* () {
55
+ if (yield* shouldFail("save")) return yield* effect.Effect.fail(new require_Errors.ColdStorageError({
56
+ documentId,
57
+ operation: "save",
58
+ cause: new Error(errorMessage)
59
+ }));
60
+ yield* effect.Ref.update(store, (map) => effect.HashMap.set(map, documentId, document));
61
+ }),
62
+ delete: (documentId) => effect.Effect.gen(function* () {
63
+ if (yield* shouldFail("delete")) return yield* effect.Effect.fail(new require_Errors.ColdStorageError({
64
+ documentId,
65
+ operation: "delete",
66
+ cause: new Error(errorMessage)
67
+ }));
68
+ yield* effect.Ref.update(store, (map) => effect.HashMap.remove(map, documentId));
69
+ })
70
+ };
71
+ }));
72
+ /**
73
+ * Create a HotStorage layer that simulates failures.
74
+ * Wraps an in-memory storage and fails according to configuration.
75
+ */
76
+ const makeHotStorage = (config = {}) => effect.Layer.effect(require_HotStorage.HotStorageTag, effect.Effect.gen(function* () {
77
+ var _config$errorMessage2;
78
+ const store = yield* effect.Ref.make(effect.HashMap.empty());
79
+ const operationCount = yield* effect.Ref.make(0);
80
+ const errorMessage = (_config$errorMessage2 = config.errorMessage) !== null && _config$errorMessage2 !== void 0 ? _config$errorMessage2 : "Simulated storage failure";
81
+ const shouldFail = (operation) => effect.Effect.gen(function* () {
82
+ const opFails = operation === "append" && config.failAppend || operation === "getEntries" && config.failGetEntries || operation === "truncate" && config.failTruncate;
83
+ if (config.failAfterN !== void 0) {
84
+ const count = yield* effect.Ref.get(operationCount);
85
+ yield* effect.Ref.update(operationCount, (n) => n + 1);
86
+ if (count < config.failAfterN) return false;
87
+ return opFails;
88
+ }
89
+ return opFails;
90
+ });
91
+ return {
92
+ append: (documentId, entry) => effect.Effect.gen(function* () {
93
+ if (yield* shouldFail("append")) return yield* effect.Effect.fail(new require_Errors.HotStorageError({
94
+ documentId,
95
+ operation: "append",
96
+ cause: new Error(errorMessage)
97
+ }));
98
+ yield* effect.Ref.update(store, (map) => {
99
+ const current = effect.HashMap.get(map, documentId);
100
+ const entries = current._tag === "Some" ? current.value : [];
101
+ return effect.HashMap.set(map, documentId, [...entries, entry]);
102
+ });
103
+ }),
104
+ appendWithCheck: (documentId, entry, expectedVersion) => effect.Effect.gen(function* () {
105
+ if (yield* shouldFail("append")) return yield* effect.Effect.fail(new require_Errors.HotStorageError({
106
+ documentId,
107
+ operation: "appendWithCheck",
108
+ cause: new Error(errorMessage)
109
+ }));
110
+ const result = yield* effect.Ref.modify(store, (map) => {
111
+ const existing = effect.HashMap.get(map, documentId);
112
+ const entries = existing._tag === "Some" ? existing.value : [];
113
+ const lastVersion = entries.length > 0 ? Math.max(...entries.map((e) => e.version)) : 0;
114
+ if (expectedVersion === 1) {
115
+ if (lastVersion >= 1) return [{
116
+ type: "gap",
117
+ lastVersion
118
+ }, map];
119
+ } else if (lastVersion !== expectedVersion - 1) return [{
120
+ type: "gap",
121
+ lastVersion: lastVersion > 0 ? lastVersion : void 0
122
+ }, map];
123
+ return [{ type: "ok" }, effect.HashMap.set(map, documentId, [...entries, entry])];
124
+ });
125
+ if (result.type === "gap") return yield* effect.Effect.fail(new require_Errors.WalVersionGapError({
126
+ documentId,
127
+ expectedVersion,
128
+ actualPreviousVersion: result.lastVersion
129
+ }));
130
+ }),
131
+ getEntries: (documentId, sinceVersion) => effect.Effect.gen(function* () {
132
+ if (yield* shouldFail("getEntries")) return yield* effect.Effect.fail(new require_Errors.HotStorageError({
133
+ documentId,
134
+ operation: "getEntries",
135
+ cause: new Error(errorMessage)
136
+ }));
137
+ const current = yield* effect.Ref.get(store);
138
+ const existing = effect.HashMap.get(current, documentId);
139
+ return (existing._tag === "Some" ? existing.value : []).filter((e) => e.version > sinceVersion).sort((a, b) => a.version - b.version);
140
+ }),
141
+ truncate: (documentId, upToVersion) => effect.Effect.gen(function* () {
142
+ if (yield* shouldFail("truncate")) return yield* effect.Effect.fail(new require_Errors.HotStorageError({
143
+ documentId,
144
+ operation: "truncate",
145
+ cause: new Error(errorMessage)
146
+ }));
147
+ yield* effect.Ref.update(store, (map) => {
148
+ const existing = effect.HashMap.get(map, documentId);
149
+ if (existing._tag === "None") return map;
150
+ const filtered = existing.value.filter((e) => e.version > upToVersion);
151
+ return effect.HashMap.set(map, documentId, filtered);
152
+ });
153
+ })
154
+ };
155
+ }));
156
+ const FailingStorage = {
157
+ makeColdStorage,
158
+ makeHotStorage
159
+ };
160
+
161
+ //#endregion
162
+ exports.FailingStorage = FailingStorage;
@@ -0,0 +1,43 @@
1
+ import { ColdStorageTag } from "../ColdStorage.cjs";
2
+ import { HotStorageTag } from "../HotStorage.cjs";
3
+ import { Layer } from "effect";
4
+
5
+ //#region src/testing/FailingStorage.d.ts
6
+
7
+ /**
8
+ * Configuration for failing ColdStorage
9
+ */
10
+ interface FailingColdStorageConfig {
11
+ /** Fail load operations */
12
+ readonly failLoad?: boolean;
13
+ /** Fail save operations */
14
+ readonly failSave?: boolean;
15
+ /** Fail delete operations */
16
+ readonly failDelete?: boolean;
17
+ /** Fail after N successful operations (total across all operation types) */
18
+ readonly failAfterN?: number;
19
+ /** Custom error message */
20
+ readonly errorMessage?: string;
21
+ }
22
+ /**
23
+ * Configuration for failing HotStorage
24
+ */
25
+ interface FailingHotStorageConfig {
26
+ /** Fail append operations */
27
+ readonly failAppend?: boolean;
28
+ /** Fail getEntries operations */
29
+ readonly failGetEntries?: boolean;
30
+ /** Fail truncate operations */
31
+ readonly failTruncate?: boolean;
32
+ /** Fail after N successful operations (total across all operation types) */
33
+ readonly failAfterN?: number;
34
+ /** Custom error message */
35
+ readonly errorMessage?: string;
36
+ }
37
+ declare const FailingStorage: {
38
+ makeColdStorage: (config?: FailingColdStorageConfig) => Layer.Layer<ColdStorageTag>;
39
+ makeHotStorage: (config?: FailingHotStorageConfig) => Layer.Layer<HotStorageTag>;
40
+ };
41
+ //#endregion
42
+ export { FailingColdStorageConfig, FailingHotStorageConfig, FailingStorage };
43
+ //# sourceMappingURL=FailingStorage.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailingStorage.d.cts","names":[],"sources":["../../src/testing/FailingStorage.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UA8BiB,wBAAA;;;;;;;;;;;;;;;UAgBA,uBAAA;;;;;;;;;;;;cAgSJ;6BA1QH,6BACP,KAAA,CAAM,MAAM;4BAqGL,4BACP,KAAA,CAAM,MAAM"}
@@ -0,0 +1,43 @@
1
+ import { ColdStorageTag } from "../ColdStorage.mjs";
2
+ import { HotStorageTag } from "../HotStorage.mjs";
3
+ import { Layer } from "effect";
4
+
5
+ //#region src/testing/FailingStorage.d.ts
6
+
7
+ /**
8
+ * Configuration for failing ColdStorage
9
+ */
10
+ interface FailingColdStorageConfig {
11
+ /** Fail load operations */
12
+ readonly failLoad?: boolean;
13
+ /** Fail save operations */
14
+ readonly failSave?: boolean;
15
+ /** Fail delete operations */
16
+ readonly failDelete?: boolean;
17
+ /** Fail after N successful operations (total across all operation types) */
18
+ readonly failAfterN?: number;
19
+ /** Custom error message */
20
+ readonly errorMessage?: string;
21
+ }
22
+ /**
23
+ * Configuration for failing HotStorage
24
+ */
25
+ interface FailingHotStorageConfig {
26
+ /** Fail append operations */
27
+ readonly failAppend?: boolean;
28
+ /** Fail getEntries operations */
29
+ readonly failGetEntries?: boolean;
30
+ /** Fail truncate operations */
31
+ readonly failTruncate?: boolean;
32
+ /** Fail after N successful operations (total across all operation types) */
33
+ readonly failAfterN?: number;
34
+ /** Custom error message */
35
+ readonly errorMessage?: string;
36
+ }
37
+ declare const FailingStorage: {
38
+ makeColdStorage: (config?: FailingColdStorageConfig) => Layer.Layer<ColdStorageTag>;
39
+ makeHotStorage: (config?: FailingHotStorageConfig) => Layer.Layer<HotStorageTag>;
40
+ };
41
+ //#endregion
42
+ export { FailingColdStorageConfig, FailingHotStorageConfig, FailingStorage };
43
+ //# sourceMappingURL=FailingStorage.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailingStorage.d.mts","names":[],"sources":["../../src/testing/FailingStorage.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UA8BiB,wBAAA;;;;;;;;;;;;;;;UAgBA,uBAAA;;;;;;;;;;;;cAgSJ;6BA1QH,6BACP,KAAA,CAAM,MAAM;4BAqGL,4BACP,KAAA,CAAM,MAAM"}
@@ -0,0 +1,163 @@
1
+ import { ColdStorageTag } from "../ColdStorage.mjs";
2
+ import { ColdStorageError, HotStorageError, WalVersionGapError } from "../Errors.mjs";
3
+ import { HotStorageTag } from "../HotStorage.mjs";
4
+ import { Effect, HashMap, Layer, Ref } from "effect";
5
+
6
+ //#region src/testing/FailingStorage.ts
7
+ /**
8
+ * @voidhash/mimic-effect/testing - FailingStorage
9
+ *
10
+ * Mock storage implementations that simulate failures for testing error handling.
11
+ * Use these to verify that your application correctly handles storage unavailability.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { FailingStorage } from "@voidhash/mimic-effect/testing";
16
+ *
17
+ * // Create a ColdStorage that fails on load
18
+ * const failingCold = FailingStorage.makeColdStorage({ failLoad: true });
19
+ *
20
+ * // Create a HotStorage that fails after 3 successful appends
21
+ * const failingHot = FailingStorage.makeHotStorage({ failAfterN: 3, failAppend: true });
22
+ * ```
23
+ */
24
+ /**
25
+ * Create a ColdStorage layer that simulates failures.
26
+ * Wraps an in-memory storage and fails according to configuration.
27
+ */
28
+ const makeColdStorage = (config = {}) => Layer.effect(ColdStorageTag, Effect.gen(function* () {
29
+ var _config$errorMessage;
30
+ const store = yield* Ref.make(HashMap.empty());
31
+ const operationCount = yield* Ref.make(0);
32
+ const errorMessage = (_config$errorMessage = config.errorMessage) !== null && _config$errorMessage !== void 0 ? _config$errorMessage : "Simulated storage failure";
33
+ const shouldFail = (operation) => Effect.gen(function* () {
34
+ const opFails = operation === "load" && config.failLoad || operation === "save" && config.failSave || operation === "delete" && config.failDelete;
35
+ if (config.failAfterN !== void 0) {
36
+ const count = yield* Ref.get(operationCount);
37
+ yield* Ref.update(operationCount, (n) => n + 1);
38
+ if (count < config.failAfterN) return false;
39
+ return opFails;
40
+ }
41
+ return opFails;
42
+ });
43
+ return {
44
+ load: (documentId) => Effect.gen(function* () {
45
+ if (yield* shouldFail("load")) return yield* Effect.fail(new ColdStorageError({
46
+ documentId,
47
+ operation: "load",
48
+ cause: new Error(errorMessage)
49
+ }));
50
+ const current = yield* Ref.get(store);
51
+ const doc = HashMap.get(current, documentId);
52
+ return doc._tag === "Some" ? doc.value : void 0;
53
+ }),
54
+ save: (documentId, document) => Effect.gen(function* () {
55
+ if (yield* shouldFail("save")) return yield* Effect.fail(new ColdStorageError({
56
+ documentId,
57
+ operation: "save",
58
+ cause: new Error(errorMessage)
59
+ }));
60
+ yield* Ref.update(store, (map) => HashMap.set(map, documentId, document));
61
+ }),
62
+ delete: (documentId) => Effect.gen(function* () {
63
+ if (yield* shouldFail("delete")) return yield* Effect.fail(new ColdStorageError({
64
+ documentId,
65
+ operation: "delete",
66
+ cause: new Error(errorMessage)
67
+ }));
68
+ yield* Ref.update(store, (map) => HashMap.remove(map, documentId));
69
+ })
70
+ };
71
+ }));
72
+ /**
73
+ * Create a HotStorage layer that simulates failures.
74
+ * Wraps an in-memory storage and fails according to configuration.
75
+ */
76
+ const makeHotStorage = (config = {}) => Layer.effect(HotStorageTag, Effect.gen(function* () {
77
+ var _config$errorMessage2;
78
+ const store = yield* Ref.make(HashMap.empty());
79
+ const operationCount = yield* Ref.make(0);
80
+ const errorMessage = (_config$errorMessage2 = config.errorMessage) !== null && _config$errorMessage2 !== void 0 ? _config$errorMessage2 : "Simulated storage failure";
81
+ const shouldFail = (operation) => Effect.gen(function* () {
82
+ const opFails = operation === "append" && config.failAppend || operation === "getEntries" && config.failGetEntries || operation === "truncate" && config.failTruncate;
83
+ if (config.failAfterN !== void 0) {
84
+ const count = yield* Ref.get(operationCount);
85
+ yield* Ref.update(operationCount, (n) => n + 1);
86
+ if (count < config.failAfterN) return false;
87
+ return opFails;
88
+ }
89
+ return opFails;
90
+ });
91
+ return {
92
+ append: (documentId, entry) => Effect.gen(function* () {
93
+ if (yield* shouldFail("append")) return yield* Effect.fail(new HotStorageError({
94
+ documentId,
95
+ operation: "append",
96
+ cause: new Error(errorMessage)
97
+ }));
98
+ yield* Ref.update(store, (map) => {
99
+ const current = HashMap.get(map, documentId);
100
+ const entries = current._tag === "Some" ? current.value : [];
101
+ return HashMap.set(map, documentId, [...entries, entry]);
102
+ });
103
+ }),
104
+ appendWithCheck: (documentId, entry, expectedVersion) => Effect.gen(function* () {
105
+ if (yield* shouldFail("append")) return yield* Effect.fail(new HotStorageError({
106
+ documentId,
107
+ operation: "appendWithCheck",
108
+ cause: new Error(errorMessage)
109
+ }));
110
+ const result = yield* Ref.modify(store, (map) => {
111
+ const existing = HashMap.get(map, documentId);
112
+ const entries = existing._tag === "Some" ? existing.value : [];
113
+ const lastVersion = entries.length > 0 ? Math.max(...entries.map((e) => e.version)) : 0;
114
+ if (expectedVersion === 1) {
115
+ if (lastVersion >= 1) return [{
116
+ type: "gap",
117
+ lastVersion
118
+ }, map];
119
+ } else if (lastVersion !== expectedVersion - 1) return [{
120
+ type: "gap",
121
+ lastVersion: lastVersion > 0 ? lastVersion : void 0
122
+ }, map];
123
+ return [{ type: "ok" }, HashMap.set(map, documentId, [...entries, entry])];
124
+ });
125
+ if (result.type === "gap") return yield* Effect.fail(new WalVersionGapError({
126
+ documentId,
127
+ expectedVersion,
128
+ actualPreviousVersion: result.lastVersion
129
+ }));
130
+ }),
131
+ getEntries: (documentId, sinceVersion) => Effect.gen(function* () {
132
+ if (yield* shouldFail("getEntries")) return yield* Effect.fail(new HotStorageError({
133
+ documentId,
134
+ operation: "getEntries",
135
+ cause: new Error(errorMessage)
136
+ }));
137
+ const current = yield* Ref.get(store);
138
+ const existing = HashMap.get(current, documentId);
139
+ return (existing._tag === "Some" ? existing.value : []).filter((e) => e.version > sinceVersion).sort((a, b) => a.version - b.version);
140
+ }),
141
+ truncate: (documentId, upToVersion) => Effect.gen(function* () {
142
+ if (yield* shouldFail("truncate")) return yield* Effect.fail(new HotStorageError({
143
+ documentId,
144
+ operation: "truncate",
145
+ cause: new Error(errorMessage)
146
+ }));
147
+ yield* Ref.update(store, (map) => {
148
+ const existing = HashMap.get(map, documentId);
149
+ if (existing._tag === "None") return map;
150
+ const filtered = existing.value.filter((e) => e.version > upToVersion);
151
+ return HashMap.set(map, documentId, filtered);
152
+ });
153
+ })
154
+ };
155
+ }));
156
+ const FailingStorage = {
157
+ makeColdStorage,
158
+ makeHotStorage
159
+ };
160
+
161
+ //#endregion
162
+ export { FailingStorage };
163
+ //# sourceMappingURL=FailingStorage.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailingStorage.mjs","names":["result: CheckResult"],"sources":["../../src/testing/FailingStorage.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect/testing - FailingStorage\n *\n * Mock storage implementations that simulate failures for testing error handling.\n * Use these to verify that your application correctly handles storage unavailability.\n *\n * @example\n * ```typescript\n * import { FailingStorage } from \"@voidhash/mimic-effect/testing\";\n *\n * // Create a ColdStorage that fails on load\n * const failingCold = FailingStorage.makeColdStorage({ failLoad: true });\n *\n * // Create a HotStorage that fails after 3 successful appends\n * const failingHot = FailingStorage.makeHotStorage({ failAfterN: 3, failAppend: true });\n * ```\n */\nimport { Effect, Layer, Ref, HashMap } from \"effect\";\nimport { ColdStorageTag, type ColdStorage } from \"../ColdStorage\";\nimport { HotStorageTag, type HotStorage } from \"../HotStorage\";\nimport { ColdStorageError, HotStorageError, WalVersionGapError } from \"../Errors\";\nimport type { StoredDocument, WalEntry } from \"../Types\";\n\n// =============================================================================\n// Configuration Types\n// =============================================================================\n\n/**\n * Configuration for failing ColdStorage\n */\nexport interface FailingColdStorageConfig {\n /** Fail load operations */\n readonly failLoad?: boolean;\n /** Fail save operations */\n readonly failSave?: boolean;\n /** Fail delete operations */\n readonly failDelete?: boolean;\n /** Fail after N successful operations (total across all operation types) */\n readonly failAfterN?: number;\n /** Custom error message */\n readonly errorMessage?: string;\n}\n\n/**\n * Configuration for failing HotStorage\n */\nexport interface FailingHotStorageConfig {\n /** Fail append operations */\n readonly failAppend?: boolean;\n /** Fail getEntries operations */\n readonly failGetEntries?: boolean;\n /** Fail truncate operations */\n readonly failTruncate?: boolean;\n /** Fail after N successful operations (total across all operation types) */\n readonly failAfterN?: number;\n /** Custom error message */\n readonly errorMessage?: string;\n}\n\n// =============================================================================\n// Failing ColdStorage\n// =============================================================================\n\n/**\n * Create a ColdStorage layer that simulates failures.\n * Wraps an in-memory storage and fails according to configuration.\n */\nexport const makeColdStorage = (\n config: FailingColdStorageConfig = {}\n): Layer.Layer<ColdStorageTag> =>\n Layer.effect(\n ColdStorageTag,\n Effect.gen(function* () {\n const store = yield* Ref.make(HashMap.empty<string, StoredDocument>());\n const operationCount = yield* Ref.make(0);\n\n const errorMessage = config.errorMessage ?? \"Simulated storage failure\";\n\n const shouldFail = (operation: \"load\" | \"save\" | \"delete\") =>\n Effect.gen(function* () {\n // Check if this specific operation should fail\n const opFails =\n (operation === \"load\" && config.failLoad) ||\n (operation === \"save\" && config.failSave) ||\n (operation === \"delete\" && config.failDelete);\n\n // If failAfterN is set, count operations first\n if (config.failAfterN !== undefined) {\n const count = yield* Ref.get(operationCount);\n yield* Ref.update(operationCount, (n) => n + 1);\n\n // Only start failing after N successful operations\n if (count < config.failAfterN) {\n return false;\n }\n // After N operations, fail if the specific op flag is set\n return opFails;\n }\n\n // No failAfterN - fail immediately if op flag is set\n return opFails;\n });\n\n const storage: ColdStorage = {\n load: (documentId) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"load\");\n if (fail) {\n return yield* Effect.fail(\n new ColdStorageError({\n documentId,\n operation: \"load\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n const current = yield* Ref.get(store);\n const doc = HashMap.get(current, documentId);\n return doc._tag === \"Some\" ? doc.value : undefined;\n }),\n\n save: (documentId, document) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"save\");\n if (fail) {\n return yield* Effect.fail(\n new ColdStorageError({\n documentId,\n operation: \"save\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n yield* Ref.update(store, (map) =>\n HashMap.set(map, documentId, document)\n );\n }),\n\n delete: (documentId) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"delete\");\n if (fail) {\n return yield* Effect.fail(\n new ColdStorageError({\n documentId,\n operation: \"delete\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n yield* Ref.update(store, (map) => HashMap.remove(map, documentId));\n }),\n };\n\n return storage;\n })\n );\n\n// =============================================================================\n// Failing HotStorage\n// =============================================================================\n\n/**\n * Create a HotStorage layer that simulates failures.\n * Wraps an in-memory storage and fails according to configuration.\n */\nexport const makeHotStorage = (\n config: FailingHotStorageConfig = {}\n): Layer.Layer<HotStorageTag> =>\n Layer.effect(\n HotStorageTag,\n Effect.gen(function* () {\n const store = yield* Ref.make(HashMap.empty<string, WalEntry[]>());\n const operationCount = yield* Ref.make(0);\n\n const errorMessage = config.errorMessage ?? \"Simulated storage failure\";\n\n const shouldFail = (operation: \"append\" | \"getEntries\" | \"truncate\") =>\n Effect.gen(function* () {\n // Check if this specific operation should fail\n const opFails =\n (operation === \"append\" && config.failAppend) ||\n (operation === \"getEntries\" && config.failGetEntries) ||\n (operation === \"truncate\" && config.failTruncate);\n\n // If failAfterN is set, count operations first\n if (config.failAfterN !== undefined) {\n const count = yield* Ref.get(operationCount);\n yield* Ref.update(operationCount, (n) => n + 1);\n\n // Only start failing after N successful operations\n if (count < config.failAfterN) {\n return false;\n }\n // After N operations, fail if the specific op flag is set\n return opFails;\n }\n\n // No failAfterN - fail immediately if op flag is set\n return opFails;\n });\n\n const storage: HotStorage = {\n append: (documentId, entry) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"append\");\n if (fail) {\n return yield* Effect.fail(\n new HotStorageError({\n documentId,\n operation: \"append\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n yield* Ref.update(store, (map) => {\n const current = HashMap.get(map, documentId);\n const entries = current._tag === \"Some\" ? current.value : [];\n return HashMap.set(map, documentId, [...entries, entry]);\n });\n }),\n\n appendWithCheck: (documentId, entry, expectedVersion) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"append\");\n if (fail) {\n return yield* Effect.fail(\n new HotStorageError({\n documentId,\n operation: \"appendWithCheck\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n type CheckResult =\n | { type: \"ok\" }\n | { type: \"gap\"; lastVersion: number | undefined };\n\n const result: CheckResult = yield* Ref.modify(store, (map): [CheckResult, HashMap.HashMap<string, WalEntry[]>] => {\n const existing = HashMap.get(map, documentId);\n const entries = existing._tag === \"Some\" ? existing.value : [];\n\n const lastVersion = entries.length > 0\n ? Math.max(...entries.map((e) => e.version))\n : 0;\n\n if (expectedVersion === 1) {\n if (lastVersion >= 1) {\n return [{ type: \"gap\", lastVersion }, map];\n }\n } else {\n if (lastVersion !== expectedVersion - 1) {\n return [{ type: \"gap\", lastVersion: lastVersion > 0 ? lastVersion : undefined }, map];\n }\n }\n\n return [\n { type: \"ok\" },\n HashMap.set(map, documentId, [...entries, entry]),\n ];\n });\n\n if (result.type === \"gap\") {\n return yield* Effect.fail(\n new WalVersionGapError({\n documentId,\n expectedVersion,\n actualPreviousVersion: result.lastVersion,\n })\n );\n }\n }),\n\n getEntries: (documentId, sinceVersion) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"getEntries\");\n if (fail) {\n return yield* Effect.fail(\n new HotStorageError({\n documentId,\n operation: \"getEntries\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n const entries = existing._tag === \"Some\" ? existing.value : [];\n\n return entries\n .filter((e) => e.version > sinceVersion)\n .sort((a, b) => a.version - b.version);\n }),\n\n truncate: (documentId, upToVersion) =>\n Effect.gen(function* () {\n const fail = yield* shouldFail(\"truncate\");\n if (fail) {\n return yield* Effect.fail(\n new HotStorageError({\n documentId,\n operation: \"truncate\",\n cause: new Error(errorMessage),\n })\n );\n }\n\n yield* Ref.update(store, (map) => {\n const existing = HashMap.get(map, documentId);\n if (existing._tag === \"None\") {\n return map;\n }\n const filtered = existing.value.filter(\n (e) => e.version > upToVersion\n );\n return HashMap.set(map, documentId, filtered);\n });\n }),\n };\n\n return storage;\n })\n );\n\n// =============================================================================\n// Export Namespace\n// =============================================================================\n\nexport const FailingStorage = {\n makeColdStorage,\n makeHotStorage,\n};\n"],"mappings":"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"}