@moltzap/router 2026.902.0
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/LICENSE +202 -0
- package/NOTICE +19 -0
- package/README.md +31 -0
- package/bin/moltzap-router +9 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/router/contract.d.ts +209 -0
- package/dist/router/contract.d.ts.map +1 -0
- package/dist/router/contract.js +347 -0
- package/dist/router/contract.js.map +1 -0
- package/dist/router/feed.d.ts +89 -0
- package/dist/router/feed.d.ts.map +1 -0
- package/dist/router/feed.js +157 -0
- package/dist/router/feed.js.map +1 -0
- package/dist/router/http.d.ts +30 -0
- package/dist/router/http.d.ts.map +1 -0
- package/dist/router/http.js +219 -0
- package/dist/router/http.js.map +1 -0
- package/dist/router/poll-cursor.d.ts +50 -0
- package/dist/router/poll-cursor.d.ts.map +1 -0
- package/dist/router/poll-cursor.js +142 -0
- package/dist/router/poll-cursor.js.map +1 -0
- package/dist/router/poll-waiters.d.ts +24 -0
- package/dist/router/poll-waiters.d.ts.map +1 -0
- package/dist/router/poll-waiters.js +66 -0
- package/dist/router/poll-waiters.js.map +1 -0
- package/dist/router/poll.d.ts +38 -0
- package/dist/router/poll.d.ts.map +1 -0
- package/dist/router/poll.js +118 -0
- package/dist/router/poll.js.map +1 -0
- package/dist/router/rpc.d.ts +37 -0
- package/dist/router/rpc.d.ts.map +1 -0
- package/dist/router/rpc.js +75 -0
- package/dist/router/rpc.js.map +1 -0
- package/dist/router/send.d.ts +27 -0
- package/dist/router/send.d.ts.map +1 -0
- package/dist/router/send.js +91 -0
- package/dist/router/send.js.map +1 -0
- package/dist/router.d.ts +60 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +177 -0
- package/dist/router.js.map +1 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +112 -0
- package/dist/server.js.map +1 -0
- package/package.json +128 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/** @file Authenticated Router HTTP routes and exact envelope projection. */
|
|
2
|
+
import { HttpRouter, HttpServerRequest, HttpServerResponse, } from "@effect/platform";
|
|
3
|
+
import { NodeHttpServerRequest } from "@effect/platform-node";
|
|
4
|
+
import { AuthenticatedHttp, InternalServerError, MalformedRequestError, MethodNotAllowedError, OverloadedError, PayloadTooLargeError, RouteNotFoundError, UnsupportedMediaTypeError, } from "@moltzap/identity";
|
|
5
|
+
import canonicalize from "canonicalize";
|
|
6
|
+
import { Cause, Effect, Option, Schema } from "effect";
|
|
7
|
+
import { maximumPollCursorLength, RawRouterSendRequest, routerHealthPath, routerHttpErrorEnvelope, routerJsonContentType, routerPollPath, RouterPollRequest, RouterPollResult, routerPollRoutePattern, routerRepresentationLimits, routerSendPath, RouterSendResult, routerSendRoutePattern, } from "./contract.js";
|
|
8
|
+
import { withVerifiedRouterRequest } from "./rpc.js";
|
|
9
|
+
/**
|
|
10
|
+
* Builds the complete Router HTTP application over private capabilities.
|
|
11
|
+
*
|
|
12
|
+
* @param input Correlated operations, readiness, and admission capability.
|
|
13
|
+
* @param input.client Private correlated operations.
|
|
14
|
+
* @param input.feed Feed readiness state.
|
|
15
|
+
* @param input.requestSemaphore Immediate active-request admission.
|
|
16
|
+
* @returns The complete named-route HTTP application.
|
|
17
|
+
*/
|
|
18
|
+
export const makeRouterHttpApp = (input) => HttpRouter.empty.pipe(sendRoute(input), pollRoute(input), healthRoute(input), fallbackRoute());
|
|
19
|
+
/**
|
|
20
|
+
* Reports local readiness without exposing feed state.
|
|
21
|
+
*
|
|
22
|
+
* @param feed Current process feed.
|
|
23
|
+
* @returns An empty 204 or 503 response.
|
|
24
|
+
*/
|
|
25
|
+
export const routerHealthResponse = (feed) => feed.freshAppendReady.pipe(Effect.map((ready) => HttpServerResponse.empty({ status: ready ? 204 : 503 })));
|
|
26
|
+
const rawPollRequest = Schema.Struct({
|
|
27
|
+
pollCursor: Schema.optional(Schema.String.pipe(Schema.maxLength(maximumPollCursorLength))),
|
|
28
|
+
}).annotations({
|
|
29
|
+
parseOptions: { exact: true, onExcessProperty: "error" },
|
|
30
|
+
});
|
|
31
|
+
const jsonText = (body, status) => HttpServerResponse.text(body, {
|
|
32
|
+
status,
|
|
33
|
+
contentType: routerJsonContentType,
|
|
34
|
+
});
|
|
35
|
+
const errorResponse = (error) => {
|
|
36
|
+
const mapped = routerHttpErrorEnvelope(error._tag);
|
|
37
|
+
return mapped === undefined
|
|
38
|
+
? jsonText('{"error":"internal"}', 500)
|
|
39
|
+
: jsonText(mapped.body, mapped.status);
|
|
40
|
+
};
|
|
41
|
+
const recoverUnexpectedFailure = (effect) => effect.pipe(Effect.catchAllCause((cause) => Effect.logError("Router route failed", Cause.squash(cause)).pipe(Effect.as(errorResponse(new InternalServerError())))));
|
|
42
|
+
const resultResponse = (schema, value) => Schema.encode(schema)(value).pipe(Effect.catchTag("ParseError", () => Effect.fail(new InternalServerError())), Effect.flatMap((encoded) => {
|
|
43
|
+
const body = canonicalize(encoded);
|
|
44
|
+
return body === undefined
|
|
45
|
+
? Effect.fail(new InternalServerError())
|
|
46
|
+
: Effect.succeed(jsonText(body, 200));
|
|
47
|
+
}));
|
|
48
|
+
const canonicalContentLength = (request) => {
|
|
49
|
+
const value = request.headers["content-length"];
|
|
50
|
+
if (value === undefined || !/^(?:0|[1-9]\d*)$/.test(value)) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const parsed = Number(value);
|
|
54
|
+
return Number.isSafeInteger(parsed) ? parsed : undefined;
|
|
55
|
+
};
|
|
56
|
+
const rawHeaderCount = (request, name) => {
|
|
57
|
+
const incomingMessage = NodeHttpServerRequest.toIncomingMessage(request);
|
|
58
|
+
let count = 0;
|
|
59
|
+
for (let index = 0; index < incomingMessage.rawHeaders.length; index += 2) {
|
|
60
|
+
if (incomingMessage.rawHeaders[index]?.toLowerCase() === name) {
|
|
61
|
+
count += 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return count;
|
|
65
|
+
};
|
|
66
|
+
const SINGLE_VALUE_HEADERS = [
|
|
67
|
+
"host",
|
|
68
|
+
"content-type",
|
|
69
|
+
"content-encoding",
|
|
70
|
+
"content-length",
|
|
71
|
+
"transfer-encoding",
|
|
72
|
+
"moltzap-version",
|
|
73
|
+
"content-digest",
|
|
74
|
+
"signature-input",
|
|
75
|
+
"signature",
|
|
76
|
+
];
|
|
77
|
+
const framingIsMalformed = (request) => {
|
|
78
|
+
const duplicate = SINGLE_VALUE_HEADERS.some((name) => rawHeaderCount(request, name) > 1);
|
|
79
|
+
const conflictingLength = request.headers["content-length"] !== undefined &&
|
|
80
|
+
request.headers["transfer-encoding"] !== undefined;
|
|
81
|
+
return duplicate || conflictingLength;
|
|
82
|
+
};
|
|
83
|
+
const mediaTypeIsUnsupported = (request) => request.headers["content-type"] !== routerJsonContentType ||
|
|
84
|
+
request.headers["content-encoding"] !== undefined;
|
|
85
|
+
const validateDeclaredLength = (request, maximumBytes) => {
|
|
86
|
+
const headerIsPresent = request.headers["content-length"] !== undefined;
|
|
87
|
+
const declaredLength = canonicalContentLength(request);
|
|
88
|
+
if (headerIsPresent && declaredLength === undefined) {
|
|
89
|
+
return Effect.fail(new MalformedRequestError());
|
|
90
|
+
}
|
|
91
|
+
if (declaredLength !== undefined && declaredLength > maximumBytes) {
|
|
92
|
+
return Effect.fail(new PayloadTooLargeError());
|
|
93
|
+
}
|
|
94
|
+
return Effect.void;
|
|
95
|
+
};
|
|
96
|
+
const validateRequestFraming = (request, maximumBytes) => {
|
|
97
|
+
if (framingIsMalformed(request)) {
|
|
98
|
+
return Effect.fail(new MalformedRequestError());
|
|
99
|
+
}
|
|
100
|
+
if (mediaTypeIsUnsupported(request)) {
|
|
101
|
+
return Effect.fail(new UnsupportedMediaTypeError());
|
|
102
|
+
}
|
|
103
|
+
return validateDeclaredLength(request, maximumBytes);
|
|
104
|
+
};
|
|
105
|
+
const readBoundedBody = (request, maximumBytes) =>
|
|
106
|
+
// #ignore-sloppy-code-next-line[async-keyword]: This Effect callback constructor does not use the JavaScript keyword.
|
|
107
|
+
Effect.async((resume) => {
|
|
108
|
+
const incoming = NodeHttpServerRequest.toIncomingMessage(request);
|
|
109
|
+
const body = new Uint8Array(maximumBytes);
|
|
110
|
+
let length = 0;
|
|
111
|
+
function cleanup() {
|
|
112
|
+
incoming.off("data", onData);
|
|
113
|
+
incoming.off("end", onEnd);
|
|
114
|
+
incoming.off("error", onError);
|
|
115
|
+
}
|
|
116
|
+
function onData(chunk) {
|
|
117
|
+
if (length + chunk.byteLength > maximumBytes) {
|
|
118
|
+
cleanup();
|
|
119
|
+
incoming.resume();
|
|
120
|
+
resume(Effect.fail(new PayloadTooLargeError()));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
body.set(chunk, length);
|
|
124
|
+
length += chunk.byteLength;
|
|
125
|
+
}
|
|
126
|
+
function onEnd() {
|
|
127
|
+
cleanup();
|
|
128
|
+
resume(Effect.succeed(body.slice(0, length)));
|
|
129
|
+
}
|
|
130
|
+
function onError() {
|
|
131
|
+
cleanup();
|
|
132
|
+
resume(Effect.fail(new MalformedRequestError()));
|
|
133
|
+
}
|
|
134
|
+
incoming.on("data", onData);
|
|
135
|
+
incoming.once("end", onEnd);
|
|
136
|
+
incoming.once("error", onError);
|
|
137
|
+
return Effect.sync(() => {
|
|
138
|
+
cleanup();
|
|
139
|
+
incoming.resume();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
const runAdmittedDomainRequest = (input, httpRequest) => Effect.gen(function* () {
|
|
143
|
+
const bodyBytes = yield* readBoundedBody(httpRequest, input.bodyCap);
|
|
144
|
+
const verifiedRequest = yield* AuthenticatedHttp.verifyAgentRequest({
|
|
145
|
+
httpRequest,
|
|
146
|
+
bodyBytes,
|
|
147
|
+
});
|
|
148
|
+
const request = yield* Schema.decodeUnknown(input.outerRequestSchema)(verifiedRequest.request, {
|
|
149
|
+
exact: true,
|
|
150
|
+
onExcessProperty: "error",
|
|
151
|
+
}).pipe(Effect.catchTag("ParseError", () => Effect.fail(new MalformedRequestError())));
|
|
152
|
+
const result = yield* withVerifiedRouterRequest(verifiedRequest, input.invoke(input.client, request));
|
|
153
|
+
return yield* resultResponse(input.resultSchema, result);
|
|
154
|
+
});
|
|
155
|
+
const handleDomainRequest = (input) => Effect.gen(function* () {
|
|
156
|
+
const httpRequest = yield* HttpServerRequest.HttpServerRequest;
|
|
157
|
+
if (httpRequest.url !== input.routePath) {
|
|
158
|
+
return errorResponse(new RouteNotFoundError());
|
|
159
|
+
}
|
|
160
|
+
yield* validateRequestFraming(httpRequest, input.bodyCap);
|
|
161
|
+
const admitted = yield* input.requestSemaphore.withPermitsIfAvailable(1)(runAdmittedDomainRequest(input, httpRequest));
|
|
162
|
+
return yield* Option.match(admitted, {
|
|
163
|
+
onNone: () => Effect.succeed(errorResponse(new OverloadedError())),
|
|
164
|
+
onSome: Effect.succeed,
|
|
165
|
+
});
|
|
166
|
+
}).pipe(Effect.catchAll((error) => Effect.succeed(errorResponse(error))), recoverUnexpectedFailure);
|
|
167
|
+
function sendRoute(input) {
|
|
168
|
+
return HttpRouter.post(routerSendRoutePattern, handleDomainRequest({
|
|
169
|
+
routePath: routerSendPath,
|
|
170
|
+
outerRequestSchema: RawRouterSendRequest,
|
|
171
|
+
resultSchema: RouterSendResult,
|
|
172
|
+
bodyCap: routerRepresentationLimits.sendRequestBodyBytes,
|
|
173
|
+
invoke: (client, request) => client.send({ request }),
|
|
174
|
+
client: input.client,
|
|
175
|
+
requestSemaphore: input.requestSemaphore,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
function pollRoute(input) {
|
|
179
|
+
return HttpRouter.post(routerPollRoutePattern, handleDomainRequest({
|
|
180
|
+
routePath: routerPollPath,
|
|
181
|
+
outerRequestSchema: rawPollRequest,
|
|
182
|
+
resultSchema: RouterPollResult,
|
|
183
|
+
bodyCap: routerRepresentationLimits.pollRequestBodyBytes,
|
|
184
|
+
invoke: (client, request) => Schema.decodeUnknown(RouterPollRequest)(request, {
|
|
185
|
+
exact: true,
|
|
186
|
+
onExcessProperty: "error",
|
|
187
|
+
}).pipe(Effect.matchEffect({
|
|
188
|
+
onFailure: () => Effect.succeed({ kind: "cursor_invalid" }),
|
|
189
|
+
onSuccess: (decoded) => client.poll({ request: decoded }),
|
|
190
|
+
})),
|
|
191
|
+
client: input.client,
|
|
192
|
+
requestSemaphore: input.requestSemaphore,
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
function healthRoute(input) {
|
|
196
|
+
return HttpRouter.get(routerHealthPath, recoverUnexpectedFailure(Effect.gen(function* () {
|
|
197
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
198
|
+
if (request.url !== routerHealthPath) {
|
|
199
|
+
return errorResponse(new RouteNotFoundError());
|
|
200
|
+
}
|
|
201
|
+
return yield* routerHealthResponse(input.feed);
|
|
202
|
+
})));
|
|
203
|
+
}
|
|
204
|
+
function fallbackRoute() {
|
|
205
|
+
return HttpRouter.all("*", recoverUnexpectedFailure(Effect.gen(function* () {
|
|
206
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
207
|
+
if (request.url.includes("?")) {
|
|
208
|
+
return errorResponse(new RouteNotFoundError());
|
|
209
|
+
}
|
|
210
|
+
const path = request.url.split("?", 1)[0];
|
|
211
|
+
if (path === routerSendPath ||
|
|
212
|
+
path === routerPollPath ||
|
|
213
|
+
path === routerHealthPath) {
|
|
214
|
+
return errorResponse(new MethodNotAllowedError());
|
|
215
|
+
}
|
|
216
|
+
return errorResponse(new RouteNotFoundError());
|
|
217
|
+
})));
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/router/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** @file Caller-bound authenticated cursor encoding for volatile Router polling. */
|
|
2
|
+
import { type AgentId } from "@moltzap/identity";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { type PollCursor as PollCursorValue, type RouterInstanceId as RouterInstanceIdValue } from "./contract.js";
|
|
5
|
+
declare const PollCursorEncryptionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
6
|
+
readonly _tag: "PollCursorEncryptionError";
|
|
7
|
+
} & Readonly<A>;
|
|
8
|
+
/** Cursor construction failed before a result could be returned. */
|
|
9
|
+
declare class PollCursorEncryptionError extends PollCursorEncryptionError_base {
|
|
10
|
+
}
|
|
11
|
+
declare const PollCursorInvalidError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
12
|
+
readonly _tag: "PollCursorInvalidError";
|
|
13
|
+
} & Readonly<A>;
|
|
14
|
+
/** A cursor does not authenticate for this caller and process. */
|
|
15
|
+
declare class PollCursorInvalidError extends PollCursorInvalidError_base {
|
|
16
|
+
}
|
|
17
|
+
/** Authenticated current-process cursor operations. */
|
|
18
|
+
export interface PollCursorCodec {
|
|
19
|
+
readonly encrypt: (input: {
|
|
20
|
+
readonly agentId: AgentId;
|
|
21
|
+
readonly lastScannedOrder: bigint;
|
|
22
|
+
}) => Effect.Effect<PollCursorValue, PollCursorEncryptionError>;
|
|
23
|
+
readonly decrypt: (cursor: PollCursorValue, callerAgentId: AgentId) => Effect.Effect<bigint, PollCursorInvalidError>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates the current-instance authenticated client-held cursor codec.
|
|
27
|
+
*
|
|
28
|
+
* @param input Process-local key and instance identity.
|
|
29
|
+
* @param input.key Process-local direct encryption key.
|
|
30
|
+
* @param input.routerInstanceId Current volatile process identity.
|
|
31
|
+
* @returns A caller-bound cursor codec.
|
|
32
|
+
*/
|
|
33
|
+
export declare const makePollCursorCodec: (input: {
|
|
34
|
+
readonly key: Uint8Array;
|
|
35
|
+
readonly routerInstanceId: RouterInstanceIdValue;
|
|
36
|
+
}) => PollCursorCodec;
|
|
37
|
+
/**
|
|
38
|
+
* Generates one process-local random Router instance identity.
|
|
39
|
+
*
|
|
40
|
+
* @returns A fresh random instance identifier.
|
|
41
|
+
*/
|
|
42
|
+
export declare const generateRouterInstanceId: () => RouterInstanceIdValue;
|
|
43
|
+
/**
|
|
44
|
+
* Generates the process-local direct A256GCM key.
|
|
45
|
+
*
|
|
46
|
+
* @returns A fresh random 256-bit key.
|
|
47
|
+
*/
|
|
48
|
+
export declare const generatePollCursorKey: () => Uint8Array;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=poll-cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-cursor.d.ts","sourceRoot":"","sources":["../../src/router/poll-cursor.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,KAAK,OAAO,EAA4B,MAAM,mBAAmB,CAAC;AAE3E,OAAO,EAAQ,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAExD,OAAO,EAKL,KAAK,UAAU,IAAI,eAAe,EAGlC,KAAK,gBAAgB,IAAI,qBAAqB,EAC/C,MAAM,eAAe,CAAC;;;;AAGvB,oEAAoE;AACpE,cAAM,yBAA0B,SAAQ,8BAEvC;CAAG;;;;AAEJ,kEAAkE;AAClE,cAAM,sBAAuB,SAAQ,2BAEpC;CAAG;AAEJ,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;QACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;KACnC,KAAK,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAAC;IAChE,QAAQ,CAAC,OAAO,EAAE,CAChB,MAAM,EAAE,eAAe,EACvB,aAAa,EAAE,OAAO,KACnB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,UAAW;IACzC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;CAClD,KAAG,eAcH,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAO,qBAOzC,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAO,UACc,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** @file Caller-bound authenticated cursor encoding for volatile Router polling. */
|
|
2
|
+
import { AgentId as AgentIdSchema } from "@moltzap/identity";
|
|
3
|
+
import canonicalize from "canonicalize";
|
|
4
|
+
import { Data, Effect, Encoding, Schema } from "effect";
|
|
5
|
+
import { compactDecrypt, CompactEncrypt } from "jose";
|
|
6
|
+
import { encodedPollCursorProtectedHeader, PollCursor, pollCursorPrefix, pollCursorProtectedHeader, RouterInstanceId, routerInstanceIdByteLength, } from "./contract.js";
|
|
7
|
+
import { maximumPrivateOrder } from "./feed.js";
|
|
8
|
+
/** Cursor construction failed before a result could be returned. */
|
|
9
|
+
class PollCursorEncryptionError extends Data.TaggedError("PollCursorEncryptionError") {
|
|
10
|
+
}
|
|
11
|
+
/** A cursor does not authenticate for this caller and process. */
|
|
12
|
+
class PollCursorInvalidError extends Data.TaggedError("PollCursorInvalidError") {
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates the current-instance authenticated client-held cursor codec.
|
|
16
|
+
*
|
|
17
|
+
* @param input Process-local key and instance identity.
|
|
18
|
+
* @param input.key Process-local direct encryption key.
|
|
19
|
+
* @param input.routerInstanceId Current volatile process identity.
|
|
20
|
+
* @returns A caller-bound cursor codec.
|
|
21
|
+
*/
|
|
22
|
+
export const makePollCursorCodec = (input) => {
|
|
23
|
+
const key = Uint8Array.from(input.key);
|
|
24
|
+
const routerInstanceId = input.routerInstanceId;
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
encrypt: ({ agentId, lastScannedOrder, }) => encodePollCursor(key, routerInstanceId, agentId, lastScannedOrder),
|
|
27
|
+
decrypt: (cursor, callerAgentId) => decodePollCursor(key, routerInstanceId, cursor, callerAgentId),
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Generates one process-local random Router instance identity.
|
|
32
|
+
*
|
|
33
|
+
* @returns A fresh random instance identifier.
|
|
34
|
+
*/
|
|
35
|
+
export const generateRouterInstanceId = () => Schema.decodeUnknownSync(RouterInstanceId)(`rti_${Encoding.encodeBase64Url(globalThis.crypto.getRandomValues(new Uint8Array(routerInstanceIdByteLength)))}`);
|
|
36
|
+
/**
|
|
37
|
+
* Generates the process-local direct A256GCM key.
|
|
38
|
+
*
|
|
39
|
+
* @returns A fresh random 256-bit key.
|
|
40
|
+
*/
|
|
41
|
+
export const generatePollCursorKey = () => globalThis.crypto.getRandomValues(new Uint8Array(32));
|
|
42
|
+
const utf8Encoder = new TextEncoder();
|
|
43
|
+
const utf8Decoder = new TextDecoder("utf-8", {
|
|
44
|
+
fatal: true,
|
|
45
|
+
ignoreBOM: true,
|
|
46
|
+
});
|
|
47
|
+
const orderText = Schema.String.pipe(Schema.pattern(/^(?:0|[1-9]\d*)$/), Schema.filter((value) => BigInt(value) <= maximumPrivateOrder));
|
|
48
|
+
const cursorPlaintext = Schema.Struct({
|
|
49
|
+
agentId: AgentIdSchema,
|
|
50
|
+
routerInstanceId: RouterInstanceId,
|
|
51
|
+
lastScannedOrder: orderText,
|
|
52
|
+
}).annotations({
|
|
53
|
+
parseOptions: {
|
|
54
|
+
exact: true,
|
|
55
|
+
onExcessProperty: "error",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const canonicalPlaintext = (input) => canonicalize({
|
|
59
|
+
agentId: input.agentId,
|
|
60
|
+
routerInstanceId: input.routerInstanceId,
|
|
61
|
+
lastScannedOrder: input.lastScannedOrder.toString(10),
|
|
62
|
+
});
|
|
63
|
+
const invalidCursor = () => new PollCursorInvalidError();
|
|
64
|
+
const headerIsExact = (header) => {
|
|
65
|
+
if (Reflect.ownKeys(header).length !== 3) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return (header.alg === pollCursorProtectedHeader.alg &&
|
|
69
|
+
header.enc === pollCursorProtectedHeader.enc &&
|
|
70
|
+
header.typ === pollCursorProtectedHeader.typ);
|
|
71
|
+
};
|
|
72
|
+
function encodePollCursor(key, routerInstanceId, agentId, lastScannedOrder) {
|
|
73
|
+
return Effect.gen(function* () {
|
|
74
|
+
if (lastScannedOrder < 0n || lastScannedOrder > maximumPrivateOrder) {
|
|
75
|
+
return yield* Effect.fail(new PollCursorEncryptionError());
|
|
76
|
+
}
|
|
77
|
+
const plaintext = canonicalPlaintext({
|
|
78
|
+
agentId,
|
|
79
|
+
routerInstanceId,
|
|
80
|
+
lastScannedOrder,
|
|
81
|
+
});
|
|
82
|
+
if (plaintext === undefined) {
|
|
83
|
+
return yield* Effect.fail(new PollCursorEncryptionError());
|
|
84
|
+
}
|
|
85
|
+
const compact = yield* Effect.tryPromise({
|
|
86
|
+
try: () => new CompactEncrypt(utf8Encoder.encode(plaintext))
|
|
87
|
+
.setProtectedHeader(pollCursorProtectedHeader)
|
|
88
|
+
.encrypt(key),
|
|
89
|
+
catch: () => new PollCursorEncryptionError(),
|
|
90
|
+
});
|
|
91
|
+
return yield* Schema.decodeUnknown(PollCursor)(`${pollCursorPrefix}${compact}`).pipe(Effect.catchTag("ParseError", () => Effect.fail(new PollCursorEncryptionError())));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const plaintextIsCurrent = (input) => {
|
|
95
|
+
if (input.canonical !== input.text) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return (input.agentId === input.callerAgentId &&
|
|
99
|
+
input.routerInstanceId === input.currentRouterInstanceId);
|
|
100
|
+
};
|
|
101
|
+
const decodeCursorPlaintext = (text) => Schema.decodeUnknown(Schema.parseJson(cursorPlaintext))(text, {
|
|
102
|
+
exact: true,
|
|
103
|
+
onExcessProperty: "error",
|
|
104
|
+
}).pipe(Effect.catchTag("ParseError", () => Effect.fail(invalidCursor())));
|
|
105
|
+
function decodePollCursor(key, routerInstanceId, cursor, callerAgentId) {
|
|
106
|
+
return Effect.gen(function* () {
|
|
107
|
+
const compact = cursor.slice(pollCursorPrefix.length);
|
|
108
|
+
if (compact.split(".", 1)[0] !== encodedPollCursorProtectedHeader) {
|
|
109
|
+
return yield* Effect.fail(invalidCursor());
|
|
110
|
+
}
|
|
111
|
+
const decrypted = yield* Effect.tryPromise({
|
|
112
|
+
try: () => compactDecrypt(compact, key),
|
|
113
|
+
catch: invalidCursor,
|
|
114
|
+
});
|
|
115
|
+
if (!headerIsExact(decrypted.protectedHeader)) {
|
|
116
|
+
return yield* Effect.fail(invalidCursor());
|
|
117
|
+
}
|
|
118
|
+
const text = yield* Effect.try({
|
|
119
|
+
try: () => utf8Decoder.decode(decrypted.plaintext),
|
|
120
|
+
catch: invalidCursor,
|
|
121
|
+
});
|
|
122
|
+
const value = yield* decodeCursorPlaintext(text);
|
|
123
|
+
const canonical = canonicalize({
|
|
124
|
+
agentId: value.agentId,
|
|
125
|
+
routerInstanceId: value.routerInstanceId,
|
|
126
|
+
lastScannedOrder: value.lastScannedOrder,
|
|
127
|
+
});
|
|
128
|
+
if (canonical === undefined ||
|
|
129
|
+
!plaintextIsCurrent({
|
|
130
|
+
canonical,
|
|
131
|
+
text,
|
|
132
|
+
agentId: value.agentId,
|
|
133
|
+
callerAgentId,
|
|
134
|
+
routerInstanceId: value.routerInstanceId,
|
|
135
|
+
currentRouterInstanceId: routerInstanceId,
|
|
136
|
+
})) {
|
|
137
|
+
return yield* Effect.fail(invalidCursor());
|
|
138
|
+
}
|
|
139
|
+
return BigInt(value.lastScannedOrder);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=poll-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-cursor.js","sourceRoot":"","sources":["../../src/router/poll-cursor.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAgB,OAAO,IAAI,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,YAAY,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AACtD,OAAO,EACL,gCAAgC,EAChC,UAAU,EACV,gBAAgB,EAChB,yBAAyB,EAEzB,gBAAgB,EAChB,0BAA0B,GAE3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEhD,oEAAoE;AACpE,MAAM,yBAA0B,SAAQ,IAAI,CAAC,WAAW,CACtD,2BAA2B,CAC5B;CAAG;AAEJ,kEAAkE;AAClE,MAAM,sBAAuB,SAAQ,IAAI,CAAC,WAAW,CACnD,wBAAwB,CACzB;CAAG;AAcJ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAGnC,EAAmB,EAAE;IACpB,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,CAAC,EACR,OAAO,EACP,gBAAgB,GAIjB,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,CAAC;QACxE,OAAO,EAAE,CAAC,MAAuB,EAAE,aAAsB,EAAE,EAAE,CAC3D,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,CAAC;KACjE,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAA0B,EAAE,CAClE,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CACxC,OAAO,QAAQ,CAAC,eAAe,CAC7B,UAAU,CAAC,MAAM,CAAC,eAAe,CAC/B,IAAI,UAAU,CAAC,0BAA0B,CAAC,CAC3C,CACF,EAAE,CACJ,CAAC;AAEJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAe,EAAE,CACpD,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AAExD,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE;IAC3C,KAAK,EAAE,IAAI;IACX,SAAS,EAAE,IAAI;CAChB,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAClC,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAClC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,mBAAmB,CAAC,CAC/D,CAAC;AAEF,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,aAAa;IACtB,gBAAgB,EAAE,gBAAgB;IAClC,gBAAgB,EAAE,SAAS;CAC5B,CAAC,CAAC,WAAW,CAAC;IACb,YAAY,EAAE;QACZ,KAAK,EAAE,IAAI;QACX,gBAAgB,EAAE,OAAO;KAC1B;CACF,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,CAAC,KAI3B,EAAsB,EAAE,CACvB,YAAY,CAAC;IACX,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;IACxC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;CACtD,CAAC,CAAC;AAEL,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,sBAAsB,EAAE,CAAC;AAEzD,MAAM,aAAa,GAAG,CAAC,MAAyC,EAAW,EAAE;IAC3E,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,CACL,MAAM,CAAC,GAAG,KAAK,yBAAyB,CAAC,GAAG;QAC5C,MAAM,CAAC,GAAG,KAAK,yBAAyB,CAAC,GAAG;QAC5C,MAAM,CAAC,GAAG,KAAK,yBAAyB,CAAC,GAAG,CAC7C,CAAC;AACJ,CAAC,CAAC;AAEF,SAAS,gBAAgB,CACvB,GAAe,EACf,gBAAuC,EACvC,OAAgB,EAChB,gBAAwB;IAExB,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzB,IAAI,gBAAgB,GAAG,EAAE,IAAI,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;YACpE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,SAAS,GAAG,kBAAkB,CAAC;YACnC,OAAO;YACP,gBAAgB;YAChB,gBAAgB;SACjB,CAAC,CAAC;QACH,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACvC,GAAG,EAAE,GAAG,EAAE,CACR,IAAI,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;iBAC9C,kBAAkB,CAAC,yBAAyB,CAAC;iBAC7C,OAAO,CAAC,GAAG,CAAC;YACjB,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,yBAAyB,EAAE;SAC7C,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAC5C,GAAG,gBAAgB,GAAG,OAAO,EAAE,CAChC,CAAC,IAAI,CACJ,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE,CACjC,MAAM,CAAC,IAAI,CAAC,IAAI,yBAAyB,EAAE,CAAC,CAC7C,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,KAO3B,EAAW,EAAE;IACZ,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,CACL,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,aAAa;QACrC,KAAK,CAAC,gBAAgB,KAAK,KAAK,CAAC,uBAAuB,CACzD,CAAC;AACJ,CAAC,CAAC;AAIF,MAAM,qBAAqB,GAAG,CAC5B,IAAY,EAC4C,EAAE,CAC1D,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE;IAC5D,KAAK,EAAE,IAAI;IACX,gBAAgB,EAAE,OAAO;CAC1B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;AAE7E,SAAS,gBAAgB,CACvB,GAAe,EACf,gBAAuC,EACvC,MAAuB,EACvB,aAAsB;IAEtB,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,gCAAgC,EAAE,CAAC;YAClE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACzC,GAAG,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC;YACvC,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAC7B,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;YAClD,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,YAAY,CAAC;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;SACzC,CAAC,CAAC;QACH,IACE,SAAS,KAAK,SAAS;YACvB,CAAC,kBAAkB,CAAC;gBAClB,SAAS;gBACT,IAAI;gBACJ,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,aAAa;gBACb,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;gBACxC,uBAAuB,EAAE,gBAAgB;aAC1C,CAAC,EACF,CAAC;YACD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** @file Bounded long-poll waiter coordination keyed by addressed agent. */
|
|
2
|
+
import type { AgentId } from "@moltzap/identity";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
/** Bounded addressed-notification capability. */
|
|
5
|
+
export interface PollWaiters {
|
|
6
|
+
readonly awaitSignal: (agentId: AgentId, addressedDataIsReady: Effect.Effect<boolean>) => Effect.Effect<void, HeldPollCapacityError>;
|
|
7
|
+
readonly notify: (recipients: ReadonlySet<AgentId>) => Effect.Effect<void>;
|
|
8
|
+
readonly activeCount: Effect.Effect<number>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates request-scoped poll waiters with global and per-agent bounds.
|
|
12
|
+
*
|
|
13
|
+
* @param capacity Maximum simultaneous poll waiters.
|
|
14
|
+
* @returns The bounded waiter capability.
|
|
15
|
+
*/
|
|
16
|
+
export declare const makePollWaiters: (capacity: number) => Effect.Effect<PollWaiters>;
|
|
17
|
+
declare const HeldPollCapacityError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
18
|
+
readonly _tag: "HeldPollCapacityError";
|
|
19
|
+
} & Readonly<A>;
|
|
20
|
+
/** No poll-waiter slot is immediately available. */
|
|
21
|
+
declare class HeldPollCapacityError extends HeldPollCapacityError_base {
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=poll-waiters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-waiters.d.ts","sourceRoot":"","sources":["../../src/router/poll-waiters.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAkB,MAAM,EAAO,MAAM,QAAQ,CAAC;AAErD,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,CACpB,OAAO,EAAE,OAAO,EAChB,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KACzC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,aAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAc/B,CAAC;;;;AAE9C,oDAAoD;AACpD,cAAM,qBAAsB,SAAQ,0BAAyC;CAAG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Data, Deferred, Effect, Ref } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Creates request-scoped poll waiters with global and per-agent bounds.
|
|
4
|
+
*
|
|
5
|
+
* @param capacity Maximum simultaneous poll waiters.
|
|
6
|
+
* @returns The bounded waiter capability.
|
|
7
|
+
*/
|
|
8
|
+
export const makePollWaiters = (capacity) => Effect.gen(function* () {
|
|
9
|
+
const waiters = yield* Ref.make(new Map());
|
|
10
|
+
const service = {
|
|
11
|
+
awaitSignal: (agentId, addressedDataIsReady) => awaitRegisteredWaiter(waiters, capacity, agentId, addressedDataIsReady),
|
|
12
|
+
notify: (recipients) => notifyWaiters(waiters, recipients),
|
|
13
|
+
activeCount: Ref.get(waiters).pipe(Effect.map((current) => current.size)),
|
|
14
|
+
};
|
|
15
|
+
return Object.freeze(service);
|
|
16
|
+
}).pipe(Effect.withSpan("makePollWaiters"));
|
|
17
|
+
/** No poll-waiter slot is immediately available. */
|
|
18
|
+
class HeldPollCapacityError extends Data.TaggedError("HeldPollCapacityError") {
|
|
19
|
+
}
|
|
20
|
+
const removeWaiter = (waiters, agentId, deferred) => Ref.update(waiters, (current) => {
|
|
21
|
+
if (current.get(agentId) !== deferred) {
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
const next = new Map(current);
|
|
25
|
+
next.delete(agentId);
|
|
26
|
+
return next;
|
|
27
|
+
});
|
|
28
|
+
const registerWaiter = (waiters, capacity, agentId, deferred) => Ref.modify(waiters, (current) => {
|
|
29
|
+
if (current.has(agentId) || current.size >= capacity) {
|
|
30
|
+
return [false, current];
|
|
31
|
+
}
|
|
32
|
+
const next = new Map(current);
|
|
33
|
+
next.set(agentId, deferred);
|
|
34
|
+
return [true, next];
|
|
35
|
+
});
|
|
36
|
+
const detachWaiters = (waiters, recipients) => Ref.modify(waiters, (current) => {
|
|
37
|
+
const next = new Map(current);
|
|
38
|
+
const found = [];
|
|
39
|
+
for (const agentId of recipients) {
|
|
40
|
+
const deferred = next.get(agentId);
|
|
41
|
+
if (deferred !== undefined) {
|
|
42
|
+
found.push(deferred);
|
|
43
|
+
next.delete(agentId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [found, next];
|
|
47
|
+
});
|
|
48
|
+
function awaitRegisteredWaiter(waiters, capacity, agentId, addressedDataIsReady) {
|
|
49
|
+
return Effect.gen(function* () {
|
|
50
|
+
const deferred = yield* Deferred.make();
|
|
51
|
+
const registered = yield* registerWaiter(waiters, capacity, agentId, deferred);
|
|
52
|
+
if (!registered) {
|
|
53
|
+
return yield* Effect.fail(new HeldPollCapacityError());
|
|
54
|
+
}
|
|
55
|
+
return yield* Effect.gen(function* () {
|
|
56
|
+
if (yield* addressedDataIsReady) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
yield* Deferred.await(deferred);
|
|
60
|
+
}).pipe(Effect.ensuring(removeWaiter(waiters, agentId, deferred)));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function notifyWaiters(waiters, recipients) {
|
|
64
|
+
return detachWaiters(waiters, recipients).pipe(Effect.flatMap((detached) => Effect.forEach(detached, (deferred) => Deferred.succeed(deferred, undefined), { concurrency: 128, discard: true })));
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=poll-waiters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-waiters.js","sourceRoot":"","sources":["../../src/router/poll-waiters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** @file Verified polling over the volatile feed, cursor codec, and bounded waiters. */
|
|
2
|
+
import { type AgentId, InternalServerError, OverloadedError } from "@moltzap/identity";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import type { PollCursorCodec } from "./poll-cursor.js";
|
|
5
|
+
import type { PollWaiters } from "./poll-waiters.js";
|
|
6
|
+
import { type RouterInstanceId, type RouterPollRequest, type RouterPollResult as RouterPollResultValue } from "./contract.js";
|
|
7
|
+
import { type RouterFeed } from "./feed.js";
|
|
8
|
+
type PollFailure = OverloadedError | InternalServerError;
|
|
9
|
+
interface VerifiedCaller {
|
|
10
|
+
readonly callerAgentId: AgentId;
|
|
11
|
+
}
|
|
12
|
+
interface PollDependencies {
|
|
13
|
+
readonly routerInstanceId: RouterInstanceId;
|
|
14
|
+
readonly feed: RouterFeed;
|
|
15
|
+
readonly pollWaiters: PollWaiters;
|
|
16
|
+
readonly cursorCodec: PollCursorCodec;
|
|
17
|
+
readonly pollMessageLimit: number;
|
|
18
|
+
readonly pollResponseByteLimit: number;
|
|
19
|
+
}
|
|
20
|
+
/** Poll domain operation after HTTP authentication. */
|
|
21
|
+
export interface RouterPoll {
|
|
22
|
+
readonly handle: (request: RouterPollRequest, verifiedRequest: VerifiedCaller) => Effect.Effect<RouterPollResultValue, PollFailure>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds endpoint-wide bounded polling over one volatile global feed.
|
|
26
|
+
*
|
|
27
|
+
* @param input Current process state and finite poll bounds.
|
|
28
|
+
* @param input.routerInstanceId Current volatile process identity.
|
|
29
|
+
* @param input.feed Global volatile feed.
|
|
30
|
+
* @param input.pollWaiters Addressed poll-waiter notifications.
|
|
31
|
+
* @param input.cursorCodec Caller-bound cursor codec.
|
|
32
|
+
* @param input.pollMessageLimit Maximum messages in one result.
|
|
33
|
+
* @param input.pollResponseByteLimit Maximum complete result bytes.
|
|
34
|
+
* @returns The authenticated poll domain operation.
|
|
35
|
+
*/
|
|
36
|
+
export declare const makeRouterPoll: (input: PollDependencies) => RouterPoll;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/router/poll.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EACL,KAAK,OAAO,EACZ,mBAAmB,EACnB,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAY,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAEtB,KAAK,gBAAgB,IAAI,qBAAqB,EAC/C,MAAM,eAAe,CAAC;AACvB,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAE7E,KAAK,WAAW,GAAG,eAAe,GAAG,mBAAmB,CAAC;AAEzD,UAAU,cAAc;IACtB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,UAAU,gBAAgB;IACxB,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACxC;AAED,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,CACf,OAAO,EAAE,iBAAiB,EAC1B,eAAe,EAAE,cAAc,KAC5B,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,UAAW,gBAAgB,KAAG,UAIrD,CAAC"}
|