effect-actors 0.0.1
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 +21 -0
- package/README.md +467 -0
- package/dist/actor/client.d.ts +11 -0
- package/dist/actor/client.js +68 -0
- package/dist/actor/definition.d.ts +77 -0
- package/dist/actor/definition.js +110 -0
- package/dist/actor/state/codec.d.ts +10 -0
- package/dist/actor/state/codec.js +117 -0
- package/dist/actor/state/compiled.d.ts +17 -0
- package/dist/actor/state/compiled.js +1 -0
- package/dist/actor/state/layer.d.ts +7 -0
- package/dist/actor/state/layer.js +167 -0
- package/dist/client/http/errors.d.ts +30 -0
- package/dist/client/http/errors.js +72 -0
- package/dist/client/http/events.d.ts +10 -0
- package/dist/client/http/events.js +30 -0
- package/dist/client/http/layer.d.ts +5 -0
- package/dist/client/http/layer.js +133 -0
- package/dist/client/http.d.ts +1 -0
- package/dist/client/http.js +1 -0
- package/dist/client/outcome.d.ts +4 -0
- package/dist/client/outcome.js +10 -0
- package/dist/client/service.d.ts +28 -0
- package/dist/client/service.js +4 -0
- package/dist/errors/index.d.ts +93 -0
- package/dist/errors/index.js +93 -0
- package/dist/gateway/access.d.ts +11 -0
- package/dist/gateway/access.js +38 -0
- package/dist/gateway/dispatch.d.ts +13 -0
- package/dist/gateway/dispatch.js +45 -0
- package/dist/gateway/fault.d.ts +32 -0
- package/dist/gateway/fault.js +85 -0
- package/dist/gateway/http/events.d.ts +8 -0
- package/dist/gateway/http/events.js +32 -0
- package/dist/gateway/http/response.d.ts +11 -0
- package/dist/gateway/http/response.js +57 -0
- package/dist/gateway/http/routes.d.ts +6 -0
- package/dist/gateway/http/routes.js +184 -0
- package/dist/gateway/http/validation.d.ts +19 -0
- package/dist/gateway/http/validation.js +92 -0
- package/dist/gateway/options.d.ts +31 -0
- package/dist/gateway/options.js +107 -0
- package/dist/gateway/security.d.ts +25 -0
- package/dist/gateway/security.js +4 -0
- package/dist/gateway/service.d.ts +113 -0
- package/dist/gateway/service.js +5 -0
- package/dist/gateway/websocket/operations.d.ts +20 -0
- package/dist/gateway/websocket/operations.js +96 -0
- package/dist/gateway/websocket.d.ts +8 -0
- package/dist/gateway/websocket.js +96 -0
- package/dist/gateway/wire.d.ts +172 -0
- package/dist/gateway/wire.js +102 -0
- package/dist/host/client.d.ts +9 -0
- package/dist/host/client.js +46 -0
- package/dist/host/definition.d.ts +71 -0
- package/dist/host/definition.js +68 -0
- package/dist/host/entity.d.ts +9 -0
- package/dist/host/entity.js +302 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/protocol/encoding.d.ts +29 -0
- package/dist/protocol/encoding.js +165 -0
- package/dist/protocol/index.d.ts +90 -0
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/schemas.d.ts +49 -0
- package/dist/protocol/schemas.js +30 -0
- package/dist/runtime/cluster/address.d.ts +3 -0
- package/dist/runtime/cluster/address.js +21 -0
- package/dist/runtime/cluster/client.d.ts +9 -0
- package/dist/runtime/cluster/client.js +131 -0
- package/dist/runtime/cluster/protocol.d.ts +104 -0
- package/dist/runtime/cluster/protocol.js +69 -0
- package/dist/runtime/configuration.d.ts +4 -0
- package/dist/runtime/configuration.js +34 -0
- package/dist/runtime/dispatchers/layer.d.ts +6 -0
- package/dist/runtime/dispatchers/layer.js +53 -0
- package/dist/runtime/dispatchers/outbox.d.ts +4 -0
- package/dist/runtime/dispatchers/outbox.js +57 -0
- package/dist/runtime/layer.d.ts +6 -0
- package/dist/runtime/layer.js +43 -0
- package/dist/runtime/service.d.ts +40 -0
- package/dist/runtime/service.js +5 -0
- package/dist/store/memory/background.d.ts +39 -0
- package/dist/store/memory/background.js +206 -0
- package/dist/store/memory/model.d.ts +72 -0
- package/dist/store/memory/model.js +149 -0
- package/dist/store/memory/ownership.d.ts +11 -0
- package/dist/store/memory/ownership.js +82 -0
- package/dist/store/memory/service.d.ts +3 -0
- package/dist/store/memory/service.js +32 -0
- package/dist/store/memory/state.d.ts +13 -0
- package/dist/store/memory/state.js +109 -0
- package/dist/store/service.d.ts +89 -0
- package/dist/store/service.js +9 -0
- package/dist/store/sql/events.d.ts +13 -0
- package/dist/store/sql/events.js +89 -0
- package/dist/store/sql/model.d.ts +39 -0
- package/dist/store/sql/model.js +126 -0
- package/dist/store/sql/outbox.d.ts +15 -0
- package/dist/store/sql/outbox.js +130 -0
- package/dist/store/sql/ownership.d.ts +44 -0
- package/dist/store/sql/ownership.js +130 -0
- package/dist/store/sql/receipts.d.ts +25 -0
- package/dist/store/sql/receipts.js +93 -0
- package/dist/store/sql/schema.d.ts +9 -0
- package/dist/store/sql/schema.js +124 -0
- package/dist/store/sql/service.d.ts +5 -0
- package/dist/store/sql/service.js +42 -0
- package/dist/store/sql/state.d.ts +13 -0
- package/dist/store/sql/state.js +109 -0
- package/dist/store/sql/timers.d.ts +10 -0
- package/dist/store/sql/timers.js +90 -0
- package/dist/store/sql/transaction.d.ts +6 -0
- package/dist/store/sql/transaction.js +57 -0
- package/dist/store/sql/transition.d.ts +15 -0
- package/dist/store/sql/transition.js +154 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.js +27 -0
- package/package.json +80 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { ClientFrame as ClientFrameValue, ServerFrame as ServerFrameValue } from "./wire.js";
|
|
2
|
+
export { layer } from "./http/routes.js";
|
|
3
|
+
export { Security } from "./security.js";
|
|
4
|
+
export type { Options } from "./options.js";
|
|
5
|
+
export type { Access, Principal, SecurityService } from "./security.js";
|
|
6
|
+
export declare const ClientFrame: import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
7
|
+
readonly type: import("effect/Schema").Literal<"command">;
|
|
8
|
+
readonly requestId: import("effect/Schema").String;
|
|
9
|
+
readonly address: import("effect/Schema").Struct<{
|
|
10
|
+
readonly namespace: import("effect/Schema").String;
|
|
11
|
+
readonly tenant: import("effect/Schema").String;
|
|
12
|
+
readonly actorType: import("effect/Schema").String;
|
|
13
|
+
readonly key: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
14
|
+
}>;
|
|
15
|
+
readonly command: import("effect/Schema").String;
|
|
16
|
+
readonly protocolVersion: import("effect/Schema").Int;
|
|
17
|
+
readonly commandId: import("effect/Schema").String;
|
|
18
|
+
readonly expectedRevision: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
19
|
+
readonly payload: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
20
|
+
}>, import("effect/Schema").Struct<{
|
|
21
|
+
readonly type: import("effect/Schema").Literal<"query">;
|
|
22
|
+
readonly requestId: import("effect/Schema").String;
|
|
23
|
+
readonly address: import("effect/Schema").Struct<{
|
|
24
|
+
readonly namespace: import("effect/Schema").String;
|
|
25
|
+
readonly tenant: import("effect/Schema").String;
|
|
26
|
+
readonly actorType: import("effect/Schema").String;
|
|
27
|
+
readonly key: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
28
|
+
}>;
|
|
29
|
+
readonly query: import("effect/Schema").String;
|
|
30
|
+
readonly protocolVersion: import("effect/Schema").Int;
|
|
31
|
+
readonly payload: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
32
|
+
}>, import("effect/Schema").Struct<{
|
|
33
|
+
readonly type: import("effect/Schema").Literal<"subscribe">;
|
|
34
|
+
readonly subscriptionId: import("effect/Schema").String;
|
|
35
|
+
readonly address: import("effect/Schema").Struct<{
|
|
36
|
+
readonly namespace: import("effect/Schema").String;
|
|
37
|
+
readonly tenant: import("effect/Schema").String;
|
|
38
|
+
readonly actorType: import("effect/Schema").String;
|
|
39
|
+
readonly key: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
40
|
+
}>;
|
|
41
|
+
readonly after: import("effect/Schema").String;
|
|
42
|
+
}>, import("effect/Schema").Struct<{
|
|
43
|
+
readonly type: import("effect/Schema").Literal<"unsubscribe">;
|
|
44
|
+
readonly subscriptionId: import("effect/Schema").String;
|
|
45
|
+
}>]>;
|
|
46
|
+
export type ClientFrame = ClientFrameValue;
|
|
47
|
+
export declare const ServerFrame: import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
48
|
+
readonly type: import("effect/Schema").Literal<"command-result">;
|
|
49
|
+
readonly requestId: import("effect/Schema").String;
|
|
50
|
+
readonly receipt: import("effect/Schema").Struct<{
|
|
51
|
+
readonly commandId: import("effect/Schema").String;
|
|
52
|
+
readonly revision: import("effect/Schema").String;
|
|
53
|
+
readonly cursor: import("effect/Schema").String;
|
|
54
|
+
readonly committedAtMillis: import("effect/Schema").Number;
|
|
55
|
+
readonly value: import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
56
|
+
readonly _tag: import("effect/Schema").tag<"Success">;
|
|
57
|
+
readonly value: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
58
|
+
}>, import("effect/Schema").Struct<{
|
|
59
|
+
readonly _tag: import("effect/Schema").tag<"Failure">;
|
|
60
|
+
readonly error: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
61
|
+
}>]>;
|
|
62
|
+
}>;
|
|
63
|
+
}>, import("effect/Schema").Struct<{
|
|
64
|
+
readonly type: import("effect/Schema").Literal<"query-result">;
|
|
65
|
+
readonly requestId: import("effect/Schema").String;
|
|
66
|
+
readonly result: import("effect/Schema").Struct<{
|
|
67
|
+
readonly revision: import("effect/Schema").String;
|
|
68
|
+
readonly cursor: import("effect/Schema").String;
|
|
69
|
+
readonly value: import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
70
|
+
readonly _tag: import("effect/Schema").tag<"Success">;
|
|
71
|
+
readonly value: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
72
|
+
}>, import("effect/Schema").Struct<{
|
|
73
|
+
readonly _tag: import("effect/Schema").tag<"Failure">;
|
|
74
|
+
readonly error: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
75
|
+
}>]>;
|
|
76
|
+
}>;
|
|
77
|
+
}>, import("effect/Schema").Struct<{
|
|
78
|
+
readonly type: import("effect/Schema").Literal<"event">;
|
|
79
|
+
readonly subscriptionId: import("effect/Schema").String;
|
|
80
|
+
readonly event: import("effect/Schema").Struct<{
|
|
81
|
+
readonly cursor: import("effect/Schema").String;
|
|
82
|
+
readonly commandId: import("effect/Schema").String;
|
|
83
|
+
readonly committedAtMillis: import("effect/Schema").Number;
|
|
84
|
+
readonly value: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
|
|
85
|
+
}>;
|
|
86
|
+
}>, import("effect/Schema").Struct<{
|
|
87
|
+
readonly type: import("effect/Schema").Literal<"subscribed">;
|
|
88
|
+
readonly subscriptionId: import("effect/Schema").String;
|
|
89
|
+
readonly after: import("effect/Schema").String;
|
|
90
|
+
}>, import("effect/Schema").Struct<{
|
|
91
|
+
readonly type: import("effect/Schema").Literal<"subscription-error">;
|
|
92
|
+
readonly subscriptionId: import("effect/Schema").String;
|
|
93
|
+
readonly error: import("effect/Schema").Struct<{
|
|
94
|
+
readonly code: import("effect/Schema").String;
|
|
95
|
+
readonly message: import("effect/Schema").String;
|
|
96
|
+
readonly commandId: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
97
|
+
readonly expected: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
98
|
+
readonly actual: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
99
|
+
readonly retryAfterMillis: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
100
|
+
}>;
|
|
101
|
+
}>, import("effect/Schema").Struct<{
|
|
102
|
+
readonly type: import("effect/Schema").Literal<"request-error">;
|
|
103
|
+
readonly requestId: import("effect/Schema").String;
|
|
104
|
+
readonly error: import("effect/Schema").Struct<{
|
|
105
|
+
readonly code: import("effect/Schema").String;
|
|
106
|
+
readonly message: import("effect/Schema").String;
|
|
107
|
+
readonly commandId: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
108
|
+
readonly expected: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
109
|
+
readonly actual: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
110
|
+
readonly retryAfterMillis: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
111
|
+
}>;
|
|
112
|
+
}>]>;
|
|
113
|
+
export type ServerFrame = ServerFrameValue;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ClientFrame as ClientFrameSchema, ServerFrame as ServerFrameSchema } from "./wire.js";
|
|
2
|
+
export { layer } from "./http/routes.js";
|
|
3
|
+
export { Security } from "./security.js";
|
|
4
|
+
export const ClientFrame = ClientFrameSchema;
|
|
5
|
+
export const ServerFrame = ServerFrameSchema;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect, Fiber } from "effect";
|
|
2
|
+
import type { Scope } from "effect";
|
|
3
|
+
import type { Service as ClientService } from "../../client/service.js";
|
|
4
|
+
import type { GatewayFault } from "../fault.js";
|
|
5
|
+
import type { Registry } from "../options.js";
|
|
6
|
+
import type { SecurityService } from "../security.js";
|
|
7
|
+
import type { ClientFrame as IncomingFrame, ServerFrame as OutgoingFrame } from "../wire.js";
|
|
8
|
+
export type Send = (frame: OutgoingFrame) => Effect.Effect<void, GatewayFault>;
|
|
9
|
+
export type Close = (code: number, reason: string) => Effect.Effect<void>;
|
|
10
|
+
export declare const sendRequestFailure: (send: Send, requestId: string, fault: GatewayFault) => Effect.Effect<void, GatewayFault>;
|
|
11
|
+
export declare const sendSubscriptionFailure: (send: Send, subscriptionId: string, fault: GatewayFault) => Effect.Effect<void, GatewayFault>;
|
|
12
|
+
export declare const command: (frame: Extract<IncomingFrame, {
|
|
13
|
+
readonly type: "command";
|
|
14
|
+
}>, registry: Registry, client: ClientService, security: SecurityService, request: Parameters<SecurityService["authenticate"]>[0], send: Send) => Effect.Effect<void, GatewayFault>;
|
|
15
|
+
export declare const query: (frame: Extract<IncomingFrame, {
|
|
16
|
+
readonly type: "query";
|
|
17
|
+
}>, registry: Registry, client: ClientService, security: SecurityService, request: Parameters<SecurityService["authenticate"]>[0], send: Send) => Effect.Effect<void, GatewayFault>;
|
|
18
|
+
export declare const subscribe: (frame: Extract<IncomingFrame, {
|
|
19
|
+
readonly type: "subscribe";
|
|
20
|
+
}>, registry: Registry, client: ClientService, security: SecurityService, request: Parameters<SecurityService["authenticate"]>[0], send: Send, subscriptions: Map<string, Fiber.Fiber<void, never>>, release: Effect.Effect<void>, close: Close) => Effect.Effect<void, GatewayFault, Scope.Scope>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Effect, Fiber, Stream } from "effect";
|
|
2
|
+
import { boundary, fingerprint } from "../../protocol/index.js";
|
|
3
|
+
import { boundedEvents } from "../http/events.js";
|
|
4
|
+
import { validateCommandId, validateCursor, validatePublicName, validateRevision, validateRequestId, } from "../http/validation.js";
|
|
5
|
+
import { admit, assertTenant, authenticate, authorize, recheck } from "../access.js";
|
|
6
|
+
import { mapInputError, stateProcedure, validateAddress, validatePayload, validateQueryResult, validateReceipt, } from "../dispatch.js";
|
|
7
|
+
import { fromServiceError, invalidInput, notFound } from "../fault.js";
|
|
8
|
+
const errorFrame = (fault) => ({
|
|
9
|
+
code: fault.code,
|
|
10
|
+
message: fault.message,
|
|
11
|
+
...(fault.commandId === undefined ? {} : { commandId: fault.commandId }),
|
|
12
|
+
...(fault.expected === undefined ? {} : { expected: fault.expected }),
|
|
13
|
+
...(fault.actual === undefined ? {} : { actual: fault.actual }),
|
|
14
|
+
...(fault.retryAfterMillis === undefined ? {} : { retryAfterMillis: fault.retryAfterMillis }),
|
|
15
|
+
});
|
|
16
|
+
export const sendRequestFailure = (send, requestId, fault) => fault.code === "SlowConsumer"
|
|
17
|
+
? Effect.void
|
|
18
|
+
: send({ type: "request-error", requestId, error: errorFrame(fault) });
|
|
19
|
+
export const sendSubscriptionFailure = (send, subscriptionId, fault) => fault.code === "SlowConsumer"
|
|
20
|
+
? Effect.void
|
|
21
|
+
: send({ type: "subscription-error", subscriptionId, error: errorFrame(fault) });
|
|
22
|
+
export const command = (frame, registry, client, security, request, send) => Effect.gen(function* () {
|
|
23
|
+
const requestId = yield* validateRequestId(frame.requestId);
|
|
24
|
+
const commandId = yield* validateCommandId(frame.commandId);
|
|
25
|
+
const expectedRevision = frame.expectedRevision === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: yield* validateRevision(frame.expectedRevision);
|
|
28
|
+
const address = yield* validateAddress(registry, frame.address);
|
|
29
|
+
const commandName = yield* validatePublicName(frame.command, registry.isPublicOperation);
|
|
30
|
+
const procedure = yield* stateProcedure(registry, address, frame.protocolVersion, commandName, "command");
|
|
31
|
+
const principal = yield* authenticate(security, request);
|
|
32
|
+
yield* assertTenant(principal, address);
|
|
33
|
+
const access = { address, operation: "command", name: commandName };
|
|
34
|
+
yield* authorize(security, principal, access);
|
|
35
|
+
yield* admit(security, principal, access);
|
|
36
|
+
const payload = yield* validatePayload(procedure, frame.payload);
|
|
37
|
+
const input = yield* boundary(() => ({
|
|
38
|
+
address,
|
|
39
|
+
command: commandName,
|
|
40
|
+
protocolVersion: frame.protocolVersion,
|
|
41
|
+
payload,
|
|
42
|
+
commandId,
|
|
43
|
+
fingerprint: fingerprint({
|
|
44
|
+
command: commandName,
|
|
45
|
+
protocolVersion: frame.protocolVersion,
|
|
46
|
+
payload,
|
|
47
|
+
...(expectedRevision === undefined ? {} : { expectedRevision }),
|
|
48
|
+
}),
|
|
49
|
+
...(expectedRevision === undefined ? {} : { expectedRevision }),
|
|
50
|
+
})).pipe(Effect.mapError(mapInputError));
|
|
51
|
+
const receipt = yield* client
|
|
52
|
+
.command(input)
|
|
53
|
+
.pipe(Effect.mapError((error) => fromServiceError(error, commandId)));
|
|
54
|
+
const checked = yield* validateReceipt(procedure, address, receipt);
|
|
55
|
+
yield* send({ type: "command-result", requestId, receipt: checked });
|
|
56
|
+
});
|
|
57
|
+
export const query = (frame, registry, client, security, request, send) => Effect.gen(function* () {
|
|
58
|
+
const requestId = yield* validateRequestId(frame.requestId);
|
|
59
|
+
const address = yield* validateAddress(registry, frame.address);
|
|
60
|
+
const queryName = yield* validatePublicName(frame.query, registry.isPublicOperation);
|
|
61
|
+
const procedure = yield* stateProcedure(registry, address, frame.protocolVersion, queryName, "query");
|
|
62
|
+
const principal = yield* authenticate(security, request);
|
|
63
|
+
yield* assertTenant(principal, address);
|
|
64
|
+
const access = { address, operation: "query", name: queryName };
|
|
65
|
+
yield* authorize(security, principal, access);
|
|
66
|
+
yield* admit(security, principal, access);
|
|
67
|
+
const payload = yield* validatePayload(procedure, frame.payload);
|
|
68
|
+
const result = yield* client
|
|
69
|
+
.query({ address, query: queryName, protocolVersion: frame.protocolVersion, payload })
|
|
70
|
+
.pipe(Effect.mapError(fromServiceError));
|
|
71
|
+
const checked = yield* validateQueryResult(procedure, address, result);
|
|
72
|
+
yield* send({ type: "query-result", requestId, result: checked });
|
|
73
|
+
});
|
|
74
|
+
export const subscribe = (frame, registry, client, security, request, send, subscriptions, release, close) => Effect.gen(function* () {
|
|
75
|
+
const subscriptionId = yield* validateRequestId(frame.subscriptionId);
|
|
76
|
+
if (subscriptions.has(subscriptionId))
|
|
77
|
+
return yield* Effect.fail(invalidInput());
|
|
78
|
+
const address = yield* validateAddress(registry, frame.address);
|
|
79
|
+
const protocol = registry.get(address.actorType);
|
|
80
|
+
if (!protocol || protocol.kind !== "state")
|
|
81
|
+
return yield* Effect.fail(notFound());
|
|
82
|
+
const after = yield* validateCursor(address, frame.after);
|
|
83
|
+
const principal = yield* authenticate(security, request);
|
|
84
|
+
yield* assertTenant(principal, address);
|
|
85
|
+
const access = { address, operation: "events" };
|
|
86
|
+
yield* authorize(security, principal, access);
|
|
87
|
+
yield* admit(security, principal, access);
|
|
88
|
+
const events = yield* boundedEvents(client, address, after, protocol.eventSchema, registry.options, recheck(security, request, principal, access));
|
|
89
|
+
yield* send({ type: "subscribed", subscriptionId, after });
|
|
90
|
+
const fiber = yield* Stream.runForEach(events, (event) => send({ type: "event", subscriptionId, event })).pipe(Effect.catchIf((fault) => true, (fault) => {
|
|
91
|
+
if (fault.code === "SlowConsumer")
|
|
92
|
+
return close(1013, "slow consumer");
|
|
93
|
+
return sendSubscriptionFailure(send, subscriptionId, fault).pipe(Effect.catchIf((sendFault) => true, () => Effect.void));
|
|
94
|
+
}), Effect.ensuring(Effect.sync(() => subscriptions.delete(subscriptionId)).pipe(Effect.andThen(release))), Effect.forkScoped);
|
|
95
|
+
subscriptions.set(subscriptionId, fiber);
|
|
96
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect, Ref } from "effect";
|
|
2
|
+
import type { Scope } from "effect";
|
|
3
|
+
import { HttpServerResponse } from "effect/unstable/http";
|
|
4
|
+
import type { Service as ClientService } from "../client/service.js";
|
|
5
|
+
import type { GatewayFault } from "./fault.js";
|
|
6
|
+
import type { Registry } from "./options.js";
|
|
7
|
+
import type { SecurityService } from "./security.js";
|
|
8
|
+
export declare const handler: (registry: Registry, client: ClientService, security: SecurityService, connections: Ref.Ref<ReadonlyMap<string, number>>) => (request: Parameters<SecurityService["authenticate"]>[0]) => Effect.Effect<HttpServerResponse.HttpServerResponse, GatewayFault, Scope.Scope>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Effect, Encoding, Fiber, Queue, Ref, Schema, Semaphore, Stream } from "effect";
|
|
2
|
+
import { HttpServerResponse } from "effect/unstable/http";
|
|
3
|
+
import { Socket } from "effect/unstable/socket";
|
|
4
|
+
import { boundary, decode } from "../protocol/index.js";
|
|
5
|
+
import { validateRequestId } from "./http/validation.js";
|
|
6
|
+
import { authenticate, leaseConnection, recheck } from "./access.js";
|
|
7
|
+
import { mapInputError } from "./dispatch.js";
|
|
8
|
+
import { overloaded, unauthorized, unavailable } from "./fault.js";
|
|
9
|
+
import { command, query, sendRequestFailure, sendSubscriptionFailure, subscribe, } from "./websocket/operations.js";
|
|
10
|
+
import { ClientFrame, ServerFrame } from "./wire.js";
|
|
11
|
+
const frameSize = (frame) => Encoding.encodeBase64(frame).length;
|
|
12
|
+
const decodeFrame = (frame, maximum) => {
|
|
13
|
+
if (frameSize(frame) > Math.ceil(maximum / 3) * 4 + 4) {
|
|
14
|
+
return Effect.fail({
|
|
15
|
+
_tag: "GatewayFault",
|
|
16
|
+
code: "InvalidInput",
|
|
17
|
+
status: 413,
|
|
18
|
+
message: "WebSocket frame is too large",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return boundary(() => JSON.parse(frame)).pipe(Effect.flatMap((parsed) => decode(ClientFrame, parsed)), Effect.mapError(mapInputError));
|
|
22
|
+
};
|
|
23
|
+
const encodeFrame = (frame) => boundary(() => JSON.stringify(Schema.encodeSync(ServerFrame)(frame))).pipe(Effect.mapError(() => unavailable()));
|
|
24
|
+
const isExpectedSocketClose = (error) => Socket.SocketError.is(error) &&
|
|
25
|
+
error.reason._tag === "SocketCloseError" &&
|
|
26
|
+
[1000, 1001, 1003, 1007, 1008, 1009, 1013].includes(error.reason.code);
|
|
27
|
+
export const handler = (registry, client, security, connections) => (request) => Effect.gen(function* () {
|
|
28
|
+
if (request.headers.origin === undefined ||
|
|
29
|
+
!registry.options.allowedOrigins.includes(request.headers.origin)) {
|
|
30
|
+
return yield* Effect.fail(unauthorized());
|
|
31
|
+
}
|
|
32
|
+
const principal = yield* authenticate(security, request);
|
|
33
|
+
yield* leaseConnection(connections, principal, registry.options.maxConnectionsPerPrincipal);
|
|
34
|
+
const socket = yield* request.upgrade.pipe(Effect.mapError(() => unavailable()));
|
|
35
|
+
const writer = yield* socket.writer;
|
|
36
|
+
const outgoing = yield* Queue.make({
|
|
37
|
+
capacity: registry.options.maxBufferedEvents,
|
|
38
|
+
strategy: "dropping",
|
|
39
|
+
});
|
|
40
|
+
yield* Effect.addFinalizer(() => Queue.shutdown(outgoing).pipe(Effect.asVoid));
|
|
41
|
+
const close = (code, reason) => writer(new Socket.CloseEvent(code, reason)).pipe(Effect.catchIf((error) => true, () => Effect.void));
|
|
42
|
+
const send = (frame) => Queue.offer(outgoing, frame).pipe(Effect.flatMap((accepted) => {
|
|
43
|
+
if (accepted)
|
|
44
|
+
return Effect.void;
|
|
45
|
+
const fault = {
|
|
46
|
+
_tag: "GatewayFault",
|
|
47
|
+
code: "SlowConsumer",
|
|
48
|
+
status: 503,
|
|
49
|
+
message: "Event consumer is too slow",
|
|
50
|
+
};
|
|
51
|
+
return close(1013, "slow consumer").pipe(Effect.andThen(Effect.fail(fault)));
|
|
52
|
+
}));
|
|
53
|
+
const output = Stream.runForEach(Stream.fromQueue(outgoing), (frame) => encodeFrame(frame).pipe(Effect.flatMap((encoded) => writer(encoded).pipe(Effect.mapError(() => unavailable())))));
|
|
54
|
+
yield* Effect.forkScoped(output);
|
|
55
|
+
const watchdog = Effect.forever(Effect.sleep(registry.options.authenticationRecheckMillis).pipe(Effect.andThen(recheck(security, request, principal)))).pipe(Effect.catchIf((fault) => true, () => close(1008, "authentication expired")));
|
|
56
|
+
yield* Effect.forkScoped(watchdog);
|
|
57
|
+
const subscriptions = new Map();
|
|
58
|
+
const requests = yield* Semaphore.make(registry.maxInFlightRequestsPerConnection);
|
|
59
|
+
const subscriptionSlots = yield* Semaphore.make(registry.maxSubscriptionsPerConnection);
|
|
60
|
+
const handle = (frame) => {
|
|
61
|
+
if (frame.type === "unsubscribe") {
|
|
62
|
+
return validateRequestId(frame.subscriptionId).pipe(Effect.flatMap((subscriptionId) => {
|
|
63
|
+
const subscription = subscriptions.get(subscriptionId);
|
|
64
|
+
return subscription === undefined
|
|
65
|
+
? Effect.void
|
|
66
|
+
: Fiber.interrupt(subscription).pipe(Effect.asVoid);
|
|
67
|
+
}), Effect.catchIf((fault) => true, () => close(1007, "invalid frame")));
|
|
68
|
+
}
|
|
69
|
+
if (frame.type === "subscribe") {
|
|
70
|
+
return subscriptionSlots.takeIfAvailable(1).pipe(Effect.flatMap((acquired) => {
|
|
71
|
+
if (!acquired)
|
|
72
|
+
return sendSubscriptionFailure(send, frame.subscriptionId, overloaded(1_000)).pipe(Effect.catchIf((fault) => true, () => Effect.void));
|
|
73
|
+
const release = subscriptionSlots.release(1).pipe(Effect.asVoid);
|
|
74
|
+
return subscribe(frame, registry, client, security, request, send, subscriptions, release, close).pipe(Effect.catchIf((fault) => true, (fault) => release.pipe(Effect.andThen(sendSubscriptionFailure(send, frame.subscriptionId, fault)))), Effect.catchIf((fault) => true, () => Effect.void));
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
const work = frame.type === "command"
|
|
78
|
+
? command(frame, registry, client, security, request, send)
|
|
79
|
+
: query(frame, registry, client, security, request, send);
|
|
80
|
+
return requests.takeIfAvailable(1).pipe(Effect.flatMap((acquired) => {
|
|
81
|
+
if (!acquired)
|
|
82
|
+
return sendRequestFailure(send, frame.requestId, overloaded(1_000)).pipe(Effect.catchIf((fault) => true, () => Effect.void));
|
|
83
|
+
return work.pipe(Effect.catchIf((fault) => true, (fault) => sendRequestFailure(send, frame.requestId, fault)), Effect.catchIf((fault) => true, () => Effect.void), Effect.ensuring(requests.release(1).pipe(Effect.asVoid)), Effect.forkScoped, Effect.asVoid);
|
|
84
|
+
}));
|
|
85
|
+
};
|
|
86
|
+
yield* socket
|
|
87
|
+
.runRaw((raw) => {
|
|
88
|
+
if (typeof raw !== "string")
|
|
89
|
+
return close(1003, "text frames required").pipe(Effect.asVoid);
|
|
90
|
+
return decodeFrame(raw, registry.options.maxRequestBytes)
|
|
91
|
+
.pipe(Effect.flatMap(handle))
|
|
92
|
+
.pipe(Effect.catchIf((fault) => true, (fault) => close(fault.status === 413 ? 1009 : 1007, "invalid frame")));
|
|
93
|
+
})
|
|
94
|
+
.pipe(Effect.catchIf(isExpectedSocketClose, () => Effect.void), Effect.mapError(() => unavailable()));
|
|
95
|
+
return HttpServerResponse.empty();
|
|
96
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Event, Outcome, QueryResult, Receipt as WireReceipt } from "../protocol/schemas.js";
|
|
3
|
+
export { Event, Outcome, QueryResult, WireReceipt };
|
|
4
|
+
export declare const Address: Schema.Struct<{
|
|
5
|
+
readonly namespace: Schema.String;
|
|
6
|
+
readonly tenant: Schema.String;
|
|
7
|
+
readonly actorType: Schema.String;
|
|
8
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const CommandRequest: Schema.Struct<{
|
|
11
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
12
|
+
readonly protocolVersion: Schema.Int;
|
|
13
|
+
readonly commandId: Schema.String;
|
|
14
|
+
readonly expectedRevision: Schema.optionalKey<Schema.String>;
|
|
15
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const QueryRequest: Schema.Struct<{
|
|
18
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
19
|
+
readonly protocolVersion: Schema.Int;
|
|
20
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const ReceiptRequest: Schema.Struct<{
|
|
23
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const HostedRequest: Schema.Struct<{
|
|
26
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
27
|
+
readonly protocolVersion: Schema.Int;
|
|
28
|
+
readonly requestKey: Schema.optionalKey<Schema.String>;
|
|
29
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
30
|
+
}>;
|
|
31
|
+
export declare const GatewayError: Schema.Struct<{
|
|
32
|
+
readonly code: Schema.String;
|
|
33
|
+
readonly message: Schema.String;
|
|
34
|
+
readonly commandId: Schema.optionalKey<Schema.String>;
|
|
35
|
+
readonly expected: Schema.optionalKey<Schema.String>;
|
|
36
|
+
readonly actual: Schema.optionalKey<Schema.String>;
|
|
37
|
+
readonly retryAfterMillis: Schema.optionalKey<Schema.Number>;
|
|
38
|
+
}>;
|
|
39
|
+
export type GatewayError = (typeof GatewayError)["Type"];
|
|
40
|
+
export declare const GatewayErrorResponse: Schema.Struct<{
|
|
41
|
+
readonly error: Schema.Struct<{
|
|
42
|
+
readonly code: Schema.String;
|
|
43
|
+
readonly message: Schema.String;
|
|
44
|
+
readonly commandId: Schema.optionalKey<Schema.String>;
|
|
45
|
+
readonly expected: Schema.optionalKey<Schema.String>;
|
|
46
|
+
readonly actual: Schema.optionalKey<Schema.String>;
|
|
47
|
+
readonly retryAfterMillis: Schema.optionalKey<Schema.Number>;
|
|
48
|
+
}>;
|
|
49
|
+
}>;
|
|
50
|
+
export declare const ReceiptResponse: Schema.Struct<{
|
|
51
|
+
readonly receipt: Schema.NullOr<Schema.Struct<{
|
|
52
|
+
readonly commandId: Schema.String;
|
|
53
|
+
readonly revision: Schema.String;
|
|
54
|
+
readonly cursor: Schema.String;
|
|
55
|
+
readonly committedAtMillis: Schema.Number;
|
|
56
|
+
readonly value: Schema.Union<readonly [Schema.Struct<{
|
|
57
|
+
readonly _tag: Schema.tag<"Success">;
|
|
58
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
59
|
+
}>, Schema.Struct<{
|
|
60
|
+
readonly _tag: Schema.tag<"Failure">;
|
|
61
|
+
readonly error: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
62
|
+
}>]>;
|
|
63
|
+
}>>;
|
|
64
|
+
}>;
|
|
65
|
+
export declare const ClientFrame: Schema.Union<readonly [Schema.Struct<{
|
|
66
|
+
readonly type: Schema.Literal<"command">;
|
|
67
|
+
readonly requestId: Schema.String;
|
|
68
|
+
readonly address: Schema.Struct<{
|
|
69
|
+
readonly namespace: Schema.String;
|
|
70
|
+
readonly tenant: Schema.String;
|
|
71
|
+
readonly actorType: Schema.String;
|
|
72
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
73
|
+
}>;
|
|
74
|
+
readonly command: Schema.String;
|
|
75
|
+
readonly protocolVersion: Schema.Int;
|
|
76
|
+
readonly commandId: Schema.String;
|
|
77
|
+
readonly expectedRevision: Schema.optionalKey<Schema.String>;
|
|
78
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
79
|
+
}>, Schema.Struct<{
|
|
80
|
+
readonly type: Schema.Literal<"query">;
|
|
81
|
+
readonly requestId: Schema.String;
|
|
82
|
+
readonly address: Schema.Struct<{
|
|
83
|
+
readonly namespace: Schema.String;
|
|
84
|
+
readonly tenant: Schema.String;
|
|
85
|
+
readonly actorType: Schema.String;
|
|
86
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
87
|
+
}>;
|
|
88
|
+
readonly query: Schema.String;
|
|
89
|
+
readonly protocolVersion: Schema.Int;
|
|
90
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
91
|
+
}>, Schema.Struct<{
|
|
92
|
+
readonly type: Schema.Literal<"subscribe">;
|
|
93
|
+
readonly subscriptionId: Schema.String;
|
|
94
|
+
readonly address: Schema.Struct<{
|
|
95
|
+
readonly namespace: Schema.String;
|
|
96
|
+
readonly tenant: Schema.String;
|
|
97
|
+
readonly actorType: Schema.String;
|
|
98
|
+
readonly key: Schema.$Array<Schema.String>;
|
|
99
|
+
}>;
|
|
100
|
+
readonly after: Schema.String;
|
|
101
|
+
}>, Schema.Struct<{
|
|
102
|
+
readonly type: Schema.Literal<"unsubscribe">;
|
|
103
|
+
readonly subscriptionId: Schema.String;
|
|
104
|
+
}>]>;
|
|
105
|
+
export type ClientFrame = (typeof ClientFrame)["Type"];
|
|
106
|
+
export declare const ServerFrame: Schema.Union<readonly [Schema.Struct<{
|
|
107
|
+
readonly type: Schema.Literal<"command-result">;
|
|
108
|
+
readonly requestId: Schema.String;
|
|
109
|
+
readonly receipt: Schema.Struct<{
|
|
110
|
+
readonly commandId: Schema.String;
|
|
111
|
+
readonly revision: Schema.String;
|
|
112
|
+
readonly cursor: Schema.String;
|
|
113
|
+
readonly committedAtMillis: Schema.Number;
|
|
114
|
+
readonly value: Schema.Union<readonly [Schema.Struct<{
|
|
115
|
+
readonly _tag: Schema.tag<"Success">;
|
|
116
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
117
|
+
}>, Schema.Struct<{
|
|
118
|
+
readonly _tag: Schema.tag<"Failure">;
|
|
119
|
+
readonly error: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
120
|
+
}>]>;
|
|
121
|
+
}>;
|
|
122
|
+
}>, Schema.Struct<{
|
|
123
|
+
readonly type: Schema.Literal<"query-result">;
|
|
124
|
+
readonly requestId: Schema.String;
|
|
125
|
+
readonly result: Schema.Struct<{
|
|
126
|
+
readonly revision: Schema.String;
|
|
127
|
+
readonly cursor: Schema.String;
|
|
128
|
+
readonly value: Schema.Union<readonly [Schema.Struct<{
|
|
129
|
+
readonly _tag: Schema.tag<"Success">;
|
|
130
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
131
|
+
}>, Schema.Struct<{
|
|
132
|
+
readonly _tag: Schema.tag<"Failure">;
|
|
133
|
+
readonly error: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
134
|
+
}>]>;
|
|
135
|
+
}>;
|
|
136
|
+
}>, Schema.Struct<{
|
|
137
|
+
readonly type: Schema.Literal<"event">;
|
|
138
|
+
readonly subscriptionId: Schema.String;
|
|
139
|
+
readonly event: Schema.Struct<{
|
|
140
|
+
readonly cursor: Schema.String;
|
|
141
|
+
readonly commandId: Schema.String;
|
|
142
|
+
readonly committedAtMillis: Schema.Number;
|
|
143
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
144
|
+
}>;
|
|
145
|
+
}>, Schema.Struct<{
|
|
146
|
+
readonly type: Schema.Literal<"subscribed">;
|
|
147
|
+
readonly subscriptionId: Schema.String;
|
|
148
|
+
readonly after: Schema.String;
|
|
149
|
+
}>, Schema.Struct<{
|
|
150
|
+
readonly type: Schema.Literal<"subscription-error">;
|
|
151
|
+
readonly subscriptionId: Schema.String;
|
|
152
|
+
readonly error: Schema.Struct<{
|
|
153
|
+
readonly code: Schema.String;
|
|
154
|
+
readonly message: Schema.String;
|
|
155
|
+
readonly commandId: Schema.optionalKey<Schema.String>;
|
|
156
|
+
readonly expected: Schema.optionalKey<Schema.String>;
|
|
157
|
+
readonly actual: Schema.optionalKey<Schema.String>;
|
|
158
|
+
readonly retryAfterMillis: Schema.optionalKey<Schema.Number>;
|
|
159
|
+
}>;
|
|
160
|
+
}>, Schema.Struct<{
|
|
161
|
+
readonly type: Schema.Literal<"request-error">;
|
|
162
|
+
readonly requestId: Schema.String;
|
|
163
|
+
readonly error: Schema.Struct<{
|
|
164
|
+
readonly code: Schema.String;
|
|
165
|
+
readonly message: Schema.String;
|
|
166
|
+
readonly commandId: Schema.optionalKey<Schema.String>;
|
|
167
|
+
readonly expected: Schema.optionalKey<Schema.String>;
|
|
168
|
+
readonly actual: Schema.optionalKey<Schema.String>;
|
|
169
|
+
readonly retryAfterMillis: Schema.optionalKey<Schema.Number>;
|
|
170
|
+
}>;
|
|
171
|
+
}>]>;
|
|
172
|
+
export type ServerFrame = (typeof ServerFrame)["Type"];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Event, Outcome, QueryResult, Receipt as WireReceipt } from "../protocol/schemas.js";
|
|
3
|
+
export { Event, Outcome, QueryResult, WireReceipt };
|
|
4
|
+
export const Address = Schema.Struct({
|
|
5
|
+
namespace: Schema.String,
|
|
6
|
+
tenant: Schema.String,
|
|
7
|
+
actorType: Schema.String,
|
|
8
|
+
key: Schema.Array(Schema.String),
|
|
9
|
+
});
|
|
10
|
+
export const CommandRequest = Schema.Struct({
|
|
11
|
+
key: Schema.Array(Schema.String),
|
|
12
|
+
protocolVersion: Schema.Int,
|
|
13
|
+
commandId: Schema.String,
|
|
14
|
+
expectedRevision: Schema.optionalKey(Schema.String),
|
|
15
|
+
payload: Schema.Json,
|
|
16
|
+
});
|
|
17
|
+
export const QueryRequest = Schema.Struct({
|
|
18
|
+
key: Schema.Array(Schema.String),
|
|
19
|
+
protocolVersion: Schema.Int,
|
|
20
|
+
payload: Schema.Json,
|
|
21
|
+
});
|
|
22
|
+
export const ReceiptRequest = Schema.Struct({
|
|
23
|
+
key: Schema.Array(Schema.String),
|
|
24
|
+
});
|
|
25
|
+
export const HostedRequest = Schema.Struct({
|
|
26
|
+
key: Schema.Array(Schema.String),
|
|
27
|
+
protocolVersion: Schema.Int,
|
|
28
|
+
requestKey: Schema.optionalKey(Schema.String),
|
|
29
|
+
payload: Schema.Json,
|
|
30
|
+
});
|
|
31
|
+
export const GatewayError = Schema.Struct({
|
|
32
|
+
code: Schema.String,
|
|
33
|
+
message: Schema.String,
|
|
34
|
+
commandId: Schema.optionalKey(Schema.String),
|
|
35
|
+
expected: Schema.optionalKey(Schema.String),
|
|
36
|
+
actual: Schema.optionalKey(Schema.String),
|
|
37
|
+
retryAfterMillis: Schema.optionalKey(Schema.Number),
|
|
38
|
+
});
|
|
39
|
+
export const GatewayErrorResponse = Schema.Struct({ error: GatewayError });
|
|
40
|
+
export const ReceiptResponse = Schema.Struct({ receipt: Schema.NullOr(WireReceipt) });
|
|
41
|
+
export const ClientFrame = Schema.Union([
|
|
42
|
+
Schema.Struct({
|
|
43
|
+
type: Schema.Literal("command"),
|
|
44
|
+
requestId: Schema.String,
|
|
45
|
+
address: Address,
|
|
46
|
+
command: Schema.String,
|
|
47
|
+
protocolVersion: Schema.Int,
|
|
48
|
+
commandId: Schema.String,
|
|
49
|
+
expectedRevision: Schema.optionalKey(Schema.String),
|
|
50
|
+
payload: Schema.Json,
|
|
51
|
+
}),
|
|
52
|
+
Schema.Struct({
|
|
53
|
+
type: Schema.Literal("query"),
|
|
54
|
+
requestId: Schema.String,
|
|
55
|
+
address: Address,
|
|
56
|
+
query: Schema.String,
|
|
57
|
+
protocolVersion: Schema.Int,
|
|
58
|
+
payload: Schema.Json,
|
|
59
|
+
}),
|
|
60
|
+
Schema.Struct({
|
|
61
|
+
type: Schema.Literal("subscribe"),
|
|
62
|
+
subscriptionId: Schema.String,
|
|
63
|
+
address: Address,
|
|
64
|
+
after: Schema.String,
|
|
65
|
+
}),
|
|
66
|
+
Schema.Struct({
|
|
67
|
+
type: Schema.Literal("unsubscribe"),
|
|
68
|
+
subscriptionId: Schema.String,
|
|
69
|
+
}),
|
|
70
|
+
]);
|
|
71
|
+
export const ServerFrame = Schema.Union([
|
|
72
|
+
Schema.Struct({
|
|
73
|
+
type: Schema.Literal("command-result"),
|
|
74
|
+
requestId: Schema.String,
|
|
75
|
+
receipt: WireReceipt,
|
|
76
|
+
}),
|
|
77
|
+
Schema.Struct({
|
|
78
|
+
type: Schema.Literal("query-result"),
|
|
79
|
+
requestId: Schema.String,
|
|
80
|
+
result: QueryResult,
|
|
81
|
+
}),
|
|
82
|
+
Schema.Struct({
|
|
83
|
+
type: Schema.Literal("event"),
|
|
84
|
+
subscriptionId: Schema.String,
|
|
85
|
+
event: Event,
|
|
86
|
+
}),
|
|
87
|
+
Schema.Struct({
|
|
88
|
+
type: Schema.Literal("subscribed"),
|
|
89
|
+
subscriptionId: Schema.String,
|
|
90
|
+
after: Schema.String,
|
|
91
|
+
}),
|
|
92
|
+
Schema.Struct({
|
|
93
|
+
type: Schema.Literal("subscription-error"),
|
|
94
|
+
subscriptionId: Schema.String,
|
|
95
|
+
error: GatewayError,
|
|
96
|
+
}),
|
|
97
|
+
Schema.Struct({
|
|
98
|
+
type: Schema.Literal("request-error"),
|
|
99
|
+
requestId: Schema.String,
|
|
100
|
+
error: GatewayError,
|
|
101
|
+
}),
|
|
102
|
+
]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Client } from "../client/service.js";
|
|
3
|
+
import type { Key } from "../protocol/index.js";
|
|
4
|
+
import type { Actions, Reference } from "./definition.js";
|
|
5
|
+
export declare const makeClient: <A extends Actions>(options: {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly protocolVersion: number;
|
|
8
|
+
readonly actions: A;
|
|
9
|
+
}) => Effect.Effect<(key: Key) => Reference<A>, never, Client>;
|