@smthrs/sync 0.0.0-stage → 1.0.0-rc.3
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/CHANGELOG.md +208 -0
- package/LICENSE +21 -0
- package/README.md +208 -2
- package/dist/cjs/BranchCommands.d.ts +228 -0
- package/dist/cjs/BranchCommands.d.ts.map +1 -0
- package/dist/cjs/BranchCommands.js +316 -0
- package/dist/cjs/BranchCommands.js.map +7 -0
- package/dist/cjs/BranchIds.d.ts +65 -0
- package/dist/cjs/BranchIds.d.ts.map +1 -0
- package/dist/cjs/BranchIds.js +53 -0
- package/dist/cjs/BranchIds.js.map +7 -0
- package/dist/cjs/BranchPresence.d.ts +195 -0
- package/dist/cjs/BranchPresence.d.ts.map +1 -0
- package/dist/cjs/BranchPresence.js +276 -0
- package/dist/cjs/BranchPresence.js.map +7 -0
- package/dist/cjs/BranchProjection.d.ts +179 -0
- package/dist/cjs/BranchProjection.d.ts.map +1 -0
- package/dist/cjs/BranchProjection.js +197 -0
- package/dist/cjs/BranchProjection.js.map +7 -0
- package/dist/cjs/BranchProtocol.d.ts +415 -0
- package/dist/cjs/BranchProtocol.d.ts.map +1 -0
- package/dist/cjs/BranchProtocol.js +172 -0
- package/dist/cjs/BranchProtocol.js.map +7 -0
- package/dist/cjs/BranchRpcs.d.ts +177 -0
- package/dist/cjs/BranchRpcs.d.ts.map +1 -0
- package/dist/cjs/BranchRpcs.js +85 -0
- package/dist/cjs/BranchRpcs.js.map +7 -0
- package/dist/cjs/BranchServer.d.ts +55 -0
- package/dist/cjs/BranchServer.d.ts.map +1 -0
- package/dist/cjs/BranchServer.js +147 -0
- package/dist/cjs/BranchServer.js.map +7 -0
- package/dist/cjs/BranchShare.d.ts +159 -0
- package/dist/cjs/BranchShare.d.ts.map +1 -0
- package/dist/cjs/BranchShare.js +147 -0
- package/dist/cjs/BranchShare.js.map +7 -0
- package/dist/cjs/RunCatalog.d.ts +207 -0
- package/dist/cjs/RunCatalog.d.ts.map +1 -0
- package/dist/cjs/RunCatalog.js +122 -0
- package/dist/cjs/RunCatalog.js.map +7 -0
- package/dist/cjs/SyncAuth.d.ts +70 -0
- package/dist/cjs/SyncAuth.d.ts.map +1 -0
- package/dist/cjs/SyncAuth.js +109 -0
- package/dist/cjs/SyncAuth.js.map +7 -0
- package/dist/cjs/SyncClient.d.ts +274 -0
- package/dist/cjs/SyncClient.d.ts.map +1 -0
- package/dist/cjs/SyncClient.js +474 -0
- package/dist/cjs/SyncClient.js.map +7 -0
- package/dist/cjs/SyncError.d.ts +107 -0
- package/dist/cjs/SyncError.d.ts.map +1 -0
- package/dist/cjs/SyncError.js +110 -0
- package/dist/cjs/SyncError.js.map +7 -0
- package/dist/cjs/SyncPrincipal.d.ts +100 -0
- package/dist/cjs/SyncPrincipal.d.ts.map +1 -0
- package/dist/cjs/SyncPrincipal.js +48 -0
- package/dist/cjs/SyncPrincipal.js.map +7 -0
- package/dist/cjs/SyncProtocol.d.ts +592 -0
- package/dist/cjs/SyncProtocol.d.ts.map +1 -0
- package/dist/cjs/SyncProtocol.js +165 -0
- package/dist/cjs/SyncProtocol.js.map +7 -0
- package/dist/cjs/SyncRpcs.d.ts +92 -0
- package/dist/cjs/SyncRpcs.d.ts.map +1 -0
- package/dist/cjs/SyncRpcs.js +35 -0
- package/dist/cjs/SyncRpcs.js.map +7 -0
- package/dist/cjs/SyncServer.d.ts +184 -0
- package/dist/cjs/SyncServer.d.ts.map +1 -0
- package/dist/cjs/SyncServer.js +513 -0
- package/dist/cjs/SyncServer.js.map +7 -0
- package/dist/cjs/WorkspaceShare.d.ts +174 -0
- package/dist/cjs/WorkspaceShare.d.ts.map +1 -0
- package/dist/cjs/WorkspaceShare.js +149 -0
- package/dist/cjs/WorkspaceShare.js.map +7 -0
- package/dist/cjs/index.d.ts +91 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +67 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Admission.d.ts +43 -0
- package/dist/cjs/internal/Admission.d.ts.map +1 -0
- package/dist/cjs/internal/Admission.js +90 -0
- package/dist/cjs/internal/Admission.js.map +7 -0
- package/dist/cjs/internal/CauseText.d.ts +55 -0
- package/dist/cjs/internal/CauseText.d.ts.map +1 -0
- package/dist/cjs/internal/CauseText.js +38 -0
- package/dist/cjs/internal/CauseText.js.map +7 -0
- package/dist/cjs/internal/JournalErrorCode.d.ts +23 -0
- package/dist/cjs/internal/JournalErrorCode.d.ts.map +1 -0
- package/dist/cjs/internal/JournalErrorCode.js +34 -0
- package/dist/cjs/internal/JournalErrorCode.js.map +7 -0
- package/dist/cjs/internal/PolicyOptions.d.ts +57 -0
- package/dist/cjs/internal/PolicyOptions.d.ts.map +1 -0
- package/dist/cjs/internal/PolicyOptions.js +62 -0
- package/dist/cjs/internal/PolicyOptions.js.map +7 -0
- package/dist/cjs/internal/ProtocolVersion.d.ts +17 -0
- package/dist/cjs/internal/ProtocolVersion.d.ts.map +1 -0
- package/dist/cjs/internal/ProtocolVersion.js +44 -0
- package/dist/cjs/internal/ProtocolVersion.js.map +7 -0
- package/dist/cjs/internal/ShareSigner.d.ts +173 -0
- package/dist/cjs/internal/ShareSigner.d.ts.map +1 -0
- package/dist/cjs/internal/ShareSigner.js +150 -0
- package/dist/cjs/internal/ShareSigner.js.map +7 -0
- package/dist/cjs/internal/SnapshotBoundary.d.ts +17 -0
- package/dist/cjs/internal/SnapshotBoundary.d.ts.map +1 -0
- package/dist/cjs/internal/SnapshotBoundary.js +97 -0
- package/dist/cjs/internal/SnapshotBoundary.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestSocket.d.ts +55 -0
- package/dist/cjs/test/TestSocket.d.ts.map +1 -0
- package/dist/cjs/test/TestSocket.js +145 -0
- package/dist/cjs/test/TestSocket.js.map +7 -0
- package/dist/cjs/test/TestSync.d.ts +54 -0
- package/dist/cjs/test/TestSync.d.ts.map +1 -0
- package/dist/cjs/test/TestSync.js +96 -0
- package/dist/cjs/test/TestSync.js.map +7 -0
- package/dist/esm/BranchCommands.d.ts +228 -0
- package/dist/esm/BranchCommands.d.ts.map +1 -0
- package/dist/esm/BranchCommands.js +496 -0
- package/dist/esm/BranchCommands.js.map +1 -0
- package/dist/esm/BranchIds.d.ts +65 -0
- package/dist/esm/BranchIds.d.ts.map +1 -0
- package/dist/esm/BranchIds.js +61 -0
- package/dist/esm/BranchIds.js.map +1 -0
- package/dist/esm/BranchPresence.d.ts +195 -0
- package/dist/esm/BranchPresence.d.ts.map +1 -0
- package/dist/esm/BranchPresence.js +361 -0
- package/dist/esm/BranchPresence.js.map +1 -0
- package/dist/esm/BranchProjection.d.ts +179 -0
- package/dist/esm/BranchProjection.d.ts.map +1 -0
- package/dist/esm/BranchProjection.js +269 -0
- package/dist/esm/BranchProjection.js.map +1 -0
- package/dist/esm/BranchProtocol.d.ts +415 -0
- package/dist/esm/BranchProtocol.d.ts.map +1 -0
- package/dist/esm/BranchProtocol.js +323 -0
- package/dist/esm/BranchProtocol.js.map +1 -0
- package/dist/esm/BranchRpcs.d.ts +177 -0
- package/dist/esm/BranchRpcs.d.ts.map +1 -0
- package/dist/esm/BranchRpcs.js +103 -0
- package/dist/esm/BranchRpcs.js.map +1 -0
- package/dist/esm/BranchServer.d.ts +55 -0
- package/dist/esm/BranchServer.d.ts.map +1 -0
- package/dist/esm/BranchServer.js +168 -0
- package/dist/esm/BranchServer.js.map +1 -0
- package/dist/esm/BranchShare.d.ts +159 -0
- package/dist/esm/BranchShare.d.ts.map +1 -0
- package/dist/esm/BranchShare.js +213 -0
- package/dist/esm/BranchShare.js.map +1 -0
- package/dist/esm/RunCatalog.d.ts +207 -0
- package/dist/esm/RunCatalog.d.ts.map +1 -0
- package/dist/esm/RunCatalog.js +196 -0
- package/dist/esm/RunCatalog.js.map +1 -0
- package/dist/esm/SyncAuth.d.ts +70 -0
- package/dist/esm/SyncAuth.d.ts.map +1 -0
- package/dist/esm/SyncAuth.js +119 -0
- package/dist/esm/SyncAuth.js.map +1 -0
- package/dist/esm/SyncClient.d.ts +274 -0
- package/dist/esm/SyncClient.d.ts.map +1 -0
- package/dist/esm/SyncClient.js +586 -0
- package/dist/esm/SyncClient.js.map +1 -0
- package/dist/esm/SyncError.d.ts +107 -0
- package/dist/esm/SyncError.d.ts.map +1 -0
- package/dist/esm/SyncError.js +133 -0
- package/dist/esm/SyncError.js.map +1 -0
- package/dist/esm/SyncPrincipal.d.ts +100 -0
- package/dist/esm/SyncPrincipal.d.ts.map +1 -0
- package/dist/esm/SyncPrincipal.js +58 -0
- package/dist/esm/SyncPrincipal.js.map +1 -0
- package/dist/esm/SyncProtocol.d.ts +592 -0
- package/dist/esm/SyncProtocol.d.ts.map +1 -0
- package/dist/esm/SyncProtocol.js +434 -0
- package/dist/esm/SyncProtocol.js.map +1 -0
- package/dist/esm/SyncRpcs.d.ts +92 -0
- package/dist/esm/SyncRpcs.d.ts.map +1 -0
- package/dist/esm/SyncRpcs.js +35 -0
- package/dist/esm/SyncRpcs.js.map +1 -0
- package/dist/esm/SyncServer.d.ts +184 -0
- package/dist/esm/SyncServer.d.ts.map +1 -0
- package/dist/esm/SyncServer.js +897 -0
- package/dist/esm/SyncServer.js.map +1 -0
- package/dist/esm/WorkspaceShare.d.ts +174 -0
- package/dist/esm/WorkspaceShare.d.ts.map +1 -0
- package/dist/esm/WorkspaceShare.js +208 -0
- package/dist/esm/WorkspaceShare.js.map +1 -0
- package/dist/esm/index.d.ts +91 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +91 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Admission.d.ts +43 -0
- package/dist/esm/internal/Admission.d.ts.map +1 -0
- package/dist/esm/internal/Admission.js +80 -0
- package/dist/esm/internal/Admission.js.map +1 -0
- package/dist/esm/internal/CauseText.d.ts +55 -0
- package/dist/esm/internal/CauseText.d.ts.map +1 -0
- package/dist/esm/internal/CauseText.js +67 -0
- package/dist/esm/internal/CauseText.js.map +1 -0
- package/dist/esm/internal/JournalErrorCode.d.ts +23 -0
- package/dist/esm/internal/JournalErrorCode.d.ts.map +1 -0
- package/dist/esm/internal/JournalErrorCode.js +44 -0
- package/dist/esm/internal/JournalErrorCode.js.map +1 -0
- package/dist/esm/internal/PolicyOptions.d.ts +57 -0
- package/dist/esm/internal/PolicyOptions.d.ts.map +1 -0
- package/dist/esm/internal/PolicyOptions.js +70 -0
- package/dist/esm/internal/PolicyOptions.js.map +1 -0
- package/dist/esm/internal/ProtocolVersion.d.ts +17 -0
- package/dist/esm/internal/ProtocolVersion.d.ts.map +1 -0
- package/dist/esm/internal/ProtocolVersion.js +24 -0
- package/dist/esm/internal/ProtocolVersion.js.map +1 -0
- package/dist/esm/internal/ShareSigner.d.ts +173 -0
- package/dist/esm/internal/ShareSigner.d.ts.map +1 -0
- package/dist/esm/internal/ShareSigner.js +219 -0
- package/dist/esm/internal/ShareSigner.js.map +1 -0
- package/dist/esm/internal/SnapshotBoundary.d.ts +17 -0
- package/dist/esm/internal/SnapshotBoundary.d.ts.map +1 -0
- package/dist/esm/internal/SnapshotBoundary.js +68 -0
- package/dist/esm/internal/SnapshotBoundary.js.map +1 -0
- package/dist/esm/test/TestSocket.d.ts +55 -0
- package/dist/esm/test/TestSocket.d.ts.map +1 -0
- package/dist/esm/test/TestSocket.js +128 -0
- package/dist/esm/test/TestSocket.js.map +1 -0
- package/dist/esm/test/TestSync.d.ts +54 -0
- package/dist/esm/test/TestSync.d.ts.map +1 -0
- package/dist/esm/test/TestSync.js +76 -0
- package/dist/esm/test/TestSync.js.map +1 -0
- package/docs/README.md +156 -0
- package/docs/api.md +328 -0
- package/docs/concepts/authorization.md +117 -0
- package/docs/concepts/branches.md +195 -0
- package/docs/concepts/compaction.md +144 -0
- package/docs/concepts/replay-then-follow.md +129 -0
- package/docs/concepts/scopes-and-cursors.md +135 -0
- package/docs/guides/authorize-a-connection.md +174 -0
- package/docs/guides/follow-a-run.md +174 -0
- package/docs/guides/handle-a-compacted-run.md +158 -0
- package/docs/guides/list-workspace-runs.md +120 -0
- package/docs/guides/run-a-long-soak.md +80 -0
- package/docs/guides/serve-the-read-path.md +146 -0
- package/docs/guides/test-a-follower.md +273 -0
- package/docs/installation.md +92 -0
- package/docs/protocol.md +183 -0
- package/docs/quickstart.md +159 -0
- package/docs/troubleshooting.md +234 -0
- package/package.json +304 -3
- package/src/BranchCommands.ts +684 -0
- package/src/BranchIds.ts +76 -0
- package/src/BranchPresence.ts +512 -0
- package/src/BranchProjection.ts +308 -0
- package/src/BranchProtocol.ts +411 -0
- package/src/BranchRpcs.ts +163 -0
- package/src/BranchServer.ts +202 -0
- package/src/BranchShare.ts +297 -0
- package/src/RunCatalog.ts +308 -0
- package/src/SyncAuth.ts +146 -0
- package/src/SyncClient.ts +902 -0
- package/src/SyncError.ts +147 -0
- package/src/SyncPrincipal.ts +115 -0
- package/src/SyncProtocol.ts +594 -0
- package/src/SyncRpcs.ts +40 -0
- package/src/SyncServer.ts +1184 -0
- package/src/WorkspaceShare.ts +292 -0
- package/src/index.ts +107 -0
- package/src/internal/Admission.ts +119 -0
- package/src/internal/CauseText.ts +74 -0
- package/src/internal/JournalErrorCode.ts +46 -0
- package/src/internal/PolicyOptions.ts +97 -0
- package/src/internal/ProtocolVersion.ts +28 -0
- package/src/internal/ShareSigner.ts +300 -0
- package/src/internal/SnapshotBoundary.ts +91 -0
- package/src/test/TestSocket.ts +192 -0
- package/src/test/TestSync.ts +113 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var TestSocket_exports = {};
|
|
30
|
+
__export(TestSocket_exports, {
|
|
31
|
+
makePair: () => makePair
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(TestSocket_exports);
|
|
34
|
+
var Deferred = __toESM(require("effect/Deferred"), 1);
|
|
35
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
36
|
+
var Latch = __toESM(require("effect/Latch"), 1);
|
|
37
|
+
var Queue = __toESM(require("effect/Queue"), 1);
|
|
38
|
+
var Scope = __toESM(require("effect/Scope"), 1);
|
|
39
|
+
var Socket = __toESM(require("effect/unstable/socket/Socket"), 1);
|
|
40
|
+
const queueCapacity = 16;
|
|
41
|
+
const transportError = () => new Socket.SocketError({
|
|
42
|
+
reason: new Socket.SocketReadError({ cause: new Error("test socket disconnected") })
|
|
43
|
+
});
|
|
44
|
+
const intersects = (leftFrom, leftTo, rightFrom, rightTo) => leftFrom <= rightTo && rightFrom <= leftTo;
|
|
45
|
+
const entriesRange = (bytes) => {
|
|
46
|
+
try {
|
|
47
|
+
const value = JSON.parse(new TextDecoder().decode(bytes));
|
|
48
|
+
return findEntriesRange(value);
|
|
49
|
+
} catch {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const findEntriesRange = (value) => {
|
|
54
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
55
|
+
const record = value;
|
|
56
|
+
if (record["_tag"] === "Entries" && typeof record["runId"] === "string" && typeof record["fromSeq"] === "number" && typeof record["toSeq"] === "number") {
|
|
57
|
+
return { runId: record["runId"], from: record["fromSeq"], to: record["toSeq"] };
|
|
58
|
+
}
|
|
59
|
+
for (const child of Object.values(record)) {
|
|
60
|
+
if (Array.isArray(child)) {
|
|
61
|
+
for (const element of child) {
|
|
62
|
+
const result = findEntriesRange(element);
|
|
63
|
+
if (result !== void 0) return result;
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const result = findEntriesRange(child);
|
|
67
|
+
if (result !== void 0) return result;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return void 0;
|
|
71
|
+
};
|
|
72
|
+
const makePair = () => Effect.gen(function* () {
|
|
73
|
+
const scope = yield* Effect.scope;
|
|
74
|
+
const clientIncoming = yield* Queue.bounded(queueCapacity);
|
|
75
|
+
const serverIncoming = yield* Queue.bounded(queueCapacity);
|
|
76
|
+
yield* Scope.addFinalizer(
|
|
77
|
+
scope,
|
|
78
|
+
Effect.all([Queue.shutdown(clientIncoming), Queue.shutdown(serverIncoming)], { discard: true })
|
|
79
|
+
);
|
|
80
|
+
const delivery = Latch.makeUnsafe(true);
|
|
81
|
+
const filters = [];
|
|
82
|
+
const offer = (queue, bytes) => Effect.suspend(() => {
|
|
83
|
+
let current = bytes;
|
|
84
|
+
for (const filter of filters) {
|
|
85
|
+
const result = filter(current);
|
|
86
|
+
if (typeof result === "boolean") {
|
|
87
|
+
if (result === false) return Effect.void;
|
|
88
|
+
} else {
|
|
89
|
+
current = result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return Queue.offer(queue, current).pipe(Effect.asVoid);
|
|
93
|
+
});
|
|
94
|
+
const socket = (incoming, outgoing) => {
|
|
95
|
+
const write = (chunk) => {
|
|
96
|
+
if (Socket.isCloseEvent(chunk)) return Effect.void;
|
|
97
|
+
const bytes = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
|
|
98
|
+
return offer(outgoing, bytes);
|
|
99
|
+
};
|
|
100
|
+
return Socket.make({
|
|
101
|
+
reader: Effect.gen(function* () {
|
|
102
|
+
const closed = yield* Deferred.make();
|
|
103
|
+
yield* Effect.addFinalizer(() => Deferred.fail(closed, transportError()));
|
|
104
|
+
return {
|
|
105
|
+
pull: Effect.raceFirst(
|
|
106
|
+
Effect.map(delivery.whenOpen(Queue.take(incoming)), (bytes) => [bytes]),
|
|
107
|
+
Deferred.await(closed)
|
|
108
|
+
),
|
|
109
|
+
upgrade: Socket.SocketUpgradeError.unsupported
|
|
110
|
+
};
|
|
111
|
+
}),
|
|
112
|
+
writer: Effect.succeed({
|
|
113
|
+
write,
|
|
114
|
+
writeAll: (chunks) => Effect.forEach(chunks, write, { discard: true })
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
const faults = {
|
|
119
|
+
installFilter: (filter) => {
|
|
120
|
+
filters.push(filter);
|
|
121
|
+
},
|
|
122
|
+
dropRange: (runId, from, to) => {
|
|
123
|
+
filters.push((bytes) => {
|
|
124
|
+
const range = entriesRange(bytes);
|
|
125
|
+
return range === void 0 || range.runId !== runId || !intersects(range.from, range.to, from, to);
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
stall: () => {
|
|
129
|
+
delivery.closeUnsafe();
|
|
130
|
+
},
|
|
131
|
+
resume: () => {
|
|
132
|
+
delivery.openUnsafe();
|
|
133
|
+
},
|
|
134
|
+
disconnect: () => Effect.andThen(
|
|
135
|
+
Queue.fail(clientIncoming, transportError()),
|
|
136
|
+
Queue.fail(serverIncoming, transportError())
|
|
137
|
+
).pipe(Effect.asVoid)
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
client: socket(clientIncoming, serverIncoming),
|
|
141
|
+
server: socket(serverIncoming, clientIncoming),
|
|
142
|
+
faults
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=TestSocket.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/test/TestSocket.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Deterministic in-memory socket transport for sync tests.\n *\n * @since 0.1.0\n */\n\nimport * as Deferred from \"effect/Deferred\"\nimport * as Effect from \"effect/Effect\"\nimport * as Latch from \"effect/Latch\"\nimport * as Queue from \"effect/Queue\"\nimport * as Scope from \"effect/Scope\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\n\n/**\n * A predicate or byte transformation installed between the two socket ends.\n * Returning `false` drops the frame.\n *\n * @category models\n * @since 0.1.0\n */\nexport type FrameFilter = (bytes: Uint8Array) => boolean | Uint8Array\n\n/**\n * Deterministic transport failures that can be injected into a test socket pair.\n *\n * `dropRange` recognizes the JSON representation of an `Entries` frame without\n * importing the sync protocol. Tests using another wire representation can use\n * `installFilter` directly.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface TestFaults {\n readonly installFilter: (filter: FrameFilter) => void\n readonly dropRange: (runId: string, from: number, to: number) => void\n readonly stall: () => void\n readonly resume: () => void\n readonly disconnect: () => Effect.Effect<void>\n}\n\n/**\n * The two endpoints and controls of an in-memory socket connection.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Pair {\n readonly client: Socket.Socket\n readonly server: Socket.Socket\n readonly faults: TestFaults\n}\n\nconst queueCapacity = 16\n\nconst transportError = () =>\n new Socket.SocketError({\n reason: new Socket.SocketReadError({ cause: new Error(\"test socket disconnected\") })\n })\n\nconst intersects = (leftFrom: number, leftTo: number, rightFrom: number, rightTo: number) =>\n leftFrom <= rightTo && rightFrom <= leftTo\n\nconst entriesRange = (\n bytes: Uint8Array\n): { readonly runId: string; readonly from: number; readonly to: number } | undefined => {\n try {\n const value: unknown = JSON.parse(new TextDecoder().decode(bytes))\n return findEntriesRange(value)\n } catch {\n return undefined\n }\n}\n\nconst findEntriesRange = (\n value: unknown\n): { readonly runId: string; readonly from: number; readonly to: number } | undefined => {\n if (typeof value !== \"object\" || value === null) return undefined\n const record = value as Readonly<Record<string, unknown>>\n if (\n record[\"_tag\"] === \"Entries\" &&\n typeof record[\"runId\"] === \"string\" &&\n typeof record[\"fromSeq\"] === \"number\" &&\n typeof record[\"toSeq\"] === \"number\"\n ) {\n return { runId: record[\"runId\"], from: record[\"fromSeq\"], to: record[\"toSeq\"] }\n }\n for (const child of Object.values(record)) {\n if (Array.isArray(child)) {\n for (const element of child) {\n const result = findEntriesRange(element)\n if (result !== undefined) return result\n }\n } else {\n const result = findEntriesRange(child)\n if (result !== undefined) return result\n }\n }\n return undefined\n}\n\n/**\n * Creates a scoped, bounded, bidirectional in-memory socket connection.\n *\n * Socket runs wait on queue reads and are therefore interrupted with their\n * enclosing scope; no host abort signal or timer is involved.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makePair = (): Effect.Effect<Pair, never, Scope.Scope> =>\n Effect.gen(function*() {\n const scope = yield* Effect.scope\n const clientIncoming = yield* Queue.bounded<Uint8Array, Socket.SocketError>(queueCapacity)\n const serverIncoming = yield* Queue.bounded<Uint8Array, Socket.SocketError>(queueCapacity)\n yield* Scope.addFinalizer(\n scope,\n Effect.all([Queue.shutdown(clientIncoming), Queue.shutdown(serverIncoming)], { discard: true })\n )\n const delivery = Latch.makeUnsafe(true)\n const filters: Array<FrameFilter> = []\n\n const offer = (queue: Queue.Queue<Uint8Array, Socket.SocketError>, bytes: Uint8Array) =>\n Effect.suspend(() => {\n let current = bytes\n for (const filter of filters) {\n const result = filter(current)\n if (typeof result === \"boolean\") {\n if (result === false) return Effect.void\n } else {\n current = result\n }\n }\n return Queue.offer(queue, current).pipe(Effect.asVoid)\n })\n\n const socket = (\n incoming: Queue.Queue<Uint8Array, Socket.SocketError>,\n outgoing: Queue.Queue<Uint8Array, Socket.SocketError>\n ) => {\n const write: Socket.Writer[\"write\"] = (chunk) => {\n if (Socket.isCloseEvent(chunk)) return Effect.void\n const bytes = typeof chunk === \"string\" ? new TextEncoder().encode(chunk) : chunk\n return offer(outgoing, bytes)\n }\n return Socket.make({\n reader: Effect.gen(function*() {\n const closed = yield* Deferred.make<never, Socket.SocketError>()\n yield* Effect.addFinalizer(() => Deferred.fail(closed, transportError()))\n return {\n pull: Effect.raceFirst(\n Effect.map(delivery.whenOpen(Queue.take(incoming)), (bytes) => [bytes] as const),\n Deferred.await(closed)\n ),\n upgrade: Socket.SocketUpgradeError.unsupported\n }\n }),\n writer: Effect.succeed({\n write,\n writeAll: (chunks) => Effect.forEach(chunks, write, { discard: true })\n })\n })\n }\n\n const faults: TestFaults = {\n installFilter: (filter) => {\n filters.push(filter)\n },\n dropRange: (runId, from, to) => {\n filters.push((bytes) => {\n const range = entriesRange(bytes)\n return range === undefined || range.runId !== runId || !intersects(range.from, range.to, from, to)\n })\n },\n stall: () => {\n delivery.closeUnsafe()\n },\n resume: () => {\n delivery.openUnsafe()\n },\n disconnect: () =>\n Effect.andThen(\n Queue.fail(clientIncoming, transportError()),\n Queue.fail(serverIncoming, transportError())\n ).pipe(Effect.asVoid)\n }\n\n return {\n client: socket(clientIncoming, serverIncoming),\n server: socket(serverIncoming, clientIncoming),\n faults\n }\n })\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process sync service and RPC transport wiring for tests.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as RunCatalog from "../RunCatalog.ts";
|
|
9
|
+
import * as SyncClient from "../SyncClient.ts";
|
|
10
|
+
import * as SyncRpcs from "../SyncRpcs.ts";
|
|
11
|
+
import * as SyncServer from "../SyncServer.ts";
|
|
12
|
+
import type { Pair } from "./TestSocket.ts";
|
|
13
|
+
/**
|
|
14
|
+
* An authentication middleware that trusts every connection as the workspace
|
|
15
|
+
* owner. Test-only by construction: replication-mechanics suites use it so
|
|
16
|
+
* they exercise paging and streaming without provisioning capabilities,
|
|
17
|
+
* while the authentication suites use the production `SyncAuth.layer`.
|
|
18
|
+
*
|
|
19
|
+
* Never compose it into a served transport: every network client would read
|
|
20
|
+
* every workspace run as the owner. The name says so at the call site.
|
|
21
|
+
*
|
|
22
|
+
* @category layers
|
|
23
|
+
* @since 1.0.0-rc.1
|
|
24
|
+
*/
|
|
25
|
+
export declare const layerTrustAllAsOwner: Layer.Layer<SyncRpcs.SyncAuth, never, never>;
|
|
26
|
+
/**
|
|
27
|
+
* Provides the durable in-memory journal, a mutable empty run catalog, the
|
|
28
|
+
* owner-trusting {@link layerTrustAllAsOwner} middleware, and the production
|
|
29
|
+
* sync server.
|
|
30
|
+
*
|
|
31
|
+
* Tests that need runs in the workspace may register them through their own
|
|
32
|
+
* `RunCatalog.makeMemory` fixture before providing a more specific layer.
|
|
33
|
+
*
|
|
34
|
+
* @category layers
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export declare const layerTest: Layer.Layer<import("@smthrs/journal/Journal").Journal | SyncRpcs.SyncAuth | RunCatalog.RunCatalog | SyncServer.SyncServer, import("effect/unstable/sql/SqlError").SqlError | import("@smthrs/journal/Journal").JournalError | import("effect/unstable/sql/Migrator").MigrationError | import("../SyncError.ts").SyncError, never>;
|
|
38
|
+
/**
|
|
39
|
+
* Provides no-op sync dependencies for consumers that only require the ports.
|
|
40
|
+
*
|
|
41
|
+
* @category layers
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
*/
|
|
44
|
+
export declare const layerNoop: Layer.Layer<SyncRpcs.SyncAuth | RunCatalog.RunCatalog | SyncClient.SyncClient | SyncServer.SyncServer, never, never>;
|
|
45
|
+
/**
|
|
46
|
+
* Starts an RPC server over the pair's server endpoint and returns a sync
|
|
47
|
+
* client over the client endpoint. JSON serialization is deliberately used so
|
|
48
|
+
* frame faults can inspect test traffic without coupling to sync schemas.
|
|
49
|
+
*
|
|
50
|
+
* @category constructors
|
|
51
|
+
* @since 0.1.0
|
|
52
|
+
*/
|
|
53
|
+
export declare const connect: (pair: Pair) => Effect.Effect<SyncClient.Service, never, import("effect/Scope").Scope | SyncRpcs.SyncAuth | SyncServer.SyncServer>;
|
|
54
|
+
//# sourceMappingURL=TestSync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestSync.d.ts","sourceRoot":"","sources":["../../../src/test/TestSync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAE9C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAI3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,8CAEhC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,mUAQrB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,sHAKrB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,IAAI,uHA8B9B,CAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var TestSync_exports = {};
|
|
30
|
+
__export(TestSync_exports, {
|
|
31
|
+
connect: () => connect,
|
|
32
|
+
layerNoop: () => layerNoop,
|
|
33
|
+
layerTest: () => layerTest,
|
|
34
|
+
layerTrustAllAsOwner: () => layerTrustAllAsOwner
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(TestSync_exports);
|
|
37
|
+
var TestJournal = __toESM(require("@smthrs/journal/test/TestJournal"), 1);
|
|
38
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
39
|
+
var Layer = __toESM(require("effect/Layer"), 1);
|
|
40
|
+
var NetAddress = __toESM(require("effect/unstable/net/NetAddress"), 1);
|
|
41
|
+
var RpcClient = __toESM(require("effect/unstable/rpc/RpcClient"), 1);
|
|
42
|
+
var RpcSerialization = __toESM(require("effect/unstable/rpc/RpcSerialization"), 1);
|
|
43
|
+
var RpcServer = __toESM(require("effect/unstable/rpc/RpcServer"), 1);
|
|
44
|
+
var Socket = __toESM(require("effect/unstable/socket/Socket"), 1);
|
|
45
|
+
var SocketServer = __toESM(require("effect/unstable/socket/SocketServer"), 1);
|
|
46
|
+
var RunCatalog = __toESM(require("../RunCatalog.js"), 1);
|
|
47
|
+
var SyncClient = __toESM(require("../SyncClient.js"), 1);
|
|
48
|
+
var SyncPrincipal = __toESM(require("../SyncPrincipal.js"), 1);
|
|
49
|
+
var SyncRpcs = __toESM(require("../SyncRpcs.js"), 1);
|
|
50
|
+
var SyncServer = __toESM(require("../SyncServer.js"), 1);
|
|
51
|
+
const layerNoopAuth = Layer.succeed(SyncRpcs.SyncAuth)((effect) => effect);
|
|
52
|
+
const layerTrustAllAsOwner = Layer.succeed(SyncRpcs.SyncAuth)(
|
|
53
|
+
(effect) => Effect.provideService(effect, SyncPrincipal.SyncPrincipal, SyncPrincipal.workspace("test-workspace-owner"))
|
|
54
|
+
);
|
|
55
|
+
const layerTest = SyncServer.layer.pipe(
|
|
56
|
+
Layer.provideMerge(
|
|
57
|
+
Layer.mergeAll(
|
|
58
|
+
TestJournal.layer(),
|
|
59
|
+
RunCatalog.layerMemory(),
|
|
60
|
+
layerTrustAllAsOwner
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
const layerNoop = Layer.mergeAll(
|
|
65
|
+
SyncClient.layerNoop(),
|
|
66
|
+
SyncServer.layerNoop,
|
|
67
|
+
RunCatalog.layerNoop,
|
|
68
|
+
layerNoopAuth
|
|
69
|
+
);
|
|
70
|
+
const connect = (pair) => Effect.gen(function* () {
|
|
71
|
+
const sync = yield* SyncServer.SyncServer;
|
|
72
|
+
const handlers = SyncServer.layerHandlers.pipe(
|
|
73
|
+
Layer.provide(Layer.succeed(SyncServer.SyncServer, sync))
|
|
74
|
+
);
|
|
75
|
+
const serverProtocol = yield* RpcServer.makeProtocolSocketServer.pipe(
|
|
76
|
+
Effect.provideService(SocketServer.SocketServer, {
|
|
77
|
+
address: NetAddress.inetAddressFromIpStringUnsafe("127.0.0.1", 0),
|
|
78
|
+
run: (handler) => handler(pair.server).pipe(Effect.orDie, Effect.andThen(Effect.never))
|
|
79
|
+
}),
|
|
80
|
+
Effect.provide(RpcSerialization.layerJson)
|
|
81
|
+
);
|
|
82
|
+
yield* RpcServer.make(SyncRpcs.SyncRpcs, { disableFatalDefects: true }).pipe(
|
|
83
|
+
Effect.provideService(RpcServer.Protocol, serverProtocol),
|
|
84
|
+
Effect.provide(handlers),
|
|
85
|
+
Effect.forkScoped
|
|
86
|
+
);
|
|
87
|
+
const protocol = yield* RpcClient.makeProtocolSocket().pipe(
|
|
88
|
+
Effect.provideService(Socket.Socket, pair.client),
|
|
89
|
+
Effect.provide(RpcSerialization.layerJson)
|
|
90
|
+
);
|
|
91
|
+
const client = yield* RpcClient.make(SyncRpcs.SyncRpcs).pipe(
|
|
92
|
+
Effect.provideService(RpcClient.Protocol, protocol)
|
|
93
|
+
);
|
|
94
|
+
return yield* SyncClient.make({ client });
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=TestSync.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/test/TestSync.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * In-process sync service and RPC transport wiring for tests.\n *\n * @since 0.1.0\n */\n\nimport * as TestJournal from \"@smthrs/journal/test/TestJournal\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as NetAddress from \"effect/unstable/net/NetAddress\"\nimport * as RpcClient from \"effect/unstable/rpc/RpcClient\"\nimport * as RpcSerialization from \"effect/unstable/rpc/RpcSerialization\"\nimport * as RpcServer from \"effect/unstable/rpc/RpcServer\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport * as SocketServer from \"effect/unstable/socket/SocketServer\"\nimport * as RunCatalog from \"../RunCatalog.ts\"\nimport * as SyncClient from \"../SyncClient.ts\"\nimport * as SyncPrincipal from \"../SyncPrincipal.ts\"\nimport * as SyncRpcs from \"../SyncRpcs.ts\"\nimport * as SyncServer from \"../SyncServer.ts\"\nimport type { Pair } from \"./TestSocket.ts\"\n\nconst layerNoopAuth = Layer.succeed(SyncRpcs.SyncAuth)((effect) => effect)\n\n/**\n * An authentication middleware that trusts every connection as the workspace\n * owner. Test-only by construction: replication-mechanics suites use it so\n * they exercise paging and streaming without provisioning capabilities,\n * while the authentication suites use the production `SyncAuth.layer`.\n *\n * Never compose it into a served transport: every network client would read\n * every workspace run as the owner. The name says so at the call site.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const layerTrustAllAsOwner = Layer.succeed(SyncRpcs.SyncAuth)((effect) =>\n Effect.provideService(effect, SyncPrincipal.SyncPrincipal, SyncPrincipal.workspace(\"test-workspace-owner\"))\n)\n\n/**\n * Provides the durable in-memory journal, a mutable empty run catalog, the\n * owner-trusting {@link layerTrustAllAsOwner} middleware, and the production\n * sync server.\n *\n * Tests that need runs in the workspace may register them through their own\n * `RunCatalog.makeMemory` fixture before providing a more specific layer.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerTest = SyncServer.layer.pipe(\n Layer.provideMerge(\n Layer.mergeAll(\n TestJournal.layer(),\n RunCatalog.layerMemory(),\n layerTrustAllAsOwner\n )\n )\n)\n\n/**\n * Provides no-op sync dependencies for consumers that only require the ports.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop = Layer.mergeAll(\n SyncClient.layerNoop(),\n SyncServer.layerNoop,\n RunCatalog.layerNoop,\n layerNoopAuth\n)\n\n/**\n * Starts an RPC server over the pair's server endpoint and returns a sync\n * client over the client endpoint. JSON serialization is deliberately used so\n * frame faults can inspect test traffic without coupling to sync schemas.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const connect = (pair: Pair) =>\n Effect.gen(function*() {\n const sync = yield* SyncServer.SyncServer\n const handlers = SyncServer.layerHandlers.pipe(\n Layer.provide(Layer.succeed(SyncServer.SyncServer, sync))\n )\n // Use the production schema-aware protocol. JSON.stringify on the raw\n // makeNoSerialization responses happened to encode successes, but not\n // Effect's Cause representation: typed RPC failures never reached clients.\n const serverProtocol = yield* RpcServer.makeProtocolSocketServer.pipe(\n Effect.provideService(SocketServer.SocketServer, {\n address: NetAddress.inetAddressFromIpStringUnsafe(\"127.0.0.1\", 0),\n run: (handler) => handler(pair.server).pipe(Effect.orDie, Effect.andThen(Effect.never))\n }),\n Effect.provide(RpcSerialization.layerJson)\n )\n yield* RpcServer.make(SyncRpcs.SyncRpcs, { disableFatalDefects: true }).pipe(\n Effect.provideService(RpcServer.Protocol, serverProtocol),\n Effect.provide(handlers),\n Effect.forkScoped\n )\n\n const protocol = yield* RpcClient.makeProtocolSocket().pipe(\n Effect.provideService(Socket.Socket, pair.client),\n Effect.provide(RpcSerialization.layerJson)\n )\n const client = yield* RpcClient.make(SyncRpcs.SyncRpcs).pipe(\n Effect.provideService(RpcClient.Protocol, protocol)\n )\n return yield* SyncClient.make({ client })\n })\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,kBAA6B;AAC7B,aAAwB;AACxB,YAAuB;AACvB,iBAA4B;AAC5B,gBAA2B;AAC3B,uBAAkC;AAClC,gBAA2B;AAC3B,aAAwB;AACxB,mBAA8B;AAC9B,iBAA4B;AAC5B,iBAA4B;AAC5B,oBAA+B;AAC/B,eAA0B;AAC1B,iBAA4B;AAG5B,MAAM,gBAAgB,MAAM,QAAQ,SAAS,QAAQ,EAAE,CAAC,WAAW,MAAM;AAclE,MAAM,uBAAuB,MAAM,QAAQ,SAAS,QAAQ;AAAA,EAAE,CAAC,WACpE,OAAO,eAAe,QAAQ,cAAc,eAAe,cAAc,UAAU,sBAAsB,CAAC;AAC5G;AAaO,MAAM,YAAY,WAAW,MAAM;AAAA,EACxC,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,YAAY,MAAM;AAAA,MAClB,WAAW,YAAY;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAQO,MAAM,YAAY,MAAM;AAAA,EAC7B,WAAW,UAAU;AAAA,EACrB,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AACF;AAUO,MAAM,UAAU,CAAC,SACtB,OAAO,IAAI,aAAY;AACrB,QAAM,OAAO,OAAO,WAAW;AAC/B,QAAM,WAAW,WAAW,cAAc;AAAA,IACxC,MAAM,QAAQ,MAAM,QAAQ,WAAW,YAAY,IAAI,CAAC;AAAA,EAC1D;AAIA,QAAM,iBAAiB,OAAO,UAAU,yBAAyB;AAAA,IAC/D,OAAO,eAAe,aAAa,cAAc;AAAA,MAC/C,SAAS,WAAW,8BAA8B,aAAa,CAAC;AAAA,MAChE,KAAK,CAAC,YAAY,QAAQ,KAAK,MAAM,EAAE,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC;AAAA,IACxF,CAAC;AAAA,IACD,OAAO,QAAQ,iBAAiB,SAAS;AAAA,EAC3C;AACA,SAAO,UAAU,KAAK,SAAS,UAAU,EAAE,qBAAqB,KAAK,CAAC,EAAE;AAAA,IACtE,OAAO,eAAe,UAAU,UAAU,cAAc;AAAA,IACxD,OAAO,QAAQ,QAAQ;AAAA,IACvB,OAAO;AAAA,EACT;AAEA,QAAM,WAAW,OAAO,UAAU,mBAAmB,EAAE;AAAA,IACrD,OAAO,eAAe,OAAO,QAAQ,KAAK,MAAM;AAAA,IAChD,OAAO,QAAQ,iBAAiB,SAAS;AAAA,EAC3C;AACA,QAAM,SAAS,OAAO,UAAU,KAAK,SAAS,QAAQ,EAAE;AAAA,IACtD,OAAO,eAAe,UAAU,UAAU,QAAQ;AAAA,EACpD;AACA,SAAO,OAAO,WAAW,KAAK,EAAE,OAAO,CAAC;AAC1C,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idempotent admission of collaborative commands onto a branch document.
|
|
3
|
+
*
|
|
4
|
+
* Three different things look identical from the server's side — an optimistic
|
|
5
|
+
* client re-sending after a timeout, a reconnecting client flushing its
|
|
6
|
+
* outbox, and two people pressing the same button at once — and all three must
|
|
7
|
+
* produce exactly one durable command. The client-minted `commandId` is the
|
|
8
|
+
* idempotency key for all three.
|
|
9
|
+
*
|
|
10
|
+
* The exactly-once constraint is durable, not process-local: every append
|
|
11
|
+
* carries the producer identity `(branch run, commandSourceId(commandId),
|
|
12
|
+
* commandSourceSeq)`, which the journal enforces inside its own write
|
|
13
|
+
* transaction. Two independently constructed servers that race the same
|
|
14
|
+
* command therefore collide in the journal — one appends, the other receives
|
|
15
|
+
* a duplicate receipt or an idempotency conflict and resolves the canonical
|
|
16
|
+
* sequence by replaying the branch (audit finding F-14). The in-memory
|
|
17
|
+
* ledger, permit, and replay cursor are a fast path only: they answer known
|
|
18
|
+
* duplicates without a journal write and keep a restarted server from
|
|
19
|
+
* re-executing history, but correctness never depends on them.
|
|
20
|
+
*
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
*/
|
|
23
|
+
import { Journal } from "@smthrs/journal";
|
|
24
|
+
import * as Context from "effect/Context";
|
|
25
|
+
import * as Effect from "effect/Effect";
|
|
26
|
+
import * as Layer from "effect/Layer";
|
|
27
|
+
import type * as Scope from "effect/Scope";
|
|
28
|
+
import { type BranchId, type CommandId, CommandReceipt, CommandSubmission, SubmitRequest } from "./BranchProtocol.ts";
|
|
29
|
+
import * as BranchShare from "./BranchShare.ts";
|
|
30
|
+
import { SyncError } from "./SyncError.ts";
|
|
31
|
+
/**
|
|
32
|
+
* Branch command admission operations.
|
|
33
|
+
*
|
|
34
|
+
* @category models
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export interface Service {
|
|
38
|
+
readonly submit: (request: SubmitRequest) => Effect.Effect<CommandReceipt, SyncError>;
|
|
39
|
+
}
|
|
40
|
+
declare const BranchCommands_base: Context.ServiceClass<BranchCommands, "@smthrs/sync/BranchCommands", Service>;
|
|
41
|
+
/**
|
|
42
|
+
* The branch command ledger.
|
|
43
|
+
*
|
|
44
|
+
* @category services
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
*/
|
|
47
|
+
export declare class BranchCommands extends BranchCommands_base {
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Constructs a command ledger that admits nothing.
|
|
51
|
+
*
|
|
52
|
+
* @category constructors
|
|
53
|
+
* @since 0.1.0
|
|
54
|
+
*/
|
|
55
|
+
export declare const makeNoop: (overrides?: Partial<Service>) => Service;
|
|
56
|
+
/**
|
|
57
|
+
* Provides a command ledger that admits nothing.
|
|
58
|
+
*
|
|
59
|
+
* @category layers
|
|
60
|
+
* @since 0.1.0
|
|
61
|
+
*/
|
|
62
|
+
export declare const layerNoop: Layer.Layer<BranchCommands>;
|
|
63
|
+
/**
|
|
64
|
+
* Receipts one branch keeps in memory before the oldest are evicted.
|
|
65
|
+
*
|
|
66
|
+
* The ledger is a fast path: it answers a known duplicate without a journal
|
|
67
|
+
* write, and losing an entry costs a round trip, never correctness, because
|
|
68
|
+
* the journal's own producer identity is the durable exactly-once constraint
|
|
69
|
+
* and returns a `Duplicate` receipt for a command already admitted. Retaining
|
|
70
|
+
* one receipt per command forever meant a process serving a hundred branches
|
|
71
|
+
* of a hundred thousand commands held ten million of them, so the fast path
|
|
72
|
+
* is bounded and the durable path is what makes it safe to bound.
|
|
73
|
+
*
|
|
74
|
+
* @category constants
|
|
75
|
+
* @since 1.0.0-rc.0
|
|
76
|
+
*/
|
|
77
|
+
export declare const defaultLedgerCapacity = 4096;
|
|
78
|
+
/**
|
|
79
|
+
* Inclusive UTF-8 JSON submission ceiling. The separate sync entry ceiling
|
|
80
|
+
* leaves room for the durable envelope around every admitted command.
|
|
81
|
+
*
|
|
82
|
+
* @category constants
|
|
83
|
+
* @since 1.0.0-rc.0
|
|
84
|
+
*/
|
|
85
|
+
export declare const defaultMaxCommandBytes: number;
|
|
86
|
+
/**
|
|
87
|
+
* Milliseconds a branch may have no active submissions before its in-memory state is
|
|
88
|
+
* dropped.
|
|
89
|
+
*
|
|
90
|
+
* The receipt ledger is bounded per branch; this bounds the branches. A
|
|
91
|
+
* dropped branch re-hydrates on its next submission exactly as it does after
|
|
92
|
+
* a restart, so eviction costs a journal read, never correctness. The default
|
|
93
|
+
* is the longest lifetime a branch capability may carry, so a branch is only
|
|
94
|
+
* dropped once every capability minted at its last touch can have expired.
|
|
95
|
+
*
|
|
96
|
+
* @category constants
|
|
97
|
+
* @since 1.0.0-rc.0
|
|
98
|
+
*/
|
|
99
|
+
export declare const defaultBranchIdleMs: number;
|
|
100
|
+
/**
|
|
101
|
+
* Entries one branch's first-touch hydration reads before it stops.
|
|
102
|
+
*
|
|
103
|
+
* Hydration is the only full history read that ever sat on the WRITE path: the
|
|
104
|
+
* first submission a process makes to a branch paged that branch's whole
|
|
105
|
+
* durable log, 256 entries at a time, while holding the branch's admission
|
|
106
|
+
* permit, before its own append was attempted. A hundred-thousand-entry branch
|
|
107
|
+
* therefore charged its next writer four hundred journal reads for a fast path
|
|
108
|
+
* that keeps four thousand receipts.
|
|
109
|
+
*
|
|
110
|
+
* The budget defaults to {@link defaultLedgerCapacity} because hydration
|
|
111
|
+
* cannot seed more receipts than the ledger holds: everything a longer walk
|
|
112
|
+
* records displaces something the same walk already recorded. What the extra
|
|
113
|
+
* reads buy is a FRESHER selection — the tail of history rather than its head
|
|
114
|
+
* — and the price is an unbounded wait on somebody's write. This trades that
|
|
115
|
+
* back: the walk stops, the cursor stays where it stopped, and a later replay
|
|
116
|
+
* continues forward from there.
|
|
117
|
+
*
|
|
118
|
+
* A branch longer than the budget starts with a partly cold fast path and
|
|
119
|
+
* nothing else. Correctness never rested on the ledger: the journal's producer
|
|
120
|
+
* identity is the durable exactly-once constraint, an exact retry receives a
|
|
121
|
+
* `Duplicate` receipt carrying the canonical sequence, and an
|
|
122
|
+
* `idempotency_conflict` is resolved by a read of the whole history that the
|
|
123
|
+
* ledger's own bound already made necessary.
|
|
124
|
+
*
|
|
125
|
+
* @category constants
|
|
126
|
+
* @since 1.0.0-rc.0
|
|
127
|
+
*/
|
|
128
|
+
export declare const defaultHydrationLimit = 4096;
|
|
129
|
+
/**
|
|
130
|
+
* Admission policy.
|
|
131
|
+
*
|
|
132
|
+
* The default caps one encoded command submission at 1 MiB, one branch's
|
|
133
|
+
* in-memory receipt ledger at {@link defaultLedgerCapacity} commands, and one
|
|
134
|
+
* branch's first-touch hydration at {@link defaultHydrationLimit} entries.
|
|
135
|
+
*
|
|
136
|
+
* @category models
|
|
137
|
+
* @since 0.1.0
|
|
138
|
+
*/
|
|
139
|
+
export interface Options {
|
|
140
|
+
/**
|
|
141
|
+
* Largest encoded journal entry followers accept. Defaults to
|
|
142
|
+
* {@link SyncProtocol.defaultMaxFrameBytes}. Configure the server and client
|
|
143
|
+
* with the same or a larger ceiling. Admission reserves the full durable
|
|
144
|
+
* envelope, including generated sequence and timestamp fields, before writing.
|
|
145
|
+
*/
|
|
146
|
+
readonly maxFrameBytes?: number | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* Largest encoded command submission admitted, in bytes. Defaults to
|
|
149
|
+
* {@link defaultMaxCommandBytes}.
|
|
150
|
+
*/
|
|
151
|
+
readonly maxCommandBytes?: number | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Receipts one branch keeps in memory before the oldest are evicted.
|
|
154
|
+
* Defaults to {@link defaultLedgerCapacity}.
|
|
155
|
+
*/
|
|
156
|
+
readonly ledgerCapacity?: number | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Entries one branch's first-touch hydration reads before it stops.
|
|
159
|
+
* Defaults to {@link defaultHydrationLimit}.
|
|
160
|
+
*/
|
|
161
|
+
readonly hydrationLimit?: number | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* Milliseconds a branch may have no active submissions before its in-memory state is
|
|
164
|
+
* dropped. Defaults to {@link defaultBranchIdleMs}.
|
|
165
|
+
*/
|
|
166
|
+
readonly branchIdleMs?: number | undefined;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Constructs the journal-backed branch command ledger under an explicit
|
|
170
|
+
* policy. The caller scope owns cached states and their idle-expiration timers.
|
|
171
|
+
*
|
|
172
|
+
* A submission whose encoded form exceeds the command ceiling is refused with
|
|
173
|
+
* `frame_too_large` before anything is appended, so one oversized `args`
|
|
174
|
+
* cannot enter the branch journal and poison every follower that replays it.
|
|
175
|
+
*
|
|
176
|
+
* First-touch hydration reads at most `hydrationLimit` entries, so the length
|
|
177
|
+
* of a branch's history is not charged to the next writer's latency; see
|
|
178
|
+
* {@link defaultHydrationLimit} for why the fast path may be seeded partially
|
|
179
|
+
* without any effect on what the ledger admits.
|
|
180
|
+
*
|
|
181
|
+
* Every option is validated as a positive safe integer at construction: the
|
|
182
|
+
* TypeScript type says `number`, and `NaN` silently disabled the ceiling it
|
|
183
|
+
* was compared against.
|
|
184
|
+
*
|
|
185
|
+
* @category constructors
|
|
186
|
+
* @since 0.1.0
|
|
187
|
+
*/
|
|
188
|
+
export declare const makeLiveWith: (options?: Options) => Effect.Effect<Service, SyncError, Journal.Journal | BranchShare.BranchShare | Scope.Scope>;
|
|
189
|
+
/**
|
|
190
|
+
* Constructs the journal-backed branch command ledger with default policy.
|
|
191
|
+
* The caller scope owns cached states and their idle-expiration timers.
|
|
192
|
+
*
|
|
193
|
+
* @category constructors
|
|
194
|
+
* @since 0.1.0
|
|
195
|
+
*/
|
|
196
|
+
export declare const makeLive: Effect.Effect<Service, never, Journal.Journal | BranchShare.BranchShare | Scope.Scope>;
|
|
197
|
+
/**
|
|
198
|
+
* Provides the journal-backed branch command ledger.
|
|
199
|
+
*
|
|
200
|
+
* @category layers
|
|
201
|
+
* @since 0.1.0
|
|
202
|
+
*/
|
|
203
|
+
export declare const layer: Layer.Layer<BranchCommands, never, Journal.Journal | BranchShare.BranchShare>;
|
|
204
|
+
/**
|
|
205
|
+
* Provides the journal-backed branch command ledger under an explicit policy.
|
|
206
|
+
* Fails with `invalid_request` when an option is not a positive safe integer.
|
|
207
|
+
*
|
|
208
|
+
* @category layers
|
|
209
|
+
* @since 0.1.0
|
|
210
|
+
*/
|
|
211
|
+
export declare const layerWith: (options: Options) => Layer.Layer<BranchCommands, SyncError, Journal.Journal | BranchShare.BranchShare>;
|
|
212
|
+
/**
|
|
213
|
+
* Builds a submission, filling in the fields a plain command never sets, and
|
|
214
|
+
* refusing a field the schema forbids with a `SyncError` rather than a defect.
|
|
215
|
+
*
|
|
216
|
+
* @category constructors
|
|
217
|
+
* @since 0.1.0
|
|
218
|
+
*/
|
|
219
|
+
export declare const submission: (fields: {
|
|
220
|
+
readonly branchId: BranchId;
|
|
221
|
+
readonly commandId: CommandId;
|
|
222
|
+
readonly participantId: CommandSubmission["participantId"];
|
|
223
|
+
readonly name: string;
|
|
224
|
+
readonly args?: string;
|
|
225
|
+
readonly target?: string;
|
|
226
|
+
}) => Effect.Effect<CommandSubmission, SyncError>;
|
|
227
|
+
export {};
|
|
228
|
+
//# sourceMappingURL=BranchCommands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BranchCommands.d.ts","sourceRoot":"","sources":["../../src/BranchCommands.ts"],"names":[],"mappings":"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"}
|