stitchkit 0.44.1 → 0.45.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/dist/browser/socket-io.d.ts +14 -0
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/{index-4fyn9f09.js → index-w0mec79h.js} +171 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -1
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +3 -1
- package/dist/realtime/contract.d.ts +36 -0
- package/dist/realtime/contract.d.ts.map +1 -0
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +17 -0
- package/dist/realtime/socket.d.ts.map +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/realtime.d.ts +22 -0
- package/dist/server/realtime.d.ts.map +1 -0
- package/llms-full.txt +68 -25
- package/package.json +1 -1
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* unsubscribe), so it plugs straight into `createCacheBridge`.
|
|
22
22
|
*/
|
|
23
23
|
import type { EventsMap } from '@socket.io/component-emitter';
|
|
24
|
+
import type { RealtimeContract, RealtimeEventRegistry, RealtimeRejectedEventHook } from '../realtime/contract';
|
|
25
|
+
import { type ValidatedRealtimeSocket } from '../realtime/socket';
|
|
24
26
|
/**
|
|
25
27
|
* Socket.IO event map — `{ event: (...args) => void }`. Aliases Socket.IO's
|
|
26
28
|
* own `EventsMap` constraint, which intentionally maps to `any`: that is what
|
|
@@ -113,5 +115,17 @@ export interface SocketIOClient<TServerEvents extends SocketEventMap, TClientEve
|
|
|
113
115
|
*/
|
|
114
116
|
onConnectionChange(listener: (connected: boolean, reason?: string) => void): () => void;
|
|
115
117
|
}
|
|
118
|
+
export interface RealtimeClient<TServerToClient extends RealtimeEventRegistry, TClientToServer extends RealtimeEventRegistry> extends ValidatedRealtimeSocket<TServerToClient, TClientToServer> {
|
|
119
|
+
connect(): void;
|
|
120
|
+
disconnect(): void;
|
|
121
|
+
readonly connected: boolean;
|
|
122
|
+
onConnectionChange(listener: (connected: boolean, reason?: string) => void): () => void;
|
|
123
|
+
}
|
|
124
|
+
export interface RealtimeClientOptions<TServerToClient extends RealtimeEventRegistry> extends SocketIOClientConfig<{
|
|
125
|
+
[TEvent in keyof TServerToClient]: (...args: unknown[]) => void;
|
|
126
|
+
}> {
|
|
127
|
+
onRejected?: RealtimeRejectedEventHook;
|
|
128
|
+
}
|
|
129
|
+
export declare function createRealtimeClient<const TServerToClient extends RealtimeEventRegistry, const TClientToServer extends RealtimeEventRegistry>(contract: RealtimeContract<TServerToClient, TClientToServer>, { onRejected, ...config }: RealtimeClientOptions<TServerToClient>): RealtimeClient<TServerToClient, TClientToServer>;
|
|
116
130
|
export declare function createSocketIOClient<TServerEvents extends SocketEventMap, TClientEvents extends SocketEventMap>(config: SocketIOClientConfig<TServerEvents>): SocketIOClient<TServerEvents, TClientEvents>;
|
|
117
131
|
//# sourceMappingURL=socket-io.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-io.d.ts","sourceRoot":"","sources":["../../src/browser/socket-io.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-io.d.ts","sourceRoot":"","sources":["../../src/browser/socket-io.ts"],"names":[],"mappings":"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"}
|
|
@@ -1056,6 +1056,176 @@ function createImplement() {
|
|
|
1056
1056
|
return (contract, handlers) => implement(contract, handlers);
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
|
+
// src/realtime/socket.ts
|
|
1060
|
+
import { z } from "zod";
|
|
1061
|
+
var AcknowledgementCallbackSchema = z.function();
|
|
1062
|
+
function method(target, name) {
|
|
1063
|
+
const value = Reflect.get(target, name);
|
|
1064
|
+
if (typeof value !== "function") {
|
|
1065
|
+
throw new Error(`Realtime target does not implement ${name}()`);
|
|
1066
|
+
}
|
|
1067
|
+
return (...args) => Reflect.apply(value, target, args);
|
|
1068
|
+
}
|
|
1069
|
+
function splitWireArguments(definition, args) {
|
|
1070
|
+
if (!definition.ack)
|
|
1071
|
+
return { values: args };
|
|
1072
|
+
const acknowledgement = args.at(-1);
|
|
1073
|
+
if (typeof acknowledgement !== "function")
|
|
1074
|
+
return { values: args };
|
|
1075
|
+
return {
|
|
1076
|
+
values: args.slice(0, -1),
|
|
1077
|
+
acknowledgement: (...callbackArgs) => Reflect.apply(acknowledgement, undefined, callbackArgs)
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
function eventDefinition(registry, event) {
|
|
1081
|
+
const definition = registry[event];
|
|
1082
|
+
if (!definition)
|
|
1083
|
+
throw new Error(`Unknown realtime event "${event}"`);
|
|
1084
|
+
return definition;
|
|
1085
|
+
}
|
|
1086
|
+
function reportRejected(rejected, hook) {
|
|
1087
|
+
if (!hook) {
|
|
1088
|
+
console.error(`[stitchkit] rejected realtime ${rejected.direction} event "${rejected.event}" (${rejected.phase})`, rejected.error);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
Promise.resolve(hook(rejected)).catch((error) => {
|
|
1092
|
+
console.error("[stitchkit] realtime rejection hook failed", error);
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
function createValidatedRealtimeSocket({
|
|
1096
|
+
target,
|
|
1097
|
+
inbound,
|
|
1098
|
+
outbound,
|
|
1099
|
+
inboundDirection,
|
|
1100
|
+
outboundDirection,
|
|
1101
|
+
onRejected,
|
|
1102
|
+
subscribe
|
|
1103
|
+
}) {
|
|
1104
|
+
const onTarget = method(target, "on");
|
|
1105
|
+
const offTarget = subscribe ? undefined : method(target, "off");
|
|
1106
|
+
const emitTarget = method(target, "emit");
|
|
1107
|
+
return {
|
|
1108
|
+
on: (event, handler) => {
|
|
1109
|
+
const definition = eventDefinition(inbound, event);
|
|
1110
|
+
const wrapped = (...wireArgs) => {
|
|
1111
|
+
const { values, acknowledgement } = splitWireArguments(definition, wireArgs);
|
|
1112
|
+
const parsed = definition.args.safeParse(values);
|
|
1113
|
+
if (!parsed.success) {
|
|
1114
|
+
reportRejected({
|
|
1115
|
+
event,
|
|
1116
|
+
direction: inboundDirection,
|
|
1117
|
+
phase: "arguments",
|
|
1118
|
+
error: parsed.error
|
|
1119
|
+
}, onRejected);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (definition.ack && !acknowledgement) {
|
|
1123
|
+
const callback = AcknowledgementCallbackSchema.safeParse(undefined);
|
|
1124
|
+
if (!callback.success) {
|
|
1125
|
+
reportRejected({
|
|
1126
|
+
event,
|
|
1127
|
+
direction: inboundDirection,
|
|
1128
|
+
phase: "acknowledgement",
|
|
1129
|
+
error: callback.error
|
|
1130
|
+
}, onRejected);
|
|
1131
|
+
}
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const applicationArgs = [...parsed.data];
|
|
1135
|
+
if (definition.ack) {
|
|
1136
|
+
applicationArgs.push((value) => {
|
|
1137
|
+
const ack = definition.ack?.safeParse(value);
|
|
1138
|
+
if (!ack?.success) {
|
|
1139
|
+
if (ack) {
|
|
1140
|
+
reportRejected({
|
|
1141
|
+
event,
|
|
1142
|
+
direction: outboundDirection,
|
|
1143
|
+
phase: "acknowledgement",
|
|
1144
|
+
error: ack.error
|
|
1145
|
+
}, onRejected);
|
|
1146
|
+
}
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
acknowledgement?.(ack.data);
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
Reflect.apply(handler, undefined, applicationArgs);
|
|
1153
|
+
};
|
|
1154
|
+
if (subscribe)
|
|
1155
|
+
return subscribe(event, wrapped);
|
|
1156
|
+
onTarget(event, wrapped);
|
|
1157
|
+
return () => {
|
|
1158
|
+
offTarget?.(event, wrapped);
|
|
1159
|
+
};
|
|
1160
|
+
},
|
|
1161
|
+
emit: (event, ...args) => {
|
|
1162
|
+
const definition = eventDefinition(outbound, event);
|
|
1163
|
+
const { values, acknowledgement } = splitWireArguments(definition, args);
|
|
1164
|
+
const parsed = definition.args.parse(values);
|
|
1165
|
+
if (definition.ack && !acknowledgement) {
|
|
1166
|
+
AcknowledgementCallbackSchema.parse(undefined);
|
|
1167
|
+
}
|
|
1168
|
+
const wireArgs = [...parsed];
|
|
1169
|
+
if (definition.ack && acknowledgement) {
|
|
1170
|
+
wireArgs.push((value) => {
|
|
1171
|
+
const ack = definition.ack?.safeParse(value);
|
|
1172
|
+
if (!ack?.success) {
|
|
1173
|
+
if (ack) {
|
|
1174
|
+
reportRejected({
|
|
1175
|
+
event,
|
|
1176
|
+
direction: inboundDirection,
|
|
1177
|
+
phase: "acknowledgement",
|
|
1178
|
+
error: ack.error
|
|
1179
|
+
}, onRejected);
|
|
1180
|
+
}
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
acknowledgement(ack.data);
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
emitTarget(event, ...wireArgs);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// src/server/realtime.ts
|
|
1192
|
+
function bindRealtimeServer(contract, handle, options = {}) {
|
|
1193
|
+
const outbound = (target) => createValidatedRealtimeSocket({
|
|
1194
|
+
target,
|
|
1195
|
+
inbound: {},
|
|
1196
|
+
outbound: contract.serverToClient,
|
|
1197
|
+
inboundDirection: "server-inbound",
|
|
1198
|
+
outboundDirection: "server-outbound",
|
|
1199
|
+
onRejected: options.onRejected
|
|
1200
|
+
});
|
|
1201
|
+
const broadcast = outbound(handle.io);
|
|
1202
|
+
return {
|
|
1203
|
+
onConnection: (handler) => {
|
|
1204
|
+
const listener = (raw) => {
|
|
1205
|
+
const events = createValidatedRealtimeSocket({
|
|
1206
|
+
target: raw,
|
|
1207
|
+
inbound: contract.clientToServer,
|
|
1208
|
+
outbound: contract.serverToClient,
|
|
1209
|
+
inboundDirection: "server-inbound",
|
|
1210
|
+
outboundDirection: "server-outbound",
|
|
1211
|
+
onRejected: options.onRejected
|
|
1212
|
+
});
|
|
1213
|
+
handler({
|
|
1214
|
+
raw,
|
|
1215
|
+
events,
|
|
1216
|
+
to: (room) => outbound(raw.to(room))
|
|
1217
|
+
});
|
|
1218
|
+
};
|
|
1219
|
+
handle.io.on("connection", listener);
|
|
1220
|
+
return () => {
|
|
1221
|
+
handle.io.off("connection", listener);
|
|
1222
|
+
};
|
|
1223
|
+
},
|
|
1224
|
+
emit: broadcast.emit,
|
|
1225
|
+
to: (room) => outbound(handle.io.to(room))
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1059
1229
|
// src/server/websocket.ts
|
|
1060
1230
|
function webSocketLane(lane) {
|
|
1061
1231
|
const { match, handlers } = lane;
|
|
@@ -1202,4 +1372,4 @@ function socketIoLane(websocket) {
|
|
|
1202
1372
|
});
|
|
1203
1373
|
}
|
|
1204
1374
|
|
|
1205
|
-
export { parseMultipart, mimeForPath, staticRoute, createHandler, implement, createImplement, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
|
|
1375
|
+
export { parseMultipart, mimeForPath, staticRoute, createHandler, implement, createImplement, bindRealtimeServer, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { type ClientConfig, type ClientContract, type ClientRegistryValue, type ContractClientConfig, contractEndpointMatchers, createClient, createClients, createScopedClients, createScopedUrlBuilders, createUrlBuilder, createUrlBuilders, type PathPrefixArgs, type RegistryScope, type ScopeClientConfigs, type ScopedClientRegistry, type ScopedUrlBuilderRegistry, type UrlBuilderConfig, } from './browser/client';
|
|
2
2
|
export { ApiError, type ApiEvent, type ApiEventListener, type ConfiguredHttpClient, createHttpClient, type HeaderProvider, type HttpClient, type HttpClientConfig, type RequestOptions, type UnauthorizedMatcher, } from './browser/http';
|
|
3
|
-
export type { SocketEventMap, SocketIOClient, SocketIOClientConfig, } from './browser/socket-io';
|
|
4
|
-
export { createSocketIOClient } from './browser/socket-io';
|
|
3
|
+
export type { RealtimeClient, RealtimeClientOptions, SocketEventMap, SocketIOClient, SocketIOClientConfig, } from './browser/socket-io';
|
|
4
|
+
export { createRealtimeClient, createSocketIOClient } from './browser/socket-io';
|
|
5
5
|
export { type ParseSSEOptions, parseSSE } from './browser/stream';
|
|
6
6
|
export * from './contract';
|
|
7
7
|
export { childSpan, createTraceContext, formatTraceparent, parseTraceparent, type TraceContext, } from './observability/trace';
|
|
8
|
+
export * from './realtime';
|
|
8
9
|
export { createRetainedTopics, type RetainedTopics } from './retained';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAClE,cAAc,YAAY,CAAC;AAG3B,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -646,6 +646,138 @@ function createUrlBuilders(contracts, source, contractConfig) {
|
|
|
646
646
|
function createScopedUrlBuilders(contracts, source, scopeConfigs) {
|
|
647
647
|
return buildScopedRegistry(contracts, scopeConfigs, "URL builder", (contract, config) => createUrlBuilder(contract, source, config));
|
|
648
648
|
}
|
|
649
|
+
// src/realtime/socket.ts
|
|
650
|
+
import { z } from "zod";
|
|
651
|
+
var AcknowledgementCallbackSchema = z.function();
|
|
652
|
+
function method(target, name) {
|
|
653
|
+
const value = Reflect.get(target, name);
|
|
654
|
+
if (typeof value !== "function") {
|
|
655
|
+
throw new Error(`Realtime target does not implement ${name}()`);
|
|
656
|
+
}
|
|
657
|
+
return (...args) => Reflect.apply(value, target, args);
|
|
658
|
+
}
|
|
659
|
+
function splitWireArguments(definition, args) {
|
|
660
|
+
if (!definition.ack)
|
|
661
|
+
return { values: args };
|
|
662
|
+
const acknowledgement = args.at(-1);
|
|
663
|
+
if (typeof acknowledgement !== "function")
|
|
664
|
+
return { values: args };
|
|
665
|
+
return {
|
|
666
|
+
values: args.slice(0, -1),
|
|
667
|
+
acknowledgement: (...callbackArgs) => Reflect.apply(acknowledgement, undefined, callbackArgs)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function eventDefinition(registry, event) {
|
|
671
|
+
const definition = registry[event];
|
|
672
|
+
if (!definition)
|
|
673
|
+
throw new Error(`Unknown realtime event "${event}"`);
|
|
674
|
+
return definition;
|
|
675
|
+
}
|
|
676
|
+
function reportRejected(rejected, hook) {
|
|
677
|
+
if (!hook) {
|
|
678
|
+
console.error(`[stitchkit] rejected realtime ${rejected.direction} event "${rejected.event}" (${rejected.phase})`, rejected.error);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
Promise.resolve(hook(rejected)).catch((error) => {
|
|
682
|
+
console.error("[stitchkit] realtime rejection hook failed", error);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
function createValidatedRealtimeSocket({
|
|
686
|
+
target,
|
|
687
|
+
inbound,
|
|
688
|
+
outbound,
|
|
689
|
+
inboundDirection,
|
|
690
|
+
outboundDirection,
|
|
691
|
+
onRejected,
|
|
692
|
+
subscribe
|
|
693
|
+
}) {
|
|
694
|
+
const onTarget = method(target, "on");
|
|
695
|
+
const offTarget = subscribe ? undefined : method(target, "off");
|
|
696
|
+
const emitTarget = method(target, "emit");
|
|
697
|
+
return {
|
|
698
|
+
on: (event, handler) => {
|
|
699
|
+
const definition = eventDefinition(inbound, event);
|
|
700
|
+
const wrapped = (...wireArgs) => {
|
|
701
|
+
const { values, acknowledgement } = splitWireArguments(definition, wireArgs);
|
|
702
|
+
const parsed = definition.args.safeParse(values);
|
|
703
|
+
if (!parsed.success) {
|
|
704
|
+
reportRejected({
|
|
705
|
+
event,
|
|
706
|
+
direction: inboundDirection,
|
|
707
|
+
phase: "arguments",
|
|
708
|
+
error: parsed.error
|
|
709
|
+
}, onRejected);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (definition.ack && !acknowledgement) {
|
|
713
|
+
const callback = AcknowledgementCallbackSchema.safeParse(undefined);
|
|
714
|
+
if (!callback.success) {
|
|
715
|
+
reportRejected({
|
|
716
|
+
event,
|
|
717
|
+
direction: inboundDirection,
|
|
718
|
+
phase: "acknowledgement",
|
|
719
|
+
error: callback.error
|
|
720
|
+
}, onRejected);
|
|
721
|
+
}
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const applicationArgs = [...parsed.data];
|
|
725
|
+
if (definition.ack) {
|
|
726
|
+
applicationArgs.push((value) => {
|
|
727
|
+
const ack = definition.ack?.safeParse(value);
|
|
728
|
+
if (!ack?.success) {
|
|
729
|
+
if (ack) {
|
|
730
|
+
reportRejected({
|
|
731
|
+
event,
|
|
732
|
+
direction: outboundDirection,
|
|
733
|
+
phase: "acknowledgement",
|
|
734
|
+
error: ack.error
|
|
735
|
+
}, onRejected);
|
|
736
|
+
}
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
acknowledgement?.(ack.data);
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
Reflect.apply(handler, undefined, applicationArgs);
|
|
743
|
+
};
|
|
744
|
+
if (subscribe)
|
|
745
|
+
return subscribe(event, wrapped);
|
|
746
|
+
onTarget(event, wrapped);
|
|
747
|
+
return () => {
|
|
748
|
+
offTarget?.(event, wrapped);
|
|
749
|
+
};
|
|
750
|
+
},
|
|
751
|
+
emit: (event, ...args) => {
|
|
752
|
+
const definition = eventDefinition(outbound, event);
|
|
753
|
+
const { values, acknowledgement } = splitWireArguments(definition, args);
|
|
754
|
+
const parsed = definition.args.parse(values);
|
|
755
|
+
if (definition.ack && !acknowledgement) {
|
|
756
|
+
AcknowledgementCallbackSchema.parse(undefined);
|
|
757
|
+
}
|
|
758
|
+
const wireArgs = [...parsed];
|
|
759
|
+
if (definition.ack && acknowledgement) {
|
|
760
|
+
wireArgs.push((value) => {
|
|
761
|
+
const ack = definition.ack?.safeParse(value);
|
|
762
|
+
if (!ack?.success) {
|
|
763
|
+
if (ack) {
|
|
764
|
+
reportRejected({
|
|
765
|
+
event,
|
|
766
|
+
direction: inboundDirection,
|
|
767
|
+
phase: "acknowledgement",
|
|
768
|
+
error: ack.error
|
|
769
|
+
}, onRejected);
|
|
770
|
+
}
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
acknowledgement(ack.data);
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
emitTarget(event, ...wireArgs);
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
649
781
|
// src/retained.ts
|
|
650
782
|
function createRetainedTopics() {
|
|
651
783
|
const last = {};
|
|
@@ -691,6 +823,39 @@ function toIoAuth(auth) {
|
|
|
691
823
|
Promise.resolve().then(auth).then(cb, () => cb({}));
|
|
692
824
|
};
|
|
693
825
|
}
|
|
826
|
+
function createRealtimeClient(contract, { onRejected, ...config }) {
|
|
827
|
+
const transport = createSocketIOClient(config);
|
|
828
|
+
const events = createValidatedRealtimeSocket({
|
|
829
|
+
target: transport,
|
|
830
|
+
inbound: contract.serverToClient,
|
|
831
|
+
outbound: contract.clientToServer,
|
|
832
|
+
inboundDirection: "client-inbound",
|
|
833
|
+
outboundDirection: "client-outbound",
|
|
834
|
+
onRejected,
|
|
835
|
+
subscribe: (event, handler) => {
|
|
836
|
+
const subscribe = Reflect.get(transport, "on");
|
|
837
|
+
if (typeof subscribe !== "function") {
|
|
838
|
+
throw new Error("Socket.IO client does not implement on()");
|
|
839
|
+
}
|
|
840
|
+
const unsubscribe = Reflect.apply(subscribe, transport, [event, handler]);
|
|
841
|
+
if (typeof unsubscribe !== "function") {
|
|
842
|
+
throw new Error("Socket.IO client on() did not return an unsubscribe function");
|
|
843
|
+
}
|
|
844
|
+
return () => {
|
|
845
|
+
Reflect.apply(unsubscribe, undefined, []);
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
return {
|
|
850
|
+
...events,
|
|
851
|
+
connect: transport.connect,
|
|
852
|
+
disconnect: transport.disconnect,
|
|
853
|
+
get connected() {
|
|
854
|
+
return transport.connected;
|
|
855
|
+
},
|
|
856
|
+
onConnectionChange: transport.onConnectionChange
|
|
857
|
+
};
|
|
858
|
+
}
|
|
694
859
|
function createSocketIOClient(config) {
|
|
695
860
|
let socket = null;
|
|
696
861
|
let desiredConnected = false;
|
|
@@ -802,7 +967,7 @@ function createSocketIOClient(config) {
|
|
|
802
967
|
};
|
|
803
968
|
}
|
|
804
969
|
// src/internal/errors.ts
|
|
805
|
-
import { z } from "zod";
|
|
970
|
+
import { z as z2 } from "zod";
|
|
806
971
|
|
|
807
972
|
// src/server/stream.ts
|
|
808
973
|
async function* parseSSE(response, options) {
|
|
@@ -838,6 +1003,10 @@ async function* parseSSE(response, options) {
|
|
|
838
1003
|
reader.releaseLock();
|
|
839
1004
|
}
|
|
840
1005
|
}
|
|
1006
|
+
// src/realtime/contract.ts
|
|
1007
|
+
function defineRealtimeContract(contract) {
|
|
1008
|
+
return contract;
|
|
1009
|
+
}
|
|
841
1010
|
export {
|
|
842
1011
|
unauthorized,
|
|
843
1012
|
rateLimited,
|
|
@@ -849,6 +1018,7 @@ export {
|
|
|
849
1018
|
formatTraceparent,
|
|
850
1019
|
forbidden,
|
|
851
1020
|
encodeCursor,
|
|
1021
|
+
defineRealtimeContract,
|
|
852
1022
|
defineErrors,
|
|
853
1023
|
defineContract,
|
|
854
1024
|
decodeCursor,
|
|
@@ -859,6 +1029,7 @@ export {
|
|
|
859
1029
|
createScopedUrlBuilders,
|
|
860
1030
|
createScopedClients,
|
|
861
1031
|
createRetainedTopics,
|
|
1032
|
+
createRealtimeClient,
|
|
862
1033
|
createHttpClient,
|
|
863
1034
|
createContractFactory,
|
|
864
1035
|
createClients,
|
package/dist/node.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { createHandler } from './server/create';
|
|
|
9
9
|
export { createImplement, implement } from './server/implement';
|
|
10
10
|
export type { LogFormat } from './server/logger';
|
|
11
11
|
export { type NodeServerConfig, type NodeServerHandle, serveNode } from './server/node';
|
|
12
|
+
export { bindRealtimeServer, type RealtimeServer, type RealtimeServerConnection, type RealtimeServerHandle, } from './server/realtime';
|
|
12
13
|
export { createNodeSocketIOServer as createSocketIOServer, type NodeSocketIOServerHandle as SocketIOServerHandle, type SocketIOServerConfig, } from './server/socket-io-node';
|
|
13
14
|
export type { FetchComposition, FetchHandler, HandlerConfig, LoggingConfig, LogOutcome, RawRoute, RawRouteContext, ServiceDef, } from './server/types';
|
|
14
15
|
//# sourceMappingURL=node.d.ts.map
|
package/dist/node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,UAAU,GACX,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,UAAU,GACX,MAAM,gBAAgB,CAAC"}
|
package/dist/node.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
bindRealtimeServer,
|
|
2
3
|
createHandler,
|
|
3
4
|
createImplement,
|
|
4
5
|
createSocketIOServer,
|
|
5
6
|
implement
|
|
6
|
-
} from "./index-
|
|
7
|
+
} from "./index-w0mec79h.js";
|
|
7
8
|
import"./index-6jypn22c.js";
|
|
8
9
|
import"./index-x3fcszf8.js";
|
|
9
10
|
import {
|
|
@@ -55,6 +56,7 @@ export {
|
|
|
55
56
|
createImplement,
|
|
56
57
|
createHandler,
|
|
57
58
|
conflict,
|
|
59
|
+
bindRealtimeServer,
|
|
58
60
|
badRequest,
|
|
59
61
|
appError,
|
|
60
62
|
AppError
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
export interface RealtimeEventDefinition<TArgs extends z.ZodType<unknown[]> = z.ZodType<unknown[]>, TAck extends z.ZodType | undefined = z.ZodType | undefined> {
|
|
3
|
+
args: TArgs;
|
|
4
|
+
ack?: TAck;
|
|
5
|
+
}
|
|
6
|
+
export type RealtimeEventRegistry = Record<string, RealtimeEventDefinition>;
|
|
7
|
+
export interface RealtimeContract<TServerToClient extends RealtimeEventRegistry, TClientToServer extends RealtimeEventRegistry> {
|
|
8
|
+
serverToClient: TServerToClient;
|
|
9
|
+
clientToServer: TClientToServer;
|
|
10
|
+
}
|
|
11
|
+
export declare function defineRealtimeContract<const TServerToClient extends RealtimeEventRegistry, const TClientToServer extends RealtimeEventRegistry>(contract: RealtimeContract<TServerToClient, TClientToServer>): RealtimeContract<TServerToClient, TClientToServer>;
|
|
12
|
+
export type RealtimeEventArguments<TDefinition extends RealtimeEventDefinition> = [
|
|
13
|
+
...z.output<TDefinition['args']>,
|
|
14
|
+
...(TDefinition extends {
|
|
15
|
+
ack: infer TAck extends z.ZodType;
|
|
16
|
+
} ? [(value: z.input<TAck>) => void] : [])
|
|
17
|
+
];
|
|
18
|
+
export type RealtimeEmitArguments<TDefinition extends RealtimeEventDefinition> = [
|
|
19
|
+
...(z.input<TDefinition['args']> extends unknown[] ? z.input<TDefinition['args']> : never),
|
|
20
|
+
...(TDefinition extends {
|
|
21
|
+
ack: infer TAck extends z.ZodType;
|
|
22
|
+
} ? [(value: z.output<TAck>) => void] : [])
|
|
23
|
+
];
|
|
24
|
+
export type RealtimeEventHandler<TDefinition extends RealtimeEventDefinition> = (...args: RealtimeEventArguments<TDefinition>) => void;
|
|
25
|
+
export type InferRealtimeEventMap<TRegistry extends RealtimeEventRegistry> = {
|
|
26
|
+
[TEvent in keyof TRegistry]: RealtimeEventHandler<TRegistry[TEvent]>;
|
|
27
|
+
};
|
|
28
|
+
export type RealtimeRejectDirection = 'client-inbound' | 'client-outbound' | 'server-inbound' | 'server-outbound';
|
|
29
|
+
export interface RealtimeRejectedEvent {
|
|
30
|
+
event: string;
|
|
31
|
+
direction: RealtimeRejectDirection;
|
|
32
|
+
phase: 'arguments' | 'acknowledgement';
|
|
33
|
+
error: z.ZodError;
|
|
34
|
+
}
|
|
35
|
+
export type RealtimeRejectedEventHook = (rejected: RealtimeRejectedEvent) => void | Promise<void>;
|
|
36
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/realtime/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,WAAW,uBAAuB,CACtC,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EACzD,IAAI,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,CAAC,CAAC,OAAO,GAAG,SAAS;IAE1D,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;AAE5E,MAAM,WAAW,gBAAgB,CAC/B,eAAe,SAAS,qBAAqB,EAC7C,eAAe,SAAS,qBAAqB;IAE7C,cAAc,EAAE,eAAe,CAAC;IAChC,cAAc,EAAE,eAAe,CAAC;CACjC;AAED,wBAAgB,sBAAsB,CACpC,KAAK,CAAC,eAAe,SAAS,qBAAqB,EACnD,KAAK,CAAC,eAAe,SAAS,qBAAqB,EACnD,QAAQ,EAAE,gBAAgB,CAAC,eAAe,EAAE,eAAe,CAAC,sDAE7D;AAED,MAAM,MAAM,sBAAsB,CAAC,WAAW,SAAS,uBAAuB,IAAI;IAChF,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,GAAG,CAAC,WAAW,SAAS;QAAE,GAAG,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC,OAAO,CAAA;KAAE,GACzD,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAChC,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,WAAW,SAAS,uBAAuB,IAAI;IAC/E,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC;IAC1F,GAAG,CAAC,WAAW,SAAS;QAAE,GAAG,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC,OAAO,CAAA;KAAE,GACzD,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GACjC,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,WAAW,SAAS,uBAAuB,IAAI,CAC9E,GAAG,IAAI,EAAE,sBAAsB,CAAC,WAAW,CAAC,KACzC,IAAI,CAAC;AAEV,MAAM,MAAM,qBAAqB,CAAC,SAAS,SAAS,qBAAqB,IAAI;KAC1E,MAAM,IAAI,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,uBAAuB,CAAC;IACnC,KAAK,EAAE,WAAW,GAAG,iBAAiB,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC;CACnB;AAED,MAAM,MAAM,yBAAyB,GAAG,CACtC,QAAQ,EAAE,qBAAqB,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { defineRealtimeContract, type InferRealtimeEventMap, type RealtimeContract, type RealtimeEmitArguments, type RealtimeEventArguments, type RealtimeEventDefinition, type RealtimeEventHandler, type RealtimeEventRegistry, type RealtimeRejectDirection, type RealtimeRejectedEvent, type RealtimeRejectedEventHook, } from './contract';
|
|
2
|
+
export type { ValidatedRealtimeSocket } from './socket';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,GAC/B,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RealtimeEmitArguments, RealtimeEventHandler, RealtimeEventRegistry, RealtimeRejectDirection, RealtimeRejectedEventHook } from './contract';
|
|
2
|
+
interface ValidatedRealtimeSocketOptions<TInbound extends RealtimeEventRegistry, TOutbound extends RealtimeEventRegistry> {
|
|
3
|
+
target: object;
|
|
4
|
+
inbound: TInbound;
|
|
5
|
+
outbound: TOutbound;
|
|
6
|
+
inboundDirection: RealtimeRejectDirection;
|
|
7
|
+
outboundDirection: RealtimeRejectDirection;
|
|
8
|
+
onRejected?: RealtimeRejectedEventHook;
|
|
9
|
+
subscribe?: (event: string, handler: (...args: unknown[]) => void) => () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface ValidatedRealtimeSocket<TInbound extends RealtimeEventRegistry, TOutbound extends RealtimeEventRegistry> {
|
|
12
|
+
on<TEvent extends keyof TInbound & string>(event: TEvent, handler: RealtimeEventHandler<TInbound[TEvent]>): () => void;
|
|
13
|
+
emit<TEvent extends keyof TOutbound & string>(event: TEvent, ...args: RealtimeEmitArguments<TOutbound[TEvent]>): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createValidatedRealtimeSocket<const TInbound extends RealtimeEventRegistry, const TOutbound extends RealtimeEventRegistry>({ target, inbound, outbound, inboundDirection, outboundDirection, onRejected, subscribe, }: ValidatedRealtimeSocketOptions<TInbound, TOutbound>): ValidatedRealtimeSocket<TInbound, TOutbound>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=socket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/realtime/socket.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EAErB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EAEvB,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAEpB,UAAU,8BAA8B,CACtC,QAAQ,SAAS,qBAAqB,EACtC,SAAS,SAAS,qBAAqB;IAEvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CAClF;AAED,MAAM,WAAW,uBAAuB,CACtC,QAAQ,SAAS,qBAAqB,EACtC,SAAS,SAAS,qBAAqB;IAEvC,EAAE,CAAC,MAAM,SAAS,MAAM,QAAQ,GAAG,MAAM,EACvC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAC9C,MAAM,IAAI,CAAC;IACd,IAAI,CAAC,MAAM,SAAS,MAAM,SAAS,GAAG,MAAM,EAC1C,KAAK,EAAE,MAAM,EACb,GAAG,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAChD,IAAI,CAAC;CACT;AAmDD,wBAAgB,6BAA6B,CAC3C,KAAK,CAAC,QAAQ,SAAS,qBAAqB,EAC5C,KAAK,CAAC,SAAS,SAAS,qBAAqB,EAC7C,EACA,MAAM,EACN,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,SAAS,GACV,EAAE,8BAA8B,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,uBAAuB,CAC9E,QAAQ,EACR,SAAS,CACV,CAkGA"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { type MultipartResult, parseMultipart } from './multipart';
|
|
|
17
17
|
export { generateOpenApiDocument, type OpenApiConfig, type OpenApiDocument, type OpenApiInfo, type OpenApiServer, openApiRoute, } from './openapi';
|
|
18
18
|
export { createRateLimiter, type RateLimitConfig } from './rate-limit';
|
|
19
19
|
export { errorResponse, parseBody, respondJson } from './raw';
|
|
20
|
+
export { bindRealtimeServer, type RealtimeServer, type RealtimeServerConnection, type RealtimeServerHandle, } from './realtime';
|
|
20
21
|
export { type ClientIpOptions, extractIp, generateTraceId, getClientInfo, resolveSocketIp, resolveTraceId, } from './request';
|
|
21
22
|
export { staticRoute } from './router';
|
|
22
23
|
export type { SocketIOServerConfig, SocketIOServerHandle } from './socket-io';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
bindRealtimeServer,
|
|
2
3
|
composeWebSocketHandlers,
|
|
3
4
|
createHandler,
|
|
4
5
|
createImplement,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
socketIoLane,
|
|
10
11
|
staticRoute,
|
|
11
12
|
webSocketLane
|
|
12
|
-
} from "../index-
|
|
13
|
+
} from "../index-w0mec79h.js";
|
|
13
14
|
import {
|
|
14
15
|
createAuthHook,
|
|
15
16
|
createBearerResolver,
|
|
@@ -718,6 +719,7 @@ export {
|
|
|
718
719
|
conflict,
|
|
719
720
|
composeWebSocketHandlers,
|
|
720
721
|
cacheHeaders,
|
|
722
|
+
bindRealtimeServer,
|
|
721
723
|
badRequest,
|
|
722
724
|
appError,
|
|
723
725
|
STITCH_ERROR_STATUS,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Socket, Server as SocketIOServer } from 'socket.io';
|
|
2
|
+
import type { SocketEventMap } from '../browser/socket-io';
|
|
3
|
+
import type { RealtimeContract, RealtimeEventRegistry, RealtimeRejectedEventHook } from '../realtime/contract';
|
|
4
|
+
import { type ValidatedRealtimeSocket } from '../realtime/socket';
|
|
5
|
+
export interface RealtimeServerConnection<TServerToClient extends RealtimeEventRegistry, TClientToServer extends RealtimeEventRegistry> {
|
|
6
|
+
/** Raw Socket.IO socket for handshake auth, rooms and application-owned delivery policy. */
|
|
7
|
+
raw: Socket;
|
|
8
|
+
events: ValidatedRealtimeSocket<TClientToServer, TServerToClient>;
|
|
9
|
+
to(room: string): Pick<ValidatedRealtimeSocket<RealtimeEventRegistry, TServerToClient>, 'emit'>;
|
|
10
|
+
}
|
|
11
|
+
export interface RealtimeServer<TServerToClient extends RealtimeEventRegistry, TClientToServer extends RealtimeEventRegistry> {
|
|
12
|
+
onConnection(handler: (connection: RealtimeServerConnection<TServerToClient, TClientToServer>) => void | Promise<void>): () => void;
|
|
13
|
+
emit: ValidatedRealtimeSocket<RealtimeEventRegistry, TServerToClient>['emit'];
|
|
14
|
+
to(room: string): Pick<ValidatedRealtimeSocket<RealtimeEventRegistry, TServerToClient>, 'emit'>;
|
|
15
|
+
}
|
|
16
|
+
export interface RealtimeServerHandle {
|
|
17
|
+
io: SocketIOServer<SocketEventMap, SocketEventMap>;
|
|
18
|
+
}
|
|
19
|
+
export declare function bindRealtimeServer<const TServerToClient extends RealtimeEventRegistry, const TClientToServer extends RealtimeEventRegistry>(contract: RealtimeContract<TServerToClient, TClientToServer>, handle: RealtimeServerHandle, options?: {
|
|
20
|
+
onRejected?: RealtimeRejectedEventHook;
|
|
21
|
+
}): RealtimeServer<TServerToClient, TClientToServer>;
|
|
22
|
+
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/server/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACrB,yBAAyB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,wBAAwB,CACvC,eAAe,SAAS,qBAAqB,EAC7C,eAAe,SAAS,qBAAqB;IAE7C,4FAA4F;IAC5F,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,uBAAuB,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;IAClE,EAAE,CACA,IAAI,EAAE,MAAM,GACX,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,cAAc,CAC7B,eAAe,SAAS,qBAAqB,EAC7C,eAAe,SAAS,qBAAqB;IAE7C,YAAY,CACV,OAAO,EAAE,CACP,UAAU,EAAE,wBAAwB,CAAC,eAAe,EAAE,eAAe,CAAC,KACnE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACxB,MAAM,IAAI,CAAC;IACd,IAAI,EAAE,uBAAuB,CAAC,qBAAqB,EAAE,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9E,EAAE,CACA,IAAI,EAAE,MAAM,GACX,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,cAAc,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;CACpD;AAED,wBAAgB,kBAAkB,CAChC,KAAK,CAAC,eAAe,SAAS,qBAAqB,EACnD,KAAK,CAAC,eAAe,SAAS,qBAAqB,EAEnD,QAAQ,EAAE,gBAAgB,CAAC,eAAe,EAAE,eAAe,CAAC,EAC5D,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACvD,cAAc,CAAC,eAAe,EAAE,eAAe,CAAC,CAoClD"}
|
package/llms-full.txt
CHANGED
|
@@ -2799,35 +2799,61 @@ WebSocket engine; it ships thin, typed wrappers over Socket.IO and a bridge that
|
|
|
2799
2799
|
syncs socket events into the TanStack Query cache. See
|
|
2800
2800
|
[ADR 0008](../decisions/0008-thin-wrappers.md).
|
|
2801
2801
|
|
|
2802
|
-
##
|
|
2802
|
+
## Zod-first event contract
|
|
2803
2803
|
|
|
2804
|
-
Declare
|
|
2804
|
+
Declare wire events once in the shared module. Each `args` schema is a tuple, so
|
|
2805
|
+
Socket.IO's no-payload and variadic forms stay intact; `ack` describes the value
|
|
2806
|
+
returned through an acknowledgement callback.
|
|
2805
2807
|
|
|
2806
2808
|
```ts
|
|
2807
2809
|
// shared/contracts.ts
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
}
|
|
2810
|
+
import { defineRealtimeContract } from 'stitchkit'
|
|
2811
|
+
import { z } from 'zod'
|
|
2812
|
+
|
|
2813
|
+
export const realtimeContract = defineRealtimeContract({
|
|
2814
|
+
serverToClient: {
|
|
2815
|
+
'note:created': { args: z.tuple([NoteSchema]) },
|
|
2816
|
+
'note:deleted': { args: z.tuple([z.string()]) },
|
|
2817
|
+
},
|
|
2818
|
+
clientToServer: {
|
|
2819
|
+
'room:join': {
|
|
2820
|
+
args: z.tuple([z.string()]),
|
|
2821
|
+
ack: z.object({ joined: z.boolean() }),
|
|
2822
|
+
},
|
|
2823
|
+
ready: { args: z.tuple([]) },
|
|
2824
|
+
},
|
|
2825
|
+
})
|
|
2815
2826
|
```
|
|
2816
2827
|
|
|
2817
|
-
|
|
2818
|
-
|
|
2828
|
+
The contract derives every event handler type and validates both directions at
|
|
2829
|
+
runtime. A malformed inbound tuple never reaches the application handler;
|
|
2830
|
+
invalid outbound data throws before Socket.IO publishes it. Rejections call the
|
|
2831
|
+
optional `onRejected` hook with event, direction, phase and the Zod error.
|
|
2819
2832
|
|
|
2820
2833
|
## Server — `createSocketIOServer`
|
|
2821
2834
|
|
|
2822
2835
|
```ts
|
|
2823
|
-
import {
|
|
2836
|
+
import {
|
|
2837
|
+
bindRealtimeServer,
|
|
2838
|
+
createServer,
|
|
2839
|
+
createSocketIOServer,
|
|
2840
|
+
} from 'stitchkit/server'
|
|
2841
|
+
import { realtimeContract } from '@app/shared'
|
|
2824
2842
|
|
|
2825
|
-
const socket = await createSocketIOServer
|
|
2843
|
+
const socket = await createSocketIOServer({
|
|
2826
2844
|
cors: { origin: 'https://app.example.com' },
|
|
2827
2845
|
})
|
|
2828
2846
|
|
|
2829
|
-
|
|
2830
|
-
|
|
2847
|
+
const realtime = bindRealtimeServer(realtimeContract, socket, {
|
|
2848
|
+
onRejected: (event) => audit.realtimeRejected(event),
|
|
2849
|
+
})
|
|
2850
|
+
|
|
2851
|
+
realtime.onConnection(({ raw, events, to }) => {
|
|
2852
|
+
events.on('room:join', (room, acknowledge) => {
|
|
2853
|
+
raw.join(room) // authorization and room membership remain application policy
|
|
2854
|
+
acknowledge({ joined: true })
|
|
2855
|
+
to(room).emit('note:created', note)
|
|
2856
|
+
})
|
|
2831
2857
|
})
|
|
2832
2858
|
```
|
|
2833
2859
|
|
|
@@ -2840,13 +2866,13 @@ createServer({
|
|
|
2840
2866
|
rawRoutes: [socket.route], // ready-made /socket.io/*socketPath route
|
|
2841
2867
|
})
|
|
2842
2868
|
|
|
2843
|
-
// elsewhere — broadcast:
|
|
2844
|
-
|
|
2869
|
+
// elsewhere — validated broadcast:
|
|
2870
|
+
realtime.emit('note:created', note)
|
|
2845
2871
|
```
|
|
2846
2872
|
|
|
2847
2873
|
| Handle field | Purpose |
|
|
2848
2874
|
|--------------|---------|
|
|
2849
|
-
| `io` |
|
|
2875
|
+
| `io` | raw Socket.IO server for middleware, handshake auth and transport ownership |
|
|
2850
2876
|
| `websocket` | Bun WebSocket handlers — pass to `createServer({ websocket })` |
|
|
2851
2877
|
| `route` | the `/socket.io/*socketPath` raw route — pass to `createServer({ rawRoutes })` |
|
|
2852
2878
|
|
|
@@ -2868,20 +2894,29 @@ precedence over the same keys in `serverOptions`. On Bun the engine-level option
|
|
|
2868
2894
|
`@socket.io/bun-engine` too — so a configured `maxHttpBufferSize` actually applies
|
|
2869
2895
|
instead of silently truncating at 1 MB.
|
|
2870
2896
|
|
|
2871
|
-
## Client — `
|
|
2897
|
+
## Client — `createRealtimeClient`
|
|
2872
2898
|
|
|
2873
2899
|
```ts
|
|
2874
|
-
import {
|
|
2900
|
+
import { createRealtimeClient } from 'stitchkit'
|
|
2901
|
+
import { realtimeContract } from '@app/shared'
|
|
2875
2902
|
|
|
2876
|
-
const socket =
|
|
2903
|
+
const socket = createRealtimeClient(realtimeContract, {
|
|
2877
2904
|
url: 'https://api.example.com',
|
|
2905
|
+
retain: ['note:created'],
|
|
2906
|
+
onRejected: (event) => reportClientError(event),
|
|
2878
2907
|
})
|
|
2879
2908
|
|
|
2880
2909
|
socket.connect()
|
|
2881
2910
|
socket.on('note:created', (note) => { /* typed note */ })
|
|
2882
|
-
socket.emit('room:join', 'general')
|
|
2911
|
+
socket.emit('room:join', 'general', ({ joined }) => { /* typed + validated */ })
|
|
2883
2912
|
```
|
|
2884
2913
|
|
|
2914
|
+
## Low-level transport
|
|
2915
|
+
|
|
2916
|
+
`createSocketIOClient` remains the low-level Socket.IO transport wrapper for
|
|
2917
|
+
schema-agnostic infrastructure. Application wire events should use
|
|
2918
|
+
`createRealtimeClient`; it adds the shared contract without replacing Socket.IO.
|
|
2919
|
+
|
|
2885
2920
|
### Durable subscriptions
|
|
2886
2921
|
|
|
2887
2922
|
`socket.on(...)` returns an unsubscribe and is **durable** — the handler is
|
|
@@ -2901,7 +2936,7 @@ subscribes (and on the next subscribe after a re-render). It is the pub/sub
|
|
|
2901
2936
|
analogue of an MQTT *retained* message or an RxJS `BehaviorSubject`.
|
|
2902
2937
|
|
|
2903
2938
|
```ts
|
|
2904
|
-
const client =
|
|
2939
|
+
const client = createRealtimeClient(realtimeContract, {
|
|
2905
2940
|
url,
|
|
2906
2941
|
retain: ['presence:changed', 'job:state'], // events to keep the last value of
|
|
2907
2942
|
})
|
|
@@ -2938,7 +2973,7 @@ handshake with **`auth`** instead — the token reaches the server as
|
|
|
2938
2973
|
// client — a function is re-read on every (re)connect, so a rotated token is
|
|
2939
2974
|
// picked up automatically; no need to recreate the client (or lose durable
|
|
2940
2975
|
// subscriptions). It may be async.
|
|
2941
|
-
const socket =
|
|
2976
|
+
const socket = createRealtimeClient(realtimeContract, {
|
|
2942
2977
|
url: 'https://api.example.com',
|
|
2943
2978
|
auth: () => ({ token: getAccessToken() }),
|
|
2944
2979
|
})
|
|
@@ -5207,12 +5242,16 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
5207
5242
|
|
|
5208
5243
|
| Export | Kind | Summary |
|
|
5209
5244
|
|--------|------|---------|
|
|
5210
|
-
| `createSocketIOClient` | function |
|
|
5245
|
+
| `createSocketIOClient` | function | low-level typed Socket.IO transport primitive — [guide](../guide/realtime.md#low-level-transport) |
|
|
5246
|
+
| `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
|
|
5247
|
+
| `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
|
|
5211
5248
|
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
5212
5249
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
5213
5250
|
| `SocketIOClient` | _type_ | the client handle |
|
|
5214
5251
|
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`) |
|
|
5215
5252
|
| `SocketEventMap` | _type_ | the shape of an event map |
|
|
5253
|
+
| `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
|
|
5254
|
+
| `RealtimeClientOptions` | _type_ | transport options and the rejected-event hook for `createRealtimeClient` |
|
|
5216
5255
|
| `RetainedTopics` | _type_ | the `createRetainedTopics` handle |
|
|
5217
5256
|
| `ParseSSEOptions` | _type_ | options for `parseSSE` |
|
|
5218
5257
|
|
|
@@ -5407,6 +5446,10 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
5407
5446
|
| Export | Kind | Summary |
|
|
5408
5447
|
|--------|------|---------|
|
|
5409
5448
|
| `createSocketIOServer` | function | the typed Socket.IO server — [guide](../guide/realtime.md#server--createsocketioserver) |
|
|
5449
|
+
| `bindRealtimeServer` | function | inferred, runtime-validated connection and broadcast boundary |
|
|
5450
|
+
| `RealtimeServer` | _type_ | validated broadcast and connection API inferred from a realtime contract |
|
|
5451
|
+
| `RealtimeServerConnection` | _type_ | one validated connection with raw socket access for auth and rooms |
|
|
5452
|
+
| `RealtimeServerHandle` | _type_ | minimal Socket.IO server handle accepted by `bindRealtimeServer` |
|
|
5410
5453
|
| `SocketIOServerConfig` | _type_ | config for `createSocketIOServer` |
|
|
5411
5454
|
| `SocketIOServerHandle` | _type_ | the `{ io, websocket, route }` handle |
|
|
5412
5455
|
| `composeWebSocketHandlers` | function | compose one Bun `websocket` from N lanes — a raw binary lane beside Socket.IO ([guide](../guide/realtime.md#raw-binary-lane-bun)) |
|
package/package.json
CHANGED