@synap-core/client 0.1.0 → 0.2.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.
@@ -0,0 +1,66 @@
1
+ // src/react/provider.tsx
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
+ import { createTRPCReact, httpBatchLink } from "@trpc/react-query";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var trpc = createTRPCReact();
7
+ var SynapContext = createContext(null);
8
+ function SynapProvider({ url = "http://localhost:3000", getToken, headers, children }) {
9
+ const queryClient = useMemo(() => new QueryClient({
10
+ defaultOptions: {
11
+ queries: {
12
+ staleTime: 5 * 60 * 1e3,
13
+ // 5 minutes
14
+ retry: 1
15
+ }
16
+ }
17
+ }), []);
18
+ const trpcClient = useMemo(() => trpc.createClient({
19
+ links: [
20
+ httpBatchLink({
21
+ url: `${url}/trpc`,
22
+ async headers() {
23
+ const baseHeaders = {
24
+ ...headers
25
+ };
26
+ if (getToken) {
27
+ const token = await getToken();
28
+ if (token) {
29
+ baseHeaders["Authorization"] = `Bearer ${token}`;
30
+ }
31
+ }
32
+ return baseHeaders;
33
+ },
34
+ fetch(url2, options) {
35
+ return fetch(url2, {
36
+ ...options,
37
+ credentials: "include"
38
+ // Send cookies
39
+ });
40
+ }
41
+ })
42
+ ]
43
+ }), [url, getToken, headers]);
44
+ const contextValue = useMemo(() => ({
45
+ api: trpc,
46
+ url
47
+ }), [url]);
48
+ return /* @__PURE__ */ jsx(SynapContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(trpc.Provider, { client: trpcClient, queryClient, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children }) }) });
49
+ }
50
+ function useSynap() {
51
+ const context = useContext(SynapContext);
52
+ if (!context) {
53
+ throw new Error("useSynap must be used within a SynapProvider");
54
+ }
55
+ return context;
56
+ }
57
+ function useSynapClient() {
58
+ return useSynap().api;
59
+ }
60
+
61
+ export {
62
+ trpc,
63
+ SynapProvider,
64
+ useSynap,
65
+ useSynapClient
66
+ };
@@ -0,0 +1,118 @@
1
+ // src/realtime.ts
2
+ import { io } from "socket.io-client";
3
+ import * as Y from "yjs";
4
+ var RealtimeClient = class {
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ presenceSocket = null;
9
+ yjsSocket = null;
10
+ listeners = /* @__PURE__ */ new Map();
11
+ /**
12
+ * Connect to presence namespace for real-time collaboration
13
+ */
14
+ connectPresence(viewId, viewType = "document") {
15
+ if (this.presenceSocket) {
16
+ this.presenceSocket.disconnect();
17
+ }
18
+ this.presenceSocket = io(`${this.config.url}/presence`, {
19
+ auth: {
20
+ ...this.config.auth,
21
+ viewId,
22
+ viewType
23
+ }
24
+ });
25
+ this.presenceSocket.on("presence:init", (data) => {
26
+ this.emit("presence:init", data);
27
+ });
28
+ this.presenceSocket.on("user:joined", (user) => {
29
+ this.emit("user-joined", user);
30
+ });
31
+ this.presenceSocket.on("user:left", (data) => {
32
+ this.emit("user-left", data);
33
+ });
34
+ this.presenceSocket.on("cursor:update", (data) => {
35
+ this.emit("cursor-update", data);
36
+ });
37
+ this.presenceSocket.on("typing", (data) => {
38
+ this.emit("typing", data);
39
+ });
40
+ }
41
+ /**
42
+ * Connect to Yjs namespace for CRDT synchronization
43
+ *
44
+ * @returns Yjs document that auto-syncs with server
45
+ */
46
+ connectYjs(roomName) {
47
+ if (this.yjsSocket) {
48
+ this.yjsSocket.disconnect();
49
+ }
50
+ const ydoc = new Y.Doc();
51
+ this.yjsSocket = io(`${this.config.url}/yjs`, {
52
+ query: { room: roomName }
53
+ });
54
+ this.yjsSocket.on("yjs:sync", (message) => {
55
+ Y.applyUpdate(ydoc, Uint8Array.from(message));
56
+ });
57
+ this.yjsSocket.on("yjs:update", (message) => {
58
+ Y.applyUpdate(ydoc, Uint8Array.from(message), this.yjsSocket);
59
+ });
60
+ ydoc.on("update", (update, origin) => {
61
+ if (origin !== this.yjsSocket) {
62
+ this.yjsSocket?.emit("yjs:update", Array.from(update));
63
+ }
64
+ });
65
+ return ydoc;
66
+ }
67
+ /**
68
+ * Subscribe to events
69
+ */
70
+ on(event, callback) {
71
+ if (!this.listeners.has(event)) {
72
+ this.listeners.set(event, /* @__PURE__ */ new Set());
73
+ }
74
+ this.listeners.get(event).add(callback);
75
+ }
76
+ /**
77
+ * Unsubscribe from events
78
+ */
79
+ off(event, callback) {
80
+ this.listeners.get(event)?.delete(callback);
81
+ }
82
+ /**
83
+ * Emit event to all listeners
84
+ */
85
+ emit(event, data) {
86
+ this.listeners.get(event)?.forEach((cb) => cb(data));
87
+ }
88
+ /**
89
+ * Move cursor (presence)
90
+ */
91
+ moveCursor(x, y) {
92
+ this.presenceSocket?.emit("cursor:move", { x, y });
93
+ }
94
+ /**
95
+ * Set typing indicator
96
+ */
97
+ setTyping(isTyping) {
98
+ this.presenceSocket?.emit("typing", isTyping);
99
+ }
100
+ /**
101
+ * Disconnect all connections
102
+ */
103
+ disconnect() {
104
+ this.presenceSocket?.disconnect();
105
+ this.yjsSocket?.disconnect();
106
+ this.presenceSocket = null;
107
+ this.yjsSocket = null;
108
+ this.listeners.clear();
109
+ }
110
+ };
111
+ function createRealtimeClient(config) {
112
+ return new RealtimeClient(config);
113
+ }
114
+
115
+ export {
116
+ RealtimeClient,
117
+ createRealtimeClient
118
+ };
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -25,8 +35,124 @@ __export(index_exports, {
25
35
  });
26
36
  module.exports = __toCommonJS(index_exports);
27
37
  var import_client = require("@trpc/client");
38
+
39
+ // src/realtime.ts
40
+ var import_socket = require("socket.io-client");
41
+ var Y = __toESM(require("yjs"), 1);
42
+ var RealtimeClient = class {
43
+ constructor(config) {
44
+ this.config = config;
45
+ }
46
+ presenceSocket = null;
47
+ yjsSocket = null;
48
+ listeners = /* @__PURE__ */ new Map();
49
+ /**
50
+ * Connect to presence namespace for real-time collaboration
51
+ */
52
+ connectPresence(viewId, viewType = "document") {
53
+ if (this.presenceSocket) {
54
+ this.presenceSocket.disconnect();
55
+ }
56
+ this.presenceSocket = (0, import_socket.io)(`${this.config.url}/presence`, {
57
+ auth: {
58
+ ...this.config.auth,
59
+ viewId,
60
+ viewType
61
+ }
62
+ });
63
+ this.presenceSocket.on("presence:init", (data) => {
64
+ this.emit("presence:init", data);
65
+ });
66
+ this.presenceSocket.on("user:joined", (user) => {
67
+ this.emit("user-joined", user);
68
+ });
69
+ this.presenceSocket.on("user:left", (data) => {
70
+ this.emit("user-left", data);
71
+ });
72
+ this.presenceSocket.on("cursor:update", (data) => {
73
+ this.emit("cursor-update", data);
74
+ });
75
+ this.presenceSocket.on("typing", (data) => {
76
+ this.emit("typing", data);
77
+ });
78
+ }
79
+ /**
80
+ * Connect to Yjs namespace for CRDT synchronization
81
+ *
82
+ * @returns Yjs document that auto-syncs with server
83
+ */
84
+ connectYjs(roomName) {
85
+ if (this.yjsSocket) {
86
+ this.yjsSocket.disconnect();
87
+ }
88
+ const ydoc = new Y.Doc();
89
+ this.yjsSocket = (0, import_socket.io)(`${this.config.url}/yjs`, {
90
+ query: { room: roomName }
91
+ });
92
+ this.yjsSocket.on("yjs:sync", (message) => {
93
+ Y.applyUpdate(ydoc, Uint8Array.from(message));
94
+ });
95
+ this.yjsSocket.on("yjs:update", (message) => {
96
+ Y.applyUpdate(ydoc, Uint8Array.from(message), this.yjsSocket);
97
+ });
98
+ ydoc.on("update", (update, origin) => {
99
+ if (origin !== this.yjsSocket) {
100
+ this.yjsSocket?.emit("yjs:update", Array.from(update));
101
+ }
102
+ });
103
+ return ydoc;
104
+ }
105
+ /**
106
+ * Subscribe to events
107
+ */
108
+ on(event, callback) {
109
+ if (!this.listeners.has(event)) {
110
+ this.listeners.set(event, /* @__PURE__ */ new Set());
111
+ }
112
+ this.listeners.get(event).add(callback);
113
+ }
114
+ /**
115
+ * Unsubscribe from events
116
+ */
117
+ off(event, callback) {
118
+ this.listeners.get(event)?.delete(callback);
119
+ }
120
+ /**
121
+ * Emit event to all listeners
122
+ */
123
+ emit(event, data) {
124
+ this.listeners.get(event)?.forEach((cb) => cb(data));
125
+ }
126
+ /**
127
+ * Move cursor (presence)
128
+ */
129
+ moveCursor(x, y) {
130
+ this.presenceSocket?.emit("cursor:move", { x, y });
131
+ }
132
+ /**
133
+ * Set typing indicator
134
+ */
135
+ setTyping(isTyping) {
136
+ this.presenceSocket?.emit("typing", isTyping);
137
+ }
138
+ /**
139
+ * Disconnect all connections
140
+ */
141
+ disconnect() {
142
+ this.presenceSocket?.disconnect();
143
+ this.yjsSocket?.disconnect();
144
+ this.presenceSocket = null;
145
+ this.yjsSocket = null;
146
+ this.listeners.clear();
147
+ }
148
+ };
149
+ function createRealtimeClient(config) {
150
+ return new RealtimeClient(config);
151
+ }
152
+
153
+ // src/index.ts
28
154
  function createSynapClient(config) {
29
- return (0, import_client.createTRPCClient)({
155
+ const trpcClient = (0, import_client.createTRPCClient)({
30
156
  links: [
31
157
  (0, import_client.httpBatchLink)({
32
158
  url: `${config.url}/trpc`,
@@ -35,6 +161,14 @@ function createSynapClient(config) {
35
161
  })
36
162
  ]
37
163
  });
164
+ const realtimeClient = config.realtime ? createRealtimeClient({
165
+ url: config.url,
166
+ auth: config.realtime
167
+ }) : null;
168
+ return {
169
+ trpc: trpcClient,
170
+ realtime: realtimeClient
171
+ };
38
172
  }
39
173
  var index_default = createSynapClient;
40
174
  // Annotate the CommonJS export names for ESM import in node: