@voidhash/mimic-effect 0.0.9 → 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.
- package/.turbo/turbo-build.log +136 -90
- package/README.md +385 -0
- package/dist/ColdStorage.cjs +64 -0
- package/dist/ColdStorage.d.cts +53 -0
- package/dist/ColdStorage.d.cts.map +1 -0
- package/dist/ColdStorage.d.mts +53 -0
- package/dist/ColdStorage.d.mts.map +1 -0
- package/dist/ColdStorage.mjs +64 -0
- package/dist/ColdStorage.mjs.map +1 -0
- package/dist/DocumentInstance.cjs +263 -0
- package/dist/DocumentInstance.d.cts +78 -0
- package/dist/DocumentInstance.d.cts.map +1 -0
- package/dist/DocumentInstance.d.mts +78 -0
- package/dist/DocumentInstance.d.mts.map +1 -0
- package/dist/DocumentInstance.mjs +264 -0
- package/dist/DocumentInstance.mjs.map +1 -0
- package/dist/Errors.cjs +54 -0
- package/dist/Errors.d.cts +96 -0
- package/dist/Errors.d.cts.map +1 -0
- package/dist/Errors.d.mts +96 -0
- package/dist/Errors.d.mts.map +1 -0
- package/dist/Errors.mjs +48 -0
- package/dist/Errors.mjs.map +1 -0
- package/dist/HotStorage.cjs +104 -0
- package/dist/HotStorage.d.cts +70 -0
- package/dist/HotStorage.d.cts.map +1 -0
- package/dist/HotStorage.d.mts +70 -0
- package/dist/HotStorage.d.mts.map +1 -0
- package/dist/HotStorage.mjs +104 -0
- package/dist/HotStorage.mjs.map +1 -0
- package/dist/Metrics.cjs +149 -0
- package/dist/Metrics.d.cts +32 -0
- package/dist/Metrics.d.cts.map +1 -0
- package/dist/Metrics.d.mts +32 -0
- package/dist/Metrics.d.mts.map +1 -0
- package/dist/Metrics.mjs +131 -0
- package/dist/Metrics.mjs.map +1 -0
- package/dist/MimicAuthService.cjs +61 -45
- package/dist/MimicAuthService.d.cts +61 -48
- package/dist/MimicAuthService.d.cts.map +1 -1
- package/dist/MimicAuthService.d.mts +61 -48
- package/dist/MimicAuthService.d.mts.map +1 -1
- package/dist/MimicAuthService.mjs +60 -36
- package/dist/MimicAuthService.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +348 -0
- package/dist/MimicClusterServerEngine.d.cts +17 -0
- package/dist/MimicClusterServerEngine.d.cts.map +1 -0
- package/dist/MimicClusterServerEngine.d.mts +17 -0
- package/dist/MimicClusterServerEngine.d.mts.map +1 -0
- package/dist/MimicClusterServerEngine.mjs +350 -0
- package/dist/MimicClusterServerEngine.mjs.map +1 -0
- package/dist/MimicServer.cjs +205 -96
- package/dist/MimicServer.d.cts +9 -110
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts +9 -110
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +206 -90
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +178 -0
- package/dist/MimicServerEngine.d.cts +83 -0
- package/dist/MimicServerEngine.d.cts.map +1 -0
- package/dist/MimicServerEngine.d.mts +83 -0
- package/dist/MimicServerEngine.d.mts.map +1 -0
- package/dist/MimicServerEngine.mjs +178 -0
- package/dist/MimicServerEngine.mjs.map +1 -0
- package/dist/PresenceManager.cjs +75 -91
- package/dist/PresenceManager.d.cts +17 -66
- package/dist/PresenceManager.d.cts.map +1 -1
- package/dist/PresenceManager.d.mts +17 -66
- package/dist/PresenceManager.d.mts.map +1 -1
- package/dist/PresenceManager.mjs +74 -78
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Protocol.cjs +146 -0
- package/dist/Protocol.d.cts +203 -0
- package/dist/Protocol.d.cts.map +1 -0
- package/dist/Protocol.d.mts +203 -0
- package/dist/Protocol.d.mts.map +1 -0
- package/dist/Protocol.mjs +132 -0
- package/dist/Protocol.mjs.map +1 -0
- package/dist/Types.d.cts +179 -0
- package/dist/Types.d.cts.map +1 -0
- package/dist/Types.d.mts +179 -0
- package/dist/Types.d.mts.map +1 -0
- package/dist/_virtual/rolldown_runtime.cjs +1 -25
- package/dist/_virtual/rolldown_runtime.mjs +4 -1
- package/dist/index.cjs +37 -76
- package/dist/index.d.cts +13 -12
- package/dist/index.d.mts +13 -12
- package/dist/index.mjs +12 -12
- package/dist/testing/ColdStorageTestSuite.cjs +508 -0
- package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.mjs +508 -0
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
- package/dist/testing/FailingStorage.cjs +162 -0
- package/dist/testing/FailingStorage.d.cts +43 -0
- package/dist/testing/FailingStorage.d.cts.map +1 -0
- package/dist/testing/FailingStorage.d.mts +43 -0
- package/dist/testing/FailingStorage.d.mts.map +1 -0
- package/dist/testing/FailingStorage.mjs +163 -0
- package/dist/testing/FailingStorage.mjs.map +1 -0
- package/dist/testing/HotStorageTestSuite.cjs +820 -0
- package/dist/testing/HotStorageTestSuite.d.cts +42 -0
- package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/HotStorageTestSuite.d.mts +42 -0
- package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/HotStorageTestSuite.mjs +820 -0
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.cjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
- package/dist/testing/assertions.cjs +117 -0
- package/dist/testing/assertions.mjs +112 -0
- package/dist/testing/assertions.mjs.map +1 -0
- package/dist/testing/index.cjs +14 -0
- package/dist/testing/index.d.cts +6 -0
- package/dist/testing/index.d.mts +6 -0
- package/dist/testing/index.mjs +7 -0
- package/dist/testing/types.cjs +15 -0
- package/dist/testing/types.d.cts +90 -0
- package/dist/testing/types.d.cts.map +1 -0
- package/dist/testing/types.d.mts +90 -0
- package/dist/testing/types.d.mts.map +1 -0
- package/dist/testing/types.mjs +16 -0
- package/dist/testing/types.mjs.map +1 -0
- package/package.json +18 -3
- package/src/ColdStorage.ts +145 -0
- package/src/DocumentInstance.ts +527 -0
- package/src/Errors.ts +114 -0
- package/src/HotStorage.ts +256 -0
- package/src/Metrics.ts +193 -0
- package/src/MimicAuthService.ts +126 -64
- package/src/MimicClusterServerEngine.ts +669 -0
- package/src/MimicServer.ts +459 -198
- package/src/MimicServerEngine.ts +472 -0
- package/src/PresenceManager.ts +173 -234
- package/src/Protocol.ts +350 -0
- package/src/Types.ts +238 -0
- package/src/index.ts +27 -23
- package/src/testing/ColdStorageTestSuite.ts +589 -0
- package/src/testing/FailingStorage.ts +338 -0
- package/src/testing/HotStorageTestSuite.ts +1105 -0
- package/src/testing/StorageIntegrationTestSuite.ts +736 -0
- package/src/testing/assertions.ts +188 -0
- package/src/testing/index.ts +83 -0
- package/src/testing/types.ts +100 -0
- package/tests/ColdStorage.test.ts +24 -0
- package/tests/DocumentInstance.test.ts +669 -0
- package/tests/HotStorage.test.ts +24 -0
- package/tests/MimicAuthService.test.ts +102 -134
- package/tests/MimicClusterServerEngine.test.ts +587 -0
- package/tests/MimicServer.test.ts +90 -226
- package/tests/MimicServerEngine.test.ts +521 -0
- package/tests/PresenceManager.test.ts +22 -63
- package/tests/Protocol.test.ts +190 -0
- package/tests/StorageIntegration.test.ts +259 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +1 -1
- package/dist/DocumentManager.cjs +0 -118
- package/dist/DocumentManager.d.cts +0 -45
- package/dist/DocumentManager.d.cts.map +0 -1
- package/dist/DocumentManager.d.mts +0 -45
- package/dist/DocumentManager.d.mts.map +0 -1
- package/dist/DocumentManager.mjs +0 -105
- package/dist/DocumentManager.mjs.map +0 -1
- package/dist/DocumentProtocol.cjs +0 -94
- package/dist/DocumentProtocol.d.cts +0 -113
- package/dist/DocumentProtocol.d.cts.map +0 -1
- package/dist/DocumentProtocol.d.mts +0 -113
- package/dist/DocumentProtocol.d.mts.map +0 -1
- package/dist/DocumentProtocol.mjs +0 -89
- package/dist/DocumentProtocol.mjs.map +0 -1
- package/dist/MimicConfig.cjs +0 -60
- package/dist/MimicConfig.d.cts +0 -141
- package/dist/MimicConfig.d.cts.map +0 -1
- package/dist/MimicConfig.d.mts +0 -141
- package/dist/MimicConfig.d.mts.map +0 -1
- package/dist/MimicConfig.mjs +0 -50
- package/dist/MimicConfig.mjs.map +0 -1
- package/dist/MimicDataStorage.cjs +0 -83
- package/dist/MimicDataStorage.d.cts +0 -113
- package/dist/MimicDataStorage.d.cts.map +0 -1
- package/dist/MimicDataStorage.d.mts +0 -113
- package/dist/MimicDataStorage.d.mts.map +0 -1
- package/dist/MimicDataStorage.mjs +0 -74
- package/dist/MimicDataStorage.mjs.map +0 -1
- package/dist/WebSocketHandler.cjs +0 -365
- package/dist/WebSocketHandler.d.cts +0 -34
- package/dist/WebSocketHandler.d.cts.map +0 -1
- package/dist/WebSocketHandler.d.mts +0 -34
- package/dist/WebSocketHandler.d.mts.map +0 -1
- package/dist/WebSocketHandler.mjs +0 -355
- package/dist/WebSocketHandler.mjs.map +0 -1
- package/dist/auth/NoAuth.cjs +0 -43
- package/dist/auth/NoAuth.d.cts +0 -22
- package/dist/auth/NoAuth.d.cts.map +0 -1
- package/dist/auth/NoAuth.d.mts +0 -22
- package/dist/auth/NoAuth.d.mts.map +0 -1
- package/dist/auth/NoAuth.mjs +0 -36
- package/dist/auth/NoAuth.mjs.map +0 -1
- package/dist/errors.cjs +0 -74
- package/dist/errors.d.cts +0 -89
- package/dist/errors.d.cts.map +0 -1
- package/dist/errors.d.mts +0 -89
- package/dist/errors.d.mts.map +0 -1
- package/dist/errors.mjs +0 -67
- package/dist/errors.mjs.map +0 -1
- package/dist/storage/InMemoryDataStorage.cjs +0 -57
- package/dist/storage/InMemoryDataStorage.d.cts +0 -19
- package/dist/storage/InMemoryDataStorage.d.cts.map +0 -1
- package/dist/storage/InMemoryDataStorage.d.mts +0 -19
- package/dist/storage/InMemoryDataStorage.d.mts.map +0 -1
- package/dist/storage/InMemoryDataStorage.mjs +0 -48
- package/dist/storage/InMemoryDataStorage.mjs.map +0 -1
- package/src/DocumentManager.ts +0 -254
- package/src/DocumentProtocol.ts +0 -112
- package/src/MimicConfig.ts +0 -211
- package/src/MimicDataStorage.ts +0 -157
- package/src/WebSocketHandler.ts +0 -735
- package/src/auth/NoAuth.ts +0 -46
- package/src/errors.ts +0 -113
- package/src/storage/InMemoryDataStorage.ts +0 -66
- package/tests/DocumentManager.test.ts +0 -464
- package/tests/DocumentProtocol.test.ts +0 -113
- package/tests/InMemoryDataStorage.test.ts +0 -190
- package/tests/MimicConfig.test.ts +0 -290
- package/tests/MimicDataStorage.test.ts +0 -190
- package/tests/NoAuth.test.ts +0 -94
- package/tests/WebSocketHandler.test.ts +0 -321
- package/tests/errors.test.ts +0 -77
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import * as Schema from "effect/Schema";
|
|
3
|
-
import * as Protocol from "../src/DocumentProtocol";
|
|
4
|
-
|
|
5
|
-
// =============================================================================
|
|
6
|
-
// Schema Tests
|
|
7
|
-
// =============================================================================
|
|
8
|
-
|
|
9
|
-
describe("DocumentProtocol", () => {
|
|
10
|
-
describe("TransactionSchema", () => {
|
|
11
|
-
it("should validate a valid transaction", () => {
|
|
12
|
-
const transaction = {
|
|
13
|
-
id: "tx-123",
|
|
14
|
-
ops: [
|
|
15
|
-
{ kind: "string.set", path: { segments: ["title"] }, payload: "Hello" },
|
|
16
|
-
],
|
|
17
|
-
timestamp: Date.now(),
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const result = Schema.decodeUnknownSync(Protocol.TransactionSchema)(transaction);
|
|
21
|
-
expect(result.id).toBe("tx-123");
|
|
22
|
-
expect(result.ops).toHaveLength(1);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should reject invalid transaction", () => {
|
|
26
|
-
const invalid = {
|
|
27
|
-
id: 123, // should be string
|
|
28
|
-
ops: [],
|
|
29
|
-
timestamp: Date.now(),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
expect(() =>
|
|
33
|
-
Schema.decodeUnknownSync(Protocol.TransactionSchema)(invalid)
|
|
34
|
-
).toThrow();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe("TransactionMessageSchema", () => {
|
|
39
|
-
it("should validate a transaction message", () => {
|
|
40
|
-
const message = {
|
|
41
|
-
type: "transaction",
|
|
42
|
-
transaction: {
|
|
43
|
-
id: "tx-456",
|
|
44
|
-
ops: [],
|
|
45
|
-
timestamp: Date.now(),
|
|
46
|
-
},
|
|
47
|
-
version: 1,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const result = Schema.decodeUnknownSync(Protocol.TransactionMessageSchema)(message);
|
|
51
|
-
expect(result.type).toBe("transaction");
|
|
52
|
-
expect(result.version).toBe(1);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe("SnapshotMessageSchema", () => {
|
|
57
|
-
it("should validate a snapshot message", () => {
|
|
58
|
-
const message = {
|
|
59
|
-
type: "snapshot",
|
|
60
|
-
state: { title: "Test", count: 42 },
|
|
61
|
-
version: 5,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const result = Schema.decodeUnknownSync(Protocol.SnapshotMessageSchema)(message);
|
|
65
|
-
expect(result.type).toBe("snapshot");
|
|
66
|
-
expect(result.state).toEqual({ title: "Test", count: 42 });
|
|
67
|
-
expect(result.version).toBe(5);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe("ErrorMessageSchema", () => {
|
|
72
|
-
it("should validate an error message", () => {
|
|
73
|
-
const message = {
|
|
74
|
-
type: "error",
|
|
75
|
-
transactionId: "tx-789",
|
|
76
|
-
reason: "Transaction is empty",
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const result = Schema.decodeUnknownSync(Protocol.ErrorMessageSchema)(message);
|
|
80
|
-
expect(result.type).toBe("error");
|
|
81
|
-
expect(result.transactionId).toBe("tx-789");
|
|
82
|
-
expect(result.reason).toBe("Transaction is empty");
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("SubmitResultSchema", () => {
|
|
87
|
-
it("should validate a success result", () => {
|
|
88
|
-
const result = {
|
|
89
|
-
success: true,
|
|
90
|
-
version: 10,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const decoded = Schema.decodeUnknownSync(Protocol.SubmitResultSchema)(result);
|
|
94
|
-
expect(decoded.success).toBe(true);
|
|
95
|
-
if (decoded.success) {
|
|
96
|
-
expect(decoded.version).toBe(10);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("should validate a failure result", () => {
|
|
101
|
-
const result = {
|
|
102
|
-
success: false,
|
|
103
|
-
reason: "Invalid operation",
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const decoded = Schema.decodeUnknownSync(Protocol.SubmitResultSchema)(result);
|
|
107
|
-
expect(decoded.success).toBe(false);
|
|
108
|
-
if (!decoded.success) {
|
|
109
|
-
expect(decoded.reason).toBe("Invalid operation");
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as InMemoryDataStorage from "../src/storage/InMemoryDataStorage";
|
|
4
|
-
import { MimicDataStorageTag } from "../src/MimicDataStorage";
|
|
5
|
-
|
|
6
|
-
// =============================================================================
|
|
7
|
-
// InMemoryDataStorage Tests
|
|
8
|
-
// =============================================================================
|
|
9
|
-
|
|
10
|
-
describe("InMemoryDataStorage", () => {
|
|
11
|
-
describe("load", () => {
|
|
12
|
-
it("should return undefined for non-existent documents", async () => {
|
|
13
|
-
const result = await Effect.runPromise(
|
|
14
|
-
Effect.gen(function* () {
|
|
15
|
-
const storage = yield* MimicDataStorageTag;
|
|
16
|
-
return yield* storage.load("non-existent-doc");
|
|
17
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
expect(result).toBeUndefined();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("save and load", () => {
|
|
25
|
-
it("should save and load document state", async () => {
|
|
26
|
-
const testState = { title: "Test Document", count: 42 };
|
|
27
|
-
|
|
28
|
-
const result = await Effect.runPromise(
|
|
29
|
-
Effect.gen(function* () {
|
|
30
|
-
const storage = yield* MimicDataStorageTag;
|
|
31
|
-
yield* storage.save("doc-1", testState);
|
|
32
|
-
return yield* storage.load("doc-1");
|
|
33
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(result).toEqual(testState);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should update existing document", async () => {
|
|
40
|
-
const initialState = { title: "Initial", count: 0 };
|
|
41
|
-
const updatedState = { title: "Updated", count: 100 };
|
|
42
|
-
|
|
43
|
-
const result = await Effect.runPromise(
|
|
44
|
-
Effect.gen(function* () {
|
|
45
|
-
const storage = yield* MimicDataStorageTag;
|
|
46
|
-
yield* storage.save("doc-1", initialState);
|
|
47
|
-
yield* storage.save("doc-1", updatedState);
|
|
48
|
-
return yield* storage.load("doc-1");
|
|
49
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(result).toEqual(updatedState);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should store multiple documents independently", async () => {
|
|
56
|
-
const state1 = { title: "Doc 1" };
|
|
57
|
-
const state2 = { title: "Doc 2" };
|
|
58
|
-
const state3 = { title: "Doc 3" };
|
|
59
|
-
|
|
60
|
-
const result = await Effect.runPromise(
|
|
61
|
-
Effect.gen(function* () {
|
|
62
|
-
const storage = yield* MimicDataStorageTag;
|
|
63
|
-
yield* storage.save("doc-1", state1);
|
|
64
|
-
yield* storage.save("doc-2", state2);
|
|
65
|
-
yield* storage.save("doc-3", state3);
|
|
66
|
-
return {
|
|
67
|
-
doc1: yield* storage.load("doc-1"),
|
|
68
|
-
doc2: yield* storage.load("doc-2"),
|
|
69
|
-
doc3: yield* storage.load("doc-3"),
|
|
70
|
-
};
|
|
71
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
expect(result.doc1).toEqual(state1);
|
|
75
|
-
expect(result.doc2).toEqual(state2);
|
|
76
|
-
expect(result.doc3).toEqual(state3);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe("delete", () => {
|
|
81
|
-
it("should delete existing document", async () => {
|
|
82
|
-
const testState = { title: "To be deleted" };
|
|
83
|
-
|
|
84
|
-
const result = await Effect.runPromise(
|
|
85
|
-
Effect.gen(function* () {
|
|
86
|
-
const storage = yield* MimicDataStorageTag;
|
|
87
|
-
yield* storage.save("doc-1", testState);
|
|
88
|
-
const beforeDelete = yield* storage.load("doc-1");
|
|
89
|
-
yield* storage.delete("doc-1");
|
|
90
|
-
const afterDelete = yield* storage.load("doc-1");
|
|
91
|
-
return { beforeDelete, afterDelete };
|
|
92
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(result.beforeDelete).toEqual(testState);
|
|
96
|
-
expect(result.afterDelete).toBeUndefined();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("should handle deleting non-existent document gracefully", async () => {
|
|
100
|
-
await Effect.runPromise(
|
|
101
|
-
Effect.gen(function* () {
|
|
102
|
-
const storage = yield* MimicDataStorageTag;
|
|
103
|
-
yield* storage.delete("non-existent-doc");
|
|
104
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
105
|
-
);
|
|
106
|
-
// Should not throw
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("should not affect other documents when deleting", async () => {
|
|
110
|
-
const state1 = { title: "Doc 1" };
|
|
111
|
-
const state2 = { title: "Doc 2" };
|
|
112
|
-
|
|
113
|
-
const result = await Effect.runPromise(
|
|
114
|
-
Effect.gen(function* () {
|
|
115
|
-
const storage = yield* MimicDataStorageTag;
|
|
116
|
-
yield* storage.save("doc-1", state1);
|
|
117
|
-
yield* storage.save("doc-2", state2);
|
|
118
|
-
yield* storage.delete("doc-1");
|
|
119
|
-
return {
|
|
120
|
-
doc1: yield* storage.load("doc-1"),
|
|
121
|
-
doc2: yield* storage.load("doc-2"),
|
|
122
|
-
};
|
|
123
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
expect(result.doc1).toBeUndefined();
|
|
127
|
-
expect(result.doc2).toEqual(state2);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("onLoad", () => {
|
|
132
|
-
it("should pass through state unchanged", async () => {
|
|
133
|
-
const testState = { title: "Test", nested: { value: 123 } };
|
|
134
|
-
|
|
135
|
-
const result = await Effect.runPromise(
|
|
136
|
-
Effect.gen(function* () {
|
|
137
|
-
const storage = yield* MimicDataStorageTag;
|
|
138
|
-
return yield* storage.onLoad(testState);
|
|
139
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
expect(result).toBe(testState);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("onSave", () => {
|
|
147
|
-
it("should pass through state unchanged", async () => {
|
|
148
|
-
const testState = { title: "Test", nested: { value: 456 } };
|
|
149
|
-
|
|
150
|
-
const result = await Effect.runPromise(
|
|
151
|
-
Effect.gen(function* () {
|
|
152
|
-
const storage = yield* MimicDataStorageTag;
|
|
153
|
-
return yield* storage.onSave(testState);
|
|
154
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
expect(result).toBe(testState);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("layer aliases", () => {
|
|
162
|
-
it("should have layerDefault as an alias for layer", () => {
|
|
163
|
-
expect(InMemoryDataStorage.layerDefault).toBe(InMemoryDataStorage.layer);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe("isolation", () => {
|
|
168
|
-
it("should have independent storage per layer instance", async () => {
|
|
169
|
-
const testState = { title: "Isolated" };
|
|
170
|
-
|
|
171
|
-
// Save in one layer
|
|
172
|
-
await Effect.runPromise(
|
|
173
|
-
Effect.gen(function* () {
|
|
174
|
-
const storage = yield* MimicDataStorageTag;
|
|
175
|
-
yield* storage.save("doc-1", testState);
|
|
176
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Load in a new layer instance - should not find the document
|
|
180
|
-
const result = await Effect.runPromise(
|
|
181
|
-
Effect.gen(function* () {
|
|
182
|
-
const storage = yield* MimicDataStorageTag;
|
|
183
|
-
return yield* storage.load("doc-1");
|
|
184
|
-
}).pipe(Effect.provide(InMemoryDataStorage.layer))
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
expect(result).toBeUndefined();
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
});
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Duration from "effect/Duration";
|
|
4
|
-
import * as Schema from "effect/Schema";
|
|
5
|
-
import { Primitive, Presence } from "@voidhash/mimic";
|
|
6
|
-
import * as MimicConfig from "../src/MimicConfig";
|
|
7
|
-
|
|
8
|
-
// =============================================================================
|
|
9
|
-
// Test Schema
|
|
10
|
-
// =============================================================================
|
|
11
|
-
|
|
12
|
-
const TestSchema = Primitive.Struct({
|
|
13
|
-
title: Primitive.String().default(""),
|
|
14
|
-
count: Primitive.Number().default(0),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// =============================================================================
|
|
18
|
-
// MimicConfig Tests
|
|
19
|
-
// =============================================================================
|
|
20
|
-
|
|
21
|
-
describe("MimicConfig", () => {
|
|
22
|
-
describe("make", () => {
|
|
23
|
-
it("should create config with default values", () => {
|
|
24
|
-
const config = MimicConfig.make({
|
|
25
|
-
schema: TestSchema,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
expect(config.schema).toBe(TestSchema);
|
|
29
|
-
expect(Duration.toMillis(config.maxIdleTime)).toBe(5 * 60 * 1000); // 5 minutes
|
|
30
|
-
expect(config.maxTransactionHistory).toBe(1000);
|
|
31
|
-
expect(Duration.toMillis(config.heartbeatInterval)).toBe(30 * 1000); // 30 seconds
|
|
32
|
-
expect(Duration.toMillis(config.heartbeatTimeout)).toBe(10 * 1000); // 10 seconds
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should accept custom maxIdleTime", () => {
|
|
36
|
-
const config = MimicConfig.make({
|
|
37
|
-
schema: TestSchema,
|
|
38
|
-
maxIdleTime: "10 minutes",
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(Duration.toMillis(config.maxIdleTime)).toBe(10 * 60 * 1000);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should accept custom maxTransactionHistory", () => {
|
|
45
|
-
const config = MimicConfig.make({
|
|
46
|
-
schema: TestSchema,
|
|
47
|
-
maxTransactionHistory: 500,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
expect(config.maxTransactionHistory).toBe(500);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should accept custom heartbeatInterval", () => {
|
|
54
|
-
const config = MimicConfig.make({
|
|
55
|
-
schema: TestSchema,
|
|
56
|
-
heartbeatInterval: "1 minute",
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
expect(Duration.toMillis(config.heartbeatInterval)).toBe(60 * 1000);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should accept custom heartbeatTimeout", () => {
|
|
63
|
-
const config = MimicConfig.make({
|
|
64
|
-
schema: TestSchema,
|
|
65
|
-
heartbeatTimeout: "30 seconds",
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(Duration.toMillis(config.heartbeatTimeout)).toBe(30 * 1000);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("should accept all custom values", () => {
|
|
72
|
-
const config = MimicConfig.make({
|
|
73
|
-
schema: TestSchema,
|
|
74
|
-
maxIdleTime: "15 minutes",
|
|
75
|
-
maxTransactionHistory: 2000,
|
|
76
|
-
heartbeatInterval: "45 seconds",
|
|
77
|
-
heartbeatTimeout: "15 seconds",
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expect(config.schema).toBe(TestSchema);
|
|
81
|
-
expect(Duration.toMillis(config.maxIdleTime)).toBe(15 * 60 * 1000);
|
|
82
|
-
expect(config.maxTransactionHistory).toBe(2000);
|
|
83
|
-
expect(Duration.toMillis(config.heartbeatInterval)).toBe(45 * 1000);
|
|
84
|
-
expect(Duration.toMillis(config.heartbeatTimeout)).toBe(15 * 1000);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe("layer", () => {
|
|
89
|
-
it("should create a layer that provides MimicServerConfigTag", async () => {
|
|
90
|
-
const testLayer = MimicConfig.layer({
|
|
91
|
-
schema: TestSchema,
|
|
92
|
-
maxTransactionHistory: 100,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const result = await Effect.runPromise(
|
|
96
|
-
Effect.gen(function* () {
|
|
97
|
-
const config = yield* MimicConfig.MimicServerConfigTag;
|
|
98
|
-
return config;
|
|
99
|
-
}).pipe(Effect.provide(testLayer))
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
expect(result.schema).toBe(TestSchema);
|
|
103
|
-
expect(result.maxTransactionHistory).toBe(100);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe("MimicServerConfigTag", () => {
|
|
108
|
-
it("should have the correct tag identifier", () => {
|
|
109
|
-
expect(MimicConfig.MimicServerConfigTag.key).toBe(
|
|
110
|
-
"@voidhash/mimic-server-effect/MimicServerConfig"
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe("presence configuration", () => {
|
|
116
|
-
const CursorPresence = Presence.make({
|
|
117
|
-
schema: Schema.Struct({
|
|
118
|
-
x: Schema.Number,
|
|
119
|
-
y: Schema.Number,
|
|
120
|
-
name: Schema.optional(Schema.String),
|
|
121
|
-
}),
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should have undefined presence by default", () => {
|
|
125
|
-
const config = MimicConfig.make({
|
|
126
|
-
schema: TestSchema,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
expect(config.presence).toBeUndefined();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("should accept presence schema option", () => {
|
|
133
|
-
const config = MimicConfig.make({
|
|
134
|
-
schema: TestSchema,
|
|
135
|
-
presence: CursorPresence,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
expect(config.presence).toBe(CursorPresence);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("should provide presence through layer", async () => {
|
|
142
|
-
const testLayer = MimicConfig.layer({
|
|
143
|
-
schema: TestSchema,
|
|
144
|
-
presence: CursorPresence,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const result = await Effect.runPromise(
|
|
148
|
-
Effect.gen(function* () {
|
|
149
|
-
const config = yield* MimicConfig.MimicServerConfigTag;
|
|
150
|
-
return config.presence;
|
|
151
|
-
}).pipe(Effect.provide(testLayer))
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
expect(result).toBe(CursorPresence);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should work with all options including presence", () => {
|
|
158
|
-
const config = MimicConfig.make({
|
|
159
|
-
schema: TestSchema,
|
|
160
|
-
maxIdleTime: "10 minutes",
|
|
161
|
-
maxTransactionHistory: 500,
|
|
162
|
-
heartbeatInterval: "1 minute",
|
|
163
|
-
heartbeatTimeout: "20 seconds",
|
|
164
|
-
presence: CursorPresence,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
expect(config.schema).toBe(TestSchema);
|
|
168
|
-
expect(Duration.toMillis(config.maxIdleTime)).toBe(10 * 60 * 1000);
|
|
169
|
-
expect(config.maxTransactionHistory).toBe(500);
|
|
170
|
-
expect(Duration.toMillis(config.heartbeatInterval)).toBe(60 * 1000);
|
|
171
|
-
expect(Duration.toMillis(config.heartbeatTimeout)).toBe(20 * 1000);
|
|
172
|
-
expect(config.presence).toBe(CursorPresence);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe("initial state configuration", () => {
|
|
177
|
-
it("should have undefined initial state by default", () => {
|
|
178
|
-
const config = MimicConfig.make({
|
|
179
|
-
schema: TestSchema,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(config.initial).toBeUndefined();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("should accept initial state object and convert to function", async () => {
|
|
186
|
-
const config = MimicConfig.make({
|
|
187
|
-
schema: TestSchema,
|
|
188
|
-
initial: { title: "My Document", count: 42 },
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
expect(typeof config.initial).toBe("function");
|
|
192
|
-
// The function should return the initial state when called
|
|
193
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "test-doc" }));
|
|
194
|
-
expect(result).toEqual({ title: "My Document", count: 42 });
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("should apply defaults for omitted fields in initial state object", async () => {
|
|
198
|
-
const config = MimicConfig.make({
|
|
199
|
-
schema: TestSchema,
|
|
200
|
-
initial: { title: "My Document" }, // count has default of 0
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "test-doc" }));
|
|
204
|
-
expect(result).toEqual({ title: "My Document", count: 0 });
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("should accept initial state function", async () => {
|
|
208
|
-
const config = MimicConfig.make({
|
|
209
|
-
schema: TestSchema,
|
|
210
|
-
initial: ({ documentId }) => Effect.succeed({ title: `Doc ${documentId}`, count: 123 }),
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
expect(typeof config.initial).toBe("function");
|
|
214
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "my-doc-id" }));
|
|
215
|
-
expect(result).toEqual({ title: "Doc my-doc-id", count: 123 });
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("should apply defaults to initial state function result", async () => {
|
|
219
|
-
const config = MimicConfig.make({
|
|
220
|
-
schema: TestSchema,
|
|
221
|
-
initial: ({ documentId }) => Effect.succeed({ title: documentId }), // count omitted
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "test" }));
|
|
225
|
-
expect(result).toEqual({ title: "test", count: 0 });
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("should provide initial function through layer", async () => {
|
|
229
|
-
const testLayer = MimicConfig.layer({
|
|
230
|
-
schema: TestSchema,
|
|
231
|
-
initial: { title: "From Layer", count: 100 },
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
const result = await Effect.runPromise(
|
|
235
|
-
Effect.gen(function* () {
|
|
236
|
-
const config = yield* MimicConfig.MimicServerConfigTag;
|
|
237
|
-
return yield* config.initial!({ documentId: "layer-doc" });
|
|
238
|
-
}).pipe(Effect.provide(testLayer))
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
expect(result).toEqual({ title: "From Layer", count: 100 });
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("should work with schema that has required fields without defaults", async () => {
|
|
245
|
-
const SchemaWithRequired = Primitive.Struct({
|
|
246
|
-
name: Primitive.String().required(),
|
|
247
|
-
optional: Primitive.String().default("default"),
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const config = MimicConfig.make({
|
|
251
|
-
schema: SchemaWithRequired,
|
|
252
|
-
initial: { name: "Required Name" },
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "test" }));
|
|
256
|
-
expect(result).toEqual({ name: "Required Name", optional: "default" });
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it("should work with all options including initial object", async () => {
|
|
260
|
-
const config = MimicConfig.make({
|
|
261
|
-
schema: TestSchema,
|
|
262
|
-
maxIdleTime: "10 minutes",
|
|
263
|
-
maxTransactionHistory: 500,
|
|
264
|
-
initial: { title: "Full Options", count: 999 },
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
expect(config.schema).toBe(TestSchema);
|
|
268
|
-
expect(Duration.toMillis(config.maxIdleTime)).toBe(10 * 60 * 1000);
|
|
269
|
-
expect(config.maxTransactionHistory).toBe(500);
|
|
270
|
-
const result = await Effect.runPromise(config.initial!({ documentId: "test" }));
|
|
271
|
-
expect(result).toEqual({ title: "Full Options", count: 999 });
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("should work with initial function that uses documentId", async () => {
|
|
275
|
-
const config = MimicConfig.make({
|
|
276
|
-
schema: TestSchema,
|
|
277
|
-
initial: ({ documentId }) => Effect.succeed({
|
|
278
|
-
title: `Document: ${documentId}`,
|
|
279
|
-
count: documentId.length,
|
|
280
|
-
}),
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const result1 = await Effect.runPromise(config.initial!({ documentId: "short" }));
|
|
284
|
-
expect(result1).toEqual({ title: "Document: short", count: 5 });
|
|
285
|
-
|
|
286
|
-
const result2 = await Effect.runPromise(config.initial!({ documentId: "longer-id" }));
|
|
287
|
-
expect(result2).toEqual({ title: "Document: longer-id", count: 9 });
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
});
|