@sansavision/create-pulse 0.4.3 → 0.4.5
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/README.md +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/templates/aurora-auth-node-demo/README.md +43 -0
- package/templates/aurora-auth-node-demo/aurora.config.ts +15 -0
- package/templates/aurora-auth-node-demo/bun.lock +679 -0
- package/templates/aurora-auth-node-demo/drizzle.config.ts +9 -0
- package/templates/aurora-auth-node-demo/package.json +39 -0
- package/templates/aurora-auth-node-demo/postcss.config.mjs +7 -0
- package/templates/aurora-auth-node-demo/server.mjs +46 -0
- package/templates/aurora-auth-node-demo/src/actions/createMessage.action.server.ts +31 -0
- package/templates/aurora-auth-node-demo/src/aurora.auth.ts +65 -0
- package/templates/aurora-auth-node-demo/src/lib/auth-client.ts +30 -0
- package/templates/aurora-auth-node-demo/src/lib/auth.server.ts +11 -0
- package/templates/aurora-auth-node-demo/src/lib/auth.ts +30 -0
- package/templates/aurora-auth-node-demo/src/lib/db.ts +6 -0
- package/templates/aurora-auth-node-demo/src/lib/pulse.ts +45 -0
- package/templates/aurora-auth-node-demo/src/lib/schema.ts +107 -0
- package/templates/aurora-auth-node-demo/src/queries/listMessages.server.ts +25 -0
- package/templates/aurora-auth-node-demo/src/routes/api/auth/[...slug]/handler.ts +14 -0
- package/templates/aurora-auth-node-demo/src/routes/api/pulse/verify/handler.ts +55 -0
- package/templates/aurora-auth-node-demo/src/routes/auth/sign-in/page.client.tsx +132 -0
- package/templates/aurora-auth-node-demo/src/routes/auth/sign-in/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/auth/sign-up/page.client.tsx +154 -0
- package/templates/aurora-auth-node-demo/src/routes/auth/sign-up/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/arena-game/page.client.tsx +640 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/arena-game/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/chat/page.client.tsx +349 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/chat/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/encrypted-chat/page.client.tsx +472 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/encrypted-chat/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/game-sync/page.client.tsx +375 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/game-sync/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/queues/page.client.tsx +423 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/queues/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/video-call/page.client.tsx +840 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/video-call/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/watch-together/page.client.tsx +722 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/demos/watch-together/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/layout.client.tsx +113 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/layout.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/page.client.tsx +195 -0
- package/templates/aurora-auth-node-demo/src/routes/dashboard/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/routes/favicon.ico +0 -0
- package/templates/aurora-auth-node-demo/src/routes/layout.tsx +18 -0
- package/templates/aurora-auth-node-demo/src/routes/page.client.tsx +263 -0
- package/templates/aurora-auth-node-demo/src/routes/page.tsx +5 -0
- package/templates/aurora-auth-node-demo/src/styles/app.css +96 -0
- package/templates/aurora-auth-node-demo/tsconfig.json +27 -0
- package/templates/aurora-auth-node-demo/tsconfig.tsbuildinfo +1 -0
- package/templates/nextjs-auth-demo/README.md +1 -1
- package/templates/nextjs-auth-demo/next-env.d.ts +1 -1
- package/templates/nextjs-auth-demo/package.json +8 -7
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/arena-game/page.tsx +640 -0
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/chat/page.tsx +124 -23
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/encrypted-chat/page.tsx +350 -76
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/game-sync/page.tsx +232 -49
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/queues/page.tsx +213 -87
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/video-call/page.tsx +840 -0
- package/templates/nextjs-auth-demo/src/app/dashboard/demos/watch-together/page.tsx +589 -123
- package/templates/nextjs-auth-demo/src/app/dashboard/layout.tsx +4 -0
- package/templates/nextjs-auth-demo/src/app/dashboard/page.tsx +53 -5
- package/templates/nextjs-auth-demo/src/app/layout.tsx +1 -1
- package/templates/nextjs-auth-node-demo/.env.example +10 -0
- package/templates/nextjs-auth-node-demo/Dockerfile +19 -0
- package/templates/nextjs-auth-node-demo/README.md +159 -0
- package/templates/nextjs-auth-node-demo/_gitignore +33 -0
- package/templates/nextjs-auth-node-demo/drizzle.config.ts +10 -0
- package/templates/nextjs-auth-node-demo/eslint.config.mjs +18 -0
- package/templates/nextjs-auth-node-demo/next-env.d.ts +6 -0
- package/templates/nextjs-auth-node-demo/next.config.ts +7 -0
- package/templates/nextjs-auth-node-demo/package.json +38 -0
- package/templates/nextjs-auth-node-demo/postcss.config.mjs +7 -0
- package/templates/nextjs-auth-node-demo/public/file.svg +1 -0
- package/templates/nextjs-auth-node-demo/public/globe.svg +1 -0
- package/templates/nextjs-auth-node-demo/public/next.svg +1 -0
- package/templates/nextjs-auth-node-demo/public/vercel.svg +1 -0
- package/templates/nextjs-auth-node-demo/public/window.svg +1 -0
- package/templates/nextjs-auth-node-demo/server.mjs +45 -0
- package/templates/nextjs-auth-node-demo/src/app/api/auth/[...all]/route.ts +4 -0
- package/templates/nextjs-auth-node-demo/src/app/api/pulse/verify/route.ts +54 -0
- package/templates/nextjs-auth-node-demo/src/app/auth/sign-in/page.tsx +131 -0
- package/templates/nextjs-auth-node-demo/src/app/auth/sign-up/page.tsx +153 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/arena-game/page.tsx +640 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/chat/page.tsx +349 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/encrypted-chat/page.tsx +472 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/game-sync/page.tsx +375 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/queues/page.tsx +423 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/video-call/page.tsx +840 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/demos/watch-together/page.tsx +724 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/layout.tsx +113 -0
- package/templates/nextjs-auth-node-demo/src/app/dashboard/page.tsx +195 -0
- package/templates/nextjs-auth-node-demo/src/app/favicon.ico +0 -0
- package/templates/nextjs-auth-node-demo/src/app/globals.css +96 -0
- package/templates/nextjs-auth-node-demo/src/app/layout.tsx +27 -0
- package/templates/nextjs-auth-node-demo/src/app/page.tsx +254 -0
- package/templates/nextjs-auth-node-demo/src/lib/auth-client.ts +15 -0
- package/templates/nextjs-auth-node-demo/src/lib/auth.ts +14 -0
- package/templates/nextjs-auth-node-demo/src/lib/db.ts +6 -0
- package/templates/nextjs-auth-node-demo/src/lib/pulse.ts +45 -0
- package/templates/nextjs-auth-node-demo/src/lib/schema.ts +107 -0
- package/templates/nextjs-auth-node-demo/tsconfig.json +34 -0
|
@@ -25,7 +25,15 @@ interface ChatMessage {
|
|
|
25
25
|
export default function ChatDemoPage() {
|
|
26
26
|
const { data: session } = useSession();
|
|
27
27
|
const [connected, setConnected] = useState(false);
|
|
28
|
-
const [messages, setMessages] = useState<ChatMessage[]>(
|
|
28
|
+
const [messages, setMessages] = useState<ChatMessage[]>(() => {
|
|
29
|
+
if (typeof window !== "undefined") {
|
|
30
|
+
try {
|
|
31
|
+
const stored = localStorage.getItem("pulse-chat-messages");
|
|
32
|
+
if (stored) return JSON.parse(stored);
|
|
33
|
+
} catch { /* ignore */ }
|
|
34
|
+
}
|
|
35
|
+
return [];
|
|
36
|
+
});
|
|
29
37
|
const [input, setInput] = useState("");
|
|
30
38
|
const [authUser, setAuthUser] = useState<{
|
|
31
39
|
id: string;
|
|
@@ -35,11 +43,24 @@ export default function ChatDemoPage() {
|
|
|
35
43
|
const [connecting, setConnecting] = useState(true);
|
|
36
44
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
37
45
|
const connRef = useRef<PulseConnection | null>(null);
|
|
46
|
+
const streamRef = useRef<ReturnType<PulseConnection["stream"]> | null>(null);
|
|
47
|
+
const sessionRef = useRef(session);
|
|
48
|
+
const seenIdsRef = useRef(new Set<string>());
|
|
49
|
+
|
|
50
|
+
// Keep session ref in sync
|
|
51
|
+
useEffect(() => { sessionRef.current = session; }, [session]);
|
|
38
52
|
|
|
39
53
|
const scrollToBottom = useCallback(() => {
|
|
40
54
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
41
55
|
}, []);
|
|
42
56
|
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (typeof window !== "undefined") {
|
|
59
|
+
localStorage.setItem("pulse-chat-messages", JSON.stringify(messages.slice(-100)));
|
|
60
|
+
}
|
|
61
|
+
}, [messages]);
|
|
62
|
+
|
|
63
|
+
// Connect to relay — run ONCE when session is available
|
|
43
64
|
useEffect(() => {
|
|
44
65
|
if (!session) return;
|
|
45
66
|
let cancelled = false;
|
|
@@ -53,39 +74,50 @@ export default function ChatDemoPage() {
|
|
|
53
74
|
setConnected(true);
|
|
54
75
|
setConnecting(false);
|
|
55
76
|
|
|
56
|
-
// Check auth user from the ACCEPT payload
|
|
57
77
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
78
|
const user = (connection as any).user;
|
|
59
79
|
if (user) setAuthUser(user);
|
|
60
80
|
|
|
61
|
-
// Open
|
|
81
|
+
// Open the chat stream ONCE and store in ref
|
|
62
82
|
const stream = connection.stream("chat-room");
|
|
83
|
+
streamRef.current = stream;
|
|
63
84
|
|
|
64
85
|
// Announce join
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
const sess = sessionRef.current;
|
|
87
|
+
if (sess) {
|
|
88
|
+
stream.send(
|
|
89
|
+
JSON.stringify({
|
|
90
|
+
type: "join",
|
|
91
|
+
user: sess.user.name,
|
|
92
|
+
userId: sess.user.id,
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Listen for messages — uses sessionRef to avoid stale closures
|
|
74
98
|
stream.on("data", (data: Uint8Array) => {
|
|
75
99
|
try {
|
|
76
100
|
const msg = JSON.parse(new TextDecoder().decode(data));
|
|
77
|
-
|
|
101
|
+
const currentSess = sessionRef.current;
|
|
102
|
+
if (!currentSess) return;
|
|
103
|
+
const selfId = currentSess.user.id;
|
|
104
|
+
|
|
105
|
+
// Deduplication via message ID
|
|
106
|
+
if (msg.msgId && seenIdsRef.current.has(msg.msgId)) return;
|
|
107
|
+
if (msg.msgId) seenIdsRef.current.add(msg.msgId);
|
|
108
|
+
|
|
109
|
+
if (msg.type === "chat" && msg.userId !== selfId) {
|
|
78
110
|
setMessages((prev) => [
|
|
79
111
|
...prev,
|
|
80
112
|
{
|
|
81
|
-
id: crypto.randomUUID(),
|
|
113
|
+
id: msg.msgId || crypto.randomUUID(),
|
|
82
114
|
user: msg.user,
|
|
83
115
|
userId: msg.userId,
|
|
84
116
|
text: msg.text,
|
|
85
117
|
timestamp: Date.now(),
|
|
86
118
|
},
|
|
87
119
|
]);
|
|
88
|
-
} else if (msg.type === "join") {
|
|
120
|
+
} else if (msg.type === "join" && msg.userId !== selfId) {
|
|
89
121
|
setOnlineUsers((prev) =>
|
|
90
122
|
prev.includes(msg.user) ? prev : [...prev, msg.user]
|
|
91
123
|
);
|
|
@@ -99,6 +131,32 @@ export default function ChatDemoPage() {
|
|
|
99
131
|
timestamp: Date.now(),
|
|
100
132
|
},
|
|
101
133
|
]);
|
|
134
|
+
|
|
135
|
+
// Announce our presence back
|
|
136
|
+
const s = sessionRef.current;
|
|
137
|
+
if (s) {
|
|
138
|
+
stream.send(JSON.stringify({
|
|
139
|
+
type: "presence",
|
|
140
|
+
user: s.user.name,
|
|
141
|
+
userId: s.user.id,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
} else if (msg.type === "presence" && msg.userId !== selfId) {
|
|
145
|
+
setOnlineUsers((prev) =>
|
|
146
|
+
prev.includes(msg.user) ? prev : [...prev, msg.user]
|
|
147
|
+
);
|
|
148
|
+
} else if (msg.type === "leave" && msg.userId !== selfId) {
|
|
149
|
+
setOnlineUsers((prev) => prev.filter(u => u !== msg.user));
|
|
150
|
+
setMessages((prev) => [
|
|
151
|
+
...prev,
|
|
152
|
+
{
|
|
153
|
+
id: crypto.randomUUID(),
|
|
154
|
+
user: "System",
|
|
155
|
+
userId: "system",
|
|
156
|
+
text: `${msg.user} left the chat`,
|
|
157
|
+
timestamp: Date.now(),
|
|
158
|
+
},
|
|
159
|
+
]);
|
|
102
160
|
}
|
|
103
161
|
} catch {
|
|
104
162
|
// ignore parse errors
|
|
@@ -106,7 +164,18 @@ export default function ChatDemoPage() {
|
|
|
106
164
|
});
|
|
107
165
|
|
|
108
166
|
connection.on("disconnect", () => setConnected(false));
|
|
109
|
-
connection.on("reconnected", () =>
|
|
167
|
+
connection.on("reconnected", () => {
|
|
168
|
+
setConnected(true);
|
|
169
|
+
// Re-announce on reconnect
|
|
170
|
+
const s = sessionRef.current;
|
|
171
|
+
if (s && streamRef.current) {
|
|
172
|
+
streamRef.current.send(JSON.stringify({
|
|
173
|
+
type: "join",
|
|
174
|
+
user: s.user.name,
|
|
175
|
+
userId: s.user.id,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
});
|
|
110
179
|
} catch (err) {
|
|
111
180
|
console.error("Failed to connect:", err);
|
|
112
181
|
setConnecting(false);
|
|
@@ -117,6 +186,17 @@ export default function ChatDemoPage() {
|
|
|
117
186
|
|
|
118
187
|
return () => {
|
|
119
188
|
cancelled = true;
|
|
189
|
+
const sess = sessionRef.current;
|
|
190
|
+
if (streamRef.current && sess) {
|
|
191
|
+
try {
|
|
192
|
+
streamRef.current.send(JSON.stringify({
|
|
193
|
+
type: "leave",
|
|
194
|
+
user: sess.user.name,
|
|
195
|
+
userId: sess.user.id
|
|
196
|
+
}));
|
|
197
|
+
} catch { /* ignore */ }
|
|
198
|
+
}
|
|
199
|
+
streamRef.current = null;
|
|
120
200
|
connRef.current?.disconnect();
|
|
121
201
|
};
|
|
122
202
|
}, [session]);
|
|
@@ -125,17 +205,38 @@ export default function ChatDemoPage() {
|
|
|
125
205
|
|
|
126
206
|
function sendMessage(e: React.FormEvent) {
|
|
127
207
|
e.preventDefault();
|
|
128
|
-
if (!input.trim() || !
|
|
208
|
+
if (!input.trim() || !streamRef.current) return;
|
|
129
209
|
|
|
130
|
-
const
|
|
131
|
-
|
|
210
|
+
const text = input.trim();
|
|
211
|
+
const sess = sessionRef.current;
|
|
212
|
+
const user = sess?.user?.name || "Anonymous";
|
|
213
|
+
const usrId = sess?.user?.id || "unknown";
|
|
214
|
+
const msgId = crypto.randomUUID();
|
|
215
|
+
|
|
216
|
+
// Mark as seen so we don't add our own echo
|
|
217
|
+
seenIdsRef.current.add(msgId);
|
|
218
|
+
|
|
219
|
+
streamRef.current.send(
|
|
132
220
|
JSON.stringify({
|
|
133
221
|
type: "chat",
|
|
134
|
-
user
|
|
135
|
-
userId:
|
|
136
|
-
text
|
|
222
|
+
user,
|
|
223
|
+
userId: usrId,
|
|
224
|
+
text,
|
|
225
|
+
msgId,
|
|
137
226
|
})
|
|
138
227
|
);
|
|
228
|
+
|
|
229
|
+
// Add to local state immediately
|
|
230
|
+
setMessages((prev) => [
|
|
231
|
+
...prev,
|
|
232
|
+
{
|
|
233
|
+
id: msgId,
|
|
234
|
+
user,
|
|
235
|
+
userId: usrId,
|
|
236
|
+
text,
|
|
237
|
+
timestamp: Date.now(),
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
139
240
|
setInput("");
|
|
140
241
|
}
|
|
141
242
|
|
|
@@ -179,7 +280,7 @@ export default function ChatDemoPage() {
|
|
|
179
280
|
<div className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-slate-800">
|
|
180
281
|
<Users className="w-3.5 h-3.5 text-slate-400" />
|
|
181
282
|
<span className="text-xs text-slate-400">
|
|
182
|
-
{onlineUsers.length
|
|
283
|
+
{onlineUsers.length + 1} online
|
|
183
284
|
</span>
|
|
184
285
|
</div>
|
|
185
286
|
</div>
|