svelte-realtime 0.1.8 → 0.4.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/README.md +144 -6
- package/cli.js +205 -0
- package/client.d.ts +1 -1
- package/client.js +405 -110
- package/devtools.js +6 -5
- package/package.json +10 -3
- package/server.d.ts +853 -851
- package/server.js +765 -469
- package/test.d.ts +110 -110
- package/test.js +489 -330
- package/vite.js +1232 -141
package/README.md
CHANGED
|
@@ -6,7 +6,19 @@ Write server functions. Import them in components. Call them over WebSocket. No
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx svelte-realtime my-app
|
|
13
|
+
cd my-app
|
|
14
|
+
npm run dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This creates a SvelteKit project with svelte-realtime fully wired: adapter, vite plugins, WebSocket hooks, and a working counter example you can open in your browser right away.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Manual setup
|
|
10
22
|
|
|
11
23
|
Starting from a SvelteKit project. If you do not have one yet, run `npx sv create my-app && cd my-app && npm install` first.
|
|
12
24
|
|
|
@@ -19,7 +31,7 @@ npm install -D ws
|
|
|
19
31
|
```
|
|
20
32
|
|
|
21
33
|
What each package does:
|
|
22
|
-
- `svelte-adapter-uws` -- the SvelteKit adapter that runs your app on uWebSockets.js with built-in WebSocket support
|
|
34
|
+
- `svelte-adapter-uws` (>=0.4.0) -- the SvelteKit adapter that runs your app on uWebSockets.js with built-in WebSocket support
|
|
23
35
|
- `svelte-realtime` -- this library (RPC + streams on top of the adapter)
|
|
24
36
|
- `uWebSockets.js` -- the native C++ HTTP/WebSocket server (installed from GitHub, not npm)
|
|
25
37
|
- `ws` -- dev dependency used by the adapter during `npm run dev` (not needed in production)
|
|
@@ -75,6 +87,8 @@ export function upgrade({ cookies }) {
|
|
|
75
87
|
|
|
76
88
|
`message` is a ready-made hook that routes incoming WebSocket messages to your live functions. `upgrade` decides who can connect and attaches user data to the connection.
|
|
77
89
|
|
|
90
|
+
> **This file is required.** The Vite plugin will warn at startup if it finds live modules in `src/live/` but no `src/hooks.ws.js` (or `.ts`). Without it, WebSocket messages have nothing on the server side to route them, and all RPC calls will silently time out.
|
|
91
|
+
|
|
78
92
|
### Step 5: Write a server function
|
|
79
93
|
|
|
80
94
|
Create the `src/live/` directory. Every `.js` file in this directory becomes a module of callable server functions.
|
|
@@ -162,6 +176,9 @@ The `ctx` object passed to every server function contains:
|
|
|
162
176
|
| `ctx.throttle` | `(topic, event, data, ms)` -- publish at most once per `ms` ms |
|
|
163
177
|
| `ctx.debounce` | `(topic, event, data, ms)` -- publish after `ms` ms of silence |
|
|
164
178
|
| `ctx.signal` | `(userId, event, data)` -- point-to-point message |
|
|
179
|
+
| `ctx.batch` | `(messages)` -- publish multiple messages in one call via `platform.batch()` |
|
|
180
|
+
|
|
181
|
+
Note: `ctx.user` may contain adapter-injected properties (`__subscriptions`, `remoteAddress`) in addition to whatever your `upgrade()` function returned. These are stripped automatically by the adapter before broadcasting to other clients.
|
|
165
182
|
|
|
166
183
|
---
|
|
167
184
|
|
|
@@ -385,6 +402,16 @@ try {
|
|
|
385
402
|
}
|
|
386
403
|
```
|
|
387
404
|
|
|
405
|
+
### Terminal close codes
|
|
406
|
+
|
|
407
|
+
When the adapter's `ready()` promise rejects (terminal close codes 1008, 4401, 4403, exhausted retries, or explicit `close()`), svelte-realtime:
|
|
408
|
+
|
|
409
|
+
- Rejects all pending RPCs immediately with `RpcError('CONNECTION_CLOSED', ...)`
|
|
410
|
+
- Sets an `{ error }` state on all active stream stores
|
|
411
|
+
- Drains the offline queue with errors
|
|
412
|
+
|
|
413
|
+
RPCs called after a terminal close reject immediately without sending.
|
|
414
|
+
|
|
388
415
|
### Reusable error boundary component
|
|
389
416
|
|
|
390
417
|
For Svelte 5, you can build a reusable boundary that handles all three stream states:
|
|
@@ -649,6 +676,20 @@ const [board, column] = await batch(() => [
|
|
|
649
676
|
|
|
650
677
|
Each call resolves or rejects independently -- one failure does not cancel the others. Batches are limited to 50 calls -- enforced both client-side (rejects before sending) and server-side.
|
|
651
678
|
|
|
679
|
+
### Server-side batching
|
|
680
|
+
|
|
681
|
+
Use `ctx.batch()` inside RPC handlers to publish multiple messages in a single call:
|
|
682
|
+
|
|
683
|
+
```js
|
|
684
|
+
export const resetBoard = live(async (ctx, boardId) => {
|
|
685
|
+
await db.boards.reset(boardId);
|
|
686
|
+
ctx.batch([
|
|
687
|
+
{ topic: `board:${boardId}`, event: 'set', data: [] },
|
|
688
|
+
{ topic: `board:${boardId}:presence`, event: 'set', data: [] }
|
|
689
|
+
]);
|
|
690
|
+
});
|
|
691
|
+
```
|
|
692
|
+
|
|
652
693
|
---
|
|
653
694
|
|
|
654
695
|
## Optimistic updates
|
|
@@ -911,13 +952,13 @@ export const presence = live.stream('room:lobby', async (ctx) => {
|
|
|
911
952
|
});
|
|
912
953
|
```
|
|
913
954
|
|
|
914
|
-
`onSubscribe` fires after `ws.subscribe(topic)` and the initial data fetch. `onUnsubscribe` fires when the WebSocket closes
|
|
955
|
+
`onSubscribe` fires after `ws.subscribe(topic)` and the initial data fetch. `onUnsubscribe` fires in real time when a client unsubscribes from a topic (adapter 0.4.0+), and also when the WebSocket closes for any remaining topics. Export both hooks from your `hooks.ws.js`:
|
|
915
956
|
|
|
916
957
|
```js
|
|
917
|
-
export { message, close } from 'svelte-realtime/server';
|
|
958
|
+
export { message, close, unsubscribe } from 'svelte-realtime/server';
|
|
918
959
|
```
|
|
919
960
|
|
|
920
|
-
`onUnsubscribe` fires for both static and dynamic topics. For dynamic topics, the server tracks which stream produced each subscription and
|
|
961
|
+
`onUnsubscribe` fires for both static and dynamic topics. For dynamic topics, the server tracks which stream produced each subscription and fires the correct hook. The `unsubscribe` hook fires as soon as the client drops a topic; `close` only fires for topics still active at disconnect time. There is no double-firing.
|
|
921
962
|
|
|
922
963
|
---
|
|
923
964
|
|
|
@@ -1005,6 +1046,49 @@ export const message = createMessage({
|
|
|
1005
1046
|
|
|
1006
1047
|
---
|
|
1007
1048
|
|
|
1049
|
+
## Prometheus metrics
|
|
1050
|
+
|
|
1051
|
+
Opt-in instrumentation for RPC calls, stream subscriptions, and cron executions. Zero overhead if not called.
|
|
1052
|
+
|
|
1053
|
+
```js
|
|
1054
|
+
import { live } from 'svelte-realtime/server';
|
|
1055
|
+
import { createMetricsRegistry } from 'svelte-adapter-uws-extensions/prometheus';
|
|
1056
|
+
|
|
1057
|
+
const registry = createMetricsRegistry();
|
|
1058
|
+
live.metrics(registry);
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
This registers counters/histograms for:
|
|
1062
|
+
- `svelte_realtime_rpc_total` -- RPC call count by path and status
|
|
1063
|
+
- `svelte_realtime_rpc_duration_seconds` -- RPC latency by path
|
|
1064
|
+
- `svelte_realtime_rpc_errors_total` -- RPC errors by path and code
|
|
1065
|
+
- `svelte_realtime_stream_subscriptions` -- active stream subscription gauge by topic
|
|
1066
|
+
- `svelte_realtime_cron_total` -- cron execution count by path and status
|
|
1067
|
+
- `svelte_realtime_cron_errors_total` -- cron errors by path
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1071
|
+
## Circuit breaker
|
|
1072
|
+
|
|
1073
|
+
Wrap a stream or RPC init function with a circuit breaker from `svelte-adapter-uws-extensions`. When the breaker is open, returns a fallback value or throws `SERVICE_UNAVAILABLE`.
|
|
1074
|
+
|
|
1075
|
+
```js
|
|
1076
|
+
import { live } from 'svelte-realtime/server';
|
|
1077
|
+
import { createBreaker } from 'svelte-adapter-uws-extensions/breaker';
|
|
1078
|
+
|
|
1079
|
+
const dbBreaker = createBreaker({ threshold: 5, resetMs: 30000 });
|
|
1080
|
+
|
|
1081
|
+
export const items = live.stream('items',
|
|
1082
|
+
live.breaker({ breaker: dbBreaker, fallback: [] }, async (ctx) => {
|
|
1083
|
+
return db.items.list();
|
|
1084
|
+
})
|
|
1085
|
+
);
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
If `fallback` is omitted and the circuit is open, the call throws `LiveError('SERVICE_UNAVAILABLE', ...)`.
|
|
1089
|
+
|
|
1090
|
+
---
|
|
1091
|
+
|
|
1008
1092
|
## Cron scheduling
|
|
1009
1093
|
|
|
1010
1094
|
Use `live.cron()` to run server-side functions on a schedule and publish results to a topic.
|
|
@@ -1273,6 +1357,17 @@ On the client, the room export becomes an object with sub-streams and actions. R
|
|
|
1273
1357
|
<button onclick={() => board.addCard(boardId, 'New card')}>Add</button>
|
|
1274
1358
|
```
|
|
1275
1359
|
|
|
1360
|
+
### Room hooks shortcut
|
|
1361
|
+
|
|
1362
|
+
Rooms expose a `.hooks` property for one-liner wiring in `hooks.ws.js`:
|
|
1363
|
+
|
|
1364
|
+
```js
|
|
1365
|
+
// src/hooks.ws.js
|
|
1366
|
+
import { board } from './live/collab.js';
|
|
1367
|
+
|
|
1368
|
+
export const { message, close, unsubscribe } = board.hooks;
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1276
1371
|
---
|
|
1277
1372
|
|
|
1278
1373
|
## Webhooks
|
|
@@ -1409,6 +1504,8 @@ export const feed = live.stream('feed', async (ctx) => {
|
|
|
1409
1504
|
|
|
1410
1505
|
Replay requires the replay extension from `svelte-adapter-uws-extensions`. When replay is not available or the gap is too large, the client falls back to a full refetch automatically.
|
|
1411
1506
|
|
|
1507
|
+
With adapter 0.4.0+, the replay end marker sends `{ reqId }` (replay complete) or `{ reqId, truncated: true }` (cache miss). When truncated, the client automatically resets its sequence number and triggers a full refetch.
|
|
1508
|
+
|
|
1412
1509
|
---
|
|
1413
1510
|
|
|
1414
1511
|
## Redis multi-instance
|
|
@@ -1714,11 +1811,14 @@ Import from `svelte-realtime/server`.
|
|
|
1714
1811
|
| `message` | Ready-made message hook |
|
|
1715
1812
|
| `createMessage(options?)` | Custom message hook factory |
|
|
1716
1813
|
| `pipe(stream, ...transforms)` | Composable stream transforms |
|
|
1717
|
-
| `close` | Ready-made close hook (fires onUnsubscribe) |
|
|
1814
|
+
| `close` | Ready-made close hook (fires onUnsubscribe for remaining topics) |
|
|
1815
|
+
| `unsubscribe` | Ready-made unsubscribe hook (fires onUnsubscribe in real time) |
|
|
1718
1816
|
| `setCronPlatform(platform)` | Capture platform for cron jobs |
|
|
1719
1817
|
| `onCronError(handler)` | Global cron error handler |
|
|
1720
1818
|
| `enableSignals(ws)` | Enable point-to-point signal delivery |
|
|
1721
1819
|
| `_activateDerived(platform)` | Enable derived stream listeners |
|
|
1820
|
+
| `live.metrics(registry)` | Opt-in Prometheus metrics |
|
|
1821
|
+
| `live.breaker(options, fn)` | Circuit breaker wrapper |
|
|
1722
1822
|
|
|
1723
1823
|
---
|
|
1724
1824
|
|
|
@@ -1733,6 +1833,7 @@ Import from `svelte-realtime/client`.
|
|
|
1733
1833
|
| `configure(config)` | Connection hooks and offline queue setup |
|
|
1734
1834
|
| `combine(...stores, fn)` | Multi-store composition |
|
|
1735
1835
|
| `onSignal(userId, callback)` | Listen for point-to-point signals |
|
|
1836
|
+
| `onDerived` | Re-exported from adapter: reactive derived topic subscription |
|
|
1736
1837
|
|
|
1737
1838
|
**Stream store methods** (on `$live/` stream imports):
|
|
1738
1839
|
|
|
@@ -1808,6 +1909,43 @@ npm test
|
|
|
1808
1909
|
|
|
1809
1910
|
---
|
|
1810
1911
|
|
|
1912
|
+
## Tauri and Capacitor
|
|
1913
|
+
|
|
1914
|
+
svelte-realtime works with Tauri and Capacitor without any static build or architectural changes.
|
|
1915
|
+
|
|
1916
|
+
Both runtimes let you point their webview at a live URL instead of local files. Your SvelteKit app runs on the server as normal -- SSR, WebSocket hydration, live stores, RPC -- and the native wrapper adds platform APIs (camera, push notifications, filesystem, etc.) on top.
|
|
1917
|
+
|
|
1918
|
+
**Capacitor** -- `capacitor.config.ts`:
|
|
1919
|
+
|
|
1920
|
+
```ts
|
|
1921
|
+
import { CapacitorConfig } from '@capacitor/cli';
|
|
1922
|
+
|
|
1923
|
+
const config: CapacitorConfig = {
|
|
1924
|
+
appId: 'com.example.app',
|
|
1925
|
+
appName: 'My App',
|
|
1926
|
+
server: {
|
|
1927
|
+
url: 'https://yourapp.com'
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
|
|
1931
|
+
export default config;
|
|
1932
|
+
```
|
|
1933
|
+
|
|
1934
|
+
**Tauri** -- `tauri.conf.json`:
|
|
1935
|
+
|
|
1936
|
+
```json
|
|
1937
|
+
{
|
|
1938
|
+
"build": {
|
|
1939
|
+
"devPath": "https://yourapp.com",
|
|
1940
|
+
"distDir": "https://yourapp.com"
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
```
|
|
1944
|
+
|
|
1945
|
+
The webview loads your server directly. No static adapter, no URL configuration in the client, nothing special in your SvelteKit code.
|
|
1946
|
+
|
|
1947
|
+
---
|
|
1948
|
+
|
|
1811
1949
|
## License
|
|
1812
1950
|
|
|
1813
1951
|
MIT
|
package/cli.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { resolve, join } from 'path';
|
|
6
|
+
import * as p from '@clack/prompts';
|
|
7
|
+
import { detectAgent, parseArgs } from './cli-utils.js';
|
|
8
|
+
|
|
9
|
+
const DEMO_REPO = 'https://github.com/lanteanio/svelte-realtime-demo.git';
|
|
10
|
+
|
|
11
|
+
const parsed = parseArgs(process.argv.slice(2), {
|
|
12
|
+
dirExists: (name) => existsSync(resolve(process.cwd(), name))
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if ('help' in parsed) {
|
|
16
|
+
console.log(`
|
|
17
|
+
Usage: npx svelte-realtime [project-name] [--template minimal|example|demo]
|
|
18
|
+
|
|
19
|
+
Scaffolds a SvelteKit project with svelte-realtime wired up and ready to go.
|
|
20
|
+
`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if ('error' in parsed) {
|
|
25
|
+
console.error(parsed.error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
p.intro('svelte-realtime');
|
|
30
|
+
|
|
31
|
+
const name =
|
|
32
|
+
parsed.name ||
|
|
33
|
+
/** @type {string} */ (
|
|
34
|
+
await p.text({
|
|
35
|
+
message: 'Project name',
|
|
36
|
+
placeholder: 'my-app',
|
|
37
|
+
validate(value) {
|
|
38
|
+
if (!value) return 'Required.';
|
|
39
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(value))
|
|
40
|
+
return 'Use only letters, numbers, hyphens, and underscores.';
|
|
41
|
+
if (existsSync(resolve(process.cwd(), value))) return `Directory "${value}" already exists.`;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (p.isCancel(name)) {
|
|
47
|
+
p.cancel('Cancelled.');
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const dest = resolve(process.cwd(), name);
|
|
52
|
+
|
|
53
|
+
const template =
|
|
54
|
+
parsed.template ||
|
|
55
|
+
/** @type {string} */ (
|
|
56
|
+
await p.select({
|
|
57
|
+
message: 'Which template would you like?',
|
|
58
|
+
options: [
|
|
59
|
+
{
|
|
60
|
+
value: 'minimal',
|
|
61
|
+
label: 'Wiring only',
|
|
62
|
+
hint: 'SvelteKit + svelte-realtime, no example code'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
value: 'example',
|
|
66
|
+
label: 'Barebones example',
|
|
67
|
+
hint: 'SvelteKit + svelte-realtime with a working counter'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
value: 'demo',
|
|
71
|
+
label: 'Full demo app',
|
|
72
|
+
hint: 'clone svelte-realtime-demo'
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (p.isCancel(template)) {
|
|
79
|
+
p.cancel('Cancelled.');
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const agent = detectAgent(process.env.npm_config_user_agent);
|
|
84
|
+
|
|
85
|
+
if (template === 'demo') {
|
|
86
|
+
const s = p.spinner();
|
|
87
|
+
s.start('Cloning demo repository');
|
|
88
|
+
run(`git clone ${DEMO_REPO} "${name}"`);
|
|
89
|
+
s.stop('Cloned.');
|
|
90
|
+
|
|
91
|
+
s.start('Installing dependencies');
|
|
92
|
+
run(`${agent} install`, dest);
|
|
93
|
+
s.stop('Installed.');
|
|
94
|
+
|
|
95
|
+
p.outro(`Done. cd ${name} && ${agent} run dev`);
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const s = p.spinner();
|
|
100
|
+
|
|
101
|
+
s.start('Creating SvelteKit project');
|
|
102
|
+
run(`npx -y sv create "${name}" --template minimal --types ts`);
|
|
103
|
+
s.stop('Project created.');
|
|
104
|
+
|
|
105
|
+
s.start('Installing dependencies');
|
|
106
|
+
const add = agent === 'npm' ? 'install' : 'add';
|
|
107
|
+
run(`${agent} ${add} svelte-adapter-uws svelte-realtime`, dest);
|
|
108
|
+
run(`${agent} ${add} uNetworking/uWebSockets.js#v20.60.0`, dest);
|
|
109
|
+
run(`${agent} ${add} -D ws`, dest);
|
|
110
|
+
s.stop('Dependencies installed.');
|
|
111
|
+
|
|
112
|
+
s.start('Configuring svelte-realtime');
|
|
113
|
+
|
|
114
|
+
writeFileSync(
|
|
115
|
+
join(dest, 'svelte.config.js'),
|
|
116
|
+
`import adapter from 'svelte-adapter-uws';
|
|
117
|
+
import { vitePreprocess } from '@sveltejs/kit/vite';
|
|
118
|
+
|
|
119
|
+
export default {
|
|
120
|
+
\tkit: {
|
|
121
|
+
\t\tadapter: adapter({ websocket: true })
|
|
122
|
+
\t},
|
|
123
|
+
\tpreprocess: [vitePreprocess()]
|
|
124
|
+
};
|
|
125
|
+
`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
writeFileSync(
|
|
129
|
+
join(dest, 'vite.config.ts'),
|
|
130
|
+
`import { sveltekit } from '@sveltejs/kit/vite';
|
|
131
|
+
import uws from 'svelte-adapter-uws/vite';
|
|
132
|
+
import realtime from 'svelte-realtime/vite';
|
|
133
|
+
import { defineConfig } from 'vite';
|
|
134
|
+
|
|
135
|
+
export default defineConfig({
|
|
136
|
+
\tplugins: [sveltekit(), uws(), realtime()]
|
|
137
|
+
});
|
|
138
|
+
`
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
writeFileSync(
|
|
142
|
+
join(dest, 'src', 'hooks.ws.ts'),
|
|
143
|
+
`import { message } from 'svelte-realtime/server';
|
|
144
|
+
export { message };
|
|
145
|
+
|
|
146
|
+
export function upgrade() {
|
|
147
|
+
\treturn { id: crypto.randomUUID() };
|
|
148
|
+
}
|
|
149
|
+
`
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (template === 'example') {
|
|
153
|
+
mkdirSync(join(dest, 'src', 'live'), { recursive: true });
|
|
154
|
+
|
|
155
|
+
writeFileSync(
|
|
156
|
+
join(dest, 'src', 'live', 'counter.ts'),
|
|
157
|
+
`import { live } from 'svelte-realtime/server';
|
|
158
|
+
|
|
159
|
+
let count = 0;
|
|
160
|
+
|
|
161
|
+
export const increment = live((ctx) => {
|
|
162
|
+
\tcount++;
|
|
163
|
+
\tctx.publish('count', 'set', count);
|
|
164
|
+
\treturn count;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export const counter = live.stream('count', () => {
|
|
168
|
+
\treturn count;
|
|
169
|
+
}, { merge: 'set' });
|
|
170
|
+
`
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
writeFileSync(
|
|
174
|
+
join(dest, 'src', 'routes', '+page.svelte'),
|
|
175
|
+
`<script lang="ts">
|
|
176
|
+
\timport { increment, counter } from '$live/counter';
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<h1>svelte-realtime</h1>
|
|
180
|
+
|
|
181
|
+
{#if $counter === undefined}
|
|
182
|
+
\t<p>Connecting...</p>
|
|
183
|
+
{:else}
|
|
184
|
+
\t<p>Count: {$counter}</p>
|
|
185
|
+
{/if}
|
|
186
|
+
|
|
187
|
+
<button onclick={() => increment()}>+1</button>
|
|
188
|
+
`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
s.stop('Configured.');
|
|
193
|
+
|
|
194
|
+
p.outro(`Done. cd ${name} && ${agent} run dev`);
|
|
195
|
+
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
function run(cmd, cwd) {
|
|
199
|
+
try {
|
|
200
|
+
execSync(cmd, { cwd, stdio: 'pipe' });
|
|
201
|
+
} catch (e) {
|
|
202
|
+
p.cancel(`Command failed: ${cmd}\n${e.stderr || e.message}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
package/client.d.ts
CHANGED
|
@@ -129,7 +129,7 @@ export function batch<T extends Promise<any>[]>(
|
|
|
129
129
|
*
|
|
130
130
|
* @internal
|
|
131
131
|
*/
|
|
132
|
-
export function __binaryRpc(path: string): (buffer: ArrayBuffer, ...args: any[]) => Promise<any>;
|
|
132
|
+
export function __binaryRpc(path: string): (buffer: ArrayBuffer | ArrayBufferView, ...args: any[]) => Promise<any>;
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* Configure client-side connection hooks.
|