create-rudder-app 1.1.2 → 1.2.1
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-flags.d.ts +0 -9
- package/dist/cli-flags.d.ts.map +1 -1
- package/dist/cli-flags.js +0 -8
- package/dist/cli-flags.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/templates/app/service-provider.d.ts.map +1 -1
- package/dist/templates/app/service-provider.js +0 -15
- package/dist/templates/app/service-provider.js.map +1 -1
- package/dist/templates/components/site-header.d.ts.map +1 -1
- package/dist/templates/components/site-header.js +2 -8
- package/dist/templates/components/site-header.js.map +1 -1
- package/dist/templates/package-json.d.ts.map +1 -1
- package/dist/templates/package-json.js +4 -5
- package/dist/templates/package-json.js.map +1 -1
- package/dist/templates/pages/index.d.ts +13 -0
- package/dist/templates/pages/index.d.ts.map +1 -1
- package/dist/templates/pages/index.js +43 -0
- package/dist/templates/pages/index.js.map +1 -1
- package/dist/templates/routes/api.d.ts.map +1 -1
- package/dist/templates/routes/api.js +0 -99
- package/dist/templates/routes/api.js.map +1 -1
- package/dist/templates/routes/console.d.ts.map +1 -1
- package/dist/templates/routes/console.js +3 -4
- package/dist/templates/routes/console.js.map +1 -1
- package/dist/templates/routes/web.d.ts.map +1 -1
- package/dist/templates/routes/web.js +10 -83
- package/dist/templates/routes/web.js.map +1 -1
- package/dist/templates/routes-manifest.d.ts +2 -2
- package/dist/templates/routes-manifest.d.ts.map +1 -1
- package/dist/templates/routes-manifest.js +19 -22
- package/dist/templates/routes-manifest.js.map +1 -1
- package/dist/templates/views/welcome.d.ts +13 -0
- package/dist/templates/views/welcome.d.ts.map +1 -1
- package/dist/templates/views/welcome.js +53 -0
- package/dist/templates/views/welcome.js.map +1 -1
- package/dist/templates.d.ts +0 -9
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +38 -120
- package/dist/templates.js.map +1 -1
- package/package.json +1 -5
- package/dist/templates/demos/avatar.d.ts +0 -3
- package/dist/templates/demos/avatar.d.ts.map +0 -1
- package/dist/templates/demos/avatar.js +0 -174
- package/dist/templates/demos/avatar.js.map +0 -1
- package/dist/templates/demos/cache.d.ts +0 -3
- package/dist/templates/demos/cache.d.ts.map +0 -1
- package/dist/templates/demos/cache.js +0 -91
- package/dist/templates/demos/cache.js.map +0 -1
- package/dist/templates/demos/contact.d.ts +0 -3
- package/dist/templates/demos/contact.d.ts.map +0 -1
- package/dist/templates/demos/contact.js +0 -99
- package/dist/templates/demos/contact.js.map +0 -1
- package/dist/templates/demos/fibonacci.d.ts +0 -7
- package/dist/templates/demos/fibonacci.d.ts.map +0 -1
- package/dist/templates/demos/fibonacci.js +0 -164
- package/dist/templates/demos/fibonacci.js.map +0 -1
- package/dist/templates/demos/http.d.ts +0 -3
- package/dist/templates/demos/http.d.ts.map +0 -1
- package/dist/templates/demos/http.js +0 -109
- package/dist/templates/demos/http.js.map +0 -1
- package/dist/templates/demos/index-view.d.ts +0 -3
- package/dist/templates/demos/index-view.d.ts.map +0 -1
- package/dist/templates/demos/index-view.js +0 -47
- package/dist/templates/demos/index-view.js.map +0 -1
- package/dist/templates/demos/localization.d.ts +0 -4
- package/dist/templates/demos/localization.d.ts.map +0 -1
- package/dist/templates/demos/localization.js +0 -122
- package/dist/templates/demos/localization.js.map +0 -1
- package/dist/templates/demos/mail.d.ts +0 -4
- package/dist/templates/demos/mail.d.ts.map +0 -1
- package/dist/templates/demos/mail.js +0 -119
- package/dist/templates/demos/mail.js.map +0 -1
- package/dist/templates/demos/notifications.d.ts +0 -4
- package/dist/templates/demos/notifications.d.ts.map +0 -1
- package/dist/templates/demos/notifications.js +0 -125
- package/dist/templates/demos/notifications.js.map +0 -1
- package/dist/templates/demos/pennant.d.ts +0 -8
- package/dist/templates/demos/pennant.d.ts.map +0 -1
- package/dist/templates/demos/pennant.js +0 -135
- package/dist/templates/demos/pennant.js.map +0 -1
- package/dist/templates/demos/polymorphic.d.ts +0 -19
- package/dist/templates/demos/polymorphic.d.ts.map +0 -1
- package/dist/templates/demos/polymorphic.js +0 -663
- package/dist/templates/demos/polymorphic.js.map +0 -1
- package/dist/templates/demos/queue.d.ts +0 -4
- package/dist/templates/demos/queue.d.ts.map +0 -1
- package/dist/templates/demos/queue.js +0 -99
- package/dist/templates/demos/queue.js.map +0 -1
- package/dist/templates/demos/registry.d.ts +0 -26
- package/dist/templates/demos/registry.d.ts.map +0 -1
- package/dist/templates/demos/registry.js +0 -140
- package/dist/templates/demos/registry.js.map +0 -1
- package/dist/templates/demos/rudder-socket.d.ts +0 -2
- package/dist/templates/demos/rudder-socket.d.ts.map +0 -1
- package/dist/templates/demos/rudder-socket.js +0 -95
- package/dist/templates/demos/rudder-socket.js.map +0 -1
- package/dist/templates/demos/sync.d.ts +0 -2
- package/dist/templates/demos/sync.d.ts.map +0 -1
- package/dist/templates/demos/sync.js +0 -90
- package/dist/templates/demos/sync.js.map +0 -1
- package/dist/templates/demos/system-info.d.ts +0 -3
- package/dist/templates/demos/system-info.d.ts.map +0 -1
- package/dist/templates/demos/system-info.js +0 -134
- package/dist/templates/demos/system-info.js.map +0 -1
- package/dist/templates/demos/todos.d.ts +0 -6
- package/dist/templates/demos/todos.d.ts.map +0 -1
- package/dist/templates/demos/todos.js +0 -238
- package/dist/templates/demos/todos.js.map +0 -1
- package/dist/templates/demos/ws.d.ts +0 -2
- package/dist/templates/demos/ws.d.ts.map +0 -1
- package/dist/templates/demos/ws.js +0 -99
- package/dist/templates/demos/ws.js.map +0 -1
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
export function demosContactView(ctx) {
|
|
2
|
-
// CSRF is only enforced when @rudderjs/auth is installed (CsrfMiddleware
|
|
3
|
-
// wraps the /api/contact route in routes/api.ts). Without auth, the
|
|
4
|
-
// unprotected form just succeeds.
|
|
5
|
-
const sendsCsrf = ctx.packages.auth ? 'true' : 'false';
|
|
6
|
-
return `import '@/index.css'
|
|
7
|
-
import { SiteHeader } from 'App/Components/SiteHeader.js'
|
|
8
|
-
import { useState } from 'react'
|
|
9
|
-
import { getCsrfToken } from '@rudderjs/middleware'
|
|
10
|
-
|
|
11
|
-
interface FormFields { name: string; email: string; message: string }
|
|
12
|
-
interface FormErrors { name?: string; email?: string; message?: string }
|
|
13
|
-
|
|
14
|
-
export default function ContactDemo() {
|
|
15
|
-
const [fields, setFields] = useState<FormFields>({ name: '', email: '', message: '' })
|
|
16
|
-
const [errors, setErrors] = useState<FormErrors>({})
|
|
17
|
-
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
|
|
18
|
-
const [message, setMessage] = useState('')
|
|
19
|
-
|
|
20
|
-
function setField(key: keyof FormFields, value: string) {
|
|
21
|
-
setFields(f => ({ ...f, [key]: value }))
|
|
22
|
-
if (errors[key]) setErrors(e => ({ ...e, [key]: undefined }))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function submit(e: React.FormEvent) {
|
|
26
|
-
e.preventDefault()
|
|
27
|
-
setStatus('loading')
|
|
28
|
-
setErrors({})
|
|
29
|
-
|
|
30
|
-
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
31
|
-
if (${sendsCsrf}) headers['X-CSRF-Token'] = getCsrfToken()
|
|
32
|
-
|
|
33
|
-
const res = await fetch('/api/contact', {
|
|
34
|
-
method: 'POST',
|
|
35
|
-
headers,
|
|
36
|
-
body: JSON.stringify(fields),
|
|
37
|
-
})
|
|
38
|
-
const data = await res.json() as { ok?: boolean; message?: string; errors?: FormErrors }
|
|
39
|
-
|
|
40
|
-
if (res.ok) {
|
|
41
|
-
setStatus('success')
|
|
42
|
-
setMessage(data.message ?? 'Thanks!')
|
|
43
|
-
setFields({ name: '', email: '', message: '' })
|
|
44
|
-
} else if (res.status === 422) {
|
|
45
|
-
setStatus('error')
|
|
46
|
-
setErrors(data.errors ?? {})
|
|
47
|
-
} else {
|
|
48
|
-
setStatus('error')
|
|
49
|
-
setMessage(\`\${res.status} — \${data.message ?? 'Request failed.'}\`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className="page">
|
|
55
|
-
<SiteHeader />
|
|
56
|
-
|
|
57
|
-
<section className="hero">
|
|
58
|
-
<h1 className="hero-title">Contact</h1>
|
|
59
|
-
<p className="hero-lead">
|
|
60
|
-
POSTs to <code className="inline-code">/api/contact</code>${ctx.packages.auth
|
|
61
|
-
? ' with an X-CSRF-Token header.'
|
|
62
|
-
: '. Add @rudderjs/auth to require CSRF.'}{' '}
|
|
63
|
-
Server-side validated with Zod.
|
|
64
|
-
</p>
|
|
65
|
-
</section>
|
|
66
|
-
|
|
67
|
-
<section className="feature-section" style={{ maxWidth: '32rem', margin: '0 auto' }}>
|
|
68
|
-
<form onSubmit={submit} className="form-card">
|
|
69
|
-
<div>
|
|
70
|
-
<label className="form-label" htmlFor="name">Name</label>
|
|
71
|
-
<input id="name" className="form-input" value={fields.name}
|
|
72
|
-
onChange={e => setField('name', e.target.value)} />
|
|
73
|
-
{errors.name && <p className="form-error">{errors.name}</p>}
|
|
74
|
-
</div>
|
|
75
|
-
<div>
|
|
76
|
-
<label className="form-label" htmlFor="email">Email</label>
|
|
77
|
-
<input id="email" type="email" className="form-input" value={fields.email}
|
|
78
|
-
onChange={e => setField('email', e.target.value)} />
|
|
79
|
-
{errors.email && <p className="form-error">{errors.email}</p>}
|
|
80
|
-
</div>
|
|
81
|
-
<div>
|
|
82
|
-
<label className="form-label" htmlFor="message">Message</label>
|
|
83
|
-
<textarea id="message" rows={4} className="form-input" value={fields.message}
|
|
84
|
-
onChange={e => setField('message', e.target.value)} />
|
|
85
|
-
{errors.message && <p className="form-error">{errors.message}</p>}
|
|
86
|
-
</div>
|
|
87
|
-
<button type="submit" className="form-submit" disabled={status === 'loading'}>
|
|
88
|
-
{status === 'loading' ? 'Sending…' : 'Send message'}
|
|
89
|
-
</button>
|
|
90
|
-
{status === 'success' && <p className="form-success">{message}</p>}
|
|
91
|
-
{status === 'error' && message && <p className="form-error">{message}</p>}
|
|
92
|
-
</form>
|
|
93
|
-
</section>
|
|
94
|
-
</div>
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
`;
|
|
98
|
-
}
|
|
99
|
-
//# sourceMappingURL=contact.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contact.js","sourceRoot":"","sources":["../../../src/templates/demos/contact.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,GAAoB;IACnD,yEAAyE;IACzE,oEAAoE;IACpE,kCAAkC;IAClC,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IAEtD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;UAyBC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sEA6BmD,GAAG,CAAC,QAAQ,CAAC,IAAI;QAC/E,CAAC,CAAC,+BAA+B;QACjC,CAAC,CAAC,uCAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmChD,CAAA;AACD,CAAC"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare function demosFibonacciView(): string;
|
|
2
|
-
/**
|
|
3
|
-
* Returns the inline lines that live in routes/api.ts to handle GET /api/fib.
|
|
4
|
-
* Inlined (not a separate Service) to keep the demo single-file readable.
|
|
5
|
-
*/
|
|
6
|
-
export declare function demosFibonacciApiBlock(): string;
|
|
7
|
-
//# sourceMappingURL=fibonacci.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fibonacci.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/fibonacci.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,IAAI,MAAM,CAyH3C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAmC/C"}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
// Fibonacci demo — sequential vs worker-pool parallel via @rudderjs/concurrency.
|
|
2
|
-
export function demosFibonacciView() {
|
|
3
|
-
return `import { useState } from 'react'
|
|
4
|
-
import '@/index.css'
|
|
5
|
-
import { SiteHeader } from 'App/Components/SiteHeader.js'
|
|
6
|
-
|
|
7
|
-
interface FibResponse {
|
|
8
|
-
n: number
|
|
9
|
-
count: number
|
|
10
|
-
result: number
|
|
11
|
-
sequentialMs: number
|
|
12
|
-
parallelMs: number
|
|
13
|
-
workers: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function FibonacciDemo() {
|
|
17
|
-
const [n, setN ] = useState(36)
|
|
18
|
-
const [count, setCount ] = useState(4)
|
|
19
|
-
const [data, setData ] = useState<FibResponse | null>(null)
|
|
20
|
-
const [loading, setLoading] = useState(false)
|
|
21
|
-
const [error, setError ] = useState<string | null>(null)
|
|
22
|
-
|
|
23
|
-
async function run() {
|
|
24
|
-
setLoading(true); setError(null); setData(null)
|
|
25
|
-
try {
|
|
26
|
-
const res = await fetch(\`/api/fib?n=\${n}&count=\${count}\`)
|
|
27
|
-
const body = await res.json() as FibResponse | { message: string }
|
|
28
|
-
if (!res.ok) throw new Error((body as { message: string }).message ?? 'Failed')
|
|
29
|
-
setData(body as FibResponse)
|
|
30
|
-
} catch (e) {
|
|
31
|
-
setError((e as Error).message)
|
|
32
|
-
} finally {
|
|
33
|
-
setLoading(false)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div className="page">
|
|
39
|
-
<SiteHeader />
|
|
40
|
-
|
|
41
|
-
<section className="hero">
|
|
42
|
-
<h1 className="hero-title">Worker Threads</h1>
|
|
43
|
-
<p className="hero-lead">
|
|
44
|
-
Compute <code className="inline-code">fib(n)</code> N times — sequentially on the main thread, then
|
|
45
|
-
in parallel via <code className="inline-code">@rudderjs/concurrency</code>'s worker pool. Watch the
|
|
46
|
-
parallel cost stay flat as N grows (until you saturate workers).
|
|
47
|
-
</p>
|
|
48
|
-
</section>
|
|
49
|
-
|
|
50
|
-
<section className="feature-section">
|
|
51
|
-
<div className="form-card">
|
|
52
|
-
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
|
|
53
|
-
<div style={{ flex: 1 }}>
|
|
54
|
-
<p className="form-label">n (Fibonacci index)</p>
|
|
55
|
-
<input
|
|
56
|
-
className="form-input"
|
|
57
|
-
type="number"
|
|
58
|
-
min={20}
|
|
59
|
-
max={42}
|
|
60
|
-
value={n}
|
|
61
|
-
onChange={e => setN(Number(e.target.value))}
|
|
62
|
-
/>
|
|
63
|
-
</div>
|
|
64
|
-
<div style={{ flex: 1 }}>
|
|
65
|
-
<p className="form-label">count (parallel calls)</p>
|
|
66
|
-
<input
|
|
67
|
-
className="form-input"
|
|
68
|
-
type="number"
|
|
69
|
-
min={1}
|
|
70
|
-
max={16}
|
|
71
|
-
value={count}
|
|
72
|
-
onChange={e => setCount(Number(e.target.value))}
|
|
73
|
-
/>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<button
|
|
78
|
-
className="form-submit"
|
|
79
|
-
onClick={run}
|
|
80
|
-
disabled={loading}
|
|
81
|
-
style={{ marginBottom: '1rem' }}
|
|
82
|
-
>
|
|
83
|
-
{loading ? 'Computing…' : \`Run \${count} × fib(\${n})\`}
|
|
84
|
-
</button>
|
|
85
|
-
|
|
86
|
-
{error && (
|
|
87
|
-
<p className="form-error">{error}</p>
|
|
88
|
-
)}
|
|
89
|
-
|
|
90
|
-
{data && (
|
|
91
|
-
<>
|
|
92
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '0.75rem' }}>
|
|
93
|
-
<div style={{ padding: '0.75rem', borderRadius: '0.375rem', border: '1px solid var(--border, #e5e7eb)' }}>
|
|
94
|
-
<p className="form-label" style={{ marginBottom: '0.25rem' }}>Sequential (main thread)</p>
|
|
95
|
-
<p style={{ fontSize: '1.25rem', fontWeight: 600, margin: 0 }}>{data.sequentialMs}ms</p>
|
|
96
|
-
<p className="feature-desc" style={{ fontSize: '0.7rem', marginTop: '0.25rem' }}>
|
|
97
|
-
{data.count} × fib({data.n}) one after another, blocking the event loop
|
|
98
|
-
</p>
|
|
99
|
-
</div>
|
|
100
|
-
<div style={{ padding: '0.75rem', borderRadius: '0.375rem', border: '1px solid var(--border, #e5e7eb)' }}>
|
|
101
|
-
<p className="form-label" style={{ marginBottom: '0.25rem' }}>Parallel ({data.workers} workers)</p>
|
|
102
|
-
<p style={{ fontSize: '1.25rem', fontWeight: 600, margin: 0 }}>{data.parallelMs}ms</p>
|
|
103
|
-
<p className="feature-desc" style={{ fontSize: '0.7rem', marginTop: '0.25rem' }}>
|
|
104
|
-
same {data.count} tasks via <code>Concurrency.run([...])</code>
|
|
105
|
-
</p>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
<p className="feature-desc" style={{ fontSize: '0.85rem' }}>
|
|
110
|
-
Result: <code>fib({data.n}) = {data.result.toLocaleString()}</code>
|
|
111
|
-
{' · '}
|
|
112
|
-
<strong>{Math.round((data.sequentialMs / Math.max(data.parallelMs, 1)) * 10) / 10}× faster</strong>
|
|
113
|
-
{' '}via worker pool
|
|
114
|
-
</p>
|
|
115
|
-
</>
|
|
116
|
-
)}
|
|
117
|
-
</div>
|
|
118
|
-
</section>
|
|
119
|
-
</div>
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
`;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Returns the inline lines that live in routes/api.ts to handle GET /api/fib.
|
|
126
|
-
* Inlined (not a separate Service) to keep the demo single-file readable.
|
|
127
|
-
*/
|
|
128
|
-
export function demosFibonacciApiBlock() {
|
|
129
|
-
return `// GET /api/fib?n=36&count=4 — sequential vs worker-pool parallel via @rudderjs/concurrency.
|
|
130
|
-
router.get('/api/fib', async (req, res) => {
|
|
131
|
-
const n = Math.max(1, Math.min(42, Number((req.query as Record<string, string>)['n'] ?? 36)))
|
|
132
|
-
const count = Math.max(1, Math.min(16, Number((req.query as Record<string, string>)['count'] ?? 4)))
|
|
133
|
-
|
|
134
|
-
const { Concurrency } = await import('@rudderjs/concurrency')
|
|
135
|
-
const { cpus } = await import('node:os')
|
|
136
|
-
|
|
137
|
-
// The task body must be self-contained — closures don't capture variables across the
|
|
138
|
-
// worker boundary. Inline 'fib' and bind 'n' via a Function-style template.
|
|
139
|
-
const buildTask = (val: number): (() => number) => {
|
|
140
|
-
const src = \`
|
|
141
|
-
function fib(k) { return k < 2 ? k : fib(k - 1) + fib(k - 2) }
|
|
142
|
-
return fib(\${val})
|
|
143
|
-
\`
|
|
144
|
-
return new Function(src) as () => number
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const seqStart = Date.now()
|
|
148
|
-
let result = 0
|
|
149
|
-
for (let i = 0; i < count; i++) result = buildTask(n)()
|
|
150
|
-
const sequentialMs = Date.now() - seqStart
|
|
151
|
-
|
|
152
|
-
const parStart = Date.now()
|
|
153
|
-
const tasks = Array.from({ length: count }, () => buildTask(n))
|
|
154
|
-
const results = await Concurrency.run(tasks)
|
|
155
|
-
const parallelMs = Date.now() - parStart
|
|
156
|
-
result = results[0] ?? result
|
|
157
|
-
|
|
158
|
-
res.json({
|
|
159
|
-
n, count, result, sequentialMs, parallelMs,
|
|
160
|
-
workers: Math.min(count, cpus().length),
|
|
161
|
-
})
|
|
162
|
-
})`;
|
|
163
|
-
}
|
|
164
|
-
//# sourceMappingURL=fibonacci.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fibonacci.js","sourceRoot":"","sources":["../../../src/templates/demos/fibonacci.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,MAAM,UAAU,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuHR,CAAA;AACD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCN,CAAA;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/http.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,IAAI,MAAM,CAiFtC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAwB1C"}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// HTTP client demo — fluent fetch with retry + timeout against a public API.
|
|
2
|
-
export function demosHttpView() {
|
|
3
|
-
return `import { useState } from 'react'
|
|
4
|
-
import '@/index.css'
|
|
5
|
-
import { SiteHeader } from 'App/Components/SiteHeader.js'
|
|
6
|
-
|
|
7
|
-
interface HttpResponseShape {
|
|
8
|
-
status: number
|
|
9
|
-
ok: boolean
|
|
10
|
-
durationMs: number
|
|
11
|
-
url: string
|
|
12
|
-
body: unknown
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ENDPOINTS = [
|
|
16
|
-
{ url: 'https://jsonplaceholder.typicode.com/todos/1', label: 'Todo (works)' },
|
|
17
|
-
{ url: 'https://jsonplaceholder.typicode.com/users/1', label: 'User (works)' },
|
|
18
|
-
{ url: 'https://httpstat.us/500?sleep=300', label: 'Force 500 (retries 3×)' },
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
export default function HttpDemo() {
|
|
22
|
-
const [url, setUrl] = useState(ENDPOINTS[0]!.url)
|
|
23
|
-
const [data, setData] = useState<HttpResponseShape | null>(null)
|
|
24
|
-
const [loading, setLoading] = useState(false)
|
|
25
|
-
const [error, setError] = useState<string | null>(null)
|
|
26
|
-
|
|
27
|
-
async function fetchUrl() {
|
|
28
|
-
setLoading(true); setError(null)
|
|
29
|
-
try {
|
|
30
|
-
const res = await fetch('/api/http/fetch?url=' + encodeURIComponent(url))
|
|
31
|
-
const body = await res.json() as HttpResponseShape | { message: string }
|
|
32
|
-
if (!res.ok) throw new Error((body as { message: string }).message ?? 'Failed')
|
|
33
|
-
setData(body as HttpResponseShape)
|
|
34
|
-
} catch (e) {
|
|
35
|
-
setError((e as Error).message)
|
|
36
|
-
} finally {
|
|
37
|
-
setLoading(false)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className="page">
|
|
43
|
-
<SiteHeader />
|
|
44
|
-
|
|
45
|
-
<section className="hero">
|
|
46
|
-
<h1 className="hero-title">HTTP client</h1>
|
|
47
|
-
<p className="hero-lead">
|
|
48
|
-
Server-side <code className="inline-code">Http.retry(3, 200).timeout(5000).get(url)</code>{' '}
|
|
49
|
-
against a public API. Pick a URL — the 500 endpoint exercises the retry path.
|
|
50
|
-
</p>
|
|
51
|
-
</section>
|
|
52
|
-
|
|
53
|
-
<section className="feature-section" style={{ maxWidth: '40rem', margin: '0 auto' }}>
|
|
54
|
-
<div className="form-card">
|
|
55
|
-
<label className="form-label" htmlFor="http-url">URL</label>
|
|
56
|
-
<select id="http-url" className="form-input" value={url} onChange={e => setUrl(e.target.value)} style={{ marginBottom: '0.75rem' }}>
|
|
57
|
-
{ENDPOINTS.map(e => (
|
|
58
|
-
<option key={e.url} value={e.url}>{e.label}</option>
|
|
59
|
-
))}
|
|
60
|
-
</select>
|
|
61
|
-
<button className="form-submit" onClick={fetchUrl} disabled={loading}>
|
|
62
|
-
{loading ? 'Fetching…' : 'Fetch'}
|
|
63
|
-
</button>
|
|
64
|
-
{error && (
|
|
65
|
-
<p className="form-error" style={{ marginTop: '1rem' }}>{error}</p>
|
|
66
|
-
)}
|
|
67
|
-
{data && (
|
|
68
|
-
<div style={{ marginTop: '1rem' }}>
|
|
69
|
-
<p className="feature-desc">
|
|
70
|
-
<strong>{data.status}</strong> · {data.durationMs}ms · ok={String(data.ok)}
|
|
71
|
-
</p>
|
|
72
|
-
<pre style={{ marginTop: '0.5rem', padding: '0.75rem', borderRadius: '0.375rem', background: 'var(--muted, #f4f4f5)', fontSize: '0.7rem', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
|
|
73
|
-
{JSON.stringify(data.body, null, 2).slice(0, 600)}
|
|
74
|
-
</pre>
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
</div>
|
|
78
|
-
</section>
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
`;
|
|
83
|
-
}
|
|
84
|
-
export function demosHttpApiBlock() {
|
|
85
|
-
return `// GET /api/http/fetch?url=… — server-side HTTP with retry + timeout.
|
|
86
|
-
router.get('/api/http/fetch', async (req, res) => {
|
|
87
|
-
const url = (req.query as Record<string, string>)['url']
|
|
88
|
-
if (!url) return res.status(422).json({ message: 'url is required' })
|
|
89
|
-
if (!/^https?:\\/\\//.test(url)) return res.status(422).json({ message: 'url must be http(s)' })
|
|
90
|
-
|
|
91
|
-
const { Http } = await import('@rudderjs/http')
|
|
92
|
-
const t0 = Date.now()
|
|
93
|
-
try {
|
|
94
|
-
const response = await Http.retry(3, 200).timeout(5000).get(url)
|
|
95
|
-
let body: unknown = null
|
|
96
|
-
try { body = response.json() } catch { body = response.body.slice(0, 600) }
|
|
97
|
-
res.json({
|
|
98
|
-
status: response.status,
|
|
99
|
-
ok: response.ok(),
|
|
100
|
-
durationMs: Date.now() - t0,
|
|
101
|
-
url,
|
|
102
|
-
body,
|
|
103
|
-
})
|
|
104
|
-
} catch (e) {
|
|
105
|
-
res.status(502).json({ message: (e as Error).message ?? 'Request failed', durationMs: Date.now() - t0, url })
|
|
106
|
-
}
|
|
107
|
-
})`;
|
|
108
|
-
}
|
|
109
|
-
//# sourceMappingURL=http.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/templates/demos/http.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+ER,CAAA;AACD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;GAsBN,CAAA;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-view.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/index-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAG7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAsC3D"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { shouldScaffoldDemo } from '../../templates.js';
|
|
2
|
-
import { DEMOS, demoHref, demoTitle } from './registry.js';
|
|
3
|
-
export function demosIndexView(ctx) {
|
|
4
|
-
const cards = DEMOS
|
|
5
|
-
.filter(d => shouldScaffoldDemo(ctx, d.value))
|
|
6
|
-
.map(d => ` <a key="${demoHref(d)}" href="${demoHref(d)}" className="feature-card">
|
|
7
|
-
<h3 className="feature-title">${demoTitle(d)}</h3>
|
|
8
|
-
<p className="feature-desc">${escapeJsxText(d.description)}</p>
|
|
9
|
-
<p className="feature-desc" style={{ fontSize: '0.7rem', opacity: 0.7 }}>${d.packages.join(' · ')}</p>
|
|
10
|
-
</a>`)
|
|
11
|
-
.join('\n');
|
|
12
|
-
return `import '@/index.css'
|
|
13
|
-
import { SiteHeader } from 'App/Components/SiteHeader.js'
|
|
14
|
-
|
|
15
|
-
// Override the id-derived URL ('/demos/index') so SPA nav matches the controller ('/demos').
|
|
16
|
-
export const route = '/demos'
|
|
17
|
-
|
|
18
|
-
export default function DemosIndex() {
|
|
19
|
-
return (
|
|
20
|
-
<div className="page">
|
|
21
|
-
<SiteHeader />
|
|
22
|
-
|
|
23
|
-
<section className="hero">
|
|
24
|
-
<h1 className="hero-title">Demos</h1>
|
|
25
|
-
<p className="hero-lead">
|
|
26
|
-
Small, focused examples of what the framework can do. Each one is a single
|
|
27
|
-
controller returning <code className="inline-code">view('demos.<name>')</code>.
|
|
28
|
-
</p>
|
|
29
|
-
</section>
|
|
30
|
-
|
|
31
|
-
<section className="feature-section">
|
|
32
|
-
<div className="feature-grid">
|
|
33
|
-
${cards}
|
|
34
|
-
</div>
|
|
35
|
-
</section>
|
|
36
|
-
</div>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
}
|
|
41
|
-
// Description strings live as plain text in the registry, but they're emitted
|
|
42
|
-
// inside JSX text nodes. Escape '<' and '>' so a raw '<name>' (e.g. in the
|
|
43
|
-
// localization description) doesn't get parsed as a JSX element.
|
|
44
|
-
function escapeJsxText(s) {
|
|
45
|
-
return s.replace(/</g, '<').replace(/>/g, '>');
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=index-view.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-view.js","sourceRoot":"","sources":["../../../src/templates/demos/index-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAwB,MAAM,oBAAoB,CAAA;AAC7E,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE1D,MAAM,UAAU,cAAc,CAAC,GAAoB;IACjD,MAAM,KAAK,GAAG,KAAK;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;SAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,mBAAmB,QAAQ,CAAC,CAAC,CAAC,WAAW,QAAQ,CAAC,CAAC,CAAC;0CACxB,SAAS,CAAC,CAAC,CAAC;wCACd,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;qFACiB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;aAC9F,CAAC;SACT,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;;;;;;;;;;;;;;;;;;;;;EAqBP,KAAK;;;;;;CAMN,CAAA;AACD,CAAC;AAED,8EAA8E;AAC9E,2EAA2E;AAC3E,iEAAiE;AACjE,SAAS,aAAa,CAAC,CAAS;IAC9B,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACtD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/localization.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,IAAI,MAAM,CA0E9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAkBlD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAuB/D"}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// Localization demo — language switcher hits /api/i18n?locale=…
|
|
2
|
-
// The route uses runWithLocale + setLocale + trans() to render strings.
|
|
3
|
-
// Lang files at lang/<locale>/messages.json.
|
|
4
|
-
export function demosLocalizationView() {
|
|
5
|
-
return `import { useEffect, useState } from 'react'
|
|
6
|
-
import '@/index.css'
|
|
7
|
-
import { SiteHeader } from 'App/Components/SiteHeader.js'
|
|
8
|
-
|
|
9
|
-
interface I18nResponse {
|
|
10
|
-
locale: string
|
|
11
|
-
greeting: string
|
|
12
|
-
items: string
|
|
13
|
-
welcome: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const LOCALES = [
|
|
17
|
-
{ code: 'en', label: 'English' },
|
|
18
|
-
{ code: 'es', label: 'Español' },
|
|
19
|
-
{ code: 'ar', label: 'العربية' },
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
export default function LocalizationDemo() {
|
|
23
|
-
const [locale, setLocale] = useState('en')
|
|
24
|
-
const [data, setData] = useState<I18nResponse | null>(null)
|
|
25
|
-
|
|
26
|
-
async function load(loc: string) {
|
|
27
|
-
const res = await fetch(\`/api/i18n?locale=\${loc}\`)
|
|
28
|
-
setData(await res.json() as I18nResponse)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
useEffect(() => { load(locale) }, [locale])
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div className="page">
|
|
35
|
-
<SiteHeader />
|
|
36
|
-
|
|
37
|
-
<section className="hero">
|
|
38
|
-
<h1 className="hero-title">Localization</h1>
|
|
39
|
-
<p className="hero-lead">
|
|
40
|
-
Pick a locale to fetch the same keys via{' '}
|
|
41
|
-
<code className="inline-code">trans()</code> on the server. Strings live in{' '}
|
|
42
|
-
<code className="inline-code">lang/<locale>/messages.json</code>.
|
|
43
|
-
</p>
|
|
44
|
-
</section>
|
|
45
|
-
|
|
46
|
-
<section className="feature-section" style={{ maxWidth: '32rem', margin: '0 auto' }}>
|
|
47
|
-
<div className="form-card">
|
|
48
|
-
<div style={{ marginBottom: '1rem' }}>
|
|
49
|
-
<label className="form-label" htmlFor="locale">Locale</label>
|
|
50
|
-
<select
|
|
51
|
-
id="locale"
|
|
52
|
-
className="form-input"
|
|
53
|
-
value={locale}
|
|
54
|
-
onChange={e => setLocale(e.target.value)}
|
|
55
|
-
>
|
|
56
|
-
{LOCALES.map(l => (
|
|
57
|
-
<option key={l.code} value={l.code}>{l.label} ({l.code})</option>
|
|
58
|
-
))}
|
|
59
|
-
</select>
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
{data && (
|
|
63
|
-
<div style={{ display: 'grid', gap: '0.5rem' }}>
|
|
64
|
-
<p className="feature-desc"><strong>welcome:</strong> {data.welcome}</p>
|
|
65
|
-
<p className="feature-desc"><strong>greeting:</strong> {data.greeting}</p>
|
|
66
|
-
<p className="feature-desc"><strong>items (3):</strong> {data.items}</p>
|
|
67
|
-
<p className="feature-desc" style={{ fontSize: '0.7rem', opacity: 0.7 }}>
|
|
68
|
-
resolved server-side with locale = <code>{data.locale}</code>
|
|
69
|
-
</p>
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
</section>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
`;
|
|
78
|
-
}
|
|
79
|
-
export function demosLocalizationApiBlock() {
|
|
80
|
-
return `// GET /api/i18n?locale=… — resolves the same keys in the requested locale.
|
|
81
|
-
router.get('/api/i18n', async (req, res) => {
|
|
82
|
-
const { runWithLocale, setLocale, getLocale, trans } = await import('@rudderjs/localization')
|
|
83
|
-
const requested = (req.query as Record<string, string>)['locale'] ?? 'en'
|
|
84
|
-
|
|
85
|
-
const payload = await runWithLocale(requested, async () => {
|
|
86
|
-
setLocale(requested)
|
|
87
|
-
return {
|
|
88
|
-
locale: getLocale(),
|
|
89
|
-
welcome: await trans('messages.welcome'),
|
|
90
|
-
greeting: await trans('messages.greeting', { name: 'World' }),
|
|
91
|
-
items: await trans('messages.items', 3),
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
res.json(payload)
|
|
96
|
-
})`;
|
|
97
|
-
}
|
|
98
|
-
export function langMessages(locale) {
|
|
99
|
-
if (locale === 'es') {
|
|
100
|
-
return `{
|
|
101
|
-
"welcome": "¡Bienvenido a RudderJS!",
|
|
102
|
-
"greeting": "Hola, :name!",
|
|
103
|
-
"items": "{0} sin elementos|{1} un elemento|{n} :count elementos"
|
|
104
|
-
}
|
|
105
|
-
`;
|
|
106
|
-
}
|
|
107
|
-
if (locale === 'ar') {
|
|
108
|
-
return `{
|
|
109
|
-
"welcome": "مرحباً بك في RudderJS!",
|
|
110
|
-
"greeting": "مرحباً، :name!",
|
|
111
|
-
"items": "{0} لا توجد عناصر|{1} عنصر واحد|{n} :count عناصر"
|
|
112
|
-
}
|
|
113
|
-
`;
|
|
114
|
-
}
|
|
115
|
-
return `{
|
|
116
|
-
"welcome": "Welcome to RudderJS!",
|
|
117
|
-
"greeting": "Hello, :name!",
|
|
118
|
-
"items": "{0} no items|{1} one item|{n} :count items"
|
|
119
|
-
}
|
|
120
|
-
`;
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=localization.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"localization.js","sourceRoot":"","sources":["../../../src/templates/demos/localization.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wEAAwE;AACxE,6CAA6C;AAE7C,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwER,CAAA;AACD,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;GAgBN,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;;;;;CAKV,CAAA;IACC,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;;;;;CAKV,CAAA;IACC,CAAC;IACD,OAAO;;;;;CAKR,CAAA;AACD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../../../src/templates/demos/mail.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,IAAI,MAAM,CA6EtC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAkBrC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAkB1C"}
|