|
1 | | -import type { RpcFunctionDefinitionAny } from './types' |
2 | | -import { structuredCloneParse, structuredCloneStringify } from 'devframe/utils/structured-clone' |
3 | 1 | import { diagnostics } from './diagnostics' |
4 | 2 |
|
5 | 3 | /** |
@@ -84,79 +82,6 @@ export function strictJsonStringify(value: unknown, fnName: string = ''): string |
84 | 82 | }) |
85 | 83 | } |
86 | 84 |
|
87 | | -/** The per-connection `serialize`/`deserialize` pair for a live RPC wire. */ |
88 | | -export interface RpcWireCodec { |
89 | | - serialize: (msg: any) => string |
90 | | - deserialize: (raw: string) => any |
91 | | -} |
92 | | - |
93 | | -const EMPTY_WIRE_DEFS: ReadonlyMap<string, Pick<RpcFunctionDefinitionAny, 'jsonSerializable'>> = new Map() |
94 | | - |
95 | | -/** |
96 | | - * Build the per-connection wire codec every live transport (WS server, WS |
97 | | - * client, SSE server, SSE client) shares: per-method dispatch between strict |
98 | | - * JSON (methods declared `jsonSerializable: true`) and `s:`-prefixed |
99 | | - * structured-clone (everything else, including all error envelopes), with a |
100 | | - * request-id → method map so a response independently picks the same |
101 | | - * encoder as its request. One codec per connection — request-id spaces |
102 | | - * don't collide across connections. |
103 | | - */ |
104 | | -export function createRpcWireCodec( |
105 | | - definitions: ReadonlyMap<string, Pick<RpcFunctionDefinitionAny, 'jsonSerializable'>> = EMPTY_WIRE_DEFS, |
106 | | -): RpcWireCodec { |
107 | | - // Maps an incoming request id to its method name so the matching |
108 | | - // outgoing response can look the method back up in `definitions` and |
109 | | - // pick the right encoder. |
110 | | - const pendingRequestMethods = new Map<string, string>() |
111 | | - return { |
112 | | - serialize: (msg: any): string => { |
113 | | - let method: string | undefined |
114 | | - if (msg.t === 'q') { |
115 | | - method = msg.m |
116 | | - } |
117 | | - else { |
118 | | - method = pendingRequestMethods.get(msg.i) |
119 | | - pendingRequestMethods.delete(msg.i) |
120 | | - } |
121 | | - // `jsonSerializable` constrains the return-value path (args + return). |
122 | | - // Error envelopes (`{ t: 's', i, e }`) carry a thrown value — fall back |
123 | | - // to structured-clone so they round-trip instead of crashing the serializer. |
124 | | - // Detect via `'e' in msg` so `throw undefined` still routes through SC. |
125 | | - const isErrorResponse = msg.t === 's' && 'e' in msg |
126 | | - const useJson = !isErrorResponse && !!method && definitions.get(method)?.jsonSerializable === true |
127 | | - if (useJson) |
128 | | - return strictJsonStringify(msg, method ?? '') |
129 | | - return `${STRUCTURED_CLONE_PREFIX}${structuredCloneStringify(msg)}` |
130 | | - }, |
131 | | - deserialize: (raw: string): any => { |
132 | | - const msg: any = raw.startsWith(STRUCTURED_CLONE_PREFIX) |
133 | | - ? structuredCloneParse(raw.slice(STRUCTURED_CLONE_PREFIX.length)) |
134 | | - : JSON.parse(raw) |
135 | | - if (msg.t === 'q' && msg.i && msg.m) |
136 | | - pendingRequestMethods.set(msg.i, msg.m) |
137 | | - return msg |
138 | | - }, |
139 | | - } |
140 | | -} |
141 | | - |
142 | | -/** |
143 | | - * Peek at a wire frame's birpc envelope without engaging a codec's |
144 | | - * request-id bookkeeping — used by the SSE transport to route a frame |
145 | | - * (park a POST for its response / answer with a bare 202) before it is |
146 | | - * handed to birpc proper. |
147 | | - */ |
148 | | -export function peekRpcWireFrame(raw: string): { t?: string, i?: string } { |
149 | | - try { |
150 | | - const msg: any = raw.startsWith(STRUCTURED_CLONE_PREFIX) |
151 | | - ? structuredCloneParse(raw.slice(STRUCTURED_CLONE_PREFIX.length)) |
152 | | - : JSON.parse(raw) |
153 | | - return { t: msg?.t, i: msg?.i } |
154 | | - } |
155 | | - catch { |
156 | | - return {} |
157 | | - } |
158 | | -} |
159 | | - |
160 | 85 | function nonJsonAt(fnName: string, type: string, parent: unknown, key: string): Error { |
161 | 86 | const path = formatPath(parent, key) |
162 | 87 | return diagnostics.DF0020({ name: fnName || '<anonymous>', type, path }) |
|
0 commit comments