create-rudder-app 0.3.1 → 0.5.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/index.js +98 -20
- package/dist/index.js.map +1 -1
- package/dist/templates/app/auth-controller.d.ts +2 -0
- package/dist/templates/app/auth-controller.d.ts.map +1 -0
- package/dist/templates/app/auth-controller.js +51 -0
- package/dist/templates/app/auth-controller.js.map +1 -0
- package/dist/templates/app/mcp-echo-server.d.ts +2 -0
- package/dist/templates/app/mcp-echo-server.d.ts.map +1 -0
- package/dist/templates/app/mcp-echo-server.js +13 -0
- package/dist/templates/app/mcp-echo-server.js.map +1 -0
- package/dist/templates/app/mcp-echo-tool.d.ts +2 -0
- package/dist/templates/app/mcp-echo-tool.d.ts.map +1 -0
- package/dist/templates/app/mcp-echo-tool.js +20 -0
- package/dist/templates/app/mcp-echo-tool.js.map +1 -0
- package/dist/templates/app/service-provider.d.ts +3 -0
- package/dist/templates/app/service-provider.d.ts.map +1 -0
- package/dist/templates/app/service-provider.js +48 -0
- package/dist/templates/app/service-provider.js.map +1 -0
- package/dist/templates/app/user-model.d.ts +2 -0
- package/dist/templates/app/user-model.d.ts.map +1 -0
- package/dist/templates/app/user-model.js +19 -0
- package/dist/templates/app/user-model.js.map +1 -0
- package/dist/templates/bootstrap/app.d.ts +3 -0
- package/dist/templates/bootstrap/app.d.ts.map +1 -0
- package/dist/templates/bootstrap/app.js +41 -0
- package/dist/templates/bootstrap/app.js.map +1 -0
- package/dist/templates/bootstrap/providers.d.ts +3 -0
- package/dist/templates/bootstrap/providers.d.ts.map +1 -0
- package/dist/templates/bootstrap/providers.js +27 -0
- package/dist/templates/bootstrap/providers.js.map +1 -0
- package/dist/templates/configs/ai.d.ts +2 -0
- package/dist/templates/configs/ai.d.ts.map +1 -0
- package/dist/templates/configs/ai.js +32 -0
- package/dist/templates/configs/ai.js.map +1 -0
- package/dist/templates/configs/app.d.ts +2 -0
- package/dist/templates/configs/app.d.ts.map +1 -0
- package/dist/templates/configs/app.js +12 -0
- package/dist/templates/configs/app.js.map +1 -0
- package/dist/templates/configs/auth.d.ts +3 -0
- package/dist/templates/configs/auth.d.ts.map +1 -0
- package/dist/templates/configs/auth.js +16 -0
- package/dist/templates/configs/auth.js.map +1 -0
- package/dist/templates/configs/cache.d.ts +2 -0
- package/dist/templates/configs/cache.d.ts.map +1 -0
- package/dist/templates/configs/cache.js +28 -0
- package/dist/templates/configs/cache.js.map +1 -0
- package/dist/templates/configs/crypt.d.ts +2 -0
- package/dist/templates/configs/crypt.d.ts.map +1 -0
- package/dist/templates/configs/crypt.js +16 -0
- package/dist/templates/configs/crypt.js.map +1 -0
- package/dist/templates/configs/database.d.ts +3 -0
- package/dist/templates/configs/database.d.ts.map +1 -0
- package/dist/templates/configs/database.js +28 -0
- package/dist/templates/configs/database.js.map +1 -0
- package/dist/templates/configs/hash.d.ts +2 -0
- package/dist/templates/configs/hash.d.ts.map +1 -0
- package/dist/templates/configs/hash.js +12 -0
- package/dist/templates/configs/hash.js.map +1 -0
- package/dist/templates/configs/horizon.d.ts +2 -0
- package/dist/templates/configs/horizon.d.ts.map +1 -0
- package/dist/templates/configs/horizon.js +30 -0
- package/dist/templates/configs/horizon.js.map +1 -0
- package/dist/templates/configs/index.d.ts +3 -0
- package/dist/templates/configs/index.d.ts.map +1 -0
- package/dist/templates/configs/index.js +92 -0
- package/dist/templates/configs/index.js.map +1 -0
- package/dist/templates/configs/localization.d.ts +2 -0
- package/dist/templates/configs/localization.d.ts.map +1 -0
- package/dist/templates/configs/localization.js +13 -0
- package/dist/templates/configs/localization.js.map +1 -0
- package/dist/templates/configs/log.d.ts +2 -0
- package/dist/templates/configs/log.d.ts.map +1 -0
- package/dist/templates/configs/log.js +40 -0
- package/dist/templates/configs/log.js.map +1 -0
- package/dist/templates/configs/mail.d.ts +2 -0
- package/dist/templates/configs/mail.d.ts.map +1 -0
- package/dist/templates/configs/mail.js +33 -0
- package/dist/templates/configs/mail.js.map +1 -0
- package/dist/templates/configs/passport.d.ts +2 -0
- package/dist/templates/configs/passport.d.ts.map +1 -0
- package/dist/templates/configs/passport.js +22 -0
- package/dist/templates/configs/passport.js.map +1 -0
- package/dist/templates/configs/pennant.d.ts +2 -0
- package/dist/templates/configs/pennant.d.ts.map +1 -0
- package/dist/templates/configs/pennant.js +16 -0
- package/dist/templates/configs/pennant.js.map +1 -0
- package/dist/templates/configs/pulse.d.ts +2 -0
- package/dist/templates/configs/pulse.d.ts.map +1 -0
- package/dist/templates/configs/pulse.js +21 -0
- package/dist/templates/configs/pulse.js.map +1 -0
- package/dist/templates/configs/queue.d.ts +2 -0
- package/dist/templates/configs/queue.d.ts.map +1 -0
- package/dist/templates/configs/queue.js +28 -0
- package/dist/templates/configs/queue.js.map +1 -0
- package/dist/templates/configs/sanctum.d.ts +2 -0
- package/dist/templates/configs/sanctum.d.ts.map +1 -0
- package/dist/templates/configs/sanctum.js +19 -0
- package/dist/templates/configs/sanctum.js.map +1 -0
- package/dist/templates/configs/server.d.ts +2 -0
- package/dist/templates/configs/server.d.ts.map +1 -0
- package/dist/templates/configs/server.js +15 -0
- package/dist/templates/configs/server.js.map +1 -0
- package/dist/templates/configs/session.d.ts +2 -0
- package/dist/templates/configs/session.d.ts.map +1 -0
- package/dist/templates/configs/session.js +26 -0
- package/dist/templates/configs/session.js.map +1 -0
- package/dist/templates/configs/socialite.d.ts +2 -0
- package/dist/templates/configs/socialite.d.ts.map +1 -0
- package/dist/templates/configs/socialite.js +27 -0
- package/dist/templates/configs/socialite.js.map +1 -0
- package/dist/templates/configs/storage.d.ts +2 -0
- package/dist/templates/configs/storage.d.ts.map +1 -0
- package/dist/templates/configs/storage.js +35 -0
- package/dist/templates/configs/storage.js.map +1 -0
- package/dist/templates/configs/sync.d.ts +3 -0
- package/dist/templates/configs/sync.d.ts.map +1 -0
- package/dist/templates/configs/sync.js +17 -0
- package/dist/templates/configs/sync.js.map +1 -0
- package/dist/templates/configs/telescope.d.ts +2 -0
- package/dist/templates/configs/telescope.d.ts.map +1 -0
- package/dist/templates/configs/telescope.js +25 -0
- package/dist/templates/configs/telescope.js.map +1 -0
- package/dist/templates/css/index.d.ts +3 -0
- package/dist/templates/css/index.d.ts.map +1 -0
- package/dist/templates/css/index.js +140 -0
- package/dist/templates/css/index.js.map +1 -0
- package/dist/templates/css/plain.d.ts +2 -0
- package/dist/templates/css/plain.d.ts.map +1 -0
- package/dist/templates/css/plain.js +373 -0
- package/dist/templates/css/plain.js.map +1 -0
- package/dist/templates/css/tailwind.d.ts +2 -0
- package/dist/templates/css/tailwind.d.ts.map +1 -0
- package/dist/templates/css/tailwind.js +176 -0
- package/dist/templates/css/tailwind.js.map +1 -0
- package/dist/templates/demos/avatar.d.ts +3 -0
- package/dist/templates/demos/avatar.d.ts.map +1 -0
- package/dist/templates/demos/avatar.js +182 -0
- package/dist/templates/demos/avatar.js.map +1 -0
- package/dist/templates/demos/cache.d.ts +3 -0
- package/dist/templates/demos/cache.d.ts.map +1 -0
- package/dist/templates/demos/cache.js +99 -0
- package/dist/templates/demos/cache.js.map +1 -0
- package/dist/templates/demos/contact.d.ts +3 -0
- package/dist/templates/demos/contact.d.ts.map +1 -0
- package/dist/templates/demos/contact.js +106 -0
- package/dist/templates/demos/contact.js.map +1 -0
- package/dist/templates/demos/fibonacci.d.ts +7 -0
- package/dist/templates/demos/fibonacci.d.ts.map +1 -0
- package/dist/templates/demos/fibonacci.js +172 -0
- package/dist/templates/demos/fibonacci.js.map +1 -0
- package/dist/templates/demos/http.d.ts +3 -0
- package/dist/templates/demos/http.d.ts.map +1 -0
- package/dist/templates/demos/http.js +117 -0
- package/dist/templates/demos/http.js.map +1 -0
- package/dist/templates/demos/index-view.d.ts +3 -0
- package/dist/templates/demos/index-view.d.ts.map +1 -0
- package/dist/templates/demos/index-view.js +144 -0
- package/dist/templates/demos/index-view.js.map +1 -0
- package/dist/templates/demos/localization.d.ts +4 -0
- package/dist/templates/demos/localization.d.ts.map +1 -0
- package/dist/templates/demos/localization.js +130 -0
- package/dist/templates/demos/localization.js.map +1 -0
- package/dist/templates/demos/mail.d.ts +4 -0
- package/dist/templates/demos/mail.d.ts.map +1 -0
- package/dist/templates/demos/mail.js +127 -0
- package/dist/templates/demos/mail.js.map +1 -0
- package/dist/templates/demos/notifications.d.ts +4 -0
- package/dist/templates/demos/notifications.d.ts.map +1 -0
- package/dist/templates/demos/notifications.js +133 -0
- package/dist/templates/demos/notifications.js.map +1 -0
- package/dist/templates/demos/pennant.d.ts +8 -0
- package/dist/templates/demos/pennant.d.ts.map +1 -0
- package/dist/templates/demos/pennant.js +138 -0
- package/dist/templates/demos/pennant.js.map +1 -0
- package/dist/templates/demos/queue.d.ts +4 -0
- package/dist/templates/demos/queue.d.ts.map +1 -0
- package/dist/templates/demos/queue.js +107 -0
- package/dist/templates/demos/queue.js.map +1 -0
- package/dist/templates/demos/registry.d.ts +13 -0
- package/dist/templates/demos/registry.d.ts.map +1 -0
- package/dist/templates/demos/registry.js +26 -0
- package/dist/templates/demos/registry.js.map +1 -0
- package/dist/templates/demos/rudder-socket.d.ts +2 -0
- package/dist/templates/demos/rudder-socket.d.ts.map +1 -0
- package/dist/templates/demos/rudder-socket.js +95 -0
- package/dist/templates/demos/rudder-socket.js.map +1 -0
- package/dist/templates/demos/sync.d.ts +2 -0
- package/dist/templates/demos/sync.d.ts.map +1 -0
- package/dist/templates/demos/sync.js +97 -0
- package/dist/templates/demos/sync.js.map +1 -0
- package/dist/templates/demos/system-info.d.ts +3 -0
- package/dist/templates/demos/system-info.d.ts.map +1 -0
- package/dist/templates/demos/system-info.js +142 -0
- package/dist/templates/demos/system-info.js.map +1 -0
- package/dist/templates/demos/todos.d.ts +6 -0
- package/dist/templates/demos/todos.d.ts.map +1 -0
- package/dist/templates/demos/todos.js +246 -0
- package/dist/templates/demos/todos.js.map +1 -0
- package/dist/templates/demos/ws.d.ts +2 -0
- package/dist/templates/demos/ws.d.ts.map +1 -0
- package/dist/templates/demos/ws.js +106 -0
- package/dist/templates/demos/ws.js.map +1 -0
- package/dist/templates/env.d.ts +7 -0
- package/dist/templates/env.d.ts.map +1 -0
- package/dist/templates/env.js +113 -0
- package/dist/templates/env.js.map +1 -0
- package/dist/templates/package-json.d.ts +3 -0
- package/dist/templates/package-json.d.ts.map +1 -0
- package/dist/templates/package-json.js +193 -0
- package/dist/templates/package-json.js.map +1 -0
- package/dist/templates/package-managers.d.ts +14 -0
- package/dist/templates/package-managers.d.ts.map +1 -0
- package/dist/templates/package-managers.js +49 -0
- package/dist/templates/package-managers.js.map +1 -0
- package/dist/templates/pages/ai-chat.d.ts +7 -0
- package/dist/templates/pages/ai-chat.d.ts.map +1 -0
- package/dist/templates/pages/ai-chat.js +285 -0
- package/dist/templates/pages/ai-chat.js.map +1 -0
- package/dist/templates/pages/demo.d.ts +4 -0
- package/dist/templates/pages/demo.d.ts.map +1 -0
- package/dist/templates/pages/demo.js +71 -0
- package/dist/templates/pages/demo.js.map +1 -0
- package/dist/templates/pages/error.d.ts +7 -0
- package/dist/templates/pages/error.d.ts.map +1 -0
- package/dist/templates/pages/error.js +148 -0
- package/dist/templates/pages/error.js.map +1 -0
- package/dist/templates/pages/index.d.ts +9 -0
- package/dist/templates/pages/index.d.ts.map +1 -0
- package/dist/templates/pages/index.js +311 -0
- package/dist/templates/pages/index.js.map +1 -0
- package/dist/templates/prisma/auth.d.ts +2 -0
- package/dist/templates/prisma/auth.d.ts.map +1 -0
- package/dist/templates/prisma/auth.js +22 -0
- package/dist/templates/prisma/auth.js.map +1 -0
- package/dist/templates/prisma/base.d.ts +3 -0
- package/dist/templates/prisma/base.d.ts.map +1 -0
- package/dist/templates/prisma/base.js +14 -0
- package/dist/templates/prisma/base.js.map +1 -0
- package/dist/templates/prisma/config.d.ts +3 -0
- package/dist/templates/prisma/config.d.ts.map +1 -0
- package/dist/templates/prisma/config.js +15 -0
- package/dist/templates/prisma/config.js.map +1 -0
- package/dist/templates/prisma/notification.d.ts +2 -0
- package/dist/templates/prisma/notification.d.ts.map +1 -0
- package/dist/templates/prisma/notification.js +16 -0
- package/dist/templates/prisma/notification.js.map +1 -0
- package/dist/templates/prisma/passport.d.ts +2 -0
- package/dist/templates/prisma/passport.d.ts.map +1 -0
- package/dist/templates/prisma/passport.js +69 -0
- package/dist/templates/prisma/passport.js.map +1 -0
- package/dist/templates/routes/api.d.ts +3 -0
- package/dist/templates/routes/api.d.ts.map +1 -0
- package/dist/templates/routes/api.js +166 -0
- package/dist/templates/routes/api.js.map +1 -0
- package/dist/templates/routes/console.d.ts +2 -0
- package/dist/templates/routes/console.d.ts.map +1 -0
- package/dist/templates/routes/console.js +22 -0
- package/dist/templates/routes/console.js.map +1 -0
- package/dist/templates/routes/web.d.ts +4 -0
- package/dist/templates/routes/web.d.ts.map +1 -0
- package/dist/templates/routes/web.js +155 -0
- package/dist/templates/routes/web.js.map +1 -0
- package/dist/templates/server.d.ts +2 -0
- package/dist/templates/server.d.ts.map +1 -0
- package/dist/templates/server.js +10 -0
- package/dist/templates/server.js.map +1 -0
- package/dist/templates/tsconfig.d.ts +3 -0
- package/dist/templates/tsconfig.d.ts.map +1 -0
- package/dist/templates/tsconfig.js +33 -0
- package/dist/templates/tsconfig.js.map +1 -0
- package/dist/templates/views/welcome.d.ts +6 -0
- package/dist/templates/views/welcome.d.ts.map +1 -0
- package/dist/templates/views/welcome.js +396 -0
- package/dist/templates/views/welcome.js.map +1 -0
- package/dist/templates/vite.d.ts +3 -0
- package/dist/templates/vite.d.ts.map +1 -0
- package/dist/templates/vite.js +61 -0
- package/dist/templates/vite.js.map +1 -0
- package/dist/templates.d.ts +27 -17
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +158 -3778
- package/dist/templates.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export function demosSyncView() {
|
|
2
|
+
return `import '@/index.css'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import * as Y from 'yjs'
|
|
5
|
+
import { WebsocketProvider } from 'y-websocket'
|
|
6
|
+
|
|
7
|
+
function getWsUrl() {
|
|
8
|
+
if (typeof window === 'undefined') return ''
|
|
9
|
+
return \`ws://\${window.location.host}/ws-sync\`
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function SyncDemo() {
|
|
13
|
+
const [connected, setConnected] = useState(false)
|
|
14
|
+
const [text, setText] = useState('')
|
|
15
|
+
const [users, setUsers] = useState<{ name: string; color: string }[]>([])
|
|
16
|
+
const [myName] = useState(() => \`User-\${Math.floor(Math.random() * 1000)}\`)
|
|
17
|
+
const [myColor] = useState(() => \`hsl(\${Math.floor(Math.random() * 360)}, 70%, 50%)\`)
|
|
18
|
+
|
|
19
|
+
const docRef = useRef<Y.Doc | null>(null)
|
|
20
|
+
const provRef = useRef<WebsocketProvider | null>(null)
|
|
21
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const doc = new Y.Doc()
|
|
25
|
+
const ytext = doc.getText('content')
|
|
26
|
+
const provider = new WebsocketProvider(getWsUrl(), 'sync-demo', doc)
|
|
27
|
+
|
|
28
|
+
docRef.current = doc
|
|
29
|
+
provRef.current = provider
|
|
30
|
+
|
|
31
|
+
ytext.observe(() => setText(ytext.toString()))
|
|
32
|
+
provider.on('status', ({ status }: { status: string }) => setConnected(status === 'connected'))
|
|
33
|
+
provider.awareness.setLocalStateField('user', { name: myName, color: myColor })
|
|
34
|
+
|
|
35
|
+
const syncUsers = () => {
|
|
36
|
+
const states = [...provider.awareness.getStates().values()] as { user?: { name: string; color: string } }[]
|
|
37
|
+
setUsers(states.map(s => s.user).filter((u): u is { name: string; color: string } => Boolean(u)))
|
|
38
|
+
}
|
|
39
|
+
provider.awareness.on('change', syncUsers)
|
|
40
|
+
syncUsers()
|
|
41
|
+
|
|
42
|
+
return () => { provider.destroy(); doc.destroy() }
|
|
43
|
+
}, [myName, myColor])
|
|
44
|
+
|
|
45
|
+
function onChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
|
|
46
|
+
const ytext = docRef.current?.getText('content')
|
|
47
|
+
if (!ytext) return
|
|
48
|
+
docRef.current?.transact(() => {
|
|
49
|
+
ytext.delete(0, ytext.length)
|
|
50
|
+
ytext.insert(0, e.target.value)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="page">
|
|
56
|
+
<nav className="page-nav">
|
|
57
|
+
<div className="brand">
|
|
58
|
+
<span className="brand-dot" />
|
|
59
|
+
RudderJS
|
|
60
|
+
</div>
|
|
61
|
+
<div className="nav-right">
|
|
62
|
+
<a href="/demos" className="nav-link">← Demos</a>
|
|
63
|
+
</div>
|
|
64
|
+
</nav>
|
|
65
|
+
|
|
66
|
+
<section className="hero">
|
|
67
|
+
<h1 className="hero-title">Collaborative editor</h1>
|
|
68
|
+
<p className="hero-lead">
|
|
69
|
+
Yjs CRDT over @rudderjs/sync. Open this page in two tabs to see real-time updates.{' '}
|
|
70
|
+
{connected ? '🟢 connected' : '⚪ connecting…'}
|
|
71
|
+
</p>
|
|
72
|
+
</section>
|
|
73
|
+
|
|
74
|
+
<section className="feature-section" style={{ maxWidth: '40rem', margin: '0 auto' }}>
|
|
75
|
+
<p className="form-label">Active users:</p>
|
|
76
|
+
<ul style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '1rem' }}>
|
|
77
|
+
{users.map((u, i) => (
|
|
78
|
+
<li key={i} className="inline-code" style={{ borderLeft: \`3px solid \${u.color}\`, paddingLeft: '0.5rem' }}>
|
|
79
|
+
{u.name}
|
|
80
|
+
</li>
|
|
81
|
+
))}
|
|
82
|
+
</ul>
|
|
83
|
+
<textarea
|
|
84
|
+
ref={textareaRef}
|
|
85
|
+
className="form-input"
|
|
86
|
+
rows={10}
|
|
87
|
+
value={text}
|
|
88
|
+
onChange={onChange}
|
|
89
|
+
placeholder="Start typing…"
|
|
90
|
+
/>
|
|
91
|
+
</section>
|
|
92
|
+
</div>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/templates/demos/sync.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6FR,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-info.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/system-info.ts"],"names":[],"mappings":"AAGA,wBAAgB,mBAAmB,IAAI,MAAM,CA4G5C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CA6BhD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// SystemInfo demo — three shell commands run via @rudderjs/process,
|
|
2
|
+
// comparing sequential vs parallel cost via Process.pool().
|
|
3
|
+
export function demosSystemInfoView() {
|
|
4
|
+
return `import { useState } from 'react'
|
|
5
|
+
import '@/index.css'
|
|
6
|
+
|
|
7
|
+
interface CommandResult {
|
|
8
|
+
command: string
|
|
9
|
+
ok: boolean
|
|
10
|
+
exitCode: number
|
|
11
|
+
duration: number
|
|
12
|
+
stdout: string
|
|
13
|
+
stderr: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SystemInfoResponse {
|
|
17
|
+
results: CommandResult[]
|
|
18
|
+
totalMs: number
|
|
19
|
+
parallelMs: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function SystemInfo() {
|
|
23
|
+
const [data, setData ] = useState<SystemInfoResponse | null>(null)
|
|
24
|
+
const [loading, setLoading] = useState(false)
|
|
25
|
+
const [error, setError ] = useState<string | null>(null)
|
|
26
|
+
|
|
27
|
+
async function run() {
|
|
28
|
+
setLoading(true); setError(null)
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch('/api/system-info')
|
|
31
|
+
const body = await res.json() as SystemInfoResponse | { message: string }
|
|
32
|
+
if (!res.ok) throw new Error((body as { message: string }).message ?? 'Failed')
|
|
33
|
+
setData(body as SystemInfoResponse)
|
|
34
|
+
} catch (e) {
|
|
35
|
+
setError((e as Error).message)
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="page">
|
|
43
|
+
<nav className="page-nav">
|
|
44
|
+
<div className="brand">
|
|
45
|
+
<span className="brand-dot" />
|
|
46
|
+
RudderJS
|
|
47
|
+
</div>
|
|
48
|
+
<div className="nav-right">
|
|
49
|
+
<a href="/demos" className="nav-link">Demos</a>
|
|
50
|
+
<a href="/" className="nav-link">Home</a>
|
|
51
|
+
</div>
|
|
52
|
+
</nav>
|
|
53
|
+
|
|
54
|
+
<section className="hero">
|
|
55
|
+
<h1 className="hero-title">System Info</h1>
|
|
56
|
+
<p className="hero-lead">
|
|
57
|
+
Three shell commands run in parallel via{' '}
|
|
58
|
+
<code className="inline-code">@rudderjs/process</code> —{' '}
|
|
59
|
+
<code className="inline-code">git rev-parse HEAD</code>,{' '}
|
|
60
|
+
<code className="inline-code">node --version</code>,{' '}
|
|
61
|
+
<code className="inline-code">uptime</code>. Click run to dispatch.
|
|
62
|
+
</p>
|
|
63
|
+
</section>
|
|
64
|
+
|
|
65
|
+
<section className="feature-section">
|
|
66
|
+
<div className="form-card">
|
|
67
|
+
<button
|
|
68
|
+
className="form-submit"
|
|
69
|
+
onClick={run}
|
|
70
|
+
disabled={loading}
|
|
71
|
+
style={{ marginBottom: '1rem' }}
|
|
72
|
+
>
|
|
73
|
+
{loading ? 'Running…' : 'Run commands'}
|
|
74
|
+
</button>
|
|
75
|
+
|
|
76
|
+
{error && <p className="form-error">{error}</p>}
|
|
77
|
+
|
|
78
|
+
{data && (
|
|
79
|
+
<>
|
|
80
|
+
<p className="feature-desc" style={{ fontSize: '0.75rem', marginBottom: '0.75rem' }}>
|
|
81
|
+
3 commands · sequential cost {data.totalMs}ms · parallel cost {data.parallelMs}ms
|
|
82
|
+
{' · '}
|
|
83
|
+
<strong>{Math.round((1 - data.parallelMs / data.totalMs) * 100)}% faster</strong> via{' '}
|
|
84
|
+
<code className="inline-code">Process.pool()</code>
|
|
85
|
+
</p>
|
|
86
|
+
|
|
87
|
+
{data.results.map((r, i) => (
|
|
88
|
+
<div key={i} style={{ marginBottom: '0.75rem', padding: '0.75rem', borderRadius: '0.375rem', border: '1px solid var(--border, #e5e7eb)' }}>
|
|
89
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.25rem' }}>
|
|
90
|
+
<code style={{ fontSize: '0.85rem', fontWeight: 600 }}>{r.command}</code>
|
|
91
|
+
<span style={{ fontSize: '0.7rem', opacity: 0.7 }}>
|
|
92
|
+
exit {r.exitCode} · {r.duration}ms
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
{r.stdout && (
|
|
96
|
+
<pre style={{ margin: 0, padding: '0.5rem', borderRadius: '0.25rem', background: 'var(--muted, #f4f4f5)', fontSize: '0.75rem', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>{r.stdout}</pre>
|
|
97
|
+
)}
|
|
98
|
+
{r.stderr && (
|
|
99
|
+
<pre style={{ margin: '0.25rem 0 0', padding: '0.5rem', borderRadius: '0.25rem', background: 'var(--destructive-bg, #fef2f2)', color: 'var(--destructive, #b91c1c)', fontSize: '0.75rem', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>{r.stderr}</pre>
|
|
100
|
+
)}
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
</>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
</section>
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
export function demosSystemInfoApiBlock() {
|
|
113
|
+
return `// GET /api/system-info — three shell commands, sequential vs Process.pool() parallel.
|
|
114
|
+
router.get('/api/system-info', async (_req, res) => {
|
|
115
|
+
const { Process } = await import('@rudderjs/process')
|
|
116
|
+
const commands = ['git rev-parse HEAD', 'node --version', 'uptime']
|
|
117
|
+
|
|
118
|
+
const sequential: { command: string; duration: number }[] = []
|
|
119
|
+
for (const cmd of commands) {
|
|
120
|
+
const t0 = Date.now()
|
|
121
|
+
await Process.run(cmd)
|
|
122
|
+
sequential.push({ command: cmd, duration: Date.now() - t0 })
|
|
123
|
+
}
|
|
124
|
+
const totalMs = sequential.reduce((sum, r) => sum + r.duration, 0)
|
|
125
|
+
|
|
126
|
+
const t0 = Date.now()
|
|
127
|
+
const pool = await Process.pool(commands)
|
|
128
|
+
const parallelMs = Date.now() - t0
|
|
129
|
+
|
|
130
|
+
const results = pool.results.map((r, i) => ({
|
|
131
|
+
command: commands[i],
|
|
132
|
+
ok: r.successful(),
|
|
133
|
+
exitCode: r.exitCode,
|
|
134
|
+
duration: sequential[i]!.duration,
|
|
135
|
+
stdout: r.stdout.trim(),
|
|
136
|
+
stderr: r.stderr.trim(),
|
|
137
|
+
}))
|
|
138
|
+
|
|
139
|
+
res.json({ results, totalMs, parallelMs })
|
|
140
|
+
})`;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=system-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-info.js","sourceRoot":"","sources":["../../../src/templates/demos/system-info.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,4DAA4D;AAE5D,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GR,CAAA;AACD,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BN,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function demosTodosView(): string;
|
|
2
|
+
export declare function todoModelPrisma(): string;
|
|
3
|
+
export declare function todoSchema(): string;
|
|
4
|
+
export declare function todoService(): string;
|
|
5
|
+
export declare function todoServiceProvider(): string;
|
|
6
|
+
//# sourceMappingURL=todos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todos.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/todos.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,IAAI,MAAM,CA4HvC;AAED,wBAAgB,eAAe,IAAI,MAAM,CASxC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAwBnC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAgCpC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA8C5C"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Todos demo — ORM + interactive CRUD via @rudderjs/router.
|
|
2
|
+
//
|
|
3
|
+
// Scaffolds five files under `app/Modules/Todo/` (Laravel-style self-contained
|
|
4
|
+
// module) plus the React view, and wires them into AppServiceProvider's boot()
|
|
5
|
+
// so the API routes register at app startup.
|
|
6
|
+
export function demosTodosView() {
|
|
7
|
+
return `import '@/index.css'
|
|
8
|
+
import { useState, useRef } from 'react'
|
|
9
|
+
import type { Todo } from '../../Modules/Todo/TodoSchema.js'
|
|
10
|
+
|
|
11
|
+
interface TodosDemoProps {
|
|
12
|
+
todos: Todo[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function TodosDemo({ todos: initial }: TodosDemoProps) {
|
|
16
|
+
const [todos, setTodos] = useState<Todo[]>(initial)
|
|
17
|
+
const [loading, setLoading] = useState(false)
|
|
18
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
19
|
+
|
|
20
|
+
async function addTodo() {
|
|
21
|
+
const title = inputRef.current?.value.trim()
|
|
22
|
+
if (!title) return
|
|
23
|
+
setLoading(true)
|
|
24
|
+
const res = await fetch('/api/todos', {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({ title }),
|
|
28
|
+
})
|
|
29
|
+
const { data } = await res.json() as { data: Todo }
|
|
30
|
+
setTodos(prev => [data, ...prev])
|
|
31
|
+
if (inputRef.current) inputRef.current.value = ''
|
|
32
|
+
setLoading(false)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function toggleTodo(todo: Todo) {
|
|
36
|
+
const res = await fetch(\`/api/todos/\${todo.id}\`, {
|
|
37
|
+
method: 'PATCH',
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({ completed: !todo.completed }),
|
|
40
|
+
})
|
|
41
|
+
const { data } = await res.json() as { data: Todo }
|
|
42
|
+
setTodos(prev => prev.map(t => t.id === data.id ? data : t))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function deleteTodo(id: string) {
|
|
46
|
+
await fetch(\`/api/todos/\${id}\`, { method: 'DELETE' })
|
|
47
|
+
setTodos(prev => prev.filter(t => t.id !== id))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const done = todos.filter(t => t.completed).length
|
|
51
|
+
const pending = todos.length - done
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="page">
|
|
55
|
+
<nav className="page-nav">
|
|
56
|
+
<div className="brand">
|
|
57
|
+
<span className="brand-dot" />
|
|
58
|
+
RudderJS
|
|
59
|
+
</div>
|
|
60
|
+
<div className="nav-right">
|
|
61
|
+
<a href="/demos" className="nav-link">Demos</a>
|
|
62
|
+
<a href="/" className="nav-link">Home</a>
|
|
63
|
+
</div>
|
|
64
|
+
</nav>
|
|
65
|
+
|
|
66
|
+
<section className="hero">
|
|
67
|
+
<h1 className="hero-title">Todo List</h1>
|
|
68
|
+
<p className="hero-lead">
|
|
69
|
+
{pending} remaining · {done} completed
|
|
70
|
+
</p>
|
|
71
|
+
<p className="hero-meta">
|
|
72
|
+
Rendered from <code className="inline-code">app/Views/Demos/Todos.tsx</code> via{' '}
|
|
73
|
+
<code className="inline-code">view('demos.todos', { todos })</code>.
|
|
74
|
+
Initial data fetched by the controller, not the view.
|
|
75
|
+
</p>
|
|
76
|
+
</section>
|
|
77
|
+
|
|
78
|
+
<section className="feature-section">
|
|
79
|
+
<div className="demo-narrow">
|
|
80
|
+
<div className="demo-card">
|
|
81
|
+
<div className="input-row">
|
|
82
|
+
<input
|
|
83
|
+
ref={inputRef}
|
|
84
|
+
className="form-input"
|
|
85
|
+
placeholder="What needs to be done?"
|
|
86
|
+
onKeyDown={e => e.key === 'Enter' && addTodo()}
|
|
87
|
+
/>
|
|
88
|
+
<button className="button-primary" onClick={addTodo} disabled={loading}>
|
|
89
|
+
{loading ? '...' : 'Add'}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div className="demo-card">
|
|
95
|
+
<div className="demo-card-header">
|
|
96
|
+
<h2 className="demo-card-title">Tasks</h2>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="demo-card-body">
|
|
99
|
+
{todos.length === 0 && (
|
|
100
|
+
<p className="empty-state">No todos yet. Add one above!</p>
|
|
101
|
+
)}
|
|
102
|
+
{todos.map(todo => (
|
|
103
|
+
<div key={todo.id} className="list-row group">
|
|
104
|
+
<input
|
|
105
|
+
type="checkbox"
|
|
106
|
+
className="checkbox-input"
|
|
107
|
+
checked={todo.completed}
|
|
108
|
+
onChange={() => toggleTodo(todo)}
|
|
109
|
+
/>
|
|
110
|
+
<span className={\`list-row-text\${todo.completed ? ' list-row-text-done' : ''}\`}>
|
|
111
|
+
{todo.title}
|
|
112
|
+
</span>
|
|
113
|
+
<button
|
|
114
|
+
onClick={() => deleteTodo(todo.id)}
|
|
115
|
+
className="list-row-delete"
|
|
116
|
+
aria-label="Delete"
|
|
117
|
+
>
|
|
118
|
+
✕
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</section>
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
export function todoModelPrisma() {
|
|
132
|
+
return `model Todo {
|
|
133
|
+
id String @id @default(cuid())
|
|
134
|
+
title String
|
|
135
|
+
completed Boolean @default(false)
|
|
136
|
+
createdAt DateTime @default(now())
|
|
137
|
+
updatedAt DateTime @updatedAt
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
export function todoSchema() {
|
|
142
|
+
return `import { z } from 'zod'
|
|
143
|
+
|
|
144
|
+
export const TodoInputSchema = z.object({
|
|
145
|
+
title: z.string().min(1, 'Title is required'),
|
|
146
|
+
completed: z.boolean().optional().default(false),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
export const TodoUpdateSchema = z.object({
|
|
150
|
+
title: z.string().min(1).optional(),
|
|
151
|
+
completed: z.boolean().optional(),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
export type TodoInput = z.infer<typeof TodoInputSchema>
|
|
155
|
+
export type TodoUpdate = z.infer<typeof TodoUpdateSchema>
|
|
156
|
+
|
|
157
|
+
export interface Todo {
|
|
158
|
+
id: string
|
|
159
|
+
title: string
|
|
160
|
+
completed: boolean
|
|
161
|
+
createdAt: Date
|
|
162
|
+
updatedAt: Date
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
export function todoService() {
|
|
167
|
+
return `import { Injectable, resolve } from '@rudderjs/core'
|
|
168
|
+
import type { OrmAdapter } from '@rudderjs/orm'
|
|
169
|
+
import type { Todo, TodoInput, TodoUpdate } from './TodoSchema.js'
|
|
170
|
+
|
|
171
|
+
@Injectable()
|
|
172
|
+
export class TodoService {
|
|
173
|
+
private get db(): OrmAdapter {
|
|
174
|
+
return resolve<OrmAdapter>('db')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
findAll(): Promise<Todo[]> {
|
|
178
|
+
return this.db.query<Todo>('todo').orderBy('createdAt', 'DESC').get()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
findById(id: string): Promise<Todo | null> {
|
|
182
|
+
return this.db.query<Todo>('todo').find(id)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
create(input: TodoInput): Promise<Todo> {
|
|
186
|
+
return this.db.query<Todo>('todo').create(input)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
update(id: string, input: TodoUpdate): Promise<Todo> {
|
|
190
|
+
return this.db.query<Todo>('todo').update(id, input as Partial<Todo>)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
delete(id: string): Promise<void> {
|
|
194
|
+
return this.db.query<Todo>('todo').delete(id)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
export function todoServiceProvider() {
|
|
200
|
+
return `import { ServiceProvider } from '@rudderjs/core'
|
|
201
|
+
import { router } from '@rudderjs/router'
|
|
202
|
+
import { TodoService } from './TodoService.js'
|
|
203
|
+
import { TodoInputSchema, TodoUpdateSchema } from './TodoSchema.js'
|
|
204
|
+
|
|
205
|
+
export class TodoServiceProvider extends ServiceProvider {
|
|
206
|
+
register(): void {
|
|
207
|
+
this.app.singleton(TodoService, () => new TodoService())
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
override async boot(): Promise<void> {
|
|
211
|
+
const service = this.app.make<TodoService>(TodoService)
|
|
212
|
+
|
|
213
|
+
router.get('/api/todos', async (_req, res) => {
|
|
214
|
+
const todos = await service.findAll()
|
|
215
|
+
res.json({ data: todos })
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
router.post('/api/todos', async (req, res) => {
|
|
219
|
+
const parsed = TodoInputSchema.safeParse(req.body)
|
|
220
|
+
if (!parsed.success) {
|
|
221
|
+
res.status(422).json({ errors: parsed.error.flatten().fieldErrors })
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
const todo = await service.create(parsed.data)
|
|
225
|
+
res.status(201).json({ data: todo })
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
router.patch('/api/todos/:id', async (req, res) => {
|
|
229
|
+
const parsed = TodoUpdateSchema.safeParse(req.body)
|
|
230
|
+
if (!parsed.success) {
|
|
231
|
+
res.status(422).json({ errors: parsed.error.flatten().fieldErrors })
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
const todo = await service.update(req.params['id']!, parsed.data)
|
|
235
|
+
res.json({ data: todo })
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
router.delete('/api/todos/:id', async (req, res) => {
|
|
239
|
+
await service.delete(req.params['id']!)
|
|
240
|
+
res.status(204).send('')
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=todos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todos.js","sourceRoot":"","sources":["../../../src/templates/demos/todos.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,6CAA6C;AAE7C,MAAM,UAAU,cAAc;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0HR,CAAA;AACD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO;;;;;;;CAOR,CAAA;AACD,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;CAsBR,CAAA;AACD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BR,CAAA;AACD,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CR,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/ws.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,IAAI,MAAM,CAwGpC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export function demosWsView() {
|
|
2
|
+
return `import '@/index.css'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { RudderSocket } from '@/RudderSocket'
|
|
5
|
+
|
|
6
|
+
type Message = { user: string; text: string; ts: number }
|
|
7
|
+
type Member = { id: string; name: string }
|
|
8
|
+
|
|
9
|
+
function getWsUrl() {
|
|
10
|
+
if (typeof window === 'undefined') return ''
|
|
11
|
+
return \`ws://\${window.location.host}/ws\`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function WsDemo() {
|
|
15
|
+
const [me, setMe] = useState('')
|
|
16
|
+
const socketRef = useRef<RudderSocket | null>(null)
|
|
17
|
+
const [connected, setConnected] = useState(false)
|
|
18
|
+
const [messages, setMessages] = useState<Message[]>([])
|
|
19
|
+
const [members, setMembers] = useState<Member[]>([])
|
|
20
|
+
const [input, setInput] = useState('')
|
|
21
|
+
|
|
22
|
+
useEffect(() => { setMe(\`User-\${Math.floor(Math.random() * 1000)}\`) }, [])
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!me) return
|
|
26
|
+
const socket = new RudderSocket(getWsUrl())
|
|
27
|
+
socketRef.current = socket
|
|
28
|
+
|
|
29
|
+
const chat = socket.channel('chat')
|
|
30
|
+
chat.on('message', d => setMessages(prev => [...prev, d as Message]))
|
|
31
|
+
|
|
32
|
+
const room = socket.presence('lobby', 'demo-token')
|
|
33
|
+
room.on('presence.members', d => {
|
|
34
|
+
setMembers(d as Member[])
|
|
35
|
+
setConnected(true)
|
|
36
|
+
})
|
|
37
|
+
room.on('presence.joined', d => {
|
|
38
|
+
const u = d as Member
|
|
39
|
+
setMembers(prev => [...prev.filter(m => m.id !== u.id), u])
|
|
40
|
+
})
|
|
41
|
+
room.on('presence.left', d => {
|
|
42
|
+
const id = (d as { id: string }).id
|
|
43
|
+
setMembers(prev => prev.filter(m => m.id !== id))
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return () => { socket.disconnect() }
|
|
47
|
+
}, [me])
|
|
48
|
+
|
|
49
|
+
async function send() {
|
|
50
|
+
if (!input.trim()) return
|
|
51
|
+
await fetch('/api/ws/broadcast', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify({ user: me, text: input.trim() }),
|
|
55
|
+
})
|
|
56
|
+
setInput('')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="page">
|
|
61
|
+
<nav className="page-nav">
|
|
62
|
+
<div className="brand">
|
|
63
|
+
<span className="brand-dot" />
|
|
64
|
+
RudderJS
|
|
65
|
+
</div>
|
|
66
|
+
<div className="nav-right">
|
|
67
|
+
<a href="/demos" className="nav-link">← Demos</a>
|
|
68
|
+
</div>
|
|
69
|
+
</nav>
|
|
70
|
+
|
|
71
|
+
<section className="hero">
|
|
72
|
+
<h1 className="hero-title">WebSocket chat</h1>
|
|
73
|
+
<p className="hero-lead">
|
|
74
|
+
Pub/sub + presence over a single WebSocket. Connected as <strong>{me}</strong>.{' '}
|
|
75
|
+
{connected ? '🟢 connected' : '⚪ connecting…'}
|
|
76
|
+
</p>
|
|
77
|
+
</section>
|
|
78
|
+
|
|
79
|
+
<section className="feature-section" style={{ maxWidth: '40rem', margin: '0 auto' }}>
|
|
80
|
+
<p className="form-label">Members ({members.length})</p>
|
|
81
|
+
<ul style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '1rem' }}>
|
|
82
|
+
{members.map(m => (
|
|
83
|
+
<li key={m.id} className="inline-code">{m.name}</li>
|
|
84
|
+
))}
|
|
85
|
+
</ul>
|
|
86
|
+
|
|
87
|
+
<div style={{ minHeight: '12rem', marginBottom: '1rem' }}>
|
|
88
|
+
{messages.map((m, i) => (
|
|
89
|
+
<p key={i} style={{ margin: '0.25rem 0' }}>
|
|
90
|
+
<strong>{m.user}:</strong> {m.text}
|
|
91
|
+
</p>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<form onSubmit={e => { e.preventDefault(); void send() }} style={{ display: 'flex', gap: '0.5rem' }}>
|
|
96
|
+
<input className="form-input" value={input}
|
|
97
|
+
onChange={e => setInput(e.target.value)} placeholder="Say something…" />
|
|
98
|
+
<button type="submit" className="form-submit" style={{ width: 'auto' }}>Send</button>
|
|
99
|
+
</form>
|
|
100
|
+
</section>
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../../src/templates/demos/ws.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGR,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TemplateContext } from '../templates.js';
|
|
2
|
+
export declare function dotenv(ctx: TemplateContext): string;
|
|
3
|
+
export declare function dotenvExample(ctx: TemplateContext): string;
|
|
4
|
+
export declare function envDts(): string;
|
|
5
|
+
export declare function gitignore(): string;
|
|
6
|
+
export declare function pnpmWorkspace(): string;
|
|
7
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/templates/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,wBAAgB,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA+CnD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAgD1D;AAED,wBAAgB,MAAM,IAAI,MAAM,CAO/B;AAED,wBAAgB,SAAS,IAAI,MAAM,CASlC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|