zynapse 0.1.11 → 0.1.12
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/cli/index.d.ts +1 -0
- package/dist/cli/index.js +25 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/schema/client_side.d.ts.map +1 -1
- package/dist/schema/client_side.js +19 -90
- package/dist/schema/client_side.js.map +1 -1
- package/dist/schema/index.d.ts +3 -14
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/useWebSocket.d.ts +124 -0
- package/dist/schema/useWebSocket.d.ts.map +1 -0
- package/dist/schema/useWebSocket.js +257 -0
- package/dist/schema/useWebSocket.js.map +1 -0
- package/dist/schema/useWebSocket.ts +428 -0
- package/dist/server/index.d.ts +2 -4
- package/dist/server/index.d.ts.map +1 -1
- package/package.json +12 -9
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
#!/usr/bin/env bun
|
|
2
3
|
import path from "path";
|
|
3
4
|
import { parseArgs } from "util";
|
|
4
5
|
import { GenerateCode } from "../schema/client_side";
|
|
5
|
-
import { readdirSync,
|
|
6
|
+
import { readdirSync, readFileSync } from "fs";
|
|
7
|
+
import { rm } from "fs/promises";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
// Read the WebSocket template file
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const templatePath = path.join(__dirname, "../schema/useWebSocket.ts");
|
|
13
|
+
const useWebsocketTemplate = readFileSync(templatePath, "utf-8");
|
|
6
14
|
const { values: { inputFile, outputFolder }, } = parseArgs({
|
|
7
15
|
args: Bun.argv || process.argv,
|
|
8
16
|
allowPositionals: true,
|
|
@@ -29,15 +37,23 @@ const oldFilesDirectory = path.join(process.cwd(), outputFolder);
|
|
|
29
37
|
const files = readdirSync(oldFilesDirectory);
|
|
30
38
|
for (const file of files) {
|
|
31
39
|
const toDelete = path.join(oldFilesDirectory, file);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
+
try {
|
|
41
|
+
await rm(toDelete, {
|
|
42
|
+
maxRetries: 3,
|
|
43
|
+
recursive: true,
|
|
44
|
+
force: true,
|
|
45
|
+
});
|
|
46
|
+
console.log(`${toDelete} deleted`);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error("Fialed at deleting file", e);
|
|
50
|
+
}
|
|
40
51
|
}
|
|
52
|
+
// First, update the websocket lib.
|
|
53
|
+
const wsFileName = path.join(process.cwd(), outputFolder, "useWebsocket.ts");
|
|
54
|
+
const wsFHandle = Bun.file(wsFileName);
|
|
55
|
+
const bytes = await wsFHandle.write(useWebsocketTemplate);
|
|
56
|
+
console.log(`Websocket lib updated with ${bytes} at ${wsFileName}`);
|
|
41
57
|
// TODO: Add some sort of validation to make sure the mentioned file is actually a schema
|
|
42
58
|
const services_buffers = await GenerateCode(file);
|
|
43
59
|
let total_bytes = 0;
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAiC,WAAW,EAAE,EAAE,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client_side.d.ts","sourceRoot":"","sources":["../../src/schema/client_side.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAW,MAAM,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"client_side.d.ts","sourceRoot":"","sources":["../../src/schema/client_side.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAW,MAAM,GAAG,CAAC;AAuWvC,KAAK,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAWtD,KAAK,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;AAEtC,wBAAsB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAgC1E"}
|
|
@@ -84,94 +84,22 @@ async function bidirectionalProcedureCodeGen(proc, parentService) {
|
|
|
84
84
|
name: inputIdentifier,
|
|
85
85
|
});
|
|
86
86
|
let buff = `export ${schema}\nexport ${stringifiedAlias}\n\nexport function use${parentService.name}${proc.name}Bidirectional`;
|
|
87
|
-
buff += `(
|
|
88
|
-
onError?: (errorMessage: string) => void;
|
|
89
|
-
onClose?: () => void;
|
|
90
|
-
})`;
|
|
87
|
+
buff += `(options: UseWebSocketOptions = {}): UseWebSocketReturn<z.infer<typeof ${inputIdentifier}>, ${outputTypeIdentifier}>`;
|
|
91
88
|
buff += "{\n";
|
|
92
89
|
buff += `/*${proc.description}*/\n`;
|
|
93
|
-
// Initial setup of state.
|
|
94
|
-
buff += `const socketRef = useRef<WebSocket>();\n`;
|
|
95
|
-
buff += `const [messages, setMessages] = useState<Array<${outputTypeIdentifier}>>([]);\n`;
|
|
96
|
-
buff += `const [isConnected, setIsConnected] = useState<boolean>(false);\n`;
|
|
97
|
-
// Avoid re-render on callback change.
|
|
98
|
-
buff += `
|
|
99
|
-
const onErrorRef = useRef(extraOptions?.onError);
|
|
100
|
-
const onCloseRef = useRef(extraOptions?.onClose);
|
|
101
|
-
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
onErrorRef.current = extraOptions?.onError;
|
|
104
|
-
onCloseRef.current = extraOptions?.onClose;
|
|
105
|
-
}, [extraOptions]);\n`;
|
|
106
|
-
// Send function
|
|
107
|
-
buff += `
|
|
108
|
-
const send = useCallback((data: z.infer<typeof ${inputIdentifier}>) => {
|
|
109
|
-
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
|
|
110
|
-
socketRef.current.send(JSON.stringify(data));
|
|
111
|
-
}
|
|
112
|
-
}, []);\n`;
|
|
113
90
|
// Use effect main logic.
|
|
114
|
-
buff += `
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const stringifiedArguments = JSON.stringify(fullPayload);
|
|
127
|
-
const encodedArguments = encodeURIComponent(stringifiedArguments);
|
|
128
|
-
targetURL.searchParams.set("payload", encodedArguments);
|
|
129
|
-
|
|
130
|
-
const socket = new WebSocket(targetURL);
|
|
131
|
-
socketRef.current = socket;
|
|
132
|
-
|
|
133
|
-
socket.addEventListener("open", () => {
|
|
134
|
-
setIsConnected(true);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
socket.addEventListener("error", () => {
|
|
138
|
-
if (onErrorRef.current) {
|
|
139
|
-
onErrorRef.current("WebSocket connection error.");
|
|
140
|
-
}
|
|
141
|
-
setIsConnected(false);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
socket.addEventListener("message", (ev) => {
|
|
145
|
-
try {
|
|
146
|
-
const data = JSON.parse(ev.data);
|
|
147
|
-
setMessages((prev) => [...prev, data]);
|
|
148
|
-
} catch {
|
|
149
|
-
if (onErrorRef.current) {
|
|
150
|
-
onErrorRef.current("Failed to decode data");
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
socket.addEventListener("close", () => {
|
|
156
|
-
if (onCloseRef.current) {
|
|
157
|
-
onCloseRef.current();
|
|
158
|
-
}
|
|
159
|
-
setIsConnected(false);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
return () => {
|
|
163
|
-
socket.close();
|
|
164
|
-
socketRef.current = undefined;
|
|
165
|
-
setIsConnected(false);
|
|
166
|
-
};
|
|
167
|
-
}, [active]);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
messages,
|
|
172
|
-
isConnected,
|
|
173
|
-
send,
|
|
174
|
-
};
|
|
91
|
+
buff += `
|
|
92
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
93
|
+
const targetURL = new URL(\`\${protocol}//\${window.location.host}/_api\`);
|
|
94
|
+
const fullPayload = {
|
|
95
|
+
service: "Greeting",
|
|
96
|
+
procedure: "echo",
|
|
97
|
+
data: {},
|
|
98
|
+
};
|
|
99
|
+
const stringifiedArguments = JSON.stringify(fullPayload);
|
|
100
|
+
const encodedArguments = encodeURIComponent(stringifiedArguments);
|
|
101
|
+
targetURL.searchParams.set("payload", encodedArguments);
|
|
102
|
+
return useWebSocket<z.infer<typeof ${inputIdentifier}>, ${outputTypeIdentifier}>(targetURL.href,options);
|
|
175
103
|
}
|
|
176
104
|
`;
|
|
177
105
|
return await prettier.format(buff, { parser: "babel-ts" });
|
|
@@ -380,7 +308,7 @@ async function getServiceCode(service, code) {
|
|
|
380
308
|
const prettified = await prettier.format(code, { parser: "babel-ts" });
|
|
381
309
|
return {
|
|
382
310
|
filename: service.name.toLowerCase() + ".service.ts",
|
|
383
|
-
code:
|
|
311
|
+
code: prettified,
|
|
384
312
|
};
|
|
385
313
|
}
|
|
386
314
|
export async function GenerateCode(schema) {
|
|
@@ -395,13 +323,14 @@ export async function GenerateCode(schema) {
|
|
|
395
323
|
}
|
|
396
324
|
const hasSubscription = serviceHasMethod(service, "SUBSCRIPTION");
|
|
397
325
|
const hasBidirectional = serviceHasMethod(service, "BIDIRECTIONAL");
|
|
398
|
-
if (hasSubscription
|
|
326
|
+
if (hasSubscription) {
|
|
399
327
|
const reactImports = ["useEffect", "useRef", "useState"];
|
|
400
|
-
if (hasBidirectional) {
|
|
401
|
-
reactImports.unshift("useCallback");
|
|
402
|
-
}
|
|
403
328
|
finalBuffer += `import { ${reactImports.join(", ")} } from "react";\n`;
|
|
404
329
|
}
|
|
330
|
+
if (hasBidirectional) {
|
|
331
|
+
// Import the websocket library
|
|
332
|
+
finalBuffer += `import {useWebSocket, UseWebSocketReturn, UseWebSocketOptions} from "./useWebsocket";\n`;
|
|
333
|
+
}
|
|
405
334
|
finalBuffer += 'import {z} from "zod"\n\n';
|
|
406
335
|
const buffers = await GenerateServiceCode(service);
|
|
407
336
|
finalBuffer += `// ---- Service Name: ${service.name} ----\n`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client_side.js","sourceRoot":"","sources":["../../src/schema/client_side.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,GAAG,MAAM,oBAAoB,CAAC;AACrC,OAAO,GAAG,MAAM,oBAAoB,CAAC;AAErC,MAAM,MAAM,GAAW;;;;;;;CAOtB,CAAC;AAEF,SAAS,cAAc,CAAC,IAAe,EAAE,aAAsB,EAAE;IAChE,MAAM,oBAAoB,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,YAAY,CAAC;IAC3E,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAC1D,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;AAAA,CAClD;AAED,KAAK,UAAU,wBAAwB,CACtC,IAAe,EACf,aAAsB,EACrB;IACD,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAChE,IAAI,EACJ,aAAa,CACb,CAAC;IAEF,MAAM,oBAAoB,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,aAAa,CAAC;IAC5E,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE;QAC9B,aAAa,EAAE,IAAI;QACnB,mBAAmB,EAAE,IAAI;KACzB,CAAC,CAAC;IACH,aAAa;IACb,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE;QAC3B,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,oBAAoB;KAC1B,CAAC,CAAC;IAEH,IAAI,IAAI,GAAW,UAAU,gBAAgB,YAAY,OAAO,wBAAwB,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,UAAU,CAAC;IAEjI,IAAI,IAAI,2CAA2C,oBAAoB,2BAA2B,oBAAoB;IACnH,IAAI,CAAC,WAAW;;;2CAGuB,oBAAoB;mCAC5B,oBAAoB;;;;;;;;;;iBAUtC,aAAa,CAAC,IAAI;mBAChB,IAAI,CAAC,IAAI;;;;;;;;;;;;kCAYM,IAAI,CAAC,IAAI;;;;;;kCAMT,oBAAoB;;;;GAInD,CAAC;IAEH,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,CAC3D;AAED,KAAK,UAAU,6BAA6B,CAC3C,IAAe,EACf,aAAsB,EACJ;IAClB,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAChE,IAAI,EACJ,aAAa,CACb,CAAC;IAEF,MAAM,eAAe,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,0BAA0B,CAAC;IACpF,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE;QAClC,aAAa,EAAE,IAAI;QACnB,mBAAmB,EAAE,IAAI;KACzB,CAAC,CAAC;IACH,aAAa;IACb,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,EAAE;QAC9B,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,eAAe;KACrB,CAAC,CAAC;IAEH,IAAI,IAAI,GAAW,UAAU,MAAM,YAAY,gBAAgB,0BAA0B,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,eAAe,CAAC;IACvI,IAAI,IAAI
|
|
1
|
+
{"version":3,"file":"client_side.js","sourceRoot":"","sources":["../../src/schema/client_side.ts"],"names":[],"mappings":"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"}
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -4,15 +4,10 @@ export type ProcedureType = `MUTATION` | `QUERY` | `SUBSCRIPTION` | `BIDIRECTION
|
|
|
4
4
|
* Represents a single procedure.
|
|
5
5
|
*/
|
|
6
6
|
export interface Procedure<TMethod extends ProcedureType = ProcedureType, TInput extends z.Schema = z.AnyZodObject, TOuput extends z.Schema = z.AnyZodObject> {
|
|
7
|
-
// The name of the procdedure, must be unique and URI safe.
|
|
8
7
|
name: string;
|
|
9
|
-
// A description of what the procedure is supposed to do
|
|
10
8
|
description: string;
|
|
11
|
-
// The method to be used for the procedure
|
|
12
9
|
method: TMethod;
|
|
13
|
-
// If the method requires an input.
|
|
14
10
|
input: TInput;
|
|
15
|
-
// The output of the endpoint
|
|
16
11
|
output: TOuput;
|
|
17
12
|
}
|
|
18
13
|
/**
|
|
@@ -29,19 +24,13 @@ export declare class Service<TProcedures extends ServiceProcedures = {
|
|
|
29
24
|
procedures: TProcedures;
|
|
30
25
|
constructor(name: string);
|
|
31
26
|
setMiddlewareDescription(middlewareDescription: string): this;
|
|
32
|
-
|
|
33
|
-
addProcedure<M extends ProcedureType, N extends string,
|
|
34
|
-
// Ensure Desc matches the Procedure interface (string | undefined if optional)
|
|
35
|
-
Desc extends string, // Or string | undefined if description is optional
|
|
36
|
-
I extends z.Schema, O extends z.Schema>(procDefinition: {
|
|
27
|
+
addProcedure<M extends ProcedureType, N extends string, Desc extends string, I extends z.Schema, O extends z.Schema>(procDefinition: {
|
|
37
28
|
method: M;
|
|
38
29
|
name: N;
|
|
39
|
-
description: Desc;
|
|
30
|
+
description: Desc;
|
|
40
31
|
input: I;
|
|
41
32
|
output: O;
|
|
42
|
-
}): Service<
|
|
43
|
-
// The return *type* reflects the added procedure
|
|
44
|
-
TProcedures & {
|
|
33
|
+
}): Service<TProcedures & {
|
|
45
34
|
[K in N]: Procedure<M, I, O> & {
|
|
46
35
|
method: M;
|
|
47
36
|
name: N;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,aAAa,GACtB,UAAU,GACV,OAAO,GACP,cAAc,GACd,eAAe,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,SAAS,CACzB,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7C,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY,EACxC,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,aAAa,GACtB,UAAU,GACV,OAAO,GACP,cAAc,GACd,eAAe,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,SAAS,CACzB,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7C,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY,EACxC,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY;IAGxC,IAAI,EAAE,MAAM,CAAC;IAGb,WAAW,EAAE,MAAM,CAAC;IAGpB,MAAM,EAAE,OAAO,CAAC;IAGhB,KAAK,EAAE,MAAM,CAAC;IAGd,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AAEH,KAAK,iBAAiB,GAAG;IACxB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;CACvD,CAAC;AAOF,qBAAa,OAAO,CACnB,WAAW,SAAS,iBAAiB,GAAG;IACvC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;CACtC;IAED,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAM;IAClB,UAAU,EAAE,WAAW,CAAC;IACxB,YAAY,IAAI,EAAE,MAAM,EAIvB;IAED,wBAAwB,CAAC,qBAAqB,EAAE,MAAM,QAGrD;IAGD,YAAY,CACX,CAAC,SAAS,aAAa,EACvB,CAAC,SAAS,MAAM,EAEhB,IAAI,SAAS,MAAM,EACnB,CAAC,SAAS,CAAC,CAAC,MAAM,EAClB,CAAC,SAAS,CAAC,CAAC,MAAM,EACjB,cAAc,EAAE;QACjB,MAAM,EAAE,CAAC,CAAC;QACV,IAAI,EAAE,CAAC,CAAC;QACR,WAAW,EAAE,IAAI,CAAC;QAClB,KAAK,EAAE,CAAC,CAAC;QACT,MAAM,EAAE,CAAC,CAAC;KACV,GAAG,OAAO,CAEV,WAAW,GAAG;SAEZ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG;YAAE,MAAM,EAAE,CAAC,CAAC;YAAC,IAAI,EAAE,CAAC,CAAC;YAAC,WAAW,EAAE,IAAI,CAAA;SAAE;KACxE,CACD,CA6BA;IAED,YAAY,CAAC,KAAK,SAAS,MAAM,WAAW,EAC3C,IAAI,EAAE,KAAK,GACT,WAAW,CAAC,KAAK,CAAC,CAIpB;CACD;AAED,qBAAa,SAAS,CACrB,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG;IAChD,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACpC;IAEM,QAAQ,EAAE,SAAS,CAAC;IAE3B,YAAY,QAAQ,GAAE,SAA2B,EAEhD;CACD"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export declare enum WebSocketConnectionStatus {
|
|
2
|
+
CONNECTING = "CONNECTING",
|
|
3
|
+
CONNECTED = "CONNECTED",
|
|
4
|
+
DISCONNECTED = "DISCONNECTED",
|
|
5
|
+
ERROR = "ERROR"
|
|
6
|
+
}
|
|
7
|
+
export interface UseWebSocketOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Callback fired when a message is received
|
|
10
|
+
*/
|
|
11
|
+
onMessage?: (event: MessageEvent) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Callback fired when connection opens
|
|
14
|
+
*/
|
|
15
|
+
onOpen?: (event: Event) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Callback fired when connection closes
|
|
18
|
+
*/
|
|
19
|
+
onClose?: (event: CloseEvent) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Callback fired on error
|
|
22
|
+
*/
|
|
23
|
+
onError?: (event: Event) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Enable automatic reconnection
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
reconnect?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum number of reconnection attempts
|
|
31
|
+
* @default Infinity
|
|
32
|
+
*/
|
|
33
|
+
maxReconnectAttempts?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Initial reconnection delay in milliseconds
|
|
36
|
+
* @default 1000
|
|
37
|
+
*/
|
|
38
|
+
reconnectInterval?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum reconnection delay in milliseconds
|
|
41
|
+
* @default 30000
|
|
42
|
+
*/
|
|
43
|
+
maxReconnectInterval?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Multiplier for exponential backoff
|
|
46
|
+
* @default 1.5
|
|
47
|
+
*/
|
|
48
|
+
reconnectBackoffMultiplier?: number;
|
|
49
|
+
/**
|
|
50
|
+
* WebSocket protocols
|
|
51
|
+
*/
|
|
52
|
+
protocols?: string | string[];
|
|
53
|
+
/**
|
|
54
|
+
* Should connect immediately on mount
|
|
55
|
+
* @default true
|
|
56
|
+
*/
|
|
57
|
+
connectOnMount?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface UseWebSocketReturn<TInput = any, TOutput = any> {
|
|
60
|
+
/**
|
|
61
|
+
* Send a message through the WebSocket (client to server)
|
|
62
|
+
*/
|
|
63
|
+
sendMessage: (message: TInput) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Send a raw string message
|
|
66
|
+
*/
|
|
67
|
+
sendRawMessage: (message: string) => void;
|
|
68
|
+
/**
|
|
69
|
+
* The last received message (raw MessageEvent)
|
|
70
|
+
*/
|
|
71
|
+
lastRawMessage: MessageEvent | null;
|
|
72
|
+
/**
|
|
73
|
+
* The last received message parsed as JSON (server to client)
|
|
74
|
+
*/
|
|
75
|
+
lastMessage: TOutput | null;
|
|
76
|
+
/**
|
|
77
|
+
* Current connection status
|
|
78
|
+
*/
|
|
79
|
+
connectionStatus: WebSocketConnectionStatus;
|
|
80
|
+
/**
|
|
81
|
+
* Manually connect to the WebSocket
|
|
82
|
+
*/
|
|
83
|
+
connect: () => void;
|
|
84
|
+
/**
|
|
85
|
+
* Manually disconnect from the WebSocket
|
|
86
|
+
*/
|
|
87
|
+
disconnect: () => void;
|
|
88
|
+
/**
|
|
89
|
+
* Check if currently connected
|
|
90
|
+
*/
|
|
91
|
+
isConnected: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Number of reconnection attempts made
|
|
94
|
+
*/
|
|
95
|
+
reconnectAttempts: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Advanced WebSocket hook with reconnection logic and type safety
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* interface ClientMessage {
|
|
103
|
+
* type: 'subscribe';
|
|
104
|
+
* channel: string;
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* interface ServerMessage {
|
|
108
|
+
* type: 'update';
|
|
109
|
+
* data: any;
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* const { sendMessage, lastParsedMessage, connectionStatus, isConnected } =
|
|
113
|
+
* useWebSocket<ClientMessage, ServerMessage>(
|
|
114
|
+
* 'wss://api.example.com',
|
|
115
|
+
* {
|
|
116
|
+
* onMessage: (event) => console.log('Received:', event.data),
|
|
117
|
+
* reconnect: true,
|
|
118
|
+
* maxReconnectAttempts: 10,
|
|
119
|
+
* }
|
|
120
|
+
* );
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export declare function useWebSocket<TInput = any, TOutput = any>(url: string | null, options?: UseWebSocketOptions): UseWebSocketReturn<TInput, TOutput>;
|
|
124
|
+
//# sourceMappingURL=useWebSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebSocket.d.ts","sourceRoot":"","sources":["../../src/schema/useWebSocket.ts"],"names":[],"mappings":"AAEA,oBAAY,yBAAyB;IACpC,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,YAAY,iBAAiB;IAC7B,KAAK,UAAU;CACf;AAED,MAAM,WAAW,mBAAmB;IACnC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEhC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAEtC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE9B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG;IAC9D;;OAEG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvC;;OAEG;IACH,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IAEpC;;OAEG;IACH,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,gBAAgB,EAAE,yBAAyB,CAAC;IAE5C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,EACvD,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,OAAO,GAAE,mBAAwB,GAC/B,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAuRrC"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { useEffect, useRef, useCallback, useState } from "react";
|
|
2
|
+
export { WebSocketConnectionStatus };
|
|
3
|
+
var WebSocketConnectionStatus;
|
|
4
|
+
(function (WebSocketConnectionStatus) {
|
|
5
|
+
WebSocketConnectionStatus["CONNECTING"] = "CONNECTING";
|
|
6
|
+
WebSocketConnectionStatus["CONNECTED"] = "CONNECTED";
|
|
7
|
+
WebSocketConnectionStatus["DISCONNECTED"] = "DISCONNECTED";
|
|
8
|
+
WebSocketConnectionStatus["ERROR"] = "ERROR";
|
|
9
|
+
})(WebSocketConnectionStatus || (WebSocketConnectionStatus = {}));
|
|
10
|
+
/**
|
|
11
|
+
* Advanced WebSocket hook with reconnection logic and type safety
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* interface ClientMessage {
|
|
16
|
+
* type: 'subscribe';
|
|
17
|
+
* channel: string;
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* interface ServerMessage {
|
|
21
|
+
* type: 'update';
|
|
22
|
+
* data: any;
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* const { sendMessage, lastParsedMessage, connectionStatus, isConnected } =
|
|
26
|
+
* useWebSocket<ClientMessage, ServerMessage>(
|
|
27
|
+
* 'wss://api.example.com',
|
|
28
|
+
* {
|
|
29
|
+
* onMessage: (event) => console.log('Received:', event.data),
|
|
30
|
+
* reconnect: true,
|
|
31
|
+
* maxReconnectAttempts: 10,
|
|
32
|
+
* }
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function useWebSocket(url, options = {}) {
|
|
37
|
+
const { onMessage, onOpen, onClose, onError, reconnect = true, maxReconnectAttempts = Infinity, reconnectInterval = 1000, maxReconnectInterval = 30000, reconnectBackoffMultiplier = 1.5, protocols, connectOnMount = true, } = options;
|
|
38
|
+
// Use refs to store values that shouldn't trigger re-renders
|
|
39
|
+
const webSocketRef = useRef(null);
|
|
40
|
+
const reconnectTimeoutRef = useRef(null);
|
|
41
|
+
const reconnectAttemptsRef = useRef(0);
|
|
42
|
+
const currentReconnectIntervalRef = useRef(reconnectInterval);
|
|
43
|
+
const shouldReconnectRef = useRef(reconnect);
|
|
44
|
+
const isManualDisconnectRef = useRef(false);
|
|
45
|
+
const cleanupDelayTimeoutRef = useRef(null);
|
|
46
|
+
const isMountedRef = useRef(false);
|
|
47
|
+
// Store callbacks in refs to avoid recreating WebSocket on callback changes
|
|
48
|
+
const onMessageRef = useRef(onMessage);
|
|
49
|
+
const onOpenRef = useRef(onOpen);
|
|
50
|
+
const onCloseRef = useRef(onClose);
|
|
51
|
+
const onErrorRef = useRef(onError);
|
|
52
|
+
// Update refs when callbacks change
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
onMessageRef.current = onMessage;
|
|
55
|
+
}, [onMessage]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
onOpenRef.current = onOpen;
|
|
58
|
+
}, [onOpen]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
onCloseRef.current = onClose;
|
|
61
|
+
}, [onClose]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
onErrorRef.current = onError;
|
|
64
|
+
}, [onError]);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
shouldReconnectRef.current = reconnect;
|
|
67
|
+
}, [reconnect]);
|
|
68
|
+
// State for values that should trigger re-renders
|
|
69
|
+
const [lastRawMessage, setLastMessage] = useState(null);
|
|
70
|
+
const [lastMessage, setLastParsedMessage] = useState(null);
|
|
71
|
+
const [connectionStatus, setConnectionStatus] = useState(WebSocketConnectionStatus.DISCONNECTED);
|
|
72
|
+
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
|
73
|
+
// Clear all timers
|
|
74
|
+
const clearTimers = useCallback(() => {
|
|
75
|
+
if (reconnectTimeoutRef.current) {
|
|
76
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
77
|
+
reconnectTimeoutRef.current = null;
|
|
78
|
+
}
|
|
79
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
80
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
81
|
+
cleanupDelayTimeoutRef.current = null;
|
|
82
|
+
}
|
|
83
|
+
}, []);
|
|
84
|
+
// Cleanup WebSocket
|
|
85
|
+
const cleanup = useCallback(() => {
|
|
86
|
+
clearTimers();
|
|
87
|
+
if (webSocketRef.current) {
|
|
88
|
+
const ws = webSocketRef.current;
|
|
89
|
+
webSocketRef.current = null;
|
|
90
|
+
// Remove all event listeners before closing
|
|
91
|
+
ws.onopen = null;
|
|
92
|
+
ws.onclose = null;
|
|
93
|
+
ws.onerror = null;
|
|
94
|
+
ws.onmessage = null;
|
|
95
|
+
if (ws.readyState === WebSocket.OPEN ||
|
|
96
|
+
ws.readyState === WebSocket.CONNECTING) {
|
|
97
|
+
ws.close(1000, "Component unmounting or reconnecting");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}, [clearTimers]);
|
|
101
|
+
// Connect to WebSocket
|
|
102
|
+
const connect = useCallback(() => {
|
|
103
|
+
if (!url)
|
|
104
|
+
return;
|
|
105
|
+
// Cancel any pending delayed cleanup
|
|
106
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
107
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
108
|
+
cleanupDelayTimeoutRef.current = null;
|
|
109
|
+
}
|
|
110
|
+
// Prevent multiple simultaneous connections
|
|
111
|
+
if (webSocketRef.current?.readyState === WebSocket.CONNECTING ||
|
|
112
|
+
webSocketRef.current?.readyState === WebSocket.OPEN) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
cleanup();
|
|
116
|
+
isManualDisconnectRef.current = false;
|
|
117
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTING);
|
|
118
|
+
try {
|
|
119
|
+
const ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
120
|
+
webSocketRef.current = ws;
|
|
121
|
+
ws.onopen = (event) => {
|
|
122
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTED);
|
|
123
|
+
reconnectAttemptsRef.current = 0;
|
|
124
|
+
currentReconnectIntervalRef.current = reconnectInterval;
|
|
125
|
+
setReconnectAttempts(0);
|
|
126
|
+
onOpenRef.current?.(event);
|
|
127
|
+
};
|
|
128
|
+
ws.onclose = (event) => {
|
|
129
|
+
setConnectionStatus(WebSocketConnectionStatus.DISCONNECTED);
|
|
130
|
+
clearTimers();
|
|
131
|
+
onCloseRef.current?.(event);
|
|
132
|
+
// Attempt reconnection if enabled and not manually disconnected
|
|
133
|
+
if (shouldReconnectRef.current &&
|
|
134
|
+
!isManualDisconnectRef.current &&
|
|
135
|
+
reconnectAttemptsRef.current < maxReconnectAttempts) {
|
|
136
|
+
reconnectAttemptsRef.current += 1;
|
|
137
|
+
setReconnectAttempts(reconnectAttemptsRef.current);
|
|
138
|
+
const delay = Math.min(currentReconnectIntervalRef.current, maxReconnectInterval);
|
|
139
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
140
|
+
currentReconnectIntervalRef.current *= reconnectBackoffMultiplier;
|
|
141
|
+
connect();
|
|
142
|
+
}, delay);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
ws.onerror = (event) => {
|
|
146
|
+
setConnectionStatus(WebSocketConnectionStatus.ERROR);
|
|
147
|
+
onErrorRef.current?.(event);
|
|
148
|
+
};
|
|
149
|
+
ws.onmessage = (event) => {
|
|
150
|
+
setLastMessage(event);
|
|
151
|
+
// Attempt to parse JSON data for typed message
|
|
152
|
+
try {
|
|
153
|
+
const parsed = typeof event.data === "string"
|
|
154
|
+
? JSON.parse(event.data)
|
|
155
|
+
: event.data;
|
|
156
|
+
setLastParsedMessage(parsed);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// If parsing fails, set parsed message to null
|
|
160
|
+
setLastParsedMessage(null);
|
|
161
|
+
}
|
|
162
|
+
onMessageRef.current?.(event);
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
setConnectionStatus(WebSocketConnectionStatus.ERROR);
|
|
167
|
+
console.error("WebSocket connection error:", error);
|
|
168
|
+
}
|
|
169
|
+
}, [
|
|
170
|
+
url,
|
|
171
|
+
protocols,
|
|
172
|
+
reconnectInterval,
|
|
173
|
+
maxReconnectInterval,
|
|
174
|
+
maxReconnectAttempts,
|
|
175
|
+
reconnectBackoffMultiplier,
|
|
176
|
+
cleanup,
|
|
177
|
+
clearTimers,
|
|
178
|
+
]);
|
|
179
|
+
// Disconnect from WebSocket
|
|
180
|
+
const disconnect = useCallback(() => {
|
|
181
|
+
isManualDisconnectRef.current = true;
|
|
182
|
+
cleanup();
|
|
183
|
+
setConnectionStatus(WebSocketConnectionStatus.DISCONNECTED);
|
|
184
|
+
reconnectAttemptsRef.current = 0;
|
|
185
|
+
setReconnectAttempts(0);
|
|
186
|
+
currentReconnectIntervalRef.current = reconnectInterval;
|
|
187
|
+
}, [cleanup, reconnectInterval]);
|
|
188
|
+
// Send typed message
|
|
189
|
+
const sendMessage = useCallback((message) => {
|
|
190
|
+
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
|
191
|
+
const messageStr = typeof message === "string" ? message : JSON.stringify(message);
|
|
192
|
+
webSocketRef.current.send(messageStr);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.warn("WebSocket is not connected. Message not sent:", message);
|
|
196
|
+
}
|
|
197
|
+
}, []);
|
|
198
|
+
// Send raw string message
|
|
199
|
+
const sendRawMessage = useCallback((message) => {
|
|
200
|
+
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
|
201
|
+
webSocketRef.current.send(message);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.warn("WebSocket is not connected. Message not sent:", message);
|
|
205
|
+
}
|
|
206
|
+
}, []);
|
|
207
|
+
// Connect on mount if enabled
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
isMountedRef.current = true;
|
|
210
|
+
// Cancel any pending cleanup from previous unmount (Strict Mode case)
|
|
211
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
212
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
213
|
+
cleanupDelayTimeoutRef.current = null;
|
|
214
|
+
}
|
|
215
|
+
// If connection exists and is open/connecting, reuse it
|
|
216
|
+
if (webSocketRef.current &&
|
|
217
|
+
(webSocketRef.current.readyState === WebSocket.OPEN ||
|
|
218
|
+
webSocketRef.current.readyState === WebSocket.CONNECTING)) {
|
|
219
|
+
// Connection already exists, just update status if needed
|
|
220
|
+
if (webSocketRef.current.readyState === WebSocket.OPEN) {
|
|
221
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTED);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTING);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (connectOnMount && url) {
|
|
228
|
+
// No existing connection, create new one
|
|
229
|
+
connect();
|
|
230
|
+
}
|
|
231
|
+
// Delayed cleanup on unmount to handle Strict Mode
|
|
232
|
+
return () => {
|
|
233
|
+
isMountedRef.current = false;
|
|
234
|
+
// Delay cleanup by 100ms to handle Strict Mode remounting
|
|
235
|
+
cleanupDelayTimeoutRef.current = setTimeout(() => {
|
|
236
|
+
// Only cleanup if component hasn't remounted
|
|
237
|
+
if (!isMountedRef.current) {
|
|
238
|
+
isManualDisconnectRef.current = true;
|
|
239
|
+
cleanup();
|
|
240
|
+
}
|
|
241
|
+
}, 100);
|
|
242
|
+
};
|
|
243
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
244
|
+
}, [url, connectOnMount]);
|
|
245
|
+
return {
|
|
246
|
+
sendMessage,
|
|
247
|
+
sendRawMessage,
|
|
248
|
+
lastRawMessage,
|
|
249
|
+
lastMessage,
|
|
250
|
+
connectionStatus,
|
|
251
|
+
connect,
|
|
252
|
+
disconnect,
|
|
253
|
+
isConnected: connectionStatus === WebSocketConnectionStatus.CONNECTED,
|
|
254
|
+
reconnectAttempts,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=useWebSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebSocket.js","sourceRoot":"","sources":["../../src/schema/useWebSocket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { useEffect, useRef, useCallback, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export enum WebSocketConnectionStatus {
|
|
4
|
+
CONNECTING = "CONNECTING",
|
|
5
|
+
CONNECTED = "CONNECTED",
|
|
6
|
+
DISCONNECTED = "DISCONNECTED",
|
|
7
|
+
ERROR = "ERROR",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface UseWebSocketOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Callback fired when a message is received
|
|
13
|
+
*/
|
|
14
|
+
onMessage?: (event: MessageEvent) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Callback fired when connection opens
|
|
18
|
+
*/
|
|
19
|
+
onOpen?: (event: Event) => void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Callback fired when connection closes
|
|
23
|
+
*/
|
|
24
|
+
onClose?: (event: CloseEvent) => void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Callback fired on error
|
|
28
|
+
*/
|
|
29
|
+
onError?: (event: Event) => void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Enable automatic reconnection
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
reconnect?: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of reconnection attempts
|
|
39
|
+
* @default Infinity
|
|
40
|
+
*/
|
|
41
|
+
maxReconnectAttempts?: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initial reconnection delay in milliseconds
|
|
45
|
+
* @default 1000
|
|
46
|
+
*/
|
|
47
|
+
reconnectInterval?: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Maximum reconnection delay in milliseconds
|
|
51
|
+
* @default 30000
|
|
52
|
+
*/
|
|
53
|
+
maxReconnectInterval?: number;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Multiplier for exponential backoff
|
|
57
|
+
* @default 1.5
|
|
58
|
+
*/
|
|
59
|
+
reconnectBackoffMultiplier?: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* WebSocket protocols
|
|
63
|
+
*/
|
|
64
|
+
protocols?: string | string[];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Should connect immediately on mount
|
|
68
|
+
* @default true
|
|
69
|
+
*/
|
|
70
|
+
connectOnMount?: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface UseWebSocketReturn<TInput = any, TOutput = any> {
|
|
74
|
+
/**
|
|
75
|
+
* Send a message through the WebSocket (client to server)
|
|
76
|
+
*/
|
|
77
|
+
sendMessage: (message: TInput) => void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Send a raw string message
|
|
81
|
+
*/
|
|
82
|
+
sendRawMessage: (message: string) => void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The last received message (raw MessageEvent)
|
|
86
|
+
*/
|
|
87
|
+
lastRawMessage: MessageEvent | null;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The last received message parsed as JSON (server to client)
|
|
91
|
+
*/
|
|
92
|
+
lastMessage: TOutput | null;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Current connection status
|
|
96
|
+
*/
|
|
97
|
+
connectionStatus: WebSocketConnectionStatus;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Manually connect to the WebSocket
|
|
101
|
+
*/
|
|
102
|
+
connect: () => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Manually disconnect from the WebSocket
|
|
106
|
+
*/
|
|
107
|
+
disconnect: () => void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if currently connected
|
|
111
|
+
*/
|
|
112
|
+
isConnected: boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Number of reconnection attempts made
|
|
116
|
+
*/
|
|
117
|
+
reconnectAttempts: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Advanced WebSocket hook with reconnection logic and type safety
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* interface ClientMessage {
|
|
126
|
+
* type: 'subscribe';
|
|
127
|
+
* channel: string;
|
|
128
|
+
* }
|
|
129
|
+
*
|
|
130
|
+
* interface ServerMessage {
|
|
131
|
+
* type: 'update';
|
|
132
|
+
* data: any;
|
|
133
|
+
* }
|
|
134
|
+
*
|
|
135
|
+
* const { sendMessage, lastParsedMessage, connectionStatus, isConnected } =
|
|
136
|
+
* useWebSocket<ClientMessage, ServerMessage>(
|
|
137
|
+
* 'wss://api.example.com',
|
|
138
|
+
* {
|
|
139
|
+
* onMessage: (event) => console.log('Received:', event.data),
|
|
140
|
+
* reconnect: true,
|
|
141
|
+
* maxReconnectAttempts: 10,
|
|
142
|
+
* }
|
|
143
|
+
* );
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function useWebSocket<TInput = any, TOutput = any>(
|
|
147
|
+
url: string | null,
|
|
148
|
+
options: UseWebSocketOptions = {},
|
|
149
|
+
): UseWebSocketReturn<TInput, TOutput> {
|
|
150
|
+
const {
|
|
151
|
+
onMessage,
|
|
152
|
+
onOpen,
|
|
153
|
+
onClose,
|
|
154
|
+
onError,
|
|
155
|
+
reconnect = true,
|
|
156
|
+
maxReconnectAttempts = Infinity,
|
|
157
|
+
reconnectInterval = 1000,
|
|
158
|
+
maxReconnectInterval = 30000,
|
|
159
|
+
reconnectBackoffMultiplier = 1.5,
|
|
160
|
+
protocols,
|
|
161
|
+
connectOnMount = true,
|
|
162
|
+
} = options;
|
|
163
|
+
|
|
164
|
+
// Use refs to store values that shouldn't trigger re-renders
|
|
165
|
+
const webSocketRef = useRef<WebSocket | null>(null);
|
|
166
|
+
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
167
|
+
|
|
168
|
+
const reconnectAttemptsRef = useRef(0);
|
|
169
|
+
const currentReconnectIntervalRef = useRef(reconnectInterval);
|
|
170
|
+
const shouldReconnectRef = useRef(reconnect);
|
|
171
|
+
const isManualDisconnectRef = useRef(false);
|
|
172
|
+
const cleanupDelayTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
173
|
+
const isMountedRef = useRef(false);
|
|
174
|
+
|
|
175
|
+
// Store callbacks in refs to avoid recreating WebSocket on callback changes
|
|
176
|
+
const onMessageRef = useRef(onMessage);
|
|
177
|
+
const onOpenRef = useRef(onOpen);
|
|
178
|
+
const onCloseRef = useRef(onClose);
|
|
179
|
+
const onErrorRef = useRef(onError);
|
|
180
|
+
|
|
181
|
+
// Update refs when callbacks change
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
onMessageRef.current = onMessage;
|
|
184
|
+
}, [onMessage]);
|
|
185
|
+
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
onOpenRef.current = onOpen;
|
|
188
|
+
}, [onOpen]);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
onCloseRef.current = onClose;
|
|
192
|
+
}, [onClose]);
|
|
193
|
+
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
onErrorRef.current = onError;
|
|
196
|
+
}, [onError]);
|
|
197
|
+
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
shouldReconnectRef.current = reconnect;
|
|
200
|
+
}, [reconnect]);
|
|
201
|
+
|
|
202
|
+
// State for values that should trigger re-renders
|
|
203
|
+
const [lastRawMessage, setLastMessage] = useState<MessageEvent | null>(null);
|
|
204
|
+
const [lastMessage, setLastParsedMessage] = useState<TOutput | null>(null);
|
|
205
|
+
const [connectionStatus, setConnectionStatus] =
|
|
206
|
+
useState<WebSocketConnectionStatus>(WebSocketConnectionStatus.DISCONNECTED);
|
|
207
|
+
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
|
208
|
+
|
|
209
|
+
// Clear all timers
|
|
210
|
+
const clearTimers = useCallback(() => {
|
|
211
|
+
if (reconnectTimeoutRef.current) {
|
|
212
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
213
|
+
reconnectTimeoutRef.current = null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
217
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
218
|
+
cleanupDelayTimeoutRef.current = null;
|
|
219
|
+
}
|
|
220
|
+
}, []);
|
|
221
|
+
|
|
222
|
+
// Cleanup WebSocket
|
|
223
|
+
const cleanup = useCallback(() => {
|
|
224
|
+
clearTimers();
|
|
225
|
+
|
|
226
|
+
if (webSocketRef.current) {
|
|
227
|
+
const ws = webSocketRef.current;
|
|
228
|
+
webSocketRef.current = null;
|
|
229
|
+
|
|
230
|
+
// Remove all event listeners before closing
|
|
231
|
+
ws.onopen = null;
|
|
232
|
+
ws.onclose = null;
|
|
233
|
+
ws.onerror = null;
|
|
234
|
+
ws.onmessage = null;
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
ws.readyState === WebSocket.OPEN ||
|
|
238
|
+
ws.readyState === WebSocket.CONNECTING
|
|
239
|
+
) {
|
|
240
|
+
ws.close(1000, "Component unmounting or reconnecting");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}, [clearTimers]);
|
|
244
|
+
|
|
245
|
+
// Connect to WebSocket
|
|
246
|
+
const connect = useCallback(() => {
|
|
247
|
+
if (!url) return;
|
|
248
|
+
|
|
249
|
+
// Cancel any pending delayed cleanup
|
|
250
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
251
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
252
|
+
cleanupDelayTimeoutRef.current = null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Prevent multiple simultaneous connections
|
|
256
|
+
if (
|
|
257
|
+
webSocketRef.current?.readyState === WebSocket.CONNECTING ||
|
|
258
|
+
webSocketRef.current?.readyState === WebSocket.OPEN
|
|
259
|
+
) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
cleanup();
|
|
264
|
+
isManualDisconnectRef.current = false;
|
|
265
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTING);
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
269
|
+
|
|
270
|
+
webSocketRef.current = ws;
|
|
271
|
+
|
|
272
|
+
ws.onopen = (event) => {
|
|
273
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTED);
|
|
274
|
+
reconnectAttemptsRef.current = 0;
|
|
275
|
+
currentReconnectIntervalRef.current = reconnectInterval;
|
|
276
|
+
setReconnectAttempts(0);
|
|
277
|
+
|
|
278
|
+
onOpenRef.current?.(event);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
ws.onclose = (event) => {
|
|
282
|
+
setConnectionStatus(WebSocketConnectionStatus.DISCONNECTED);
|
|
283
|
+
clearTimers();
|
|
284
|
+
onCloseRef.current?.(event);
|
|
285
|
+
|
|
286
|
+
// Attempt reconnection if enabled and not manually disconnected
|
|
287
|
+
if (
|
|
288
|
+
shouldReconnectRef.current &&
|
|
289
|
+
!isManualDisconnectRef.current &&
|
|
290
|
+
reconnectAttemptsRef.current < maxReconnectAttempts
|
|
291
|
+
) {
|
|
292
|
+
reconnectAttemptsRef.current += 1;
|
|
293
|
+
setReconnectAttempts(reconnectAttemptsRef.current);
|
|
294
|
+
|
|
295
|
+
const delay = Math.min(
|
|
296
|
+
currentReconnectIntervalRef.current,
|
|
297
|
+
maxReconnectInterval,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
301
|
+
currentReconnectIntervalRef.current *= reconnectBackoffMultiplier;
|
|
302
|
+
connect();
|
|
303
|
+
}, delay);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
ws.onerror = (event) => {
|
|
308
|
+
setConnectionStatus(WebSocketConnectionStatus.ERROR);
|
|
309
|
+
onErrorRef.current?.(event);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
ws.onmessage = (event) => {
|
|
313
|
+
setLastMessage(event);
|
|
314
|
+
|
|
315
|
+
// Attempt to parse JSON data for typed message
|
|
316
|
+
try {
|
|
317
|
+
const parsed =
|
|
318
|
+
typeof event.data === "string"
|
|
319
|
+
? JSON.parse(event.data)
|
|
320
|
+
: event.data;
|
|
321
|
+
setLastParsedMessage(parsed as TOutput);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
// If parsing fails, set parsed message to null
|
|
324
|
+
setLastParsedMessage(null);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
onMessageRef.current?.(event);
|
|
328
|
+
};
|
|
329
|
+
} catch (error) {
|
|
330
|
+
setConnectionStatus(WebSocketConnectionStatus.ERROR);
|
|
331
|
+
console.error("WebSocket connection error:", error);
|
|
332
|
+
}
|
|
333
|
+
}, [
|
|
334
|
+
url,
|
|
335
|
+
protocols,
|
|
336
|
+
reconnectInterval,
|
|
337
|
+
maxReconnectInterval,
|
|
338
|
+
maxReconnectAttempts,
|
|
339
|
+
reconnectBackoffMultiplier,
|
|
340
|
+
cleanup,
|
|
341
|
+
clearTimers,
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
// Disconnect from WebSocket
|
|
345
|
+
const disconnect = useCallback(() => {
|
|
346
|
+
isManualDisconnectRef.current = true;
|
|
347
|
+
cleanup();
|
|
348
|
+
setConnectionStatus(WebSocketConnectionStatus.DISCONNECTED);
|
|
349
|
+
reconnectAttemptsRef.current = 0;
|
|
350
|
+
setReconnectAttempts(0);
|
|
351
|
+
currentReconnectIntervalRef.current = reconnectInterval;
|
|
352
|
+
}, [cleanup, reconnectInterval]);
|
|
353
|
+
|
|
354
|
+
// Send typed message
|
|
355
|
+
const sendMessage = useCallback((message: TInput) => {
|
|
356
|
+
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
|
357
|
+
const messageStr =
|
|
358
|
+
typeof message === "string" ? message : JSON.stringify(message);
|
|
359
|
+
webSocketRef.current.send(messageStr);
|
|
360
|
+
} else {
|
|
361
|
+
console.warn("WebSocket is not connected. Message not sent:", message);
|
|
362
|
+
}
|
|
363
|
+
}, []);
|
|
364
|
+
|
|
365
|
+
// Send raw string message
|
|
366
|
+
const sendRawMessage = useCallback((message: string) => {
|
|
367
|
+
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
|
368
|
+
webSocketRef.current.send(message);
|
|
369
|
+
} else {
|
|
370
|
+
console.warn("WebSocket is not connected. Message not sent:", message);
|
|
371
|
+
}
|
|
372
|
+
}, []);
|
|
373
|
+
|
|
374
|
+
// Connect on mount if enabled
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
isMountedRef.current = true;
|
|
377
|
+
|
|
378
|
+
// Cancel any pending cleanup from previous unmount (Strict Mode case)
|
|
379
|
+
if (cleanupDelayTimeoutRef.current) {
|
|
380
|
+
clearTimeout(cleanupDelayTimeoutRef.current);
|
|
381
|
+
cleanupDelayTimeoutRef.current = null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// If connection exists and is open/connecting, reuse it
|
|
385
|
+
if (
|
|
386
|
+
webSocketRef.current &&
|
|
387
|
+
(webSocketRef.current.readyState === WebSocket.OPEN ||
|
|
388
|
+
webSocketRef.current.readyState === WebSocket.CONNECTING)
|
|
389
|
+
) {
|
|
390
|
+
// Connection already exists, just update status if needed
|
|
391
|
+
if (webSocketRef.current.readyState === WebSocket.OPEN) {
|
|
392
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTED);
|
|
393
|
+
} else {
|
|
394
|
+
setConnectionStatus(WebSocketConnectionStatus.CONNECTING);
|
|
395
|
+
}
|
|
396
|
+
} else if (connectOnMount && url) {
|
|
397
|
+
// No existing connection, create new one
|
|
398
|
+
connect();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Delayed cleanup on unmount to handle Strict Mode
|
|
402
|
+
return () => {
|
|
403
|
+
isMountedRef.current = false;
|
|
404
|
+
|
|
405
|
+
// Delay cleanup by 100ms to handle Strict Mode remounting
|
|
406
|
+
cleanupDelayTimeoutRef.current = setTimeout(() => {
|
|
407
|
+
// Only cleanup if component hasn't remounted
|
|
408
|
+
if (!isMountedRef.current) {
|
|
409
|
+
isManualDisconnectRef.current = true;
|
|
410
|
+
cleanup();
|
|
411
|
+
}
|
|
412
|
+
}, 100);
|
|
413
|
+
};
|
|
414
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
415
|
+
}, [url, connectOnMount]);
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
sendMessage,
|
|
419
|
+
sendRawMessage,
|
|
420
|
+
lastRawMessage,
|
|
421
|
+
lastMessage,
|
|
422
|
+
connectionStatus,
|
|
423
|
+
connect,
|
|
424
|
+
disconnect,
|
|
425
|
+
isConnected: connectionStatus === WebSocketConnectionStatus.CONNECTED,
|
|
426
|
+
reconnectAttempts,
|
|
427
|
+
};
|
|
428
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -4,15 +4,13 @@ import { z } from "zod";
|
|
|
4
4
|
import { ConnectionWritter } from "./connection";
|
|
5
5
|
import { BidirectionalConnection } from "./bidirectional-connection";
|
|
6
6
|
export type ContextType = Map<string, any>;
|
|
7
|
-
type SubscriptionHandler<P extends Procedure<ProcedureType, any, any>> = (args: z.infer<P["input"]>, request: BunRequest, context: ContextType,
|
|
8
|
-
connection: ConnectionWritter<P>) => Promise<undefined>;
|
|
7
|
+
type SubscriptionHandler<P extends Procedure<ProcedureType, any, any>> = (args: z.infer<P["input"]>, request: BunRequest, context: ContextType, connection: ConnectionWritter<P>) => Promise<undefined>;
|
|
9
8
|
type NormalProcedureHandler<P extends Procedure<ProcedureType, any, any>> = (args: z.infer<P["input"]>, request: BunRequest, context: ContextType) => Promise<z.infer<P["output"]>>;
|
|
10
9
|
type ProcedureHandler<P extends Procedure<ProcedureType, any, any>> = P["method"] extends "SUBSCRIPTION" ? SubscriptionHandler<P> : P["method"] extends "BIDIRECTIONAL" ? (initialRequest: BunRequest, bidirectionalconnection: BidirectionalConnection<P["input"], P["output"]>, context: ContextType) => Promise<undefined> : NormalProcedureHandler<P>;
|
|
11
10
|
type FullImplementation<SchemaT extends APISchema> = {
|
|
12
11
|
[ServiceName in keyof SchemaT["services"]]: ServiceImplementationHandlers<SchemaT["services"][ServiceName]>;
|
|
13
12
|
};
|
|
14
|
-
export type MiddlewareFunction = (req: BunRequest, procedureName: string,
|
|
15
|
-
context: ContextType) => Promise<void>;
|
|
13
|
+
export type MiddlewareFunction = (req: BunRequest, procedureName: string, context: ContextType) => Promise<void>;
|
|
16
14
|
type ServiceImplementationHandlers<ServiceT extends Service> = {
|
|
17
15
|
[ProcName in keyof ServiceT["procedures"]]: ProcedureHandler<ServiceT["procedures"][ProcName]>;
|
|
18
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,EACN,SAAS,EACT,OAAO,EACP,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAc,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE3C,KAAK,mBAAmB,CAAC,CAAC,SAAS,SAAS,CAAC,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CACxE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EACzB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zynapse",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "A TypeScript library providing a schema-based API system with type-safe server implementations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/schema/index.js",
|
|
@@ -27,8 +27,9 @@
|
|
|
27
27
|
"LICENSE"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "tsgo -p tsconfig.build.json && bun run fix-cli-shebang",
|
|
30
|
+
"build": "tsgo -p tsconfig.build.json && bun run fix-cli-shebang && bun run copy-template",
|
|
31
31
|
"fix-cli-shebang": "echo '#!/usr/bin/env bun' | cat - dist/cli/index.js > dist/cli/index.js.tmp && mv dist/cli/index.js.tmp dist/cli/index.js && chmod +x dist/cli/index.js",
|
|
32
|
+
"copy-template": "cp src/schema/useWebSocket.ts dist/schema/useWebSocket.ts",
|
|
32
33
|
"prepublishOnly": "bun run build",
|
|
33
34
|
"test": "bun test"
|
|
34
35
|
},
|
|
@@ -40,18 +41,20 @@
|
|
|
40
41
|
"type-safe"
|
|
41
42
|
],
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@types/bun": "^1.3.
|
|
44
|
-
"@
|
|
45
|
-
"typescript": "^
|
|
44
|
+
"@types/bun": "^1.3.5",
|
|
45
|
+
"@types/react": "^19.2.7",
|
|
46
|
+
"@typescript/native-preview": "^7.0.0-dev.20260105.1",
|
|
47
|
+
"react": "^19.2.3",
|
|
48
|
+
"typescript": "^5.9.3"
|
|
46
49
|
},
|
|
47
50
|
"peerDependencies": {
|
|
48
51
|
"typescript": "^5"
|
|
49
52
|
},
|
|
50
53
|
"dependencies": {
|
|
51
|
-
"json-schema-to-zod": "^2.
|
|
52
|
-
"prettier": "^3.
|
|
53
|
-
"zod": "^3.
|
|
54
|
-
"zod-to-json-schema": "^3.
|
|
54
|
+
"json-schema-to-zod": "^2.7.0",
|
|
55
|
+
"prettier": "^3.7.4",
|
|
56
|
+
"zod": "^3.25.76",
|
|
57
|
+
"zod-to-json-schema": "^3.25.1",
|
|
55
58
|
"zod-to-ts": "^1.2.0"
|
|
56
59
|
}
|
|
57
60
|
}
|