+144
examples/bluesky/README.md
+144
examples/bluesky/README.md
···
1
+
# Bluesky Feed Reader Example
2
+
3
+
This example demonstrates how to use Cap'n Web RPC with AT Protocol's XRPC endpoints to build a Bluesky profile and feed viewer. It showcases:
4
+
5
+
- **AT Protocol Integration**: Bridging Cap'n Web RPC to Bluesky's XRPC API
6
+
- **Batch Pipelining**: Fetching profile and feed data in a single HTTP request
7
+
- **External API Calls**: Real-world pattern for integrating third-party APIs
8
+
- **Performance Comparison**: Toggle between batched and sequential modes
9
+
10
+
## Features
11
+
12
+
- Fetch Bluesky profiles by handle (e.g., `bsky.app`, `alice.bsky.social`)
13
+
- Display user info: avatar, bio, follower/following counts
14
+
- Show recent posts with engagement metrics
15
+
- Compare batched vs sequential request performance
16
+
- Beautiful, responsive UI with Svelte
17
+
18
+
## Running the Example
19
+
20
+
### Start the Go Backend
21
+
22
+
```bash
23
+
cd examples/bluesky
24
+
go mod tidy
25
+
go run main.go
26
+
```
27
+
28
+
The server will start on `http://localhost:8000`
29
+
30
+
### Start the Frontend
31
+
32
+
In a new terminal:
33
+
34
+
```bash
35
+
cd examples/bluesky/static
36
+
npm install
37
+
npm run dev
38
+
```
39
+
40
+
The Svelte dev server will start on `http://localhost:3000`
41
+
42
+
## How It Works
43
+
44
+
### Backend (Go)
45
+
46
+
The Go server implements two RPC methods:
47
+
48
+
1. **`getProfile(handle)`** - Fetches profile data from `app.bsky.actor.getProfile`
49
+
2. **`getFeed(handle, limit)`** - Fetches posts from `app.bsky.feed.getAuthorFeed`
50
+
51
+
Both methods call the public Bluesky API at `https://public.api.bsky.app/xrpc/`
52
+
53
+
### Frontend (Svelte)
54
+
55
+
The frontend uses the `capnweb` JavaScript library to make RPC calls:
56
+
57
+
**Batched Mode** (default):
58
+
```javascript
59
+
const [profile, feed] = await Promise.all([
60
+
api.getProfile(handle),
61
+
api.getFeed(handle, 10),
62
+
]);
63
+
```
64
+
65
+
Both calls are sent in a **single HTTP request** using Cap'n Web RPC pipelining!
66
+
67
+
**Sequential Mode**:
68
+
```javascript
69
+
const profile = await api.getProfile(handle); // Request 1
70
+
const feed = await api.getFeed(handle, 10); // Request 2
71
+
```
72
+
73
+
Two separate HTTP requests, demonstrating the performance difference.
74
+
75
+
## AT Protocol / XRPC
76
+
77
+
This example integrates with Bluesky's AT Protocol using their public XRPC endpoints:
78
+
79
+
- `app.bsky.actor.getProfile` - Get user profile information
80
+
- `app.bsky.feed.getAuthorFeed` - Get a user's posts
81
+
82
+
The Go backend acts as a bridge, translating Cap'n Web RPC calls to XRPC HTTP requests.
83
+
84
+
## Try These Handles
85
+
86
+
- `bsky.app` - Official Bluesky account
87
+
- `jay.bsky.team` - Jay Graber (Bluesky CEO)
88
+
- `pfrazee.com` - Paul Frazee (Bluesky engineer)
89
+
90
+
## Testing with curl
91
+
92
+
Test the backend directly:
93
+
94
+
```bash
95
+
# Get profile
96
+
curl -X POST http://localhost:8000/rpc \
97
+
-d '["push",["pipeline",1,["getProfile"],["bsky.app"]]]'
98
+
99
+
curl -X POST http://localhost:8000/rpc \
100
+
-d '["pull",1]'
101
+
102
+
# Get feed
103
+
curl -X POST http://localhost:8000/rpc \
104
+
-d '["push",["pipeline",2,["getFeed"],["bsky.app", 5]]]'
105
+
106
+
curl -X POST http://localhost:8000/rpc \
107
+
-d '["pull",2]'
108
+
```
109
+
110
+
## Performance Benefits
111
+
112
+
Batched mode typically shows **40-60% faster** load times compared to sequential mode by:
113
+
114
+
1. Reducing network round trips (1 request vs 2)
115
+
2. Eliminating network latency between requests
116
+
3. Pipelining dependent operations efficiently
117
+
118
+
## Architecture
119
+
120
+
```
121
+
Browser (Svelte)
122
+
↓ Cap'n Web RPC (batched)
123
+
Go Server (localhost:8000)
124
+
↓ HTTPS
125
+
AT Protocol API (public.api.bsky.app)
126
+
↓ XRPC
127
+
Bluesky Network
128
+
```
129
+
130
+
## Error Handling
131
+
132
+
The example handles:
133
+
134
+
- Invalid or non-existent handles
135
+
- Network failures
136
+
- API rate limits
137
+
- Malformed responses
138
+
139
+
## Learn More
140
+
141
+
- [Cap'n Web RPC Spec](https://github.com/cloudflare/capnweb)
142
+
- [AT Protocol Docs](https://atproto.com)
143
+
- [Bluesky XRPC API](https://docs.bsky.app)
144
+
+24
examples/bluesky/go.mod
+24
examples/bluesky/go.mod
···
1
+
module bluesky
2
+
3
+
go 1.23.0
4
+
5
+
toolchain go1.24.2
6
+
7
+
require github.com/gocapnweb v0.0.0
8
+
9
+
require (
10
+
github.com/gorilla/websocket v1.5.0 // indirect
11
+
github.com/labstack/echo/v4 v4.13.4 // indirect
12
+
github.com/labstack/gommon v0.4.2 // indirect
13
+
github.com/mattn/go-colorable v0.1.14 // indirect
14
+
github.com/mattn/go-isatty v0.0.20 // indirect
15
+
github.com/valyala/bytebufferpool v1.0.0 // indirect
16
+
github.com/valyala/fasttemplate v1.2.2 // indirect
17
+
golang.org/x/crypto v0.38.0 // indirect
18
+
golang.org/x/net v0.40.0 // indirect
19
+
golang.org/x/sys v0.33.0 // indirect
20
+
golang.org/x/text v0.25.0 // indirect
21
+
golang.org/x/time v0.11.0 // indirect
22
+
)
23
+
24
+
replace github.com/gocapnweb => ../../
+33
examples/bluesky/go.sum
+33
examples/bluesky/go.sum
···
1
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3
+
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
4
+
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
5
+
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
6
+
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
7
+
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
8
+
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
9
+
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
10
+
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
11
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
12
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
13
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15
+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
16
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
17
+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
18
+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
19
+
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
20
+
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
21
+
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
22
+
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
23
+
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
24
+
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
25
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
26
+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
27
+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
28
+
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
29
+
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
30
+
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
31
+
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
32
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
33
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+322
examples/bluesky/main.go
+322
examples/bluesky/main.go
···
1
+
package main
2
+
3
+
import (
4
+
"encoding/json"
5
+
"fmt"
6
+
"io"
7
+
"log"
8
+
"net/http"
9
+
"net/url"
10
+
"os"
11
+
12
+
"github.com/gocapnweb"
13
+
)
14
+
15
+
const blueskyAPIBase = "https://public.api.bsky.app/xrpc"
16
+
17
+
// sanitizeJSON recursively removes or renames keys starting with "$" to avoid
18
+
// conflicts with Cap'n Web RPC's special value handling
19
+
func sanitizeJSON(data interface{}) interface{} {
20
+
switch v := data.(type) {
21
+
case map[string]interface{}:
22
+
result := make(map[string]interface{})
23
+
for key, val := range v {
24
+
// Rename keys starting with "$" to avoid Cap'n Web protocol conflicts
25
+
newKey := key
26
+
if len(key) > 0 && key[0] == '$' {
27
+
newKey = "_" + key[1:] // Replace $ with _ (e.g., $type -> _type)
28
+
}
29
+
result[newKey] = sanitizeJSON(val)
30
+
}
31
+
return result
32
+
case []interface{}:
33
+
result := make([]interface{}, len(v))
34
+
for i, item := range v {
35
+
result[i] = sanitizeJSON(item)
36
+
}
37
+
return result
38
+
default:
39
+
return v
40
+
}
41
+
}
42
+
43
+
// BlueskyProfile represents a Bluesky profile response
44
+
type BlueskyProfile struct {
45
+
DID string `json:"did"`
46
+
Handle string `json:"handle"`
47
+
DisplayName string `json:"displayName,omitempty"`
48
+
Description string `json:"description,omitempty"`
49
+
Avatar string `json:"avatar,omitempty"`
50
+
Banner string `json:"banner,omitempty"`
51
+
FollowersCount int `json:"followersCount"`
52
+
FollowsCount int `json:"followsCount"`
53
+
PostsCount int `json:"postsCount"`
54
+
}
55
+
56
+
// BlueskyPost represents a single post in a feed
57
+
type BlueskyPost struct {
58
+
URI string `json:"uri"`
59
+
CID string `json:"cid"`
60
+
Author BlueskyPostAuthor `json:"author"`
61
+
Record map[string]interface{} `json:"record"`
62
+
ReplyCount int `json:"replyCount,omitempty"`
63
+
RepostCount int `json:"repostCount,omitempty"`
64
+
LikeCount int `json:"likeCount,omitempty"`
65
+
IndexedAt string `json:"indexedAt"`
66
+
}
67
+
68
+
// BlueskyPostAuthor represents the author of a post
69
+
type BlueskyPostAuthor struct {
70
+
DID string `json:"did"`
71
+
Handle string `json:"handle"`
72
+
DisplayName string `json:"displayName,omitempty"`
73
+
Avatar string `json:"avatar,omitempty"`
74
+
}
75
+
76
+
// BlueskyFeedResponse represents the feed response structure
77
+
type BlueskyFeedResponse struct {
78
+
Feed []struct {
79
+
Post BlueskyPost `json:"post"`
80
+
} `json:"feed"`
81
+
Cursor string `json:"cursor,omitempty"`
82
+
}
83
+
84
+
// BlueskyServer implements RPC methods for fetching Bluesky data
85
+
type BlueskyServer struct {
86
+
*gocapnweb.BaseRpcTarget
87
+
httpClient *http.Client
88
+
}
89
+
90
+
// NewBlueskyServer creates a new BlueskyServer instance
91
+
func NewBlueskyServer() *BlueskyServer {
92
+
server := &BlueskyServer{
93
+
BaseRpcTarget: gocapnweb.NewBaseRpcTarget(),
94
+
httpClient: &http.Client{},
95
+
}
96
+
97
+
// Register RPC methods
98
+
server.Method("getProfile", server.getProfile)
99
+
server.Method("getFeed", server.getFeed)
100
+
101
+
return server
102
+
}
103
+
104
+
// getProfile fetches a Bluesky profile by handle
105
+
func (s *BlueskyServer) getProfile(args json.RawMessage) (interface{}, error) {
106
+
// Extract handle from arguments
107
+
var handle string
108
+
109
+
// Try to parse as array first
110
+
var argArray []string
111
+
if err := json.Unmarshal(args, &argArray); err == nil && len(argArray) > 0 {
112
+
handle = argArray[0]
113
+
} else {
114
+
// Try to parse as string
115
+
if err := json.Unmarshal(args, &handle); err != nil {
116
+
return nil, fmt.Errorf("invalid arguments: expected handle")
117
+
}
118
+
}
119
+
120
+
if handle == "" {
121
+
return nil, fmt.Errorf("handle is required")
122
+
}
123
+
124
+
// Build API URL
125
+
apiURL := fmt.Sprintf("%s/app.bsky.actor.getProfile?actor=%s", blueskyAPIBase, url.QueryEscape(handle))
126
+
127
+
log.Printf("Fetching profile for handle: %s", handle)
128
+
129
+
// Make API request
130
+
resp, err := s.httpClient.Get(apiURL)
131
+
if err != nil {
132
+
return nil, fmt.Errorf("failed to fetch profile: %w", err)
133
+
}
134
+
defer resp.Body.Close()
135
+
136
+
if resp.StatusCode != http.StatusOK {
137
+
body, _ := io.ReadAll(resp.Body)
138
+
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
139
+
}
140
+
141
+
// Parse response
142
+
body, err := io.ReadAll(resp.Body)
143
+
if err != nil {
144
+
return nil, fmt.Errorf("failed to read response: %w", err)
145
+
}
146
+
147
+
var profile BlueskyProfile
148
+
if err := json.Unmarshal(body, &profile); err != nil {
149
+
return nil, fmt.Errorf("failed to parse profile: %w", err)
150
+
}
151
+
152
+
log.Printf("Successfully fetched profile for %s (DID: %s)", profile.Handle, profile.DID)
153
+
154
+
return profile, nil
155
+
}
156
+
157
+
// getFeed fetches a user's feed by handle
158
+
func (s *BlueskyServer) getFeed(args json.RawMessage) (interface{}, error) {
159
+
// Extract arguments
160
+
var argArray []interface{}
161
+
if err := json.Unmarshal(args, &argArray); err != nil {
162
+
return nil, fmt.Errorf("invalid arguments: expected [handle, limit]")
163
+
}
164
+
165
+
if len(argArray) == 0 {
166
+
return nil, fmt.Errorf("handle is required")
167
+
}
168
+
169
+
handle, ok := argArray[0].(string)
170
+
if !ok {
171
+
return nil, fmt.Errorf("handle must be a string")
172
+
}
173
+
174
+
limit := 10
175
+
if len(argArray) > 1 {
176
+
if limitFloat, ok := argArray[1].(float64); ok {
177
+
limit = int(limitFloat)
178
+
}
179
+
}
180
+
181
+
// Build API URL
182
+
apiURL := fmt.Sprintf("%s/app.bsky.feed.getAuthorFeed?actor=%s&limit=%d",
183
+
blueskyAPIBase, url.QueryEscape(handle), limit)
184
+
185
+
log.Printf("Fetching feed for handle: %s (limit: %d)", handle, limit)
186
+
187
+
// Make API request
188
+
resp, err := s.httpClient.Get(apiURL)
189
+
if err != nil {
190
+
return nil, fmt.Errorf("failed to fetch feed: %w", err)
191
+
}
192
+
defer resp.Body.Close()
193
+
194
+
if resp.StatusCode != http.StatusOK {
195
+
body, _ := io.ReadAll(resp.Body)
196
+
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
197
+
}
198
+
199
+
// Parse response
200
+
body, err := io.ReadAll(resp.Body)
201
+
if err != nil {
202
+
return nil, fmt.Errorf("failed to read response: %w", err)
203
+
}
204
+
205
+
// Parse as generic JSON first, then sanitize to avoid $ key conflicts
206
+
var rawResponse map[string]interface{}
207
+
if err := json.Unmarshal(body, &rawResponse); err != nil {
208
+
return nil, fmt.Errorf("failed to parse feed: %w", err)
209
+
}
210
+
211
+
// Sanitize the entire response
212
+
sanitized := sanitizeJSON(rawResponse).(map[string]interface{})
213
+
214
+
// Extract feed array
215
+
feedArray, ok := sanitized["feed"].([]interface{})
216
+
if !ok {
217
+
return nil, fmt.Errorf("unexpected feed response format")
218
+
}
219
+
220
+
// Extract and simplify posts - only include fields we actually display
221
+
// Use []interface{} instead of []map[string]interface{} for Cap'n Web compatibility
222
+
posts := make([]interface{}, 0, len(feedArray))
223
+
for _, item := range feedArray {
224
+
if itemMap, ok := item.(map[string]interface{}); ok {
225
+
if postData, ok := itemMap["post"].(map[string]interface{}); ok {
226
+
// Extract only the fields we need for display
227
+
simplifiedPost := map[string]interface{}{
228
+
"uri": postData["uri"],
229
+
"cid": postData["cid"],
230
+
"indexedAt": postData["indexedAt"],
231
+
"replyCount": getIntOrZero(postData, "replyCount"),
232
+
"repostCount": getIntOrZero(postData, "repostCount"),
233
+
"likeCount": getIntOrZero(postData, "likeCount"),
234
+
}
235
+
236
+
// Extract author info
237
+
if author, ok := postData["author"].(map[string]interface{}); ok {
238
+
simplifiedPost["author"] = map[string]interface{}{
239
+
"did": author["did"],
240
+
"handle": author["handle"],
241
+
"displayName": author["displayName"],
242
+
"avatar": author["avatar"],
243
+
}
244
+
}
245
+
246
+
// Extract record.text
247
+
if record, ok := postData["record"].(map[string]interface{}); ok {
248
+
simplifiedPost["record"] = map[string]interface{}{
249
+
"text": record["text"],
250
+
}
251
+
}
252
+
253
+
posts = append(posts, simplifiedPost)
254
+
}
255
+
}
256
+
}
257
+
258
+
log.Printf("Successfully fetched %d posts for %s", len(posts), handle)
259
+
260
+
cursor := ""
261
+
if c, ok := sanitized["cursor"].(string); ok {
262
+
cursor = c
263
+
}
264
+
265
+
// Wrap the posts array in another array to escape it for Cap'n Web
266
+
result := map[string]interface{}{
267
+
"posts": []interface{}{posts}, // Double-wrap: [[{...}]]
268
+
"cursor": cursor,
269
+
}
270
+
271
+
return result, nil
272
+
}
273
+
274
+
// Helper to safely extract int values with zero default
275
+
func getIntOrZero(m map[string]interface{}, key string) int {
276
+
if val, ok := m[key]; ok {
277
+
if floatVal, ok := val.(float64); ok {
278
+
return int(floatVal)
279
+
}
280
+
if intVal, ok := val.(int); ok {
281
+
return intVal
282
+
}
283
+
}
284
+
return 0
285
+
}
286
+
287
+
func main() {
288
+
// Default to serving static files from the examples/static directory
289
+
staticPath := "/static"
290
+
if len(os.Args) >= 2 {
291
+
staticPath = os.Args[1]
292
+
}
293
+
294
+
port := ":8000"
295
+
296
+
// Create Echo server with middleware
297
+
e := gocapnweb.SetupEchoServer()
298
+
299
+
// Setup RPC endpoint
300
+
server := NewBlueskyServer()
301
+
gocapnweb.SetupRpcEndpoint(e, "/rpc", server)
302
+
303
+
// Setup static file endpoint
304
+
gocapnweb.SetupFileEndpoint(e, "/static", staticPath)
305
+
306
+
log.Printf("🚀 Bluesky Feed Reader Go Server starting on port %s", port)
307
+
log.Printf("🔌 HTTP Batch RPC endpoint: http://localhost%s/rpc", port)
308
+
log.Printf("🌐 Demo URL: http://localhost:3000 (available once you start the Svelte development server)")
309
+
log.Println()
310
+
log.Println("Features:")
311
+
log.Println(" 🦋 Fetch Bluesky profiles and feeds")
312
+
log.Println(" ⚡ Batch pipelining for optimal performance")
313
+
log.Println(" 🔗 AT Protocol/XRPC integration")
314
+
log.Println()
315
+
log.Println("Try it with curl:")
316
+
log.Printf(" curl -X POST http://localhost%s/rpc -d '[\"push\",[\"pipeline\",1,[\"getProfile\"],[\"bsky.app\"]]]'", port)
317
+
log.Printf(" curl -X POST http://localhost%s/rpc -d '[\"pull\",1]'", port)
318
+
319
+
if err := e.Start(port); err != nil {
320
+
log.Fatal("Failed to start server:", err)
321
+
}
322
+
}
+13
examples/bluesky/static/index.html
+13
examples/bluesky/static/index.html
···
1
+
<!DOCTYPE html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+
<title>Bluesky Feed Reader - Cap'n Web RPC</title>
7
+
</head>
8
+
<body>
9
+
<div id="app"></div>
10
+
<script type="module" src="/src/main.js"></script>
11
+
</body>
12
+
</html>
13
+
+1238
examples/bluesky/static/package-lock.json
+1238
examples/bluesky/static/package-lock.json
···
1
+
{
2
+
"name": "bluesky-feed-reader",
3
+
"version": "1.0.0",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "bluesky-feed-reader",
9
+
"version": "1.0.0",
10
+
"dependencies": {
11
+
"capnweb": "latest"
12
+
},
13
+
"devDependencies": {
14
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
15
+
"svelte": "^5.0.0",
16
+
"vite": "^5.4.4"
17
+
}
18
+
},
19
+
"node_modules/@esbuild/aix-ppc64": {
20
+
"version": "0.21.5",
21
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
22
+
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
23
+
"cpu": [
24
+
"ppc64"
25
+
],
26
+
"dev": true,
27
+
"license": "MIT",
28
+
"optional": true,
29
+
"os": [
30
+
"aix"
31
+
],
32
+
"engines": {
33
+
"node": ">=12"
34
+
}
35
+
},
36
+
"node_modules/@esbuild/android-arm": {
37
+
"version": "0.21.5",
38
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
39
+
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
40
+
"cpu": [
41
+
"arm"
42
+
],
43
+
"dev": true,
44
+
"license": "MIT",
45
+
"optional": true,
46
+
"os": [
47
+
"android"
48
+
],
49
+
"engines": {
50
+
"node": ">=12"
51
+
}
52
+
},
53
+
"node_modules/@esbuild/android-arm64": {
54
+
"version": "0.21.5",
55
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
56
+
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
57
+
"cpu": [
58
+
"arm64"
59
+
],
60
+
"dev": true,
61
+
"license": "MIT",
62
+
"optional": true,
63
+
"os": [
64
+
"android"
65
+
],
66
+
"engines": {
67
+
"node": ">=12"
68
+
}
69
+
},
70
+
"node_modules/@esbuild/android-x64": {
71
+
"version": "0.21.5",
72
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
73
+
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
74
+
"cpu": [
75
+
"x64"
76
+
],
77
+
"dev": true,
78
+
"license": "MIT",
79
+
"optional": true,
80
+
"os": [
81
+
"android"
82
+
],
83
+
"engines": {
84
+
"node": ">=12"
85
+
}
86
+
},
87
+
"node_modules/@esbuild/darwin-arm64": {
88
+
"version": "0.21.5",
89
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
90
+
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
91
+
"cpu": [
92
+
"arm64"
93
+
],
94
+
"dev": true,
95
+
"license": "MIT",
96
+
"optional": true,
97
+
"os": [
98
+
"darwin"
99
+
],
100
+
"engines": {
101
+
"node": ">=12"
102
+
}
103
+
},
104
+
"node_modules/@esbuild/darwin-x64": {
105
+
"version": "0.21.5",
106
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
107
+
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
108
+
"cpu": [
109
+
"x64"
110
+
],
111
+
"dev": true,
112
+
"license": "MIT",
113
+
"optional": true,
114
+
"os": [
115
+
"darwin"
116
+
],
117
+
"engines": {
118
+
"node": ">=12"
119
+
}
120
+
},
121
+
"node_modules/@esbuild/freebsd-arm64": {
122
+
"version": "0.21.5",
123
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
124
+
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
125
+
"cpu": [
126
+
"arm64"
127
+
],
128
+
"dev": true,
129
+
"license": "MIT",
130
+
"optional": true,
131
+
"os": [
132
+
"freebsd"
133
+
],
134
+
"engines": {
135
+
"node": ">=12"
136
+
}
137
+
},
138
+
"node_modules/@esbuild/freebsd-x64": {
139
+
"version": "0.21.5",
140
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
141
+
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
142
+
"cpu": [
143
+
"x64"
144
+
],
145
+
"dev": true,
146
+
"license": "MIT",
147
+
"optional": true,
148
+
"os": [
149
+
"freebsd"
150
+
],
151
+
"engines": {
152
+
"node": ">=12"
153
+
}
154
+
},
155
+
"node_modules/@esbuild/linux-arm": {
156
+
"version": "0.21.5",
157
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
158
+
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
159
+
"cpu": [
160
+
"arm"
161
+
],
162
+
"dev": true,
163
+
"license": "MIT",
164
+
"optional": true,
165
+
"os": [
166
+
"linux"
167
+
],
168
+
"engines": {
169
+
"node": ">=12"
170
+
}
171
+
},
172
+
"node_modules/@esbuild/linux-arm64": {
173
+
"version": "0.21.5",
174
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
175
+
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
176
+
"cpu": [
177
+
"arm64"
178
+
],
179
+
"dev": true,
180
+
"license": "MIT",
181
+
"optional": true,
182
+
"os": [
183
+
"linux"
184
+
],
185
+
"engines": {
186
+
"node": ">=12"
187
+
}
188
+
},
189
+
"node_modules/@esbuild/linux-ia32": {
190
+
"version": "0.21.5",
191
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
192
+
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
193
+
"cpu": [
194
+
"ia32"
195
+
],
196
+
"dev": true,
197
+
"license": "MIT",
198
+
"optional": true,
199
+
"os": [
200
+
"linux"
201
+
],
202
+
"engines": {
203
+
"node": ">=12"
204
+
}
205
+
},
206
+
"node_modules/@esbuild/linux-loong64": {
207
+
"version": "0.21.5",
208
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
209
+
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
210
+
"cpu": [
211
+
"loong64"
212
+
],
213
+
"dev": true,
214
+
"license": "MIT",
215
+
"optional": true,
216
+
"os": [
217
+
"linux"
218
+
],
219
+
"engines": {
220
+
"node": ">=12"
221
+
}
222
+
},
223
+
"node_modules/@esbuild/linux-mips64el": {
224
+
"version": "0.21.5",
225
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
226
+
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
227
+
"cpu": [
228
+
"mips64el"
229
+
],
230
+
"dev": true,
231
+
"license": "MIT",
232
+
"optional": true,
233
+
"os": [
234
+
"linux"
235
+
],
236
+
"engines": {
237
+
"node": ">=12"
238
+
}
239
+
},
240
+
"node_modules/@esbuild/linux-ppc64": {
241
+
"version": "0.21.5",
242
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
243
+
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
244
+
"cpu": [
245
+
"ppc64"
246
+
],
247
+
"dev": true,
248
+
"license": "MIT",
249
+
"optional": true,
250
+
"os": [
251
+
"linux"
252
+
],
253
+
"engines": {
254
+
"node": ">=12"
255
+
}
256
+
},
257
+
"node_modules/@esbuild/linux-riscv64": {
258
+
"version": "0.21.5",
259
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
260
+
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
261
+
"cpu": [
262
+
"riscv64"
263
+
],
264
+
"dev": true,
265
+
"license": "MIT",
266
+
"optional": true,
267
+
"os": [
268
+
"linux"
269
+
],
270
+
"engines": {
271
+
"node": ">=12"
272
+
}
273
+
},
274
+
"node_modules/@esbuild/linux-s390x": {
275
+
"version": "0.21.5",
276
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
277
+
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
278
+
"cpu": [
279
+
"s390x"
280
+
],
281
+
"dev": true,
282
+
"license": "MIT",
283
+
"optional": true,
284
+
"os": [
285
+
"linux"
286
+
],
287
+
"engines": {
288
+
"node": ">=12"
289
+
}
290
+
},
291
+
"node_modules/@esbuild/linux-x64": {
292
+
"version": "0.21.5",
293
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
294
+
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
295
+
"cpu": [
296
+
"x64"
297
+
],
298
+
"dev": true,
299
+
"license": "MIT",
300
+
"optional": true,
301
+
"os": [
302
+
"linux"
303
+
],
304
+
"engines": {
305
+
"node": ">=12"
306
+
}
307
+
},
308
+
"node_modules/@esbuild/netbsd-x64": {
309
+
"version": "0.21.5",
310
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
311
+
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
312
+
"cpu": [
313
+
"x64"
314
+
],
315
+
"dev": true,
316
+
"license": "MIT",
317
+
"optional": true,
318
+
"os": [
319
+
"netbsd"
320
+
],
321
+
"engines": {
322
+
"node": ">=12"
323
+
}
324
+
},
325
+
"node_modules/@esbuild/openbsd-x64": {
326
+
"version": "0.21.5",
327
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
328
+
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
329
+
"cpu": [
330
+
"x64"
331
+
],
332
+
"dev": true,
333
+
"license": "MIT",
334
+
"optional": true,
335
+
"os": [
336
+
"openbsd"
337
+
],
338
+
"engines": {
339
+
"node": ">=12"
340
+
}
341
+
},
342
+
"node_modules/@esbuild/sunos-x64": {
343
+
"version": "0.21.5",
344
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
345
+
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
346
+
"cpu": [
347
+
"x64"
348
+
],
349
+
"dev": true,
350
+
"license": "MIT",
351
+
"optional": true,
352
+
"os": [
353
+
"sunos"
354
+
],
355
+
"engines": {
356
+
"node": ">=12"
357
+
}
358
+
},
359
+
"node_modules/@esbuild/win32-arm64": {
360
+
"version": "0.21.5",
361
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
362
+
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
363
+
"cpu": [
364
+
"arm64"
365
+
],
366
+
"dev": true,
367
+
"license": "MIT",
368
+
"optional": true,
369
+
"os": [
370
+
"win32"
371
+
],
372
+
"engines": {
373
+
"node": ">=12"
374
+
}
375
+
},
376
+
"node_modules/@esbuild/win32-ia32": {
377
+
"version": "0.21.5",
378
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
379
+
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
380
+
"cpu": [
381
+
"ia32"
382
+
],
383
+
"dev": true,
384
+
"license": "MIT",
385
+
"optional": true,
386
+
"os": [
387
+
"win32"
388
+
],
389
+
"engines": {
390
+
"node": ">=12"
391
+
}
392
+
},
393
+
"node_modules/@esbuild/win32-x64": {
394
+
"version": "0.21.5",
395
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
396
+
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
397
+
"cpu": [
398
+
"x64"
399
+
],
400
+
"dev": true,
401
+
"license": "MIT",
402
+
"optional": true,
403
+
"os": [
404
+
"win32"
405
+
],
406
+
"engines": {
407
+
"node": ">=12"
408
+
}
409
+
},
410
+
"node_modules/@jridgewell/gen-mapping": {
411
+
"version": "0.3.13",
412
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
413
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
414
+
"dev": true,
415
+
"license": "MIT",
416
+
"dependencies": {
417
+
"@jridgewell/sourcemap-codec": "^1.5.0",
418
+
"@jridgewell/trace-mapping": "^0.3.24"
419
+
}
420
+
},
421
+
"node_modules/@jridgewell/remapping": {
422
+
"version": "2.3.5",
423
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
424
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
425
+
"dev": true,
426
+
"license": "MIT",
427
+
"dependencies": {
428
+
"@jridgewell/gen-mapping": "^0.3.5",
429
+
"@jridgewell/trace-mapping": "^0.3.24"
430
+
}
431
+
},
432
+
"node_modules/@jridgewell/resolve-uri": {
433
+
"version": "3.1.2",
434
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
435
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
436
+
"dev": true,
437
+
"license": "MIT",
438
+
"engines": {
439
+
"node": ">=6.0.0"
440
+
}
441
+
},
442
+
"node_modules/@jridgewell/sourcemap-codec": {
443
+
"version": "1.5.5",
444
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
445
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
446
+
"dev": true,
447
+
"license": "MIT"
448
+
},
449
+
"node_modules/@jridgewell/trace-mapping": {
450
+
"version": "0.3.31",
451
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
452
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
453
+
"dev": true,
454
+
"license": "MIT",
455
+
"dependencies": {
456
+
"@jridgewell/resolve-uri": "^3.1.0",
457
+
"@jridgewell/sourcemap-codec": "^1.4.14"
458
+
}
459
+
},
460
+
"node_modules/@rollup/rollup-android-arm-eabi": {
461
+
"version": "4.52.5",
462
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
463
+
"integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
464
+
"cpu": [
465
+
"arm"
466
+
],
467
+
"dev": true,
468
+
"license": "MIT",
469
+
"optional": true,
470
+
"os": [
471
+
"android"
472
+
]
473
+
},
474
+
"node_modules/@rollup/rollup-android-arm64": {
475
+
"version": "4.52.5",
476
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
477
+
"integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
478
+
"cpu": [
479
+
"arm64"
480
+
],
481
+
"dev": true,
482
+
"license": "MIT",
483
+
"optional": true,
484
+
"os": [
485
+
"android"
486
+
]
487
+
},
488
+
"node_modules/@rollup/rollup-darwin-arm64": {
489
+
"version": "4.52.5",
490
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
491
+
"integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
492
+
"cpu": [
493
+
"arm64"
494
+
],
495
+
"dev": true,
496
+
"license": "MIT",
497
+
"optional": true,
498
+
"os": [
499
+
"darwin"
500
+
]
501
+
},
502
+
"node_modules/@rollup/rollup-darwin-x64": {
503
+
"version": "4.52.5",
504
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
505
+
"integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
506
+
"cpu": [
507
+
"x64"
508
+
],
509
+
"dev": true,
510
+
"license": "MIT",
511
+
"optional": true,
512
+
"os": [
513
+
"darwin"
514
+
]
515
+
},
516
+
"node_modules/@rollup/rollup-freebsd-arm64": {
517
+
"version": "4.52.5",
518
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
519
+
"integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
520
+
"cpu": [
521
+
"arm64"
522
+
],
523
+
"dev": true,
524
+
"license": "MIT",
525
+
"optional": true,
526
+
"os": [
527
+
"freebsd"
528
+
]
529
+
},
530
+
"node_modules/@rollup/rollup-freebsd-x64": {
531
+
"version": "4.52.5",
532
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
533
+
"integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
534
+
"cpu": [
535
+
"x64"
536
+
],
537
+
"dev": true,
538
+
"license": "MIT",
539
+
"optional": true,
540
+
"os": [
541
+
"freebsd"
542
+
]
543
+
},
544
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
545
+
"version": "4.52.5",
546
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
547
+
"integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
548
+
"cpu": [
549
+
"arm"
550
+
],
551
+
"dev": true,
552
+
"license": "MIT",
553
+
"optional": true,
554
+
"os": [
555
+
"linux"
556
+
]
557
+
},
558
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
559
+
"version": "4.52.5",
560
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
561
+
"integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
562
+
"cpu": [
563
+
"arm"
564
+
],
565
+
"dev": true,
566
+
"license": "MIT",
567
+
"optional": true,
568
+
"os": [
569
+
"linux"
570
+
]
571
+
},
572
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
573
+
"version": "4.52.5",
574
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
575
+
"integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
576
+
"cpu": [
577
+
"arm64"
578
+
],
579
+
"dev": true,
580
+
"license": "MIT",
581
+
"optional": true,
582
+
"os": [
583
+
"linux"
584
+
]
585
+
},
586
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
587
+
"version": "4.52.5",
588
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
589
+
"integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
590
+
"cpu": [
591
+
"arm64"
592
+
],
593
+
"dev": true,
594
+
"license": "MIT",
595
+
"optional": true,
596
+
"os": [
597
+
"linux"
598
+
]
599
+
},
600
+
"node_modules/@rollup/rollup-linux-loong64-gnu": {
601
+
"version": "4.52.5",
602
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
603
+
"integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
604
+
"cpu": [
605
+
"loong64"
606
+
],
607
+
"dev": true,
608
+
"license": "MIT",
609
+
"optional": true,
610
+
"os": [
611
+
"linux"
612
+
]
613
+
},
614
+
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
615
+
"version": "4.52.5",
616
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
617
+
"integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
618
+
"cpu": [
619
+
"ppc64"
620
+
],
621
+
"dev": true,
622
+
"license": "MIT",
623
+
"optional": true,
624
+
"os": [
625
+
"linux"
626
+
]
627
+
},
628
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
629
+
"version": "4.52.5",
630
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
631
+
"integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
632
+
"cpu": [
633
+
"riscv64"
634
+
],
635
+
"dev": true,
636
+
"license": "MIT",
637
+
"optional": true,
638
+
"os": [
639
+
"linux"
640
+
]
641
+
},
642
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
643
+
"version": "4.52.5",
644
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
645
+
"integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
646
+
"cpu": [
647
+
"riscv64"
648
+
],
649
+
"dev": true,
650
+
"license": "MIT",
651
+
"optional": true,
652
+
"os": [
653
+
"linux"
654
+
]
655
+
},
656
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
657
+
"version": "4.52.5",
658
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
659
+
"integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
660
+
"cpu": [
661
+
"s390x"
662
+
],
663
+
"dev": true,
664
+
"license": "MIT",
665
+
"optional": true,
666
+
"os": [
667
+
"linux"
668
+
]
669
+
},
670
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
671
+
"version": "4.52.5",
672
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
673
+
"integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
674
+
"cpu": [
675
+
"x64"
676
+
],
677
+
"dev": true,
678
+
"license": "MIT",
679
+
"optional": true,
680
+
"os": [
681
+
"linux"
682
+
]
683
+
},
684
+
"node_modules/@rollup/rollup-linux-x64-musl": {
685
+
"version": "4.52.5",
686
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
687
+
"integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
688
+
"cpu": [
689
+
"x64"
690
+
],
691
+
"dev": true,
692
+
"license": "MIT",
693
+
"optional": true,
694
+
"os": [
695
+
"linux"
696
+
]
697
+
},
698
+
"node_modules/@rollup/rollup-openharmony-arm64": {
699
+
"version": "4.52.5",
700
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
701
+
"integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
702
+
"cpu": [
703
+
"arm64"
704
+
],
705
+
"dev": true,
706
+
"license": "MIT",
707
+
"optional": true,
708
+
"os": [
709
+
"openharmony"
710
+
]
711
+
},
712
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
713
+
"version": "4.52.5",
714
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
715
+
"integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
716
+
"cpu": [
717
+
"arm64"
718
+
],
719
+
"dev": true,
720
+
"license": "MIT",
721
+
"optional": true,
722
+
"os": [
723
+
"win32"
724
+
]
725
+
},
726
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
727
+
"version": "4.52.5",
728
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
729
+
"integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
730
+
"cpu": [
731
+
"ia32"
732
+
],
733
+
"dev": true,
734
+
"license": "MIT",
735
+
"optional": true,
736
+
"os": [
737
+
"win32"
738
+
]
739
+
},
740
+
"node_modules/@rollup/rollup-win32-x64-gnu": {
741
+
"version": "4.52.5",
742
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
743
+
"integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
744
+
"cpu": [
745
+
"x64"
746
+
],
747
+
"dev": true,
748
+
"license": "MIT",
749
+
"optional": true,
750
+
"os": [
751
+
"win32"
752
+
]
753
+
},
754
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
755
+
"version": "4.52.5",
756
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
757
+
"integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
758
+
"cpu": [
759
+
"x64"
760
+
],
761
+
"dev": true,
762
+
"license": "MIT",
763
+
"optional": true,
764
+
"os": [
765
+
"win32"
766
+
]
767
+
},
768
+
"node_modules/@sveltejs/acorn-typescript": {
769
+
"version": "1.0.6",
770
+
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz",
771
+
"integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==",
772
+
"dev": true,
773
+
"license": "MIT",
774
+
"peerDependencies": {
775
+
"acorn": "^8.9.0"
776
+
}
777
+
},
778
+
"node_modules/@sveltejs/vite-plugin-svelte": {
779
+
"version": "4.0.4",
780
+
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz",
781
+
"integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==",
782
+
"dev": true,
783
+
"license": "MIT",
784
+
"dependencies": {
785
+
"@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
786
+
"debug": "^4.3.7",
787
+
"deepmerge": "^4.3.1",
788
+
"kleur": "^4.1.5",
789
+
"magic-string": "^0.30.12",
790
+
"vitefu": "^1.0.3"
791
+
},
792
+
"engines": {
793
+
"node": "^18.0.0 || ^20.0.0 || >=22"
794
+
},
795
+
"peerDependencies": {
796
+
"svelte": "^5.0.0-next.96 || ^5.0.0",
797
+
"vite": "^5.0.0"
798
+
}
799
+
},
800
+
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
801
+
"version": "3.0.1",
802
+
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz",
803
+
"integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==",
804
+
"dev": true,
805
+
"license": "MIT",
806
+
"dependencies": {
807
+
"debug": "^4.3.7"
808
+
},
809
+
"engines": {
810
+
"node": "^18.0.0 || ^20.0.0 || >=22"
811
+
},
812
+
"peerDependencies": {
813
+
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
814
+
"svelte": "^5.0.0-next.96 || ^5.0.0",
815
+
"vite": "^5.0.0"
816
+
}
817
+
},
818
+
"node_modules/@types/estree": {
819
+
"version": "1.0.8",
820
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
821
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
822
+
"dev": true,
823
+
"license": "MIT"
824
+
},
825
+
"node_modules/acorn": {
826
+
"version": "8.15.0",
827
+
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
828
+
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
829
+
"dev": true,
830
+
"license": "MIT",
831
+
"bin": {
832
+
"acorn": "bin/acorn"
833
+
},
834
+
"engines": {
835
+
"node": ">=0.4.0"
836
+
}
837
+
},
838
+
"node_modules/aria-query": {
839
+
"version": "5.3.2",
840
+
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
841
+
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
842
+
"dev": true,
843
+
"license": "Apache-2.0",
844
+
"engines": {
845
+
"node": ">= 0.4"
846
+
}
847
+
},
848
+
"node_modules/axobject-query": {
849
+
"version": "4.1.0",
850
+
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
851
+
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
852
+
"dev": true,
853
+
"license": "Apache-2.0",
854
+
"engines": {
855
+
"node": ">= 0.4"
856
+
}
857
+
},
858
+
"node_modules/capnweb": {
859
+
"version": "0.2.0",
860
+
"resolved": "https://registry.npmjs.org/capnweb/-/capnweb-0.2.0.tgz",
861
+
"integrity": "sha512-fQSW5h6HIefRM4rHZMyAsWcu/qE/6Qr2OC8B99whifjDJatI5KLFcODSykKmpyCCKF50N3HvZ5lB26YBdh0fRg==",
862
+
"license": "MIT"
863
+
},
864
+
"node_modules/clsx": {
865
+
"version": "2.1.1",
866
+
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
867
+
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
868
+
"dev": true,
869
+
"license": "MIT",
870
+
"engines": {
871
+
"node": ">=6"
872
+
}
873
+
},
874
+
"node_modules/debug": {
875
+
"version": "4.4.3",
876
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
877
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
878
+
"dev": true,
879
+
"license": "MIT",
880
+
"dependencies": {
881
+
"ms": "^2.1.3"
882
+
},
883
+
"engines": {
884
+
"node": ">=6.0"
885
+
},
886
+
"peerDependenciesMeta": {
887
+
"supports-color": {
888
+
"optional": true
889
+
}
890
+
}
891
+
},
892
+
"node_modules/deepmerge": {
893
+
"version": "4.3.1",
894
+
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
895
+
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
896
+
"dev": true,
897
+
"license": "MIT",
898
+
"engines": {
899
+
"node": ">=0.10.0"
900
+
}
901
+
},
902
+
"node_modules/esbuild": {
903
+
"version": "0.21.5",
904
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
905
+
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
906
+
"dev": true,
907
+
"hasInstallScript": true,
908
+
"license": "MIT",
909
+
"bin": {
910
+
"esbuild": "bin/esbuild"
911
+
},
912
+
"engines": {
913
+
"node": ">=12"
914
+
},
915
+
"optionalDependencies": {
916
+
"@esbuild/aix-ppc64": "0.21.5",
917
+
"@esbuild/android-arm": "0.21.5",
918
+
"@esbuild/android-arm64": "0.21.5",
919
+
"@esbuild/android-x64": "0.21.5",
920
+
"@esbuild/darwin-arm64": "0.21.5",
921
+
"@esbuild/darwin-x64": "0.21.5",
922
+
"@esbuild/freebsd-arm64": "0.21.5",
923
+
"@esbuild/freebsd-x64": "0.21.5",
924
+
"@esbuild/linux-arm": "0.21.5",
925
+
"@esbuild/linux-arm64": "0.21.5",
926
+
"@esbuild/linux-ia32": "0.21.5",
927
+
"@esbuild/linux-loong64": "0.21.5",
928
+
"@esbuild/linux-mips64el": "0.21.5",
929
+
"@esbuild/linux-ppc64": "0.21.5",
930
+
"@esbuild/linux-riscv64": "0.21.5",
931
+
"@esbuild/linux-s390x": "0.21.5",
932
+
"@esbuild/linux-x64": "0.21.5",
933
+
"@esbuild/netbsd-x64": "0.21.5",
934
+
"@esbuild/openbsd-x64": "0.21.5",
935
+
"@esbuild/sunos-x64": "0.21.5",
936
+
"@esbuild/win32-arm64": "0.21.5",
937
+
"@esbuild/win32-ia32": "0.21.5",
938
+
"@esbuild/win32-x64": "0.21.5"
939
+
}
940
+
},
941
+
"node_modules/esm-env": {
942
+
"version": "1.2.2",
943
+
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
944
+
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
945
+
"dev": true,
946
+
"license": "MIT"
947
+
},
948
+
"node_modules/esrap": {
949
+
"version": "2.1.2",
950
+
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz",
951
+
"integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==",
952
+
"dev": true,
953
+
"license": "MIT",
954
+
"dependencies": {
955
+
"@jridgewell/sourcemap-codec": "^1.4.15"
956
+
}
957
+
},
958
+
"node_modules/fsevents": {
959
+
"version": "2.3.3",
960
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
961
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
962
+
"dev": true,
963
+
"hasInstallScript": true,
964
+
"license": "MIT",
965
+
"optional": true,
966
+
"os": [
967
+
"darwin"
968
+
],
969
+
"engines": {
970
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
971
+
}
972
+
},
973
+
"node_modules/is-reference": {
974
+
"version": "3.0.3",
975
+
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
976
+
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
977
+
"dev": true,
978
+
"license": "MIT",
979
+
"dependencies": {
980
+
"@types/estree": "^1.0.6"
981
+
}
982
+
},
983
+
"node_modules/kleur": {
984
+
"version": "4.1.5",
985
+
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
986
+
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
987
+
"dev": true,
988
+
"license": "MIT",
989
+
"engines": {
990
+
"node": ">=6"
991
+
}
992
+
},
993
+
"node_modules/locate-character": {
994
+
"version": "3.0.0",
995
+
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
996
+
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
997
+
"dev": true,
998
+
"license": "MIT"
999
+
},
1000
+
"node_modules/magic-string": {
1001
+
"version": "0.30.21",
1002
+
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
1003
+
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
1004
+
"dev": true,
1005
+
"license": "MIT",
1006
+
"dependencies": {
1007
+
"@jridgewell/sourcemap-codec": "^1.5.5"
1008
+
}
1009
+
},
1010
+
"node_modules/ms": {
1011
+
"version": "2.1.3",
1012
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1013
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1014
+
"dev": true,
1015
+
"license": "MIT"
1016
+
},
1017
+
"node_modules/nanoid": {
1018
+
"version": "3.3.11",
1019
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1020
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1021
+
"dev": true,
1022
+
"funding": [
1023
+
{
1024
+
"type": "github",
1025
+
"url": "https://github.com/sponsors/ai"
1026
+
}
1027
+
],
1028
+
"license": "MIT",
1029
+
"bin": {
1030
+
"nanoid": "bin/nanoid.cjs"
1031
+
},
1032
+
"engines": {
1033
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1034
+
}
1035
+
},
1036
+
"node_modules/picocolors": {
1037
+
"version": "1.1.1",
1038
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1039
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1040
+
"dev": true,
1041
+
"license": "ISC"
1042
+
},
1043
+
"node_modules/postcss": {
1044
+
"version": "8.5.6",
1045
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1046
+
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1047
+
"dev": true,
1048
+
"funding": [
1049
+
{
1050
+
"type": "opencollective",
1051
+
"url": "https://opencollective.com/postcss/"
1052
+
},
1053
+
{
1054
+
"type": "tidelift",
1055
+
"url": "https://tidelift.com/funding/github/npm/postcss"
1056
+
},
1057
+
{
1058
+
"type": "github",
1059
+
"url": "https://github.com/sponsors/ai"
1060
+
}
1061
+
],
1062
+
"license": "MIT",
1063
+
"dependencies": {
1064
+
"nanoid": "^3.3.11",
1065
+
"picocolors": "^1.1.1",
1066
+
"source-map-js": "^1.2.1"
1067
+
},
1068
+
"engines": {
1069
+
"node": "^10 || ^12 || >=14"
1070
+
}
1071
+
},
1072
+
"node_modules/rollup": {
1073
+
"version": "4.52.5",
1074
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
1075
+
"integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
1076
+
"dev": true,
1077
+
"license": "MIT",
1078
+
"dependencies": {
1079
+
"@types/estree": "1.0.8"
1080
+
},
1081
+
"bin": {
1082
+
"rollup": "dist/bin/rollup"
1083
+
},
1084
+
"engines": {
1085
+
"node": ">=18.0.0",
1086
+
"npm": ">=8.0.0"
1087
+
},
1088
+
"optionalDependencies": {
1089
+
"@rollup/rollup-android-arm-eabi": "4.52.5",
1090
+
"@rollup/rollup-android-arm64": "4.52.5",
1091
+
"@rollup/rollup-darwin-arm64": "4.52.5",
1092
+
"@rollup/rollup-darwin-x64": "4.52.5",
1093
+
"@rollup/rollup-freebsd-arm64": "4.52.5",
1094
+
"@rollup/rollup-freebsd-x64": "4.52.5",
1095
+
"@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
1096
+
"@rollup/rollup-linux-arm-musleabihf": "4.52.5",
1097
+
"@rollup/rollup-linux-arm64-gnu": "4.52.5",
1098
+
"@rollup/rollup-linux-arm64-musl": "4.52.5",
1099
+
"@rollup/rollup-linux-loong64-gnu": "4.52.5",
1100
+
"@rollup/rollup-linux-ppc64-gnu": "4.52.5",
1101
+
"@rollup/rollup-linux-riscv64-gnu": "4.52.5",
1102
+
"@rollup/rollup-linux-riscv64-musl": "4.52.5",
1103
+
"@rollup/rollup-linux-s390x-gnu": "4.52.5",
1104
+
"@rollup/rollup-linux-x64-gnu": "4.52.5",
1105
+
"@rollup/rollup-linux-x64-musl": "4.52.5",
1106
+
"@rollup/rollup-openharmony-arm64": "4.52.5",
1107
+
"@rollup/rollup-win32-arm64-msvc": "4.52.5",
1108
+
"@rollup/rollup-win32-ia32-msvc": "4.52.5",
1109
+
"@rollup/rollup-win32-x64-gnu": "4.52.5",
1110
+
"@rollup/rollup-win32-x64-msvc": "4.52.5",
1111
+
"fsevents": "~2.3.2"
1112
+
}
1113
+
},
1114
+
"node_modules/source-map-js": {
1115
+
"version": "1.2.1",
1116
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1117
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1118
+
"dev": true,
1119
+
"license": "BSD-3-Clause",
1120
+
"engines": {
1121
+
"node": ">=0.10.0"
1122
+
}
1123
+
},
1124
+
"node_modules/svelte": {
1125
+
"version": "5.43.3",
1126
+
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.3.tgz",
1127
+
"integrity": "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==",
1128
+
"dev": true,
1129
+
"license": "MIT",
1130
+
"dependencies": {
1131
+
"@jridgewell/remapping": "^2.3.4",
1132
+
"@jridgewell/sourcemap-codec": "^1.5.0",
1133
+
"@sveltejs/acorn-typescript": "^1.0.5",
1134
+
"@types/estree": "^1.0.5",
1135
+
"acorn": "^8.12.1",
1136
+
"aria-query": "^5.3.1",
1137
+
"axobject-query": "^4.1.0",
1138
+
"clsx": "^2.1.1",
1139
+
"esm-env": "^1.2.1",
1140
+
"esrap": "^2.1.0",
1141
+
"is-reference": "^3.0.3",
1142
+
"locate-character": "^3.0.0",
1143
+
"magic-string": "^0.30.11",
1144
+
"zimmerframe": "^1.1.2"
1145
+
},
1146
+
"engines": {
1147
+
"node": ">=18"
1148
+
}
1149
+
},
1150
+
"node_modules/vite": {
1151
+
"version": "5.4.21",
1152
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
1153
+
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
1154
+
"dev": true,
1155
+
"license": "MIT",
1156
+
"dependencies": {
1157
+
"esbuild": "^0.21.3",
1158
+
"postcss": "^8.4.43",
1159
+
"rollup": "^4.20.0"
1160
+
},
1161
+
"bin": {
1162
+
"vite": "bin/vite.js"
1163
+
},
1164
+
"engines": {
1165
+
"node": "^18.0.0 || >=20.0.0"
1166
+
},
1167
+
"funding": {
1168
+
"url": "https://github.com/vitejs/vite?sponsor=1"
1169
+
},
1170
+
"optionalDependencies": {
1171
+
"fsevents": "~2.3.3"
1172
+
},
1173
+
"peerDependencies": {
1174
+
"@types/node": "^18.0.0 || >=20.0.0",
1175
+
"less": "*",
1176
+
"lightningcss": "^1.21.0",
1177
+
"sass": "*",
1178
+
"sass-embedded": "*",
1179
+
"stylus": "*",
1180
+
"sugarss": "*",
1181
+
"terser": "^5.4.0"
1182
+
},
1183
+
"peerDependenciesMeta": {
1184
+
"@types/node": {
1185
+
"optional": true
1186
+
},
1187
+
"less": {
1188
+
"optional": true
1189
+
},
1190
+
"lightningcss": {
1191
+
"optional": true
1192
+
},
1193
+
"sass": {
1194
+
"optional": true
1195
+
},
1196
+
"sass-embedded": {
1197
+
"optional": true
1198
+
},
1199
+
"stylus": {
1200
+
"optional": true
1201
+
},
1202
+
"sugarss": {
1203
+
"optional": true
1204
+
},
1205
+
"terser": {
1206
+
"optional": true
1207
+
}
1208
+
}
1209
+
},
1210
+
"node_modules/vitefu": {
1211
+
"version": "1.1.1",
1212
+
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
1213
+
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
1214
+
"dev": true,
1215
+
"license": "MIT",
1216
+
"workspaces": [
1217
+
"tests/deps/*",
1218
+
"tests/projects/*",
1219
+
"tests/projects/workspace/packages/*"
1220
+
],
1221
+
"peerDependencies": {
1222
+
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
1223
+
},
1224
+
"peerDependenciesMeta": {
1225
+
"vite": {
1226
+
"optional": true
1227
+
}
1228
+
}
1229
+
},
1230
+
"node_modules/zimmerframe": {
1231
+
"version": "1.1.4",
1232
+
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
1233
+
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
1234
+
"dev": true,
1235
+
"license": "MIT"
1236
+
}
1237
+
}
1238
+
}
+26
examples/bluesky/static/package.json
+26
examples/bluesky/static/package.json
···
1
+
{
2
+
"name": "bluesky-feed-reader",
3
+
"version": "1.0.0",
4
+
"description": "Bluesky Cap'n Web RPC Svelte Demo",
5
+
"type": "module",
6
+
"browserslist": [
7
+
"Chrome >= 89",
8
+
"Firefox >= 89",
9
+
"Safari >= 15",
10
+
"Edge >= 89"
11
+
],
12
+
"scripts": {
13
+
"dev": "vite",
14
+
"build": "vite build",
15
+
"preview": "vite preview"
16
+
},
17
+
"devDependencies": {
18
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
19
+
"svelte": "^5.0.0",
20
+
"vite": "^5.4.4"
21
+
},
22
+
"dependencies": {
23
+
"capnweb": "latest"
24
+
}
25
+
}
26
+
+516
examples/bluesky/static/src/App.svelte
+516
examples/bluesky/static/src/App.svelte
···
1
+
<script>
2
+
import { newHttpBatchRpcSession } from 'capnweb';
3
+
4
+
let handle = $state('bsky.app');
5
+
let loading = $state(false);
6
+
let error = $state(null);
7
+
let profile = $state(null);
8
+
let feed = $state(null);
9
+
let mode = $state('batched'); // 'batched' or 'sequential'
10
+
let timingInfo = $state(null);
11
+
12
+
async function loadProfileAndFeed() {
13
+
loading = true;
14
+
error = null;
15
+
profile = null;
16
+
feed = null;
17
+
timingInfo = null;
18
+
19
+
try {
20
+
const startTime = performance.now();
21
+
22
+
if (mode === 'batched') {
23
+
// Batched mode: both calls in a single HTTP request with pipelining
24
+
// Create a new session for this batch
25
+
const api = newHttpBatchRpcSession('http://localhost:3000/rpc');
26
+
27
+
try {
28
+
const profilePromise = api.getProfile(handle);
29
+
const feedPromise = api.getFeed(handle, 10);
30
+
31
+
const [profileResult, feedResult] = await Promise.all([
32
+
profilePromise,
33
+
feedPromise,
34
+
]);
35
+
36
+
const endTime = performance.now();
37
+
38
+
console.log('Batched results:', { profileResult, feedResult });
39
+
40
+
profile = profileResult;
41
+
// Unwrap the double-wrapped posts array (Cap'n Web escaping)
42
+
if (feedResult.posts && Array.isArray(feedResult.posts) && feedResult.posts.length > 0 && Array.isArray(feedResult.posts[0])) {
43
+
feedResult.posts = feedResult.posts[0];
44
+
}
45
+
feed = feedResult;
46
+
timingInfo = {
47
+
mode: 'Batched (Pipeline)',
48
+
duration: Math.round(endTime - startTime),
49
+
requests: 1,
50
+
description: 'Both profile and feed fetched in a single HTTP request using Cap\'n Web RPC pipelining'
51
+
};
52
+
} catch (batchError) {
53
+
console.error('Batched mode error:', batchError);
54
+
throw batchError;
55
+
}
56
+
} else {
57
+
// Sequential mode: two separate HTTP requests
58
+
// Create a new session for each request
59
+
const start1 = performance.now();
60
+
const api1 = newHttpBatchRpcSession('http://localhost:3000/rpc');
61
+
profile = await api1.getProfile(handle);
62
+
const end1 = performance.now();
63
+
64
+
const start2 = performance.now();
65
+
const api2 = newHttpBatchRpcSession('http://localhost:3000/rpc');
66
+
feed = await api2.getFeed(handle, 10);
67
+
// Unwrap the double-wrapped posts array (Cap'n Web escaping)
68
+
if (feed.posts && Array.isArray(feed.posts) && feed.posts.length > 0 && Array.isArray(feed.posts[0])) {
69
+
feed.posts = feed.posts[0];
70
+
}
71
+
const end2 = performance.now();
72
+
73
+
const totalDuration = Math.round(end2 - start1);
74
+
75
+
timingInfo = {
76
+
mode: 'Sequential',
77
+
duration: totalDuration,
78
+
requests: 2,
79
+
description: 'Profile and feed fetched in two separate HTTP requests',
80
+
request1Duration: Math.round(end1 - start1),
81
+
request2Duration: Math.round(end2 - start2),
82
+
};
83
+
}
84
+
} catch (err) {
85
+
error = err.message || 'Failed to load profile and feed';
86
+
console.error('Error:', err);
87
+
} finally {
88
+
loading = false;
89
+
}
90
+
}
91
+
92
+
function formatDate(dateString) {
93
+
const date = new Date(dateString);
94
+
const now = new Date();
95
+
const diffMs = now - date;
96
+
const diffMins = Math.floor(diffMs / 60000);
97
+
const diffHours = Math.floor(diffMs / 3600000);
98
+
const diffDays = Math.floor(diffMs / 86400000);
99
+
100
+
if (diffMins < 1) return 'just now';
101
+
if (diffMins < 60) return `${diffMins}m ago`;
102
+
if (diffHours < 24) return `${diffHours}h ago`;
103
+
if (diffDays < 7) return `${diffDays}d ago`;
104
+
105
+
return date.toLocaleDateString();
106
+
}
107
+
</script>
108
+
109
+
<h1>🦋 Bluesky Feed Reader</h1>
110
+
111
+
<div class="container">
112
+
<div class="info-box">
113
+
<h3>Cap'n Web RPC + AT Protocol</h3>
114
+
<p>
115
+
This demo showcases <strong>Cap'n Web RPC</strong> batch pipelining by fetching
116
+
Bluesky profiles and feeds via <strong>AT Protocol's XRPC</strong> endpoints.
117
+
</p>
118
+
<p>
119
+
Try switching between <strong>Batched</strong> and <strong>Sequential</strong> modes
120
+
to see how pipelining reduces round trips and improves performance!
121
+
</p>
122
+
</div>
123
+
124
+
<div class="input-section">
125
+
<div class="mode-toggle">
126
+
<span><strong>Mode:</strong></span>
127
+
<label>
128
+
<input type="radio" bind:group={mode} value="batched" aria-label="Batched (Pipeline) Mode" />
129
+
Batched (Pipeline)
130
+
</label>
131
+
<label>
132
+
<input type="radio" bind:group={mode} value="sequential" aria-label="Sequential Mode" />
133
+
Sequential
134
+
</label>
135
+
</div>
136
+
137
+
<div class="input-group">
138
+
<input
139
+
type="text"
140
+
bind:value={handle}
141
+
placeholder="Enter Bluesky handle (e.g., bsky.app)"
142
+
onkeydown={(e) => e.key === 'Enter' && !loading && loadProfileAndFeed()}
143
+
disabled={loading}
144
+
aria-label="Bluesky handle"
145
+
/>
146
+
<button onclick={loadProfileAndFeed} disabled={loading} aria-label="Load Profile & Feed">
147
+
{loading ? 'Loading...' : 'Load Profile & Feed'}
148
+
</button>
149
+
</div>
150
+
</div>
151
+
152
+
{#if error}
153
+
<div class="error">
154
+
<strong>Error:</strong> {error}
155
+
</div>
156
+
{/if}
157
+
158
+
{#if loading}
159
+
<div class="loading">
160
+
<div>Loading profile and feed...</div>
161
+
</div>
162
+
{/if}
163
+
164
+
{#if timingInfo}
165
+
<div class="timing-info">
166
+
<h4>⚡ Performance: {timingInfo.mode}</h4>
167
+
<p><strong>Total Duration:</strong> {timingInfo.duration}ms</p>
168
+
<p><strong>HTTP Requests:</strong> {timingInfo.requests}</p>
169
+
<p>{timingInfo.description}</p>
170
+
{#if timingInfo.request1Duration}
171
+
<p style="margin-top: 12px;">
172
+
<strong>Request 1 (Profile):</strong> {timingInfo.request1Duration}ms<br/>
173
+
<strong>Request 2 (Feed):</strong> {timingInfo.request2Duration}ms
174
+
</p>
175
+
{/if}
176
+
</div>
177
+
{/if}
178
+
179
+
{#if profile}
180
+
<div class="profile-section">
181
+
<div class="profile-header">
182
+
{#if profile.avatar}
183
+
<img src={profile.avatar} alt={profile.displayName || profile.handle} class="profile-avatar" />
184
+
{/if}
185
+
<div class="profile-info">
186
+
<h2>{profile.displayName || profile.handle}</h2>
187
+
<div class="profile-handle">@{profile.handle}</div>
188
+
<div class="profile-stats">
189
+
<div class="stat">
190
+
<div class="stat-value">{profile.postsCount || 0}</div>
191
+
<div class="stat-label">Posts</div>
192
+
</div>
193
+
<div class="stat">
194
+
<div class="stat-value">{profile.followersCount || 0}</div>
195
+
<div class="stat-label">Followers</div>
196
+
</div>
197
+
<div class="stat">
198
+
<div class="stat-value">{profile.followsCount || 0}</div>
199
+
<div class="stat-label">Following</div>
200
+
</div>
201
+
</div>
202
+
</div>
203
+
</div>
204
+
{#if profile.description}
205
+
<div class="profile-bio">{profile.description}</div>
206
+
{/if}
207
+
</div>
208
+
{/if}
209
+
210
+
{#if feed && feed.posts}
211
+
<div class="feed-section">
212
+
<h3>Recent Posts ({feed.posts.length})</h3>
213
+
{#each feed.posts as post}
214
+
<div class="post">
215
+
<div class="post-author">
216
+
{#if post.author.avatar}
217
+
<img src={post.author.avatar} alt={post.author.handle} class="post-avatar" />
218
+
{/if}
219
+
<div class="post-author-info">
220
+
<div class="post-author-name">{post.author.displayName || post.author.handle}</div>
221
+
<div class="post-author-handle">@{post.author.handle}</div>
222
+
</div>
223
+
</div>
224
+
{#if post.record.text}
225
+
<div class="post-text">{post.record.text}</div>
226
+
{/if}
227
+
<div class="post-stats">
228
+
<div class="post-stat">💬 {post.replyCount || 0}</div>
229
+
<div class="post-stat">🔄 {post.repostCount || 0}</div>
230
+
<div class="post-stat">❤️ {post.likeCount || 0}</div>
231
+
</div>
232
+
{#if post.indexedAt}
233
+
<div class="post-time">{formatDate(post.indexedAt)}</div>
234
+
{/if}
235
+
</div>
236
+
{/each}
237
+
</div>
238
+
{/if}
239
+
</div>
240
+
241
+
<style>
242
+
.container {
243
+
background: white;
244
+
border-radius: 12px;
245
+
padding: 30px;
246
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
247
+
}
248
+
249
+
.input-section {
250
+
margin-bottom: 30px;
251
+
}
252
+
253
+
.input-group {
254
+
display: flex;
255
+
gap: 10px;
256
+
margin-bottom: 15px;
257
+
}
258
+
259
+
input[type="text"] {
260
+
flex: 1;
261
+
padding: 12px 16px;
262
+
border: 2px solid #e0e0e0;
263
+
border-radius: 8px;
264
+
font-size: 16px;
265
+
transition: border-color 0.3s;
266
+
}
267
+
268
+
input[type="text"]:focus {
269
+
outline: none;
270
+
border-color: #667eea;
271
+
}
272
+
273
+
button {
274
+
padding: 12px 24px;
275
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
276
+
color: white;
277
+
border: none;
278
+
border-radius: 8px;
279
+
font-size: 16px;
280
+
font-weight: 600;
281
+
cursor: pointer;
282
+
transition: transform 0.2s, opacity 0.3s;
283
+
}
284
+
285
+
button:hover:not(:disabled) {
286
+
transform: translateY(-2px);
287
+
}
288
+
289
+
button:disabled {
290
+
opacity: 0.6;
291
+
cursor: not-allowed;
292
+
}
293
+
294
+
.mode-toggle {
295
+
display: flex;
296
+
gap: 10px;
297
+
align-items: center;
298
+
margin-bottom: 15px;
299
+
padding: 12px;
300
+
background: #f5f5f5;
301
+
border-radius: 8px;
302
+
}
303
+
304
+
.mode-toggle label {
305
+
display: flex;
306
+
align-items: center;
307
+
gap: 8px;
308
+
cursor: pointer;
309
+
font-size: 14px;
310
+
}
311
+
312
+
.mode-toggle input[type="radio"] {
313
+
cursor: pointer;
314
+
}
315
+
316
+
.loading {
317
+
text-align: center;
318
+
padding: 40px;
319
+
color: #666;
320
+
font-size: 18px;
321
+
}
322
+
323
+
.error {
324
+
padding: 15px;
325
+
background: #fee;
326
+
color: #c33;
327
+
border-radius: 8px;
328
+
margin-bottom: 20px;
329
+
border-left: 4px solid #c33;
330
+
}
331
+
332
+
.profile-section {
333
+
margin-bottom: 30px;
334
+
padding: 25px;
335
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
336
+
border-radius: 12px;
337
+
}
338
+
339
+
.profile-header {
340
+
display: flex;
341
+
gap: 20px;
342
+
align-items: start;
343
+
margin-bottom: 20px;
344
+
}
345
+
346
+
.profile-avatar {
347
+
width: 100px;
348
+
height: 100px;
349
+
border-radius: 50%;
350
+
object-fit: cover;
351
+
border: 4px solid white;
352
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
353
+
}
354
+
355
+
.profile-info h2 {
356
+
margin: 0 0 8px 0;
357
+
font-size: 24px;
358
+
color: #333;
359
+
}
360
+
361
+
.profile-handle {
362
+
color: #667eea;
363
+
font-size: 16px;
364
+
margin-bottom: 12px;
365
+
}
366
+
367
+
.profile-stats {
368
+
display: flex;
369
+
gap: 20px;
370
+
margin-top: 15px;
371
+
}
372
+
373
+
.stat {
374
+
display: flex;
375
+
flex-direction: column;
376
+
align-items: center;
377
+
padding: 10px 20px;
378
+
background: white;
379
+
border-radius: 8px;
380
+
min-width: 80px;
381
+
}
382
+
383
+
.stat-value {
384
+
font-size: 24px;
385
+
font-weight: bold;
386
+
color: #667eea;
387
+
}
388
+
389
+
.stat-label {
390
+
font-size: 12px;
391
+
color: #666;
392
+
text-transform: uppercase;
393
+
margin-top: 4px;
394
+
}
395
+
396
+
.profile-bio {
397
+
margin-top: 15px;
398
+
padding: 15px;
399
+
background: white;
400
+
border-radius: 8px;
401
+
color: #555;
402
+
line-height: 1.6;
403
+
}
404
+
405
+
.feed-section h3 {
406
+
margin-top: 0;
407
+
margin-bottom: 20px;
408
+
color: #333;
409
+
font-size: 22px;
410
+
}
411
+
412
+
.post {
413
+
padding: 20px;
414
+
background: #f9f9f9;
415
+
border-radius: 8px;
416
+
margin-bottom: 15px;
417
+
border-left: 4px solid #667eea;
418
+
}
419
+
420
+
.post-author {
421
+
display: flex;
422
+
align-items: center;
423
+
gap: 12px;
424
+
margin-bottom: 12px;
425
+
}
426
+
427
+
.post-avatar {
428
+
width: 40px;
429
+
height: 40px;
430
+
border-radius: 50%;
431
+
object-fit: cover;
432
+
}
433
+
434
+
.post-author-info {
435
+
flex: 1;
436
+
}
437
+
438
+
.post-author-name {
439
+
font-weight: 600;
440
+
color: #333;
441
+
font-size: 15px;
442
+
}
443
+
444
+
.post-author-handle {
445
+
color: #888;
446
+
font-size: 13px;
447
+
}
448
+
449
+
.post-text {
450
+
margin-bottom: 12px;
451
+
line-height: 1.6;
452
+
color: #333;
453
+
white-space: pre-wrap;
454
+
}
455
+
456
+
.post-stats {
457
+
display: flex;
458
+
gap: 20px;
459
+
font-size: 14px;
460
+
color: #666;
461
+
}
462
+
463
+
.post-stat {
464
+
display: flex;
465
+
align-items: center;
466
+
gap: 6px;
467
+
}
468
+
469
+
.post-time {
470
+
font-size: 13px;
471
+
color: #999;
472
+
margin-top: 8px;
473
+
}
474
+
475
+
.timing-info {
476
+
margin: 20px 0;
477
+
padding: 15px;
478
+
background: #e8f5e9;
479
+
border-radius: 8px;
480
+
border-left: 4px solid #4caf50;
481
+
}
482
+
483
+
.timing-info h4 {
484
+
margin: 0 0 8px 0;
485
+
color: #2e7d32;
486
+
font-size: 16px;
487
+
}
488
+
489
+
.timing-info p {
490
+
margin: 4px 0;
491
+
color: #555;
492
+
font-size: 14px;
493
+
}
494
+
495
+
.info-box {
496
+
padding: 20px;
497
+
background: #f0f7ff;
498
+
border-radius: 8px;
499
+
border-left: 4px solid #2196f3;
500
+
margin-bottom: 20px;
501
+
}
502
+
503
+
.info-box h3 {
504
+
margin: 0 0 10px 0;
505
+
color: #1565c0;
506
+
font-size: 18px;
507
+
}
508
+
509
+
.info-box p {
510
+
margin: 8px 0;
511
+
color: #555;
512
+
line-height: 1.6;
513
+
}
514
+
515
+
516
+
</style>
+28
examples/bluesky/static/src/app.css
+28
examples/bluesky/static/src/app.css
···
1
+
* {
2
+
box-sizing: border-box;
3
+
}
4
+
5
+
body {
6
+
margin: 0;
7
+
padding: 20px;
8
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
9
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
10
+
sans-serif;
11
+
-webkit-font-smoothing: antialiased;
12
+
-moz-osx-font-smoothing: grayscale;
13
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
14
+
min-height: 100vh;
15
+
}
16
+
17
+
#app {
18
+
max-width: 900px;
19
+
margin: 0 auto;
20
+
}
21
+
22
+
h1 {
23
+
color: white;
24
+
text-align: center;
25
+
margin-bottom: 30px;
26
+
font-size: 2.5rem;
27
+
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
28
+
}
+10
examples/bluesky/static/src/main.js
+10
examples/bluesky/static/src/main.js
+6
examples/bluesky/static/svelte.config.js
+6
examples/bluesky/static/svelte.config.js
+13
examples/bluesky/static/vite.config.js
+13
examples/bluesky/static/vite.config.js