@voidhash/mimic-effect 1.0.0-beta.15 → 1.0.0-beta.17
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/dist/ColdStorage.cjs +1 -1
- package/dist/ColdStorage.d.cts +2 -2
- package/dist/ColdStorage.d.cts.map +1 -1
- package/dist/ColdStorage.d.mts +2 -2
- package/dist/ColdStorage.d.mts.map +1 -1
- package/dist/ColdStorage.mjs +2 -2
- package/dist/ColdStorage.mjs.map +1 -1
- package/dist/DocumentInstance.cjs +13 -13
- package/dist/DocumentInstance.mjs +13 -13
- package/dist/DocumentInstance.mjs.map +1 -1
- package/dist/Errors.d.cts +8 -8
- package/dist/Errors.d.cts.map +1 -1
- package/dist/Errors.d.mts +8 -8
- package/dist/Errors.d.mts.map +1 -1
- package/dist/HotStorage.cjs +1 -1
- package/dist/HotStorage.d.cts +2 -2
- package/dist/HotStorage.d.mts +2 -2
- package/dist/HotStorage.mjs +2 -2
- package/dist/HotStorage.mjs.map +1 -1
- package/dist/Metrics.cjs +6 -6
- package/dist/Metrics.d.cts +21 -23
- package/dist/Metrics.d.cts.map +1 -1
- package/dist/Metrics.d.mts +21 -23
- package/dist/Metrics.d.mts.map +1 -1
- package/dist/Metrics.mjs +7 -7
- package/dist/Metrics.mjs.map +1 -1
- package/dist/MimicAuthService.cjs +1 -1
- package/dist/MimicAuthService.d.cts +2 -2
- package/dist/MimicAuthService.d.cts.map +1 -1
- package/dist/MimicAuthService.d.mts +2 -2
- package/dist/MimicAuthService.d.mts.map +1 -1
- package/dist/MimicAuthService.mjs +2 -2
- package/dist/MimicAuthService.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +38 -41
- package/dist/MimicClusterServerEngine.d.cts +1 -1
- package/dist/MimicClusterServerEngine.d.mts +1 -1
- package/dist/MimicClusterServerEngine.mjs +31 -34
- package/dist/MimicClusterServerEngine.mjs.map +1 -1
- package/dist/MimicServer.cjs +23 -23
- package/dist/MimicServer.d.cts +3 -3
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts +3 -3
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +22 -22
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +13 -13
- package/dist/MimicServerEngine.d.cts +2 -2
- package/dist/MimicServerEngine.d.mts +2 -2
- package/dist/MimicServerEngine.mjs +14 -14
- package/dist/MimicServerEngine.mjs.map +1 -1
- package/dist/PresenceManager.cjs +4 -4
- package/dist/PresenceManager.d.cts +2 -2
- package/dist/PresenceManager.d.mts +2 -2
- package/dist/PresenceManager.mjs +5 -5
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Types.d.cts +1 -1
- package/dist/Types.d.mts +1 -1
- package/dist/testing/ColdStorageTestSuite.cjs +3 -3
- package/dist/testing/ColdStorageTestSuite.mjs +3 -3
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -1
- package/dist/testing/HotStorageTestSuite.cjs +13 -13
- package/dist/testing/HotStorageTestSuite.mjs +13 -13
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -1
- package/dist/testing/StorageIntegrationTestSuite.cjs +3 -3
- package/dist/testing/StorageIntegrationTestSuite.mjs +3 -3
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -1
- package/dist/testing/types.d.cts +1 -1
- package/dist/testing/types.d.cts.map +1 -1
- package/dist/testing/types.d.mts +3 -3
- package/dist/testing/types.d.mts.map +1 -1
- package/package.json +17 -21
- package/src/ColdStorage.ts +4 -5
- package/src/DocumentInstance.ts +13 -13
- package/src/HotStorage.ts +3 -3
- package/src/Metrics.ts +22 -16
- package/src/MimicAuthService.ts +3 -3
- package/src/MimicClusterServerEngine.ts +35 -35
- package/src/MimicServer.ts +26 -30
- package/src/MimicServerEngine.ts +15 -15
- package/src/PresenceManager.ts +6 -6
- package/src/Types.ts +1 -1
- package/src/testing/ColdStorageTestSuite.ts +3 -3
- package/src/testing/HotStorageTestSuite.ts +17 -17
- package/src/testing/StorageIntegrationTestSuite.ts +3 -3
- package/.turbo/turbo-build.log +0 -154
- package/tests/ColdStorage.test.ts +0 -24
- package/tests/DocumentInstance.test.ts +0 -669
- package/tests/HotStorage.test.ts +0 -24
- package/tests/MimicAuthService.test.ts +0 -153
- package/tests/MimicClusterServerEngine.test.ts +0 -587
- package/tests/MimicServer.test.ts +0 -142
- package/tests/MimicServerEngine.test.ts +0 -547
- package/tests/PresenceManager.test.ts +0 -380
- package/tests/Protocol.test.ts +0 -190
- package/tests/StorageIntegration.test.ts +0 -259
- package/tsconfig.build.json +0 -24
- package/tsconfig.json +0 -8
- package/tsdown.config.ts +0 -18
- package/vitest.mts +0 -11
package/package.json
CHANGED
|
@@ -1,52 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidhash/mimic-effect",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/voidhashcom/
|
|
8
|
-
"directory": "packages/mimic-
|
|
7
|
+
"url": "https://github.com/voidhashcom/voidhash",
|
|
8
|
+
"directory": "packages/mimic-effect"
|
|
9
9
|
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
10
17
|
"main": "./src/index.ts",
|
|
11
18
|
"exports": {
|
|
12
19
|
".": {
|
|
13
|
-
"types": "./
|
|
20
|
+
"types": "./src/index.ts",
|
|
14
21
|
"import": "./dist/index.mjs",
|
|
15
22
|
"require": "./dist/index.cjs"
|
|
16
23
|
},
|
|
17
24
|
"./testing": {
|
|
18
|
-
"types": "./
|
|
25
|
+
"types": "./src/testing/index.ts",
|
|
19
26
|
"import": "./dist/testing/index.mjs",
|
|
20
27
|
"require": "./dist/testing/index.cjs"
|
|
21
28
|
}
|
|
22
29
|
},
|
|
23
30
|
"devDependencies": {
|
|
24
|
-
"@effect/vitest": "
|
|
31
|
+
"@effect/vitest": "4.0.0-beta.21",
|
|
25
32
|
"tsdown": "^0.18.2",
|
|
26
33
|
"typescript": "5.8.3",
|
|
27
34
|
"vite-tsconfig-paths": "^5.1.4",
|
|
28
35
|
"vitest": "^3.2.4",
|
|
29
|
-
"@voidhash/tsconfig": "1.0.0-beta.
|
|
36
|
+
"@voidhash/tsconfig": "1.0.0-beta.17"
|
|
30
37
|
},
|
|
31
38
|
"peerDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"@
|
|
34
|
-
"@effect/rpc": "^0.72.2",
|
|
35
|
-
"effect": "^3.19.12",
|
|
36
|
-
"@voidhash/mimic": "1.0.0-beta.15"
|
|
37
|
-
},
|
|
38
|
-
"peerDependenciesMeta": {
|
|
39
|
-
"@effect/cluster": {
|
|
40
|
-
"optional": true
|
|
41
|
-
},
|
|
42
|
-
"@effect/rpc": {
|
|
43
|
-
"optional": true
|
|
44
|
-
}
|
|
39
|
+
"effect": "4.0.0-beta.21",
|
|
40
|
+
"@voidhash/mimic": "1.0.0-beta.17"
|
|
45
41
|
},
|
|
46
42
|
"scripts": {
|
|
47
43
|
"build": "tsdown",
|
|
48
44
|
"lint": "biome check .",
|
|
49
|
-
"typecheck": "
|
|
45
|
+
"typecheck": "tsgo --noEmit",
|
|
50
46
|
"test": "vitest run -c vitest.mts"
|
|
51
47
|
}
|
|
52
48
|
}
|
package/src/ColdStorage.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Interface and implementations for document snapshot storage.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { Effect, HashMap, Layer, Ref, ServiceMap } from "effect";
|
|
7
7
|
import type { StoredDocument } from "./Types";
|
|
8
8
|
import { ColdStorageError } from "./Errors";
|
|
9
9
|
|
|
@@ -50,10 +50,9 @@ export interface ColdStorage {
|
|
|
50
50
|
/**
|
|
51
51
|
* Context tag for ColdStorage service
|
|
52
52
|
*/
|
|
53
|
-
export class ColdStorageTag extends
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
>() {}
|
|
53
|
+
export class ColdStorageTag extends ServiceMap.Service<ColdStorageTag, ColdStorage>()(
|
|
54
|
+
"@voidhash/mimic-effect/ColdStorage"
|
|
55
|
+
) {}
|
|
57
56
|
|
|
58
57
|
// =============================================================================
|
|
59
58
|
// Factory
|
package/src/DocumentInstance.ts
CHANGED
|
@@ -186,11 +186,11 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
186
186
|
|
|
187
187
|
// Track metrics
|
|
188
188
|
if (storedDoc) {
|
|
189
|
-
yield* Metric.
|
|
189
|
+
yield* Metric.update(Metrics.documentsRestored, 1);
|
|
190
190
|
} else {
|
|
191
|
-
yield* Metric.
|
|
191
|
+
yield* Metric.update(Metrics.documentsCreated, 1);
|
|
192
192
|
}
|
|
193
|
-
yield* Metric.
|
|
193
|
+
yield* Metric.update(Metrics.documentsActive, 1);
|
|
194
194
|
|
|
195
195
|
// ==========================================================================
|
|
196
196
|
// Instance Methods
|
|
@@ -252,7 +252,7 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
252
252
|
|
|
253
253
|
// Track snapshot metrics
|
|
254
254
|
const snapshotDuration = Date.now() - snapshotStartTime;
|
|
255
|
-
yield* Metric.
|
|
255
|
+
yield* Metric.update(Metrics.storageSnapshots, 1);
|
|
256
256
|
yield* Metric.update(Metrics.storageSnapshotLatency, snapshotDuration);
|
|
257
257
|
|
|
258
258
|
// Update tracking BEFORE truncate (for idempotency on retry)
|
|
@@ -261,7 +261,7 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
261
261
|
yield* Ref.set(transactionsSinceSnapshotRef, 0);
|
|
262
262
|
|
|
263
263
|
// Truncate WAL - non-fatal, will be retried on next snapshot
|
|
264
|
-
yield* Effect
|
|
264
|
+
yield* Effect["catch"](hotStorage.truncate(documentId, snapshotResult.version), (e) =>
|
|
265
265
|
Effect.logWarning("WAL truncate failed - will retry on next snapshot", {
|
|
266
266
|
documentId,
|
|
267
267
|
version: snapshotResult.version,
|
|
@@ -291,7 +291,7 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
291
291
|
const validation = document.validate(transaction);
|
|
292
292
|
|
|
293
293
|
if (!validation.valid) {
|
|
294
|
-
yield* Metric.
|
|
294
|
+
yield* Metric.update(Metrics.transactionsRejected, 1);
|
|
295
295
|
const latency = Date.now() - submitStartTime;
|
|
296
296
|
yield* Metric.update(Metrics.transactionsLatency, latency);
|
|
297
297
|
|
|
@@ -312,17 +312,17 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
312
312
|
// This ensures correct validation after truncation or restart
|
|
313
313
|
const snapshotVersion = yield* Ref.get(lastSnapshotVersionRef);
|
|
314
314
|
|
|
315
|
-
const appendResult = yield* Effect.
|
|
315
|
+
const appendResult = yield* Effect.result(
|
|
316
316
|
hotStorage.appendWithCheck(documentId, walEntry, validation.nextVersion, snapshotVersion)
|
|
317
317
|
);
|
|
318
318
|
|
|
319
|
-
if (appendResult._tag === "
|
|
319
|
+
if (appendResult._tag === "Failure") {
|
|
320
320
|
yield* Effect.logError("WAL append failed", {
|
|
321
321
|
documentId,
|
|
322
322
|
version: validation.nextVersion,
|
|
323
|
-
error: appendResult.
|
|
323
|
+
error: appendResult.failure,
|
|
324
324
|
});
|
|
325
|
-
yield* Metric.
|
|
325
|
+
yield* Metric.update(Metrics.walAppendFailures, 1);
|
|
326
326
|
|
|
327
327
|
const latency = Date.now() - submitStartTime;
|
|
328
328
|
yield* Metric.update(Metrics.transactionsLatency, latency);
|
|
@@ -339,8 +339,8 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
339
339
|
// Track metrics
|
|
340
340
|
const latency = Date.now() - submitStartTime;
|
|
341
341
|
yield* Metric.update(Metrics.transactionsLatency, latency);
|
|
342
|
-
yield* Metric.
|
|
343
|
-
yield* Metric.
|
|
342
|
+
yield* Metric.update(Metrics.transactionsProcessed, 1);
|
|
343
|
+
yield* Metric.update(Metrics.storageWalAppends, 1);
|
|
344
344
|
|
|
345
345
|
// Increment transaction count
|
|
346
346
|
yield* Ref.update(transactionsSinceSnapshotRef, (n) => n + 1);
|
|
@@ -425,7 +425,7 @@ const verifyWalContinuity = Effect.fn("document.wal.verify")(function* (
|
|
|
425
425
|
firstWalVersion,
|
|
426
426
|
expectedFirst,
|
|
427
427
|
});
|
|
428
|
-
yield* Metric.
|
|
428
|
+
yield* Metric.update(Metrics.storageVersionGaps, 1);
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
for (let i = 1; i < walEntries.length; i++) {
|
package/src/HotStorage.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Interface and implementations for Write-Ahead Log (WAL) storage.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { Effect, HashMap, Layer, Ref, ServiceMap } from "effect";
|
|
7
7
|
import type { WalEntry } from "./Types";
|
|
8
8
|
import { HotStorageError, WalVersionGapError } from "./Errors";
|
|
9
9
|
|
|
@@ -79,10 +79,10 @@ export interface HotStorage {
|
|
|
79
79
|
/**
|
|
80
80
|
* Context tag for HotStorage service
|
|
81
81
|
*/
|
|
82
|
-
export class HotStorageTag extends
|
|
82
|
+
export class HotStorageTag extends ServiceMap.Service<
|
|
83
83
|
HotStorageTag,
|
|
84
84
|
HotStorage
|
|
85
|
-
>() {}
|
|
85
|
+
>()("@voidhash/mimic-effect/HotStorage") {}
|
|
86
86
|
|
|
87
87
|
// =============================================================================
|
|
88
88
|
// Factory
|
package/src/Metrics.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Observability metrics using Effect's Metric API.
|
|
5
5
|
*/
|
|
6
|
-
import { Metric
|
|
6
|
+
import { Metric } from "effect";
|
|
7
7
|
|
|
8
8
|
// =============================================================================
|
|
9
9
|
// Connection Metrics
|
|
@@ -24,11 +24,13 @@ export const connectionsTotal = Metric.counter("mimic.connections.total");
|
|
|
24
24
|
*/
|
|
25
25
|
export const connectionsDuration = Metric.histogram(
|
|
26
26
|
"mimic.connections.duration_ms",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
{
|
|
28
|
+
boundaries: Metric.exponentialBoundaries({
|
|
29
|
+
start: 100,
|
|
30
|
+
factor: 2,
|
|
31
|
+
count: 15, // Up to ~3.2 million ms (~53 minutes)
|
|
32
|
+
}),
|
|
33
|
+
}
|
|
32
34
|
);
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -83,11 +85,13 @@ export const transactionsRejected = Metric.counter(
|
|
|
83
85
|
*/
|
|
84
86
|
export const transactionsLatency = Metric.histogram(
|
|
85
87
|
"mimic.transactions.latency_ms",
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
{
|
|
89
|
+
boundaries: Metric.exponentialBoundaries({
|
|
90
|
+
start: 0.1,
|
|
91
|
+
factor: 2,
|
|
92
|
+
count: 15, // Up to ~1638 ms
|
|
93
|
+
}),
|
|
94
|
+
}
|
|
91
95
|
);
|
|
92
96
|
|
|
93
97
|
// =============================================================================
|
|
@@ -109,11 +113,13 @@ export const storageIdleSnapshots = Metric.counter("mimic.storage.idle_snapshots
|
|
|
109
113
|
*/
|
|
110
114
|
export const storageSnapshotLatency = Metric.histogram(
|
|
111
115
|
"mimic.storage.snapshot_latency_ms",
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
{
|
|
117
|
+
boundaries: Metric.exponentialBoundaries({
|
|
118
|
+
start: 1,
|
|
119
|
+
factor: 2,
|
|
120
|
+
count: 12, // Up to ~4 seconds
|
|
121
|
+
}),
|
|
122
|
+
}
|
|
117
123
|
);
|
|
118
124
|
|
|
119
125
|
/**
|
package/src/MimicAuthService.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Authentication and authorization service interface and implementations.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { Effect, Layer, ServiceMap } from "effect";
|
|
7
7
|
import type { AuthContext, Permission } from "./Types";
|
|
8
8
|
import { AuthenticationError } from "./Errors";
|
|
9
9
|
|
|
@@ -43,9 +43,9 @@ export interface MimicAuthService {
|
|
|
43
43
|
/**
|
|
44
44
|
* Context tag for MimicAuthService
|
|
45
45
|
*/
|
|
46
|
-
export class MimicAuthServiceTag extends
|
|
46
|
+
export class MimicAuthServiceTag extends ServiceMap.Service<MimicAuthServiceTag, MimicAuthService>()(
|
|
47
47
|
"@voidhash/mimic-effect/MimicAuthService"
|
|
48
|
-
)
|
|
48
|
+
) {}
|
|
49
49
|
|
|
50
50
|
// =============================================================================
|
|
51
51
|
// Factory
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* This is an alternative to MimicServerEngine for distributed deployments.
|
|
8
8
|
*/
|
|
9
9
|
import {
|
|
10
|
-
Context,
|
|
11
10
|
Duration,
|
|
12
11
|
Effect,
|
|
13
12
|
HashMap,
|
|
@@ -17,10 +16,11 @@ import {
|
|
|
17
16
|
Ref,
|
|
18
17
|
Schedule,
|
|
19
18
|
Schema,
|
|
19
|
+
ServiceMap,
|
|
20
20
|
Stream,
|
|
21
21
|
} from "effect";
|
|
22
|
-
import { Entity, Sharding } from "
|
|
23
|
-
import { Rpc } from "
|
|
22
|
+
import { Entity, Sharding } from "effect/unstable/cluster";
|
|
23
|
+
import { Rpc } from "effect/unstable/rpc";
|
|
24
24
|
import { type Primitive, type Transaction } from "@voidhash/mimic";
|
|
25
25
|
import type {
|
|
26
26
|
MimicClusterServerEngineConfig,
|
|
@@ -59,13 +59,13 @@ const DEFAULT_SHARD_GROUP = "mimic-documents";
|
|
|
59
59
|
*/
|
|
60
60
|
const EncodedTransactionSchema = Schema.Struct({
|
|
61
61
|
id: Schema.String,
|
|
62
|
-
ops: Schema.Array(Schema.
|
|
62
|
+
ops: Schema.Array(Schema.Any),
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Schema for submit result
|
|
67
67
|
*/
|
|
68
|
-
const SubmitResultSchema = Schema.Union(
|
|
68
|
+
const SubmitResultSchema = Schema.Union([
|
|
69
69
|
Schema.Struct({
|
|
70
70
|
success: Schema.Literal(true),
|
|
71
71
|
version: Schema.Number,
|
|
@@ -73,14 +73,14 @@ const SubmitResultSchema = Schema.Union(
|
|
|
73
73
|
Schema.Struct({
|
|
74
74
|
success: Schema.Literal(false),
|
|
75
75
|
reason: Schema.String,
|
|
76
|
-
})
|
|
77
|
-
);
|
|
76
|
+
}),
|
|
77
|
+
]);
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* Schema for snapshot response
|
|
81
81
|
*/
|
|
82
82
|
const SnapshotResponseSchema = Schema.Struct({
|
|
83
|
-
state: Schema.
|
|
83
|
+
state: Schema.Any,
|
|
84
84
|
version: Schema.Number,
|
|
85
85
|
});
|
|
86
86
|
|
|
@@ -88,7 +88,7 @@ const SnapshotResponseSchema = Schema.Struct({
|
|
|
88
88
|
* Schema for presence entry
|
|
89
89
|
*/
|
|
90
90
|
const PresenceEntrySchema = Schema.Struct({
|
|
91
|
-
data: Schema.
|
|
91
|
+
data: Schema.Any,
|
|
92
92
|
userId: Schema.optional(Schema.String),
|
|
93
93
|
});
|
|
94
94
|
|
|
@@ -96,29 +96,29 @@ const PresenceEntrySchema = Schema.Struct({
|
|
|
96
96
|
* Schema for presence snapshot response
|
|
97
97
|
*/
|
|
98
98
|
const PresenceSnapshotResponseSchema = Schema.Struct({
|
|
99
|
-
presences: Schema.Record(
|
|
99
|
+
presences: Schema.Record(Schema.String, PresenceEntrySchema),
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Schema for presence event
|
|
104
104
|
*/
|
|
105
|
-
const PresenceEventSchema = Schema.Union(
|
|
105
|
+
const PresenceEventSchema = Schema.Union([
|
|
106
106
|
Schema.Struct({
|
|
107
107
|
type: Schema.Literal("presence_update"),
|
|
108
108
|
id: Schema.String,
|
|
109
|
-
data: Schema.
|
|
109
|
+
data: Schema.Any,
|
|
110
110
|
userId: Schema.optional(Schema.String),
|
|
111
111
|
}),
|
|
112
112
|
Schema.Struct({
|
|
113
113
|
type: Schema.Literal("presence_remove"),
|
|
114
114
|
id: Schema.String,
|
|
115
|
-
})
|
|
116
|
-
);
|
|
115
|
+
}),
|
|
116
|
+
]);
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Schema for server message (for broadcasts)
|
|
120
120
|
*/
|
|
121
|
-
const ServerMessageSchema = Schema.
|
|
121
|
+
const ServerMessageSchema = Schema.Any;
|
|
122
122
|
|
|
123
123
|
// =============================================================================
|
|
124
124
|
// Mimic Document Entity Definition
|
|
@@ -142,7 +142,7 @@ const MimicDocumentEntity = Entity.make("MimicDocument", [
|
|
|
142
142
|
|
|
143
143
|
// Get tree-like snapshot for rendering
|
|
144
144
|
Rpc.make("GetTreeSnapshot", {
|
|
145
|
-
success: Schema.
|
|
145
|
+
success: Schema.Any,
|
|
146
146
|
}),
|
|
147
147
|
|
|
148
148
|
// Touch document to prevent idle GC
|
|
@@ -191,9 +191,9 @@ interface EntityState<TSchema extends Primitive.AnyPrimitive> {
|
|
|
191
191
|
/**
|
|
192
192
|
* Context tag for cluster engine configuration
|
|
193
193
|
*/
|
|
194
|
-
class MimicClusterConfigTag extends
|
|
194
|
+
class MimicClusterConfigTag extends ServiceMap.Service<MimicClusterConfigTag, ResolvedClusterConfig<Primitive.AnyPrimitive>>()(
|
|
195
195
|
"@voidhash/mimic-effect/MimicClusterConfig"
|
|
196
|
-
)
|
|
196
|
+
) {}
|
|
197
197
|
|
|
198
198
|
// =============================================================================
|
|
199
199
|
// Resolve Configuration
|
|
@@ -206,18 +206,18 @@ const resolveClusterConfig = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
206
206
|
initial: config.initial,
|
|
207
207
|
presence: config.presence,
|
|
208
208
|
maxIdleTime: config.maxIdleTime
|
|
209
|
-
? Duration.
|
|
209
|
+
? Duration.fromInputUnsafe(config.maxIdleTime)
|
|
210
210
|
: DEFAULT_MAX_IDLE_TIME,
|
|
211
211
|
maxTransactionHistory:
|
|
212
212
|
config.maxTransactionHistory ?? DEFAULT_MAX_TRANSACTION_HISTORY,
|
|
213
213
|
snapshot: {
|
|
214
214
|
interval: config.snapshot?.interval
|
|
215
|
-
? Duration.
|
|
215
|
+
? Duration.fromInputUnsafe(config.snapshot.interval)
|
|
216
216
|
: DEFAULT_SNAPSHOT_INTERVAL,
|
|
217
217
|
transactionThreshold:
|
|
218
218
|
config.snapshot?.transactionThreshold ?? DEFAULT_SNAPSHOT_THRESHOLD,
|
|
219
219
|
idleTimeout: config.snapshot?.idleTimeout
|
|
220
|
-
? Duration.
|
|
220
|
+
? Duration.fromInputUnsafe(config.snapshot.idleTimeout)
|
|
221
221
|
: DEFAULT_SNAPSHOT_IDLE_TIMEOUT,
|
|
222
222
|
},
|
|
223
223
|
shardGroup: config.shardGroup ?? DEFAULT_SHARD_GROUP,
|
|
@@ -290,14 +290,14 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
290
290
|
yield* Effect.addFinalizer(() =>
|
|
291
291
|
Effect.fn("cluster.entity.finalize")(function* () {
|
|
292
292
|
// Best effort save - don't fail shutdown if storage is unavailable
|
|
293
|
-
yield* Effect
|
|
293
|
+
yield* Effect["catch"](instance.saveSnapshot(), (e) =>
|
|
294
294
|
Effect.logError("Failed to save snapshot during entity finalization", {
|
|
295
295
|
documentId,
|
|
296
296
|
error: e,
|
|
297
297
|
})
|
|
298
298
|
);
|
|
299
|
-
yield* Metric.
|
|
300
|
-
yield* Metric.
|
|
299
|
+
yield* Metric.update(Metrics.documentsActive, -1);
|
|
300
|
+
yield* Metric.update(Metrics.documentsEvicted, 1);
|
|
301
301
|
yield* Effect.logDebug("Entity finalized", { documentId });
|
|
302
302
|
})()
|
|
303
303
|
);
|
|
@@ -308,20 +308,20 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
308
308
|
const snapshotLoop = Effect.fn("cluster.entity.snapshot.loop")(function* () {
|
|
309
309
|
const needs = yield* instance.needsSnapshot();
|
|
310
310
|
if (needs) {
|
|
311
|
-
yield* Effect
|
|
311
|
+
yield* Effect["catch"](instance.saveSnapshot(), (e) =>
|
|
312
312
|
Effect.logWarning("Periodic snapshot failed in cluster entity", {
|
|
313
313
|
documentId,
|
|
314
314
|
error: e,
|
|
315
315
|
})
|
|
316
316
|
);
|
|
317
|
-
yield* Metric.
|
|
317
|
+
yield* Metric.update(Metrics.storageIdleSnapshots, 1);
|
|
318
318
|
}
|
|
319
319
|
});
|
|
320
320
|
|
|
321
321
|
// Run every idleTimeout
|
|
322
322
|
yield* snapshotLoop().pipe(
|
|
323
323
|
Effect.repeat(Schedule.spaced(config.snapshot.idleTimeout)),
|
|
324
|
-
Effect.
|
|
324
|
+
Effect.forkChild
|
|
325
325
|
);
|
|
326
326
|
}
|
|
327
327
|
|
|
@@ -335,7 +335,7 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
335
335
|
|
|
336
336
|
// Use DocumentInstance's submit method, catching storage errors
|
|
337
337
|
return yield* instance.submit(transaction).pipe(
|
|
338
|
-
Effect
|
|
338
|
+
Effect["catch"]((error) =>
|
|
339
339
|
Effect.succeed({
|
|
340
340
|
success: false as const,
|
|
341
341
|
reason: `Storage error: ${String(error)}`,
|
|
@@ -364,8 +364,8 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
364
364
|
presences: HashMap.set(s.presences, connectionId, entry),
|
|
365
365
|
}));
|
|
366
366
|
|
|
367
|
-
yield* Metric.
|
|
368
|
-
yield* Metric.
|
|
367
|
+
yield* Metric.update(Metrics.presenceUpdates, 1);
|
|
368
|
+
yield* Metric.update(Metrics.presenceActive, 1);
|
|
369
369
|
|
|
370
370
|
const state = yield* Ref.get(stateRef);
|
|
371
371
|
const event: PresenceEvent = {
|
|
@@ -392,7 +392,7 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
392
392
|
presences: HashMap.remove(s.presences, connectionId),
|
|
393
393
|
}));
|
|
394
394
|
|
|
395
|
-
yield* Metric.
|
|
395
|
+
yield* Metric.update(Metrics.presenceActive, -1);
|
|
396
396
|
|
|
397
397
|
const event: PresenceEvent = {
|
|
398
398
|
type: "presence_remove",
|
|
@@ -431,9 +431,9 @@ interface SubscriptionStore {
|
|
|
431
431
|
) => Effect.Effect<PubSub.PubSub<PresenceEvent>>;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
class SubscriptionStoreTag extends
|
|
434
|
+
class SubscriptionStoreTag extends ServiceMap.Service<SubscriptionStoreTag, SubscriptionStore>()(
|
|
435
435
|
"@voidhash/mimic-effect/SubscriptionStore"
|
|
436
|
-
)
|
|
436
|
+
) {}
|
|
437
437
|
|
|
438
438
|
const subscriptionStoreLayer = Layer.effect(
|
|
439
439
|
SubscriptionStoreTag,
|
|
@@ -554,7 +554,7 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
554
554
|
);
|
|
555
555
|
|
|
556
556
|
// Create the engine service
|
|
557
|
-
const engineLayer = Layer.
|
|
557
|
+
const engineLayer = Layer.effect(
|
|
558
558
|
MimicServerEngineTag,
|
|
559
559
|
Effect.gen(function* () {
|
|
560
560
|
// Get entity client maker
|
|
@@ -571,7 +571,7 @@ export const make = <TSchema extends Primitive.AnyPrimitive>(
|
|
|
571
571
|
const result = yield* client.Submit({
|
|
572
572
|
transaction: encodedTx as { id: string; ops: unknown[] },
|
|
573
573
|
}).pipe(
|
|
574
|
-
Effect
|
|
574
|
+
Effect["catch"]((error) =>
|
|
575
575
|
Effect.succeed({
|
|
576
576
|
success: false as const,
|
|
577
577
|
reason: `Cluster error: ${String(error)}`,
|